From 3764b3a8a6169e6a72ce7f380aa2e1f7fcc62dd1 Mon Sep 17 00:00:00 2001 From: minecraft1024a Date: Sat, 4 Oct 2025 16:17:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor(plugin=5Fsystem):=20=E5=BC=95=E5=85=A5?= =?UTF-8?q?=20PluginMetadata=20=E6=9B=BF=E4=BB=A3=20manifest.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将插件元数据定义从外部 `_manifest.json` 文件迁移到插件 `__init__.py` 文件中的 `__plugin_meta__` 变量。此举简化了插件加载流程,减少了文件I/O,并使元数据与插件代码更紧密地耦合。 主要变更: - 引入 `PluginMetadata` 数据类来标准化插件元数据。 - 插件基类 `PluginBase` 现在直接接收 `PluginMetadata` 对象,不再负责解析 JSON 文件。 - 插件管理器 `PluginManager` 调整加载逻辑,从插件模块的 `__plugin_meta__` 属性获取元数据。 - 删除了 `manifest_utils.py` 及其相关的验证和版本比较逻辑,简化了依赖关系。 - 更新了所有内置插件,以采用新的元数据定义方式,并删除了它们各自的 `_manifest.json` 文件。 BREAKING CHANGE: 插件加载机制已改变。所有插件必须在其 `__init__.py` 中定义一个 `__plugin_meta__` 变量,该变量是 `PluginMetadata` 类的实例,并移除旧的 `_manifest.json` 文件。 --- plugins/bilibli/__init__.py | 15 +- plugins/echo_example/__init__.py | 10 + plugins/hello_world_plugin/__init__.py | 10 + src/plugin_system/__init__.py | 7 - src/plugin_system/base/plugin_base.py | 136 +---- src/plugin_system/base/plugin_metadata.py | 25 + src/plugin_system/core/plugin_manager.py | 122 ++--- src/plugin_system/utils/__init__.py | 16 +- src/plugin_system/utils/manifest_utils.py | 517 ------------------ .../affinity_flow_chatter/__init__.py | 18 +- .../affinity_flow_chatter/_manifest.json | 23 - src/plugins/built_in/core_actions/__init__.py | 17 + .../built_in/core_actions/_manifest.json | 34 -- .../built_in/maizone_refactored/__init__.py | 23 +- .../maizone_refactored/_manifest.json | 47 -- .../napcat_adapter_plugin/__init__.py | 16 + .../napcat_adapter_plugin/_manifest.json | 42 -- .../permission_management/__init__.py | 16 + .../permission_management/_manifest.json | 33 -- .../built_in/plugin_management/__init__.py | 17 + .../built_in/plugin_management/_manifest.json | 39 -- .../built_in/proactive_thinker/__init__.py | 17 + .../built_in/proactive_thinker/_manifest.json | 25 - .../social_toolkit_plugin/__init__.py | 17 + .../social_toolkit_plugin/_manifest.json | 25 - src/plugins/built_in/tts_plugin/__init__.py | 17 + .../built_in/tts_plugin/_manifest.json | 42 -- .../built_in/web_search_tool/__init__.py | 16 + 28 files changed, 281 insertions(+), 1061 deletions(-) create mode 100644 plugins/echo_example/__init__.py create mode 100644 plugins/hello_world_plugin/__init__.py create mode 100644 src/plugin_system/base/plugin_metadata.py delete mode 100644 src/plugin_system/utils/manifest_utils.py delete mode 100644 src/plugins/built_in/affinity_flow_chatter/_manifest.json create mode 100644 src/plugins/built_in/core_actions/__init__.py delete mode 100644 src/plugins/built_in/core_actions/_manifest.json delete mode 100644 src/plugins/built_in/maizone_refactored/_manifest.json create mode 100644 src/plugins/built_in/napcat_adapter_plugin/__init__.py delete mode 100644 src/plugins/built_in/napcat_adapter_plugin/_manifest.json create mode 100644 src/plugins/built_in/permission_management/__init__.py delete mode 100644 src/plugins/built_in/permission_management/_manifest.json create mode 100644 src/plugins/built_in/plugin_management/__init__.py delete mode 100644 src/plugins/built_in/plugin_management/_manifest.json delete mode 100644 src/plugins/built_in/proactive_thinker/_manifest.json delete mode 100644 src/plugins/built_in/social_toolkit_plugin/_manifest.json create mode 100644 src/plugins/built_in/tts_plugin/__init__.py delete mode 100644 src/plugins/built_in/tts_plugin/_manifest.json create mode 100644 src/plugins/built_in/web_search_tool/__init__.py diff --git a/plugins/bilibli/__init__.py b/plugins/bilibli/__init__.py index 7f6e5c3c2..dab7b54cf 100644 --- a/plugins/bilibli/__init__.py +++ b/plugins/bilibli/__init__.py @@ -1,7 +1,10 @@ -#!/usr/bin/env python3 -""" -Bilibili 插件包 -提供B站视频观看体验功能,像真实用户一样浏览和评价视频 -""" +from src.plugin_system.base.plugin_metadata import PluginMetadata -# 插件会通过 @register_plugin 装饰器自动注册,这里不需要额外的导入 +__plugin_meta__ = PluginMetadata( + name="Bilibili Plugin", + description="A plugin for interacting with Bilibili.", + usage="Usage details for Bilibili plugin.", + version="1.0.0", + author="Your Name", + license="MIT", +) diff --git a/plugins/echo_example/__init__.py b/plugins/echo_example/__init__.py new file mode 100644 index 000000000..8747cc51a --- /dev/null +++ b/plugins/echo_example/__init__.py @@ -0,0 +1,10 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="Echo Example Plugin", + description="An example plugin that echoes messages.", + usage="!echo [message]", + version="1.0.0", + author="Your Name", + license="MIT", +) \ No newline at end of file diff --git a/plugins/hello_world_plugin/__init__.py b/plugins/hello_world_plugin/__init__.py new file mode 100644 index 000000000..fdca03bcd --- /dev/null +++ b/plugins/hello_world_plugin/__init__.py @@ -0,0 +1,10 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="Hello World Plugin", + description="A simple hello world plugin.", + usage="!hello", + version="1.0.0", + author="Your Name", + license="MIT", +) \ No newline at end of file diff --git a/src/plugin_system/__init__.py b/src/plugin_system/__init__.py index 9a3bb85d6..0f2509116 100644 --- a/src/plugin_system/__init__.py +++ b/src/plugin_system/__init__.py @@ -50,13 +50,6 @@ from .base import ( create_plus_command_adapter, ) -# 导入工具模块 -from .utils import ( - ManifestValidator, - # ManifestGenerator, - # validate_plugin_manifest, - # generate_plugin_manifest, -) from .utils.dependency_config import configure_dependency_settings, get_dependency_config # 导入依赖管理模块 diff --git a/src/plugin_system/base/plugin_base.py b/src/plugin_system/base/plugin_base.py index 683e4985c..dd4811c20 100644 --- a/src/plugin_system/base/plugin_base.py +++ b/src/plugin_system/base/plugin_base.py @@ -5,7 +5,6 @@ from abc import ABC, abstractmethod from pathlib import Path from typing import Any -import orjson import toml from src.common.logger import get_logger @@ -15,7 +14,7 @@ from src.plugin_system.base.component_types import ( PythonDependency, ) from src.plugin_system.base.config_types import ConfigField -from src.plugin_system.utils.manifest_utils import ManifestValidator +from src.plugin_system.base.plugin_metadata import PluginMetadata logger = get_logger("plugin_base") @@ -33,39 +32,34 @@ class PluginBase(ABC): dependencies: list[str] = [] python_dependencies: list[str | PythonDependency] = [] - # manifest文件相关 - manifest_file_name: str = "_manifest.json" # manifest文件名 - manifest_data: dict[str, Any] = {} # manifest数据 - config_schema: dict[str, dict[str, ConfigField] | str] = {} config_section_descriptions: dict[str, str] = {} - def __init__(self, plugin_dir: str): + def __init__(self, plugin_dir: str, metadata: PluginMetadata): """初始化插件 Args: plugin_dir: 插件目录路径,由插件管理器传递 + metadata: 插件元数据对象 """ self.config: dict[str, Any] = {} # 插件配置 self.plugin_dir = plugin_dir # 插件目录路径 + self.plugin_meta = metadata # 插件元数据 self.log_prefix = f"[Plugin:{self.plugin_name}]" self._is_enabled = self.enable_plugin # 从插件定义中获取默认启用状态 - # 加载manifest文件 - self._load_manifest() - # 验证插件信息 self._validate_plugin_info() # 加载插件配置 self._load_plugin_config() - # 从manifest获取显示信息 - self.display_name = self.get_manifest_info("name", self.plugin_name) - self.plugin_version = self.get_manifest_info("version", "1.0.0") - self.plugin_description = self.get_manifest_info("description", "") - self.plugin_author = self._get_author_name() + # 从元数据获取显示信息 + self.display_name = self.plugin_meta.name + self.plugin_version = self.plugin_meta.version + self.plugin_description = self.plugin_meta.description + self.plugin_author = self.plugin_meta.author # 标准化Python依赖为PythonDependency对象 normalized_python_deps = self._normalize_python_dependencies(self.python_dependencies) @@ -85,15 +79,6 @@ class PluginBase(ABC): config_file=self.config_file_name or "", dependencies=self.dependencies.copy(), python_dependencies=normalized_python_deps, - # manifest相关信息 - manifest_data=self.manifest_data.copy(), - license=self.get_manifest_info("license", ""), - homepage_url=self.get_manifest_info("homepage_url", ""), - repository_url=self.get_manifest_info("repository_url", ""), - keywords=self.get_manifest_info("keywords", []).copy() if self.get_manifest_info("keywords") else [], - categories=self.get_manifest_info("categories", []).copy() if self.get_manifest_info("categories") else [], - min_host_version=self.get_manifest_info("host_application.min_version", ""), - max_host_version=self.get_manifest_info("host_application.max_version", ""), ) logger.debug(f"{self.log_prefix} 插件基类初始化完成") @@ -103,93 +88,10 @@ class PluginBase(ABC): if not self.plugin_name: raise ValueError(f"插件类 {self.__class__.__name__} 必须定义 plugin_name") - # 验证manifest中的必需信息 - if not self.get_manifest_info("name"): - raise ValueError(f"插件 {self.plugin_name} 的manifest中缺少name字段") - if not self.get_manifest_info("description"): - raise ValueError(f"插件 {self.plugin_name} 的manifest中缺少description字段") - - def _load_manifest(self): # sourcery skip: raise-from-previous-error - """加载manifest文件(强制要求)""" - if not self.plugin_dir: - raise ValueError(f"{self.log_prefix} 没有插件目录路径,无法加载manifest") - - manifest_path = os.path.join(self.plugin_dir, self.manifest_file_name) - - if not os.path.exists(manifest_path): - error_msg = f"{self.log_prefix} 缺少必需的manifest文件: {manifest_path}" - logger.error(error_msg) - raise FileNotFoundError(error_msg) - - try: - with open(manifest_path, encoding="utf-8") as f: - self.manifest_data = orjson.loads(f.read()) - - logger.debug(f"{self.log_prefix} 成功加载manifest文件: {manifest_path}") - - # 验证manifest格式 - self._validate_manifest() - - except orjson.JSONDecodeError as e: - error_msg = f"{self.log_prefix} manifest文件格式错误: {e}" - logger.error(error_msg) - raise ValueError(error_msg) - except OSError as e: - error_msg = f"{self.log_prefix} 读取manifest文件失败: {e}" - logger.error(error_msg) - raise IOError(error_msg) # noqa - - def _get_author_name(self) -> str: - """从manifest获取作者名称""" - author_info = self.get_manifest_info("author", {}) - if isinstance(author_info, dict): - return author_info.get("name", "") - else: - return str(author_info) if author_info else "" - - def _validate_manifest(self): - """验证manifest文件格式(使用强化的验证器)""" - if not self.manifest_data: - raise ValueError(f"{self.log_prefix} manifest数据为空,验证失败") - - validator = ManifestValidator() - is_valid = validator.validate_manifest(self.manifest_data) - - # 记录验证结果 - if validator.validation_errors or validator.validation_warnings: - report = validator.get_validation_report() - logger.info(f"{self.log_prefix} Manifest验证结果:\n{report}") - - # 如果有验证错误,抛出异常 - if not is_valid: - error_msg = f"{self.log_prefix} Manifest文件验证失败" - if validator.validation_errors: - error_msg += f": {'; '.join(validator.validation_errors)}" - raise ValueError(error_msg) - - def get_manifest_info(self, key: str, default: Any = None) -> Any: - """获取manifest信息 - - Args: - key: 信息键,支持点分割的嵌套键(如 "author.name") - default: 默认值 - - Returns: - Any: 对应的值 - """ - if not self.manifest_data: - return default - - keys = key.split(".") - value = self.manifest_data - - for k in keys: - if isinstance(value, dict) and k in value: - value = value[k] - else: - return default - - return value + if not self.plugin_meta.name: + raise ValueError(f"插件 {self.plugin_name} 的元数据中缺少 name 字段") + if not self.plugin_meta.description: + raise ValueError(f"插件 {self.plugin_name} 的元数据中缺少 description 字段") def _generate_and_save_default_config(self, config_file_path: str): """根据插件的Schema生成并保存默认配置文件""" @@ -198,7 +100,7 @@ class PluginBase(ABC): return toml_str = f"# {self.plugin_name} - 自动生成的配置文件\n" - plugin_description = self.get_manifest_info("description", "插件配置文件") + plugin_description = self.plugin_meta.description or "插件配置文件" toml_str += f"# {plugin_description}\n\n" # 遍历每个配置节 @@ -333,7 +235,7 @@ class PluginBase(ABC): return toml_str = f"# {self.plugin_name} - 配置文件\n" - plugin_description = self.get_manifest_info("description", "插件配置文件") + plugin_description = self.plugin_meta.description or "插件配置文件" toml_str += f"# {plugin_description}\n\n" # 遍历每个配置节 @@ -564,3 +466,11 @@ class PluginBase(ABC): bool: 是否成功注册插件 """ raise NotImplementedError("Subclasses must implement this method") + + async def on_plugin_loaded(self): + """插件加载完成后的钩子函数""" + pass + + def on_unload(self): + """插件卸载时的钩子函数""" + pass diff --git a/src/plugin_system/base/plugin_metadata.py b/src/plugin_system/base/plugin_metadata.py new file mode 100644 index 000000000..d39a384d9 --- /dev/null +++ b/src/plugin_system/base/plugin_metadata.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional, Set + +@dataclass +class PluginMetadata: + """ + 插件元数据,用于存储插件的开发者信息和用户帮助信息。 + """ + name: str # 插件名称 (供用户查看) + description: str # 插件功能描述 + usage: str # 插件使用方法 + + # 以下为可选字段,参考自 _manifest.json 和 NoneBot 设计 + type: Optional[str] = None # 插件类别: "library", "application" + + # 从原 _manifest.json 迁移的字段 + version: str = "1.0.0" # 插件版本 + author: str = "" # 作者名称 + license: Optional[str] = None # 开源协议 + repository_url: Optional[str] = None # 仓库地址 + keywords: List[str] = field(default_factory=list) # 关键词 + categories: List[str] = field(default_factory=list) # 分类 + + # 扩展字段 + extra: Dict[str, Any] = field(default_factory=dict) # 其他任意信息 \ No newline at end of file diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index 046c05b4f..c146f9c00 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -9,7 +9,7 @@ from typing import Any, Optional from src.common.logger import get_logger from src.plugin_system.base.component_types import ComponentType from src.plugin_system.base.plugin_base import PluginBase -from src.plugin_system.utils.manifest_utils import VersionComparator +from src.plugin_system.base.plugin_metadata import PluginMetadata from .component_registry import component_registry @@ -27,6 +27,7 @@ class PluginManager: self.plugin_directories: list[str] = [] # 插件根目录列表 self.plugin_classes: dict[str, type[PluginBase]] = {} # 全局插件类注册表,插件名 -> 插件类 self.plugin_paths: dict[str, str] = {} # 记录插件名到目录路径的映射,插件名 -> 目录路径 + self.plugin_modules: dict[str, Any] = {} # 记录插件名到模块的映射 self.loaded_plugins: dict[str, PluginBase] = {} # 已加载的插件类实例注册表,插件名 -> 插件类实例 self.failed_plugins: dict[str, str] = {} # 记录加载失败的插件文件及其错误信息,插件名 -> 错误信息 @@ -102,23 +103,25 @@ class PluginManager: if not plugin_dir: return False, 1 - plugin_instance = plugin_class(plugin_dir=plugin_dir) # 实例化插件(可能因为缺少manifest而失败) + module = self.plugin_modules.get(plugin_name) + + if not module or not hasattr(module, "__plugin_meta__"): + self.failed_plugins[plugin_name] = "插件模块中缺少 __plugin_meta__" + logger.error(f"❌ 插件加载失败: {plugin_name} - 缺少 __plugin_meta__") + return False, 1 + + metadata: PluginMetadata = getattr(module, "__plugin_meta__") + + plugin_instance = plugin_class(plugin_dir=plugin_dir, metadata=metadata) if not plugin_instance: logger.error(f"插件 {plugin_name} 实例化失败") return False, 1 + # 检查插件是否启用 if not plugin_instance.enable_plugin: logger.info(f"插件 {plugin_name} 已禁用,跳过加载") return False, 0 - # 检查版本兼容性 - is_compatible, compatibility_error = self._check_plugin_version_compatibility( - plugin_name, plugin_instance.manifest_data - ) - if not is_compatible: - self.failed_plugins[plugin_name] = compatibility_error - logger.error(f"❌ 插件加载失败: {plugin_name} - {compatibility_error}") - return False, 1 if plugin_instance.register_plugin(): self.loaded_plugins[plugin_name] = plugin_instance self._show_plugin_components(plugin_name) @@ -138,21 +141,6 @@ class PluginManager: logger.error(f"❌ 插件注册失败: {plugin_name}") return False, 1 - except FileNotFoundError as e: - # manifest文件缺失 - error_msg = f"缺少manifest文件: {e!s}" - self.failed_plugins[plugin_name] = error_msg - logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}") - return False, 1 - - except ValueError as e: - # manifest文件格式错误或验证失败 - traceback.print_exc() - error_msg = f"manifest验证失败: {e!s}" - self.failed_plugins[plugin_name] = error_msg - logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}") - return False, 1 - except Exception as e: # 其他错误 error_msg = f"未知错误: {e!s}" @@ -284,14 +272,23 @@ class PluginManager: if os.path.isdir(item_path) and not item.startswith(".") and not item.startswith("__"): plugin_file = os.path.join(item_path, "plugin.py") if os.path.exists(plugin_file): - if self._load_plugin_module_file(plugin_file): + module = self._load_plugin_module_file(plugin_file) + if module: + # 动态查找插件类并获取真实的 plugin_name + for attr_name in dir(module): + attr = getattr(module, attr_name) + if isinstance(attr, type) and issubclass(attr, PluginBase) and attr is not PluginBase: + plugin_name = getattr(attr, "plugin_name", None) + if plugin_name: + self.plugin_modules[plugin_name] = module + break loaded_count += 1 else: failed_count += 1 return loaded_count, failed_count - def _load_plugin_module_file(self, plugin_file: str) -> bool: + def _load_plugin_module_file(self, plugin_file: str) -> Optional[Any]: # sourcery skip: extract-method """加载单个插件模块文件 @@ -305,62 +302,36 @@ class PluginManager: module_name = ".".join(plugin_path.parent.parts) try: - # 动态导入插件模块 + # 首先加载 __init__.py 来获取元数据 + init_file = os.path.join(plugin_dir, "__init__.py") + if os.path.exists(init_file): + init_spec = spec_from_file_location(f"{module_name}.__init__", init_file) + if init_spec and init_spec.loader: + init_module = module_from_spec(init_spec) + init_spec.loader.exec_module(init_module) + + # 然后加载 plugin.py spec = spec_from_file_location(module_name, plugin_file) if spec is None or spec.loader is None: logger.error(f"无法创建模块规范: {plugin_file}") - return False + return None module = module_from_spec(spec) - module.__package__ = module_name # 设置模块包名 + module.__package__ = module_name spec.loader.exec_module(module) + # 将 __plugin_meta__ 从 init_module 附加到主模块 + if init_module and hasattr(init_module, "__plugin_meta__"): + setattr(module, "__plugin_meta__", getattr(init_module, "__plugin_meta__")) + logger.debug(f"插件模块加载成功: {plugin_file} -> {plugin_name} ({plugin_dir})") - return True + return module except Exception as e: error_msg = f"加载插件模块 {plugin_file} 失败: {e}" logger.error(error_msg) self.failed_plugins[plugin_name if "plugin_name" in locals() else module_name] = error_msg - return False - - # == 兼容性检查 == - - @staticmethod - def _check_plugin_version_compatibility(plugin_name: str, manifest_data: dict[str, Any]) -> tuple[bool, str]: - """检查插件版本兼容性 - - Args: - plugin_name: 插件名称 - manifest_data: manifest数据 - - Returns: - Tuple[bool, str]: (是否兼容, 错误信息) - """ - if "host_application" not in manifest_data: - return True, "" # 没有版本要求,默认兼容 - - host_app = manifest_data["host_application"] - if not isinstance(host_app, dict): - return True, "" - - min_version = host_app.get("min_version", "") - max_version = host_app.get("max_version", "") - - if not min_version and not max_version: - return True, "" # 没有版本要求,默认兼容 - - try: - current_version = VersionComparator.get_current_host_version() - is_compatible, error_msg = VersionComparator.is_version_in_range(current_version, min_version, max_version) - if not is_compatible: - return False, f"版本不兼容: {error_msg}" - logger.debug(f"插件 {plugin_name} 版本兼容性检查通过") - return True, "" - - except Exception as e: - logger.warning(f"插件 {plugin_name} 版本兼容性检查失败: {e}") - return False, f"插件 {plugin_name} 版本兼容性检查失败: {e}" # 检查失败时默认不允许加载 + return None # == 显示统计与插件信息 == @@ -396,17 +367,6 @@ class PluginManager: logger.info(f" 📦 {plugin_info.display_name}{extra_info}") - # Manifest信息 - if plugin_info.manifest_data: - """ - if plugin_info.keywords: - logger.info(f" 🏷️ 关键词: {', '.join(plugin_info.keywords)}") - if plugin_info.categories: - logger.info(f" 📁 分类: {', '.join(plugin_info.categories)}") - """ - if plugin_info.homepage_url: - logger.info(f" 🌐 主页: {plugin_info.homepage_url}") - # 组件列表 if plugin_info.components: action_components = [ diff --git a/src/plugin_system/utils/__init__.py b/src/plugin_system/utils/__init__.py index bf49e3fa5..26bc638b7 100644 --- a/src/plugin_system/utils/__init__.py +++ b/src/plugin_system/utils/__init__.py @@ -2,18 +2,4 @@ 插件系统工具模块 提供插件开发和管理的实用工具 -""" - -from .manifest_utils import ( - ManifestValidator, - # ManifestGenerator, - # validate_plugin_manifest, - # generate_plugin_manifest, -) - -__all__ = [ - "ManifestValidator", - # "ManifestGenerator", - # "validate_plugin_manifest", - # "generate_plugin_manifest", -] +""" \ No newline at end of file diff --git a/src/plugin_system/utils/manifest_utils.py b/src/plugin_system/utils/manifest_utils.py deleted file mode 100644 index 21025127f..000000000 --- a/src/plugin_system/utils/manifest_utils.py +++ /dev/null @@ -1,517 +0,0 @@ -""" -插件Manifest工具模块 - -提供manifest文件的验证、生成和管理功能 -""" - -import re -from typing import Any - -from src.common.logger import get_logger -from src.config.config import MMC_VERSION - -# if TYPE_CHECKING: -# from src.plugin_system.base.base_plugin import BasePlugin - -logger = get_logger("manifest_utils") - - -class VersionComparator: - """版本号比较器 - - 支持语义化版本号比较,自动处理snapshot版本,并支持向前兼容性检查 - """ - - # 版本兼容性映射表(硬编码) - # 格式: {插件最大支持版本: [实际兼容的版本列表]} - COMPATIBILITY_MAP = { - # 0.8.x 系列向前兼容规则 - "0.8.0": ["0.8.1", "0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.1": ["0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.2": ["0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.3": ["0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.4": ["0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.5": ["0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.6": ["0.8.7", "0.8.8", "0.8.9", "0.8.10"], - "0.8.7": ["0.8.8", "0.8.9", "0.8.10"], - "0.8.8": ["0.8.9", "0.8.10"], - "0.8.9": ["0.8.10"], - # 可以根据需要添加更多兼容映射 - # "0.9.0": ["0.9.1", "0.9.2", "0.9.3"], # 示例:0.9.x系列兼容 - } - - @staticmethod - def normalize_version(version: str) -> str: - """标准化版本号,移除snapshot标识 - - Args: - version: 原始版本号,如 "0.8.0-snapshot.1" - - Returns: - str: 标准化后的版本号,如 "0.8.0" - """ - if not version: - return "0.0.0" - - # 移除snapshot部分 - normalized = re.sub(r"-snapshot\.\d+", "", version.strip()) - normalized = re.sub(r"-alpha\-\d+", "", version.strip()) - - # 确保版本号格式正确 - if not re.match(r"^\d+(\.\d+){0,2}$", normalized): - # 如果不是有效的版本号格式,返回默认版本 - return "0.0.0" - - # 尝试补全版本号 - parts = normalized.split(".") - while len(parts) < 3: - parts.append("0") - normalized = ".".join(parts[:3]) - - return normalized - - @staticmethod - def parse_version(version: str) -> tuple[int, int, int]: - """解析版本号为元组 - - Args: - version: 版本号字符串 - - Returns: - Tuple[int, int, int]: (major, minor, patch) - """ - normalized = VersionComparator.normalize_version(version) - try: - parts = normalized.split(".") - return int(parts[0]), int(parts[1]), int(parts[2]) - except (ValueError, IndexError): - logger.warning(f"无法解析版本号: {version},使用默认版本 0.0.0") - return 0, 0, 0 - - @staticmethod - def compare_versions(version1: str, version2: str) -> int: - """比较两个版本号 - - Args: - version1: 第一个版本号 - version2: 第二个版本号 - - Returns: - int: -1 if version1 < version2, 0 if equal, 1 if version1 > version2 - """ - v1_tuple = VersionComparator.parse_version(version1) - v2_tuple = VersionComparator.parse_version(version2) - - if v1_tuple < v2_tuple: - return -1 - elif v1_tuple > v2_tuple: - return 1 - else: - return 0 - - @staticmethod - def check_forward_compatibility(current_version: str, max_version: str) -> tuple[bool, str]: - """检查向前兼容性(仅使用兼容性映射表) - - Args: - current_version: 当前版本 - max_version: 插件声明的最大支持版本 - - Returns: - Tuple[bool, str]: (是否兼容, 兼容信息) - """ - current_normalized = VersionComparator.normalize_version(current_version) - max_normalized = VersionComparator.normalize_version(max_version) - - # 检查兼容性映射表 - if max_normalized in VersionComparator.COMPATIBILITY_MAP: - compatible_versions = VersionComparator.COMPATIBILITY_MAP[max_normalized] - if current_normalized in compatible_versions: - return True, f"根据兼容性映射表,版本 {current_normalized} 与 {max_normalized} 兼容" - - return False, "" - - @staticmethod - def is_version_in_range(version: str, min_version: str = "", max_version: str = "") -> tuple[bool, str]: - """检查版本是否在指定范围内,支持兼容性检查 - - Args: - version: 要检查的版本号 - min_version: 最小版本号(可选) - max_version: 最大版本号(可选) - - Returns: - Tuple[bool, str]: (是否兼容, 错误信息或兼容信息) - """ - if not min_version and not max_version: - return True, "" - - version_normalized = VersionComparator.normalize_version(version) - - # 检查最小版本 - if min_version: - min_normalized = VersionComparator.normalize_version(min_version) - if VersionComparator.compare_versions(version_normalized, min_normalized) < 0: - return False, f"版本 {version_normalized} 低于最小要求版本 {min_normalized}" - - # 检查最大版本 - if max_version: - max_normalized = VersionComparator.normalize_version(max_version) - comparison = VersionComparator.compare_versions(version_normalized, max_normalized) - - if comparison > 0: - # 严格版本检查失败,尝试兼容性检查 - is_compatible, compat_msg = VersionComparator.check_forward_compatibility( - version_normalized, max_normalized - ) - - if not is_compatible: - return False, f"版本 {version_normalized} 高于最大支持版本 {max_normalized},且无兼容性映射" - - logger.info(f"版本兼容性检查:{compat_msg}") - return True, compat_msg - return True, "" - - @staticmethod - def get_current_host_version() -> str: - """获取当前主机应用版本 - - Returns: - str: 当前版本号 - """ - return VersionComparator.normalize_version(MMC_VERSION) - - @staticmethod - def add_compatibility_mapping(base_version: str, compatible_versions: list) -> None: - """动态添加兼容性映射 - - Args: - base_version: 基础版本(插件声明的最大支持版本) - compatible_versions: 兼容的版本列表 - """ - base_normalized = VersionComparator.normalize_version(base_version) - VersionComparator.COMPATIBILITY_MAP[base_normalized] = [ - VersionComparator.normalize_version(v) for v in compatible_versions - ] - logger.info(f"添加兼容性映射:{base_normalized} -> {compatible_versions}") - - @staticmethod - def get_compatibility_info() -> dict[str, list]: - """获取当前的兼容性映射表 - - Returns: - Dict[str, list]: 兼容性映射表的副本 - """ - return VersionComparator.COMPATIBILITY_MAP.copy() - - -class ManifestValidator: - """Manifest文件验证器""" - - # 必需字段(必须存在且不能为空) - REQUIRED_FIELDS = ["manifest_version", "name", "version", "description", "author"] - - # 可选字段(可以不存在或为空) - OPTIONAL_FIELDS = [ - "license", - "host_application", - "homepage_url", - "repository_url", - "keywords", - "categories", - "default_locale", - "locales_path", - "plugin_info", - ] - - # 建议填写的字段(会给出警告但不会导致验证失败) - RECOMMENDED_FIELDS = ["license", "keywords", "categories"] - - SUPPORTED_MANIFEST_VERSIONS = [1] - - def __init__(self): - self.validation_errors = [] - self.validation_warnings = [] - - def validate_manifest(self, manifest_data: dict[str, Any]) -> bool: - """验证manifest数据 - - Args: - manifest_data: manifest数据字典 - - Returns: - bool: 是否验证通过(只有错误会导致验证失败,警告不会) - """ - self.validation_errors.clear() - self.validation_warnings.clear() - - # 检查必需字段 - for field in self.REQUIRED_FIELDS: - if field not in manifest_data: - self.validation_errors.append(f"缺少必需字段: {field}") - elif not manifest_data[field]: - self.validation_errors.append(f"必需字段不能为空: {field}") - - # 检查manifest版本 - if "manifest_version" in manifest_data: - version = manifest_data["manifest_version"] - if version not in self.SUPPORTED_MANIFEST_VERSIONS: - self.validation_errors.append( - f"不支持的manifest版本: {version},支持的版本: {self.SUPPORTED_MANIFEST_VERSIONS}" - ) - - # 检查作者信息格式 - if "author" in manifest_data: - author = manifest_data["author"] - if isinstance(author, dict): - if "name" not in author or not author["name"]: - self.validation_errors.append("作者信息缺少name字段或为空") - # url字段是可选的 - if author.get("url"): - url = author["url"] - if not (url.startswith("http://") or url.startswith("https://")): - self.validation_warnings.append("作者URL建议使用完整的URL格式") - elif isinstance(author, str): - if not author.strip(): - self.validation_errors.append("作者信息不能为空") - else: - self.validation_errors.append("作者信息格式错误,应为字符串或包含name字段的对象") - # 检查主机应用版本要求(可选) - if "host_application" in manifest_data: - host_app = manifest_data["host_application"] - if isinstance(host_app, dict): - min_version = host_app.get("min_version", "") - max_version = host_app.get("max_version", "") - - # 验证版本字段格式 - for version_field in ["min_version", "max_version"]: - if version_field in host_app and not host_app[version_field]: - self.validation_warnings.append(f"host_application.{version_field}为空") - - # 检查当前主机版本兼容性 - if min_version or max_version: - current_version = VersionComparator.get_current_host_version() - is_compatible, error_msg = VersionComparator.is_version_in_range( - current_version, min_version, max_version - ) - - if not is_compatible: - self.validation_errors.append(f"版本兼容性检查失败: {error_msg} (当前版本: {current_version})") - else: - logger.debug( - f"版本兼容性检查通过: 当前版本 {current_version} 符合要求 [{min_version}, {max_version}]" - ) - else: - self.validation_errors.append("host_application格式错误,应为对象") - - # 检查URL格式(可选字段) - for url_field in ["homepage_url", "repository_url"]: - if manifest_data.get(url_field): - url: str = manifest_data[url_field] - if not (url.startswith("http://") or url.startswith("https://")): - self.validation_warnings.append(f"{url_field}建议使用完整的URL格式") - - # 检查数组字段格式(可选字段) - for list_field in ["keywords", "categories"]: - if list_field in manifest_data: - field_value = manifest_data[list_field] - if field_value is not None and not isinstance(field_value, list): - self.validation_errors.append(f"{list_field}应为数组格式") - elif isinstance(field_value, list): - # 检查数组元素是否为字符串 - for i, item in enumerate(field_value): - if not isinstance(item, str): - self.validation_warnings.append(f"{list_field}[{i}]应为字符串") - - # 检查建议字段(给出警告) - for field in self.RECOMMENDED_FIELDS: - if field not in manifest_data or not manifest_data[field]: - self.validation_warnings.append(f"建议填写字段: {field}") - - # 检查plugin_info结构(可选) - if "plugin_info" in manifest_data: - plugin_info = manifest_data["plugin_info"] - if isinstance(plugin_info, dict): - # 检查components数组 - if "components" in plugin_info: - components = plugin_info["components"] - if not isinstance(components, list): - self.validation_errors.append("plugin_info.components应为数组格式") - else: - for i, component in enumerate(components): - if not isinstance(component, dict): - self.validation_errors.append(f"plugin_info.components[{i}]应为对象") - else: - # 检查组件必需字段 - for comp_field in ["type", "name", "description"]: - if comp_field not in component or not component[comp_field]: - self.validation_errors.append( - f"plugin_info.components[{i}]缺少必需字段: {comp_field}" - ) - else: - self.validation_errors.append("plugin_info应为对象格式") - - return len(self.validation_errors) == 0 - - def get_validation_report(self) -> str: - """获取验证报告""" - report = [] - - if self.validation_errors: - report.append("❌ 验证错误:") - report.extend(f" - {error}" for error in self.validation_errors) - if self.validation_warnings: - report.append("⚠️ 验证警告:") - report.extend(f" - {warning}" for warning in self.validation_warnings) - if not self.validation_errors and not self.validation_warnings: - report.append("✅ Manifest文件验证通过") - - return "\n".join(report) - - -# class ManifestGenerator: -# """Manifest文件生成器""" - -# def __init__(self): -# self.template = { -# "manifest_version": 1, -# "name": "", -# "version": "1.0.0", -# "description": "", -# "author": {"name": "", "url": ""}, -# "license": "MIT", -# "host_application": {"min_version": "1.0.0", "max_version": "4.0.0"}, -# "homepage_url": "", -# "repository_url": "", -# "keywords": [], -# "categories": [], -# "default_locale": "zh-CN", -# "locales_path": "_locales", -# } - -# def generate_from_plugin(self, plugin_instance: BasePlugin) -> Dict[str, Any]: -# """从插件实例生成manifest - -# Args: -# plugin_instance: BasePlugin实例 - -# Returns: -# Dict[str, Any]: 生成的manifest数据 -# """ -# manifest = self.template.copy() - -# # 基本信息 -# manifest["name"] = plugin_instance.plugin_name -# manifest["version"] = plugin_instance.plugin_version -# manifest["description"] = plugin_instance.plugin_description - -# # 作者信息 -# if plugin_instance.plugin_author: -# manifest["author"]["name"] = plugin_instance.plugin_author - -# # 组件信息 -# components = [] -# plugin_components = plugin_instance.get_plugin_components() - -# for component_info, component_class in plugin_components: -# component_data: Dict[str, Any] = { -# "type": component_info.component_type.value, -# "name": component_info.name, -# "description": component_info.description, -# } - -# # 添加激活模式信息(对于Action组件) -# if hasattr(component_class, "focus_activation_type"): -# activation_modes = [] -# if hasattr(component_class, "focus_activation_type"): -# activation_modes.append(component_class.focus_activation_type.value) -# if hasattr(component_class, "normal_activation_type"): -# activation_modes.append(component_class.normal_activation_type.value) -# component_data["activation_modes"] = list(set(activation_modes)) - -# # 添加关键词信息 -# if hasattr(component_class, "activation_keywords"): -# keywords = getattr(component_class, "activation_keywords", []) -# if keywords: -# component_data["keywords"] = keywords - -# components.append(component_data) - -# manifest["plugin_info"] = {"is_built_in": True, "plugin_type": "general", "components": components} - -# return manifest - -# def save_manifest(self, manifest_data: Dict[str, Any], plugin_dir: str) -> bool: -# """保存manifest文件 - -# Args: -# manifest_data: manifest数据 -# plugin_dir: 插件目录 - -# Returns: -# bool: 是否保存成功 -# """ -# try: -# manifest_path = os.path.join(plugin_dir, "_manifest.json") -# with open(manifest_path, "w", encoding="utf-8") as f: -# orjson.dumps(manifest_data, f, ensure_ascii=False, indent=2) -# logger.info(f"Manifest文件已保存: {manifest_path}") -# return True -# except Exception as e: -# logger.error(f"保存manifest文件失败: {e}") -# return False - - -# def validate_plugin_manifest(plugin_dir: str) -> bool: -# """验证插件目录中的manifest文件 - -# Args: -# plugin_dir: 插件目录路径 - -# Returns: -# bool: 是否验证通过 -# """ -# manifest_path = os.path.join(plugin_dir, "_manifest.json") - -# if not os.path.exists(manifest_path): -# logger.warning(f"未找到manifest文件: {manifest_path}") -# return False - -# try: -# with open(manifest_path, "r", encoding="utf-8") as f: -# manifest_data = orjson.loads(f.read()) - -# validator = ManifestValidator() -# is_valid = validator.validate_manifest(manifest_data) - -# logger.info(f"Manifest验证结果:\n{validator.get_validation_report()}") - -# return is_valid - -# except Exception as e: -# logger.error(f"读取或验证manifest文件失败: {e}") -# return False - - -# def generate_plugin_manifest(plugin_instance: BasePlugin, save_to_file: bool = True) -> Optional[Dict[str, Any]]: -# """为插件生成manifest文件 - -# Args: -# plugin_instance: BasePlugin实例 -# save_to_file: 是否保存到文件 - -# Returns: -# Optional[Dict[str, Any]]: 生成的manifest数据 -# """ -# try: -# generator = ManifestGenerator() -# manifest_data = generator.generate_from_plugin(plugin_instance) - -# if save_to_file and plugin_instance.plugin_dir: -# generator.save_manifest(manifest_data, plugin_instance.plugin_dir) - -# return manifest_data - -# except Exception as e: -# logger.error(f"生成manifest文件失败: {e}") -# return None diff --git a/src/plugins/built_in/affinity_flow_chatter/__init__.py b/src/plugins/built_in/affinity_flow_chatter/__init__.py index bc8ebb733..4fe7e792c 100644 --- a/src/plugins/built_in/affinity_flow_chatter/__init__.py +++ b/src/plugins/built_in/affinity_flow_chatter/__init__.py @@ -1,7 +1,15 @@ -""" -亲和力聊天处理器插件 -""" +from src.plugin_system.base.plugin_metadata import PluginMetadata -from .plugin import AffinityChatterPlugin +__plugin_meta__ = PluginMetadata( + name="Affinity Flow Chatter", + description="Built-in chatter plugin for affinity flow with interest scoring and relationship building", + usage="This plugin is automatically triggered by the system.", + version="1.0.0", + author="MoFox", + keywords=["chatter", "affinity", "conversation"], + categories=["Chat", "AI"], + extra={ + "is_built_in": True + } +) -__all__ = ["AffinityChatterPlugin"] diff --git a/src/plugins/built_in/affinity_flow_chatter/_manifest.json b/src/plugins/built_in/affinity_flow_chatter/_manifest.json deleted file mode 100644 index 253365b87..000000000 --- a/src/plugins/built_in/affinity_flow_chatter/_manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "manifest_version": 1, - "name": "affinity_chatter", - "display_name": "Affinity Flow Chatter", - "description": "Built-in chatter plugin for affinity flow with interest scoring and relationship building", - "version": "1.0.0", - "author": "MoFox", - "plugin_class": "AffinityChatterPlugin", - "enabled": true, - "is_built_in": true, - "components": [ - { - "name": "affinity_chatter", - "type": "chatter", - "description": "Affinity flow chatter with intelligent interest scoring and relationship building", - "enabled": true, - "chat_type_allow": ["all"] - } - ], - "host_application": { "min_version": "0.8.0" }, - "keywords": ["chatter", "affinity", "conversation"], - "categories": ["Chat", "AI"] -} \ No newline at end of file diff --git a/src/plugins/built_in/core_actions/__init__.py b/src/plugins/built_in/core_actions/__init__.py new file mode 100644 index 000000000..7107531a6 --- /dev/null +++ b/src/plugins/built_in/core_actions/__init__.py @@ -0,0 +1,17 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="Emoji插件 (Emoji Actions)", + description="可以发送和管理Emoji", + usage="该插件提供 `emoji` action。", + version="1.0.0", + author="SengokuCola", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MaiM-with-u/maibot", + keywords=["emoji", "action", "built-in"], + categories=["Emoji"], + extra={ + "is_built_in": True, + "plugin_type": "action_provider", + } +) \ No newline at end of file diff --git a/src/plugins/built_in/core_actions/_manifest.json b/src/plugins/built_in/core_actions/_manifest.json deleted file mode 100644 index ae70035df..000000000 --- a/src/plugins/built_in/core_actions/_manifest.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "manifest_version": 1, - "name": "Emoji插件 (Emoji Actions)", - "version": "1.0.0", - "description": "可以发送和管理Emoji", - "author": { - "name": "SengokuCola", - "url": "https://github.com/MaiM-with-u" - }, - "license": "GPL-v3.0-or-later", - - "host_application": { - "min_version": "0.10.0" - }, - "homepage_url": "https://github.com/MaiM-with-u/maibot", - "repository_url": "https://github.com/MaiM-with-u/maibot", - "keywords": ["emoji", "action", "built-in"], - "categories": ["Emoji"], - - "default_locale": "zh-CN", - "locales_path": "_locales", - - "plugin_info": { - "is_built_in": true, - "plugin_type": "action_provider", - "components": [ - { - "type": "action", - "name": "emoji", - "description": "作为一条全新的消息,发送一个符合当前情景的表情包来生动地表达情绪。" - } - ] - } -} \ No newline at end of file diff --git a/src/plugins/built_in/maizone_refactored/__init__.py b/src/plugins/built_in/maizone_refactored/__init__.py index dd094256f..6292b9207 100644 --- a/src/plugins/built_in/maizone_refactored/__init__.py +++ b/src/plugins/built_in/maizone_refactored/__init__.py @@ -1,8 +1,17 @@ -""" -让框架能够发现并加载子目录中的组件。 -""" +from src.plugin_system.base.plugin_metadata import PluginMetadata -from .actions.read_feed_action import ReadFeedAction as ReadFeedAction -from .actions.send_feed_action import SendFeedAction as SendFeedAction -from .commands.send_feed_command import SendFeedCommand as SendFeedCommand -from .plugin import MaiZoneRefactoredPlugin as MaiZoneRefactoredPlugin +__plugin_meta__ = PluginMetadata( + name="MaiZone(麦麦空间)- 重构版", + description="(重构版)让你的麦麦发QQ空间说说、评论、点赞,支持AI配图、定时发送和自动监控功能", + usage="该插件提供 `send_feed` 和 `read_feed` action,以及 `send_feed` command。", + version="3.0.0", + author="MoFox-Studio", + license="GPL-v3.0", + repository_url="https://github.com/MoFox-Studio", + keywords=["QQ空间", "说说", "动态", "评论", "点赞", "自动化", "AI配图"], + categories=["社交", "自动化", "QQ空间"], + extra={ + "is_built_in": False, + "plugin_type": "social", + } +) diff --git a/src/plugins/built_in/maizone_refactored/_manifest.json b/src/plugins/built_in/maizone_refactored/_manifest.json deleted file mode 100644 index 840e03060..000000000 --- a/src/plugins/built_in/maizone_refactored/_manifest.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "manifest_version": 1, - "name": "MaiZone(麦麦空间)- 重构版", - "version": "3.0.0", - "description": "(重构版)让你的麦麦发QQ空间说说、评论、点赞,支持AI配图、定时发送和自动监控功能", - "author": { - "name": "MoFox-Studio", - "url": "https://github.com/MoFox-Studio" - }, - "license": "GPL-v3.0", - - "host_application": { - "min_version": "0.10.0" - }, - "keywords": ["QQ空间", "说说", "动态", "评论", "点赞", "自动化", "AI配图"], - "categories": ["社交", "自动化", "QQ空间"], - - "plugin_info": { - "is_built_in": false, - "plugin_type": "social", - "components": [ - { - "type": "action", - "name": "send_feed", - "description": "根据指定主题发送一条QQ空间说说" - }, - { - "type": "action", - "name": "read_feed", - "description": "读取指定好友最近的说说,并评论点赞" - }, - { - "type": "command", - "name": "send_feed", - "description": "通过命令发送QQ空间说说" - } - ], - "features": [ - "智能生成说说内容", - "AI自动配图(硅基流动)", - "自动点赞评论好友说说", - "定时发送说说", - "权限管理系统", - "历史记录避重" - ] - } -} diff --git a/src/plugins/built_in/napcat_adapter_plugin/__init__.py b/src/plugins/built_in/napcat_adapter_plugin/__init__.py new file mode 100644 index 000000000..a279a5409 --- /dev/null +++ b/src/plugins/built_in/napcat_adapter_plugin/__init__.py @@ -0,0 +1,16 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="napcat_plugin", + description="基于OneBot 11协议的NapCat QQ协议插件,提供完整的QQ机器人API接口,使用现有adapter连接", + usage="该插件提供 `napcat_tool` tool。", + version="1.0.0", + author="Windpicker_owo", + license="GPL-v3.0-or-later", + repository_url="https://github.com/Windpicker-owo/InternetSearchPlugin", + keywords=["qq", "bot", "napcat", "onebot", "api", "websocket"], + categories=["protocol"], + extra={ + "is_built_in": False, + } +) \ No newline at end of file diff --git a/src/plugins/built_in/napcat_adapter_plugin/_manifest.json b/src/plugins/built_in/napcat_adapter_plugin/_manifest.json deleted file mode 100644 index 1c8c0686f..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/_manifest.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "manifest_version": 1, - "name": "napcat_plugin", - "version": "1.0.0", - "description": "基于OneBot 11协议的NapCat QQ协议插件,提供完整的QQ机器人API接口,使用现有adapter连接", - "author": { - "name": "Windpicker_owo", - "url": "https://github.com/Windpicker-owo" - }, - "license": "GPL-v3.0-or-later", - - "host_application": { - "min_version": "0.10.0", - "max_version": "0.11.0" - }, - "homepage_url": "https://github.com/Windpicker-owo/InternetSearchPlugin", - "repository_url": "https://github.com/Windpicker-owo/InternetSearchPlugin", - "keywords": ["qq", "bot", "napcat", "onebot", "api", "websocket"], - "categories": ["protocol"], - "default_locale": "zh-CN", - "locales_path": "_locales", - - "plugin_info": { - "is_built_in": false, - "components": [ - { - "type": "tool", - "name": "napcat_tool", - "description": "NapCat QQ协议综合工具,提供消息发送、群管理、好友管理、文件操作等完整功能" - } - ], - "features": [ - "消息发送与接收", - "群管理功能", - "好友管理功能", - "文件上传下载", - "AI语音功能", - "群签到与戳一戳", - "现有adapter连接" - ] - } -} \ No newline at end of file diff --git a/src/plugins/built_in/permission_management/__init__.py b/src/plugins/built_in/permission_management/__init__.py new file mode 100644 index 000000000..8ba15b88d --- /dev/null +++ b/src/plugins/built_in/permission_management/__init__.py @@ -0,0 +1,16 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="权限管理插件(Permission Management)", + description="通过系统API管理权限", + usage="该插件提供 `permission_management` command。", + version="1.0.0", + author="MoFox-Studio", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MoFox-Studio", + keywords=["plugins", "permission", "management", "built-in"], + extra={ + "is_built_in": True, + "plugin_type": "permission", + } +) \ No newline at end of file diff --git a/src/plugins/built_in/permission_management/_manifest.json b/src/plugins/built_in/permission_management/_manifest.json deleted file mode 100644 index 3b26bf41c..000000000 --- a/src/plugins/built_in/permission_management/_manifest.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "manifest_version": 1, - "name": "权限管理插件(Permission Management)", - "version": "1.0.0", - "description": "通过系统API管理权限", - "author": { - "name": "MoFox-Studio", - "url": "https://github.com/MoFox-Studio" - }, - "license": "GPL-v3.0-or-later", - "host_application": { - "min_version": "0.10.0" - }, - "keywords": [ - "plugins", - "permission", - "management", - "built-in" - ], - "default_locale": "zh-CN", - "locales_path": "_locales", - "plugin_info": { - "is_built_in": true, - "plugin_type": "permission", - "components": [ - { - "type": "command", - "name": "permission_management", - "description": "管理用户权限,包括添加、删除和修改权限等操作。" - } - ] - } -} \ No newline at end of file diff --git a/src/plugins/built_in/plugin_management/__init__.py b/src/plugins/built_in/plugin_management/__init__.py new file mode 100644 index 000000000..c75d4668b --- /dev/null +++ b/src/plugins/built_in/plugin_management/__init__.py @@ -0,0 +1,17 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="插件和组件管理 (Plugin and Component Management)", + description="通过系统API管理插件和组件的生命周期,包括加载、卸载、启用和禁用等操作。", + usage="该插件提供 `plugin_management` command。", + version="1.0.0", + author="MaiBot团队", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MaiM-with-u/maibot", + keywords=["plugins", "components", "management", "built-in"], + categories=["Core System", "Plugin Management"], + extra={ + "is_built_in": True, + "plugin_type": "plugin_management", + } +) \ No newline at end of file diff --git a/src/plugins/built_in/plugin_management/_manifest.json b/src/plugins/built_in/plugin_management/_manifest.json deleted file mode 100644 index f394b8677..000000000 --- a/src/plugins/built_in/plugin_management/_manifest.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "manifest_version": 1, - "name": "插件和组件管理 (Plugin and Component Management)", - "version": "1.0.0", - "description": "通过系统API管理插件和组件的生命周期,包括加载、卸载、启用和禁用等操作。", - "author": { - "name": "MaiBot团队", - "url": "https://github.com/MaiM-with-u" - }, - "license": "GPL-v3.0-or-later", - "host_application": { - "min_version": "0.9.1" - }, - "homepage_url": "https://github.com/MaiM-with-u/maibot", - "repository_url": "https://github.com/MaiM-with-u/maibot", - "keywords": [ - "plugins", - "components", - "management", - "built-in" - ], - "categories": [ - "Core System", - "Plugin Management" - ], - "default_locale": "zh-CN", - "locales_path": "_locales", - "plugin_info": { - "is_built_in": true, - "plugin_type": "plugin_management", - "components": [ - { - "type": "command", - "name": "plugin_management", - "description": "管理插件和组件的生命周期,包括加载、卸载、启用和禁用等操作。" - } - ] - } -} \ No newline at end of file diff --git a/src/plugins/built_in/proactive_thinker/__init__.py b/src/plugins/built_in/proactive_thinker/__init__.py index e69de29bb..7800a04dd 100644 --- a/src/plugins/built_in/proactive_thinker/__init__.py +++ b/src/plugins/built_in/proactive_thinker/__init__.py @@ -0,0 +1,17 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="MoFox-Bot主动思考", + description="主动思考插件", + usage="该插件由系统自动触发。", + version="1.0.0", + author="MoFox-Studio", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MoFox-Studio", + keywords=["主动思考","自己发消息"], + categories=["Chat", "Integration"], + extra={ + "is_built_in": True, + "plugin_type": "functional" + } +) \ No newline at end of file diff --git a/src/plugins/built_in/proactive_thinker/_manifest.json b/src/plugins/built_in/proactive_thinker/_manifest.json deleted file mode 100644 index 30f2a138e..000000000 --- a/src/plugins/built_in/proactive_thinker/_manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "manifest_version": 1, - "name": "MoFox-Bot主动思考", - "version": "1.0.0", - "description": "主动思考插件", - "author": { - "name": "MoFox-Studio", - "url": "https://github.com/MoFox-Studio" - }, - "license": "GPL-v3.0-or-later", - - "host_application": { - "min_version": "0.10.0" - }, - "keywords": ["主动思考","自己发消息"], - "categories": ["Chat", "Integration"], - - "default_locale": "zh-CN", - "locales_path": "_locales", - - "plugin_info": { - "is_built_in": "true", - "plugin_type": "functional" - } -} diff --git a/src/plugins/built_in/social_toolkit_plugin/__init__.py b/src/plugins/built_in/social_toolkit_plugin/__init__.py index e69de29bb..01b6fba64 100644 --- a/src/plugins/built_in/social_toolkit_plugin/__init__.py +++ b/src/plugins/built_in/social_toolkit_plugin/__init__.py @@ -0,0 +1,17 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="MoFox-Bot工具箱", + description="一个集合多种实用功能的插件,旨在提升聊天体验和效率。", + usage="该插件提供多种命令,详情请查阅文档。", + version="1.0.0", + author="MoFox-Studio", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MoFox-Studio", + keywords=["emoji", "reaction", "like", "表情", "回应", "点赞"], + categories=["Chat", "Integration"], + extra={ + "is_built_in": "true", + "plugin_type": "functional" + } +) \ No newline at end of file diff --git a/src/plugins/built_in/social_toolkit_plugin/_manifest.json b/src/plugins/built_in/social_toolkit_plugin/_manifest.json deleted file mode 100644 index d1bbb2a15..000000000 --- a/src/plugins/built_in/social_toolkit_plugin/_manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "manifest_version": 1, - "name": "MoFox-Bot工具箱", - "version": "1.0.0", - "description": "一个集合多种实用功能的插件,旨在提升聊天体验和效率。", - "author": { - "name": "MoFox-Studio", - "url": "https://github.com/MoFox-Studio" - }, - "license": "GPL-v3.0-or-later", - - "host_application": { - "min_version": "0.10.0" - }, - "keywords": ["emoji", "reaction", "like", "表情", "回应", "点赞"], - "categories": ["Chat", "Integration"], - - "default_locale": "zh-CN", - "locales_path": "_locales", - - "plugin_info": { - "is_built_in": "true", - "plugin_type": "functional" - } -} diff --git a/src/plugins/built_in/tts_plugin/__init__.py b/src/plugins/built_in/tts_plugin/__init__.py new file mode 100644 index 000000000..591d27882 --- /dev/null +++ b/src/plugins/built_in/tts_plugin/__init__.py @@ -0,0 +1,17 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="文本转语音插件 (Text-to-Speech)", + description="将文本转换为语音进行播放的插件,支持多种语音模式和智能语音输出场景判断。", + usage="该插件提供 `tts_action` action。", + version="0.1.0", + author="MaiBot团队", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MaiM-with-u/maibot", + keywords=["tts", "voice", "audio", "speech", "accessibility"], + categories=["Audio Tools", "Accessibility", "Voice Assistant"], + extra={ + "is_built_in": True, + "plugin_type": "audio_processor", + } +) \ No newline at end of file diff --git a/src/plugins/built_in/tts_plugin/_manifest.json b/src/plugins/built_in/tts_plugin/_manifest.json deleted file mode 100644 index 05a233757..000000000 --- a/src/plugins/built_in/tts_plugin/_manifest.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "manifest_version": 1, - "name": "文本转语音插件 (Text-to-Speech)", - "version": "0.1.0", - "description": "将文本转换为语音进行播放的插件,支持多种语音模式和智能语音输出场景判断。", - "author": { - "name": "MaiBot团队", - "url": "https://github.com/MaiM-with-u" - }, - "license": "GPL-v3.0-or-later", - - "host_application": { - "min_version": "0.8.0" - }, - "homepage_url": "https://github.com/MaiM-with-u/maibot", - "repository_url": "https://github.com/MaiM-with-u/maibot", - "keywords": ["tts", "voice", "audio", "speech", "accessibility"], - "categories": ["Audio Tools", "Accessibility", "Voice Assistant"], - - "default_locale": "zh-CN", - "locales_path": "_locales", - - "plugin_info": { - "is_built_in": true, - "plugin_type": "audio_processor", - "components": [ - { - "type": "action", - "name": "tts_action", - "description": "将文本转换为语音进行播放", - "activation_modes": ["llm_judge", "keyword"], - "keywords": ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"] - } - ], - "features": [ - "文本转语音播放", - "智能场景判断", - "关键词触发", - "支持多种语音模式" - ] - } -} diff --git a/src/plugins/built_in/web_search_tool/__init__.py b/src/plugins/built_in/web_search_tool/__init__.py new file mode 100644 index 000000000..5c2024cae --- /dev/null +++ b/src/plugins/built_in/web_search_tool/__init__.py @@ -0,0 +1,16 @@ +from src.plugin_system.base.plugin_metadata import PluginMetadata + +__plugin_meta__ = PluginMetadata( + name="Web Search Tool", + description="A tool for searching the web.", + usage="This plugin provides a `web_search` tool.", + version="1.0.0", + author="MoFox-Studio", + license="GPL-v3.0-or-later", + repository_url="https://github.com/MoFox-Studio", + keywords=["web", "search", "tool"], + categories=["Tools"], + extra={ + "is_built_in": True, + } +) \ No newline at end of file