From 44899527774cf350acacfeddac2bd950e97fd30c Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Thu, 13 Mar 2025 20:33:39 +0800 Subject: [PATCH 1/7] =?UTF-8?q?fix&feat:=E4=BF=AE=E5=A4=8D=E5=9B=9E?= =?UTF-8?q?=E5=BA=94=E6=89=80=E6=9C=89=E6=88=B3=E4=B8=80=E6=88=B3=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9B=E5=B0=9D=E8=AF=95=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=8D=E5=9B=9E=E7=AD=94=E5=B7=B2=E6=92=A4=E5=9B=9E=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88=E4=BD=86=E4=BC=BC?= =?UTF-8?q?=E4=B9=8E=E7=9B=AE=E5=89=8D=E4=B8=8D=E8=B5=B7=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/__init__.py | 9 +++++- src/plugins/chat/bot.py | 33 +++++++++++++++++++++ src/plugins/chat/message_sender.py | 46 ++++++++++++++++++++++-------- src/plugins/chat/storage.py | 24 ++++++++++++++++ 4 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 6dde80d24..6462d7e2f 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -20,7 +20,7 @@ from .chat_stream import chat_manager from ..memory_system.memory import hippocampus, memory_graph from .bot import ChatBot from .message_sender import message_manager, message_sender - +from .storage import MessageStorage # 创建LLM统计实例 llm_stats = LLMStatistics("llm_statistics.txt") @@ -148,3 +148,10 @@ async def generate_schedule_task(): await bot_schedule.initialize() if not bot_schedule.enable_output: bot_schedule.print_schedule() +async def remove_recalled_message(self) -> None: + """删除撤回消息""" + try: + self.storage = MessageStorage() + self.storage.remove_recalled_message(time.time()) + except Exception: + logger.exception("删除撤回消息失败") \ No newline at end of file diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 4cd5043b4..bd3cd3fd3 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -8,6 +8,9 @@ from nonebot.adapters.onebot.v11 import ( PrivateMessageEvent, NoticeEvent, PokeNotifyEvent, + GroupRecallNoticeEvent, + FriendRecallNoticeEvent, + ) from ..memory_system.memory import hippocampus @@ -114,6 +117,36 @@ class ChatBot: is_emoji=False, ) message_manager.add_message(bot_message) + + if isinstance(event, GroupRecallNoticeEvent) or isinstance(event, FriendRecallNoticeEvent): + user_info = UserInfo( + user_id=event.user_id, + user_nickname=get_user_nickname(event.user_id) or None, + user_cardname=get_user_cardname(event.user_id) or None, + platform="qq", + ) + + message_cq = MessageRecvCQ( + message_id=None, + user_info=user_info, + raw_message=str("[撤回了一条消息]"), + group_info=None, + reply_message=None, + platform="qq", + ) + message_json = message_cq.to_dict() + + group_info = GroupInfo(group_id=event.group_id, group_name=None, platform="qq") + + chat = await chat_manager.get_or_create_stream( + platform=user_info.platform, user_info=user_info, group_info=group_info + ) + + await self.storage.store_recalled_message(event.message_id, time.time(), chat) + message=MessageRecv(message_json) + message.update_chat_stream(chat) + await message.process() + async def handle_message(self, event: MessageEvent, bot: Bot) -> None: """处理收到的消息""" diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index 5b580f244..1ff081bd8 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -4,7 +4,7 @@ from typing import Dict, List, Optional, Union from loguru import logger from nonebot.adapters.onebot.v11 import Bot - +from ...common.database import db from .message_cq import MessageSendCQ from .message import MessageSending, MessageThinking, MessageRecv, MessageSet @@ -36,10 +36,7 @@ class Message_Sender: message_send = MessageSendCQ(data=message_json) # logger.debug(message_send.message_info,message_send.raw_message) message_preview = truncate_message(message.processed_plain_text) - if ( - message_send.message_info.group_info - and message_send.message_info.group_info.group_id - ): + if message_send.message_info.group_info and message_send.message_info.group_info.group_id: try: await self._current_bot.send_group_msg( group_id=message.message_info.group_info.group_id, @@ -74,6 +71,23 @@ class MessageContainer: self.last_send_time = 0 self.thinking_timeout = 20 # 思考超时时间(秒) + def get_recalled_messages(self) -> List[MessageSending]: + """获取所有撤回的Message_Sending对象""" + recalled_messages = [] + + for msg in self.messages: + if isinstance(msg, MessageSending): + # 检查是否撤回,对应stream_id和message_id + if ( + db.chat_streams.find({"stream_id": msg.chat_stream.stream_id}, {"message_id": msg.message_info.message_id}) + is not None + ): + recalled_messages.append(msg) + + # 按thinking_start_time排序,时间早的在前面 + recalled_messages.sort(key=lambda x: x.thinking_start_time) + return recalled_messages + def get_timeout_messages(self) -> List[MessageSending]: """获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序""" current_time = time.time() @@ -144,9 +158,7 @@ class MessageManager: self.containers[chat_id] = MessageContainer(chat_id) return self.containers[chat_id] - def add_message( - self, message: Union[MessageThinking, MessageSending, MessageSet] - ) -> None: + def add_message(self, message: Union[MessageThinking, MessageSending, MessageSet]) -> None: chat_stream = message.chat_stream if not chat_stream: raise ValueError("无法找到对应的聊天流") @@ -173,8 +185,20 @@ class MessageManager: if thinking_time > global_config.thinking_timeout: logger.warning(f"消息思考超时({thinking_time}秒),移除该消息") container.remove_message(message_earliest) - else: + # 检查消息是否被撤回 + recalled_messages = container.get_recalled_messages() + recalled_message_ids = [msg.message_id for msg in recalled_messages] + recalled_messages_stream_id = [msg.chat_stream.stream_id for msg in recalled_messages] + + if ( + message_earliest.message_info.message_id in recalled_message_ids + and message_earliest.chat_stream.stream_id in recalled_messages_stream_id + ): + logger.info(f"消息已被撤回,移除该消息: {message_earliest.message_id}") + container.remove_message(message_earliest) + + else: if ( message_earliest.is_head and message_earliest.update_thinking_time() > 30 @@ -189,9 +213,7 @@ class MessageManager: f"\033[1;34m[调试]\033[0m 消息“{truncate_message(message_earliest.processed_plain_text)}”正在发送中" ) - await self.storage.store_message( - message_earliest, message_earliest.chat_stream, None - ) + await self.storage.store_message(message_earliest, message_earliest.chat_stream, None) container.remove_message(message_earliest) diff --git a/src/plugins/chat/storage.py b/src/plugins/chat/storage.py index ad6662f2b..acd7db89a 100644 --- a/src/plugins/chat/storage.py +++ b/src/plugins/chat/storage.py @@ -24,4 +24,28 @@ class MessageStorage: except Exception: logger.exception("存储消息失败") + async def store_recalled_message(self, message_id: str, time: str, chat_stream:ChatStream) -> None: + """存储撤回消息到数据库""" + if "recalled_messages" not in db.list_collection_names(): + db.create_collection("recalled_messages") + else: + try: + message_data = { + "message_id": message_id, + "time": time, + "stream_id":chat_stream.stream_id, + } + db.recalled_messages.insert_one(message_data) + except Exception: + logger.exception("存储撤回消息失败") + + async def remove_recalled_message(self, time: str) -> None: + """删除撤回消息""" + try: + for msg in db.recalled_messages.distinct("message_id", {"time": time}): + if msg.time < (time-300): + db.recalled_messages.delete_one({"message_id": msg.message + }) + except Exception: + logger.exception("删除撤回消息失败") # 如果需要其他存储相关的函数,可以在这里添加 From 41b6cdba86b689ccff5738b1a4be825578416fa9 Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Thu, 13 Mar 2025 20:44:32 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat:=E5=B0=9D=E8=AF=95=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=8D=E5=9B=9E=E7=AD=94=E5=B7=B2=E6=92=A4=E5=9B=9E=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88=E4=BD=86=E4=BC=BC?= =?UTF-8?q?=E4=B9=8E=E7=9B=AE=E5=89=8D=E4=B8=8D=E8=B5=B7=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/bot.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index bdd0522f7..4d1318f2a 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -79,6 +79,24 @@ class ChatBot: raw_message += "(这是一个类似摸摸头的友善行为,而不是恶意行为,请不要作出攻击发言)" await self.directly_reply(raw_message, event.user_id, event.group_id) + if isinstance(event, GroupRecallNoticeEvent) or isinstance(event, FriendRecallNoticeEvent): + user_info = UserInfo( + user_id=event.user_id, + user_nickname=get_user_nickname(event.user_id) or None, + user_cardname=get_user_cardname(event.user_id) or None, + platform="qq", + ) + + group_info = GroupInfo(group_id=event.group_id, group_name=None, platform="qq") + + chat = await chat_manager.get_or_create_stream( + platform=user_info.platform, user_info=user_info, group_info=group_info + ) + + await self.storage.store_recalled_message(event.message_id, time.time(), chat) + + + async def handle_message(self, event: MessageEvent, bot: Bot) -> None: """处理收到的消息""" From 13c3b07085785313b24906c1c0e89c3790cc60e3 Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Thu, 13 Mar 2025 21:13:13 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=87=A0=E5=A4=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/message_sender.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index 1ff081bd8..a0d171504 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -79,7 +79,7 @@ class MessageContainer: if isinstance(msg, MessageSending): # 检查是否撤回,对应stream_id和message_id if ( - db.chat_streams.find({"stream_id": msg.chat_stream.stream_id}, {"message_id": msg.message_info.message_id}) + db.recalled_messages.find({"stream_id": msg.chat_stream.stream_id}, {"message_id": msg.message_info.message_id}) is not None ): recalled_messages.append(msg) @@ -188,15 +188,10 @@ class MessageManager: # 检查消息是否被撤回 recalled_messages = container.get_recalled_messages() - recalled_message_ids = [msg.message_id for msg in recalled_messages] - recalled_messages_stream_id = [msg.chat_stream.stream_id for msg in recalled_messages] - - if ( - message_earliest.message_info.message_id in recalled_message_ids - and message_earliest.chat_stream.stream_id in recalled_messages_stream_id - ): - logger.info(f"消息已被撤回,移除该消息: {message_earliest.message_id}") - container.remove_message(message_earliest) + for msg in recalled_messages: + if message_earliest.message_info.message_id == msg.message_info.message_id: + logger.warning(f"消息已被撤回,移除该消息: {message_earliest.message_info.message_id}") + container.remove_message(message_earliest) else: if ( From 54e82d1e3c1b22af47e896a48e1cc859274e2432 Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Fri, 14 Mar 2025 00:44:10 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=E6=92=A4=E5=9B=9E=E7=9A=84?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E4=B8=8D=E5=86=8D=E8=BF=9B=E8=A1=8C=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/message_sender.py | 94 ++++++++++++++---------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index a0d171504..a189cbbdd 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -24,6 +24,14 @@ class Message_Sender: def set_bot(self, bot: Bot): """设置当前bot实例""" self._current_bot = bot + + def get_recalled_messages(self, stream_id: str) -> list: + """获取所有撤回的消息""" + recalled_messages = [] + + recalled_messages = list(db.recalled_messages.find({"stream_id": stream_id}, {"message_id": 1})) + # 按thinking_start_time排序,时间早的在前面 + return recalled_messages async def send_message( self, @@ -32,33 +40,41 @@ class Message_Sender: """发送消息""" if isinstance(message, MessageSending): - message_json = message.to_dict() - message_send = MessageSendCQ(data=message_json) - # logger.debug(message_send.message_info,message_send.raw_message) - message_preview = truncate_message(message.processed_plain_text) - if message_send.message_info.group_info and message_send.message_info.group_info.group_id: - try: - await self._current_bot.send_group_msg( - group_id=message.message_info.group_info.group_id, - message=message_send.raw_message, - auto_escape=False, - ) - logger.success(f"[调试] 发送消息“{message_preview}”成功") - except Exception as e: - logger.error(f"[调试] 发生错误 {e}") - logger.error(f"[调试] 发送消息“{message_preview}”失败") - else: - try: - logger.debug(message.message_info.user_info) - await self._current_bot.send_private_msg( - user_id=message.sender_info.user_id, - message=message_send.raw_message, - auto_escape=False, - ) - logger.success(f"[调试] 发送消息“{message_preview}”成功") - except Exception as e: - logger.error(f"[调试] 发生错误 {e}") - logger.error(f"[调试] 发送消息“{message_preview}”失败") + recalled_messages = self.get_recalled_messages(message.chat_stream.stream_id) + is_recalled = False + for recalled_message in recalled_messages: + if message.reply_to_message_id == recalled_message["message_id"]: + is_recalled = True + logger.warning(f"消息“{message.processed_plain_text}”已被撤回,不发送") + break + if not is_recalled: + message_json = message.to_dict() + message_send = MessageSendCQ(data=message_json) + # logger.debug(message_send.message_info,message_send.raw_message) + message_preview = truncate_message(message.processed_plain_text) + if message_send.message_info.group_info and message_send.message_info.group_info.group_id: + try: + await self._current_bot.send_group_msg( + group_id=message.message_info.group_info.group_id, + message=message_send.raw_message, + auto_escape=False, + ) + logger.success(f"[调试] 发送消息“{message_preview}”成功") + except Exception as e: + logger.error(f"[调试] 发生错误 {e}") + logger.error(f"[调试] 发送消息“{message_preview}”失败") + else: + try: + logger.debug(message.message_info.user_info) + await self._current_bot.send_private_msg( + user_id=message.sender_info.user_id, + message=message_send.raw_message, + auto_escape=False, + ) + logger.success(f"[调试] 发送消息“{message_preview}”成功") + except Exception as e: + logger.error(f"[调试] 发生错误 {e}") + logger.error(f"[调试] 发送消息“{message_preview}”失败") class MessageContainer: @@ -71,23 +87,6 @@ class MessageContainer: self.last_send_time = 0 self.thinking_timeout = 20 # 思考超时时间(秒) - def get_recalled_messages(self) -> List[MessageSending]: - """获取所有撤回的Message_Sending对象""" - recalled_messages = [] - - for msg in self.messages: - if isinstance(msg, MessageSending): - # 检查是否撤回,对应stream_id和message_id - if ( - db.recalled_messages.find({"stream_id": msg.chat_stream.stream_id}, {"message_id": msg.message_info.message_id}) - is not None - ): - recalled_messages.append(msg) - - # 按thinking_start_time排序,时间早的在前面 - recalled_messages.sort(key=lambda x: x.thinking_start_time) - return recalled_messages - def get_timeout_messages(self) -> List[MessageSending]: """获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序""" current_time = time.time() @@ -186,13 +185,6 @@ class MessageManager: logger.warning(f"消息思考超时({thinking_time}秒),移除该消息") container.remove_message(message_earliest) - # 检查消息是否被撤回 - recalled_messages = container.get_recalled_messages() - for msg in recalled_messages: - if message_earliest.message_info.message_id == msg.message_info.message_id: - logger.warning(f"消息已被撤回,移除该消息: {message_earliest.message_info.message_id}") - container.remove_message(message_earliest) - else: if ( message_earliest.is_head From 8d823b59305c5452545fa705d19aed8628903eea Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Fri, 14 Mar 2025 00:51:45 +0800 Subject: [PATCH 5/7] =?UTF-8?q?fix:=20=E4=BD=BF=E8=83=BD=E5=A4=9F=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E5=88=A0=E9=99=A4=E8=B6=85=E8=BF=87=E4=B8=80=E5=B0=8F?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E6=92=A4=E5=9B=9E=E6=B6=88=E6=81=AF=EF=BC=8C?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E5=88=9B=E5=BB=BA=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/__init__.py | 3 +++ src/plugins/chat/storage.py | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 6462d7e2f..807df2ab3 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -148,6 +148,9 @@ async def generate_schedule_task(): await bot_schedule.initialize() if not bot_schedule.enable_output: bot_schedule.print_schedule() + +@scheduler.scheduled_job("interval", seconds=3600, id="remove_recalled_message") + async def remove_recalled_message(self) -> None: """删除撤回消息""" try: diff --git a/src/plugins/chat/storage.py b/src/plugins/chat/storage.py index acd7db89a..e0156788d 100644 --- a/src/plugins/chat/storage.py +++ b/src/plugins/chat/storage.py @@ -42,10 +42,7 @@ class MessageStorage: async def remove_recalled_message(self, time: str) -> None: """删除撤回消息""" try: - for msg in db.recalled_messages.distinct("message_id", {"time": time}): - if msg.time < (time-300): - db.recalled_messages.delete_one({"message_id": msg.message - }) + db.recalled_messages.delete_many({"time": {"$lt": time-300}}) except Exception: logger.exception("删除撤回消息失败") # 如果需要其他存储相关的函数,可以在这里添加 From bc13d6b2c93233597b871b09cb872648ad6f24ca Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Fri, 14 Mar 2025 00:55:35 +0800 Subject: [PATCH 6/7] =?UTF-8?q?fix:=E5=A4=9A=E5=86=99=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=B8=AAself?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 807df2ab3..253cc7a6c 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -151,10 +151,10 @@ async def generate_schedule_task(): @scheduler.scheduled_job("interval", seconds=3600, id="remove_recalled_message") -async def remove_recalled_message(self) -> None: +async def remove_recalled_message() -> None: """删除撤回消息""" try: - self.storage = MessageStorage() - self.storage.remove_recalled_message(time.time()) + storage = MessageStorage() + storage.remove_recalled_message(time.time()) except Exception: logger.exception("删除撤回消息失败") \ No newline at end of file From 06724a38168ff181600d5e76b52523c477836f94 Mon Sep 17 00:00:00 2001 From: HYY1116 Date: Fri, 14 Mar 2025 00:58:59 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 253cc7a6c..6b8f639ae 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -155,6 +155,6 @@ async def remove_recalled_message() -> None: """删除撤回消息""" try: storage = MessageStorage() - storage.remove_recalled_message(time.time()) + await storage.remove_recalled_message(time.time()) except Exception: logger.exception("删除撤回消息失败") \ No newline at end of file