From 03c7b59df5467bd8aedf938b399b8edea739653f Mon Sep 17 00:00:00 2001 From: synodriver <624805065@qq.com> Date: Thu, 19 Nov 2020 13:07:02 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86at=E6=9C=BA?= =?UTF-8?q?=E5=99=A8=E4=BA=BA=E6=97=B6=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot/adapters/cqhttp/__init__.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 88b71820..f6e91681 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -112,14 +112,14 @@ async def _check_reply(bot: "Bot", event: "Event"): return msg_seg = event.message[index] event.reply = await bot.get_msg(message_id=msg_seg.data["id"]) - if event.reply["sender"]["user_id"] == event.self_id: + if event.reply["sender"]["user_id"] == int(event.self_id): # TODO 这里一个int一个str需要修改 event.to_me = True del event.message[index] if not event.message: event.message.append(MessageSegment.text("")) -def _check_at_me(bot: "Bot", event: "Event"): +def _check_at_me(bot: "Bot", event: "Event"): # TODO 只有开头at机器人才会识别,其他的不管 应该删除前面两个如果存在的at以及后面正文前面的空格 """ :说明: @@ -138,8 +138,8 @@ def _check_at_me(bot: "Bot", event: "Event"): else: at_me_seg = MessageSegment.at(event.self_id) - # check the first segment - if event.message[0] == at_me_seg: + # check the first segment at 空 at + if event.message[0] == at_me_seg: # TODO 有时候不能正常判断 event.to_me = True del event.message[0] if event.message[0].type == "text": @@ -147,13 +147,14 @@ def _check_at_me(bot: "Bot", event: "Event"): "text"].lstrip() if not event.message[0].data["text"]: del event.message[0] - if event.message[0] == at_me_seg: - del event.message[0] - if event.message[0].type == "text": - event.message[0].data["text"] = event.message[0].data[ - "text"].lstrip() - if not event.message[0].data["text"]: - del event.message[0] + if event.message: + if event.message[0] == at_me_seg: + del event.message[0] + if event.message[0].type == "text": + event.message[0].data["text"] = event.message[0].data[ + "text"].lstrip() + if not event.message[0].data["text"]: + del event.message[0] if not event.to_me: # check the last segment From 9e1eee166b39eb1d3a1c97b5e302984effd57adc Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 19 Nov 2020 13:57:49 +0800 Subject: [PATCH 02/15] :bug: fix index error when check to me --- nonebot/adapters/cqhttp/__init__.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index f6e91681..546fe862 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -112,14 +112,15 @@ async def _check_reply(bot: "Bot", event: "Event"): return msg_seg = event.message[index] event.reply = await bot.get_msg(message_id=msg_seg.data["id"]) - if event.reply["sender"]["user_id"] == int(event.self_id): # TODO 这里一个int一个str需要修改 + # ensure string comparation + if str(event.reply["sender"]["user_id"]) == str(event.self_id): event.to_me = True del event.message[index] if not event.message: event.message.append(MessageSegment.text("")) -def _check_at_me(bot: "Bot", event: "Event"): # TODO 只有开头at机器人才会识别,其他的不管 应该删除前面两个如果存在的at以及后面正文前面的空格 +def _check_at_me(bot: "Bot", event: "Event"): """ :说明: @@ -138,23 +139,22 @@ def _check_at_me(bot: "Bot", event: "Event"): # TODO 只有开头at机器人才 else: at_me_seg = MessageSegment.at(event.self_id) - # check the first segment at 空 at - if event.message[0] == at_me_seg: # TODO 有时候不能正常判断 + # check the first segment + if event.message[0] == at_me_seg: event.to_me = True del event.message[0] - if event.message[0].type == "text": + if event.message and event.message[0].type == "text": event.message[0].data["text"] = event.message[0].data[ "text"].lstrip() if not event.message[0].data["text"]: del event.message[0] - if event.message: - if event.message[0] == at_me_seg: - del event.message[0] - if event.message[0].type == "text": - event.message[0].data["text"] = event.message[0].data[ - "text"].lstrip() - if not event.message[0].data["text"]: - del event.message[0] + if event.message and event.message[0] == at_me_seg: + del event.message[0] + if event.message and event.message[0].type == "text": + event.message[0].data["text"] = event.message[0].data[ + "text"].lstrip() + if not event.message[0].data["text"]: + del event.message[0] if not event.to_me: # check the last segment From 0c39b06355ef710132a96bebc7f42df76f941bb5 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 19 Nov 2020 14:54:58 +0800 Subject: [PATCH 03/15] :memo: add changelog --- pages/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/changelog.md b/pages/changelog.md index ef8bad8f..641a68c5 100644 --- a/pages/changelog.md +++ b/pages/changelog.md @@ -7,6 +7,7 @@ sidebar: auto ## v2.0.0a6 - 修复 block 失效问题 (hotfix) +- 修复 cqhttp 检查 to me 时出现 IndexError ## v2.0.0a5 From 8b3af9fb474c0fb3db9bfcd8f87be155127d122e Mon Sep 17 00:00:00 2001 From: Muchan Date: Thu, 19 Nov 2020 20:53:28 +0800 Subject: [PATCH 04/15] =?UTF-8?q?feat(message):=20=E4=BC=A0=E9=80=92matche?= =?UTF-8?q?r=E6=8A=9B=E5=87=BA=E7=9A=84StopPropagation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot/message.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nonebot/message.py b/nonebot/message.py index 90af9a96..ffd26b81 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -149,6 +149,8 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, try: logger.debug(f"Running matcher {matcher}") await matcher.run(bot, event, state) + except StopPropagation as e: + exception = e except Exception as e: logger.opt(colors=True, exception=e).error( f"Running matcher {matcher} failed." @@ -166,7 +168,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, "Error when running RunPostProcessors" ) - if matcher.block: + if matcher.block or isinstance(exception, StopPropagation): raise StopPropagation From 49ad65f449b62379471251da98ccaddde7e0ab85 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 19 Nov 2020 21:21:54 +0800 Subject: [PATCH 05/15] :bug: fix #66 resolve reply message --- nonebot/adapters/cqhttp/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 546fe862..5f2c8b09 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -116,6 +116,8 @@ async def _check_reply(bot: "Bot", event: "Event"): if str(event.reply["sender"]["user_id"]) == str(event.self_id): event.to_me = True del event.message[index] + if len(event.message) > index and event.message[index].type == "at": + del event.message[index] if not event.message: event.message.append(MessageSegment.text("")) From 0b6a1ca1a4eb6de9fda507f9496d86630f5d21f2 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 19 Nov 2020 22:04:44 +0800 Subject: [PATCH 06/15] :bug: fix #69 stop running expired matcher --- nonebot/message.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nonebot/message.py b/nonebot/message.py index ffd26b81..76f14b25 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -92,8 +92,9 @@ async def _check_matcher(priority: int, bot: Bot, event: Event, async def _check(Matcher: Type[Matcher], bot: Bot, event: Event, state: dict) -> Optional[Type[Matcher]]: try: - if await Matcher.check_perm( - bot, event) and await Matcher.check_rule(bot, event, state): + if (Matcher.expire_time and datetime.now() <= Matcher.expire_time + ) and await Matcher.check_perm( + bot, event) and await Matcher.check_rule(bot, event, state): return Matcher except Exception as e: logger.opt(colors=True, exception=e).error( From a5935e77c9af168358d04c5a13bef9ca263196e1 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Thu, 19 Nov 2020 23:18:03 +0800 Subject: [PATCH 07/15] :ambulance: fix matcher check --- nonebot/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nonebot/message.py b/nonebot/message.py index 76f14b25..1b102633 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -92,7 +92,7 @@ async def _check_matcher(priority: int, bot: Bot, event: Event, async def _check(Matcher: Type[Matcher], bot: Bot, event: Event, state: dict) -> Optional[Type[Matcher]]: try: - if (Matcher.expire_time and datetime.now() <= Matcher.expire_time + if (not Matcher.expire_time or datetime.now() <= Matcher.expire_time ) and await Matcher.check_perm( bot, event) and await Matcher.check_rule(bot, event, state): return Matcher From a734790bcfc8ca1f37340824cccdc37f937e522c Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 20 Nov 2020 01:07:24 +0800 Subject: [PATCH 08/15] :bug: fix remain space after reply --- nonebot/adapters/cqhttp/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 5f2c8b09..92d118af 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -118,6 +118,9 @@ async def _check_reply(bot: "Bot", event: "Event"): del event.message[index] if len(event.message) > index and event.message[index].type == "at": del event.message[index] + if len(event.message) > index and event.message[index].type == "test": + event.message[index].data["text"] = event.message[index].data[ + "text"].strip() if not event.message: event.message.append(MessageSegment.text("")) From 7fdddf4d8f86278db3853a6d9d7b8b995fcd063f Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 20 Nov 2020 01:37:53 +0800 Subject: [PATCH 09/15] :pencil2: fix typo --- nonebot/adapters/cqhttp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 92d118af..d67184fe 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -118,7 +118,7 @@ async def _check_reply(bot: "Bot", event: "Event"): del event.message[index] if len(event.message) > index and event.message[index].type == "at": del event.message[index] - if len(event.message) > index and event.message[index].type == "test": + if len(event.message) > index and event.message[index].type == "text": event.message[index].data["text"] = event.message[index].data[ "text"].strip() if not event.message: From 7b15d0d01f21315c35f709bcbee184e1ce2ee02e Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Fri, 20 Nov 2020 12:53:31 +0800 Subject: [PATCH 10/15] :ambulance: fix not delete empty text --- nonebot/adapters/cqhttp/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index d67184fe..714e9f62 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -118,9 +118,11 @@ async def _check_reply(bot: "Bot", event: "Event"): del event.message[index] if len(event.message) > index and event.message[index].type == "at": del event.message[index] - if len(event.message) > index and event.message[index].type == "text": - event.message[index].data["text"] = event.message[index].data[ - "text"].strip() + if len(event.message) > index and event.message[index].type == "text": + event.message[index].data["text"] = event.message[index].data[ + "text"].lstrip() + if not event.message[index].data["text"]: + del event.message[index] if not event.message: event.message.append(MessageSegment.text("")) From 71fad44d23350c52f20bcb638963928f798d4dcc Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sat, 21 Nov 2020 18:33:35 +0800 Subject: [PATCH 11/15] :sparkles: get plugin func --- nonebot/__init__.py | 3 ++- nonebot/plugin.py | 14 +++++++++++++- nonebot/plugin.pyi | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/nonebot/__init__.py b/nonebot/__init__.py index c704479e..61f7b9a9 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -239,4 +239,5 @@ async def _start_scheduler(): from nonebot.plugin import on_message, on_notice, on_request, on_metaevent, CommandGroup from nonebot.plugin import on_startswith, on_endswith, on_keyword, on_command, on_regex -from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins +from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins +from nonebot.plugin import get_plugin, get_loaded_plugins diff --git a/nonebot/plugin.py b/nonebot/plugin.py index 32be4dbc..d9aba299 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -459,10 +459,22 @@ def load_builtin_plugins() -> Optional[Plugin]: return load_plugin("nonebot.plugins.base") +def get_plugin(name: str) -> Optional[Plugin]: + """ + :说明: + 获取当前导入的某个插件。 + :参数: + * ``name: str``: 插件名,与 ``load_plugin`` 参数一致。如果为 ``load_plugins`` 导入的插件,则为文件(夹)名。 + :返回: + - ``Optional[Plugin]`` + """ + return plugins.get(name) + + def get_loaded_plugins() -> Set[Plugin]: """ :说明: - 获取当前已导入的插件。 + 获取当前已导入的所有插件。 :返回: - ``Set[Plugin]`` """ diff --git a/nonebot/plugin.pyi b/nonebot/plugin.pyi index 6cf93b97..68bb41fd 100644 --- a/nonebot/plugin.pyi +++ b/nonebot/plugin.pyi @@ -141,6 +141,10 @@ def load_builtin_plugins(): ... +def get_plugin(name: str) -> Optional[Plugin]: + ... + + def get_loaded_plugins() -> Set[Plugin]: ... From b36f95862a496d104727b42833afd4dbb190a8f3 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sat, 21 Nov 2020 20:37:44 +0800 Subject: [PATCH 12/15] :memo: update change log --- pages/changelog.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pages/changelog.md b/pages/changelog.md index 641a68c5..c64953d6 100644 --- a/pages/changelog.md +++ b/pages/changelog.md @@ -4,10 +4,15 @@ sidebar: auto # 更新日志 +## v2.0.0a7 + +- 修复 cqhttp 检查 to me 时出现 IndexError +- 修复已失效的事件响应器仍会运行一次的 bug +- 修改 cqhttp 检查 reply 时未去除后续 at 以及空格 + ## v2.0.0a6 - 修复 block 失效问题 (hotfix) -- 修复 cqhttp 检查 to me 时出现 IndexError ## v2.0.0a5 From 9373bd09ed66090e53546b34cd86c59767c1d08b Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sat, 21 Nov 2020 20:40:09 +0800 Subject: [PATCH 13/15] :alembic: add export require option --- nonebot/__init__.py | 2 +- nonebot/plugin.py | 136 ++++++++++++++++++++++-------- nonebot/plugin.pyi | 25 +++++- pages/changelog.md | 2 + tests/bot.py | 2 + tests/test_plugins/test_export.py | 15 ++++ 6 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 tests/test_plugins/test_export.py diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 61f7b9a9..312a8c67 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -240,4 +240,4 @@ async def _start_scheduler(): from nonebot.plugin import on_message, on_notice, on_request, on_metaevent, CommandGroup from nonebot.plugin import on_startswith, on_endswith, on_keyword, on_command, on_regex from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins -from nonebot.plugin import get_plugin, get_loaded_plugins +from nonebot.plugin import export, require, get_plugin, get_loaded_plugins diff --git a/nonebot/plugin.py b/nonebot/plugin.py index d9aba299..fd9195b7 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -11,6 +11,7 @@ import pkgutil import importlib from dataclasses import dataclass from importlib._bootstrap import _load +from contextvars import Context, ContextVar, copy_context from nonebot.log import logger from nonebot.matcher import Matcher @@ -25,7 +26,45 @@ plugins: Dict[str, "Plugin"] = {} :说明: 已加载的插件 """ -_tmp_matchers: Set[Type[Matcher]] = set() +_tmp_matchers: ContextVar[Set[Type[Matcher]]] = ContextVar("_tmp_matchers") +_export: ContextVar["Export"] = ContextVar("_export") + + +class Export(dict): + """ + :说明: + 插件导出内容以使得其他插件可以获得。 + :示例: + + .. code-block:: python + + nonebot.export().default = "bar" + + @nonebot.export() + def some_function(): + pass + + @nonebot.export().sub + def something_else(): + pass + """ + + def __call__(self, func, **kwargs): + self[func.__name__] = func + self.update(kwargs) + return func + + def __setitem__(self, key, value): + super().__setitem__(key, + Export(value) if isinstance(value, dict) else value) + + def __setattr__(self, name, value): + self[name] = Export(value) if isinstance(value, dict) else value + + def __getattr__(self, name): + if name not in self: + self[name] = Export() + return self[name] @dataclass(eq=False) @@ -46,6 +85,7 @@ class Plugin(object): - **类型**: ``Set[Type[Matcher]]`` - **说明**: 插件内定义的 ``Matcher`` """ + export: Export def on(type: str = "", @@ -80,7 +120,7 @@ def on(type: str = "", block=block, handlers=handlers, default_state=state) - _tmp_matchers.add(matcher) + _tmp_matchers.get().add(matcher) return matcher @@ -112,7 +152,7 @@ def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None, block=block, handlers=handlers, default_state=state) - _tmp_matchers.add(matcher) + _tmp_matchers.get().add(matcher) return matcher @@ -146,7 +186,7 @@ def on_message(rule: Optional[Union[Rule, RuleChecker]] = None, block=block, handlers=handlers, default_state=state) - _tmp_matchers.add(matcher) + _tmp_matchers.get().add(matcher) return matcher @@ -178,7 +218,7 @@ def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None, block=block, handlers=handlers, default_state=state) - _tmp_matchers.add(matcher) + _tmp_matchers.get().add(matcher) return matcher @@ -210,7 +250,7 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = None, block=block, handlers=handlers, default_state=state) - _tmp_matchers.add(matcher) + _tmp_matchers.get().add(matcher) return matcher @@ -387,27 +427,35 @@ def load_plugin(module_path: str) -> Optional[Plugin]: :返回: - ``Optional[Plugin]`` """ - try: - _tmp_matchers.clear() - if module_path in plugins: - return plugins[module_path] - elif module_path in sys.modules: - logger.warning( - f"Module {module_path} has been loaded by other plugins! Ignored" + + def _load_plugin(module_path: str) -> Optional[Plugin]: + try: + _tmp_matchers.set(set()) + _export.set(Export()) + if module_path in plugins: + return plugins[module_path] + elif module_path in sys.modules: + logger.warning( + f"Module {module_path} has been loaded by other plugins! Ignored" + ) + return + module = importlib.import_module(module_path) + for m in _tmp_matchers.get(): + m.module = module_path + plugin = Plugin(module_path, module, _tmp_matchers.get(), + _export.get()) + plugins[module_path] = plugin + logger.opt( + colors=True).info(f'Succeeded to import "{module_path}"') + return plugin + except Exception as e: + logger.opt(colors=True, exception=e).error( + f'Failed to import "{module_path}"' ) - return - module = importlib.import_module(module_path) - for m in _tmp_matchers: - m.module = module_path - plugin = Plugin(module_path, module, _tmp_matchers.copy()) - plugins[module_path] = plugin - logger.opt( - colors=True).info(f'Succeeded to import "{module_path}"') - return plugin - except Exception as e: - logger.opt(colors=True, exception=e).error( - f'Failed to import "{module_path}"') - return None + return None + + context: Context = copy_context() + return context.run(_load_plugin, module_path) def load_plugins(*plugin_dir: str) -> Set[Plugin]: @@ -419,33 +467,42 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]: :返回: - ``Set[Plugin]`` """ - loaded_plugins = set() - for module_info in pkgutil.iter_modules(plugin_dir): - _tmp_matchers.clear() + + def _load_plugin(module_info) -> Optional[Plugin]: + _tmp_matchers.set(set()) + _export.set(Export()) name = module_info.name if name.startswith("_"): - continue + return spec = module_info.module_finder.find_spec(name, None) if spec.name in plugins: - continue + return elif spec.name in sys.modules: logger.warning( f"Module {spec.name} has been loaded by other plugin! Ignored") - continue + return try: module = _load(spec) - for m in _tmp_matchers: + for m in _tmp_matchers.get(): m.module = name - plugin = Plugin(name, module, _tmp_matchers.copy()) + plugin = Plugin(name, module, _tmp_matchers.get(), _export.get()) plugins[name] = plugin - loaded_plugins.add(plugin) logger.opt(colors=True).info(f'Succeeded to import "{name}"') + return plugin except Exception as e: logger.opt(colors=True, exception=e).error( f'Failed to import "{name}"') + return None + + loaded_plugins = set() + for module_info in pkgutil.iter_modules(plugin_dir): + context: Context = copy_context() + result = context.run(_load_plugin, module_info) + if result: + loaded_plugins.add(result) return loaded_plugins @@ -479,3 +536,12 @@ def get_loaded_plugins() -> Set[Plugin]: - ``Set[Plugin]`` """ return set(plugins.values()) + + +def export() -> Export: + return _export.get() + + +def require(name: str) -> Optional[Export]: + plugin = get_plugin(name) + return plugin.export if plugin else None diff --git a/nonebot/plugin.pyi b/nonebot/plugin.pyi index 68bb41fd..37d775d6 100644 --- a/nonebot/plugin.pyi +++ b/nonebot/plugin.pyi @@ -1,17 +1,32 @@ import re +from contextvars import ContextVar from nonebot.typing import Rule, Matcher, Handler, Permission, RuleChecker from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType plugins: Dict[str, "Plugin"] = ... -_tmp_matchers: Set[Type[Matcher]] = ... +_tmp_matchers: ContextVar[Set[Type[Matcher]]] = ... +_export: ContextVar["Export"] = ... + + +class Export(dict): + + def __call__(self, func, **kwargs): + ... + + def __setattr__(self, name, value): + ... + + def __getattr__(self, name): + ... class Plugin(object): name: str module: ModuleType matcher: Set[Type[Matcher]] + export: Export def on(type: str = ..., @@ -149,6 +164,14 @@ def get_loaded_plugins() -> Set[Plugin]: ... +def export() -> Export: + ... + + +def require(name: str) -> Export: + ... + + class CommandGroup: def __init__(self, diff --git a/pages/changelog.md b/pages/changelog.md index c64953d6..859be509 100644 --- a/pages/changelog.md +++ b/pages/changelog.md @@ -9,6 +9,8 @@ sidebar: auto - 修复 cqhttp 检查 to me 时出现 IndexError - 修复已失效的事件响应器仍会运行一次的 bug - 修改 cqhttp 检查 reply 时未去除后续 at 以及空格 +- 添加 get_plugin 获取插件函数 +- 添加插件 export, require 方法 ## v2.0.0a6 diff --git a/tests/bot.py b/tests/bot.py index 7a294564..68a4e399 100644 --- a/tests/bot.py +++ b/tests/bot.py @@ -22,6 +22,8 @@ nonebot.load_builtin_plugins() # load local plugins nonebot.load_plugins("test_plugins") +print(nonebot.require("test_export")) + # modify some config / config depends on loaded configs config = nonebot.get_driver().config config.custom_config3 = config.custom_config1 diff --git a/tests/test_plugins/test_export.py b/tests/test_plugins/test_export.py new file mode 100644 index 00000000..ec549571 --- /dev/null +++ b/tests/test_plugins/test_export.py @@ -0,0 +1,15 @@ +import nonebot + +export = nonebot.export() +export.foo = "bar" +export["bar"] = "foo" + + +@export +def a(): + pass + + +@export.sub +def b(): + pass From d70f622a24280fe3b81707cd8b6d3aaba342cec0 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sat, 21 Nov 2020 20:50:33 +0800 Subject: [PATCH 14/15] :bulb: update docstring --- docs/api/nonebot.md | 9 ++++ docs/api/plugin.md | 104 +++++++++++++++++++++++++++++++++++++++++++- nonebot/__init__.py | 3 ++ nonebot/plugin.py | 22 ++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) diff --git a/docs/api/nonebot.md b/docs/api/nonebot.md index cdb72bdb..68708c95 100644 --- a/docs/api/nonebot.md +++ b/docs/api/nonebot.md @@ -49,9 +49,18 @@ sidebarDepth: 0 * `load_builtin_plugins` => `nonebot.plugin.load_builtin_plugins` +* `get_plugin` => `nonebot.plugin.get_plugin` + + * `get_loaded_plugins` => `nonebot.plugin.get_loaded_plugins` +* `export` => `nonebot.plugin.export` + + +* `require` => `nonebot.plugin.require` + + ## `get_driver()` diff --git a/docs/api/plugin.md b/docs/api/plugin.md index 42cdf84f..5f7a7d46 100644 --- a/docs/api/plugin.md +++ b/docs/api/plugin.md @@ -25,6 +25,37 @@ sidebarDepth: 0 +## _class_ `Export` + +基类:`dict` + + +* **说明** + + 插件导出内容以使得其他插件可以获得。 + + + +* **示例** + + +```python +nonebot.export().default = "bar" + +@nonebot.export() +def some_function(): + pass + +# this don't work under python 3.9 +# use +# export = nonebot.export(); @export.sub +# instead +@nonebot.export().sub +def something_else(): + pass +``` + + ## _class_ `Plugin` 基类:`object` @@ -59,6 +90,15 @@ sidebarDepth: 0 * **说明**: 插件内定义的 `Matcher` +### `export` + + +* **类型**: `Export` + + +* **说明**: 插件内定义的导出内容 + + ## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None)` @@ -614,12 +654,35 @@ sidebarDepth: 0 +## `get_plugin(name)` + + +* **说明** + + 获取当前导入的某个插件。 + + + +* **参数** + + + * `name: str`: 插件名,与 `load_plugin` 参数一致。如果为 `load_plugins` 导入的插件,则为文件(夹)名。 + + + +* **返回** + + + * `Optional[Plugin]` + + + ## `get_loaded_plugins()` * **说明** - 获取当前已导入的插件。 + 获取当前已导入的所有插件。 @@ -627,3 +690,42 @@ sidebarDepth: 0 * `Set[Plugin]` + + + +## `export()` + + +* **说明** + + 获取插件的导出内容对象 + + + +* **返回** + + + * `Export` + + + +## `require(name)` + + +* **说明** + + 获取一个插件的导出内容 + + + +* **参数** + + + * `name: str`: 插件名,与 `load_plugin` 参数一致。如果为 `load_plugins` 导入的插件,则为文件(夹)名。 + + + +* **返回** + + + * `Optional[Export]` diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 312a8c67..d642ff44 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -17,7 +17,10 @@ - ``load_plugin`` => ``nonebot.plugin.load_plugin`` - ``load_plugins`` => ``nonebot.plugin.load_plugins`` - ``load_builtin_plugins`` => ``nonebot.plugin.load_builtin_plugins`` +- ``get_plugin`` => ``nonebot.plugin.get_plugin`` - ``get_loaded_plugins`` => ``nonebot.plugin.get_loaded_plugins`` +- ``export`` => ``nonebot.plugin.export`` +- ``require`` => ``nonebot.plugin.require`` """ import importlib diff --git a/nonebot/plugin.py b/nonebot/plugin.py index fd9195b7..f063726c 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -44,6 +44,10 @@ class Export(dict): def some_function(): pass + # this don't work under python 3.9 + # use + # export = nonebot.export(); @export.sub + # instead @nonebot.export().sub def something_else(): pass @@ -86,6 +90,10 @@ class Plugin(object): - **说明**: 插件内定义的 ``Matcher`` """ export: Export + """ + - **类型**: ``Export`` + - **说明**: 插件内定义的导出内容 + """ def on(type: str = "", @@ -539,9 +547,23 @@ def get_loaded_plugins() -> Set[Plugin]: def export() -> Export: + """ + :说明: + 获取插件的导出内容对象 + :返回: + - ``Export`` + """ return _export.get() def require(name: str) -> Optional[Export]: + """ + :说明: + 获取一个插件的导出内容 + :参数: + * ``name: str``: 插件名,与 ``load_plugin`` 参数一致。如果为 ``load_plugins`` 导入的插件,则为文件(夹)名。 + :返回: + - ``Optional[Export]`` + """ plugin = get_plugin(name) return plugin.export if plugin else None From 07b72f87d7f91a13dfb9e51c3041c7d204014523 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 22 Nov 2020 01:51:23 +0800 Subject: [PATCH 15/15] :alien: update vuepress base --- docs/.vuepress/config.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 8faed7a5..e339e2ca 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -1,6 +1,7 @@ const path = require("path"); module.exports = context => ({ + base: process.env.VUEPRESS_BASE || "/", title: "NoneBot", description: "基于 酷Q 的 Python 异步 QQ 机器人框架", markdown: { @@ -117,12 +118,7 @@ module.exports = context => ({ title: "进阶", collapsable: false, sidebar: "auto", - children: [ - "", - "scheduler", - "permission", - "runtime-hook" - ] + children: ["", "scheduler", "permission", "runtime-hook"] } ], "/api/": [