From 41f97a0bf16600a5cd3ee8957676e14afba4b28f Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Wed, 28 May 2025 13:49:19 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=85=81=E8=AE=B8command=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=B1=95=E7=A4=BA=E5=90=8D=E7=A7=B0=EF=BC=8C=E8=80=8C?= =?UTF-8?q?=E9=9D=9E=E6=8C=87=E4=BB=A4=E5=8E=9F=E6=96=87=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=A4=84=E7=90=86=E5=99=A8=E8=B6=85=E6=97=B6=EF=BC=8C?= =?UTF-8?q?NormalChat=E8=BD=AC=E6=8D=A2=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../expressors/default_expressor.py | 5 ++++- src/chat/focus_chat/heartFC_chat.py | 15 +++++++++++--- .../info_processors/action_processor.py | 4 ++-- .../planners/actions/plugin_action.py | 3 ++- src/chat/focus_chat/planners/planner.py | 4 ++++ src/chat/message_receive/message.py | 8 ++++---- src/chat/message_receive/storage.py | 13 +++++++----- src/chat/normal_chat/normal_chat.py | 13 ++++++++++++ src/chat/utils/chat_message_builder.py | 20 +++++++++++++++++-- src/common/database/database_model.py | 1 + .../test_plugin/actions/mute_action.py | 3 ++- template/bot_config_template.toml | 9 +++++---- 12 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/chat/focus_chat/expressors/default_expressor.py b/src/chat/focus_chat/expressors/default_expressor.py index ed8409108..4e7bb643f 100644 --- a/src/chat/focus_chat/expressors/default_expressor.py +++ b/src/chat/focus_chat/expressors/default_expressor.py @@ -373,7 +373,7 @@ class DefaultExpressor: # --- 发送器 (Sender) --- # async def send_response_messages( - self, anchor_message: Optional[MessageRecv], response_set: List[Tuple[str, str]], thinking_id: str = "" + self, anchor_message: Optional[MessageRecv], response_set: List[Tuple[str, str]], thinking_id: str = "", display_message: str = "" ) -> Optional[MessageSending]: """发送回复消息 (尝试锚定到 anchor_message),使用 HeartFCSender""" chat = self.chat_stream @@ -426,6 +426,7 @@ class DefaultExpressor: anchor_message=anchor_message, message_id=part_message_id, message_segment=message_segment, + display_message=display_message, reply_to=reply_to, is_emoji=is_emoji, thinking_id=thinking_id, @@ -489,6 +490,7 @@ class DefaultExpressor: is_emoji: bool, thinking_id: str, thinking_start_time: float, + display_message: str, ) -> MessageSending: """构建单个发送消息""" @@ -508,6 +510,7 @@ class DefaultExpressor: is_head=reply_to, is_emoji=is_emoji, thinking_start_time=thinking_start_time, # 传递原始思考开始时间 + display_message=display_message, ) return bot_message diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index 936059d1f..a9ebb1a92 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -53,6 +53,9 @@ CONSECUTIVE_NO_REPLY_THRESHOLD = 3 # 连续不回复的阈值 logger = get_logger("hfc") # Logger Name Changed +# 设定处理器超时时间(秒) +PROCESSOR_TIMEOUT = 10 + async def _handle_cycle_delay(action_taken_this_cycle: bool, cycle_start_time: float, log_prefix: str): """处理循环延迟""" @@ -376,9 +379,13 @@ class HeartFChatting: for processor in self.processors: processor_name = processor.__class__.log_prefix - task = asyncio.create_task( - processor.process_info(observations=observations, running_memorys=running_memorys) - ) + # 用lambda包裹,便于传参 + async def run_with_timeout(proc=processor): + return await asyncio.wait_for( + proc.process_info(observations=observations, running_memorys=running_memorys), + timeout=PROCESSOR_TIMEOUT + ) + task = asyncio.create_task(run_with_timeout()) processor_tasks.append(task) task_to_name_map[task] = processor_name logger.debug(f"{self.log_prefix} 启动处理器任务: {processor_name}") @@ -404,6 +411,8 @@ class HeartFChatting: all_plan_info.extend(result_list) else: logger.warning(f"{self.log_prefix} 处理器 {processor_name} 返回了 None") + except asyncio.TimeoutError: + logger.error(f"{self.log_prefix} 处理器 {processor_name} 超时(>{PROCESSOR_TIMEOUT}s),已跳过") except Exception as e: logger.error( f"{self.log_prefix} 处理器 {processor_name} 执行失败,耗时 (自并行开始): {duration_since_parallel_start:.2f}秒. 错误: {e}", diff --git a/src/chat/focus_chat/info_processors/action_processor.py b/src/chat/focus_chat/info_processors/action_processor.py index 6970cd13b..dcb8ebd07 100644 --- a/src/chat/focus_chat/info_processors/action_processor.py +++ b/src/chat/focus_chat/info_processors/action_processor.py @@ -137,9 +137,9 @@ class ActionProcessor(BaseProcessor): # 检查no_reply比例 print(f"no_reply_count: {no_reply_count}, len(recent_cycles): {len(recent_cycles)}") # print(1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111) - if len(recent_cycles) >= (4 * global_config.focus_chat.exit_focus_threshold) and ( + if len(recent_cycles) >= (5 * global_config.focus_chat.exit_focus_threshold) and ( no_reply_count / len(recent_cycles) - ) >= (0.6 * global_config.focus_chat.exit_focus_threshold): + ) >= (0.8 * global_config.focus_chat.exit_focus_threshold): if global_config.chat.chat_mode == "auto": result["add"].append("exit_focus_chat") result["remove"].append("no_reply") diff --git a/src/chat/focus_chat/planners/actions/plugin_action.py b/src/chat/focus_chat/planners/actions/plugin_action.py index a74c4328d..e0f28efa2 100644 --- a/src/chat/focus_chat/planners/actions/plugin_action.py +++ b/src/chat/focus_chat/planners/actions/plugin_action.py @@ -111,7 +111,7 @@ class PluginAction(BaseAction): return platform, user_id # 提供简化的API方法 - async def send_message(self, type: str, data: str, target: Optional[str] = "") -> bool: + async def send_message(self, type: str, data: str, target: Optional[str] = "", display_message: str = "") -> bool: """发送消息的简化方法 Args: @@ -158,6 +158,7 @@ class PluginAction(BaseAction): success = await expressor.send_response_messages( anchor_message=anchor_message, response_set=response_set, + display_message=display_message, ) return success diff --git a/src/chat/focus_chat/planners/planner.py b/src/chat/focus_chat/planners/planner.py index 4a62a0cf8..a914e20b7 100644 --- a/src/chat/focus_chat/planners/planner.py +++ b/src/chat/focus_chat/planners/planner.py @@ -126,6 +126,10 @@ class ActionPlanner: reasoning = f"之前选择的动作{action}已被移除,原因: {reason}" # 继续处理其他信息 + self_info = "" + current_mind = "" + cycle_info = "" + structured_info = "" for info in all_plan_info: if isinstance(info, ObsInfo): observed_messages = info.get_talking_message() diff --git a/src/chat/message_receive/message.py b/src/chat/message_receive/message.py index 20691ce1a..ecd5c8b9a 100644 --- a/src/chat/message_receive/message.py +++ b/src/chat/message_receive/message.py @@ -29,7 +29,6 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) class Message(MessageBase): chat_stream: "ChatStream" = None reply: Optional["Message"] = None - detailed_plain_text: str = "" processed_plain_text: str = "" memorized_times: int = 0 @@ -275,6 +274,7 @@ class MessageSending(MessageProcessBase): bot_user_info: UserInfo, sender_info: UserInfo | None, # 用来记录发送者信息,用于私聊回复 message_segment: Seg, + display_message: str = "", reply: Optional["MessageRecv"] = None, is_head: bool = False, is_emoji: bool = False, @@ -298,10 +298,11 @@ class MessageSending(MessageProcessBase): self.is_emoji = is_emoji self.apply_set_reply_logic = apply_set_reply_logic + # 用于显示发送内容与显示不一致的情况 + self.display_message = display_message + def set_reply(self, reply: Optional["MessageRecv"] = None): """设置回复消息""" - # print(f"set_reply: {reply}") - # if self.message_info.format_info is not None and "reply" in self.message_info.format_info.accept_format: if True: if reply: self.reply = reply @@ -319,7 +320,6 @@ class MessageSending(MessageProcessBase): """处理消息内容,生成纯文本和详细文本""" if self.message_segment: self.processed_plain_text = await self._process_message_segments(self.message_segment) - self.detailed_plain_text = self._generate_detailed_text() @classmethod def from_thinking( diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index d0041cd51..54dd68f03 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -24,11 +24,14 @@ class MessageStorage: else: filtered_processed_plain_text = "" - detailed_plain_text = message.detailed_plain_text - if detailed_plain_text: - filtered_detailed_plain_text = re.sub(pattern, "", detailed_plain_text, flags=re.DOTALL) + if isinstance(message,MessageSending): + display_message = message.display_message + if display_message: + filtered_display_message = re.sub(pattern, "", display_message, flags=re.DOTALL) + else: + filtered_display_message = "" else: - filtered_detailed_plain_text = "" + filtered_display_message = "" chat_info_dict = chat_stream.to_dict() user_info_dict = message.message_info.user_info.to_dict() @@ -64,7 +67,7 @@ class MessageStorage: user_cardname=user_info_dict.get("user_cardname"), # Text content processed_plain_text=filtered_processed_plain_text, - detailed_plain_text=filtered_detailed_plain_text, + display_message=filtered_display_message, memorized_times=message.memorized_times, ) except Exception: diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 419482699..bd7ccfaa4 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -54,6 +54,8 @@ class NormalChat: # 添加回调函数,用于在满足条件时通知切换到focus_chat模式 self.on_switch_to_focus_callback = on_switch_to_focus_callback + self._disabled = False # 增加停用标志 + async def initialize(self): """异步初始化,获取聊天类型和目标信息。""" @@ -222,6 +224,10 @@ class NormalChat: async def normal_response( self, message: MessageRecv, is_mentioned: bool, interested_rate: float, rewind_response: bool = False ) -> None: + # 新增:如果已停用,直接返回 + if self._disabled: + logger.info(f"[{self.stream_name}] 已停用,忽略 normal_response。") + return # 检查收到的消息是否属于当前实例处理的 chat stream if message.chat_stream.stream_id != self.stream_id: logger.error( @@ -306,6 +312,10 @@ class NormalChat: return # 不执行后续步骤 logger.info(f"[{self.stream_name}] 回复内容: {response_set}") + + if self._disabled: + logger.info(f"[{self.stream_name}] 已停用,忽略 normal_response。") + return # 发送回复 (不再需要传入 chat) with Timer("消息发送", timing_results): @@ -374,6 +384,8 @@ class NormalChat: if not self._initialized: await self.initialize() # Ensure initialized before starting tasks + self._disabled = False # 启动时重置停用标志 + if self._chat_task is None or self._chat_task.done(): logger.info(f"[{self.stream_name}] 开始处理兴趣消息...") polling_task = asyncio.create_task(self._reply_interested_message()) @@ -403,6 +415,7 @@ class NormalChat: # 改为实例方法, 移除 stream_id 参数 async def stop_chat(self): """停止当前实例的兴趣监控任务。""" + self._disabled = True # 停止时设置停用标志 if self._chat_task and not self._chat_task.done(): task = self._chat_task logger.debug(f"[{self.stream_name}] 尝试取消normal聊天任务。") diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 4db9dc1b3..38c9e9a84 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -194,7 +194,15 @@ async def _build_readable_messages_internal( user_cardname = user_info.get("user_cardname") timestamp = msg.get("time") - content = msg.get("processed_plain_text", "") # 默认空字符串 + if msg.get("display_message"): + content = msg.get("display_message") + else: + content = msg.get("processed_plain_text", "") # 默认空字符串 + + if "ᶠ" in content: + content = content.replace("ᶠ", "") + if "ⁿ" in content: + content = content.replace("ⁿ", "") # 检查必要信息是否存在 if not all([platform, user_id, timestamp is not None]): @@ -453,7 +461,15 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: platform = user_info.get("platform") user_id = user_info.get("user_id") timestamp = msg.get("time") - content = msg.get("processed_plain_text", "") + if msg.get("display_message"): + content = msg.get("display_message") + else: + content = msg.get("processed_plain_text", "") + + if "ᶠ" in content: + content = content.replace("ᶠ", "") + if "ⁿ" in content: + content = content.replace("ⁿ", "") if not all([platform, user_id, timestamp is not None]): continue diff --git a/src/common/database/database_model.py b/src/common/database/database_model.py index 3544a8be0..ccf789649 100644 --- a/src/common/database/database_model.py +++ b/src/common/database/database_model.py @@ -147,6 +147,7 @@ class Messages(BaseModel): user_cardname = TextField(null=True) processed_plain_text = TextField(null=True) # 处理后的纯文本消息 + display_message = TextField(null=True) # 显示的消息 detailed_plain_text = TextField(null=True) # 详细的纯文本消息 memorized_times = IntegerField(default=0) # 被记忆的次数 diff --git a/src/plugins/test_plugin/actions/mute_action.py b/src/plugins/test_plugin/actions/mute_action.py index 6a5331baa..4f457d900 100644 --- a/src/plugins/test_plugin/actions/mute_action.py +++ b/src/plugins/test_plugin/actions/mute_action.py @@ -63,7 +63,8 @@ class MuteAction(PluginAction): # 发送群聊禁言命令,按照新格式 await self.send_message( - type="command", data={"name": "GROUP_BAN", "args": {"qq_id": str(user_id), "duration": duration_str}} + type = "command", data = {"name": "GROUP_BAN", "args": {"qq_id": str(user_id), "duration": duration_str}}, + display_message = f"我 禁言了 {target} {duration_str}秒" ) logger.info(f"{self.log_prefix} 成功发送禁言命令,用户 {target}({user_id}),时长 {duration} 秒") diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index b68fde809..31884a93d 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -186,11 +186,12 @@ pfc_chatting = false # 是否启用PFC聊天,该功能仅作用于私聊,与 #下面的模型若使用硅基流动则不需要更改,使用ds官方则改成.env自定义的宏,使用自定义模型则选择定位相似的模型自己填写 -# 额外字段 -# 下面的模型有以下额外字段可以添加: - # stream = : 用于指定模型是否是使用流式输出 -# 如果不指定,则该项是 False +# pri_in = : 用于指定模型输入价格 +# pri_out = : 用于指定模型输出价格 +# temp = : 用于指定模型温度 +# enable_thinking = : 用于指定模型是否启用思考 +# thinking_budget = : 用于指定模型思考最长长度 [model] model_max_output_length = 800 # 模型单次返回的最大token数