diff --git a/docs/api.md b/docs/api.md index 15b55a68..2345db77 100644 --- a/docs/api.md +++ b/docs/api.md @@ -52,6 +52,14 @@ sidebar: auto 命令参数的类型。 +### `CommandHandler_T` + +- **类型:** `Callable[[CommandSession], Any]` + +- **说明:** + + 命令处理函数 + ### `State_T` - **类型:** `Dict[str, Any]` @@ -725,7 +733,7 @@ sidebar: auto #### `module` -- **类型:** `Any` +- **类型:** `ModuleType` - **说明:** @@ -747,6 +755,152 @@ sidebar: auto 插件使用方法,从插件模块的 `__plugin_usage__` 特殊变量获得,如果没有此变量,则为 `None`。 +#### `commands` + +- **类型:** `Set[Command]` + +- **说明:** + + 插件包含的命令,通过 `on_command` 装饰器注册。 + +#### `nl_processors` + +- **类型:** `Set[NLProcessor]` + +- **说明:** + + 插件包含的自然语言处理器,通过 `on_natural_language` 装饰器注册。 + +#### `event_handlers` + +- **类型:** `Set[EventHandler]` + +- **说明:** + + 插件包含的事件处理器(包含通知、请求),通过 `on_notice` 以及 `on_request` 装饰器注册。 + + + +### _class_ `PluginManager` + +插件管理器:用于管理插件的加载以及插件中命令、自然语言处理器、事件处理器的开关 + +#### `cmd_manager` + +- **类型:** `CommandManager` + +- **说明:** + + 命令管理器实例 + +#### `nlp_manager` + +- **类型:** `NLPManager` + +- **说明:** + + 自然语言管理器实例 + +#### _class method_ `add_plugin(cls, module_path, plugin)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### _class method_ `get_plugin(cls, module_path)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### _class method_ `remove_plugin(cls, module_path)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### _class method_ `switch_plugin_global(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### _class method_ `switch_command_global(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### _class method_ `switch_nlprocessor_global(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### _class method_ `switch_eventhandler_global(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### `switch_plugin(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### `switch_command(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + +#### `switch_nlprocessor(cls, module_path, state=None)` + +- **说明:** + +- **参数:** + +- **返回:** + +- **用法:** + ### `load_plugin(module_name)` - **说明:** @@ -755,11 +909,11 @@ sidebar: auto - **参数:** - - `module_name: str`: 模块名 + - `module_path: str`: 模块路径 -- **返回:** +- **返回:** - - `bool`: 加载成功 + - `Optional[Plugin]`: 加载后生成的 Plugin 对象 - **用法:** @@ -769,6 +923,32 @@ sidebar: auto 加载 `nonebot_tuling` 插件。 +### `reload_plugin(module_name)` + +- **说明:** + + :::danger + 该函数为强制重载,可能导致不可预测的错误! + ::: + + 重载插件(等价于)。 + +- **参数:** + + - `module_path: str`: 模块路径 + +- **返回:** + + - `Optional[Plugin]`: 重载后生成的 Plugin 对象 + +- **用法:** + + ```python + nonebot.plugin.reload_plugin('nonebot_tuling') + ``` + + 重载 `nonebot_tuling` 插件。 + ### `load_plugins(plugin_dir, module_prefix)` - **说明:** @@ -780,14 +960,14 @@ sidebar: auto - `plugin_dir: str`: 插件目录 - `module_prefix: str`: 模块前缀 -- **返回:** +- **返回:** - - `int:` 加载成功的插件数量 + - `Set[Plugin]`: 加载成功的插件 Plugin 对象 - **用法:** ```python - nonebot.plugin.load_plugins(path.join(path.dirname(__file__), 'plugins'), 'amadeus.plugins') + nonebot.plugin.load_plugins(path.join(path.dirname(__file__), 'plugins'), 'plugins') ``` 加载 `plugins` 目录下的插件。 @@ -1280,7 +1460,7 @@ sidebar: auto - **参数:** - - `reduce: bool`: 是否先化简消息段列表(合并相邻的 `text` 段),对于从 酷Q 收到的消息,通常不需要开启 + - `reduce: bool`: 是否先化简消息段列表(合并相邻的 `text` 段),对于从 酷 Q 收到的消息,通常不需要开启 - **返回:** @@ -1325,7 +1505,7 @@ sidebar: auto ## `nonebot.command` 模块 -### _decorator_ `on_command(name, *, aliases=(), permission=perm.EVERYBODY, only_to_me=True, privileged=False, shell_like=False)` +### _decorator_ `on_command(name, *, aliases=(), permission=perm.EVERYBODY, only_to_me=True, privileged=False, shell_like=False)` - **说明:** @@ -1527,7 +1707,7 @@ sidebar: auto - **说明:** - 命令会话当前参数。实际上是 酷Q 收到的消息去掉命令名的剩下部分,因此可能存在 CQ 码。 + 命令会话当前参数。实际上是 酷 Q 收到的消息去掉命令名的剩下部分,因此可能存在 CQ 码。 #### _readonly property_ `current_arg_text` @@ -1951,7 +2131,7 @@ session.get('arg1', prompt='请输入 arg1:', ## `nonebot.natural_language` 模块 -### _decorator_ `on_natural_language(keywords=None, *, permission=EVERYBODY, only_to_me=True, only_short_message=True, allow_empty_message=False)` +### _decorator_ `on_natural_language(keywords=None, *, permission=EVERYBODY, only_to_me=True, only_short_message=True, allow_empty_message=False)` - **说明:** @@ -2090,7 +2270,7 @@ session.get('arg1', prompt='请输入 arg1:', ## `nonebot.notice_request` 模块 -### _decorator_ `on_notice(*events)` +### _decorator_ `on_notice(*events)` - **说明:** @@ -2123,7 +2303,7 @@ session.get('arg1', prompt='请输入 arg1:', 收到所有通知时打日志,收到新成员进群通知时除了打日志还发送欢迎信息。 -### _decorator_ `on_request(*events)` +### _decorator_ `on_request(*events)` - **说明:** @@ -2290,6 +2470,7 @@ session.get('arg1', prompt='请输入 arg1:', 发送消息到 Session 对应的上下文中。 - **参数:** + - `message: Message_T`: 要发送的消息内容 - `at_sender: bool`: 是否 @ 发送者,对私聊不起作用 - `ensure_private: bool`: 确保消息发送到私聊,对于群组和讨论组消息上下文,会私聊发送者 diff --git a/docs/changelog.md b/docs/changelog.md index b387ccdb..56a10a56 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,12 @@ sidebar: auto # 更新日志 +## v1.6.0 + +- 新增 `PluginManager` `CommandManager` `NLPManager` 管理类,用于插件的开启与关闭 +- 修改 `message_preprocessor` 在消息预处理阶段可以进行针对该消息的插件开启与关闭 +- 移动 `on_command` `on_natural_language` `on_notice` `on_request` 装饰器至 plugin 模块 + ## v1.5.0 - 新增 `nonebot.on_startup` 装饰器,用于注册 NoneBot 启动时回调函数 diff --git a/nonebot/plugin.py b/nonebot/plugin.py index ff89640c..9659d848 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -1,5 +1,6 @@ import os import re +import sys import shlex import warnings import importlib @@ -266,18 +267,44 @@ def reload_plugin(module_path: str) -> Optional[Plugin]: result = PluginManager.remove_plugin(module_path) if not result: return None - return load_plugin(module_path) + + for module in list( + filter(lambda x: x.startswith(module_path), sys.modules.keys())): + del sys.modules[module] + + _tmp_command.clear() + _tmp_nl_processor.clear() + _tmp_event_handler.clear() + try: + module = importlib.import_module(module_path) + name = getattr(module, '__plugin_name__', None) + usage = getattr(module, '__plugin_usage__', None) + commands = _tmp_command.copy() + nl_processors = _tmp_nl_processor.copy() + event_handlers = _tmp_event_handler.copy() + plugin = Plugin(module, name, usage, commands, nl_processors, + event_handlers) + PluginManager.add_plugin(module_path, plugin) + logger.info(f'Succeeded to reload "{module_path}"') + return plugin + except Exception as e: + logger.error(f'Failed to reload "{module_path}", error: {e}') + logger.exception(e) + return None def load_plugins(plugin_dir: str, module_prefix: str) -> Set[Plugin]: - """ - Find all non-hidden modules or packages in a given directory, + """Find all non-hidden modules or packages in a given directory, and import them with the given module prefix. - :param plugin_dir: plugin directory to search - :param module_prefix: module prefix used while importing - :return: number of plugins successfully loaded + Args: + plugin_dir (str): Plugin directory to search + module_prefix (str): Module prefix used while importing + + Returns: + Set[Plugin]: Set of plugin objects successfully loaded """ + count = set() for name in os.listdir(plugin_dir): path = os.path.join(plugin_dir, name) diff --git a/package.json b/package.json index a4437e31..12405471 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "scripts": { - "docs:dev": "vuepress dev -h 127.0.0.1 -p 8888 --debug docs", + "docs:dev": "vuepress dev --host 127.0.0.1 -p 8888 --debug docs", "docs:build": "vuepress build docs" }, "devDependencies": { "vuepress": "^0.14.8" } -} \ No newline at end of file +}