diff --git a/src/config/config.py b/src/config/config.py index 9aa25cc15..70c0edd0e 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -40,7 +40,6 @@ from src.config.official_configs import ( DependencyManagementConfig, WebSearchConfig, AntiPromptInjectionConfig, - PluginsConfig, SleepSystemConfig, MonthlyPlanSystemConfig, CrossContextConfig, @@ -389,7 +388,6 @@ class Config(ValidatedConfigBase): video_analysis: VideoAnalysisConfig = Field(default_factory=lambda: VideoAnalysisConfig(), description="视频分析配置") dependency_management: DependencyManagementConfig = Field(default_factory=lambda: DependencyManagementConfig(), description="依赖管理配置") web_search: WebSearchConfig = Field(default_factory=lambda: WebSearchConfig(), description="网络搜索配置") - plugins: PluginsConfig = Field(default_factory=lambda: PluginsConfig(), description="插件配置") sleep_system: SleepSystemConfig = Field(default_factory=lambda: SleepSystemConfig(), description="睡眠系统配置") monthly_plan_system: MonthlyPlanSystemConfig = Field(default_factory=lambda: MonthlyPlanSystemConfig(), description="月层计划系统配置") cross_context: CrossContextConfig = Field(default_factory=lambda: CrossContextConfig(), description="跨群聊上下文共享配置") diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 36e9159ec..f8bb37d95 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -600,13 +600,6 @@ class AntiPromptInjectionConfig(ValidatedConfigBase): shield_suffix: str = Field(default=" 🛡️", description="保护后缀") - -class PluginsConfig(ValidatedConfigBase): - """插件配置""" - - centralized_config: bool = Field(default=True, description="是否启用插件配置集中化管理") - - class SleepSystemConfig(ValidatedConfigBase): """睡眠系统配置类""" diff --git a/src/plugin_system/base/plugin_base.py b/src/plugin_system/base/plugin_base.py index dfe1ec94d..8b6ad84c9 100644 --- a/src/plugin_system/base/plugin_base.py +++ b/src/plugin_system/base/plugin_base.py @@ -458,54 +458,35 @@ class PluginBase(ABC): 加载插件配置文件,实现集中化管理和自动迁移。 处理逻辑: - 1. 确定插件模板配置文件路径(位于插件目录内)。 - 2. 如果模板不存在,则在插件目录内生成一份默认配置。 - 3. 确定用户配置文件路径(位于 `config/plugins/` 目录下)。 - 4. 如果用户配置文件不存在,则从插件目录复制模板文件过去。 - 5. 加载用户配置文件,并进行版本检查和自动迁移(如果需要)。 - 6. 最终加载的配置是用户配置文件。 + 1. 确定用户配置文件路径(位于 `config/plugins/` 目录下)。 + 2. 如果用户配置文件不存在,则根据 config_schema 直接在中央目录生成一份。 + 3. 加载用户配置文件,并进行版本检查和自动迁移(如果需要)。 + 4. 最终加载的配置是用户配置文件。 """ if not self.config_file_name: logger.debug(f"{self.log_prefix} 未指定配置文件,跳过加载") return - # 1. 确定插件模板配置文件路径 - template_config_path = os.path.join(self.plugin_dir, self.config_file_name) + # 1. 确定并确保用户配置文件路径存在 + user_config_path = os.path.join(CONFIG_DIR, "plugins", self.plugin_name, self.config_file_name) + os.makedirs(os.path.dirname(user_config_path), exist_ok=True) - # 2. 如果模板不存在,则在插件目录内生成 - if not os.path.exists(template_config_path): - logger.info(f"{self.log_prefix} 插件目录缺少配置文件 {template_config_path},将生成默认配置。") - self._generate_and_save_default_config(template_config_path) - - # 3. 确定用户配置文件路径 - plugin_config_dir = os.path.join(CONFIG_DIR, "plugins", self.plugin_name) - user_config_path = os.path.join(plugin_config_dir, self.config_file_name) - - # 确保用户插件配置目录存在 - os.makedirs(plugin_config_dir, exist_ok=True) - - # 4. 如果用户配置文件不存在,从模板复制 + # 2. 如果用户配置文件不存在,直接在中央目录生成 if not os.path.exists(user_config_path): - try: - shutil.copy2(template_config_path, user_config_path) - logger.info(f"{self.log_prefix} 已从模板创建用户配置文件: {user_config_path}") - except IOError as e: - logger.error(f"{self.log_prefix} 复制配置文件失败: {e}", exc_info=True) - # 如果复制失败,后续将无法加载,直接返回 - return + logger.info(f"{self.log_prefix} 用户配置文件 {user_config_path} 不存在,将生成默认配置。") + self._generate_and_save_default_config(user_config_path) # 检查最终的用户配置文件是否存在 if not os.path.exists(user_config_path): logger.warning(f"{self.log_prefix} 用户配置文件 {user_config_path} 不存在且无法创建。") return - # 5. 加载、检查和迁移用户配置文件 + # 3. 加载、检查和迁移用户配置文件 _, file_ext = os.path.splitext(self.config_file_name) if file_ext.lower() != ".toml": logger.warning(f"{self.log_prefix} 不支持的配置文件格式: {file_ext},仅支持 .toml") self.config = {} return - try: with open(user_config_path, "r", encoding="utf-8") as f: existing_config = toml.load(f) or {} diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index b16c80f61..754df34d9 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -1,5 +1,7 @@ import asyncio import os +import shutil +import hashlib import traceback import importlib @@ -37,6 +39,48 @@ class PluginManager: self._ensure_plugin_directories() logger.info("插件管理器初始化完成") + def _synchronize_plugin_config(self, plugin_name: str, plugin_dir: str): + """ + 同步单个插件的配置。 + """ + central_config_dir = os.path.join("config", "plugins", plugin_name) + plugin_config_dir = os.path.join(plugin_dir, "config") + + # 确保中央配置目录存在 + os.makedirs(central_config_dir, exist_ok=True) + + # 1. 从插件目录同步到中央目录(如果中央配置不存在) + if os.path.exists(plugin_config_dir) and os.path.isdir(plugin_config_dir): + for filename in os.listdir(plugin_config_dir): + central_config_file = os.path.join(central_config_dir, filename) + plugin_config_file = os.path.join(plugin_config_dir, filename) + + if not os.path.exists(central_config_file) and os.path.isfile(plugin_config_file): + shutil.copy2(plugin_config_file, central_config_file) + logger.info(f"从 {plugin_name} 复制默认配置到中央目录: {filename}") + + # 2. 从中央目录同步到插件目录(覆盖) + if os.path.isdir(central_config_dir): + for filename in os.listdir(central_config_dir): + central_config_file = os.path.join(central_config_dir, filename) + plugin_config_file = os.path.join(plugin_config_dir, filename) + + if not os.path.isfile(central_config_file): + continue + + # 确保插件的 config 目录存在 + os.makedirs(plugin_config_dir, exist_ok=True) + + should_copy = True + if os.path.exists(plugin_config_file): + with open(central_config_file, 'rb') as f1, open(plugin_config_file, 'rb') as f2: + if hashlib.md5(f1.read()).hexdigest() == hashlib.md5(f2.read()).hexdigest(): + should_copy = False + + if should_copy: + shutil.copy2(central_config_file, plugin_config_file) + logger.info(f"同步中央配置到 {plugin_name}: {filename}") + # === 插件目录管理 === def add_plugin_directory(self, directory: str) -> bool: @@ -104,6 +148,9 @@ class PluginManager: if not plugin_dir: return False, 1 + # 同步插件配置 + self._synchronize_plugin_config(plugin_name, plugin_dir) + plugin_instance = plugin_class(plugin_dir=plugin_dir) # 实例化插件(可能因为缺少manifest而失败) if not plugin_instance: logger.error(f"插件 {plugin_name} 实例化失败") diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index d5b4f0da7..6a5f8baa8 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "6.5.8" +version = "6.6.0" #----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读---- #如果你想要修改配置文件,请递增version的值 @@ -411,9 +411,6 @@ exa_api_keys = ["None"]# EXA API密钥列表,支持轮询机制 enabled_engines = ["ddg"] # 启用的搜索引擎列表,可选: "exa", "tavily", "ddg","bing" search_strategy = "single" # 搜索策略: "single"(使用第一个可用引擎), "parallel"(并行使用所有启用的引擎), "fallback"(按顺序尝试,失败则尝试下一个) -[plugins] # 插件配置 -centralized_config = true # 是否启用插件配置集中化管理 - # ---------------------------------------------------------------- # 月度计划系统设置 (Monthly Plan System Settings) # ----------------------------------------------------------------