From 93542cadefeb1ecd7d0cfec27603eba2d5930a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=85=E8=AF=BA=E7=8B=90?= <212194964+foxcyber907@users.noreply.github.com> Date: Sat, 20 Sep 2025 10:55:06 +0800 Subject: [PATCH] =?UTF-8?q?perf(methods):=20=E9=80=9A=E8=BF=87=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=20self=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E4=BC=98=E5=8C=96=E6=96=B9=E6=B3=95=E7=AD=BE?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在包括 chat、plugin_system、schedule 和 mais4u 在内的多个模块中,消除冗余的实例引用。此次改动将无需访问实例状态的实用函数转换为静态方法,从而提升了内存效率,并使方法依赖关系更加清晰。 --- src/__init__.py | 3 +- src/chat/antipromptinjector/anti_injector.py | 6 +- src/chat/antipromptinjector/core/detector.py | 9 +- src/chat/antipromptinjector/core/shield.py | 14 +- src/chat/antipromptinjector/counter_attack.py | 3 +- .../decision/counter_attack.py | 3 +- .../decision/decision_maker.py | 3 +- src/chat/antipromptinjector/decision_maker.py | 3 +- src/chat/antipromptinjector/detector.py | 9 +- .../management/statistics.py | 9 +- .../processors/message_processor.py | 9 +- src/chat/chat_loop/heartFC_chat.py | 5 +- src/chat/chat_loop/hfc_context.py | 16 +-- .../chat_loop/proactive/proactive_thinker.py | 17 ++- .../chat_loop/sleep_manager/sleep_manager.py | 4 +- .../chat_loop/sleep_manager/time_checker.py | 3 +- src/chat/emoji_system/emoji_history.py | 3 +- src/chat/emoji_system/emoji_manager.py | 13 +- src/chat/express/expression_learner.py | 129 ++++++++++-------- src/chat/express/expression_selector.py | 6 +- src/chat/frequency_analyzer/analyzer.py | 3 +- src/chat/frequency_analyzer/tracker.py | 3 +- .../heart_flow/heartflow_message_processor.py | 13 +- src/chat/knowledge/embedding_store.py | 9 +- src/chat/memory_system/Hippocampus.py | 4 +- .../memory_system/async_memory_optimizer.py | 9 +- src/chat/memory_system/instant_memory.py | 6 +- .../memory_system/vector_instant_memory.py | 3 +- src/chat/message_receive/bot.py | 12 +- src/chat/message_receive/chat_stream.py | 3 +- src/chat/message_receive/message.py | 24 ++-- src/chat/message_receive/storage.py | 22 +-- src/chat/planner_actions/action_manager.py | 4 +- src/chat/planner_actions/action_modifier.py | 3 +- src/chat/planner_actions/plan_executor.py | 3 +- src/chat/planner_actions/plan_filter.py | 15 +- src/chat/planner_actions/plan_generator.py | 2 +- src/chat/planner_actions/planner.py | 2 - src/chat/replyer/default_generator.py | 13 +- src/chat/utils/prompt.py | 19 +-- src/chat/utils/statistic.py | 15 +- src/chat/utils/utils.py | 5 +- src/chat/utils/utils_image.py | 3 +- src/chat/utils/utils_video.py | 26 ++-- src/chat/utils/utils_video_legacy.py | 7 +- src/common/cache_manager.py | 6 +- src/common/data_models/info_data_model.py | 4 +- src/common/data_models/llm_data_model.py | 3 +- src/common/data_models/message_data_model.py | 4 +- src/common/database/database.py | 3 +- .../database/sqlalchemy_database_api.py | 13 +- src/common/database/sqlalchemy_models.py | 17 +-- src/common/server.py | 8 +- src/config/api_ada_configs.py | 8 -- src/config/config.py | 2 - src/config/config_base.py | 2 +- src/config/official_configs.py | 9 +- src/individuality/individuality.py | 3 +- src/llm_models/payload_content/message.py | 4 +- src/llm_models/utils.py | 4 +- src/llm_models/utils_model.py | 10 +- src/main.py | 68 ++++----- .../body_emotion_action_manager.py | 4 +- src/mais4u/mais4u_chat/s4u_chat.py | 21 ++- src/mais4u/mais4u_chat/s4u_mood_manager.py | 3 +- src/mais4u/mais4u_chat/s4u_msg_processor.py | 17 ++- src/mais4u/mais4u_chat/s4u_prompt.py | 18 ++- .../mais4u_chat/s4u_stream_generator.py | 3 +- src/mais4u/mais4u_chat/super_chat_manager.py | 3 +- src/mais4u/s4u_config.py | 2 +- src/manager/async_task_manager.py | 4 +- src/person_info/person_info.py | 22 +-- src/person_info/relationship_builder.py | 22 ++- src/person_info/relationship_manager.py | 67 ++++++++- src/plugin_system/apis/message_api.py | 28 ++-- src/plugin_system/apis/permission_api.py | 2 +- src/plugin_system/apis/send_api.py | 4 +- src/plugin_system/base/base_event.py | 6 +- src/plugin_system/base/plugin_base.py | 3 +- src/plugin_system/base/plus_command.py | 2 +- src/plugin_system/core/component_registry.py | 6 +- src/plugin_system/core/event_manager.py | 3 +- src/plugin_system/core/plugin_hot_reload.py | 15 +- src/plugin_system/core/plugin_manager.py | 15 +- src/plugin_system/utils/dependency_manager.py | 6 +- src/plugin_system/utils/manifest_utils.py | 4 +- .../utils/permission_decorators.py | 16 +-- .../services/content_service.py | 7 +- .../services/cookie_service.py | 3 +- .../services/image_service.py | 3 +- .../services/qzone_service.py | 9 +- .../services/reply_tracker_service.py | 3 +- .../services/scheduler_service.py | 6 +- .../maizone_refactored/utils/history_utils.py | 3 +- .../built_in/napcat_adapter_plugin/plugin.py | 5 +- .../src/message_buffer.py | 9 +- .../src/message_chunker.py | 3 +- .../src/recv_handler/message_handler.py | 33 +++-- .../src/recv_handler/meta_event_handler.py | 2 +- .../src/recv_handler/notice_handler.py | 8 +- .../napcat_adapter_plugin/src/send_handler.py | 67 ++++++--- .../built_in/permission_management/plugin.py | 3 +- .../built_in/plugin_management/plugin.py | 7 +- .../built_in/reminder_plugin/plugin.py | 3 +- src/plugins/built_in/tts_plugin/plugin.py | 3 +- .../web_search_tool/engines/bing_engine.py | 3 +- .../web_search_tool/tools/url_parser.py | 2 +- src/schedule/llm_generator.py | 6 +- src/schedule/plan_manager.py | 9 +- src/schedule/schedule_manager.py | 11 +- src/utils/message_chunker.py | 3 +- 111 files changed, 705 insertions(+), 465 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index d359f56eb..bdb90be85 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -48,7 +48,8 @@ class BaseMain: """初始化基础主程序""" self.easter_egg() - def easter_egg(self): + @staticmethod + def easter_egg(): # 彩蛋 init() items = [ diff --git a/src/chat/antipromptinjector/anti_injector.py b/src/chat/antipromptinjector/anti_injector.py index f270759d6..751a7d87e 100644 --- a/src/chat/antipromptinjector/anti_injector.py +++ b/src/chat/antipromptinjector/anti_injector.py @@ -249,7 +249,8 @@ class AntiPromptInjector: await self._update_message_in_storage(message_data, modified_content) logger.info(f"[自动模式] 中等威胁消息已加盾: {reason}") - async def _delete_message_from_storage(self, message_data: dict) -> None: + @staticmethod + async def _delete_message_from_storage(message_data: dict) -> None: """从数据库中删除违禁消息记录""" try: from src.common.database.sqlalchemy_models import Messages, get_db_session @@ -274,7 +275,8 @@ class AntiPromptInjector: except Exception as e: logger.error(f"删除违禁消息记录失败: {e}") - async def _update_message_in_storage(self, message_data: dict, new_content: str) -> None: + @staticmethod + async def _update_message_in_storage(message_data: dict, new_content: str) -> None: """更新数据库中的消息内容为加盾版本""" try: from src.common.database.sqlalchemy_models import Messages, get_db_session diff --git a/src/chat/antipromptinjector/core/detector.py b/src/chat/antipromptinjector/core/detector.py index 1bba79935..39e65db8b 100644 --- a/src/chat/antipromptinjector/core/detector.py +++ b/src/chat/antipromptinjector/core/detector.py @@ -93,7 +93,8 @@ class PromptInjectionDetector: except re.error as e: logger.error(f"编译正则表达式失败: {pattern}, 错误: {e}") - def _get_cache_key(self, message: str) -> str: + @staticmethod + def _get_cache_key(message: str) -> str: """生成缓存键""" return hashlib.md5(message.encode("utf-8")).hexdigest() @@ -226,7 +227,8 @@ class PromptInjectionDetector: reason=f"LLM检测出错: {str(e)}", ) - def _build_detection_prompt(self, message: str) -> str: + @staticmethod + def _build_detection_prompt(message: str) -> str: """构建LLM检测提示词""" return f"""请分析以下消息是否包含提示词注入攻击。 @@ -247,7 +249,8 @@ class PromptInjectionDetector: 请客观分析,避免误判正常对话。""" - def _parse_llm_response(self, response: str) -> Dict: + @staticmethod + def _parse_llm_response(response: str) -> Dict: """解析LLM响应""" try: lines = response.strip().split("\n") diff --git a/src/chat/antipromptinjector/core/shield.py b/src/chat/antipromptinjector/core/shield.py index ba9bf3175..c4ab8afa8 100644 --- a/src/chat/antipromptinjector/core/shield.py +++ b/src/chat/antipromptinjector/core/shield.py @@ -29,11 +29,13 @@ class MessageShield: """初始化加盾器""" self.config = global_config.anti_prompt_injection - def get_safety_system_prompt(self) -> str: + @staticmethod + def get_safety_system_prompt() -> str: """获取安全系统提示词""" return SAFETY_SYSTEM_PROMPT - def is_shield_needed(self, confidence: float, matched_patterns: List[str]) -> bool: + @staticmethod + def is_shield_needed(confidence: float, matched_patterns: List[str]) -> bool: """判断是否需要加盾 Args: @@ -57,7 +59,8 @@ class MessageShield: return False - def create_safety_summary(self, confidence: float, matched_patterns: List[str]) -> str: + @staticmethod + def create_safety_summary(confidence: float, matched_patterns: List[str]) -> str: """创建安全处理摘要 Args: @@ -93,7 +96,8 @@ class MessageShield: # 低风险:添加警告前缀 return f"{self.config.shield_prefix}[内容已检查]{self.config.shield_suffix} {original_message}" - def _partially_shield_content(self, message: str) -> str: + @staticmethod + def _partially_shield_content(message: str) -> str: """部分遮蔽消息内容""" # 遮蔽策略:替换关键词 dangerous_keywords = [ @@ -231,4 +235,4 @@ def create_default_shield() -> MessageShield: """创建默认的消息加盾器""" from .config import default_config - return MessageShield(default_config) + return MessageShield() diff --git a/src/chat/antipromptinjector/counter_attack.py b/src/chat/antipromptinjector/counter_attack.py index ad16ad6b6..7c2bd86c5 100644 --- a/src/chat/antipromptinjector/counter_attack.py +++ b/src/chat/antipromptinjector/counter_attack.py @@ -18,7 +18,8 @@ logger = get_logger("anti_injector.counter_attack") class CounterAttackGenerator: """反击消息生成器""" - def get_personality_context(self) -> str: + @staticmethod + def get_personality_context() -> str: """获取人格上下文信息 Returns: diff --git a/src/chat/antipromptinjector/decision/counter_attack.py b/src/chat/antipromptinjector/decision/counter_attack.py index c12e7697e..9d6aac2ff 100644 --- a/src/chat/antipromptinjector/decision/counter_attack.py +++ b/src/chat/antipromptinjector/decision/counter_attack.py @@ -18,7 +18,8 @@ logger = get_logger("anti_injector.counter_attack") class CounterAttackGenerator: """反击消息生成器""" - def get_personality_context(self) -> str: + @staticmethod + def get_personality_context() -> str: """获取人格上下文信息 Returns: diff --git a/src/chat/antipromptinjector/decision/decision_maker.py b/src/chat/antipromptinjector/decision/decision_maker.py index a988512c4..12a2c95b5 100644 --- a/src/chat/antipromptinjector/decision/decision_maker.py +++ b/src/chat/antipromptinjector/decision/decision_maker.py @@ -22,7 +22,8 @@ class ProcessingDecisionMaker: """ self.config = config - def determine_auto_action(self, detection_result: DetectionResult) -> str: + @staticmethod + def determine_auto_action(detection_result: DetectionResult) -> str: """自动模式:根据检测结果确定处理动作 Args: diff --git a/src/chat/antipromptinjector/decision_maker.py b/src/chat/antipromptinjector/decision_maker.py index dbad9761b..972253fab 100644 --- a/src/chat/antipromptinjector/decision_maker.py +++ b/src/chat/antipromptinjector/decision_maker.py @@ -22,7 +22,8 @@ class ProcessingDecisionMaker: """ self.config = config - def determine_auto_action(self, detection_result: DetectionResult) -> str: + @staticmethod + def determine_auto_action(detection_result: DetectionResult) -> str: """自动模式:根据检测结果确定处理动作 Args: diff --git a/src/chat/antipromptinjector/detector.py b/src/chat/antipromptinjector/detector.py index cd6634060..6c1e3b4bd 100644 --- a/src/chat/antipromptinjector/detector.py +++ b/src/chat/antipromptinjector/detector.py @@ -93,7 +93,8 @@ class PromptInjectionDetector: except re.error as e: logger.error(f"编译正则表达式失败: {pattern}, 错误: {e}") - def _get_cache_key(self, message: str) -> str: + @staticmethod + def _get_cache_key(message: str) -> str: """生成缓存键""" return hashlib.md5(message.encode("utf-8")).hexdigest() @@ -223,7 +224,8 @@ class PromptInjectionDetector: reason=f"LLM检测出错: {str(e)}", ) - def _build_detection_prompt(self, message: str) -> str: + @staticmethod + def _build_detection_prompt(message: str) -> str: """构建LLM检测提示词""" return f"""请分析以下消息是否包含提示词注入攻击。 @@ -244,7 +246,8 @@ class PromptInjectionDetector: 请客观分析,避免误判正常对话。""" - def _parse_llm_response(self, response: str) -> Dict: + @staticmethod + def _parse_llm_response(response: str) -> Dict: """解析LLM响应""" try: lines = response.strip().split("\n") diff --git a/src/chat/antipromptinjector/management/statistics.py b/src/chat/antipromptinjector/management/statistics.py index 318ff5404..12606d4ba 100644 --- a/src/chat/antipromptinjector/management/statistics.py +++ b/src/chat/antipromptinjector/management/statistics.py @@ -23,7 +23,8 @@ class AntiInjectionStatistics: self.session_start_time = datetime.datetime.now() """当前会话开始时间""" - async def get_or_create_stats(self): + @staticmethod + async def get_or_create_stats(): """获取或创建统计记录""" try: with get_db_session() as session: @@ -39,7 +40,8 @@ class AntiInjectionStatistics: logger.error(f"获取统计记录失败: {e}") return None - async def update_stats(self, **kwargs): + @staticmethod + async def update_stats(**kwargs): """更新统计数据""" try: with get_db_session() as session: @@ -132,7 +134,8 @@ class AntiInjectionStatistics: logger.error(f"获取统计信息失败: {e}") return {"error": f"获取统计信息失败: {e}"} - async def reset_stats(self): + @staticmethod + async def reset_stats(): """重置统计信息""" try: with get_db_session() as session: diff --git a/src/chat/antipromptinjector/processors/message_processor.py b/src/chat/antipromptinjector/processors/message_processor.py index 76add60f0..935848c2d 100644 --- a/src/chat/antipromptinjector/processors/message_processor.py +++ b/src/chat/antipromptinjector/processors/message_processor.py @@ -37,7 +37,8 @@ class MessageProcessor: # 只返回用户新增的内容,避免重复 return new_content - def extract_new_content_from_reply(self, full_text: str) -> str: + @staticmethod + def extract_new_content_from_reply(full_text: str) -> str: """从包含引用的完整消息中提取用户新增的内容 Args: @@ -64,7 +65,8 @@ class MessageProcessor: return new_content - def check_whitelist(self, message: MessageRecv, whitelist: list) -> Optional[tuple]: + @staticmethod + def check_whitelist(message: MessageRecv, whitelist: list) -> Optional[tuple]: """检查用户白名单 Args: @@ -85,7 +87,8 @@ class MessageProcessor: return None - def check_whitelist_dict(self, user_id: str, platform: str, whitelist: list) -> bool: + @staticmethod + def check_whitelist_dict(user_id: str, platform: str, whitelist: list) -> bool: """检查用户是否在白名单中(字典格式) Args: diff --git a/src/chat/chat_loop/heartFC_chat.py b/src/chat/chat_loop/heartFC_chat.py index 05edb3ee0..7a351c97c 100644 --- a/src/chat/chat_loop/heartFC_chat.py +++ b/src/chat/chat_loop/heartFC_chat.py @@ -94,7 +94,7 @@ class HeartFChatting: self.context.running = True self.context.relationship_builder = relationship_builder_manager.get_or_create_builder(self.context.stream_id) - self.context.expression_learner = expression_learner_manager.get_expression_learner(self.context.stream_id) + self.context.expression_learner = await expression_learner_manager.get_expression_learner(self.context.stream_id) # 启动主动思考监视器 if global_config.chat.enable_proactive_thinking: @@ -281,7 +281,8 @@ class HeartFChatting: logger.error(f"{self.context.log_prefix} 动态间隔计算出错: {e},使用固定间隔") return max(300, abs(global_config.chat.proactive_thinking_interval)) - def _format_duration(self, seconds: float) -> str: + @staticmethod + def _format_duration(seconds: float) -> str: """ 格式化时长为可读字符串 diff --git a/src/chat/chat_loop/hfc_context.py b/src/chat/chat_loop/hfc_context.py index fe5d283ae..67606de12 100644 --- a/src/chat/chat_loop/hfc_context.py +++ b/src/chat/chat_loop/hfc_context.py @@ -1,17 +1,15 @@ -from typing import List, Optional, TYPE_CHECKING import time -from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager -from src.person_info.relationship_builder_manager import RelationshipBuilder -from src.chat.express.expression_learner import ExpressionLearner -from src.chat.planner_actions.action_manager import ActionManager +from typing import List, Optional, TYPE_CHECKING + from src.chat.chat_loop.hfc_utils import CycleDetail +from src.chat.express.expression_learner import ExpressionLearner +from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager +from src.chat.planner_actions.action_manager import ActionManager from src.config.config import global_config +from src.person_info.relationship_builder_manager import RelationshipBuilder if TYPE_CHECKING: - from .sleep_manager.wakeup_manager import WakeUpManager - from .energy_manager import EnergyManager - from .heartFC_chat import HeartFChatting - from .sleep_manager.sleep_manager import SleepManager + pass class HfcContext: diff --git a/src/chat/chat_loop/proactive/proactive_thinker.py b/src/chat/chat_loop/proactive/proactive_thinker.py index 3cbcc2529..adf187dca 100644 --- a/src/chat/chat_loop/proactive/proactive_thinker.py +++ b/src/chat/chat_loop/proactive/proactive_thinker.py @@ -2,19 +2,18 @@ import time import traceback from typing import TYPE_CHECKING, Dict, Any +from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages_with_id +from src.common.database.sqlalchemy_database_api import store_action_info from src.common.logger import get_logger -from src.plugin_system.base.component_types import ChatMode -from ..hfc_context import HfcContext -from .events import ProactiveTriggerEvent +from src.config.config import global_config +from src.mood.mood_manager import mood_manager +from src.plugin_system import tool_api from src.plugin_system.apis import generator_api from src.plugin_system.apis.generator_api import process_human_text +from src.plugin_system.base.component_types import ChatMode from src.schedule.schedule_manager import schedule_manager -from src.plugin_system import tool_api -from src.config.config import global_config -from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages_with_id -from src.mood.mood_manager import mood_manager -from src.common.database.sqlalchemy_database_api import store_action_info, db_get -from src.common.database.sqlalchemy_models import Messages +from .events import ProactiveTriggerEvent +from ..hfc_context import HfcContext if TYPE_CHECKING: from ..cycle_processor import CycleProcessor diff --git a/src/chat/chat_loop/sleep_manager/sleep_manager.py b/src/chat/chat_loop/sleep_manager/sleep_manager.py index 677555aef..ad4aa1ced 100644 --- a/src/chat/chat_loop/sleep_manager/sleep_manager.py +++ b/src/chat/chat_loop/sleep_manager/sleep_manager.py @@ -5,12 +5,12 @@ from typing import Optional, TYPE_CHECKING from src.common.logger import get_logger from src.config.config import global_config +from .notification_sender import NotificationSender from .sleep_state import SleepState, SleepStateSerializer from .time_checker import TimeChecker -from .notification_sender import NotificationSender if TYPE_CHECKING: - from .wakeup_manager import WakeUpManager + pass logger = get_logger("sleep_manager") diff --git a/src/chat/chat_loop/sleep_manager/time_checker.py b/src/chat/chat_loop/sleep_manager/time_checker.py index cbe3d45e8..47376ac35 100644 --- a/src/chat/chat_loop/sleep_manager/time_checker.py +++ b/src/chat/chat_loop/sleep_manager/time_checker.py @@ -34,7 +34,8 @@ class TimeChecker: return self._daily_sleep_offset, self._daily_wake_offset - def get_today_schedule(self) -> Optional[List[Dict[str, Any]]]: + @staticmethod + def get_today_schedule() -> Optional[List[Dict[str, Any]]]: """从全局 ScheduleManager 获取今天的日程安排。""" return schedule_manager.today_schedule diff --git a/src/chat/emoji_system/emoji_history.py b/src/chat/emoji_system/emoji_history.py index a25063f52..d0e2ca856 100644 --- a/src/chat/emoji_system/emoji_history.py +++ b/src/chat/emoji_system/emoji_history.py @@ -2,9 +2,8 @@ """ 表情包发送历史记录模块 """ -import os -from typing import List, Dict from collections import deque +from typing import List, Dict from src.common.logger import get_logger diff --git a/src/chat/emoji_system/emoji_manager.py b/src/chat/emoji_system/emoji_manager.py index ce7b0d074..e2a6eb7f1 100644 --- a/src/chat/emoji_system/emoji_manager.py +++ b/src/chat/emoji_system/emoji_manager.py @@ -424,7 +424,8 @@ class EmojiManager: # if not self._initialized: # raise RuntimeError("EmojiManager not initialized") - async def record_usage(self, emoji_hash: str) -> None: + @staticmethod + async def record_usage(emoji_hash: str) -> None: """记录表情使用次数""" try: async with get_db_session() as session: @@ -436,7 +437,6 @@ class EmojiManager: else: emoji_update.usage_count += 1 emoji_update.last_used_time = time.time() - await session.commit() except Exception as e: logger.error(f"记录表情使用失败: {str(e)}") @@ -523,7 +523,7 @@ class EmojiManager: # 7. 获取选中的表情包并更新使用记录 selected_emoji = candidate_emojis[selected_index] - self.record_usage(selected_emoji.hash) + await self.record_usage(selected_emoji.emoji_hash) _time_end = time.time() logger.info( @@ -680,7 +680,8 @@ class EmojiManager: self.emoji_objects = [] # 加载失败则清空列表 self.emoji_num = 0 - async def get_emoji_from_db(self, emoji_hash: Optional[str] = None) -> List["MaiEmoji"]: + @staticmethod + async def get_emoji_from_db(emoji_hash: Optional[str] = None) -> List["MaiEmoji"]: """获取指定哈希值的表情包并初始化为MaiEmoji类对象列表 (主要用于调试或特定查找) 参数: @@ -747,8 +748,8 @@ class EmojiManager: try: emoji_record = await self.get_emoji_from_db(emoji_hash) if emoji_record and emoji_record[0].emotion: - logger.info(f"[缓存命中] 从数据库获取表情包描述: {emoji_record.emotion[:50]}...") - return emoji_record.emotion + logger.info(f"[缓存命中] 从数据库获取表情包描述: {emoji_record[0].emotion[:50]}...") + return emoji_record[0].emotion except Exception as e: logger.error(f"从数据库查询表情包描述时出错: {e}") diff --git a/src/chat/express/expression_learner.py b/src/chat/express/expression_learner.py index fcb9ea49f..fe135dfbf 100644 --- a/src/chat/express/expression_learner.py +++ b/src/chat/express/expression_learner.py @@ -4,7 +4,7 @@ import orjson import os from datetime import datetime -from typing import List, Dict, Optional, Any, Tuple +from typing import List, Dict, Optional, Any, Tuple, Coroutine from src.common.logger import get_logger from src.common.database.sqlalchemy_database_api import get_db_session @@ -90,7 +90,7 @@ class ExpressionLearner: logger.error(f"检查学习权限失败: {e}") return False - def should_trigger_learning(self) -> bool: + async def should_trigger_learning(self) -> bool: """ 检查是否应该触发学习 @@ -124,7 +124,7 @@ class ExpressionLearner: return False # 检查消息数量(只检查指定聊天流的消息) - recent_messages = get_raw_msg_by_timestamp_with_chat_inclusive( + recent_messages = await get_raw_msg_by_timestamp_with_chat_inclusive( chat_id=self.chat_id, timestamp_start=self.last_learning_time, timestamp_end=time.time(), @@ -171,7 +171,7 @@ class ExpressionLearner: logger.error(f"为聊天流 {self.chat_name} 触发学习失败: {e}") return False - def get_expression_by_chat_id(self) -> Tuple[List[Dict[str, float]], List[Dict[str, float]]]: + async def get_expression_by_chat_id(self) -> Tuple[List[Dict[str, float]], List[Dict[str, float]]]: """ 获取指定chat_id的style和grammar表达方式 返回的每个表达方式字典中都包含了source_id, 用于后续的更新操作 @@ -180,8 +180,8 @@ class ExpressionLearner: learnt_grammar_expressions = [] # 直接从数据库查询 - with get_db_session() as session: - style_query = session.execute( + async with get_db_session() as session: + style_query = await session.execute( select(Expression).where((Expression.chat_id == self.chat_id) & (Expression.type == "style")) ) for expr in style_query.scalars(): @@ -198,7 +198,7 @@ class ExpressionLearner: "create_date": create_date, } ) - grammar_query = session.execute( + grammar_query = await session.execute( select(Expression).where((Expression.chat_id == self.chat_id) & (Expression.type == "grammar")) ) for expr in grammar_query.scalars(): @@ -217,14 +217,15 @@ class ExpressionLearner: ) return learnt_style_expressions, learnt_grammar_expressions - def _apply_global_decay_to_database(self, current_time: float) -> None: + async def _apply_global_decay_to_database(self, current_time: float) -> None: """ 对数据库中的所有表达方式应用全局衰减 """ try: - with get_db_session() as session: + async with get_db_session() as session: # 获取所有表达方式 - all_expressions = session.execute(select(Expression)).scalars() + all_expressions = await session.execute(select(Expression)) + all_expressions = all_expressions.scalars().all() updated_count = 0 deleted_count = 0 @@ -241,7 +242,7 @@ class ExpressionLearner: if new_count <= 0.01: # 如果count太小,删除这个表达方式 session.delete(expr) - session.commit() + await session.commit() deleted_count += 1 else: # 更新count @@ -254,7 +255,8 @@ class ExpressionLearner: except Exception as e: logger.error(f"数据库全局衰减失败: {e}") - def calculate_decay_factor(self, time_diff_days: float) -> float: + @staticmethod + def calculate_decay_factor(time_diff_days: float) -> float: """ 计算衰减值 当时间差为0天时,衰减值为0(最近活跃的不衰减) @@ -276,7 +278,8 @@ class ExpressionLearner: return min(0.01, decay) - async def learn_and_store(self, num: int = 10) -> List[Tuple[str, str, str]]: + async def learn_and_store(self, type: str, num: int = 10) -> None | list[Any] | list[tuple[str, str, str]]: + # sourcery skip: use-join """ 学习并存储表达方式 """ @@ -318,19 +321,20 @@ class ExpressionLearner: # 存储到数据库 Expression 表 for chat_id, expr_list in chat_dict.items(): - for new_expr in expr_list: - # 查找是否已存在相似表达方式 - with get_db_session() as session: - query = session.execute( + async with get_db_session() as session: + for new_expr in expr_list: + # 查找是否已存在相似表达方式 + query = await session.execute( select(Expression).where( (Expression.chat_id == chat_id) & (Expression.type == type) & (Expression.situation == new_expr["situation"]) & (Expression.style == new_expr["style"]) ) - ).scalar() - if query: - expr_obj = query + ) + existing_expr = query.scalar() + if existing_expr: + expr_obj = existing_expr # 50%概率替换内容 if random.random() < 0.5: expr_obj.situation = new_expr["situation"] @@ -347,23 +351,22 @@ class ExpressionLearner: type=type, create_date=current_time, # 手动设置创建日期 ) - session.add(new_expression) - session.commit() + await session.add(new_expression) + # 限制最大数量 - exprs = list( - session.execute( - select(Expression) - .where((Expression.chat_id == chat_id) & (Expression.type == type)) - .order_by(Expression.count.asc()) - ).scalars() + exprs_result = await session.execute( + select(Expression) + .where((Expression.chat_id == chat_id) & (Expression.type == type)) + .order_by(Expression.count.asc()) ) + exprs = list(exprs_result.scalars()) if len(exprs) > MAX_EXPRESSION_COUNT: # 删除count最小的多余表达方式 for expr in exprs[: len(exprs) - MAX_EXPRESSION_COUNT]: - session.delete(expr) - session.commit() + await session.delete(expr) return learnt_expressions + return None async def learn_expression(self, num: int = 10) -> Optional[Tuple[List[Tuple[str, str, str]], str]]: """从指定聊天流学习表达方式 @@ -377,7 +380,7 @@ class ExpressionLearner: current_time = time.time() # 获取上次学习时间 - random_msg: Optional[List[Dict[str, Any]]] = get_raw_msg_by_timestamp_with_chat_inclusive( + random_msg: Optional[List[Dict[str, Any]]] = await get_raw_msg_by_timestamp_with_chat_inclusive( chat_id=self.chat_id, timestamp_start=self.last_learning_time, timestamp_end=current_time, @@ -412,7 +415,8 @@ class ExpressionLearner: return expressions, chat_id - def parse_expression_response(self, response: str, chat_id: str) -> List[Tuple[str, str, str]]: + @staticmethod + def parse_expression_response(response: str, chat_id: str) -> List[Tuple[str, str, str]]: """ 解析LLM返回的表达风格总结,每一行提取"当"和"使用"之间的内容,存储为(situation, style)元组 """ @@ -451,15 +455,18 @@ class ExpressionLearnerManager: self.expression_learners = {} self._ensure_expression_directories() - self._auto_migrate_json_to_db() - self._migrate_old_data_create_date() - def get_expression_learner(self, chat_id: str) -> ExpressionLearner: + + async def get_expression_learner(self, chat_id: str) -> ExpressionLearner: + await self._auto_migrate_json_to_db() + await self._migrate_old_data_create_date() + if chat_id not in self.expression_learners: self.expression_learners[chat_id] = ExpressionLearner(chat_id) return self.expression_learners[chat_id] - def _ensure_expression_directories(self): + @staticmethod + def _ensure_expression_directories(): """ 确保表达方式相关的目录结构存在 """ @@ -477,7 +484,8 @@ class ExpressionLearnerManager: except Exception as e: logger.error(f"创建目录失败 {directory}: {e}") - def _auto_migrate_json_to_db(self): + @staticmethod + async def _auto_migrate_json_to_db(): """ 自动将/data/expression/learnt_style 和 learnt_grammar 下所有expressions.json迁移到数据库。 迁移完成后在/data/expression/done.done写入标记文件,存在则跳过。 @@ -538,33 +546,33 @@ class ExpressionLearnerManager: continue # 查重:同chat_id+type+situation+style - with get_db_session() as session: - query = session.execute( + async with get_db_session() as session: + query = await session.execute( select(Expression).where( (Expression.chat_id == chat_id) & (Expression.type == type_str) & (Expression.situation == situation) & (Expression.style == style_val) ) - ).scalar() - if query: - expr_obj = query - expr_obj.count = max(expr_obj.count, count) - expr_obj.last_active_time = max(expr_obj.last_active_time, last_active_time) - else: - new_expression = Expression( - situation=situation, - style=style_val, - count=count, - last_active_time=last_active_time, - chat_id=chat_id, - type=type_str, - create_date=last_active_time, # 迁移时使用last_active_time作为创建时间 ) - session.add(new_expression) - session.commit() + existing_expr = query.scalar() + if existing_expr: + expr_obj = existing_expr + expr_obj.count = max(expr_obj.count, count) + expr_obj.last_active_time = max(expr_obj.last_active_time, last_active_time) + else: + new_expression = Expression( + situation=situation, + style=style_val, + count=count, + last_active_time=last_active_time, + chat_id=chat_id, + type=type_str, + create_date=last_active_time, # 迁移时使用last_active_time作为创建时间 + ) + await session.add(new_expression) - migrated_count += 1 + migrated_count += 1 logger.info(f"已迁移 {expr_file} 到数据库,包含 {len(expressions)} 个表达方式") except orjson.JSONDecodeError as e: logger.error(f"JSON解析失败 {expr_file}: {e}") @@ -587,15 +595,17 @@ class ExpressionLearnerManager: else: logger.info("grammar表达已删除,跳过重复删除") - def _migrate_old_data_create_date(self): + @staticmethod + async def _migrate_old_data_create_date(): """ 为没有create_date的老数据设置创建日期 使用last_active_time作为create_date的默认值 """ try: - with get_db_session() as session: + async with get_db_session() as session: # 查找所有create_date为空的表达方式 - old_expressions = session.execute(select(Expression).where(Expression.create_date.is_(None))).scalars() + old_expressions_result = await session.execute(select(Expression).where(Expression.create_date.is_(None))) + old_expressions = old_expressions_result.scalars().all() updated_count = 0 for expr in old_expressions: @@ -605,7 +615,6 @@ class ExpressionLearnerManager: if updated_count > 0: logger.info(f"已为 {updated_count} 个老的表达方式设置创建日期") - session.commit() except Exception as e: logger.error(f"迁移老数据创建日期失败: {e}") diff --git a/src/chat/express/expression_selector.py b/src/chat/express/expression_selector.py index 810c3326c..27684d750 100644 --- a/src/chat/express/expression_selector.py +++ b/src/chat/express/expression_selector.py @@ -76,7 +76,8 @@ class ExpressionSelector: model_set=model_config.model_task_config.utils_small, request_type="expression.selector" ) - def can_use_expression_for_chat(self, chat_id: str) -> bool: + @staticmethod + def can_use_expression_for_chat(chat_id: str) -> bool: """ 检查指定聊天流是否允许使用表达 @@ -193,7 +194,8 @@ class ExpressionSelector: return selected_style, selected_grammar - async def update_expressions_count_batch(self, expressions_to_update: List[Dict[str, Any]], increment: float = 0.1): + @staticmethod + async def update_expressions_count_batch(expressions_to_update: List[Dict[str, Any]], increment: float = 0.1): """对一批表达方式更新count值,按chat_id+type分组后一次性写入数据库""" if not expressions_to_update: return diff --git a/src/chat/frequency_analyzer/analyzer.py b/src/chat/frequency_analyzer/analyzer.py index bd6331465..f888b9737 100644 --- a/src/chat/frequency_analyzer/analyzer.py +++ b/src/chat/frequency_analyzer/analyzer.py @@ -40,7 +40,8 @@ class ChatFrequencyAnalyzer: self._analysis_cache: dict[str, tuple[float, list[tuple[time, time]]]] = {} self._cache_ttl_seconds = 60 * 30 # 缓存30分钟 - def _find_peak_windows(self, timestamps: List[float]) -> List[Tuple[datetime, datetime]]: + @staticmethod + def _find_peak_windows(timestamps: List[float]) -> List[Tuple[datetime, datetime]]: """ 使用滑动窗口算法来识别时间戳列表中的高峰时段。 diff --git a/src/chat/frequency_analyzer/tracker.py b/src/chat/frequency_analyzer/tracker.py index bee9e4623..178435528 100644 --- a/src/chat/frequency_analyzer/tracker.py +++ b/src/chat/frequency_analyzer/tracker.py @@ -21,7 +21,8 @@ class ChatFrequencyTracker: def __init__(self): self._timestamps: Dict[str, List[float]] = self._load_timestamps() - def _load_timestamps(self) -> Dict[str, List[float]]: + @staticmethod + def _load_timestamps() -> Dict[str, List[float]]: """从本地文件加载时间戳数据。""" if not TRACKER_FILE.exists(): return {} diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index d760c4989..565b503b8 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -1,21 +1,20 @@ import asyncio -import re import math +import re import traceback -from datetime import datetime - from typing import Tuple, TYPE_CHECKING -from src.config.config import global_config +from src.chat.heart_flow.heartflow import heartflow from src.chat.memory_system.Hippocampus import hippocampus_manager from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.storage import MessageStorage -from src.chat.heart_flow.heartflow import heartflow -from src.chat.utils.utils import is_mentioned_bot_in_message -from src.chat.utils.timer_calculator import Timer from src.chat.utils.chat_message_builder import replace_user_references_sync +from src.chat.utils.timer_calculator import Timer +from src.chat.utils.utils import is_mentioned_bot_in_message from src.common.logger import get_logger +from src.config.config import global_config from src.mood.mood_manager import mood_manager +from src.person_info.relationship_manager import get_relationship_manager if TYPE_CHECKING: from src.chat.heart_flow.sub_heartflow import SubHeartflow diff --git a/src/chat/knowledge/embedding_store.py b/src/chat/knowledge/embedding_store.py index 75af35a7b..67296c0c9 100644 --- a/src/chat/knowledge/embedding_store.py +++ b/src/chat/knowledge/embedding_store.py @@ -125,7 +125,8 @@ class EmbeddingStore: self.faiss_index = None self.idx2hash = None - def _get_embedding(self, s: str) -> List[float]: + @staticmethod + def _get_embedding(s: str) -> List[float]: """获取字符串的嵌入向量,使用完全同步的方式避免事件循环问题""" # 创建新的事件循环并在完成后立即关闭 loop = asyncio.new_event_loop() @@ -157,8 +158,9 @@ class EmbeddingStore: except Exception: ... + @staticmethod def _get_embeddings_batch_threaded( - self, strs: List[str], chunk_size: int = 10, max_workers: int = 10, progress_callback=None + strs: List[str], chunk_size: int = 10, max_workers: int = 10, progress_callback=None ) -> List[Tuple[str, List[float]]]: """使用多线程批量获取嵌入向量 @@ -265,7 +267,8 @@ class EmbeddingStore: return ordered_results - def get_test_file_path(self): + @staticmethod + def get_test_file_path(): return EMBEDDING_TEST_FILE def save_embedding_test_vectors(self): diff --git a/src/chat/memory_system/Hippocampus.py b/src/chat/memory_system/Hippocampus.py index 2b33e59be..d1117e9e6 100644 --- a/src/chat/memory_system/Hippocampus.py +++ b/src/chat/memory_system/Hippocampus.py @@ -926,7 +926,7 @@ class EntorhinalCortex: timestamp_start = target_timestamp timestamp_end = target_timestamp + time_window_seconds - if chosen_message := get_raw_msg_by_timestamp( + if chosen_message := await get_raw_msg_by_timestamp( timestamp_start=timestamp_start, timestamp_end=timestamp_end, limit=1, @@ -934,7 +934,7 @@ class EntorhinalCortex: ): chat_id: str = chosen_message[0].get("chat_id") # type: ignore - if messages := get_raw_msg_by_timestamp_with_chat( + if messages := await get_raw_msg_by_timestamp_with_chat( timestamp_start=timestamp_start, timestamp_end=timestamp_end, limit=chat_size, diff --git a/src/chat/memory_system/async_memory_optimizer.py b/src/chat/memory_system/async_memory_optimizer.py index ee215abde..e80ad0efd 100644 --- a/src/chat/memory_system/async_memory_optimizer.py +++ b/src/chat/memory_system/async_memory_optimizer.py @@ -137,7 +137,8 @@ class AsyncMemoryQueue: except Exception: pass - async def _handle_store_task(self, task: MemoryTask) -> Any: + @staticmethod + async def _handle_store_task(task: MemoryTask) -> Any: """处理记忆存储任务""" # 这里需要根据具体的记忆系统来实现 # 为了避免循环导入,这里使用延迟导入 @@ -156,7 +157,8 @@ class AsyncMemoryQueue: logger.error(f"记忆存储失败: {e}") return False - async def _handle_retrieve_task(self, task: MemoryTask) -> Any: + @staticmethod + async def _handle_retrieve_task(task: MemoryTask) -> Any: """处理记忆检索任务""" try: # 获取包装器实例 @@ -173,7 +175,8 @@ class AsyncMemoryQueue: logger.error(f"记忆检索失败: {e}") return [] - async def _handle_build_task(self, task: MemoryTask) -> Any: + @staticmethod + async def _handle_build_task(task: MemoryTask) -> Any: """处理记忆构建任务(海马体系统)""" try: # 延迟导入避免循环依赖 diff --git a/src/chat/memory_system/instant_memory.py b/src/chat/memory_system/instant_memory.py index 6ea0163c0..0b4b0b2e3 100644 --- a/src/chat/memory_system/instant_memory.py +++ b/src/chat/memory_system/instant_memory.py @@ -106,7 +106,8 @@ class InstantMemory: else: logger.info(f"不需要记忆:{text}") - async def store_memory(self, memory_item: MemoryItem): + @staticmethod + async def store_memory(memory_item: MemoryItem): with get_db_session() as session: memory = Memory( memory_id=memory_item.memory_id, @@ -198,7 +199,8 @@ class InstantMemory: logger.error(f"获取记忆出现错误:{str(e)} {traceback.format_exc()}") return None - def _parse_time_range(self, time_str): + @staticmethod + def _parse_time_range(time_str): # sourcery skip: extract-duplicate-method, use-contextlib-suppress """ 支持解析如下格式: diff --git a/src/chat/memory_system/vector_instant_memory.py b/src/chat/memory_system/vector_instant_memory.py index 96af659d7..12d9622e0 100644 --- a/src/chat/memory_system/vector_instant_memory.py +++ b/src/chat/memory_system/vector_instant_memory.py @@ -243,7 +243,8 @@ class VectorInstantMemoryV2: logger.error(f"查找相似消息失败: {e}") return [] - def _format_time_ago(self, timestamp: float) -> str: + @staticmethod + def _format_time_ago(timestamp: float) -> str: """格式化时间差显示""" if timestamp <= 0: return "未知时间" diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 5c5285ac0..c8c4f2f25 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -80,7 +80,8 @@ class ChatBot: # 初始化反注入系统 self._initialize_anti_injector() - def _initialize_anti_injector(self): + @staticmethod + def _initialize_anti_injector(): """初始化反注入系统""" try: initialize_anti_injector() @@ -100,7 +101,8 @@ class ChatBot: self._started = True - async def _process_plus_commands(self, message: MessageRecv): + @staticmethod + async def _process_plus_commands(message: MessageRecv): """独立处理PlusCommand系统""" try: text = message.processed_plain_text @@ -220,7 +222,8 @@ class ChatBot: logger.error(f"处理PlusCommand时出错: {e}") return False, None, True # 出错时继续处理消息 - async def _process_commands_with_new_system(self, message: MessageRecv): + @staticmethod + async def _process_commands_with_new_system(message: MessageRecv): # sourcery skip: use-named-expression """使用新插件系统处理命令""" try: @@ -310,7 +313,8 @@ class ChatBot: return False - async def handle_adapter_response(self, message: MessageRecv): + @staticmethod + async def handle_adapter_response(message: MessageRecv): """处理适配器命令响应""" try: from src.plugin_system.apis.send_api import put_adapter_response diff --git a/src/chat/message_receive/chat_stream.py b/src/chat/message_receive/chat_stream.py index b0de6f596..9e90eed25 100644 --- a/src/chat/message_receive/chat_stream.py +++ b/src/chat/message_receive/chat_stream.py @@ -203,7 +203,8 @@ class ChatManager: key = "_".join(components) return hashlib.md5(key.encode()).hexdigest() - def get_stream_id(self, platform: str, id: str, is_group: bool = True) -> str: + @staticmethod + def get_stream_id(platform: str, id: str, is_group: bool = True) -> str: """获取聊天流ID""" components = [platform, id] if is_group else [platform, id, "private"] key = "_".join(components) diff --git a/src/chat/message_receive/message.py b/src/chat/message_receive/message.py index abab6fc4d..3bd522c8b 100644 --- a/src/chat/message_receive/message.py +++ b/src/chat/message_receive/message.py @@ -1,19 +1,18 @@ -import time -import urllib3 import base64 - -from abc import abstractmethod +import time +from abc import abstractmethod, ABCMeta from dataclasses import dataclass -from rich.traceback import install -from typing import Optional, Any, List -from maim_message import Seg, UserInfo, BaseMessageInfo, MessageBase +from typing import Optional, Any + +import urllib3 +from maim_message import Seg, UserInfo, BaseMessageInfo, MessageBase +from rich.traceback import install -from src.common.logger import get_logger from src.chat.utils.utils_image import get_image_manager -from src.chat.utils.utils_voice import get_voice_text from src.chat.utils.utils_video import get_video_analyzer, is_video_analysis_available +from src.chat.utils.utils_voice import get_voice_text +from src.common.logger import get_logger from src.config.config import global_config -from .chat_stream import ChatStream install(extra_lines=3) @@ -28,7 +27,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @dataclass -class Message(MessageBase): +class Message(MessageBase, metaclass=ABCMeta): chat_stream: "ChatStream" = None # type: ignore reply: Optional["Message"] = None processed_plain_text: str = "" @@ -96,12 +95,13 @@ class Message(MessageBase): class MessageRecv(Message): """接收消息类,用于处理从MessageCQ序列化的消息""" - def __init__(self, message_dict: dict[str, Any]): + def __init__(self, message_dict: dict[str, Any], message_id: str, chat_stream: "ChatStream", user_info: UserInfo): """从MessageCQ的字典初始化 Args: message_dict: MessageCQ序列化后的字典 """ + super().__init__(message_id, chat_stream, user_info) self.message_info = BaseMessageInfo.from_dict(message_dict.get("message_info", {})) self.message_segment = Seg.from_dict(message_dict.get("message_segment", {})) self.raw_message = message_dict.get("raw_message") diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index 9b6c69d40..edb007238 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -1,15 +1,15 @@ import re import json import traceback -import orjson from typing import Union -from src.common.database.sqlalchemy_models import Messages, Images +import orjson +from sqlalchemy import select, desc, update + +from src.common.database.sqlalchemy_models import Messages, Images, get_db_session from src.common.logger import get_logger from .chat_stream import ChatStream from .message import MessageSending, MessageRecv -from src.common.database.sqlalchemy_database_api import get_db_session -from sqlalchemy import select, update, desc logger = get_logger("message_storage") @@ -117,21 +117,13 @@ class MessageStorage: user_nickname=user_info_dict.get("user_nickname"), user_cardname=user_info_dict.get("user_cardname"), processed_plain_text=filtered_processed_plain_text, - display_message=filtered_display_message, - memorized_times=message.memorized_times, - interest_value=interest_value, priority_mode=priority_mode, priority_info=priority_info_json, is_emoji=is_emoji, is_picid=is_picid, - is_notify=is_notify, - is_command=is_command, - key_words=key_words, - key_words_lite=key_words_lite, ) async with get_db_session() as session: - session.add(new_message) - await session.commit() + await session.add(new_message) except Exception: logger.exception("存储消息失败") @@ -154,8 +146,7 @@ class MessageStorage: qq_message_id = message.message_segment.data.get("id") elif message.message_segment.type == "reply": qq_message_id = message.message_segment.data.get("id") - if qq_message_id: - logger.debug(f"从reply消息段获取到消息ID: {qq_message_id}") + logger.debug(f"从reply消息段获取到消息ID: {qq_message_id}") elif message.message_segment.type == "adapter_response": logger.debug("适配器响应消息,不需要更新ID") return @@ -198,7 +189,6 @@ class MessageStorage: f"segment_type={getattr(message.message_segment, 'type', 'N/A')}" ) - @staticmethod async def replace_image_descriptions(text: str) -> str: """将[图片:描述]替换为[picid:image_id]""" # 先检查文本中是否有图片标记 diff --git a/src/chat/planner_actions/action_manager.py b/src/chat/planner_actions/action_manager.py index 267b7a8ff..23755e42d 100644 --- a/src/chat/planner_actions/action_manager.py +++ b/src/chat/planner_actions/action_manager.py @@ -27,9 +27,9 @@ class ActionManager: # === 执行Action方法 === + @staticmethod def create_action( - self, - action_name: str, + action_name: str, action_data: dict, reasoning: str, cycle_timers: dict, diff --git a/src/chat/planner_actions/action_modifier.py b/src/chat/planner_actions/action_modifier.py index a061c15ae..154fe62a7 100644 --- a/src/chat/planner_actions/action_modifier.py +++ b/src/chat/planner_actions/action_modifier.py @@ -243,7 +243,8 @@ class ActionModifier: return deactivated_actions - def _generate_context_hash(self, chat_content: str) -> str: + @staticmethod + def _generate_context_hash(chat_content: str) -> str: """生成上下文的哈希值用于缓存""" context_content = f"{chat_content}" return hashlib.md5(context_content.encode("utf-8")).hexdigest() diff --git a/src/chat/planner_actions/plan_executor.py b/src/chat/planner_actions/plan_executor.py index b27ef12e3..591389f99 100644 --- a/src/chat/planner_actions/plan_executor.py +++ b/src/chat/planner_actions/plan_executor.py @@ -27,7 +27,8 @@ class PlanExecutor: """ self.action_manager = action_manager - async def execute(self, plan: Plan): + @staticmethod + async def execute(plan: Plan): """ 遍历并执行 Plan 对象中 `decided_actions` 列表里的所有动作。 diff --git a/src/chat/planner_actions/plan_filter.py b/src/chat/planner_actions/plan_filter.py index 2d05f0511..91237c9cb 100644 --- a/src/chat/planner_actions/plan_filter.py +++ b/src/chat/planner_actions/plan_filter.py @@ -297,15 +297,17 @@ class PlanFilter: ) return parsed_actions + @staticmethod def _filter_no_actions( - self, action_list: List[ActionPlannerInfo] + action_list: List[ActionPlannerInfo] ) -> List[ActionPlannerInfo]: non_no_actions = [a for a in action_list if a.action_type not in ["no_action", "no_reply"]] if non_no_actions: return non_no_actions return action_list[:1] if action_list else [] - async def _get_long_term_memory_context(self) -> str: + @staticmethod + async def _get_long_term_memory_context() -> str: try: now = datetime.now() keywords = ["今天", "日程", "计划"] @@ -329,7 +331,8 @@ class PlanFilter: logger.error(f"获取长期记忆时出错: {e}") return "回忆时出现了一些问题。" - async def _build_action_options(self, current_available_actions: Dict[str, ActionInfo]) -> str: + @staticmethod + async def _build_action_options(current_available_actions: Dict[str, ActionInfo]) -> str: action_options_block = "" for action_name, action_info in current_available_actions.items(): param_text = "" @@ -347,7 +350,8 @@ class PlanFilter: ) return action_options_block - def _find_message_by_id(self, message_id: str, message_id_list: list) -> Optional[Dict[str, Any]]: + @staticmethod + def _find_message_by_id(message_id: str, message_id_list: list) -> Optional[Dict[str, Any]]: if message_id.isdigit(): message_id = f"m{message_id}" for item in message_id_list: @@ -355,7 +359,8 @@ class PlanFilter: return item.get("message") return None - def _get_latest_message(self, message_id_list: list) -> Optional[Dict[str, Any]]: + @staticmethod + def _get_latest_message(message_id_list: list) -> Optional[Dict[str, Any]]: if not message_id_list: return None return message_id_list[-1].get("message") diff --git a/src/chat/planner_actions/plan_generator.py b/src/chat/planner_actions/plan_generator.py index 96af31c4b..5d1ab9c38 100644 --- a/src/chat/planner_actions/plan_generator.py +++ b/src/chat/planner_actions/plan_generator.py @@ -2,7 +2,7 @@ PlanGenerator: 负责搜集和汇总所有决策所需的信息,生成一个未经筛选的“原始计划” (Plan)。 """ import time -from typing import Dict, Optional, Tuple +from typing import Dict from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat from src.chat.utils.utils import get_chat_type_and_target_info diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index 8e4f18fae..6e45b7907 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -8,12 +8,10 @@ from src.chat.planner_actions.action_manager import ActionManager from src.chat.planner_actions.plan_executor import PlanExecutor from src.chat.planner_actions.plan_filter import PlanFilter from src.chat.planner_actions.plan_generator import PlanGenerator -from src.common.data_models.info_data_model import ActionPlannerInfo from src.common.logger import get_logger from src.plugin_system.base.component_types import ChatMode # 导入提示词模块以确保其被初始化 -from . import planner_prompts logger = get_logger("planner") diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index b372dad4b..b01906eb7 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -591,7 +591,8 @@ class DefaultReplyer: logger.error(f"工具信息获取失败: {e}") return "" - def _parse_reply_target(self, target_message: str) -> Tuple[str, str]: + @staticmethod + def _parse_reply_target(target_message: str) -> Tuple[str, str]: """解析回复目标消息 - 使用共享工具""" from src.chat.utils.prompt import Prompt if target_message is None: @@ -599,7 +600,8 @@ class DefaultReplyer: return "未知用户", "(无消息内容)" return Prompt.parse_reply_target(target_message) - async def build_keywords_reaction_prompt(self, target: Optional[str]) -> str: + @staticmethod + async def build_keywords_reaction_prompt(target: Optional[str]) -> str: """构建关键词反应提示 Args: @@ -641,7 +643,8 @@ class DefaultReplyer: return keywords_reaction_prompt - async def _time_and_run_task(self, coroutine, name: str) -> Tuple[str, Any, float]: + @staticmethod + async def _time_and_run_task(coroutine, name: str) -> Tuple[str, Any, float]: """计时并运行异步任务的辅助函数 Args: @@ -730,9 +733,9 @@ class DefaultReplyer: return core_dialogue_prompt, all_dialogue_prompt + @staticmethod def build_mai_think_context( - self, - chat_id: str, + chat_id: str, memory_block: str, relation_info: str, time_block: str, diff --git a/src/chat/utils/prompt.py b/src/chat/utils/prompt.py index ec5446e64..9dec72a28 100644 --- a/src/chat/utils/prompt.py +++ b/src/chat/utils/prompt.py @@ -215,6 +215,10 @@ class PromptManager: result = prompt.format(**kwargs) return result + @property + def context(self): + return self._context + # 全局单例 global_prompt_manager = PromptManager() @@ -256,7 +260,7 @@ class Prompt: self._processed_template = self._process_escaped_braces(template) # 自动注册 - if should_register and not global_prompt_manager._context._current_context: + if should_register and not global_prompt_manager.context._current_context: global_prompt_manager.register(self) @staticmethod @@ -459,8 +463,9 @@ class Prompt: context_data["chat_info"] = f"""群里的聊天内容: {self.parameters.chat_talking_prompt_short}""" + @staticmethod async def _build_s4u_chat_history_prompts( - self, message_list_before_now: List[Dict[str, Any]], target_user_id: str, sender: str + message_list_before_now: List[Dict[str, Any]], target_user_id: str, sender: str ) -> Tuple[str, str]: """构建S4U风格的分离对话prompt""" # 实现逻辑与原有SmartPromptBuilder相同 @@ -537,14 +542,10 @@ class Prompt: ) # 创建表情选择器 - expression_selector = ExpressionSelector(self.parameters.chat_id) + expression_selector = ExpressionSelector() # 选择合适的表情 selected_expressions = await expression_selector.select_suitable_expressions_llm( - chat_history=chat_history, - current_message=self.parameters.target, - emotional_tone="neutral", - topic_type="general" ) # 构建表达习惯块 @@ -991,7 +992,7 @@ async def create_prompt_async( ) -> Prompt: """异步创建Prompt实例""" prompt = create_prompt(template, name, parameters, **kwargs) - if global_prompt_manager._context._current_context: - await global_prompt_manager._context.register_async(prompt) + if global_prompt_manager.context._current_context: + await global_prompt_manager.context.register_async(prompt) return prompt diff --git a/src/chat/utils/statistic.py b/src/chat/utils/statistic.py index c03ae47cc..29407673a 100644 --- a/src/chat/utils/statistic.py +++ b/src/chat/utils/statistic.py @@ -775,7 +775,8 @@ class StatisticOutputTask(AsyncTask): output.append("") return "\n".join(output) - def _get_chat_display_name_from_id(self, chat_id: str) -> str: + @staticmethod + def _get_chat_display_name_from_id(chat_id: str) -> str: """从chat_id获取显示名称""" try: # 首先尝试从chat_stream获取真实群组名称 @@ -1121,7 +1122,8 @@ class StatisticOutputTask(AsyncTask): return chart_data - def _collect_interval_data(self, now: datetime, hours: int, interval_minutes: int) -> dict: + @staticmethod + def _collect_interval_data(now: datetime, hours: int, interval_minutes: int) -> dict: """收集指定时间范围内每个间隔的数据""" # 生成时间点 start_time = now - timedelta(hours=hours) @@ -1211,7 +1213,8 @@ class StatisticOutputTask(AsyncTask): "message_by_chat": message_by_chat, } - def _generate_chart_tab(self, chart_data: dict) -> str: + @staticmethod + def _generate_chart_tab(chart_data: dict) -> str: # sourcery skip: extract-duplicate-method, move-assign-in-block """生成图表选项卡HTML内容""" @@ -1575,13 +1578,13 @@ class AsyncStatisticOutputTask(AsyncTask): return StatisticOutputTask._generate_chart_data(self, stat) # type: ignore def _collect_interval_data(self, now: datetime, hours: int, interval_minutes: int) -> dict: - return StatisticOutputTask._collect_interval_data(self, now, hours, interval_minutes) # type: ignore + return StatisticOutputTask._collect_interval_data(now, hours, interval_minutes) # type: ignore def _generate_chart_tab(self, chart_data: dict) -> str: - return StatisticOutputTask._generate_chart_tab(self, chart_data) # type: ignore + return StatisticOutputTask._generate_chart_tab(chart_data) # type: ignore def _get_chat_display_name_from_id(self, chat_id: str) -> str: - return StatisticOutputTask._get_chat_display_name_from_id(self, chat_id) # type: ignore + return StatisticOutputTask._get_chat_display_name_from_id(chat_id) # type: ignore def _convert_defaultdict_to_dict(self, data): return StatisticOutputTask._convert_defaultdict_to_dict(self, data) # type: ignore diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index ba1c905b3..492e00729 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -7,7 +7,7 @@ import numpy as np from collections import Counter from maim_message import UserInfo -from typing import Optional, Tuple, Dict, List, Any +from typing import Optional, Tuple, Dict, List, Any, Coroutine from src.common.logger import get_logger from src.common.message_repository import find_messages, count_messages @@ -540,7 +540,8 @@ def get_western_ratio(paragraph): return western_count / len(alnum_chars) -def count_messages_between(start_time: float, end_time: float, stream_id: str) -> tuple[int, int]: +def count_messages_between(start_time: float, end_time: float, stream_id: str) -> tuple[int, int] | tuple[ + Coroutine[Any, Any, int], int]: """计算两个时间点之间的消息数量和文本总长度 Args: diff --git a/src/chat/utils/utils_image.py b/src/chat/utils/utils_image.py index 8069fd616..18adc8a9b 100644 --- a/src/chat/utils/utils_image.py +++ b/src/chat/utils/utils_image.py @@ -134,7 +134,8 @@ class ImageManager: except Exception as e: logger.error(f"保存描述到数据库失败 (SQLAlchemy): {str(e)}") - async def get_emoji_tag(self, image_base64: str) -> str: + @staticmethod + async def get_emoji_tag(image_base64: str) -> str: from src.chat.emoji_system.emoji_manager import get_emoji_manager emoji_manager = get_emoji_manager() diff --git a/src/chat/utils/utils_video.py b/src/chat/utils/utils_video.py index 1e186f058..6ea5a111f 100644 --- a/src/chat/utils/utils_video.py +++ b/src/chat/utils/utils_video.py @@ -167,7 +167,8 @@ class VideoAnalyzer: # 获取Rust模块系统信息 self._log_system_info() - def _log_system_info(self): + @staticmethod + def _log_system_info(): """记录系统信息""" if not RUST_VIDEO_AVAILABLE: logger.info("⚠️ Rust模块不可用,跳过系统信息获取") @@ -196,13 +197,15 @@ class VideoAnalyzer: except Exception as e: logger.warning(f"获取系统信息失败: {e}") - def _calculate_video_hash(self, video_data: bytes) -> str: + @staticmethod + def _calculate_video_hash(video_data: bytes) -> str: """计算视频文件的hash值""" hash_obj = hashlib.sha256() hash_obj.update(video_data) return hash_obj.hexdigest() - def _check_video_exists(self, video_hash: str) -> Optional[Videos]: + @staticmethod + def _check_video_exists(video_hash: str) -> Optional[Videos]: """检查视频是否已经分析过""" try: with get_db_session() as session: @@ -213,8 +216,9 @@ class VideoAnalyzer: logger.warning(f"检查视频是否存在时出错: {e}") return None + @staticmethod def _store_video_result( - self, video_hash: str, description: str, metadata: Optional[Dict] = None + video_hash: str, description: str, metadata: Optional[Dict] = None ) -> Optional[Videos]: """存储视频分析结果到数据库""" # 检查描述是否为错误信息,如果是则不保存 @@ -619,7 +623,7 @@ class VideoAnalyzer: if self.disabled: error_msg = "❌ 视频分析功能已禁用:没有可用的视频处理实现" logger.warning(error_msg) - return (False, error_msg) + return False, error_msg try: logger.info(f"开始分析视频: {os.path.basename(video_path)}") @@ -628,7 +632,7 @@ class VideoAnalyzer: frames = await self.extract_frames(video_path) if not frames: error_msg = "❌ 无法从视频中提取有效帧" - return (False, error_msg) + return False, error_msg # 根据模式选择分析方法 if self.analysis_mode == "auto": @@ -645,12 +649,12 @@ class VideoAnalyzer: result = await self.analyze_frames_sequential(frames, user_question) logger.info("✅ 视频分析完成") - return (True, result) + return True, result except Exception as e: error_msg = f"❌ 视频分析失败: {str(e)}" logger.error(error_msg) - return (False, error_msg) + return False, error_msg async def analyze_video_from_bytes( self, video_bytes: bytes, filename: str = None, user_question: str = None, prompt: str = None @@ -783,7 +787,8 @@ class VideoAnalyzer: return {"summary": error_msg} - def is_supported_video(self, file_path: str) -> bool: + @staticmethod + def is_supported_video(file_path: str) -> bool: """检查是否为支持的视频格式""" supported_formats = {".mp4", ".avi", ".mov", ".mkv", ".flv", ".wmv", ".m4v", ".3gp", ".webm"} return Path(file_path).suffix.lower() in supported_formats @@ -818,7 +823,8 @@ class VideoAnalyzer: logger.error(f"获取处理能力信息失败: {e}") return {"error": str(e), "available": False} - def _get_recommended_settings(self, cpu_features: Dict[str, bool]) -> Dict[str, any]: + @staticmethod + def _get_recommended_settings(cpu_features: Dict[str, bool]) -> Dict[str, any]: """根据CPU特性推荐最佳设置""" settings = { "use_simd": any(cpu_features.values()), diff --git a/src/chat/utils/utils_video_legacy.py b/src/chat/utils/utils_video_legacy.py index ef5f49301..4d8e06681 100644 --- a/src/chat/utils/utils_video_legacy.py +++ b/src/chat/utils/utils_video_legacy.py @@ -13,7 +13,7 @@ import base64 import numpy as np from PIL import Image from pathlib import Path -from typing import List, Tuple, Optional +from typing import List, Tuple, Optional, Any import io from concurrent.futures import ThreadPoolExecutor @@ -31,7 +31,7 @@ def _extract_frames_worker( max_image_size: int, frame_extraction_mode: str, frame_interval_seconds: Optional[float], -) -> List[Tuple[str, float]]: +) -> list[Any] | list[tuple[str, str]]: """线程池中提取视频帧的工作函数""" frames = [] try: @@ -568,7 +568,8 @@ class LegacyVideoAnalyzer: logger.error(error_msg) return error_msg - def is_supported_video(self, file_path: str) -> bool: + @staticmethod + def is_supported_video(file_path: str) -> bool: """检查是否为支持的视频格式""" supported_formats = {".mp4", ".avi", ".mov", ".mkv", ".flv", ".wmv", ".m4v", ".3gp", ".webm"} return Path(file_path).suffix.lower() in supported_formats diff --git a/src/common/cache_manager.py b/src/common/cache_manager.py index d8abc241b..ec940ec6c 100644 --- a/src/common/cache_manager.py +++ b/src/common/cache_manager.py @@ -53,7 +53,8 @@ class CacheManager: self._initialized = True logger.info("缓存管理器已初始化: L1 (内存+FAISS), L2 (数据库+ChromaDB)") - def _validate_embedding(self, embedding_result: Any) -> Optional[np.ndarray]: + @staticmethod + def _validate_embedding(embedding_result: Any) -> Optional[np.ndarray]: """ 验证和标准化嵌入向量格式 """ @@ -90,7 +91,8 @@ class CacheManager: logger.error(f"验证嵌入向量时发生错误: {e}") return None - def _generate_key(self, tool_name: str, function_args: Dict[str, Any], tool_file_path: Union[str, Path]) -> str: + @staticmethod + def _generate_key(tool_name: str, function_args: Dict[str, Any], tool_file_path: Union[str, Path]) -> str: """生成确定性的缓存键,包含文件修改时间以实现自动失效。""" try: tool_file_path = Path(tool_file_path) diff --git a/src/common/data_models/info_data_model.py b/src/common/data_models/info_data_model.py index 32893706d..7de787060 100644 --- a/src/common/data_models/info_data_model.py +++ b/src/common/data_models/info_data_model.py @@ -1,10 +1,10 @@ from dataclasses import dataclass, field from typing import Optional, Dict, List, TYPE_CHECKING + from . import BaseDataModel if TYPE_CHECKING: - from .database_data_model import DatabaseMessages - from src.plugin_system.base.component_types import ActionInfo, ChatMode + pass @dataclass diff --git a/src/common/data_models/llm_data_model.py b/src/common/data_models/llm_data_model.py index 1d5b75e0c..cd706bc55 100644 --- a/src/common/data_models/llm_data_model.py +++ b/src/common/data_models/llm_data_model.py @@ -2,8 +2,9 @@ from dataclasses import dataclass from typing import Optional, List, Tuple, TYPE_CHECKING, Any from . import BaseDataModel + if TYPE_CHECKING: - from src.llm_models.payload_content.tool_option import ToolCall + pass @dataclass class LLMGenerationDataModel(BaseDataModel): diff --git a/src/common/data_models/message_data_model.py b/src/common/data_models/message_data_model.py index 8e0b77862..bf08a0d6a 100644 --- a/src/common/data_models/message_data_model.py +++ b/src/common/data_models/message_data_model.py @@ -1,10 +1,10 @@ -from typing import Optional, TYPE_CHECKING from dataclasses import dataclass, field +from typing import Optional, TYPE_CHECKING from . import BaseDataModel if TYPE_CHECKING: - from .database_data_model import DatabaseMessages + pass @dataclass diff --git a/src/common/database/database.py b/src/common/database/database.py index d196df032..3279a67ed 100644 --- a/src/common/database/database.py +++ b/src/common/database/database.py @@ -25,7 +25,8 @@ class DatabaseProxy: self._engine = None self._session = None - def initialize(self, *args, **kwargs): + @staticmethod + def initialize(*args, **kwargs): """初始化数据库连接""" return initialize_database_compat() diff --git a/src/common/database/sqlalchemy_database_api.py b/src/common/database/sqlalchemy_database_api.py index 4f0258c2b..13ef39c1a 100644 --- a/src/common/database/sqlalchemy_database_api.py +++ b/src/common/database/sqlalchemy_database_api.py @@ -4,16 +4,14 @@ 支持自动重连、连接池管理和更好的错误处理 """ -import traceback import time -import asyncio -from typing import Dict, List, Any, Union, Type, Optional -from sqlalchemy.exc import SQLAlchemyError +import traceback +from typing import Dict, List, Any, Union, Optional + from sqlalchemy import desc, asc, func, and_, select -from sqlalchemy.ext.asyncio import AsyncSession -from src.common.logger import get_logger +from sqlalchemy.exc import SQLAlchemyError + from src.common.database.sqlalchemy_models import ( - Base, get_db_session, Messages, ActionRecords, @@ -33,6 +31,7 @@ from src.common.database.sqlalchemy_models import ( MaiZoneScheduleStatus, CacheEntries, ) +from src.common.logger import get_logger logger = get_logger("sqlalchemy_database_api") diff --git a/src/common/database/sqlalchemy_models.py b/src/common/database/sqlalchemy_models.py index e5eacee1f..0c193e358 100644 --- a/src/common/database/sqlalchemy_models.py +++ b/src/common/database/sqlalchemy_models.py @@ -3,17 +3,18 @@ 替换Peewee ORM,使用SQLAlchemy提供更好的连接池管理和错误恢复能力 """ -from sqlalchemy import Column, String, Float, Integer, Boolean, Text, Index, DateTime -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker -from sqlalchemy.orm import Mapped, mapped_column -import os import datetime +import os import time -from typing import Iterator, Optional, Any, Dict, AsyncGenerator -from src.common.logger import get_logger from contextlib import asynccontextmanager -import asyncio +from typing import Optional, Any, Dict, AsyncGenerator + +from sqlalchemy import Column, String, Float, Integer, Boolean, Text, Index, DateTime +from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import Mapped, mapped_column + +from src.common.logger import get_logger logger = get_logger("sqlalchemy_models") diff --git a/src/common/server.py b/src/common/server.py index 3263589a2..a06cf1151 100644 --- a/src/common/server.py +++ b/src/common/server.py @@ -1,10 +1,10 @@ +import os +from typing import Optional + from fastapi import FastAPI, APIRouter from fastapi.middleware.cors import CORSMiddleware # 新增导入 -from typing import Optional -from uvicorn import Config, Server as UvicornServer -from src.config.config import global_config from rich.traceback import install -import os +from uvicorn import Config, Server as UvicornServer install(extra_lines=3) diff --git a/src/config/api_ada_configs.py b/src/config/api_ada_configs.py index 0b9d333c4..5e5e035dd 100644 --- a/src/config/api_ada_configs.py +++ b/src/config/api_ada_configs.py @@ -22,7 +22,6 @@ class APIProvider(ValidatedConfigBase): enable_content_obfuscation: bool = Field(default=False, description="是否启用内容混淆(用于特定场景下的内容处理)") obfuscation_intensity: int = Field(default=1, ge=1, le=3, description="混淆强度(1-3级,数值越高混淆程度越强)") - @field_validator("base_url") @classmethod def validate_base_url(cls, v): """验证base_url,确保URL格式正确""" @@ -30,7 +29,6 @@ class APIProvider(ValidatedConfigBase): raise ValueError("base_url必须以http://或https://开头") return v - @field_validator("api_key") @classmethod def validate_api_key(cls, v): """验证API密钥不能为空""" @@ -75,7 +73,6 @@ class ModelInfo(ValidatedConfigBase): extra_params: Dict[str, Any] = Field(default_factory=dict, description="额外参数(用于API调用时的额外配置)") anti_truncation: bool = Field(default=False, description="是否启用反截断功能,防止模型输出被截断") - @field_validator("price_in", "price_out") @classmethod def validate_prices(cls, v): """验证价格必须为非负数""" @@ -83,7 +80,6 @@ class ModelInfo(ValidatedConfigBase): raise ValueError("价格不能为负数") return v - @field_validator("model_identifier") @classmethod def validate_model_identifier(cls, v): """验证模型标识符不能为空且不能包含特殊字符""" @@ -94,7 +90,6 @@ class ModelInfo(ValidatedConfigBase): raise ValueError("模型标识符不能包含空格或换行符") return v - @field_validator("name") @classmethod def validate_name(cls, v): """验证模型名称不能为空""" @@ -111,7 +106,6 @@ class TaskConfig(ValidatedConfigBase): temperature: float = Field(default=0.7, description="模型温度") concurrency_count: int = Field(default=1, description="并发请求数量") - @field_validator("model_list") @classmethod def validate_model_list(cls, v): """验证模型列表不能为空""" @@ -178,7 +172,6 @@ class APIAdapterConfig(ValidatedConfigBase): self.api_providers_dict = {provider.name: provider for provider in self.api_providers} self.models_dict = {model.name: model for model in self.models} - @field_validator("models") @classmethod def validate_models_list(cls, v): """验证模型列表""" @@ -197,7 +190,6 @@ class APIAdapterConfig(ValidatedConfigBase): return v - @field_validator("api_providers") @classmethod def validate_api_providers_list(cls, v): """验证API提供商列表""" diff --git a/src/config/config.py b/src/config/config.py index 6a28df42e..7f89e0009 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -426,7 +426,6 @@ class APIAdapterConfig(ValidatedConfigBase): self.api_providers_dict = {provider.name: provider for provider in self.api_providers} self.models_dict = {model.name: model for model in self.models} - @field_validator("models") @classmethod def validate_models_list(cls, v): """验证模型列表""" @@ -445,7 +444,6 @@ class APIAdapterConfig(ValidatedConfigBase): return v - @field_validator("api_providers") @classmethod def validate_api_providers_list(cls, v): """验证API提供商列表""" diff --git a/src/config/config_base.py b/src/config/config_base.py index 5d8c7c195..764ec5b71 100644 --- a/src/config/config_base.py +++ b/src/config/config_base.py @@ -50,7 +50,7 @@ class ConfigBase: except Exception as e: raise RuntimeError(f"Failed to convert field '{field_name}' to target type: {e}") from e - return cls(**init_args) + return cls() @classmethod def _convert_field(cls, value: Any, field_type: Type[Any]) -> Any: diff --git a/src/config/official_configs.py b/src/config/official_configs.py index be51a21e3..978c5e47b 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -122,7 +122,8 @@ class ChatConfig(ValidatedConfigBase): global_frequency = self._get_global_frequency() return self.talk_frequency if global_frequency is None else global_frequency - def _get_time_based_frequency(self, time_freq_list: list[str]) -> Optional[float]: + @staticmethod + def _get_time_based_frequency(time_freq_list: list[str]) -> Optional[float]: """ 根据时间配置列表获取当前时段的频率 @@ -201,7 +202,8 @@ class ChatConfig(ValidatedConfigBase): return None - def _parse_stream_config_to_chat_id(self, stream_config_str: str) -> Optional[str]: + @staticmethod + def _parse_stream_config_to_chat_id(stream_config_str: str) -> Optional[str]: """ 解析流配置字符串并生成对应的 chat_id @@ -280,7 +282,8 @@ class ExpressionConfig(ValidatedConfigBase): rules: List[ExpressionRule] = Field(default_factory=list, description="表达学习规则") - def _parse_stream_config_to_chat_id(self, stream_config_str: str) -> Optional[str]: + @staticmethod + def _parse_stream_config_to_chat_id(stream_config_str: str) -> Optional[str]: """ 解析流配置字符串并生成对应的 chat_id diff --git a/src/individuality/individuality.py b/src/individuality/individuality.py index 17a75d235..064ddce4b 100644 --- a/src/individuality/individuality.py +++ b/src/individuality/individuality.py @@ -80,8 +80,9 @@ class Individuality: prompt_personality = f"{personality}\n{identity}" return f"你的名字是{bot_name}{bot_nickname},你{prompt_personality}" + @staticmethod def _get_config_hash( - self, bot_nickname: str, personality_core: str, personality_side: str, identity: str + bot_nickname: str, personality_core: str, personality_side: str, identity: str ) -> tuple[str, str]: """获取personality和identity配置的哈希值 diff --git a/src/llm_models/payload_content/message.py b/src/llm_models/payload_content/message.py index f70c3ded5..4253efab0 100644 --- a/src/llm_models/payload_content/message.py +++ b/src/llm_models/payload_content/message.py @@ -58,7 +58,7 @@ class MessageBuilder: self, image_format: str, image_base64: str, - support_formats: list[str] = SUPPORTED_IMAGE_FORMATS, # 默认支持格式 + support_formats=None, # 默认支持格式 ) -> "MessageBuilder": """ 添加图片内容 @@ -66,6 +66,8 @@ class MessageBuilder: :param image_base64: 图片的base64编码 :return: MessageBuilder对象 """ + if support_formats is None: + support_formats = SUPPORTED_IMAGE_FORMATS if image_format.lower() not in support_formats: raise ValueError("不受支持的图片格式") if not image_base64: diff --git a/src/llm_models/utils.py b/src/llm_models/utils.py index ee20533ee..48f7be5f2 100644 --- a/src/llm_models/utils.py +++ b/src/llm_models/utils.py @@ -145,9 +145,9 @@ class LLMUsageRecorder: LLM使用情况记录器(SQLAlchemy版本) """ + @staticmethod def record_usage_to_database( - self, - model_info: ModelInfo, + model_info: ModelInfo, model_usage: UsageRecord, user_id: str, request_type: str, diff --git a/src/llm_models/utils_model.py b/src/llm_models/utils_model.py index b2d5267e2..f223d1913 100644 --- a/src/llm_models/utils_model.py +++ b/src/llm_models/utils_model.py @@ -627,9 +627,9 @@ class LLMRequest: logger.error(f"任务-'{task_name}' 模型-'{model_name}': 未知异常,错误信息-{str(e)}") return -1, None # 不再重试请求该模型 + @staticmethod def _check_retry( - self, - remain_try: int, + remain_try: int, retry_interval: int, can_retry_msg: str, cannot_retry_msg: str, @@ -747,7 +747,8 @@ class LLMRequest: ) return -1, None - def _build_tool_options(self, tools: Optional[List[Dict[str, Any]]]) -> Optional[List[ToolOption]]: + @staticmethod + def _build_tool_options(tools: Optional[List[Dict[str, Any]]]) -> Optional[List[ToolOption]]: # sourcery skip: extract-method """构建工具选项列表""" if not tools: @@ -811,7 +812,8 @@ class LLMRequest: return final_text - def _inject_random_noise(self, text: str, intensity: int) -> str: + @staticmethod + def _inject_random_noise(text: str, intensity: int) -> str: """在文本中注入随机乱码""" import random import string diff --git a/src/main.py b/src/main.py index ee7a67875..26e4acb49 100644 --- a/src/main.py +++ b/src/main.py @@ -1,37 +1,35 @@ # 再用这个就写一行注释来混提交的我直接全部🌿飞😡 import asyncio -import time import signal import sys +import time + from maim_message import MessageServer - -from src.common.remote import TelemetryHeartBeatTask -from src.manager.async_task_manager import async_task_manager -from src.chat.utils.statistic import OnlineTimeRecordTask, StatisticOutputTask -from src.common.remote import TelemetryHeartBeatTask -from src.chat.emoji_system.emoji_manager import get_emoji_manager -from src.chat.message_receive.chat_stream import get_chat_manager -from src.config.config import global_config -from src.chat.message_receive.bot import chat_bot -from src.common.logger import get_logger -from src.individuality.individuality import get_individuality, Individuality -from src.common.server import get_global_server, Server -from src.mood.mood_manager import mood_manager from rich.traceback import install -from src.schedule.schedule_manager import schedule_manager -from src.schedule.monthly_plan_manager import monthly_plan_manager -from src.plugin_system.core.event_manager import event_manager -from src.plugin_system.base.component_types import EventType -# from src.api.main import start_api_server - -# 导入新的插件管理器和热重载管理器 -from src.plugin_system.core.plugin_manager import plugin_manager -from src.plugin_system.core.plugin_hot_reload import hot_reload_manager +from src.chat.emoji_system.emoji_manager import get_emoji_manager +from src.chat.memory_system.Hippocampus import hippocampus_manager +from src.chat.message_receive.bot import chat_bot +from src.chat.message_receive.chat_stream import get_chat_manager +from src.chat.utils.statistic import OnlineTimeRecordTask, StatisticOutputTask +from src.common.logger import get_logger # 导入消息API和traceback模块 from src.common.message import get_global_api +from src.common.remote import TelemetryHeartBeatTask +from src.common.server import get_global_server, Server +from src.config.config import global_config +from src.individuality.individuality import get_individuality, Individuality +from src.manager.async_task_manager import async_task_manager +from src.mood.mood_manager import mood_manager +from src.plugin_system.base.component_types import EventType +from src.plugin_system.core.event_manager import event_manager +from src.plugin_system.core.plugin_hot_reload import hot_reload_manager +# 导入新的插件管理器和热重载管理器 +from src.plugin_system.core.plugin_manager import plugin_manager +from src.schedule.monthly_plan_manager import monthly_plan_manager +from src.schedule.schedule_manager import schedule_manager -from src.chat.memory_system.Hippocampus import hippocampus_manager +# from src.api.main import start_api_server if not global_config.memory.enable_memory: import src.chat.memory_system.Hippocampus as hippocampus_module @@ -43,7 +41,8 @@ if not global_config.memory.enable_memory: async def initialize_async(self): pass - def get_hippocampus(self): + @staticmethod + def get_hippocampus(): return None async def build_memory(self): @@ -55,9 +54,9 @@ if not global_config.memory.enable_memory: async def consolidate_memory(self): pass + @staticmethod async def get_memory_from_text( - self, - text: str, + text: str, max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3, @@ -65,20 +64,24 @@ if not global_config.memory.enable_memory: ) -> list: return [] + @staticmethod async def get_memory_from_topic( - self, valid_keywords: list[str], max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3 + valid_keywords: list[str], max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3 ) -> list: return [] + @staticmethod async def get_activate_from_text( - self, text: str, max_depth: int = 3, fast_retrieval: bool = False + text: str, max_depth: int = 3, fast_retrieval: bool = False ) -> tuple[float, list[str]]: return 0.0, [] - def get_memory_from_keyword(self, keyword: str, max_depth: int = 2) -> list: + @staticmethod + def get_memory_from_keyword(keyword: str, max_depth: int = 2) -> list: return [] - def get_all_node_names(self) -> list: + @staticmethod + def get_all_node_names() -> list: return [] hippocampus_module.hippocampus_manager = MockHippocampusManager() @@ -114,7 +117,8 @@ class MainSystem: signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) - def _cleanup(self): + @staticmethod + def _cleanup(): """清理资源""" try: # 停止消息重组器 diff --git a/src/mais4u/mais4u_chat/body_emotion_action_manager.py b/src/mais4u/mais4u_chat/body_emotion_action_manager.py index 01d1a0013..a28888136 100644 --- a/src/mais4u/mais4u_chat/body_emotion_action_manager.py +++ b/src/mais4u/mais4u_chat/body_emotion_action_manager.py @@ -156,7 +156,7 @@ class ChatAction: self.regression_count = 0 message_time: float = message.message_info.time # type: ignore - message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive( + message_list_before_now = await get_raw_msg_by_timestamp_with_chat_inclusive( chat_id=self.chat_id, timestamp_start=self.last_change_time, timestamp_end=message_time, @@ -220,7 +220,7 @@ class ChatAction: async def regress_action(self): message_time = time.time() - message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive( + message_list_before_now = await get_raw_msg_by_timestamp_with_chat_inclusive( chat_id=self.chat_id, timestamp_start=self.last_change_time, timestamp_end=message_time, diff --git a/src/mais4u/mais4u_chat/s4u_chat.py b/src/mais4u/mais4u_chat/s4u_chat.py index c6507be56..e081e219f 100644 --- a/src/mais4u/mais4u_chat/s4u_chat.py +++ b/src/mais4u/mais4u_chat/s4u_chat.py @@ -57,7 +57,8 @@ class MessageSenderContainer: """恢复发送。""" self._paused_event.set() - def _calculate_typing_delay(self, text: str) -> float: + @staticmethod + def _calculate_typing_delay(text: str) -> float: """根据文本长度计算模拟打字延迟。""" chars_per_second = s4u_config.chars_per_second min_delay = s4u_config.min_typing_delay @@ -149,6 +150,10 @@ class MessageSenderContainer: if self._task: await self._task + @property + def task(self): + return self._task + class S4UChatManager: def __init__(self): @@ -176,6 +181,7 @@ class S4UChat: def __init__(self, chat_stream: ChatStream): """初始化 S4UChat 实例。""" + self.last_msg_id = self.msg_id self.chat_stream = chat_stream self.stream_id = chat_stream.stream_id self.stream_name = get_chat_manager().get_stream_name(self.stream_id) or self.stream_id @@ -205,7 +211,8 @@ class S4UChat: logger.info(f"[{self.stream_name}] S4UChat with two-queue system initialized.") - def _get_priority_info(self, message: MessageRecv) -> dict: + @staticmethod + def _get_priority_info(message: MessageRecv) -> dict: """安全地从消息中提取和解析 priority_info""" priority_info_raw = message.priority_info priority_info = {} @@ -218,7 +225,8 @@ class S4UChat: priority_info = priority_info_raw return priority_info - def _is_vip(self, priority_info: dict) -> bool: + @staticmethod + def _is_vip(priority_info: dict) -> bool: """检查消息是否来自VIP用户。""" return priority_info.get("message_type") == "vip" @@ -467,7 +475,6 @@ class S4UChat: await asyncio.sleep(1) def get_processing_message_id(self): - self.last_msg_id = self.msg_id self.msg_id = f"{time.time()}_{random.randint(1000, 9999)}" async def _generate_and_send(self, message: MessageRecv): @@ -564,7 +571,7 @@ class S4UChat: # 确保发送器被妥善关闭(即使已关闭,再次调用也是安全的) sender_container.resume() - if not sender_container._task.done(): + if not sender_container.task.done(): await sender_container.close() await sender_container.join() logger.info(f"[{self.stream_name}] _generate_and_send 任务结束,资源已清理。") @@ -585,3 +592,7 @@ class S4UChat: await self._processing_task except asyncio.CancelledError: logger.info(f"处理任务已成功取消: {self.stream_name}") + + @property + def new_message_event(self): + return self._new_message_event diff --git a/src/mais4u/mais4u_chat/s4u_mood_manager.py b/src/mais4u/mais4u_chat/s4u_mood_manager.py index 78821d77c..8e530b3f8 100644 --- a/src/mais4u/mais4u_chat/s4u_mood_manager.py +++ b/src/mais4u/mais4u_chat/s4u_mood_manager.py @@ -124,7 +124,8 @@ class ChatMood: # 发送初始情绪状态到ws端 asyncio.create_task(self.send_emotion_update(self.mood_values)) - def _parse_numerical_mood(self, response: str) -> dict[str, int] | None: + @staticmethod + def _parse_numerical_mood(response: str) -> dict[str, int] | None: try: # The LLM might output markdown with json inside if "```json" in response: diff --git a/src/mais4u/mais4u_chat/s4u_msg_processor.py b/src/mais4u/mais4u_chat/s4u_msg_processor.py index 7bd1fe29e..e3682a450 100644 --- a/src/mais4u/mais4u_chat/s4u_msg_processor.py +++ b/src/mais4u/mais4u_chat/s4u_msg_processor.py @@ -161,7 +161,8 @@ class S4UMessageProcessor: else: logger.info(f"[S4U]{userinfo.user_nickname}:{message.processed_plain_text}") - async def handle_internal_message(self, message: MessageRecvS4U): + @staticmethod + async def handle_internal_message(message: MessageRecvS4U): if message.is_internal: group_info = GroupInfo(platform="amaidesu_default", group_id=660154, group_name="内心") @@ -173,7 +174,7 @@ class S4UMessageProcessor: message.message_info.platform = s4u_chat.chat_stream.platform s4u_chat.internal_message.append(message) - s4u_chat._new_message_event.set() + s4u_chat.new_message_event.set() logger.info( f"[{s4u_chat.stream_name}] 添加内部消息-------------------------------------------------------: {message.processed_plain_text}" @@ -182,20 +183,23 @@ class S4UMessageProcessor: return True return False - async def handle_screen_message(self, message: MessageRecvS4U): + @staticmethod + async def handle_screen_message(message: MessageRecvS4U): if message.is_screen: screen_manager.set_screen(message.screen_info) return True return False - async def hadle_if_voice_done(self, message: MessageRecvS4U): + @staticmethod + async def hadle_if_voice_done(message: MessageRecvS4U): if message.voice_done: s4u_chat = get_s4u_chat_manager().get_or_create_chat(message.chat_stream) s4u_chat.voice_done = message.voice_done return True return False - async def check_if_fake_gift(self, message: MessageRecvS4U) -> bool: + @staticmethod + async def check_if_fake_gift(message: MessageRecvS4U) -> bool: """检查消息是否为假礼物""" if message.is_gift: return False @@ -227,7 +231,8 @@ class S4UMessageProcessor: return True # 非礼物消息,继续正常处理 - async def _handle_context_web_update(self, chat_id: str, message: MessageRecv): + @staticmethod + async def _handle_context_web_update(chat_id: str, message: MessageRecv): """处理上下文网页更新的独立task Args: diff --git a/src/mais4u/mais4u_chat/s4u_prompt.py b/src/mais4u/mais4u_chat/s4u_prompt.py index 2fdbc5682..3dcb4322a 100644 --- a/src/mais4u/mais4u_chat/s4u_prompt.py +++ b/src/mais4u/mais4u_chat/s4u_prompt.py @@ -97,7 +97,8 @@ class PromptBuilder: self.prompt_built = "" self.activate_messages = "" - async def build_expression_habits(self, chat_stream: ChatStream, chat_history, target): + @staticmethod + async def build_expression_habits(chat_stream: ChatStream, chat_history, target): style_habits = [] # 使用从处理器传来的选中表达方式 @@ -125,7 +126,8 @@ class PromptBuilder: return expression_habits_block - async def build_relation_info(self, chat_stream) -> str: + @staticmethod + async def build_relation_info(chat_stream) -> str: is_group_chat = bool(chat_stream.group_info) who_chat_in_group = [] if is_group_chat: @@ -157,7 +159,8 @@ class PromptBuilder: ) return relation_prompt - async def build_memory_block(self, text: str) -> str: + @staticmethod + async def build_memory_block(text: str) -> str: related_memory = await hippocampus_manager.get_memory_from_text( text=text, max_memory_num=2, max_memory_length=2, max_depth=3, fast_retrieval=False ) @@ -169,7 +172,8 @@ class PromptBuilder: return await global_prompt_manager.format_prompt("memory_prompt", memory_info=related_memory_info) return "" - def build_chat_history_prompts(self, chat_stream: ChatStream, message: MessageRecvS4U): + @staticmethod + def build_chat_history_prompts(chat_stream: ChatStream, message: MessageRecvS4U): message_list_before_now = get_raw_msg_before_timestamp_with_chat( chat_id=chat_stream.stream_id, timestamp=time.time(), @@ -260,7 +264,8 @@ class PromptBuilder: return core_msg_str, background_dialogue_prompt, all_dialogue_prompt_str - def build_gift_info(self, message: MessageRecvS4U): + @staticmethod + def build_gift_info(message: MessageRecvS4U): if message.is_gift: return f"这是一条礼物信息,{message.gift_name} x{message.gift_count},请注意这位用户" else: @@ -269,7 +274,8 @@ class PromptBuilder: return "" - def build_sc_info(self, message: MessageRecvS4U): + @staticmethod + def build_sc_info(message: MessageRecvS4U): super_chat_manager = get_super_chat_manager() return super_chat_manager.build_superchat_summary_string(message.chat_stream.stream_id) diff --git a/src/mais4u/mais4u_chat/s4u_stream_generator.py b/src/mais4u/mais4u_chat/s4u_stream_generator.py index cf102a799..2c129bebf 100644 --- a/src/mais4u/mais4u_chat/s4u_stream_generator.py +++ b/src/mais4u/mais4u_chat/s4u_stream_generator.py @@ -49,7 +49,8 @@ class S4UStreamGenerator: self.chat_stream = None - async def build_last_internal_message(self, message: MessageRecvS4U, previous_reply_context: str = ""): + @staticmethod + async def build_last_internal_message(message: MessageRecvS4U, previous_reply_context: str = ""): # person_id = PersonInfoManager.get_person_id( # message.chat_stream.user_info.platform, message.chat_stream.user_info.user_id # ) diff --git a/src/mais4u/mais4u_chat/super_chat_manager.py b/src/mais4u/mais4u_chat/super_chat_manager.py index ef86a6ba1..15adc7d85 100644 --- a/src/mais4u/mais4u_chat/super_chat_manager.py +++ b/src/mais4u/mais4u_chat/super_chat_manager.py @@ -105,7 +105,8 @@ class SuperChatManager: logger.error(f"清理过期SuperChat时出错: {e}", exc_info=True) await asyncio.sleep(60) # 出错时等待更长时间 - def _calculate_expire_time(self, price: float) -> float: + @staticmethod + def _calculate_expire_time(price: float) -> float: """根据SuperChat金额计算过期时间""" current_time = time.time() diff --git a/src/mais4u/s4u_config.py b/src/mais4u/s4u_config.py index 8706b0b8a..ac019a852 100644 --- a/src/mais4u/s4u_config.py +++ b/src/mais4u/s4u_config.py @@ -77,7 +77,7 @@ class S4UConfigBase: except Exception as e: raise RuntimeError(f"Failed to convert field '{field_name}' to target type: {e}") from e - return cls(**init_args) + return cls() @classmethod def _convert_field(cls, value: Any, field_type: Type[Any]) -> Any: diff --git a/src/manager/async_task_manager.py b/src/manager/async_task_manager.py index 0a2c0d215..92f6675bd 100644 --- a/src/manager/async_task_manager.py +++ b/src/manager/async_task_manager.py @@ -1,4 +1,4 @@ -from abc import abstractmethod +from abc import abstractmethod, ABCMeta import asyncio from asyncio import Task, Event, Lock @@ -9,7 +9,7 @@ from src.common.logger import get_logger logger = get_logger("async_task_manager") -class AsyncTask: +class AsyncTask(metaclass=ABCMeta): """异步任务基类""" def __init__(self, task_name: str | None = None, wait_before_start: int = 0, run_interval: int = 0): diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index e1d0d23b1..d311ae491 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -1,16 +1,18 @@ +import copy +import datetime import hashlib -import asyncio -import orjson import time - -from json_repair import repair_json from typing import Any, Callable, Dict, Union, Optional + +import orjson +from json_repair import repair_json from sqlalchemy import select -from src.common.logger import get_logger -from src.common.database.sqlalchemy_models import PersonInfo + from src.common.database.sqlalchemy_database_api import get_db_session -from src.llm_models.utils_model import LLMRequest +from src.common.database.sqlalchemy_models import PersonInfo +from src.common.logger import get_logger from src.config.config import global_config, model_config +from src.llm_models.utils_model import LLMRequest logger = get_logger("person_info") @@ -444,7 +446,8 @@ class PersonInfoManager: logger.error(f"检查用户 {person_id} 是否已知时出错 (SQLAlchemy): {e}") return False - async def get_person_id_by_person_name(self, person_name: str) -> str: + @staticmethod + async def get_person_id_by_person_name(person_name: str) -> str: """根据用户名获取用户ID""" try: # 在需要时获取会话 @@ -516,7 +519,8 @@ class PersonInfoManager: await _db_create_async(final_data) - async def _safe_create_person_info(self, person_id: str, data: Optional[dict] = None): + @staticmethod + async def _safe_create_person_info(person_id: str, data: Optional[dict] = None): """安全地创建用户信息,处理竞态条件""" if not person_id: logger.debug("创建失败,person_id不存在") diff --git a/src/person_info/relationship_builder.py b/src/person_info/relationship_builder.py index 7636494e7..eba8e3bcb 100644 --- a/src/person_info/relationship_builder.py +++ b/src/person_info/relationship_builder.py @@ -3,7 +3,7 @@ import traceback import os import pickle import random -from typing import List, Dict, Any +from typing import List, Dict, Any, Coroutine from src.config.config import global_config from src.common.logger import get_logger from src.person_info.relationship_manager import get_relationship_manager @@ -203,7 +203,7 @@ class RelationshipBuilder: messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.chat_id, start_time, end_time) return len(messages) - def _count_messages_between(self, start_time: float, end_time: float) -> int: + def _count_messages_between(self, start_time: float, end_time: float) -> Coroutine[Any, Any, int]: """计算两个时间点之间的消息数量(不包含边界),用于间隔检查""" return num_new_messages_since(self.chat_id, start_time, end_time) @@ -316,18 +316,12 @@ class RelationshipBuilder: if not self.person_engaged_cache: return f"{self.log_prefix} 关系缓存为空" - status_lines = [f"{self.log_prefix} 关系缓存状态:"] - status_lines.append( - f"最后处理消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}" - ) - status_lines.append( - f"最后清理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_cleanup_time)) if self.last_cleanup_time > 0 else '未执行'}" - ) - status_lines.append(f"总用户数:{len(self.person_engaged_cache)}") - status_lines.append( - f"清理配置:{'启用' if SEGMENT_CLEANUP_CONFIG['enable_cleanup'] else '禁用'} (最大保存{SEGMENT_CLEANUP_CONFIG['max_segment_age_days']}天, 每用户最多{SEGMENT_CLEANUP_CONFIG['max_segments_per_user']}段)" - ) - status_lines.append("") + status_lines = [f"{self.log_prefix} 关系缓存状态:", + f"最后处理消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}", + f"最后清理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_cleanup_time)) if self.last_cleanup_time > 0 else '未执行'}", + f"总用户数:{len(self.person_engaged_cache)}", + f"清理配置:{'启用' if SEGMENT_CLEANUP_CONFIG['enable_cleanup'] else '禁用'} (最大保存{SEGMENT_CLEANUP_CONFIG['max_segment_age_days']}天, 每用户最多{SEGMENT_CLEANUP_CONFIG['max_segments_per_user']}段)", + ""] for person_id, segments in self.person_engaged_cache.items(): total_count = self._get_total_message_count(person_id) diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 9afc859b2..3d658d6c0 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -533,7 +533,8 @@ class RelationshipManager: - def calculate_time_weight(self, point_time: str, current_time: str) -> float: + @staticmethod + def calculate_time_weight(point_time: str, current_time: str) -> float: """计算基于时间的权重系数""" try: point_timestamp = datetime.strptime(point_time, "%Y-%m-%d %H:%M:%S") @@ -557,7 +558,69 @@ class RelationshipManager: logger.error(f"计算时间权重失败: {e}") return 0.5 # 发生错误时返回中等权重 -init_prompt() + @staticmethod + def tfidf_similarity(s1, s2): + """ + 使用 TF-IDF 和余弦相似度计算两个句子的相似性。 + """ + # 确保输入是字符串类型 + if isinstance(s1, list): + s1 = " ".join(str(x) for x in s1) + if isinstance(s2, list): + s2 = " ".join(str(x) for x in s2) + + # 转换为字符串类型 + s1 = str(s1) + s2 = str(s2) + + # 1. 使用 jieba 进行分词 + s1_words = " ".join(jieba.cut(s1)) + s2_words = " ".join(jieba.cut(s2)) + + # 2. 将两句话放入一个列表中 + corpus = [s1_words, s2_words] + + # 3. 创建 TF-IDF 向量化器并进行计算 + try: + vectorizer = TfidfVectorizer() + tfidf_matrix = vectorizer.fit_transform(corpus) + except ValueError: + # 如果句子完全由停用词组成,或者为空,可能会报错 + return 0.0 + + # 4. 计算余弦相似度 + similarity_matrix = cosine_similarity(tfidf_matrix) + + # 返回 s1 和 s2 的相似度 + return similarity_matrix[0, 1] + + @staticmethod + def sequence_similarity(s1, s2): + """ + 使用 SequenceMatcher 计算两个句子的相似性。 + """ + return SequenceMatcher(None, s1, s2).ratio() + + def check_similarity(self, text1, text2, tfidf_threshold=0.5, seq_threshold=0.6): + """ + 使用两种方法检查文本相似度,只要其中一种方法达到阈值就认为是相似的。 + + Args: + text1: 第一个文本 + text2: 第二个文本 + tfidf_threshold: TF-IDF相似度阈值 + seq_threshold: SequenceMatcher相似度阈值 + + Returns: + bool: 如果任一方法达到阈值则返回True + """ + # 计算两种相似度 + tfidf_sim = self.tfidf_similarity(text1, text2) + seq_sim = self.sequence_similarity(text1, text2) + + # 只要其中一种方法达到阈值就认为是相似的 + return tfidf_sim > tfidf_threshold or seq_sim > seq_threshold + relationship_manager = None diff --git a/src/plugin_system/apis/message_api.py b/src/plugin_system/apis/message_api.py index 3d161b847..76dc8f5cb 100644 --- a/src/plugin_system/apis/message_api.py +++ b/src/plugin_system/apis/message_api.py @@ -8,7 +8,7 @@ readable_text = message_api.build_readable_messages(messages) """ -from typing import List, Dict, Any, Tuple, Optional +from typing import List, Dict, Any, Tuple, Optional, Coroutine from src.config.config import global_config import time from src.chat.utils.chat_message_builder import ( @@ -36,7 +36,7 @@ from src.chat.utils.chat_message_builder import ( def get_messages_by_time( start_time: float, end_time: float, limit: int = 0, limit_mode: str = "latest", filter_mai: bool = False -) -> List[Dict[str, Any]]: +) -> Coroutine[Any, Any, list[dict[str, Any]]]: """ 获取指定时间范围内的消息 @@ -155,7 +155,7 @@ def get_messages_by_time_in_chat_for_users( person_ids: List[str], limit: int = 0, limit_mode: str = "latest", -) -> List[Dict[str, Any]]: +) -> Coroutine[Any, Any, list[dict[str, Any]]]: """ 获取指定聊天中指定用户在指定时间范围内的消息 @@ -186,7 +186,7 @@ def get_messages_by_time_in_chat_for_users( def get_random_chat_messages( start_time: float, end_time: float, limit: int = 0, limit_mode: str = "latest", filter_mai: bool = False -) -> List[Dict[str, Any]]: +) -> Coroutine[Any, Any, list[dict[str, Any]]]: """ 随机选择一个聊天,返回该聊天在指定时间范围内的消息 @@ -214,7 +214,7 @@ def get_random_chat_messages( def get_messages_by_time_for_users( start_time: float, end_time: float, person_ids: List[str], limit: int = 0, limit_mode: str = "latest" -) -> List[Dict[str, Any]]: +) -> Coroutine[Any, Any, list[dict[str, Any]]]: """ 获取指定用户在所有聊天中指定时间范围内的消息 @@ -238,7 +238,8 @@ def get_messages_by_time_for_users( return get_raw_msg_by_timestamp_with_users(start_time, end_time, person_ids, limit, limit_mode) -def get_messages_before_time(timestamp: float, limit: int = 0, filter_mai: bool = False) -> List[Dict[str, Any]]: +def get_messages_before_time(timestamp: float, limit: int = 0, filter_mai: bool = False) -> Coroutine[ + Any, Any, list[dict[str, Any]]]: """ 获取指定时间戳之前的消息 @@ -264,7 +265,7 @@ def get_messages_before_time(timestamp: float, limit: int = 0, filter_mai: bool def get_messages_before_time_in_chat( chat_id: str, timestamp: float, limit: int = 0, filter_mai: bool = False -) -> List[Dict[str, Any]]: +) -> Coroutine[Any, Any, list[dict[str, Any]]]: """ 获取指定聊天中指定时间戳之前的消息 @@ -293,7 +294,8 @@ def get_messages_before_time_in_chat( return get_raw_msg_before_timestamp_with_chat(chat_id, timestamp, limit) -def get_messages_before_time_for_users(timestamp: float, person_ids: List[str], limit: int = 0) -> List[Dict[str, Any]]: +def get_messages_before_time_for_users(timestamp: float, person_ids: List[str], limit: int = 0) -> Coroutine[ + Any, Any, list[dict[str, Any]]]: """ 获取指定用户在指定时间戳之前的消息 @@ -317,7 +319,7 @@ def get_messages_before_time_for_users(timestamp: float, person_ids: List[str], def get_recent_messages( chat_id: str, hours: float = 24.0, limit: int = 100, limit_mode: str = "latest", filter_mai: bool = False -) -> List[Dict[str, Any]]: +) -> Coroutine[Any, Any, list[dict[str, Any]]]: """ 获取指定聊天中最近一段时间的消息 @@ -354,7 +356,8 @@ def get_recent_messages( # ============================================================================= -def count_new_messages(chat_id: str, start_time: float = 0.0, end_time: Optional[float] = None) -> int: +def count_new_messages(chat_id: str, start_time: float = 0.0, end_time: Optional[float] = None) -> Coroutine[ + Any, Any, int]: """ 计算指定聊天中从开始时间到结束时间的新消息数量 @@ -378,7 +381,8 @@ def count_new_messages(chat_id: str, start_time: float = 0.0, end_time: Optional return num_new_messages_since(chat_id, start_time, end_time) -def count_new_messages_for_users(chat_id: str, start_time: float, end_time: float, person_ids: List[str]) -> int: +def count_new_messages_for_users(chat_id: str, start_time: float, end_time: float, person_ids: List[str]) -> Coroutine[ + Any, Any, int]: """ 计算指定聊天中指定用户从开始时间到结束时间的新消息数量 @@ -416,7 +420,7 @@ def build_readable_messages_to_str( read_mark: float = 0.0, truncate: bool = False, show_actions: bool = False, -) -> str: +) -> Coroutine[Any, Any, str]: """ 将消息列表构建成可读的字符串 diff --git a/src/plugin_system/apis/permission_api.py b/src/plugin_system/apis/permission_api.py index 94c5c3fdd..f40198352 100644 --- a/src/plugin_system/apis/permission_api.py +++ b/src/plugin_system/apis/permission_api.py @@ -44,7 +44,7 @@ class UserInfo: def to_tuple(self) -> tuple[str, str]: """转换为元组格式""" - return (self.platform, self.user_id) + return self.platform, self.user_id class IPermissionManager(ABC): diff --git a/src/plugin_system/apis/send_api.py b/src/plugin_system/apis/send_api.py index 944933886..359b28b49 100644 --- a/src/plugin_system/apis/send_api.py +++ b/src/plugin_system/apis/send_api.py @@ -118,10 +118,10 @@ async def wait_adapter_response(request_id: str, timeout: float = 30.0) -> dict: response = await asyncio.wait_for(future, timeout=timeout) return response except asyncio.TimeoutError: - _adapter_response_pool.pop(request_id, None) + await _adapter_response_pool.pop(request_id, None) return {"status": "error", "message": "timeout"} except Exception as e: - _adapter_response_pool.pop(request_id, None) + await _adapter_response_pool.pop(request_id, None) return {"status": "error", "message": str(e)} diff --git a/src/plugin_system/base/base_event.py b/src/plugin_system/base/base_event.py index ff33e30cd..c7dd09a58 100644 --- a/src/plugin_system/base/base_event.py +++ b/src/plugin_system/base/base_event.py @@ -1,5 +1,6 @@ import asyncio from typing import List, Dict, Any, Optional + from src.common.logger import get_logger logger = get_logger("base_event") @@ -90,8 +91,6 @@ class BaseEvent: self.allowed_subscribers = allowed_subscribers # 记录事件处理器名 self.allowed_triggers = allowed_triggers # 记录插件名 - from src.plugin_system.base.base_events_handler import BaseEventHandler - self.subscribers: List["BaseEventHandler"] = [] # 订阅该事件的事件处理器列表 self.event_handle_lock = asyncio.Lock() @@ -150,7 +149,8 @@ class BaseEvent: return HandlerResultsCollection(processed_results) - async def _execute_subscriber(self, subscriber, params: dict) -> HandlerResult: + @staticmethod + async def _execute_subscriber(subscriber, params: dict) -> HandlerResult: """执行单个订阅者处理器""" try: return await subscriber.execute(params) diff --git a/src/plugin_system/base/plugin_base.py b/src/plugin_system/base/plugin_base.py index 6cf78b19f..fa5936b81 100644 --- a/src/plugin_system/base/plugin_base.py +++ b/src/plugin_system/base/plugin_base.py @@ -277,7 +277,8 @@ class PluginBase(ABC): return config_version_field.default return "1.0.0" - def _get_current_config_version(self, config: Dict[str, Any]) -> str: + @staticmethod + def _get_current_config_version(config: Dict[str, Any]) -> str: """从配置文件中获取当前版本号""" if "plugin" in config and "config_version" in config["plugin"]: return str(config["plugin"]["config_version"]) diff --git a/src/plugin_system/base/plus_command.py b/src/plugin_system/base/plus_command.py index 0d9780ada..a64866806 100644 --- a/src/plugin_system/base/plus_command.py +++ b/src/plugin_system/base/plus_command.py @@ -149,7 +149,7 @@ class PlusCommand(ABC): Returns: bool: 如果匹配返回True """ - return not self.args.is_empty() or self._is_exact_command_call() + return not self.args.is_empty or self._is_exact_command_call() def _is_exact_command_call(self) -> bool: """检查是否是精确的命令调用(无参数)""" diff --git a/src/plugin_system/core/component_registry.py b/src/plugin_system/core/component_registry.py index 529f327a3..0f64aa9ec 100644 --- a/src/plugin_system/core/component_registry.py +++ b/src/plugin_system/core/component_registry.py @@ -31,6 +31,7 @@ class ComponentRegistry: def __init__(self): # 命名空间式组件名构成法 f"{component_type}.{component_name}" + self._plus_command_registry: Dict[str, Type[PlusCommand]] = {} self._components: Dict[str, ComponentInfo] = {} """组件注册表 命名空间式组件名 -> 组件信息""" self._components_by_type: Dict[ComponentType, Dict[str, ComponentInfo]] = {types: {} for types in ComponentType} @@ -618,7 +619,7 @@ class ComponentRegistry: def get_plus_command_registry(self) -> Dict[str, Type[PlusCommand]]: """获取PlusCommand注册表""" if not hasattr(self, "_plus_command_registry"): - self._plus_command_registry: Dict[str, Type[PlusCommand]] = {} + pass return self._plus_command_registry.copy() def get_registered_plus_command_info(self, command_name: str) -> Optional[PlusCommandInfo]: @@ -667,7 +668,8 @@ class ComponentRegistry: plugin_info = self.get_plugin_info(plugin_name) return plugin_info.components if plugin_info else [] - def get_plugin_config(self, plugin_name: str) -> dict: + @staticmethod + def get_plugin_config(plugin_name: str) -> dict: """获取插件配置 Args: diff --git a/src/plugin_system/core/event_manager.py b/src/plugin_system/core/event_manager.py index f359409af..4108adad0 100644 --- a/src/plugin_system/core/event_manager.py +++ b/src/plugin_system/core/event_manager.py @@ -7,6 +7,7 @@ from typing import Dict, Type, List, Optional, Any, Union from threading import Lock from src.common.logger import get_logger +from src.plugin_system import BaseEventHandler from src.plugin_system.base.base_event import BaseEvent, HandlerResultsCollection from src.plugin_system.base.base_events_handler import BaseEventHandler from src.plugin_system.base.component_types import EventType @@ -198,7 +199,7 @@ class EventManager: """ return self._event_handlers.get(handler_name) - def get_all_event_handlers(self) -> Dict[str, BaseEventHandler]: + def get_all_event_handlers(self) -> dict[str, type[BaseEventHandler]]: """获取所有已注册的事件处理器 Returns: diff --git a/src/plugin_system/core/plugin_hot_reload.py b/src/plugin_system/core/plugin_hot_reload.py index e85010efb..12c87a6ef 100644 --- a/src/plugin_system/core/plugin_hot_reload.py +++ b/src/plugin_system/core/plugin_hot_reload.py @@ -290,7 +290,8 @@ class PluginHotReloadManager: logger.error(f"❌ 重载插件 {plugin_name} 时发生错误: {e}", exc_info=True) return False - def _resolve_plugin_name(self, folder_name: str) -> str: + @staticmethod + def _resolve_plugin_name(folder_name: str) -> str: """ 将文件夹名称解析为实际的插件名称 通过检查插件管理器中的路径映射来找到对应的插件名 @@ -349,7 +350,8 @@ class PluginHotReloadManager: # 出错时尝试简单重载 return self._reload_plugin(plugin_name) - def _force_clear_plugin_modules(self, plugin_name: str): + @staticmethod + def _force_clear_plugin_modules(plugin_name: str): """强制清理插件相关的模块缓存""" # 找到所有相关的模块名 @@ -366,7 +368,8 @@ class PluginHotReloadManager: logger.debug(f"🗑️ 清理模块缓存: {module_name}") del sys.modules[module_name] - def _force_reimport_plugin(self, plugin_name: str): + @staticmethod + def _force_reimport_plugin(plugin_name: str): """强制重新导入插件(委托给插件管理器)""" try: # 使用插件管理器的重载功能 @@ -377,7 +380,8 @@ class PluginHotReloadManager: logger.error(f"❌ 强制重新导入插件 {plugin_name} 时发生错误: {e}", exc_info=True) return False - def _unload_plugin(self, plugin_name: str): + @staticmethod + def _unload_plugin(plugin_name: str): """卸载指定插件""" try: logger.info(f"🗑️ 开始卸载插件: {plugin_name}") @@ -490,7 +494,8 @@ class PluginHotReloadManager: "debounce_delay": self.file_handlers[0].debounce_delay if self.file_handlers else 0, } - def clear_all_caches(self): + @staticmethod + def clear_all_caches(): """清理所有Python模块缓存""" try: logger.info("🧹 开始清理所有Python模块缓存...") diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index 07d33b773..237cb6429 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -67,7 +67,8 @@ class PluginManager: except Exception as e: logger.error(f"同步插件 '{plugin_name}' 配置时发生未知错误: {e}") - def _copy_default_config_to_central(self, plugin_name: str, plugin_config_file: Path, central_config_dir: Path): + @staticmethod + def _copy_default_config_to_central(plugin_name: str, plugin_config_file: Path, central_config_dir: Path): """ 如果中央配置不存在,则将插件的默认 config.toml 复制到中央目录。 """ @@ -96,7 +97,8 @@ class PluginManager: shutil.copy2(central_file, target_plugin_file) logger.info(f"已将中央配置 '{central_file.name}' 同步到插件 '{plugin_name}'") - def _is_file_content_identical(self, file1: Path, file2: Path) -> bool: + @staticmethod + def _is_file_content_identical(file1: Path, file2: Path) -> bool: """ 通过比较 MD5 哈希值检查两个文件的内容是否相同。 """ @@ -403,7 +405,8 @@ class PluginManager: # == 兼容性检查 == - def _check_plugin_version_compatibility(self, plugin_name: str, manifest_data: Dict[str, Any]) -> Tuple[bool, str]: + @staticmethod + def _check_plugin_version_compatibility(plugin_name: str, manifest_data: Dict[str, Any]) -> Tuple[bool, str]: """检查插件版本兼容性 Args: @@ -557,7 +560,8 @@ class PluginManager: else: logger.warning("😕 没有成功加载任何插件") - def _show_plugin_components(self, plugin_name: str) -> None: + @staticmethod + def _show_plugin_components(plugin_name: str) -> None: if plugin_info := component_registry.get_plugin_info(plugin_name): component_types = {} for comp in plugin_info.components: @@ -673,7 +677,8 @@ class PluginManager: """ return self.reload_plugin(plugin_name) - def clear_all_plugin_caches(self): + @staticmethod + def clear_all_plugin_caches(): """清理所有插件相关的模块缓存(简化版)""" try: logger.info("🧹 清理模块缓存...") diff --git a/src/plugin_system/utils/dependency_manager.py b/src/plugin_system/utils/dependency_manager.py index 106748e79..980f538cc 100644 --- a/src/plugin_system/utils/dependency_manager.py +++ b/src/plugin_system/utils/dependency_manager.py @@ -162,7 +162,8 @@ class DependencyManager: return False, all_errors - def _normalize_dependencies(self, dependencies: Any) -> List[PythonDependency]: + @staticmethod + def _normalize_dependencies(dependencies: Any) -> List[PythonDependency]: """将依赖列表标准化为PythonDependency对象""" normalized = [] @@ -191,7 +192,8 @@ class DependencyManager: return normalized - def _check_single_dependency(self, dep: PythonDependency) -> bool: + @staticmethod + def _check_single_dependency(dep: PythonDependency) -> bool: """检查单个依赖是否满足要求""" def _try_check(import_name: str) -> bool: diff --git a/src/plugin_system/utils/manifest_utils.py b/src/plugin_system/utils/manifest_utils.py index 9b19c033e..b714aefd7 100644 --- a/src/plugin_system/utils/manifest_utils.py +++ b/src/plugin_system/utils/manifest_utils.py @@ -82,10 +82,10 @@ class VersionComparator: normalized = VersionComparator.normalize_version(version) try: parts = normalized.split(".") - return (int(parts[0]), int(parts[1]), int(parts[2])) + return int(parts[0]), int(parts[1]), int(parts[2]) except (ValueError, IndexError): logger.warning(f"无法解析版本号: {version},使用默认版本 0.0.0") - return (0, 0, 0) + return 0, 0, 0 @staticmethod def compare_versions(version1: str, version2: str) -> int: diff --git a/src/plugin_system/utils/permission_decorators.py b/src/plugin_system/utils/permission_decorators.py index 67db667fb..67322ba34 100644 --- a/src/plugin_system/utils/permission_decorators.py +++ b/src/plugin_system/utils/permission_decorators.py @@ -58,7 +58,7 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) if chat_stream is None: logger.error(f"权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}") - return + return None # 检查权限 has_permission = permission_api.check_permission( @@ -72,7 +72,7 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) # 对于PlusCommand的execute方法,需要返回适当的元组 if func.__name__ == "execute" and hasattr(args[0], "send_text"): return False, "权限不足", True - return + return None # 权限检查通过,执行原函数 return await func(*args, **kwargs) @@ -90,7 +90,7 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) if chat_stream is None: logger.error(f"权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}") - return + return None # 检查权限 has_permission = permission_api.check_permission( @@ -101,7 +101,7 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) logger.warning( f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 没有权限 {permission_node}" ) - return + return None # 权限检查通过,执行原函数 return func(*args, **kwargs) @@ -156,7 +156,7 @@ def require_master(deny_message: Optional[str] = None): if chat_stream is None: logger.error(f"Master权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}") - return + return None # 检查是否为Master用户 is_master = permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id) @@ -166,7 +166,7 @@ def require_master(deny_message: Optional[str] = None): await text_to_stream(message, chat_stream.stream_id) if func.__name__ == "execute" and hasattr(args[0], "send_text"): return False, "需要Master权限", True - return + return None # 权限检查通过,执行原函数 return await func(*args, **kwargs) @@ -184,14 +184,14 @@ def require_master(deny_message: Optional[str] = None): if chat_stream is None: logger.error(f"Master权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}") - return + return None # 检查是否为Master用户 is_master = permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id) if not is_master: logger.warning(f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 不是Master用户") - return + return None # 权限检查通过,执行原函数 return func(*args, **kwargs) diff --git a/src/plugins/built_in/maizone_refactored/services/content_service.py b/src/plugins/built_in/maizone_refactored/services/content_service.py index 27f2a0ee9..9f7da7ccf 100644 --- a/src/plugins/built_in/maizone_refactored/services/content_service.py +++ b/src/plugins/built_in/maizone_refactored/services/content_service.py @@ -119,10 +119,12 @@ class ContentService: logger.error(f"生成说说内容时发生异常: {e}") return "" - async def generate_comment(self, content: str, target_name: str, rt_con: str = "", images: list = []) -> str: + async def generate_comment(self, content: str, target_name: str, rt_con: str = "", images=None) -> str: """ 针对一条具体的说说内容生成评论。 """ + if images is None: + images = [] for i in range(3): # 重试3次 try: chat_manager = get_chat_manager() @@ -180,7 +182,8 @@ class ContentService: return "" return "" - async def generate_comment_reply(self, story_content: str, comment_content: str, commenter_name: str) -> str: + @staticmethod + async def generate_comment_reply(story_content: str, comment_content: str, commenter_name: str) -> str: """ 针对自己说说的评论,生成回复。 """ diff --git a/src/plugins/built_in/maizone_refactored/services/cookie_service.py b/src/plugins/built_in/maizone_refactored/services/cookie_service.py index b4aedf322..1c61a29fd 100644 --- a/src/plugins/built_in/maizone_refactored/services/cookie_service.py +++ b/src/plugins/built_in/maizone_refactored/services/cookie_service.py @@ -50,7 +50,8 @@ class CookieService: logger.error(f"无法读取或解析Cookie文件 {cookie_file_path}: {e}") return None - async def _get_cookies_from_adapter(self, stream_id: Optional[str]) -> Optional[Dict[str, str]]: + @staticmethod + async def _get_cookies_from_adapter(stream_id: Optional[str]) -> Optional[Dict[str, str]]: """通过Adapter API获取Cookie""" try: params = {"domain": "user.qzone.qq.com"} diff --git a/src/plugins/built_in/maizone_refactored/services/image_service.py b/src/plugins/built_in/maizone_refactored/services/image_service.py index cbb411da7..1ffcd7d70 100644 --- a/src/plugins/built_in/maizone_refactored/services/image_service.py +++ b/src/plugins/built_in/maizone_refactored/services/image_service.py @@ -59,7 +59,8 @@ class ImageService: logger.error(f"处理AI配图时发生异常: {e}") return False - async def _call_siliconflow_api(self, api_key: str, story: str, image_dir: str, batch_size: int) -> bool: + @staticmethod + async def _call_siliconflow_api(api_key: str, story: str, image_dir: str, batch_size: int) -> bool: """ 调用硅基流动(SiliconFlow)的API来生成图片。 diff --git a/src/plugins/built_in/maizone_refactored/services/qzone_service.py b/src/plugins/built_in/maizone_refactored/services/qzone_service.py index abb5f97e6..545e615a0 100644 --- a/src/plugins/built_in/maizone_refactored/services/qzone_service.py +++ b/src/plugins/built_in/maizone_refactored/services/qzone_service.py @@ -187,7 +187,8 @@ class QZoneService: # --- Internal Helper Methods --- - async def _get_intercom_context(self, stream_id: str) -> Optional[str]: + @staticmethod + async def _get_intercom_context(stream_id: str) -> Optional[str]: """ 根据 stream_id 查找其所属的互通组,并构建该组的聊天上下文。 @@ -398,7 +399,8 @@ class QZoneService: logger.error(f"加载本地图片失败: {e}") return [] - def _generate_gtk(self, skey: str) -> str: + @staticmethod + def _generate_gtk(skey: str) -> str: hash_val = 5381 for char in skey: hash_val += (hash_val << 5) + ord(char) @@ -435,7 +437,8 @@ class QZoneService: logger.error(f"更新或加载Cookie时发生异常: {e}") return None - async def _fetch_cookies_http(self, host: str, port: str, napcat_token: str) -> Optional[Dict]: + @staticmethod + async def _fetch_cookies_http(host: str, port: str, napcat_token: str) -> Optional[Dict]: """通过HTTP服务器获取Cookie""" url = f"http://{host}:{port}/get_cookies" max_retries = 5 diff --git a/src/plugins/built_in/maizone_refactored/services/reply_tracker_service.py b/src/plugins/built_in/maizone_refactored/services/reply_tracker_service.py index 0fa7edb99..3aabc88b6 100644 --- a/src/plugins/built_in/maizone_refactored/services/reply_tracker_service.py +++ b/src/plugins/built_in/maizone_refactored/services/reply_tracker_service.py @@ -36,7 +36,8 @@ class ReplyTrackerService: self._load_data() logger.debug(f"ReplyTrackerService initialized with data file: {self.reply_record_file}") - def _validate_data(self, data: Any) -> bool: + @staticmethod + def _validate_data(data: Any) -> bool: """验证加载的数据格式是否正确""" if not isinstance(data, dict): logger.error("加载的数据不是字典格式") diff --git a/src/plugins/built_in/maizone_refactored/services/scheduler_service.py b/src/plugins/built_in/maizone_refactored/services/scheduler_service.py index ed32da48d..69ec0956e 100644 --- a/src/plugins/built_in/maizone_refactored/services/scheduler_service.py +++ b/src/plugins/built_in/maizone_refactored/services/scheduler_service.py @@ -129,7 +129,8 @@ class SchedulerService: logger.error(f"定时任务循环中发生未知错误: {e}\n{traceback.format_exc()}") await asyncio.sleep(300) # 发生错误后,等待一段时间再重试 - async def _is_processed(self, hour_str: str, activity: str) -> bool: + @staticmethod + async def _is_processed(hour_str: str, activity: str) -> bool: """ 检查指定的任务(某个小时的某个活动)是否已经被成功处理过。 @@ -152,7 +153,8 @@ class SchedulerService: logger.error(f"检查日程处理状态时发生数据库错误: {e}") return False # 数据库异常时,默认为未处理,允许重试 - async def _mark_as_processed(self, hour_str: str, activity: str, success: bool, content: str): + @staticmethod + async def _mark_as_processed(hour_str: str, activity: str, success: bool, content: str): """ 将任务的处理状态和结果写入数据库。 diff --git a/src/plugins/built_in/maizone_refactored/utils/history_utils.py b/src/plugins/built_in/maizone_refactored/utils/history_utils.py index 19b3e7baa..171396de2 100644 --- a/src/plugins/built_in/maizone_refactored/utils/history_utils.py +++ b/src/plugins/built_in/maizone_refactored/utils/history_utils.py @@ -49,7 +49,8 @@ class _SimpleQZoneAPI: if p_skey: self.gtk2 = self._generate_gtk(p_skey) - def _generate_gtk(self, skey: str) -> str: + @staticmethod + def _generate_gtk(skey: str) -> str: hash_val = 5381 for char in skey: hash_val += (hash_val << 5) + ord(char) diff --git a/src/plugins/built_in/napcat_adapter_plugin/plugin.py b/src/plugins/built_in/napcat_adapter_plugin/plugin.py index e15e17b8f..3cd7973e7 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/plugin.py +++ b/src/plugins/built_in/napcat_adapter_plugin/plugin.py @@ -400,9 +400,8 @@ class NapcatAdapterPlugin(BasePlugin): def get_plugin_components(self): self.register_events() - components = [] - components.append((LauchNapcatAdapterHandler.get_handler_info(), LauchNapcatAdapterHandler)) - components.append((StopNapcatAdapterHandler.get_handler_info(), StopNapcatAdapterHandler)) + components = [(LauchNapcatAdapterHandler.get_handler_info(), LauchNapcatAdapterHandler), + (StopNapcatAdapterHandler.get_handler_info(), StopNapcatAdapterHandler)] for handler in get_classes_in_module(event_handlers): if issubclass(handler, BaseEventHandler): components.append((handler.get_handler_info(), handler)) diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/message_buffer.py b/src/plugins/built_in/napcat_adapter_plugin/src/message_buffer.py index 64a1e3faa..73216942e 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/message_buffer.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/message_buffer.py @@ -49,7 +49,8 @@ class SimpleMessageBuffer: """设置插件配置""" self.plugin_config = plugin_config - def get_session_id(self, event_data: Dict[str, Any]) -> str: + @staticmethod + def get_session_id(event_data: Dict[str, Any]) -> str: """根据事件数据生成会话ID""" message_type = event_data.get("message_type", "unknown") user_id = event_data.get("user_id", "unknown") @@ -62,7 +63,8 @@ class SimpleMessageBuffer: else: return f"{message_type}_{user_id}" - def extract_text_from_message(self, message: List[Dict[str, Any]]) -> Optional[str]: + @staticmethod + def extract_text_from_message(message: List[Dict[str, Any]]) -> Optional[str]: """从OneBot消息中提取纯文本,如果包含非文本内容则返回None""" text_parts = [] has_non_text = False @@ -177,7 +179,8 @@ class SimpleMessageBuffer: logger.debug(f"文本消息已添加到缓冲器 {session_id}: {text[:50]}...") return True - async def _cancel_session_timers(self, session: BufferedSession): + @staticmethod + async def _cancel_session_timers(session: BufferedSession): """取消会话的所有定时器""" for task_name in ["timer_task", "delay_task"]: task = getattr(session, task_name) diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/message_chunker.py b/src/plugins/built_in/napcat_adapter_plugin/src/message_chunker.py index 0f25bd62e..9757e7cf5 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/message_chunker.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/message_chunker.py @@ -112,7 +112,8 @@ class MessageChunker: else: return [{"_original_message": message}] - def is_chunk_message(self, message: Union[str, Dict[str, Any]]) -> bool: + @staticmethod + def is_chunk_message(message: Union[str, Dict[str, Any]]) -> bool: """判断是否是切片消息""" try: if isinstance(message, str): diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py index f289af687..c50f17e7b 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py @@ -26,7 +26,7 @@ import json import websockets as Server import base64 from pathlib import Path -from typing import List, Tuple, Optional, Dict, Any +from typing import List, Tuple, Optional, Dict, Any, Coroutine import uuid from maim_message import ( @@ -351,6 +351,7 @@ class MessageHandler: logger.debug("发送到Maibot处理信息") await message_send_instance.message_send(message_base) + return None async def handle_real_message(self, raw_message: dict, in_reply: bool = False) -> List[Seg] | None: # sourcery skip: low-code-quality @@ -518,7 +519,8 @@ class MessageHandler: logger.debug(f"handle_real_message完成,处理了{len(real_message)}个消息段,生成了{len(seg_message)}个seg") return seg_message - async def handle_text_message(self, raw_message: dict) -> Seg: + @staticmethod + async def handle_text_message(raw_message: dict) -> Seg: """ 处理纯文本信息 Parameters: @@ -530,7 +532,8 @@ class MessageHandler: plain_text: str = message_data.get("text") return Seg(type="text", data=plain_text) - async def handle_face_message(self, raw_message: dict) -> Seg | None: + @staticmethod + async def handle_face_message(raw_message: dict) -> Seg | None: """ 处理表情消息 Parameters: @@ -547,7 +550,8 @@ class MessageHandler: logger.warning(f"不支持的表情:{face_raw_id}") return None - async def handle_image_message(self, raw_message: dict) -> Seg | None: + @staticmethod + async def handle_image_message(raw_message: dict) -> Seg | None: """ 处理图片消息与表情包消息 Parameters: @@ -603,6 +607,7 @@ class MessageHandler: return Seg(type="at", data=f"{member_info.get('nickname')}:{member_info.get('user_id')}") else: return None + return None async def handle_record_message(self, raw_message: dict) -> Seg | None: """ @@ -631,7 +636,8 @@ class MessageHandler: return None return Seg(type="voice", data=audio_base64) - async def handle_video_message(self, raw_message: dict) -> Seg | None: + @staticmethod + async def handle_video_message(raw_message: dict) -> Seg | None: """ 处理视频消息 Parameters: @@ -762,7 +768,7 @@ class MessageHandler: return None processed_message: Seg - if image_count < 5 and image_count > 0: + if 5 > image_count > 0: # 处理图片数量小于5的情况,此时解析图片为base64 logger.debug("图片数量小于5,开始解析图片为base64") processed_message = await self._recursive_parse_image_seg(handled_message, True) @@ -779,15 +785,18 @@ class MessageHandler: forward_hint = Seg(type="text", data="这是一条转发消息:\n") return Seg(type="seglist", data=[forward_hint, processed_message]) - async def handle_dice_message(self, raw_message: dict) -> Seg: + @staticmethod + async def handle_dice_message(raw_message: dict) -> Seg: message_data: dict = raw_message.get("data", {}) res = message_data.get("result", "") return Seg(type="text", data=f"[扔了一个骰子,点数是{res}]") - async def handle_shake_message(self, raw_message: dict) -> Seg: + @staticmethod + async def handle_shake_message(raw_message: dict) -> Seg: return Seg(type="text", data="[向你发送了窗口抖动,现在你的屏幕猛烈地震了一下!]") - async def handle_json_message(self, raw_message: dict) -> Seg: + @staticmethod + async def handle_json_message(raw_message: dict) -> Seg | None: """ 处理JSON消息 Parameters: @@ -906,7 +915,8 @@ class MessageHandler: logger.error(f"处理JSON消息时出错: {e}") return None - async def handle_rps_message(self, raw_message: dict) -> Seg: + @staticmethod + async def handle_rps_message(raw_message: dict) -> Seg: message_data: dict = raw_message.get("data", {}) res = message_data.get("result", "") if res == "1": @@ -1089,7 +1099,8 @@ class MessageHandler: return None return response_data.get("messages") - async def _send_buffered_message(self, session_id: str, merged_text: str, original_event: Dict[str, Any]): + @staticmethod + async def _send_buffered_message(session_id: str, merged_text: str, original_event: Dict[str, Any]): """发送缓冲的合并消息""" try: # 从原始事件数据中提取信息 diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/meta_event_handler.py b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/meta_event_handler.py index 217347c36..83d19a1d7 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/meta_event_handler.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/meta_event_handler.py @@ -14,6 +14,7 @@ class MetaEventHandler: """ def __init__(self): + self.last_heart_beat = time.time() self.interval = 5.0 # 默认值,稍后通过set_plugin_config设置 self._interval_checking = False self.plugin_config = None @@ -37,7 +38,6 @@ class MetaEventHandler: if message["status"].get("online") and message["status"].get("good"): if not self._interval_checking: asyncio.create_task(self.check_heartbeat()) - self.last_heart_beat = time.time() self.interval = message.get("interval") / 1000 else: self_id = message.get("self_id") diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/notice_handler.py b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/notice_handler.py index c373a9a10..a9eaead16 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/notice_handler.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/notice_handler.py @@ -197,9 +197,11 @@ class NoticeHandler: if system_notice: await self.put_notice(message_base) + return None else: logger.debug("发送到Maibot处理通知信息") await message_send_instance.message_send(message_base) + return None async def handle_poke_notify( self, raw_message: dict, group_id: int, user_id: int @@ -464,7 +466,8 @@ class NoticeHandler: ) return seg_data, operator_info - async def put_notice(self, message_base: MessageBase) -> None: + @staticmethod + async def put_notice(message_base: MessageBase) -> None: """ 将处理后的通知消息放入通知队列 """ @@ -577,7 +580,8 @@ class NoticeHandler: self.banned_list.remove(ban_record) await asyncio.sleep(5) - async def send_notice(self) -> None: + @staticmethod + async def send_notice() -> None: """ 发送通知消息到Napcat """ diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/send_handler.py b/src/plugins/built_in/napcat_adapter_plugin/src/send_handler.py index 5d6d91467..ef380c82f 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/send_handler.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/send_handler.py @@ -76,7 +76,7 @@ class SendHandler: processed_message = await self.handle_seg_recursive(message_segment, user_info) except Exception as e: logger.error(f"处理消息时发生错误: {e}") - return + return None if not processed_message: logger.critical("现在暂时不支持解析此回复!") @@ -94,7 +94,7 @@ class SendHandler: id_name = "user_id" else: logger.error("无法识别的消息类型") - return + return None logger.info("尝试发送到napcat") response = await self.send_message_to_napcat( action, @@ -107,8 +107,10 @@ class SendHandler: logger.info("消息发送成功") qq_message_id = response.get("data", {}).get("message_id") await self.message_sent_back(raw_message_base, qq_message_id) + return None else: logger.warning(f"消息发送失败,napcat返回:{str(response)}") + return None async def send_command(self, raw_message_base: MessageBase) -> None: """ @@ -146,7 +148,7 @@ class SendHandler: command, args_dict = self.handle_send_like_command(args) case _: logger.error(f"未知命令: {command_name}") - return + return None except Exception as e: logger.error(f"处理命令时发生错误: {e}") return None @@ -158,8 +160,10 @@ class SendHandler: response = await self.send_message_to_napcat(command, args_dict) if response.get("status") == "ok": logger.info(f"命令 {command_name} 执行成功") + return None else: logger.warning(f"命令 {command_name} 执行失败,napcat返回:{str(response)}") + return None async def handle_adapter_command(self, raw_message_base: MessageBase) -> None: """ @@ -265,7 +269,8 @@ class SendHandler: new_payload = self.build_payload(payload, self.handle_file_message(file_path), False) return new_payload - def build_payload(self, payload: list, addon: dict | list, is_reply: bool = False) -> list: + @staticmethod + def build_payload(payload: list, addon: dict | list, is_reply: bool = False) -> list: # sourcery skip: for-append-to-extend, merge-list-append, simplify-generator """构建发送的消息体""" if is_reply: @@ -324,11 +329,13 @@ class SendHandler: return reply_seg - def handle_text_message(self, message: str) -> dict: + @staticmethod + def handle_text_message(message: str) -> dict: """处理文本消息""" return {"type": "text", "data": {"text": message}} - def handle_image_message(self, encoded_image: str) -> dict: + @staticmethod + def handle_image_message(encoded_image: str) -> dict: """处理图片消息""" return { "type": "image", @@ -338,7 +345,8 @@ class SendHandler: }, } # base64 编码的图片 - def handle_emoji_message(self, encoded_emoji: str) -> dict: + @staticmethod + def handle_emoji_message(encoded_emoji: str) -> dict: """处理表情消息""" encoded_image = encoded_emoji image_format = get_image_format(encoded_emoji) @@ -369,39 +377,45 @@ class SendHandler: "data": {"file": f"base64://{encoded_voice}"}, } - def handle_voiceurl_message(self, voice_url: str) -> dict: + @staticmethod + def handle_voiceurl_message(voice_url: str) -> dict: """处理语音链接消息""" return { "type": "record", "data": {"file": voice_url}, } - def handle_music_message(self, song_id: str) -> dict: + @staticmethod + def handle_music_message(song_id: str) -> dict: """处理音乐消息""" return { "type": "music", "data": {"type": "163", "id": song_id}, } - def handle_videourl_message(self, video_url: str) -> dict: + @staticmethod + def handle_videourl_message(video_url: str) -> dict: """处理视频链接消息""" return { "type": "video", "data": {"file": video_url}, } - def handle_file_message(self, file_path: str) -> dict: + @staticmethod + def handle_file_message(file_path: str) -> dict: """处理文件消息""" return { "type": "file", "data": {"file": f"file://{file_path}"}, } - def delete_msg_command(self, args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def delete_msg_command(args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: """处理删除消息命令""" return "delete_msg", {"message_id": args["message_id"]} - def handle_ban_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_ban_command(args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: """处理封禁命令 Args: @@ -429,7 +443,8 @@ class SendHandler: }, ) - def handle_whole_ban_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_whole_ban_command(args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: """处理全体禁言命令 Args: @@ -452,7 +467,8 @@ class SendHandler: }, ) - def handle_kick_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_kick_command(args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: """处理群成员踢出命令 Args: @@ -477,7 +493,8 @@ class SendHandler: }, ) - def handle_poke_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_poke_command(args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: """处理戳一戳命令 Args: @@ -504,7 +521,8 @@ class SendHandler: }, ) - def handle_set_emoji_like_command(self, args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_set_emoji_like_command(args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: """处理设置表情回应命令 Args: @@ -526,7 +544,8 @@ class SendHandler: {"message_id": message_id, "emoji_id": emoji_id, "set": set_like}, ) - def handle_send_like_command(self, args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_send_like_command(args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: """ 处理发送点赞命令的逻辑。 @@ -547,7 +566,8 @@ class SendHandler: {"user_id": user_id, "times": times}, ) - def handle_ai_voice_send_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_ai_voice_send_command(args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: """ 处理AI语音发送命令的逻辑。 并返回 NapCat 兼容的 (action, params) 元组。 @@ -594,7 +614,8 @@ class SendHandler: return {"status": "error", "message": str(e)} return response - async def message_sent_back(self, message_base: MessageBase, qq_message_id: str) -> None: + @staticmethod + async def message_sent_back(message_base: MessageBase, qq_message_id: str) -> None: # 修改 additional_config,添加 echo 字段 if message_base.message_info.additional_config is None: message_base.message_info.additional_config = {} @@ -612,8 +633,9 @@ class SendHandler: logger.debug("已回送消息ID") return + @staticmethod async def send_adapter_command_response( - self, original_message: MessageBase, response_data: dict, request_id: str + original_message: MessageBase, response_data: dict, request_id: str ) -> None: """ 发送适配器命令响应回MaiBot @@ -642,7 +664,8 @@ class SendHandler: except Exception as e: logger.error(f"发送适配器命令响应时出错: {e}") - def handle_at_message_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: + @staticmethod + def handle_at_message_command(args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]: """处理艾特并发送消息命令 Args: diff --git a/src/plugins/built_in/permission_management/plugin.py b/src/plugins/built_in/permission_management/plugin.py index 174482d47..e33a6d08f 100644 --- a/src/plugins/built_in/permission_management/plugin.py +++ b/src/plugins/built_in/permission_management/plugin.py @@ -111,7 +111,8 @@ class PermissionCommand(PlusCommand): await self.send_text(help_text) - def _parse_user_mention(self, mention: str) -> Optional[str]: + @staticmethod + def _parse_user_mention(mention: str) -> Optional[str]: """解析用户提及,提取QQ号 支持的格式: diff --git a/src/plugins/built_in/plugin_management/plugin.py b/src/plugins/built_in/plugin_management/plugin.py index cd4d753c6..741cb38b9 100644 --- a/src/plugins/built_in/plugin_management/plugin.py +++ b/src/plugins/built_in/plugin_management/plugin.py @@ -34,11 +34,11 @@ class ManagementCommand(PlusCommand): @require_permission("plugin.management.admin", "❌ 你没有插件管理的权限") async def execute(self, args: CommandArgs) -> Tuple[bool, str, bool]: """执行插件管理命令""" - if args.is_empty(): + if args.is_empty: await self._show_help("all") return True, "显示帮助信息", True - subcommand = args.get_first().lower() + subcommand = args.get_first.lower() remaining_args = args.get_args()[1:] # 获取除第一个参数外的所有参数 if subcommand in ["plugin", "插件"]: @@ -318,7 +318,8 @@ class ManagementCommand(PlusCommand): else: await self.send_text(f"❌ 插件目录添加失败: `{dir_path}`") - def _fetch_all_registered_components(self) -> List[ComponentInfo]: + @staticmethod + def _fetch_all_registered_components() -> List[ComponentInfo]: all_plugin_info = component_manage_api.get_all_plugin_info() if not all_plugin_info: return [] diff --git a/src/plugins/built_in/reminder_plugin/plugin.py b/src/plugins/built_in/reminder_plugin/plugin.py index 55fd8b85d..5382cccff 100644 --- a/src/plugins/built_in/reminder_plugin/plugin.py +++ b/src/plugins/built_in/reminder_plugin/plugin.py @@ -1,6 +1,7 @@ import asyncio from datetime import datetime from typing import List, Tuple, Type, Optional + from dateutil.parser import parse as parse_datetime from src.common.logger import get_logger @@ -14,7 +15,7 @@ from src.plugin_system import ( ActionActivationType, ) from src.plugin_system.apis import send_api, llm_api, generator_api -from src.plugin_system.base.component_types import ChatType, ComponentType +from src.plugin_system.base.component_types import ComponentType logger = get_logger(__name__) diff --git a/src/plugins/built_in/tts_plugin/plugin.py b/src/plugins/built_in/tts_plugin/plugin.py index 30748a9ff..fc625c093 100644 --- a/src/plugins/built_in/tts_plugin/plugin.py +++ b/src/plugins/built_in/tts_plugin/plugin.py @@ -74,7 +74,8 @@ class TTSAction(BaseAction): logger.error(f"{self.log_prefix} 执行TTS动作时出错: {e}") return False, f"执行TTS动作时出错: {e}" - def _process_text_for_tts(self, text: str) -> str: + @staticmethod + def _process_text_for_tts(text: str) -> str: """ 处理文本使其更适合TTS使用 - 移除不必要的特殊字符和表情符号 diff --git a/src/plugins/built_in/web_search_tool/engines/bing_engine.py b/src/plugins/built_in/web_search_tool/engines/bing_engine.py index ac90956e0..6d32492ad 100644 --- a/src/plugins/built_in/web_search_tool/engines/bing_engine.py +++ b/src/plugins/built_in/web_search_tool/engines/bing_engine.py @@ -111,7 +111,8 @@ class BingSearchEngine(BaseSearchEngine): logger.debug(f"Bing搜索 [{keyword}] 完成,总共 {len(list_result)} 个结果") return list_result[:num_results] if len(list_result) > num_results else list_result - def _parse_html(self, url: str) -> List[Dict[str, Any]]: + @staticmethod + def _parse_html(url: str) -> List[Dict[str, Any]]: """解析处理结果""" try: logger.debug(f"访问Bing搜索URL: {url}") diff --git a/src/plugins/built_in/web_search_tool/tools/url_parser.py b/src/plugins/built_in/web_search_tool/tools/url_parser.py index 315e06271..3a05423a7 100644 --- a/src/plugins/built_in/web_search_tool/tools/url_parser.py +++ b/src/plugins/built_in/web_search_tool/tools/url_parser.py @@ -89,7 +89,7 @@ class URLParserTool(BaseTool): title = soup.title.string if soup.title else "无标题" for script in soup(["script", "style"]): script.extract() - text = soup.get_text(separator="\n", strip=True) + text = soup.get_text(strip=True) if not text: return {"error": "无法从页面提取有效文本内容。"} diff --git a/src/schedule/llm_generator.py b/src/schedule/llm_generator.py index 9dda68f80..35e35b0b5 100644 --- a/src/schedule/llm_generator.py +++ b/src/schedule/llm_generator.py @@ -125,7 +125,8 @@ class ScheduleLLMGenerator: logger.info("继续重试...") await asyncio.sleep(3) - def _validate_schedule_with_pydantic(self, schedule_data) -> bool: + @staticmethod + def _validate_schedule_with_pydantic(schedule_data) -> bool: try: ScheduleData(schedule=schedule_data) logger.info("日程数据Pydantic验证通过") @@ -204,7 +205,8 @@ class MonthlyPlanLLMGenerator: logger.error(" 所有尝试都失败,无法生成月度计划") return [] - def _parse_plans_response(self, response: str) -> List[str]: + @staticmethod + def _parse_plans_response(response: str) -> List[str]: try: response = response.strip() lines = [line.strip() for line in response.split("\n") if line.strip()] diff --git a/src/schedule/plan_manager.py b/src/schedule/plan_manager.py index b84a37b72..82f8a8e04 100644 --- a/src/schedule/plan_manager.py +++ b/src/schedule/plan_manager.py @@ -80,7 +80,8 @@ class PlanManager: finally: self.generation_running = False - def _get_previous_month(self, current_month: str) -> str: + @staticmethod + def _get_previous_month(current_month: str) -> str: try: year, month = map(int, current_month.split("-")) if month == 1: @@ -90,7 +91,8 @@ class PlanManager: except Exception: return "1900-01" - async def archive_current_month_plans(self, target_month: Optional[str] = None): + @staticmethod + async def archive_current_month_plans(target_month: Optional[str] = None): try: if target_month is None: target_month = datetime.now().strftime("%Y-%m") @@ -100,6 +102,7 @@ class PlanManager: except Exception as e: logger.error(f" 归档 {target_month} 月度计划时发生错误: {e}") - async def get_plans_for_schedule(self, month: str, max_count: int) -> List: + @staticmethod + async def get_plans_for_schedule(month: str, max_count: int) -> List: avoid_days = global_config.planning_system.avoid_repetition_days return await get_smart_plans_for_daily_schedule(month, max_count=max_count, avoid_days=avoid_days) \ No newline at end of file diff --git a/src/schedule/schedule_manager.py b/src/schedule/schedule_manager.py index 822131dec..115480381 100644 --- a/src/schedule/schedule_manager.py +++ b/src/schedule/schedule_manager.py @@ -3,6 +3,8 @@ import asyncio from datetime import datetime, time, timedelta from typing import Optional, List, Dict, Any +from sqlalchemy import select + from src.common.database.sqlalchemy_models import Schedule, get_db_session from src.config.config import global_config from src.common.logger import get_logger @@ -115,7 +117,8 @@ class ScheduleManager: self.schedule_generation_running = False logger.info("日程生成任务结束") - async def _save_schedule_to_db(self, date_str: str, schedule_data: List[Dict[str, Any]]): + @staticmethod + async def _save_schedule_to_db(date_str: str, schedule_data: List[Dict[str, Any]]): async with get_db_session() as session: schedule_json = orjson.dumps(schedule_data).decode("utf-8") result = await session.execute(select(Schedule).filter(Schedule.date == date_str)) @@ -128,7 +131,8 @@ class ScheduleManager: session.add(new_schedule) await session.commit() - def _log_generated_schedule(self, date_str: str, schedule_data: List[Dict[str, Any]]): + @staticmethod + def _log_generated_schedule(date_str: str, schedule_data: List[Dict[str, Any]]): schedule_str = f"✅ 成功生成并保存今天的日程 ({date_str}):\n" for item in schedule_data: schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n" @@ -153,7 +157,8 @@ class ScheduleManager: logger.warning(f"解析日程事件失败: {event}, 错误: {e}") return None - def _validate_schedule_with_pydantic(self, schedule_data) -> bool: + @staticmethod + def _validate_schedule_with_pydantic(schedule_data) -> bool: try: ScheduleData(schedule=schedule_data) return True diff --git a/src/utils/message_chunker.py b/src/utils/message_chunker.py index ec2e300c2..66a2964e1 100644 --- a/src/utils/message_chunker.py +++ b/src/utils/message_chunker.py @@ -58,7 +58,8 @@ class MessageReassembler: except Exception as e: logger.error(f"清理过期切片时出错: {e}") - def is_chunk_message(self, message: Dict[str, Any]) -> bool: + @staticmethod + def is_chunk_message(message: Dict[str, Any]) -> bool: """检查是否是来自 Ada 的切片消息""" return ( isinstance(message, dict)