Compare commits

...

3 Commits

Author SHA1 Message Date
Soulter af62d969d7 perf: 更改 send_msg 接口 2024-07-27 11:26:02 -04:00
Soulter c4fd9a66c6 update version to 3.3.3 2024-07-27 11:08:51 -04:00
Soulter d191997a39 feat: aiocqhttp 适配器适配主动发送消息接口 2024-07-27 11:07:26 -04:00
6 changed files with 59 additions and 27 deletions
+2 -1
View File
@@ -2,6 +2,7 @@ import abc
from typing import Union, Any, List from typing import Union, Any, List
from nakuru.entities.components import Plain, At, Image, BaseMessageComponent from nakuru.entities.components import Plain, At, Image, BaseMessageComponent
from type.astrbot_message import AstrBotMessage from type.astrbot_message import AstrBotMessage
from type.command import CommandResult
class Platform(): class Platform():
@@ -24,7 +25,7 @@ class Platform():
pass pass
@abc.abstractmethod @abc.abstractmethod
async def send_msg(self, target: Any, result_message: Union[List[BaseMessageComponent], str]): async def send_msg(self, target: Any, result_message: CommandResult):
''' '''
发送消息(主动) 发送消息(主动)
''' '''
+25 -3
View File
@@ -7,6 +7,7 @@ from aiocqhttp.exceptions import ActionFailed
from . import Platform from . import Platform
from type.astrbot_message import * from type.astrbot_message import *
from type.message_event import * from type.message_event import *
from type.command import *
from typing import Union, List, Dict from typing import Union, List, Dict
from nakuru.entities.components import * from nakuru.entities.components import *
from SparkleLogging.utils.core import LogManager from SparkleLogging.utils.core import LogManager
@@ -165,7 +166,7 @@ class AIOCQHTTP(Platform):
await self._reply(message, res) await self._reply(message, res)
async def _reply(self, message: AstrBotMessage, message_chain: List[BaseMessageComponent]): async def _reply(self, message: Union[AstrBotMessage, Dict], message_chain: List[BaseMessageComponent]):
if isinstance(message_chain, str): if isinstance(message_chain, str):
message_chain = [Plain(text=message_chain), ] message_chain = [Plain(text=message_chain), ]
@@ -179,7 +180,15 @@ class AIOCQHTTP(Platform):
image_idx.append(idx) image_idx.append(idx)
ret.append(d) ret.append(d)
try: try:
await self.bot.send(message.raw_message, ret) if isinstance(message, AstrBotMessage):
await self.bot.send(message.raw_message, ret)
if isinstance(message, dict):
if 'group_id' in message:
await self.bot.send_group_msg(group_id=message['group_id'], message=ret)
elif 'user_id' in message:
await self.bot.send_private_msg(user_id=message['user_id'], message=ret)
else:
raise Exception("aiocqhttp: 无法识别的消息来源。仅支持 group_id 和 user_id。")
except ActionFailed as e: except ActionFailed as e:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
logger.error(f"回复消息失败: {e}") logger.error(f"回复消息失败: {e}")
@@ -195,4 +204,17 @@ class AIOCQHTTP(Platform):
logger.info(f"上传成功。") logger.info(f"上传成功。")
ret[idx]['data']['file'] = image_url ret[idx]['data']['file'] = image_url
ret[idx]['data']['path'] = image_url ret[idx]['data']['path'] = image_url
await self.bot.send(message.raw_message, ret) await self.bot.send(message.raw_message, ret)
async def send_msg(self, target: Dict[str, int], result_message: CommandResult):
'''
以主动的方式给QQ用户、QQ群发送一条消息。
`target` 接收一个 dict 类型的值引用。
- 要发给 QQ 下的某个用户,请添加 key `user_id`,值为 int 类型的 qq 号;
- 要发给某个群聊,请添加 key `group_id`,值为 int 类型的 qq 群号;
'''
await self._reply(target, result_message.message_chain)
+3 -2
View File
@@ -14,6 +14,7 @@ from type.types import Context
from . import Platform from . import Platform
from type.astrbot_message import * from type.astrbot_message import *
from type.message_event import * from type.message_event import *
from type.command import *
from SparkleLogging.utils.core import LogManager from SparkleLogging.utils.core import LogManager
from logging import Logger from logging import Logger
from astrbot.message.handler import MessageHandler from astrbot.message.handler import MessageHandler
@@ -199,7 +200,7 @@ class QQGOCQ(Platform):
return return
await self.client.sendGroupMessage(group_id, message_chain) await self.client.sendGroupMessage(group_id, message_chain)
async def send_msg(self, target: Dict[str, int], result_message: Union[List[BaseMessageComponent], str]): async def send_msg(self, target: Dict[str, int], result_message: CommandResult):
''' '''
以主动的方式给用户、群或者频道发送一条消息。 以主动的方式给用户、群或者频道发送一条消息。
@@ -211,7 +212,7 @@ class QQGOCQ(Platform):
guild_id 不是频道号。 guild_id 不是频道号。
''' '''
await self._reply(target, result_message) await self._reply(target, result_message.message_chain)
def convert_message(self, message: Union[GroupMessage, FriendMessage, GuildMessage]) -> AstrBotMessage: def convert_message(self, message: Union[GroupMessage, FriendMessage, GuildMessage]) -> AstrBotMessage:
abm = AstrBotMessage() abm = AstrBotMessage()
+26 -19
View File
@@ -13,6 +13,7 @@ from util.io import save_temp_img, download_image_by_url
from . import Platform from . import Platform
from type.astrbot_message import * from type.astrbot_message import *
from type.message_event import * from type.message_event import *
from type.command import *
from typing import Union, List, Dict from typing import Union, List, Dict
from nakuru.entities.components import * from nakuru.entities.components import *
from SparkleLogging.utils.core import LogManager from SparkleLogging.utils.core import LogManager
@@ -46,7 +47,7 @@ class botClient(Client):
class QQOfficial(Platform): class QQOfficial(Platform):
def __init__(self, context: Context, message_handler: MessageHandler) -> None: def __init__(self, context: Context, message_handler: MessageHandler, test_mode = False) -> None:
super().__init__() super().__init__()
self.loop = asyncio.new_event_loop() self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop) asyncio.set_event_loop(self.loop)
@@ -80,6 +81,8 @@ class QQOfficial(Platform):
self.client.set_platform(self) self.client.set_platform(self)
self.test_mode = test_mode
async def _parse_to_qqofficial(self, message: List[BaseMessageComponent], is_group: bool = False): async def _parse_to_qqofficial(self, message: List[BaseMessageComponent], is_group: bool = False):
plain_text = "" plain_text = ""
image_path = None # only one img supported image_path = None # only one img supported
@@ -209,13 +212,15 @@ class QQOfficial(Platform):
if not message_result: if not message_result:
return return
await self.reply_msg(message, message_result.result_message) ret = await self.reply_msg(message, message_result.result_message)
if message_result.callback: if message_result.callback:
message_result.callback() message_result.callback()
# 如果是等待回复的消息 # 如果是等待回复的消息
if session_id in self.waiting and self.waiting[session_id] == '': if session_id in self.waiting and self.waiting[session_id] == '':
self.waiting[session_id] = message self.waiting[session_id] = message
return ret
async def reply_msg(self, async def reply_msg(self,
message: AstrBotMessage, message: AstrBotMessage,
@@ -269,14 +274,13 @@ class QQOfficial(Platform):
_data['message_reference'] = None _data['message_reference'] = None
try: try:
await self._reply(**_data) return await self._reply(**_data)
return
except BaseException as e: except BaseException as e:
logger.warn(traceback.format_exc()) logger.warn(traceback.format_exc())
logger.warn(f"以文本转图片的形式回复消息时发生错误: {e},将尝试默认方式。") logger.warn(f"以文本转图片的形式回复消息时发生错误: {e},将尝试默认方式。")
try: try:
await self._reply(**data) return await self._reply(**data)
except BaseException as e: except BaseException as e:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# 分割过长的消息 # 分割过长的消息
@@ -286,28 +290,27 @@ class QQOfficial(Platform):
split_res.append(plain_text[len(plain_text)//2:]) split_res.append(plain_text[len(plain_text)//2:])
for i in split_res: for i in split_res:
data['content'] = i data['content'] = i
await self._reply(**data) return await self._reply(**data)
else: else:
try: try:
# 防止被qq频道过滤消息 # 防止被qq频道过滤消息
plain_text = plain_text.replace(".", " . ") plain_text = plain_text.replace(".", " . ")
await self._reply(**data) return await self._reply(**data)
except BaseException as e: except BaseException as e:
try: try:
data['content'] = str.join(" ", plain_text) data['content'] = str.join(" ", plain_text)
await self._reply(**data) return await self._reply(**data)
except BaseException as e: except BaseException as e:
plain_text = re.sub( plain_text = re.sub(
r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE) r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE)
plain_text = plain_text.replace(".", "·") plain_text = plain_text.replace(".", "·")
data['content'] = plain_text data['content'] = plain_text
await self._reply(**data) return await self._reply(**data)
async def _reply(self, **kwargs): async def _reply(self, **kwargs):
if 'group_openid' in kwargs: if 'group_openid' in kwargs:
# QQ群组消息 # QQ群组消息
# qq群组消息需要自行上传,暂时不处理 if 'file_image' in kwargs and kwargs['file_image']:
if 'file_image' in kwargs:
file_image_path = kwargs['file_image'].replace("file:///", "") file_image_path = kwargs['file_image'].replace("file:///", "")
if file_image_path: if file_image_path:
@@ -322,24 +325,30 @@ class QQOfficial(Platform):
kwargs['media'] = media kwargs['media'] = media
logger.debug(f"发送群图片: {media}") logger.debug(f"发送群图片: {media}")
kwargs['msg_type'] = 7 # 富媒体 kwargs['msg_type'] = 7 # 富媒体
if self.test_mode:
return kwargs
await self.client.api.post_group_message(**kwargs) await self.client.api.post_group_message(**kwargs)
elif 'channel_id' in kwargs: elif 'channel_id' in kwargs:
# 频道消息 # 频道消息
if 'file_image' in kwargs: if 'file_image' in kwargs and kwargs['file_image']:
kwargs['file_image'] = kwargs['file_image'].replace("file:///", "") kwargs['file_image'] = kwargs['file_image'].replace("file:///", "")
# 频道消息发图只支持本地 # 频道消息发图只支持本地
if kwargs['file_image'].startswith("http"): if kwargs['file_image'].startswith("http"):
kwargs['file_image'] = await download_image_by_url(kwargs['file_image']) kwargs['file_image'] = await download_image_by_url(kwargs['file_image'])
if self.test_mode:
return kwargs
await self.client.api.post_message(**kwargs) await self.client.api.post_message(**kwargs)
else: else:
# 频道私聊消息 # 频道私聊消息
if 'file_image' in kwargs: if 'file_image' in kwargs and kwargs['file_image']:
kwargs['file_image'] = kwargs['file_image'].replace("file:///", "") kwargs['file_image'] = kwargs['file_image'].replace("file:///", "")
if kwargs['file_image'].startswith("http"): if kwargs['file_image'].startswith("http"):
kwargs['file_image'] = await download_image_by_url(kwargs['file_image']) kwargs['file_image'] = await download_image_by_url(kwargs['file_image'])
if self.test_mode:
return kwargs
await self.client.api.post_dms(**kwargs) await self.client.api.post_dms(**kwargs)
async def send_msg(self, target: Dict[str, str], result_message: Union[List[BaseMessageComponent], str]): async def send_msg(self, target: Dict[str, str], result_message: CommandResult):
''' '''
以主动的方式给用户、群或者频道发送一条消息。 以主动的方式给用户、群或者频道发送一条消息。
@@ -349,16 +358,14 @@ class QQOfficial(Platform):
- 如果目标是 频道消息,请添加 key `channel_id`。 - 如果目标是 频道消息,请添加 key `channel_id`。
- 如果目标是 频道私聊,请添加 key `guild_id`。 - 如果目标是 频道私聊,请添加 key `guild_id`。
''' '''
if isinstance(result_message, list): plain_text, image_path = await self._parse_to_qqofficial(result_message.message_chain)
plain_text, image_path = await self._parse_to_qqofficial(result_message)
else:
plain_text = result_message
payload = { payload = {
'content': plain_text, 'content': plain_text,
'file_image': image_path,
**target **target
} }
if image_path:
payload['file_image'] = image_path
await self._reply(**payload) await self._reply(**payload)
def wait_for_message(self, channel_id: int) -> AstrBotMessage: def wait_for_message(self, channel_id: int) -> AstrBotMessage:
+1 -1
View File
@@ -1 +1 @@
VERSION = '3.3.2' VERSION = '3.3.3'
+2 -1
View File
@@ -8,4 +8,5 @@ Platform类是消息平台的抽象类,定义了消息平台的基本接口。
from model.platform import Platform from model.platform import Platform
from model.platform.qq_nakuru import QQGOCQ from model.platform.qq_nakuru import QQGOCQ
from model.platform.qq_official import QQOfficial from model.platform.qq_official import QQOfficial
from model.platform.qq_aiocqhttp import AIOCQHTTP