GZCTF平台对接QQ机器人 本文章写于2024-04-23,不保证后续可用性。
参考
采用mirai项目
安装mirai 用mirai-installer安装
安装
初始化
安装完毕
插件安装及配置 mirai-http-api插件用于开放接口
打开mcl后输入
1 mcl --update-package net.mamoe:mirai-api-http --channel stable-v2 --type plugin
即可安装成功
配置文件setting.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 adapters: - http - ws enableVerify: true verifyKey: 1234567890 debug: false singleMode: false cacheSize: 4096 adapterSettings: http: host: localhost port: 8080 cors: ["*" ] unreadQueueMaxSize: 100 ws: host: localhost port: 8080 reservedSyncId: -1
采用http,verifykey是后续用于认证的。如若使用Docker开放端口,host要改为0.0.0.0,不然访问不到。
我的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 adapters: - http enableVerify: true verifyKey: 1234567890 debug: false singleMode: false cacheSize: 4096 adapterSettings: http: host: 0.0 .0 .0 port: 7777 cors: ["*" ] unreadQueueMaxSize: 100
loginsolver插件用于登录,拖入plugins即可
需要用到22333
端口,docker中记得开放,登录时会返回地址,需要用手机下载Sakuralogin来输入地址登录。
签名服务器Qsign
下载时下载d62ddce
版本,将plugins
中的jar放到bot
的plugins
中,将txlib
放到bot
的根目录即可。
要修改配置文件中的协议版本为8.9.90
配置即可完成
运行 运行mcl
使用内置命令登录,最好使用ANDROID_PAD
,手表协议发20条就会风控。
1 /login qq password ANDROID_PAD
登录完之后可以设置autoLogin,具体不赘述。
使用 使用的什么adapter就看什么,详细描述了api接口
简单实现的一个python bot类,抛砖引玉,还可以继续扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import requestsVERIFY_KEY = '' BOT_URL = 'http://x.x.x.x:7777' class Bot : def __init__ (self, verify_key: str , url: str , qq: str ): self .verify_key = verify_key self .url = url self .qq = qq self .session = self .verify() self .bind() def verify (self ): url = f'{self.url} /verify' data = {"verifyKey" : self .verify_key} req = requests.post(url, json=data) return req.json()['session' ] def bind (self ): url = f'{self.url} /bind' data = {"sessionKey" : self .session, 'qq' : self .qq} req = requests.post(url, json=data) return req.json()['msg' ] def rebind (self ): self .session = self .verify() self .bind() def get_group_list (self ): self .check_session() url = f'{self.url} /groupList?sessionKey={self.session} ' rep = requests.get(url) return rep.json() def check_session (self ): url = f'{self.url} /sessionInfo?sessionKey={self.session} ' rep = requests.get(url) if rep.json()['code' ] == 3 : self .rebind() return def send_group_message (self, id : int , message: str ): self .check_session() url = f'{self.url} /sendGroupMessage' data = {"sessionKey" : self .session, 'target' : id , 'messageChain' : [ {"type" : "Plain" , "text" : message}, ]} req = requests.post(url, json=data) return req.json()['msg' ] if __name__ == '__main__' : bot = Bot(VERIFY_KEY, BOT_URL, '0000000000' ) print (bot.get_group_list()) print (bot.send_group_message(0000000000 , '123123' ))
对接GZCTF平台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import timefrom datetime import timezone, timedeltaimport requestsfrom dateutil.parser import isoparsefrom bot import Botimport loggingfrom rich.logging import RichHandlerfrom rich.status import StatusINTERVAL = 2 FORMAT = "%(message)s" VERIFY_KEY = '' BOT_URL = 'http://0.0.0.0:7777' BOT_QQ = '' GROUP_NOTICE_ID = '' BOT = Bot(VERIFY_KEY, BOT_URL, BOT_QQ) API_URL = 'http://0.0.0.0/api/game/4/notices' BANNER = '' NOW_ID = 0 TEMPLATES = { 'Normal' : '【比赛公告】\n内容:%s\n时间:%s' , 'NewChallenge' : '【新增题目】\n[%s]\n时间:%s' , 'NewHint' : '【题目提示】\n[%s]有新提示,请注意查收\n时间:%s' , 'FirstBlood' : '【一血播报】\n恭喜%s拿下[%s]一血\n时间:%s' , 'SecondBlood' : ' 【二血播报】\n恭喜%s拿下[%s]二血\n时间:%s' , 'ThirdBlood' : ' 【三血播报】\n恭喜%s拿下[%s]三血\n时间:%s' } def processTime (t ): t_truncated = t[:26 ] + t[26 :].split('+' )[0 ] input_time = isoparse(t_truncated) input_time_utc = input_time.replace(tzinfo=timezone.utc) beijing_timezone = timezone(timedelta(hours=8 )) beijing_time = input_time_utc.astimezone(beijing_timezone) return beijing_time.strftime("%Y-%m-%d %H:%M:%S" ) if __name__ == '__main__' : logging.basicConfig( level=logging.INFO, format =FORMAT, datefmt="[%X]" , handlers=[RichHandler()] ) log = logging.getLogger("rich" ) notices = requests.get(API_URL).json() notices = sorted (notices, key=lambda x: x['id' ]) NOW_ID = notices[-1 ]['id' ] status = Status('Waiting for new notice' ) status.start() while True : try : notices = requests.get(API_URL).json() except KeyboardInterrupt: log.info('Exit bot' ) break except Exception: log.warning('Warning: request failed' ) continue notices = sorted (notices, key=lambda x: x['id' ]) for notice in notices: if notice['id' ] > NOW_ID: message = TEMPLATES[notice['type' ]] % tuple (notice['values' ] + [processTime(notice['time' ])]) log.info(f'sending to {GROUP_NOTICE_ID} message: \n{message} ' ) BOT.send_group_message(GROUP_NOTICE_ID, message) NOW_ID = notice['id' ] try : time.sleep(INTERVAL) except KeyboardInterrupt: log.info('Exit bot' ) break status.stop()
还是挺稳的。