mirror of
https://github.com/RockChinQ/QChatGPT.git
synced 2024-11-16 03:32:33 +08:00
Merge pull request #678 from RockChinQ/feat/aiocqhttp
Feat: 适配aiocqhttp
This commit is contained in:
commit
71b54fd684
|
@ -63,19 +63,24 @@ class Application:
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# async def wait_loop(self):
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
await self.plugin_mgr.load_plugins()
|
await self.plugin_mgr.load_plugins()
|
||||||
await self.plugin_mgr.initialize_plugins()
|
await self.plugin_mgr.initialize_plugins()
|
||||||
|
|
||||||
|
tasks = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tasks = [
|
tasks = [
|
||||||
asyncio.create_task(self.im_mgr.run()),
|
asyncio.create_task(self.im_mgr.run()),
|
||||||
asyncio.create_task(self.ctrl.run())
|
asyncio.create_task(self.ctrl.run())
|
||||||
]
|
]
|
||||||
await asyncio.wait(tasks)
|
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
self.logger.info("程序退出.")
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"应用运行致命异常: {e}")
|
self.logger.error(f"应用运行致命异常: {e}")
|
||||||
self.logger.debug(f"Traceback: {traceback.format_exc()}")
|
self.logger.debug(f"Traceback: {traceback.format_exc()}")
|
||||||
|
self.logger.info("程序退出.")
|
||||||
|
|
|
@ -44,6 +44,7 @@ async def init_logging() -> logging.Logger:
|
||||||
handler.setFormatter(color_formatter)
|
handler.setFormatter(color_formatter)
|
||||||
qcg_logger.addHandler(handler)
|
qcg_logger.addHandler(handler)
|
||||||
|
|
||||||
|
qcg_logger.debug("日志初始化完成,日志级别:%s" % level)
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO, # 设置日志输出格式
|
level=logging.INFO, # 设置日志输出格式
|
||||||
format="[DEPR][%(asctime)s.%(msecs)03d] %(pathname)s (%(lineno)d) - [%(levelname)s] :\n%(message)s",
|
format="[DEPR][%(asctime)s.%(msecs)03d] %(pathname)s (%(lineno)d) - [%(levelname)s] :\n%(message)s",
|
||||||
|
|
|
@ -74,7 +74,7 @@ class MessageSourceAdapter(metaclass=abc.ABCMeta):
|
||||||
"""异步运行"""
|
"""异步运行"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def kill(self) -> bool:
|
async def kill(self) -> bool:
|
||||||
"""关闭适配器
|
"""关闭适配器
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
@ -37,6 +37,11 @@ class PlatformManager:
|
||||||
mirai_http_api_config = self.ap.platform_cfg.data['yiri-mirai-config']
|
mirai_http_api_config = self.ap.platform_cfg.data['yiri-mirai-config']
|
||||||
self.bot_account_id = mirai_http_api_config['qq']
|
self.bot_account_id = mirai_http_api_config['qq']
|
||||||
self.adapter = YiriMiraiAdapter(mirai_http_api_config)
|
self.adapter = YiriMiraiAdapter(mirai_http_api_config)
|
||||||
|
elif self.ap.platform_cfg.data['platform-adapter'] == 'aiocqhttp':
|
||||||
|
from pkg.platform.sources.aiocqhttp import AiocqhttpAdapter
|
||||||
|
|
||||||
|
aiocqhttp_config = self.ap.platform_cfg.data['aiocqhttp-config']
|
||||||
|
self.adapter = AiocqhttpAdapter(aiocqhttp_config, self.ap)
|
||||||
# elif config['msg_source_adapter'] == 'nakuru':
|
# elif config['msg_source_adapter'] == 'nakuru':
|
||||||
# from pkg.platform.sources.nakuru import NakuruProjectAdapter
|
# from pkg.platform.sources.nakuru import NakuruProjectAdapter
|
||||||
# self.adapter = NakuruProjectAdapter(config['nakuru_config'])
|
# self.adapter = NakuruProjectAdapter(config['nakuru_config'])
|
||||||
|
|
267
pkg/platform/sources/aiocqhttp.py
Normal file
267
pkg/platform/sources/aiocqhttp.py
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import typing
|
||||||
|
import asyncio
|
||||||
|
import traceback
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import mirai
|
||||||
|
import mirai.models.message as yiri_message
|
||||||
|
import aiocqhttp
|
||||||
|
|
||||||
|
from .. import adapter
|
||||||
|
from ...pipeline.longtext.strategies import forward
|
||||||
|
from ...core import app
|
||||||
|
|
||||||
|
|
||||||
|
class AiocqhttpMessageConverter(adapter.MessageConverter):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def yiri2target(message_chain: mirai.MessageChain) -> typing.Tuple[list, int, datetime.datetime]:
|
||||||
|
msg_list = aiocqhttp.Message()
|
||||||
|
|
||||||
|
msg_id = 0
|
||||||
|
msg_time = None
|
||||||
|
|
||||||
|
for msg in message_chain:
|
||||||
|
if type(msg) is mirai.Plain:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.text(msg.text))
|
||||||
|
elif type(msg) is yiri_message.Source:
|
||||||
|
msg_id = msg.id
|
||||||
|
msg_time = msg.time
|
||||||
|
elif type(msg) is mirai.Image:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.image(msg.path))
|
||||||
|
elif type(msg) is mirai.At:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.at(msg.target))
|
||||||
|
elif type(msg) is mirai.AtAll:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.at("all"))
|
||||||
|
elif type(msg) is mirai.Face:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.face(msg.face_id))
|
||||||
|
elif type(msg) is mirai.Voice:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.record(msg.path))
|
||||||
|
elif type(msg) is forward.Forward:
|
||||||
|
print("aiocqhttp 暂不支持转发消息组件的转换,使用普通消息链发送")
|
||||||
|
|
||||||
|
for node in msg.node_list:
|
||||||
|
msg_list.extend(AiocqhttpMessageConverter.yiri2target(node.message_chain)[0])
|
||||||
|
|
||||||
|
else:
|
||||||
|
msg_list.append(aiocqhttp.MessageSegment.text(str(msg)))
|
||||||
|
|
||||||
|
return msg_list, msg_id, msg_time
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def target2yiri(message: str, message_id: int = -1):
|
||||||
|
message = aiocqhttp.Message(message)
|
||||||
|
|
||||||
|
yiri_msg_list = []
|
||||||
|
|
||||||
|
yiri_msg_list.append(
|
||||||
|
yiri_message.Source(id=message_id, time=datetime.datetime.now())
|
||||||
|
)
|
||||||
|
|
||||||
|
for msg in message:
|
||||||
|
if msg.type == "at":
|
||||||
|
if msg.data["qq"] == "all":
|
||||||
|
yiri_msg_list.append(yiri_message.AtAll())
|
||||||
|
else:
|
||||||
|
yiri_msg_list.append(
|
||||||
|
yiri_message.At(
|
||||||
|
target=msg.data["qq"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif msg.type == "text":
|
||||||
|
yiri_msg_list.append(yiri_message.Plain(text=msg.data["text"]))
|
||||||
|
elif msg.type == "image":
|
||||||
|
yiri_msg_list.append(yiri_message.Image(url=msg.data["url"]))
|
||||||
|
|
||||||
|
chain = mirai.MessageChain(yiri_msg_list)
|
||||||
|
|
||||||
|
return chain
|
||||||
|
|
||||||
|
|
||||||
|
class AiocqhttpEventConverter(adapter.EventConverter):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def yiri2target(event: mirai.Event, bot_account_id: int):
|
||||||
|
|
||||||
|
msg, msg_id, msg_time = AiocqhttpMessageConverter.yiri2target(event.message_chain)
|
||||||
|
|
||||||
|
if type(event) is mirai.GroupMessage:
|
||||||
|
role = "member"
|
||||||
|
|
||||||
|
if event.sender.permission == "ADMINISTRATOR":
|
||||||
|
role = "admin"
|
||||||
|
elif event.sender.permission == "OWNER":
|
||||||
|
role = "owner"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"post_type": "message",
|
||||||
|
"message_type": "group",
|
||||||
|
"time": int(msg_time.timestamp()),
|
||||||
|
"self_id": bot_account_id,
|
||||||
|
"sub_type": "normal",
|
||||||
|
"anonymous": None,
|
||||||
|
"font": 0,
|
||||||
|
"message": str(msg),
|
||||||
|
"raw_message": str(msg),
|
||||||
|
"sender": {
|
||||||
|
"age": 0,
|
||||||
|
"area": "",
|
||||||
|
"card": "",
|
||||||
|
"level": "",
|
||||||
|
"nickname": event.sender.member_name,
|
||||||
|
"role": role,
|
||||||
|
"sex": "unknown",
|
||||||
|
"title": "",
|
||||||
|
"user_id": event.sender.id,
|
||||||
|
},
|
||||||
|
"user_id": event.sender.id,
|
||||||
|
"message_id": msg_id,
|
||||||
|
"group_id": event.group.id,
|
||||||
|
"message_seq": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return aiocqhttp.Event.from_payload(payload)
|
||||||
|
elif type(event) is mirai.FriendMessage:
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"post_type": "message",
|
||||||
|
"message_type": "private",
|
||||||
|
"time": int(msg_time.timestamp()),
|
||||||
|
"self_id": bot_account_id,
|
||||||
|
"sub_type": "friend",
|
||||||
|
"target_id": bot_account_id,
|
||||||
|
"message": str(msg),
|
||||||
|
"raw_message": str(msg),
|
||||||
|
"font": 0,
|
||||||
|
"sender": {
|
||||||
|
"age": 0,
|
||||||
|
"nickname": event.sender.nickname,
|
||||||
|
"sex": "unknown",
|
||||||
|
"user_id": event.sender.id,
|
||||||
|
},
|
||||||
|
"message_id": msg_id,
|
||||||
|
"user_id": event.sender.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
return aiocqhttp.Event.from_payload(payload)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def target2yiri(event: aiocqhttp.Event):
|
||||||
|
yiri_chain = AiocqhttpMessageConverter.target2yiri(
|
||||||
|
event.message, event.message_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if event.message_type == "group":
|
||||||
|
permission = "MEMBER"
|
||||||
|
|
||||||
|
if event.sender["role"] == "admin":
|
||||||
|
permission = "ADMINISTRATOR"
|
||||||
|
elif event.sender["role"] == "owner":
|
||||||
|
permission = "OWNER"
|
||||||
|
converted_event = mirai.GroupMessage(
|
||||||
|
sender=mirai.models.entities.GroupMember(
|
||||||
|
id=event.sender["user_id"], # message_seq 放哪?
|
||||||
|
member_name=event.sender["nickname"],
|
||||||
|
permission=permission,
|
||||||
|
group=mirai.models.entities.Group(
|
||||||
|
id=event.group_id,
|
||||||
|
name=event.sender["nickname"],
|
||||||
|
permission=mirai.models.entities.Permission.Member,
|
||||||
|
),
|
||||||
|
special_title=event.sender["title"],
|
||||||
|
join_timestamp=0,
|
||||||
|
last_speak_timestamp=0,
|
||||||
|
mute_time_remaining=0,
|
||||||
|
),
|
||||||
|
message_chain=yiri_chain,
|
||||||
|
time=event.time,
|
||||||
|
)
|
||||||
|
return converted_event
|
||||||
|
elif event.message_type == "private":
|
||||||
|
return mirai.FriendMessage(
|
||||||
|
sender=mirai.models.entities.Friend(
|
||||||
|
id=event.sender["user_id"],
|
||||||
|
nickname=event.sender["nickname"],
|
||||||
|
remark="",
|
||||||
|
),
|
||||||
|
message_chain=yiri_chain,
|
||||||
|
time=event.time,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AiocqhttpAdapter(adapter.MessageSourceAdapter):
|
||||||
|
|
||||||
|
bot: aiocqhttp.CQHttp
|
||||||
|
|
||||||
|
bot_account_id: int
|
||||||
|
|
||||||
|
message_converter: AiocqhttpMessageConverter = AiocqhttpMessageConverter()
|
||||||
|
event_converter: AiocqhttpEventConverter = AiocqhttpEventConverter()
|
||||||
|
|
||||||
|
config: dict
|
||||||
|
|
||||||
|
ap: app.Application
|
||||||
|
|
||||||
|
def __init__(self, config: dict, ap: app.Application):
|
||||||
|
self.config = config
|
||||||
|
self.ap = ap
|
||||||
|
|
||||||
|
self.bot = aiocqhttp.CQHttp()
|
||||||
|
|
||||||
|
async def send_message(
|
||||||
|
self, target_type: str, target_id: str, message: mirai.MessageChain
|
||||||
|
):
|
||||||
|
return super().send_message(target_type, target_id, message)
|
||||||
|
|
||||||
|
async def reply_message(
|
||||||
|
self,
|
||||||
|
message_source: mirai.MessageEvent,
|
||||||
|
message: mirai.MessageChain,
|
||||||
|
quote_origin: bool = False,
|
||||||
|
):
|
||||||
|
|
||||||
|
aiocq_event = AiocqhttpEventConverter.yiri2target(message_source, self.bot_account_id)
|
||||||
|
aiocq_msg = AiocqhttpMessageConverter.yiri2target(message)[0]
|
||||||
|
if quote_origin:
|
||||||
|
aiocq_msg = aiocqhttp.MessageSegment.reply(aiocq_event.message_id) + aiocq_msg
|
||||||
|
|
||||||
|
return await self.bot.send(
|
||||||
|
aiocq_event,
|
||||||
|
aiocq_msg
|
||||||
|
)
|
||||||
|
|
||||||
|
async def is_muted(self, group_id: int) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def register_listener(
|
||||||
|
self,
|
||||||
|
event_type: typing.Type[mirai.Event],
|
||||||
|
callback: typing.Callable[[mirai.Event], None],
|
||||||
|
):
|
||||||
|
async def on_message(event: aiocqhttp.Event):
|
||||||
|
self.bot_account_id = event.self_id
|
||||||
|
self.ap.im_mgr.bot_account_id = event.self_id
|
||||||
|
try:
|
||||||
|
return await callback(self.event_converter.target2yiri(event))
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
if event_type == mirai.GroupMessage:
|
||||||
|
self.bot.on_message("group")(on_message)
|
||||||
|
elif event_type == mirai.FriendMessage:
|
||||||
|
self.bot.on_message("private")(on_message)
|
||||||
|
|
||||||
|
def unregister_listener(
|
||||||
|
self,
|
||||||
|
event_type: typing.Type[mirai.Event],
|
||||||
|
callback: typing.Callable[[mirai.Event], None],
|
||||||
|
):
|
||||||
|
return super().unregister_listener(event_type, callback)
|
||||||
|
|
||||||
|
async def run_async(self):
|
||||||
|
await self.bot._server_app.run_task(**self.config)
|
||||||
|
|
||||||
|
async def kill(self) -> bool:
|
||||||
|
return False
|
|
@ -9,8 +9,7 @@ import nakuru
|
||||||
import nakuru.entities.components as nkc
|
import nakuru.entities.components as nkc
|
||||||
|
|
||||||
from .. import adapter as adapter_model
|
from .. import adapter as adapter_model
|
||||||
from ...platform import blob
|
from ...pipeline.longtext.strategies import forward
|
||||||
from ...utils import context
|
|
||||||
|
|
||||||
|
|
||||||
class NakuruProjectMessageConverter(adapter_model.MessageConverter):
|
class NakuruProjectMessageConverter(adapter_model.MessageConverter):
|
||||||
|
@ -49,7 +48,7 @@ class NakuruProjectMessageConverter(adapter_model.MessageConverter):
|
||||||
nakuru_msg_list.append(nkc.Record.fromURL(component.url))
|
nakuru_msg_list.append(nkc.Record.fromURL(component.url))
|
||||||
elif component.path is not None:
|
elif component.path is not None:
|
||||||
nakuru_msg_list.append(nkc.Record.fromFileSystem(component.path))
|
nakuru_msg_list.append(nkc.Record.fromFileSystem(component.path))
|
||||||
elif type(component) is blob.Forward:
|
elif type(component) is forward.Forward:
|
||||||
# 转发消息
|
# 转发消息
|
||||||
yiri_forward_node_list = component.node_list
|
yiri_forward_node_list = component.node_list
|
||||||
nakuru_forward_node_list = []
|
nakuru_forward_node_list = []
|
||||||
|
@ -324,8 +323,5 @@ class NakuruProjectAdapter(adapter_model.MessageSourceAdapter):
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
self.bot.run()
|
self.bot.run()
|
||||||
|
|
||||||
async def run_async(self):
|
|
||||||
return await self.bot._run()
|
|
||||||
|
|
||||||
def kill(self) -> bool:
|
def kill(self) -> bool:
|
||||||
return False
|
return False
|
|
@ -113,5 +113,5 @@ class YiriMiraiAdapter(adapter_model.MessageSourceAdapter):
|
||||||
async def run_async(self):
|
async def run_async(self):
|
||||||
return await MiraiRunner(self.bot)._run()
|
return await MiraiRunner(self.bot)._run()
|
||||||
|
|
||||||
def kill(self) -> bool:
|
async def kill(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -3,11 +3,11 @@ openai
|
||||||
dulwich~=0.21.6
|
dulwich~=0.21.6
|
||||||
colorlog~=6.6.0
|
colorlog~=6.6.0
|
||||||
yiri-mirai-rc
|
yiri-mirai-rc
|
||||||
|
aiocqhttp
|
||||||
websockets
|
websockets
|
||||||
urllib3
|
urllib3
|
||||||
func_timeout~=4.3.5
|
func_timeout~=4.3.5
|
||||||
Pillow
|
Pillow
|
||||||
nakuru-project-idk
|
|
||||||
CallingGPT
|
CallingGPT
|
||||||
tiktoken
|
tiktoken
|
||||||
PyYaml
|
PyYaml
|
||||||
|
|
|
@ -2,11 +2,15 @@
|
||||||
"platform-adapter": "yiri-mirai",
|
"platform-adapter": "yiri-mirai",
|
||||||
"yiri-mirai-config": {
|
"yiri-mirai-config": {
|
||||||
"adapter": "WebSocketAdapter",
|
"adapter": "WebSocketAdapter",
|
||||||
"host": "localhost",
|
"host": "127.0.0.1",
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
"verifyKey": "yirimirai",
|
"verifyKey": "yirimirai",
|
||||||
"qq": 123456789
|
"qq": 123456789
|
||||||
},
|
},
|
||||||
|
"aiocqhttp-config": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8080
|
||||||
|
},
|
||||||
"track-function-calls": true,
|
"track-function-calls": true,
|
||||||
"quote-origin": false,
|
"quote-origin": false,
|
||||||
"at-sender": false,
|
"at-sender": false,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user