From 41dc58d4fbd448a96fb09b18ab2dd89555482802 Mon Sep 17 00:00:00 2001 From: Windpicker-owo <3431391539@qq.com> Date: Sat, 6 Sep 2025 05:45:00 +0800 Subject: [PATCH] =?UTF-8?q?=20=20=20=E7=BB=A7=E7=BB=AD=E5=B0=9D=E8=AF=95?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=EF=BC=8C=E4=BD=86=E6=98=AF=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=8F=92=E4=BB=B6=E9=85=8D=E7=BD=AE=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugin_system/base/base_events_handler.py | 17 +- src/plugin_system/core/component_registry.py | 1 + src/plugin_system/core/event_manager.py | 10 +- .../built_in/napcat_adapter_plugin/plugin.py | 21 +- .../src/config/config.py | 151 -------- .../src/config/features_config.py | 359 ------------------ .../src/message_buffer.py | 28 +- .../src/recv_handler/message_handler.py | 38 +- .../src/recv_handler/meta_event_handler.py | 11 +- .../src/recv_handler/notice_handler.py | 34 +- .../napcat_adapter_plugin/src/send_handler.py | 8 +- 11 files changed, 94 insertions(+), 584 deletions(-) delete mode 100644 src/plugins/built_in/napcat_adapter_plugin/src/config/config.py delete mode 100644 src/plugins/built_in/napcat_adapter_plugin/src/config/features_config.py diff --git a/src/plugin_system/base/base_events_handler.py b/src/plugin_system/base/base_events_handler.py index 999126a02..07dd9a7af 100644 --- a/src/plugin_system/base/base_events_handler.py +++ b/src/plugin_system/base/base_events_handler.py @@ -23,17 +23,20 @@ class BaseEventHandler(ABC): """是否拦截消息,默认为否""" init_subscribe: List[Union[EventType, str]] = [EventType.UNKNOWN] """初始化时订阅的事件名称""" + plugin_name = None def __init__(self): self.log_prefix = "[EventHandler]" """对应插件名""" - self.plugin_config: Optional[Dict] = None - """插件配置字典""" + self.subscribed_events = [] """订阅的事件列表""" if EventType.UNKNOWN in self.init_subscribe: raise NotImplementedError("事件处理器必须指定 event_type") + from src.plugin_system.core.component_registry import component_registry + self.plugin_config = component_registry.get_plugin_config(self.plugin_name) + @abstractmethod async def execute(self, kwargs: dict | None) -> Tuple[bool, bool, Optional[str]]: """执行事件处理的抽象方法,子类必须实现 @@ -89,15 +92,7 @@ class BaseEventHandler(ABC): weight=cls.weight, intercept_message=cls.intercept_message, ) - - def set_plugin_config(self, plugin_config: Dict) -> None: - """设置插件配置 - - Args: - plugin_config (dict): 插件配置字典 - """ - self.plugin_config = plugin_config - + def set_plugin_name(self, plugin_name: str) -> None: """设置插件名称 diff --git a/src/plugin_system/core/component_registry.py b/src/plugin_system/core/component_registry.py index 9f4385fd3..7dfba5bd3 100644 --- a/src/plugin_system/core/component_registry.py +++ b/src/plugin_system/core/component_registry.py @@ -248,6 +248,7 @@ class ComponentRegistry: logger.error(f"注册失败: {handler_name} 不是有效的EventHandler") return False + handler_class.plugin_name = handler_info.plugin_name self._event_handler_registry[handler_name] = handler_class if not handler_info.enabled: diff --git a/src/plugin_system/core/event_manager.py b/src/plugin_system/core/event_manager.py index 7f92b1632..4e950fd76 100644 --- a/src/plugin_system/core/event_manager.py +++ b/src/plugin_system/core/event_manager.py @@ -145,11 +145,12 @@ class EventManager: logger.info(f"事件 {event_name} 已禁用") return True - def register_event_handler(self, handler_class: Type[BaseEventHandler]) -> bool: + def register_event_handler(self, handler_class: Type[BaseEventHandler], plugin_config: Optional[dict] = None) -> bool: """注册事件处理器 Args: handler_class (Type[BaseEventHandler]): 事件处理器类 + plugin_config (Optional[dict]): 插件配置字典,默认为None Returns: bool: 注册成功返回True,已存在返回False @@ -163,7 +164,12 @@ class EventManager: logger.warning(f"事件处理器 {handler_name} 已存在,跳过注册") return False - self._event_handlers[handler_name] = handler_class() + # 创建事件处理器实例,传递插件配置 + handler_instance = handler_class() + if plugin_config is not None and hasattr(handler_instance, 'set_plugin_config'): + handler_instance.set_plugin_config(plugin_config) + + self._event_handlers[handler_name] = handler_instance # 处理init_subscribe,缓存失败的订阅 if self._event_handlers[handler_name].init_subscribe: diff --git a/src/plugins/built_in/napcat_adapter_plugin/plugin.py b/src/plugins/built_in/napcat_adapter_plugin/plugin.py index 0067ba964..c3dc3b23b 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/plugin.py +++ b/src/plugins/built_in/napcat_adapter_plugin/plugin.py @@ -18,7 +18,6 @@ from .src.recv_handler.meta_event_handler import meta_event_handler from .src.recv_handler.notice_handler import notice_handler from .src.recv_handler.message_sending import message_send_instance from .src.send_handler import send_handler -from .src.config.features_config import features_manager from .src.config.migrate_features import auto_migrate_features from .src.mmc_com_layer import mmc_start_com, router, mmc_stop_com from .src.response_pool import put_response, check_timeout_response @@ -158,11 +157,7 @@ async def graceful_shutdown(): except Exception as e: logger.warning(f"停止消息重组器清理任务时出错: {e}") - # 停止功能管理器文件监控 - try: - await features_manager.stop_file_watcher() - except Exception as e: - logger.warning(f"停止功能管理器文件监控时出错: {e}") + # 停止功能管理器文件监控(已迁移到插件系统配置,无需操作) # 关闭消息处理器(包括消息缓冲器) try: @@ -234,11 +229,8 @@ class LauchNapcatAdapterHandler(BaseEventHandler): logger.info("启动消息重组器...") await reassembler.start_cleanup_task() - # 初始化功能管理器 - logger.info("正在初始化功能管理器...") - features_manager.load_config() - await features_manager.start_file_watcher(check_interval=2.0) - logger.info("功能管理器初始化完成") + # 功能管理器已迁移到插件系统配置 + logger.info("功能配置已迁移到插件系统") logger.info("开始启动Napcat Adapter") message_send_instance.maibot_router = router # 设置插件配置 @@ -250,6 +242,12 @@ class LauchNapcatAdapterHandler(BaseEventHandler): set_response_pool_config(self.plugin_config) # 设置send_handler的插件配置 send_handler.set_plugin_config(self.plugin_config) + # 设置message_handler的插件配置 + message_handler.set_plugin_config(self.plugin_config) + # 设置notice_handler的插件配置 + notice_handler.set_plugin_config(self.plugin_config) + # 设置meta_event_handler的插件配置 + meta_event_handler.set_plugin_config(self.plugin_config) # 创建单独的异步任务,防止阻塞主线程 asyncio.create_task(napcat_server(self.plugin_config)) asyncio.create_task(mmc_start_com(self.plugin_config)) @@ -287,6 +285,7 @@ class NapcatAdapterPlugin(BasePlugin): "plugin": { "name": ConfigField(type=str, default="napcat_adapter_plugin", description="插件名称"), "version": ConfigField(type=str, default="1.0.0", description="插件版本"), + "config_version": ConfigField(type=str, default="1.2.0", description="配置文件版本"), "enabled": ConfigField(type=bool, default=False, description="是否启用插件"), }, "inner": { diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/config.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/config.py deleted file mode 100644 index 97b3f57e3..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/config.py +++ /dev/null @@ -1,151 +0,0 @@ -import os -from dataclasses import dataclass -from datetime import datetime - -import tomlkit -import shutil - -from tomlkit import TOMLDocument -from tomlkit.items import Table -from src.common.logger import get_logger - -logger = get_logger("napcat_adapter") -from rich.traceback import install - -from .config_base import ConfigBase -from .official_configs import ( - DebugConfig, - MaiBotServerConfig, - NapcatServerConfig, - NicknameConfig, - SlicingConfig, - VoiceConfig, -) - -install(extra_lines=3) - -TEMPLATE_DIR = "plugins/napcat_adapter_plugin/template" -CONFIG_DIR = "plugins/napcat_adapter_plugin/config" -OLD_CONFIG_DIR = "plugins/napcat_adapter_plugin/config/old" - - -def ensure_config_directories(): - """确保配置目录存在""" - os.makedirs(CONFIG_DIR, exist_ok=True) - os.makedirs(OLD_CONFIG_DIR, exist_ok=True) - - -def update_config(): - """更新配置文件,统一使用 config/old 目录进行备份""" - # 确保目录存在 - ensure_config_directories() - - # 定义文件路径 - template_path = f"{TEMPLATE_DIR}/template_config.toml" - config_path = f"{CONFIG_DIR}/config.toml" - - # 检查配置文件是否存在 - if not os.path.exists(config_path): - logger.info("主配置文件不存在,从模板创建新配置") - shutil.copy2(template_path, config_path) - logger.info(f"已创建新配置文件: {config_path}") - logger.info("程序将退出,请检查配置文件后重启") - - # 读取配置文件和模板文件 - with open(config_path, "r", encoding="utf-8") as f: - old_config = tomlkit.load(f) - with open(template_path, "r", encoding="utf-8") as f: - new_config = tomlkit.load(f) - - # 检查version是否相同 - if old_config and "inner" in old_config and "inner" in new_config: - old_version = old_config["inner"].get("version") - new_version = new_config["inner"].get("version") - if old_version and new_version and old_version == new_version: - logger.info(f"检测到配置文件版本号相同 (v{old_version}),跳过更新") - return - else: - logger.info(f"检测到版本号不同: 旧版本 v{old_version} -> 新版本 v{new_version}") - else: - logger.info("已有配置文件未检测到版本号,可能是旧版本。将进行更新") - - # 创建备份文件 - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - backup_path = os.path.join(OLD_CONFIG_DIR, f"config.toml.bak.{timestamp}") - - # 备份旧配置文件 - shutil.copy2(config_path, backup_path) - logger.info(f"已备份旧配置文件到: {backup_path}") - - # 复制模板文件到配置目录 - shutil.copy2(template_path, config_path) - logger.info(f"已创建新配置文件: {config_path}") - - def update_dict(target: TOMLDocument | dict, source: TOMLDocument | dict): - """将source字典的值更新到target字典中(如果target中存在相同的键)""" - for key, value in source.items(): - # 跳过version字段的更新 - if key == "version": - continue - if key in target: - if isinstance(value, dict) and isinstance(target[key], (dict, Table)): - update_dict(target[key], value) - else: - try: - # 对数组类型进行特殊处理 - if isinstance(value, list): - # 如果是空数组,确保它保持为空数组 - target[key] = tomlkit.array(str(value)) if value else tomlkit.array() - else: - # 其他类型使用item方法创建新值 - target[key] = tomlkit.item(value) - except (TypeError, ValueError): - # 如果转换失败,直接赋值 - target[key] = value - - # 将旧配置的值更新到新配置中 - logger.info("开始合并新旧配置...") - update_dict(new_config, old_config) - - # 保存更新后的配置(保留注释和格式) - with open(config_path, "w", encoding="utf-8") as f: - f.write(tomlkit.dumps(new_config)) - logger.info("配置文件更新完成,建议检查新配置文件中的内容,以免丢失重要信息") - - -@dataclass -class Config(ConfigBase): - """总配置类""" - - nickname: NicknameConfig - napcat_server: NapcatServerConfig - maibot_server: MaiBotServerConfig - voice: VoiceConfig - slicing: SlicingConfig - debug: DebugConfig - - -def load_config(config_path: str) -> Config: - """ - 加载配置文件 - :param config_path: 配置文件路径 - :return: Config对象 - """ - # 读取配置文件 - with open(config_path, "r", encoding="utf-8") as f: - config_data = tomlkit.load(f) - - # 创建Config对象 - try: - return Config.from_dict(config_data) - except Exception as e: - logger.critical("配置文件解析失败") - raise e - - -# 更新配置 -update_config() - -logger.info("正在品鉴配置文件...") -global_config = load_config(config_path=f"{CONFIG_DIR}/config.toml") -logger.info("非常的新鲜,非常的美味!") diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/features_config.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/features_config.py deleted file mode 100644 index 08c9df079..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/features_config.py +++ /dev/null @@ -1,359 +0,0 @@ -import asyncio -from dataclasses import dataclass, field -from typing import Literal, Optional -from pathlib import Path -import tomlkit -from src.common.logger import get_logger - -logger = get_logger("napcat_adapter") -from .config_base import ConfigBase -from .config_utils import create_config_from_template, create_default_config_dict - - -@dataclass -class FeaturesConfig(ConfigBase): - """功能配置类""" - - group_list_type: Literal["whitelist", "blacklist"] = "whitelist" - """群聊列表类型 白名单/黑名单""" - - group_list: list[int] = field(default_factory=list) - """群聊列表""" - - private_list_type: Literal["whitelist", "blacklist"] = "whitelist" - """私聊列表类型 白名单/黑名单""" - - private_list: list[int] = field(default_factory=list) - """私聊列表""" - - ban_user_id: list[int] = field(default_factory=list) - """被封禁的用户ID列表,封禁后将无法与其进行交互""" - - ban_qq_bot: bool = False - """是否屏蔽QQ官方机器人,若为True,则所有QQ官方机器人将无法与MaiMCore进行交互""" - - enable_poke: bool = True - """是否启用戳一戳功能""" - - ignore_non_self_poke: bool = False - """是否无视不是针对自己的戳一戳""" - - poke_debounce_seconds: int = 3 - """戳一戳防抖时间(秒),在指定时间内第二次针对机器人的戳一戳将被忽略""" - - enable_reply_at: bool = True - """是否启用引用回复时艾特用户的功能""" - - reply_at_rate: float = 0.5 - """引用回复时艾特用户的几率 (0.0 ~ 1.0)""" - - enable_video_analysis: bool = True - """是否启用视频识别功能""" - - max_video_size_mb: int = 100 - """视频文件最大大小限制(MB)""" - - download_timeout: int = 60 - """视频下载超时时间(秒)""" - - supported_formats: list[str] = field(default_factory=lambda: ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"]) - """支持的视频格式""" - - # 消息缓冲配置 - enable_message_buffer: bool = True - """是否启用消息缓冲合并功能""" - - message_buffer_enable_group: bool = True - """是否启用群消息缓冲合并""" - - message_buffer_enable_private: bool = True - """是否启用私聊消息缓冲合并""" - - message_buffer_interval: float = 3.0 - """消息合并间隔时间(秒),在此时间内的连续消息将被合并""" - - message_buffer_initial_delay: float = 0.5 - """消息缓冲初始延迟(秒),收到第一条消息后等待此时间开始合并""" - - message_buffer_max_components: int = 50 - """单个会话最大缓冲消息组件数量,超过此数量将强制合并""" - - message_buffer_block_prefixes: list[str] = field(default_factory=lambda: ["/", "!", "!", ".", "。", "#", "%"]) - """消息缓冲屏蔽前缀,以这些前缀开头的消息不会被缓冲""" - - -class FeaturesManager: - """功能管理器,支持热重载""" - - def __init__(self, config_path: str = "plugins/napcat_adapter_plugin/config/features.toml"): - self.config_path = Path(config_path) - self.config: Optional[FeaturesConfig] = None - self._file_watcher_task: Optional[asyncio.Task] = None - self._last_modified: Optional[float] = None - self._callbacks: list = [] - - def add_reload_callback(self, callback): - """添加配置重载回调函数""" - self._callbacks.append(callback) - - def remove_reload_callback(self, callback): - """移除配置重载回调函数""" - if callback in self._callbacks: - self._callbacks.remove(callback) - - async def _notify_callbacks(self): - """通知所有回调函数配置已重载""" - for callback in self._callbacks: - try: - if asyncio.iscoroutinefunction(callback): - await callback(self.config) - else: - callback(self.config) - except Exception as e: - logger.error(f"配置重载回调执行失败: {e}") - - def load_config(self) -> FeaturesConfig: - """加载功能配置文件""" - try: - # 检查配置文件是否存在,如果不存在则创建并退出程序 - if not self.config_path.exists(): - logger.info(f"功能配置文件不存在: {self.config_path}") - self._create_default_config() - # 配置文件创建后程序应该退出,让用户检查配置 - logger.info("程序将退出,请检查功能配置文件后重启") - quit(0) - - with open(self.config_path, "r", encoding="utf-8") as f: - config_data = tomlkit.load(f) - - self.config = FeaturesConfig.from_dict(config_data) - self._last_modified = self.config_path.stat().st_mtime - logger.info(f"功能配置加载成功: {self.config_path}") - return self.config - - except Exception as e: - logger.error(f"功能配置加载失败: {e}") - logger.critical("无法加载功能配置文件,程序退出") - quit(1) - - def _create_default_config(self): - """创建默认功能配置文件""" - template_path = "template/features_template.toml" - - # 尝试从模板创建配置文件 - if create_config_from_template( - str(self.config_path), - template_path, - "功能配置文件", - should_exit=False, # 不在这里退出,由调用方决定 - ): - return - - # 如果模板文件不存在,创建基本配置 - logger.info("模板文件不存在,创建基本功能配置") - default_config = { - "group_list_type": "whitelist", - "group_list": [], - "private_list_type": "whitelist", - "private_list": [], - "ban_user_id": [], - "ban_qq_bot": False, - "enable_poke": True, - "ignore_non_self_poke": False, - "poke_debounce_seconds": 3, - "enable_reply_at": True, - "reply_at_rate": 0.5, - "enable_video_analysis": True, - "max_video_size_mb": 100, - "download_timeout": 60, - "supported_formats": ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"], - # 消息缓冲配置 - "enable_message_buffer": True, - "message_buffer_enable_group": True, - "message_buffer_enable_private": True, - "message_buffer_interval": 3.0, - "message_buffer_initial_delay": 0.5, - "message_buffer_max_components": 50, - "message_buffer_block_prefixes": ["/", "!", "!", ".", "。", "#", "%"], - } - - if not create_default_config_dict(default_config, str(self.config_path), "功能配置文件"): - logger.critical("无法创建功能配置文件") - quit(1) - - async def reload_config(self) -> bool: - """重新加载配置文件""" - try: - if not self.config_path.exists(): - logger.warning(f"功能配置文件不存在,无法重载: {self.config_path}") - return False - - current_modified = self.config_path.stat().st_mtime - if self._last_modified and current_modified <= self._last_modified: - return False # 文件未修改 - - old_config = self.config - new_config = self.load_config() - - # 检查配置是否真的发生了变化 - if old_config and self._configs_equal(old_config, new_config): - return False - - logger.info("功能配置已重载") - await self._notify_callbacks() - return True - - except Exception as e: - logger.error(f"功能配置重载失败: {e}") - return False - - def _configs_equal(self, config1: FeaturesConfig, config2: FeaturesConfig) -> bool: - """比较两个配置是否相等""" - return ( - config1.group_list_type == config2.group_list_type - and set(config1.group_list) == set(config2.group_list) - and config1.private_list_type == config2.private_list_type - and set(config1.private_list) == set(config2.private_list) - and set(config1.ban_user_id) == set(config2.ban_user_id) - and config1.ban_qq_bot == config2.ban_qq_bot - and config1.enable_poke == config2.enable_poke - and config1.ignore_non_self_poke == config2.ignore_non_self_poke - and config1.poke_debounce_seconds == config2.poke_debounce_seconds - and config1.enable_reply_at == config2.enable_reply_at - and config1.reply_at_rate == config2.reply_at_rate - and config1.enable_video_analysis == config2.enable_video_analysis - and config1.max_video_size_mb == config2.max_video_size_mb - and config1.download_timeout == config2.download_timeout - and set(config1.supported_formats) == set(config2.supported_formats) - and - # 消息缓冲配置比较 - config1.enable_message_buffer == config2.enable_message_buffer - and config1.message_buffer_enable_group == config2.message_buffer_enable_group - and config1.message_buffer_enable_private == config2.message_buffer_enable_private - and config1.message_buffer_interval == config2.message_buffer_interval - and config1.message_buffer_initial_delay == config2.message_buffer_initial_delay - and config1.message_buffer_max_components == config2.message_buffer_max_components - and set(config1.message_buffer_block_prefixes) == set(config2.message_buffer_block_prefixes) - ) - - async def start_file_watcher(self, check_interval: float = 1.0): - """启动文件监控,定期检查配置文件变化""" - if self._file_watcher_task and not self._file_watcher_task.done(): - logger.warning("文件监控已在运行") - return - - self._file_watcher_task = asyncio.create_task(self._file_watcher_loop(check_interval)) - logger.info(f"功能配置文件监控已启动,检查间隔: {check_interval}秒") - - async def stop_file_watcher(self): - """停止文件监控""" - if self._file_watcher_task and not self._file_watcher_task.done(): - self._file_watcher_task.cancel() - try: - await self._file_watcher_task - except asyncio.CancelledError: - pass - logger.info("功能配置文件监控已停止") - - async def _file_watcher_loop(self, check_interval: float): - """文件监控循环""" - while True: - try: - await asyncio.sleep(check_interval) - await self.reload_config() - except asyncio.CancelledError: - break - except Exception as e: - logger.error(f"文件监控循环出错: {e}") - await asyncio.sleep(check_interval) - - def get_config(self) -> FeaturesConfig: - """获取当前功能配置""" - if self.config is None: - return self.load_config() - return self.config - - def is_group_allowed(self, group_id: int) -> bool: - """检查群聊是否被允许""" - config = self.get_config() - if config.group_list_type == "whitelist": - return group_id in config.group_list - else: # blacklist - return group_id not in config.group_list - - def is_private_allowed(self, user_id: int) -> bool: - """检查私聊是否被允许""" - config = self.get_config() - if config.private_list_type == "whitelist": - return user_id in config.private_list - else: # blacklist - return user_id not in config.private_list - - def is_user_banned(self, user_id: int) -> bool: - """检查用户是否被全局禁止""" - config = self.get_config() - return user_id in config.ban_user_id - - def is_qq_bot_banned(self) -> bool: - """检查是否禁止QQ官方机器人""" - config = self.get_config() - return config.ban_qq_bot - - def is_poke_enabled(self) -> bool: - """检查戳一戳功能是否启用""" - config = self.get_config() - return config.enable_poke - - def is_non_self_poke_ignored(self) -> bool: - """检查是否忽略非自己戳一戳""" - config = self.get_config() - return config.ignore_non_self_poke - - def is_message_buffer_enabled(self) -> bool: - """检查消息缓冲功能是否启用""" - config = self.get_config() - return config.enable_message_buffer - - def is_message_buffer_group_enabled(self) -> bool: - """检查群消息缓冲是否启用""" - config = self.get_config() - return config.message_buffer_enable_group - - def is_message_buffer_private_enabled(self) -> bool: - """检查私聊消息缓冲是否启用""" - config = self.get_config() - return config.message_buffer_enable_private - - def get_message_buffer_interval(self) -> float: - """获取消息缓冲间隔时间""" - config = self.get_config() - return config.message_buffer_interval - - def get_message_buffer_initial_delay(self) -> float: - """获取消息缓冲初始延迟""" - config = self.get_config() - return config.message_buffer_initial_delay - - def get_message_buffer_max_components(self) -> int: - """获取消息缓冲最大组件数量""" - config = self.get_config() - return config.message_buffer_max_components - - def is_message_buffer_group_enabled(self) -> bool: - """检查是否启用群聊消息缓冲""" - config = self.get_config() - return config.message_buffer_enable_group - - def is_message_buffer_private_enabled(self) -> bool: - """检查是否启用私聊消息缓冲""" - config = self.get_config() - return config.message_buffer_enable_private - - def get_message_buffer_block_prefixes(self) -> list[str]: - """获取消息缓冲屏蔽前缀列表""" - config = self.get_config() - return config.message_buffer_block_prefixes - - -# 全局功能管理器实例 -features_manager = FeaturesManager() 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 0dccb31a8..1988e6c40 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 @@ -7,7 +7,7 @@ from src.common.logger import get_logger logger = get_logger("napcat_adapter") -from .config.features_config import features_manager +from src.plugin_system.apis import config_api from .recv_handler import RealMessageType @@ -43,6 +43,11 @@ class SimpleMessageBuffer: self.lock = asyncio.Lock() self.merge_callback = merge_callback self._shutdown = False + self.plugin_config = None + + def set_plugin_config(self, plugin_config: dict): + """设置插件配置""" + self.plugin_config = plugin_config def get_session_id(self, event_data: Dict[str, Any]) -> str: """根据事件数据生成会话ID""" @@ -97,8 +102,7 @@ class SimpleMessageBuffer: return True # 检查屏蔽前缀 - config = features_manager.get_config() - block_prefixes = tuple(config.message_buffer_block_prefixes) + block_prefixes = tuple(config_api.get_plugin_config(self.plugin_config, "features.message_buffer_block_prefixes", [])) text = text.strip() if text.startswith(block_prefixes): @@ -124,15 +128,15 @@ class SimpleMessageBuffer: if self._shutdown: return False - config = features_manager.get_config() - if not config.enable_message_buffer: + # 检查是否启用消息缓冲 + if not config_api.get_plugin_config(self.plugin_config, "features.enable_message_buffer", False): return False # 检查是否启用对应类型的缓冲 message_type = event_data.get("message_type", "") - if message_type == "group" and not config.message_buffer_enable_group: + if message_type == "group" and not config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enable_group", False): return False - elif message_type == "private" and not config.message_buffer_enable_private: + elif message_type == "private" and not config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enable_private", False): return False # 提取文本 @@ -154,7 +158,7 @@ class SimpleMessageBuffer: session = self.buffer_pool[session_id] # 检查是否超过最大组件数量 - if len(session.messages) >= config.message_buffer_max_components: + if len(session.messages) >= config_api.get_plugin_config(self.plugin_config, "features.message_buffer_max_components", 5): logger.info(f"会话 {session_id} 消息数量达到上限,强制合并") asyncio.create_task(self._force_merge_session(session_id)) self.buffer_pool[session_id] = BufferedSession(session_id=session_id, original_event=original_event) @@ -187,8 +191,8 @@ class SimpleMessageBuffer: async def _wait_and_start_merge(self, session_id: str): """等待初始延迟后开始合并定时器""" - config = features_manager.get_config() - await asyncio.sleep(config.message_buffer_initial_delay) + initial_delay = config_api.get_plugin_config(self.plugin_config, "features.message_buffer_initial_delay", 0.5) + await asyncio.sleep(initial_delay) async with self.lock: session = self.buffer_pool.get(session_id) @@ -206,8 +210,8 @@ class SimpleMessageBuffer: async def _wait_and_merge(self, session_id: str): """等待合并间隔后执行合并""" - config = features_manager.get_config() - await asyncio.sleep(config.message_buffer_interval) + interval = config_api.get_plugin_config(self.plugin_config, "features.message_buffer_interval", 2.0) + await asyncio.sleep(interval) await self._merge_session(session_id) async def _force_merge_session(self, session_id: 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 f5edbb6c5..f761dc33f 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 @@ -5,7 +5,7 @@ from ...CONSTS import PLUGIN_NAME logger = get_logger("napcat_adapter") -from src.plugin_system.core.config_manager import config_api +from src.plugin_system.apis import config_api from ..message_buffer import SimpleMessageBuffer from ..utils import ( get_group_info, @@ -47,9 +47,17 @@ class MessageHandler: def __init__(self): self.server_connection: Server.ServerConnection = None self.bot_id_list: Dict[int, bool] = {} + self.plugin_config = None # 初始化简化消息缓冲器,传入回调函数 self.message_buffer = SimpleMessageBuffer(merge_callback=self._send_buffered_message) + def set_plugin_config(self, plugin_config: dict): + """设置插件配置""" + self.plugin_config = plugin_config + # 将配置传递给消息缓冲器 + if self.message_buffer: + self.message_buffer.set_plugin_config(plugin_config) + async def shutdown(self): """关闭消息处理器,清理资源""" if self.message_buffer: @@ -89,21 +97,21 @@ class MessageHandler: # 使用新的权限管理器检查权限 if group_id: - if not config_api.get_plugin_config(PLUGIN_NAME, f"features.group_allowed.{group_id}", True): + if not config_api.get_plugin_config(self.plugin_config, f"features.group_allowed.{group_id}", True): logger.warning("群聊不在聊天权限范围内,消息被丢弃") return False else: - if not config_api.get_plugin_config(PLUGIN_NAME, f"features.private_allowed.{user_id}", True): + if not config_api.get_plugin_config(self.plugin_config, f"features.private_allowed.{user_id}", True): logger.warning("私聊不在聊天权限范围内,消息被丢弃") return False # 检查全局禁止名单 - if not ignore_global_list and config_api.get_plugin_config(PLUGIN_NAME, f"features.user_banned.{user_id}", False): + if not ignore_global_list and config_api.get_plugin_config(self.plugin_config, f"features.user_banned.{user_id}", False): logger.warning("用户在全局黑名单中,消息被丢弃") return False # 检查QQ官方机器人 - if config_api.get_plugin_config(PLUGIN_NAME, "features.qq_bot_banned", False) and group_id and not ignore_bot: + if config_api.get_plugin_config(self.plugin_config, "features.qq_bot_banned", False) and group_id and not ignore_bot: logger.debug("开始判断是否为机器人") member_info = await get_member_info(self.get_server_connection(), group_id, user_id) if member_info: @@ -148,7 +156,7 @@ class MessageHandler: # 发送者用户信息 user_info: UserInfo = UserInfo( - platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"), + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"), user_id=sender_info.get("user_id"), user_nickname=sender_info.get("nickname"), user_cardname=sender_info.get("card"), @@ -174,7 +182,7 @@ class MessageHandler: nickname = fetched_member_info.get("nickname") if fetched_member_info else None # 发送者用户信息 user_info: UserInfo = UserInfo( - platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"), + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"), user_id=sender_info.get("user_id"), user_nickname=nickname, user_cardname=None, @@ -191,7 +199,7 @@ class MessageHandler: group_name = fetched_group_info.get("group_name") group_info: GroupInfo = GroupInfo( - platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"), + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"), group_id=raw_message.get("group_id"), group_name=group_name, ) @@ -209,7 +217,7 @@ class MessageHandler: # 发送者用户信息 user_info: UserInfo = UserInfo( - platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"), + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"), user_id=sender_info.get("user_id"), user_nickname=sender_info.get("nickname"), user_cardname=sender_info.get("card"), @@ -222,7 +230,7 @@ class MessageHandler: group_name = fetched_group_info.get("group_name") group_info: GroupInfo = GroupInfo( - platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"), + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"), group_id=raw_message.get("group_id"), group_name=group_name, ) @@ -232,12 +240,12 @@ class MessageHandler: return None additional_config: dict = {} - if config_api.get_plugin_config(PLUGIN_NAME, "voice.use_tts"): + if config_api.get_plugin_config(self.plugin_config, "voice.use_tts"): additional_config["allow_tts"] = True # 消息信息 message_info: BaseMessageInfo = BaseMessageInfo( - platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"), + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"), message_id=message_id, time=message_time, user_info=user_info, @@ -259,14 +267,14 @@ class MessageHandler: return None # 检查是否需要使用消息缓冲 - if config_api.get_plugin_config(PLUGIN_NAME, "features.message_buffer_enabled", False): + if config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enabled", False): # 检查消息类型是否启用缓冲 message_type = raw_message.get("message_type") should_use_buffer = False - if message_type == "group" and config_api.get_plugin_config(PLUGIN_NAME, "features.message_buffer_group_enabled", False): + if message_type == "group" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_group_enabled", False): should_use_buffer = True - elif message_type == "private" and config_api.get_plugin_config(PLUGIN_NAME, "features.message_buffer_private_enabled", False): + elif message_type == "private" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_private_enabled", False): should_use_buffer = True if should_use_buffer: 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 eae6fd01a..217347c36 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 @@ -1,7 +1,7 @@ from src.common.logger import get_logger logger = get_logger("napcat_adapter") -from ..config import global_config +from src.plugin_system.apis import config_api import time import asyncio @@ -14,8 +14,15 @@ class MetaEventHandler: """ def __init__(self): - self.interval = global_config.napcat_server.heartbeat_interval + self.interval = 5.0 # 默认值,稍后通过set_plugin_config设置 self._interval_checking = False + self.plugin_config = None + + def set_plugin_config(self, plugin_config: dict): + """设置插件配置""" + self.plugin_config = plugin_config + # 更新interval值 + self.interval = config_api.get_plugin_config(self.plugin_config, "napcat_server.heartbeat_interval", 5000) / 1000 async def handle_meta_event(self, message: dict) -> None: event_type = message.get("meta_event_type") 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 2f4fddda2..0efdcd352 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 @@ -8,8 +8,7 @@ from src.common.logger import get_logger logger = get_logger("napcat_adapter") -from ..config import global_config -from ..config.features_config import features_manager +from src.plugin_system.apis import config_api from ..database import BanUser, db_manager, is_identical from . import NoticeType, ACCEPT_FORMAT from .message_sending import message_send_instance @@ -38,6 +37,11 @@ class NoticeHandler: def __init__(self): self.server_connection: Server.ServerConnection | None = None self.last_poke_time: float = 0.0 # 记录最后一次针对机器人的戳一戳时间 + self.plugin_config = None + + def set_plugin_config(self, plugin_config: dict): + """设置插件配置""" + self.plugin_config = plugin_config async def set_server_connection(self, server_connection: Server.ServerConnection) -> None: """设置Napcat连接""" @@ -112,7 +116,7 @@ class NoticeHandler: sub_type = raw_message.get("sub_type") match sub_type: case NoticeType.Notify.poke: - if features_manager.is_poke_enabled() and await message_handler.check_allow_to_chat( + if config_api.get_plugin_config(self.plugin_config, "features.poke_enabled", True) and await message_handler.check_allow_to_chat( user_id, group_id, False, False ): logger.info("处理戳一戳消息") @@ -159,13 +163,13 @@ class NoticeHandler: else: logger.warning("无法获取notice消息所在群的名称") group_info = GroupInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), group_id=group_id, group_name=group_name, ) message_info: BaseMessageInfo = BaseMessageInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), message_id="notice", time=message_time, user_info=user_info, @@ -206,7 +210,7 @@ class NoticeHandler: # 防抖检查:如果是针对机器人的戳一戳,检查防抖时间 if self_id == target_id: current_time = time.time() - debounce_seconds = features_manager.get_config().poke_debounce_seconds + debounce_seconds = config_api.get_plugin_config(self.plugin_config, "features.poke_debounce_seconds", 2.0) if self.last_poke_time > 0: time_diff = current_time - self.last_poke_time @@ -243,7 +247,7 @@ class NoticeHandler: else: # 如果配置为忽略不是针对自己的戳一戳,则直接返回None - if features_manager.is_non_self_poke_ignored(): + if config_api.get_plugin_config(self.plugin_config, "features.non_self_poke_ignored", False): logger.info("忽略不是针对自己的戳一戳消息") return None, None @@ -268,7 +272,7 @@ class NoticeHandler: logger.warning(f"解析戳一戳消息失败: {str(e)},将使用默认文本") user_info: UserInfo = UserInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), user_id=user_id, user_nickname=user_name, user_cardname=user_cardname, @@ -299,7 +303,7 @@ class NoticeHandler: operator_nickname = "QQ用户" operator_info: UserInfo = UserInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), user_id=operator_id, user_nickname=operator_nickname, user_cardname=operator_cardname, @@ -328,7 +332,7 @@ class NoticeHandler: user_nickname = fetched_member_info.get("nickname") user_cardname = fetched_member_info.get("card") banned_user_info: UserInfo = UserInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), user_id=user_id, user_nickname=user_nickname, user_cardname=user_cardname, @@ -367,7 +371,7 @@ class NoticeHandler: operator_nickname = "QQ用户" operator_info: UserInfo = UserInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), user_id=operator_id, user_nickname=operator_nickname, user_cardname=operator_cardname, @@ -393,7 +397,7 @@ class NoticeHandler: else: logger.warning("无法获取解除禁言消息发送者的昵称,消息可能会无效") lifted_user_info: UserInfo = UserInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), user_id=user_id, user_nickname=user_nickname, user_cardname=user_cardname, @@ -436,13 +440,13 @@ class NoticeHandler: else: logger.warning("无法获取notice消息所在群的名称") group_info = GroupInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), group_id=group_id, group_name=group_name, ) message_info: BaseMessageInfo = BaseMessageInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), message_id="notice", time=time.time(), user_info=None, # 自然解除禁言没有操作者 @@ -493,7 +497,7 @@ class NoticeHandler: user_cardname = fetched_member_info.get("card") lifted_user_info: UserInfo = UserInfo( - platform=global_config.maibot_server.platform_name, + platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"), user_id=user_id, user_nickname=user_nickname, user_cardname=user_cardname, 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 a6eda3b00..5d6d91467 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 @@ -22,7 +22,6 @@ logger = get_logger("napcat_adapter") from .utils import get_image_format, convert_image_to_gif from .recv_handler.message_sending import message_send_instance from .websocket_manager import websocket_manager -from .config.features_config import features_manager class SendHandler: @@ -292,11 +291,8 @@ class SendHandler: """处理回复消息""" reply_seg = {"type": "reply", "data": {"id": id}} - # 获取功能配置 - ft_config = features_manager.get_config() - # 检查是否启用引用艾特功能 - if not ft_config.enable_reply_at: + if not config_api.get_plugin_config(self.plugin_config, "features.enable_reply_at", False): return reply_seg try: @@ -315,7 +311,7 @@ class SendHandler: return reply_seg # 根据概率决定是否艾特用户 - if random.random() < ft_config.reply_at_rate: + if random.random() < config_api.get_plugin_config(self.plugin_config, "features.reply_at_rate", 0.5): at_seg = {"type": "at", "data": {"qq": str(replied_user_id)}} # 在艾特后面添加一个空格 text_seg = {"type": "text", "data": {"text": " "}}