diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 6dde80d24..6b8f639ae 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,13 @@ 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() -> None: + """删除撤回消息""" + try: + storage = MessageStorage() + await 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 08cafdbf7..4d1318f2a 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 @@ -76,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: """处理收到的消息""" diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index 276110622..b138af3b6 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 @@ -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,36 +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: @@ -144,9 +157,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 +184,8 @@ class MessageManager: if thinking_time > global_config.thinking_timeout: logger.warning(f"消息思考超时({thinking_time}秒),移除该消息") container.remove_message(message_earliest) - else: + else: if ( message_earliest.is_head and message_earliest.update_thinking_time() > 30 @@ -188,9 +199,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..e0156788d 100644 --- a/src/plugins/chat/storage.py +++ b/src/plugins/chat/storage.py @@ -24,4 +24,25 @@ 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: + db.recalled_messages.delete_many({"time": {"$lt": time-300}}) + except Exception: + logger.exception("删除撤回消息失败") # 如果需要其他存储相关的函数,可以在这里添加