diff --git a/nonebot/plugin/load.py b/nonebot/plugin/load.py index 2693e8f9..47f5a22a 100644 --- a/nonebot/plugin/load.py +++ b/nonebot/plugin/load.py @@ -16,7 +16,7 @@ from .manager import PluginManager from . import _managers, get_plugin, _module_name_to_plugin_name try: # pragma: py-gte-311 - import tomllib # pyright: reportMissingImports=false + import tomllib # pyright: ignore[reportMissingImports] except ModuleNotFoundError: # pragma: py-lt-311 import tomli as tomllib diff --git a/nonebot/plugin/plugin.py b/nonebot/plugin/plugin.py index 4cf1db08..7a83c4b4 100644 --- a/nonebot/plugin/plugin.py +++ b/nonebot/plugin/plugin.py @@ -1,9 +1,11 @@ -"""本模块定义插件对象。 +"""本模块定义插件相关信息。 FrontMatter: sidebar_position: 3 description: nonebot.plugin.plugin 模块 """ + +import contextlib from types import ModuleType from dataclasses import field, dataclass from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional @@ -11,11 +13,11 @@ from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional from pydantic import BaseModel from nonebot.matcher import Matcher - -# FIXME: backport for nonebug -from . import _plugins as plugins # nopycln: import +from nonebot.utils import resolve_dot_notation if TYPE_CHECKING: + from nonebot.adapters import Adapter + from .manager import PluginManager @@ -24,14 +26,39 @@ class PluginMetadata: """插件元信息,由插件编写者提供""" name: str - """插件可阅读名称""" + """插件名称""" description: str """插件功能介绍""" usage: str """插件使用方法""" + type: Optional[str] = None + """插件类型,用于商店分类""" + homepage: Optional[str] = None + """插件主页""" config: Optional[Type[BaseModel]] = None """插件配置项""" + supported_adapters: Optional[Set[str]] = None + """插件支持的适配器模块路径 + + 格式为 `[:]`,`~` 为 `nonebot.adapters.` 的缩写。 + + `None` 表示支持**所有适配器**。 + """ extra: Dict[Any, Any] = field(default_factory=dict) + """插件额外信息,可由插件编写者自由扩展定义""" + + def get_supported_adapters(self) -> Optional[Set[Type["Adapter"]]]: + """获取当前已安装的插件支持适配器类列表""" + if self.supported_adapters is None: + return None + + adapters = set() + for adapter in self.supported_adapters: + with contextlib.suppress(ModuleNotFoundError, AttributeError): + adapters.add( + resolve_dot_notation(adapter, "Adapter", "nonebot.adapters.") + ) + return adapters @dataclass(eq=False) diff --git a/tests/plugins/metadata.py b/tests/plugins/metadata.py index 4c775dc6..bf89176e 100644 --- a/tests/plugins/metadata.py +++ b/tests/plugins/metadata.py @@ -1,5 +1,6 @@ from pydantic import BaseModel +from nonebot.adapters import Adapter from nonebot.plugin import PluginMetadata @@ -7,10 +8,17 @@ class Config(BaseModel): custom: str = "" +class FakeAdapter(Adapter): + ... + + __plugin_meta__ = PluginMetadata( name="测试插件", description="测试插件元信息", usage="无法使用", + type="application", + homepage="https://v2.nonebot.dev", config=Config, + supported_adapters={"~onebot.v11", "plugins.metadata:FakeAdapter"}, extra={"author": "NoneBot"}, ) diff --git a/tests/test_plugin/test_load.py b/tests/test_plugin/test_load.py index 644bbee8..a0eb15ab 100644 --- a/tests/test_plugin/test_load.py +++ b/tests/test_plugin/test_load.py @@ -128,7 +128,7 @@ async def test_require_not_found(): @pytest.mark.asyncio async def test_plugin_metadata(): - from plugins.metadata import Config + from plugins.metadata import Config, FakeAdapter plugin = nonebot.get_plugin("metadata") assert plugin @@ -137,6 +137,11 @@ async def test_plugin_metadata(): "name": "测试插件", "description": "测试插件元信息", "usage": "无法使用", + "type": "application", + "homepage": "https://v2.nonebot.dev", "config": Config, + "supported_adapters": {"~onebot.v11", "plugins.metadata:FakeAdapter"}, "extra": {"author": "NoneBot"}, } + + assert plugin.metadata.get_supported_adapters() == {FakeAdapter}