feat: 适配nakuru基本功能

This commit is contained in:
Rock Chin
2023-04-23 23:40:08 +08:00
parent 612033f478
commit 5b96ac122f
4 changed files with 189 additions and 28 deletions
+2
View File
@@ -21,3 +21,5 @@ res/announcement_saved.json
cmdpriv.json cmdpriv.json
tips.py tips.py
.venv .venv
bin/
.vscode
+7 -3
View File
@@ -243,6 +243,8 @@ def start(first_time_init=False):
"mirai-api-http端口无法使用:{}, 解决方案: https://github.com/RockChinQ/QChatGPT/issues/22".format( "mirai-api-http端口无法使用:{}, 解决方案: https://github.com/RockChinQ/QChatGPT/issues/22".format(
e)) e))
else: else:
import traceback
traceback.print_exc()
logging.error( logging.error(
"捕捉到未知异常:{}, 请前往 https://github.com/RockChinQ/QChatGPT/issues 查找或提issue".format(e)) "捕捉到未知异常:{}, 请前往 https://github.com/RockChinQ/QChatGPT/issues 查找或提issue".format(e))
known_exception_caught = True known_exception_caught = True
@@ -262,9 +264,11 @@ def start(first_time_init=False):
if first_time_init: if first_time_init:
if not known_exception_caught: if not known_exception_caught:
logging.info("QQ: {}, MAH: {}".format(config.mirai_http_api_config['qq'], config.mirai_http_api_config['host']+":"+str(config.mirai_http_api_config['port']))) import config
logging.info('程序启动完成,如长时间未显示 ”成功登录到账号xxxxx“ ,并且不回复消息,请查看 ' if config.msg_source_adapter == "yirimirai":
'https://github.com/RockChinQ/QChatGPT/issues/37') logging.info("QQ: {}, MAH: {}".format(config.mirai_http_api_config['qq'], config.mirai_http_api_config['host']+":"+str(config.mirai_http_api_config['port'])))
logging.info('程序启动完成,如长时间未显示 ”成功登录到账号xxxxx“ ,并且不回复消息,请查看 '
'https://github.com/RockChinQ/QChatGPT/issues/37')
else: else:
sys.exit(1) sys.exit(1)
else: else:
+21 -7
View File
@@ -80,7 +80,6 @@ class QQBotManager:
def __init__(self, first_time_init=True): def __init__(self, first_time_init=True):
import config import config
mirai_http_api_config = config.mirai_http_api_config
self.timeout = config.process_message_timeout self.timeout = config.process_message_timeout
self.retry = config.retry_times self.retry = config.retry_times
@@ -88,12 +87,26 @@ class QQBotManager:
# 故只在第一次初始化时创建bot对象,重载之后使用原bot对象 # 故只在第一次初始化时创建bot对象,重载之后使用原bot对象
# 因此,bot的配置不支持热重载 # 因此,bot的配置不支持热重载
if first_time_init: if first_time_init:
logging.info("Use adapter:" + config.msg_source_adapter)
if config.msg_source_adapter == 'yirimirai': if config.msg_source_adapter == 'yirimirai':
from pkg.qqbot.sources.yirimirai import YiriMiraiAdapter from pkg.qqbot.sources.yirimirai import YiriMiraiAdapter
mirai_http_api_config = config.mirai_http_api_config
self.bot_account_id = config.mirai_http_api_config['qq'] self.bot_account_id = config.mirai_http_api_config['qq']
self.adapter = YiriMiraiAdapter(mirai_http_api_config) self.adapter = YiriMiraiAdapter(mirai_http_api_config)
elif config.msg_source_adapter == 'nakuru': elif config.msg_source_adapter == 'nakuru':
pass from pkg.qqbot.sources.nakuru import NakuruProjectAdapter
self.adapter = NakuruProjectAdapter(config.nakuru_config)
# nakuru库有bug,这个接口没法带access_token,会失败
# 所以目前自行发请求
import requests
resp = requests.get(
url="http://{}:{}/get_login_info".format(config.nakuru_config['host'], config.nakuru_config['http_port']),
headers={
'Authorization': "Bearer " + config.nakuru_config['token'] if 'token' in config.nakuru_config else ""
}
)
self.bot_account_id = int(resp.json()['data']['user_id'])
else: else:
self.adapter = pkg.utils.context.get_qqbot_manager().adapter self.adapter = pkg.utils.context.get_qqbot_manager().adapter
@@ -146,10 +159,12 @@ class QQBotManager:
pkg.utils.context.get_thread_ctl().submit_user_task( pkg.utils.context.get_thread_ctl().submit_user_task(
stranger_message_handler, stranger_message_handler,
) )
self.adapter.register_listener( # nakuru不区分好友和陌生人,故仅为yirimirai注册陌生人事件
StrangerMessage, if config.msg_source_adapter == 'yirimirai':
on_stranger_message self.adapter.register_listener(
) StrangerMessage,
on_stranger_message
)
def on_group_message(event: GroupMessage): def on_group_message(event: GroupMessage):
@@ -272,7 +287,6 @@ class QQBotManager:
def on_group_message(self, event: GroupMessage): def on_group_message(self, event: GroupMessage):
import config import config
reply = '' reply = ''
def process(text=None) -> str: def process(text=None) -> str:
replys = "" replys = ""
if At(self.bot_account_id) in event.message_chain: if At(self.bot_account_id) in event.message_chain:
+158 -17
View File
@@ -2,48 +2,141 @@ import mirai
from ..adapter import MessageSourceAdapter, MessageConverter, EventConverter from ..adapter import MessageSourceAdapter, MessageConverter, EventConverter
import nakuru import nakuru
import nakuru.entities.components as nkc
import asyncio import asyncio
import typing import typing
import traceback
import logging
import json
class NakuruProjectMessageConverter(MessageConverter): class NakuruProjectMessageConverter(MessageConverter):
@staticmethod @staticmethod
def yiri2target(message_chain: mirai.MessageChain) -> list: def yiri2target(message_chain: mirai.MessageChain) -> list:
pass msg_list = []
if type(message_chain) is mirai.MessageChain:
msg_list = message_chain.__root__
elif type(message_chain) is list:
msg_list = message_chain
else:
raise Exception("Unknown message type: " + str(message_chain) + type(message_chain))
nakuru_msg_list = []
# 遍历并转换
for component in msg_list:
if type(component) is mirai.Plain:
nakuru_msg_list.append(nkc.Plain(component.text))
elif type(component) is mirai.Image:
if component.url is not None:
nakuru_msg_list.append(nkc.Image.fromURL(component.url))
elif component.base64 is not None:
nakuru_msg_list.append(nkc.Image.fromBase64(component.base64))
elif component.path is not None:
nakuru_msg_list.append(nkc.Image.fromFileSystem(component.path))
elif type(component) is mirai.Face:
nakuru_msg_list.append(nkc.Face(id=component.face_id))
elif type(component) is mirai.At:
nakuru_msg_list.append(nkc.At(qq=component.target))
elif type(component) is mirai.AtAll:
nakuru_msg_list.append(nkc.AtAll())
elif type(component) is mirai.Voice:
pass
else:
pass
return nakuru_msg_list
@staticmethod @staticmethod
def target2yiri(message_chain: typing.Any) -> mirai.MessageChain: def target2yiri(message_chain: typing.Any) -> mirai.MessageChain:
pass assert type(message_chain) is list
yiri_msg_list = []
for component in message_chain:
if type(component) is nkc.Plain:
yiri_msg_list.append(mirai.Plain(text=component.text))
elif type(component) is nkc.Image:
yiri_msg_list.append(mirai.Image(url=component.url))
elif type(component) is nkc.Face:
yiri_msg_list.append(mirai.Face(face_id=component.id))
elif type(component) is nkc.At:
yiri_msg_list.append(mirai.At(target=component.qq))
elif type(component) is nkc.AtAll:
yiri_msg_list.append(mirai.AtAll())
else:
pass
logging.debug("转换后的消息链: " + str(yiri_msg_list))
return mirai.MessageChain(yiri_msg_list)
class NakuruProjectEventConverter(EventConverter): class NakuruProjectEventConverter(EventConverter):
@staticmethod @staticmethod
def yiri2target(event: typing.Type[mirai.Event]): def yiri2target(event: typing.Type[mirai.Event]):
if event is mirai.GroupMessage: if event is mirai.GroupMessage:
return "GroupMessage" return nakuru.GroupMessage
elif event is mirai.FriendMessage: elif event is mirai.FriendMessage:
return "FriendMessage" return nakuru.FriendMessage
elif event is mirai.StrangerMessage:
return "FriendMessage"
else: else:
raise Exception("Unknown event type: " + str(event)) raise Exception("未支持转换的事件类型: " + str(event))
@staticmethod @staticmethod
def target2yiri(event: typing.Any) -> mirai.Event: def target2yiri(event: typing.Any) -> mirai.Event:
pass if type(event) is nakuru.FriendMessage:
return mirai.FriendMessage(
sender=mirai.models.entities.Friend(
id=event.sender.user_id,
nickname=event.sender.nickname,
remark=event.sender.nickname
),
message_chain=NakuruProjectMessageConverter.target2yiri(event.message),
time=event.time
)
elif type(event) is nakuru.GroupMessage:
permission = "MEMBER"
if event.sender.role == "admin":
permission = "ADMINISTRATOR"
elif event.sender.role == "owner":
permission = "OWNER"
import mirai.models.entities as entities
return mirai.GroupMessage(
sender=mirai.models.entities.GroupMember(
id=event.sender.user_id,
member_name=event.sender.nickname,
permission=permission,
group=mirai.models.entities.Group(
id=event.group_id,
name=event.sender.nickname,
permission=entities.Permission.Member
),
special_title=event.sender.title,
join_timestamp=0,
last_speak_timestamp=0,
mute_time_remaining=0,
),
message_chain=NakuruProjectMessageConverter.target2yiri(event.message),
time=event.time
)
else:
raise Exception("未支持转换的事件类型: " + str(event))
class NakuruProjectAdapter(MessageSourceAdapter): class NakuruProjectAdapter(MessageSourceAdapter):
"""nakuru-project适配器""" """nakuru-project适配器"""
bot: nakuru.CQHTTP bot: nakuru.CQHTTP
message_converter: NakuruProjectMessageConverter message_converter: NakuruProjectMessageConverter = NakuruProjectMessageConverter()
event_converter: NakuruProjectEventConverter event_converter: NakuruProjectEventConverter = NakuruProjectEventConverter()
listener_list: list[dict]
def __init__(self, config: dict): def __init__(self, config: dict):
"""初始化nakuru-project的对象""" """初始化nakuru-project的对象"""
self.bot = nakuru.CQHTTP(**config) self.bot = nakuru.CQHTTP(**config)
self.listener_list = []
def send_message( def send_message(
self, self,
@@ -67,29 +160,77 @@ class NakuruProjectAdapter(MessageSourceAdapter):
message: mirai.MessageChain, message: mirai.MessageChain,
quote_origin: bool = False quote_origin: bool = False
): ):
pass if type(message_source) is mirai.GroupMessage:
task = self.bot.sendGroupMessage(
message_source.sender.group.id,
self.message_converter.yiri2target(message),
# quote=message_source.message_id if quote_origin else None
)
elif type(message_source) is mirai.FriendMessage:
task = self.bot.sendFriendMessage(
message_source.sender.id,
self.message_converter.yiri2target(message),
# quote=message_source.message_id if quote_origin else None
)
else:
raise Exception("Unknown message source type: " + str(type(message_source)))
asyncio.run(task)
def is_muted(self, group_id: int) -> bool: def is_muted(self, group_id: int) -> bool:
pass return False
def register_listener( def register_listener(
self, self,
event_type: typing.Type[mirai.Event], event_type: typing.Type[mirai.Event],
callback: typing.Callable[[mirai.Event], None] callback: typing.Callable[[mirai.Event], None]
): ):
def listener_wrapper(app: nakuru.CQHTTP, source: nakuru.GroupMessage): try:
callback(self.event_converter.target2yiri(source)) logging.debug("注册监听器: " + str(event_type) + " -> " + str(callback))
async def listener_wrapper(app: nakuru.CQHTTP, source: self.event_converter.yiri2target(event_type)):
self.bot.receiver(self.event_converter.yiri2target(event_type))(listener_wrapper) callback(self.event_converter.target2yiri(source))
self.listener_list.append(
{
"event_type": event_type,
"callable": callback,
"wrapper": listener_wrapper,
}
)
self.bot.receiver(self.event_converter.yiri2target(event_type).__name__)(listener_wrapper)
logging.debug("注册完成")
except Exception as e:
traceback.print_exc()
raise e
def unregister_listener( def unregister_listener(
self, self,
event_type: typing.Type[mirai.Event], event_type: typing.Type[mirai.Event],
callback: typing.Callable[[mirai.Event], None] callback: typing.Callable[[mirai.Event], None]
): ):
pass nakuru_event_name = self.event_converter.yiri2target(event_type)
new_event_list = []
# 从本对象的监听器列表中查找并删除
target_wrapper = None
for listener in self.listener_list:
if listener["event_type"] == event_type and listener["callable"] == callback:
target_wrapper = listener["wrapper"]
self.listener_list.remove(listener)
break
if target_wrapper is None:
raise Exception("未找到对应的监听器")
for func in self.bot.event[nakuru_event_name]:
if func.callable != target_wrapper:
new_event_list.append(func)
self.bot.event[nakuru_event_name] = new_event_list
def run_sync(self): def run_sync(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
self.bot.run() self.bot.run()
def kill(self) -> bool: def kill(self) -> bool: