From 59bdee7137f468f2781522e17cd69122f8acbb0b Mon Sep 17 00:00:00 2001 From: Rock Chin <1010553892@qq.com> Date: Wed, 15 Mar 2023 15:54:04 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0IM=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/openai/modelmgr.py | 2 +- pkg/qqbot/models/__init__.py | 0 pkg/qqbot/models/im.py | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 pkg/qqbot/models/__init__.py create mode 100644 pkg/qqbot/models/im.py diff --git a/pkg/openai/modelmgr.py b/pkg/openai/modelmgr.py index 7f634563..dd05cb3f 100644 --- a/pkg/openai/modelmgr.py +++ b/pkg/openai/modelmgr.py @@ -60,7 +60,7 @@ class ModelRequest: """异步请求""" try: - self.ret:dict = await self.request_fun(**kwargs) + self.ret: dict = await self.request_fun(**kwargs) self.request_ready = True except aiE.APIConnectionError as e: self.error_info = "{}\n请检查网络连接或代理是否正常".format(e) diff --git a/pkg/qqbot/models/__init__.py b/pkg/qqbot/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/qqbot/models/im.py b/pkg/qqbot/models/im.py new file mode 100644 index 00000000..6ec9293e --- /dev/null +++ b/pkg/qqbot/models/im.py @@ -0,0 +1,49 @@ +# IM 框架调用的抽象层 +from mirai import MessageChain, Event + + +class AbstractIM: + """IM框架抽象模型""" + def __init__(self): + raise NotImplementedError + + def run(self): + """启动IM框架接口, 阻塞方法""" + raise NotImplementedError + + def __send_private_msg__(self, user, msg: MessageChain): + """发送私聊消息""" + raise NotImplementedError + + def send_private_msg(self, user, msg: MessageChain): + """发送私聊消息, 供上层调用, 解决兼容性问题后再调用__send_private_msg__, 请勿重写此方法""" + return self.__send_private_msg__(user, self.message_chain_convertor(msg)) + + def __send_group_msg__(self, group, msg: MessageChain): + """发送群消息""" + raise NotImplementedError + + def send_group_msg(self, group, msg: MessageChain): + """发送群消息, 供上层调用, 解决兼容性问题后再调用__send_group_msg__, 请勿重写此方法""" + return self.__send_group_msg__(group, self.message_chain_convertor(msg)) + + def __register_handler__(self, event, handler): + """注册事件处理器""" + raise NotImplementedError + + def register_handler(self, event: Event, handler): + """注册事件处理器, 供上层调用, 解决兼容性问题后再调用__register_handler__, 请勿重写此方法""" + return self.register_handler(self.event_convertor(event), handler) + + def event_convertor(self, event: Event): + """事件转换器""" + raise NotImplementedError + + def message_chain_convertor(self, msg: MessageChain): + """消息链转换器""" + raise NotImplementedError + + def get_bot_instance(self): + """获取IM框架机器人实例""" + raise NotImplementedError + From 91746448a35c28201fa848467a59bcd54d232a17 Mon Sep 17 00:00:00 2001 From: Rock Chin <1010553892@qq.com> Date: Fri, 21 Apr 2023 16:36:59 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=E6=B6=88=E6=81=AF=E6=BA=90?= =?UTF-8?q?=E9=80=82=E9=85=8D=E5=99=A8=E6=A8=A1=E5=9E=8B=E5=8F=8AYiriMirai?= =?UTF-8?q?=E7=9A=84=E9=80=82=E9=85=8D=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/qqbot/adapter.py | 75 +++++++++++++++ pkg/qqbot/models/im.py | 49 ---------- pkg/qqbot/{models => sources}/__init__.py | 0 pkg/qqbot/sources/yirimirai.py | 106 ++++++++++++++++++++++ 4 files changed, 181 insertions(+), 49 deletions(-) create mode 100644 pkg/qqbot/adapter.py delete mode 100644 pkg/qqbot/models/im.py rename pkg/qqbot/{models => sources}/__init__.py (100%) create mode 100644 pkg/qqbot/sources/yirimirai.py diff --git a/pkg/qqbot/adapter.py b/pkg/qqbot/adapter.py new file mode 100644 index 00000000..23407564 --- /dev/null +++ b/pkg/qqbot/adapter.py @@ -0,0 +1,75 @@ +# MessageSource的适配器 +import typing + +import mirai + +class MessageSourceAdapter: + def __init__(self, config: dict): + pass + + def send_message( + self, + target_type: str, + target_id: str, + message: mirai.MessageChain + ): + """发送消息 + + Args: + target_type (str): 目标类型,`person`或`group` + target_id (str): 目标ID + message (mirai.MessageChain): YiriMirai库的消息链 + """ + raise NotImplementedError + + def reply_message( + self, + message_source: mirai.MessageEvent, + message: mirai.MessageChain + ): + """回复消息 + + Args: + message_source (mirai.MessageEvent): YiriMirai消息源事件 + message (mirai.MessageChain): YiriMirai库的消息链 + """ + raise NotImplementedError + + def register_listener( + self, + event_type: typing.Type[mirai.Event], + callback: typing.Callable[[mirai.Event], None] + ): + """注册事件监听器 + + Args: + event_type (typing.Type[mirai.Event]): YiriMirai事件类型 + callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 + """ + raise NotImplementedError + + def unregister_listener( + self, + event_type: typing.Type[mirai.Event], + callback: typing.Callable[[mirai.Event], None] + ): + """注销事件监听器 + + Args: + event_type (typing.Type[mirai.Event]): YiriMirai事件类型 + callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 + """ + raise NotImplementedError + + def run_sync(self): + """以阻塞的方式运行适配器""" + raise NotImplementedError + + def kill(self) -> bool: + """关闭适配器 + + Returns: + bool: 是否成功关闭,热重载时若此函数返回False则不会重载MessageSource底层 + """ + raise NotImplementedError + \ No newline at end of file diff --git a/pkg/qqbot/models/im.py b/pkg/qqbot/models/im.py deleted file mode 100644 index 6ec9293e..00000000 --- a/pkg/qqbot/models/im.py +++ /dev/null @@ -1,49 +0,0 @@ -# IM 框架调用的抽象层 -from mirai import MessageChain, Event - - -class AbstractIM: - """IM框架抽象模型""" - def __init__(self): - raise NotImplementedError - - def run(self): - """启动IM框架接口, 阻塞方法""" - raise NotImplementedError - - def __send_private_msg__(self, user, msg: MessageChain): - """发送私聊消息""" - raise NotImplementedError - - def send_private_msg(self, user, msg: MessageChain): - """发送私聊消息, 供上层调用, 解决兼容性问题后再调用__send_private_msg__, 请勿重写此方法""" - return self.__send_private_msg__(user, self.message_chain_convertor(msg)) - - def __send_group_msg__(self, group, msg: MessageChain): - """发送群消息""" - raise NotImplementedError - - def send_group_msg(self, group, msg: MessageChain): - """发送群消息, 供上层调用, 解决兼容性问题后再调用__send_group_msg__, 请勿重写此方法""" - return self.__send_group_msg__(group, self.message_chain_convertor(msg)) - - def __register_handler__(self, event, handler): - """注册事件处理器""" - raise NotImplementedError - - def register_handler(self, event: Event, handler): - """注册事件处理器, 供上层调用, 解决兼容性问题后再调用__register_handler__, 请勿重写此方法""" - return self.register_handler(self.event_convertor(event), handler) - - def event_convertor(self, event: Event): - """事件转换器""" - raise NotImplementedError - - def message_chain_convertor(self, msg: MessageChain): - """消息链转换器""" - raise NotImplementedError - - def get_bot_instance(self): - """获取IM框架机器人实例""" - raise NotImplementedError - diff --git a/pkg/qqbot/models/__init__.py b/pkg/qqbot/sources/__init__.py similarity index 100% rename from pkg/qqbot/models/__init__.py rename to pkg/qqbot/sources/__init__.py diff --git a/pkg/qqbot/sources/yirimirai.py b/pkg/qqbot/sources/yirimirai.py new file mode 100644 index 00000000..dcc3b8d4 --- /dev/null +++ b/pkg/qqbot/sources/yirimirai.py @@ -0,0 +1,106 @@ +from ..adapter import MessageSourceAdapter +import mirai +import mirai.models.bus + +import asyncio +import typing + +class YiriMiraiAdapter(MessageSourceAdapter): + """YiriMirai适配器""" + bot: mirai.Mirai + + def __init__(self, config: dict): + """初始化YiriMirai的对象""" + if 'adapter' not in config or \ + config['adapter'] == 'WebSocketAdapter': + self.bot = mirai.Mirai( + qq=config['qq'], + adapter=mirai.WebSocketAdapter( + host=config['host'], + port=config['port'], + verify_key=config['verifyKey'] + ) + ) + elif config['adapter'] == 'HTTPAdapter': + self.bot = mirai.Mirai( + qq=config['qq'], + adapter=mirai.HTTPAdapter( + host=config['host'], + port=config['port'], + verify_key=config['verifyKey'] + ) + ) + else: + raise Exception('Unknown adapter for YiriMirai: ' + config['adapter']) + + def send_message( + self, + target_type: str, + target_id: str, + message: mirai.MessageChain + ): + """发送消息 + + Args: + target_type (str): 目标类型,`person`或`group` + target_id (str): 目标ID + message (mirai.MessageChain): YiriMirai库的消息链 + """ + task = None + if target_type == 'person': + task = self.bot.send_friend_message(int(target_id), message) + elif target_type == 'group': + task = self.bot.send_group_message(int(target_id), message) + else: + raise Exception('Unknown target type: ' + target_type) + + asyncio.run(task) + + def reply_message( + self, + message_source: mirai.MessageEvent, + message: mirai.MessageChain + ): + """回复消息 + + Args: + message_source (mirai.MessageEvent): YiriMirai消息源事件 + message (mirai.MessageChain): YiriMirai库的消息链 + """ + asyncio.run(self.bot.send(message_source, message)) + + def register_listener( + self, + event_type: typing.Type[mirai.Event], + callback: typing.Callable[[mirai.Event], None] + ): + """注册事件监听器 + + Args: + event_type (typing.Type[mirai.Event]): YiriMirai事件类型 + callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 + """ + self.bot.on(event_type)(callback) + + def unregister_listener( + self, + event_type: typing.Type[mirai.Event], + callback: typing.Callable[[mirai.Event], None] + ): + """注销事件监听器 + + Args: + event_type (typing.Type[mirai.Event]): YiriMirai事件类型 + callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 + """ + assert isinstance(self.bot, mirai.Mirai) + bus = self.bot.bus + assert isinstance(bus, mirai.models.bus.ModelEventBus) + + bus.unsubscribe(event_type, callback) + + def run_sync(self): + self.bot.run() + + def kill(self) -> bool: + return False From 016391c97669bc224ebb11185aabbae0ef7d4bb3 Mon Sep 17 00:00:00 2001 From: Rock Chin <1010553892@qq.com> Date: Fri, 21 Apr 2023 17:15:32 +0800 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=E4=B8=8D=E5=86=8D=E5=90=91QQBo?= =?UTF-8?q?tManager=E4=B8=AD=E4=BC=A0=E9=80=92config=E4=B8=AD=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E7=9A=84=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 4 +--- pkg/qqbot/manager.py | 12 ++++++------ pkg/qqbot/sources/yirimirai.py | 1 + 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/main.py b/main.py index aa997148..bd9c71ea 100644 --- a/main.py +++ b/main.py @@ -193,9 +193,7 @@ def start(first_time_init=False): pkg.openai.session.load_sessions() # 初始化qq机器人 - qqbot = pkg.qqbot.manager.QQBotManager(mirai_http_api_config=config.mirai_http_api_config, - timeout=config.process_message_timeout, retry=config.retry_times, - first_time_init=first_time_init) + qqbot = pkg.qqbot.manager.QQBotManager(first_time_init=first_time_init) # 加载插件 import pkg.plugin.host diff --git a/pkg/qqbot/manager.py b/pkg/qqbot/manager.py index b8b9a71f..d1c38756 100644 --- a/pkg/qqbot/manager.py +++ b/pkg/qqbot/manager.py @@ -73,9 +73,12 @@ class QQBotManager: ban_person = [] ban_group = [] - def __init__(self, mirai_http_api_config: dict, timeout: int = 60, retry: int = 3, first_time_init=True): - self.timeout = timeout - self.retry = retry + def __init__(self, first_time_init=True): + import config + + mirai_http_api_config = config.mirai_http_api_config + self.timeout = config.process_message_timeout + self.retry = config.retry_times # 加载禁用列表 if os.path.exists("banlist.py"): @@ -195,9 +198,6 @@ class QQBotManager: self.unsubscribe_all = unsubscribe_all - def go(self, func, *args, **kwargs): - self.pool.submit(func, *args, **kwargs) - def first_time_init(self, mirai_http_api_config: dict): """热重载后不再运行此函数""" if 'adapter' not in mirai_http_api_config or mirai_http_api_config['adapter'] == "WebSocketAdapter": diff --git a/pkg/qqbot/sources/yirimirai.py b/pkg/qqbot/sources/yirimirai.py index dcc3b8d4..111e8f8e 100644 --- a/pkg/qqbot/sources/yirimirai.py +++ b/pkg/qqbot/sources/yirimirai.py @@ -5,6 +5,7 @@ import mirai.models.bus import asyncio import typing + class YiriMiraiAdapter(MessageSourceAdapter): """YiriMirai适配器""" bot: mirai.Mirai From 160086feb9b2320544a63ae76ae26acf6e1e0011 Mon Sep 17 00:00:00 2001 From: Rock Chin <1010553892@qq.com> Date: Fri, 21 Apr 2023 17:51:58 +0800 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=E5=AE=8C=E6=88=90MessageSource?= =?UTF-8?q?=E9=80=82=E9=85=8D=E5=99=A8=E8=A7=A3=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config-template.py | 9 + main.py | 2 +- pkg/plugin/host.py | 5 + pkg/qqbot/adapter.py | 11 +- pkg/qqbot/manager.py | 289 +++++++++++++++++---------------- pkg/qqbot/process.py | 7 +- pkg/qqbot/sources/yirimirai.py | 12 +- 7 files changed, 185 insertions(+), 150 deletions(-) diff --git a/config-template.py b/config-template.py index 0f16e021..7568f7d3 100644 --- a/config-template.py +++ b/config-template.py @@ -1,6 +1,12 @@ # 配置文件: 注释里标[必需]的参数必须修改, 其他参数根据需要修改, 但请勿删除 import logging +# 消息处理协议适配器 +# 目前支持以下适配器: +# - "yirimirai": YiriMirai框架适配器, 请填写mirai_http_api_config +# - "nonebot2": NoneBot2框架适配器, 请填写nonebot2_config +msg_source_adapter = "yirimirai" + # [必需] Mirai的配置 # 请到配置mirai的步骤中的教程查看每个字段的信息 # adapter: 选择适配器,目前支持HTTPAdapter和WebSocketAdapter @@ -18,6 +24,9 @@ mirai_http_api_config = { "qq": 1234567890 } +# NoneBot2的配置 +nonebot2_config = {} + # [必需] OpenAI的配置 # api_key: OpenAI的API Key # http_proxy: 请求OpenAI时使用的代理,None为不使用,https和socks5暂不能使用 diff --git a/main.py b/main.py index bd9c71ea..fbdd961a 100644 --- a/main.py +++ b/main.py @@ -208,7 +208,7 @@ def start(first_time_init=False): def run_bot_wrapper(): global known_exception_caught try: - qqbot.bot.run() + qqbot.adapter.run_sync() except TypeError as e: if str(e).__contains__("argument 'debug'"): logging.error( diff --git a/pkg/plugin/host.py b/pkg/plugin/host.py index 4655870f..49609e07 100644 --- a/pkg/plugin/host.py +++ b/pkg/plugin/host.py @@ -11,6 +11,7 @@ import traceback import pkg.utils.context as context import pkg.plugin.switch as switch import pkg.plugin.settings as settings +import pkg.qqbot.adapter as msadapter from mirai import Mirai @@ -276,6 +277,10 @@ class PluginHost: """获取机器人对象""" return context.get_qqbot_manager().bot + def get_bot_adapter(self) -> msadapter.MessageSourceAdapter: + """获取消息源适配器""" + return context.get_qqbot_manager().adapter + def send_person_message(self, person, message): """发送私聊消息""" asyncio.run(self.get_bot().send_friend_message(person, message)) diff --git a/pkg/qqbot/adapter.py b/pkg/qqbot/adapter.py index 23407564..5d4faa00 100644 --- a/pkg/qqbot/adapter.py +++ b/pkg/qqbot/adapter.py @@ -3,6 +3,7 @@ import typing import mirai + class MessageSourceAdapter: def __init__(self, config: dict): pass @@ -25,16 +26,22 @@ class MessageSourceAdapter: def reply_message( self, message_source: mirai.MessageEvent, - message: mirai.MessageChain + message: mirai.MessageChain, + quote_origin: bool = False ): """回复消息 Args: message_source (mirai.MessageEvent): YiriMirai消息源事件 message (mirai.MessageChain): YiriMirai库的消息链 + quote_origin (bool, optional): 是否引用原消息. Defaults to False. """ raise NotImplementedError - + + def is_muted(self, group_id: int) -> bool: + """获取账号是否在指定群被禁言""" + raise NotImplementedError + def register_listener( self, event_type: typing.Type[mirai.Event], diff --git a/pkg/qqbot/manager.py b/pkg/qqbot/manager.py index d1c38756..747854cb 100644 --- a/pkg/qqbot/manager.py +++ b/pkg/qqbot/manager.py @@ -3,9 +3,9 @@ import json import os import threading -import mirai.models.bus + from mirai import At, GroupMessage, MessageEvent, Mirai, StrangerMessage, WebSocketAdapter, HTTPAdapter, \ - FriendMessage, Image + FriendMessage, Image, MessageChain, Plain from func_timeout import func_set_timeout import pkg.openai.session @@ -21,6 +21,8 @@ import pkg.plugin.host as plugin_host import pkg.plugin.models as plugin_models import tips as tips_custom +import pkg.qqbot.adapter as msadapter + # 检查消息是否符合泛响应匹配机制 def check_response_rule(text: str): @@ -64,7 +66,9 @@ def random_responding(): class QQBotManager: retry = 3 - bot: Mirai = None + adapter: msadapter.MessageSourceAdapter = None + + bot_account_id: int = 0 reply_filter = None @@ -80,6 +84,119 @@ class QQBotManager: self.timeout = config.process_message_timeout self.retry = config.retry_times + # 由于YiriMirai的bot对象是单例的,且shutdown方法暂时无法使用 + # 故只在第一次初始化时创建bot对象,重载之后使用原bot对象 + # 因此,bot的配置不支持热重载 + if first_time_init: + if config.msg_source_adapter == 'yirimirai': + from pkg.qqbot.sources.yirimirai import YiriMiraiAdapter + self.bot_account_id = config.mirai_http_api_config['qq'] + self.adapter = YiriMiraiAdapter(mirai_http_api_config) + elif config.msg_source_adapter == 'nonebot2': + pass + else: + self.adapter = pkg.utils.context.get_qqbot_manager().adapter + + pkg.utils.context.set_qqbot_manager(self) + + # 注册诸事件 + # Caution: 注册新的事件处理器之后,请务必在unsubscribe_all中编写相应的取消订阅代码 + def on_friend_message(event: FriendMessage): + + def friend_message_handler(): + # 触发事件 + args = { + "launcher_type": "person", + "launcher_id": event.sender.id, + "sender_id": event.sender.id, + "message_chain": event.message_chain, + } + plugin_event = plugin_host.emit(plugin_models.PersonMessageReceived, **args) + + if plugin_event.is_prevented_default(): + return + + self.on_person_message(event) + + pkg.utils.context.get_thread_ctl().submit_user_task( + friend_message_handler, + ) + self.adapter.register_listener( + FriendMessage, + on_friend_message + ) + + def on_stranger_message(event: StrangerMessage): + + def stranger_message_handler(): + # 触发事件 + args = { + "launcher_type": "person", + "launcher_id": event.sender.id, + "sender_id": event.sender.id, + "message_chain": event.message_chain, + } + plugin_event = plugin_host.emit(plugin_models.PersonMessageReceived, **args) + + if plugin_event.is_prevented_default(): + return + + self.on_person_message(event) + + pkg.utils.context.get_thread_ctl().submit_user_task( + stranger_message_handler, + ) + self.adapter.register_listener( + StrangerMessage, + on_stranger_message + ) + + def on_group_message(event: GroupMessage): + + def group_message_handler(event: GroupMessage): + # 触发事件 + args = { + "launcher_type": "group", + "launcher_id": event.group.id, + "sender_id": event.sender.id, + "message_chain": event.message_chain, + } + plugin_event = plugin_host.emit(plugin_models.GroupMessageReceived, **args) + + if plugin_event.is_prevented_default(): + return + + self.on_group_message(event) + + pkg.utils.context.get_thread_ctl().submit_user_task( + group_message_handler, + event + ) + self.adapter.register_listener( + GroupMessage, + on_group_message + ) + + def unsubscribe_all(): + """取消所有订阅 + + 用于在热重载流程中卸载所有事件处理器 + """ + self.adapter.unregister_listener( + FriendMessage, + on_friend_message + ) + self.adapter.unregister_listener( + StrangerMessage, + on_stranger_message + ) + self.adapter.unregister_listener( + GroupMessage, + on_group_message + ) + + self.unsubscribe_all = unsubscribe_all + # 加载禁用列表 if os.path.exists("banlist.py"): import banlist @@ -102,139 +219,20 @@ class QQBotManager: else: self.reply_filter = pkg.qqbot.filter.ReplyFilter([]) - # 由于YiriMirai的bot对象是单例的,且shutdown方法暂时无法使用 - # 故只在第一次初始化时创建bot对象,重载之后使用原bot对象 - # 因此,bot的配置不支持热重载 - if first_time_init: - self.first_time_init(mirai_http_api_config) - else: - self.bot = pkg.utils.context.get_qqbot_manager().bot - - pkg.utils.context.set_qqbot_manager(self) - - # Caution: 注册新的事件处理器之后,请务必在unsubscribe_all中编写相应的取消订阅代码 - @self.bot.on(FriendMessage) - async def on_friend_message(event: FriendMessage): - - def friend_message_handler(event: FriendMessage): - - # 触发事件 - args = { - "launcher_type": "person", - "launcher_id": event.sender.id, - "sender_id": event.sender.id, - "message_chain": event.message_chain, - } - plugin_event = plugin_host.emit(plugin_models.PersonMessageReceived, **args) - - if plugin_event.is_prevented_default(): - return - - self.on_person_message(event) - - pkg.utils.context.get_thread_ctl().submit_user_task( - friend_message_handler, - event - ) - - @self.bot.on(StrangerMessage) - async def on_stranger_message(event: StrangerMessage): - - def stranger_message_handler(event: StrangerMessage): - # 触发事件 - args = { - "launcher_type": "person", - "launcher_id": event.sender.id, - "sender_id": event.sender.id, - "message_chain": event.message_chain, - } - plugin_event = plugin_host.emit(plugin_models.PersonMessageReceived, **args) - - if plugin_event.is_prevented_default(): - return - - self.on_person_message(event) - - pkg.utils.context.get_thread_ctl().submit_user_task( - stranger_message_handler, - event - ) - - @self.bot.on(GroupMessage) - async def on_group_message(event: GroupMessage): - - def group_message_handler(event: GroupMessage): - # 触发事件 - args = { - "launcher_type": "group", - "launcher_id": event.group.id, - "sender_id": event.sender.id, - "message_chain": event.message_chain, - } - plugin_event = plugin_host.emit(plugin_models.GroupMessageReceived, **args) - - if plugin_event.is_prevented_default(): - return - - self.on_group_message(event) - - pkg.utils.context.get_thread_ctl().submit_user_task( - group_message_handler, - event - ) - - def unsubscribe_all(): - """取消所有订阅 - - 用于在热重载流程中卸载所有事件处理器 - """ - assert isinstance(self.bot, Mirai) - bus = self.bot.bus - assert isinstance(bus, mirai.models.bus.ModelEventBus) - - bus.unsubscribe(FriendMessage, on_friend_message) - bus.unsubscribe(StrangerMessage, on_stranger_message) - bus.unsubscribe(GroupMessage, on_group_message) - - self.unsubscribe_all = unsubscribe_all - - def first_time_init(self, mirai_http_api_config: dict): - """热重载后不再运行此函数""" - if 'adapter' not in mirai_http_api_config or mirai_http_api_config['adapter'] == "WebSocketAdapter": - bot = Mirai( - qq=mirai_http_api_config['qq'], - adapter=WebSocketAdapter( - verify_key=mirai_http_api_config['verifyKey'], - host=mirai_http_api_config['host'], - port=mirai_http_api_config['port'] - ) - ) - elif mirai_http_api_config['adapter'] == "HTTPAdapter": - bot = Mirai( - qq=mirai_http_api_config['qq'], - adapter=HTTPAdapter( - verify_key=mirai_http_api_config['verifyKey'], - host=mirai_http_api_config['host'], - port=mirai_http_api_config['port'] - ) - ) - - else: - raise Exception("未知的适配器类型") - - self.bot = bot - def send(self, event, msg, check_quote=True): config = pkg.utils.context.get_config() - asyncio.run( - self.bot.send(event, msg, quote=True if config.quote_origin and check_quote else False)) + self.adapter.reply_message( + event, + msg, + quote_origin=True if config.quote_origin and check_quote else False + ) # 私聊消息处理 def on_person_message(self, event: MessageEvent): import config reply = '' - if event.sender.id == self.bot.qq: + if event.sender.id == self.bot_account_id: pass else: if Image in event.message_chain: @@ -277,8 +275,8 @@ class QQBotManager: def process(text=None) -> str: replys = "" - if At(self.bot.qq) in event.message_chain: - event.message_chain.remove(At(self.bot.qq)) + if At(self.bot_account_id) in event.message_chain: + event.message_chain.remove(At(self.bot_account_id)) # 超时则重试,重试超过次数则放弃 failed = 0 @@ -312,7 +310,7 @@ class QQBotManager: if Image in event.message_chain: pass else: - if At(self.bot.qq) in event.message_chain and response_at(): + if At(self.bot_account_id) in event.message_chain and response_at(): # 直接调用 reply = process() else: @@ -334,22 +332,33 @@ class QQBotManager: if config.admin_qq != 0 and config.admin_qq != []: logging.info("通知管理员:{}".format(message)) if type(config.admin_qq) == int: - send_task = self.bot.send_friend_message(config.admin_qq, "[bot]{}".format(message)) - threading.Thread(target=asyncio.run, args=(send_task,)).start() + self.adapter.send_message( + "person", + config.admin_qq, + MessageChain([Plain("[bot]{}".format(message))]) + ) else: for adm in config.admin_qq: - send_task = self.bot.send_friend_message(adm, "[bot]{}".format(message)) - threading.Thread(target=asyncio.run, args=(send_task,)).start() - + self.adapter.send_message( + "person", + adm, + MessageChain([Plain("[bot]{}".format(message))]) + ) def notify_admin_message_chain(self, message): config = pkg.utils.context.get_config() if config.admin_qq != 0 and config.admin_qq != []: logging.info("通知管理员:{}".format(message)) if type(config.admin_qq) == int: - send_task = self.bot.send_friend_message(config.admin_qq, message) - threading.Thread(target=asyncio.run, args=(send_task,)).start() + self.adapter.send_message( + "person", + config.admin_qq, + message + ) else: for adm in config.admin_qq: - send_task = self.bot.send_friend_message(adm, message) - threading.Thread(target=asyncio.run, args=(send_task,)).start() + self.adapter.send_message( + "person", + adm, + message + ) diff --git a/pkg/qqbot/process.py b/pkg/qqbot/process.py index 672696c8..ff85126c 100644 --- a/pkg/qqbot/process.py +++ b/pkg/qqbot/process.py @@ -66,11 +66,8 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes # 检查是否被禁言 if launcher_type == 'group': - result = mgr.bot.member_info(target=launcher_id, member_id=mgr.bot.qq).get() - result = asyncio.run(result) - if result.mute_time_remaining > 0: - logging.info("机器人被禁言,跳过消息处理(group_{},剩余{}s)".format(launcher_id, - result.mute_time_remaining)) + if mgr.adapter.is_muted(launcher_id): + logging.info("机器人被禁言,跳过消息处理(group_{})".format(launcher_id)) return reply import config diff --git a/pkg/qqbot/sources/yirimirai.py b/pkg/qqbot/sources/yirimirai.py index 111e8f8e..0026ba94 100644 --- a/pkg/qqbot/sources/yirimirai.py +++ b/pkg/qqbot/sources/yirimirai.py @@ -60,7 +60,8 @@ class YiriMiraiAdapter(MessageSourceAdapter): def reply_message( self, message_source: mirai.MessageEvent, - message: mirai.MessageChain + message: mirai.MessageChain, + quote_origin: bool = False ): """回复消息 @@ -68,7 +69,14 @@ class YiriMiraiAdapter(MessageSourceAdapter): message_source (mirai.MessageEvent): YiriMirai消息源事件 message (mirai.MessageChain): YiriMirai库的消息链 """ - asyncio.run(self.bot.send(message_source, message)) + asyncio.run(self.bot.send(message_source, message, quote_origin)) + + def is_muted(self, group_id: int) -> bool: + result = self.bot.member_info(target=group_id, member_id=self.bot.qq).get() + result = asyncio.run(result) + if result.mute_time_remaining > 0: + return True + return False def register_listener( self, From bc899e5bd05d627b370ede9ad6b15762031066f8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 21 Apr 2023 09:52:31 +0000 Subject: [PATCH 5/5] Update override-all.json --- override-all.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/override-all.json b/override-all.json index 2950b416..c3f6f1c8 100644 --- a/override-all.json +++ b/override-all.json @@ -1,5 +1,6 @@ { "comment": "这是override.json支持的字段全集, 关于override.json机制, 请查看https://github.com/RockChinQ/QChatGPT/pull/271", + "msg_source_adapter": "yirimirai", "mirai_http_api_config": { "adapter": "WebSocketAdapter", "host": "localhost", @@ -7,6 +8,7 @@ "verifyKey": "yirimirai", "qq": 1234567890 }, + "nonebot2_config": {}, "openai_config": { "api_key": { "default": "openai_api_key"