diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 7d49a9aa..5e7a2034 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -201,4 +201,6 @@ def run(host: Optional[str] = None, get_driver().run(host, port, *args, **kwargs) -from nonebot.plugin import load_plugin, load_plugins, get_loaded_plugins +from nonebot.plugin import on_message, on_notice, on_request, on_metaevent +from nonebot.plugin import on_startswith, on_endswith, on_command, on_regex +from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins diff --git a/nonebot/plugin.py b/nonebot/plugin.py index b71f1f61..6241687d 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -10,8 +10,9 @@ from importlib._bootstrap import _load from nonebot.log import logger from nonebot.matcher import Matcher from nonebot.permission import Permission +from nonebot.typing import Handler, RuleChecker from nonebot.rule import Rule, startswith, endswith, command, regex -from nonebot.typing import Set, Dict, Type, Tuple, Union, Optional, ModuleType, RuleChecker +from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType plugins: Dict[str, "Plugin"] = {} @@ -20,10 +21,9 @@ _tmp_matchers: Set[Type[Matcher]] = set() class Plugin(object): - # TODO: store plugin informations - def __init__(self, module_path: str, module: ModuleType, + def __init__(self, name: str, module: ModuleType, matchers: Set[Type[Matcher]]): - self.module_path = module_path + self.name = name self.module = module self.matchers = matchers @@ -31,7 +31,7 @@ class Plugin(object): def on(rule: Union[Rule, RuleChecker] = Rule(), permission: Permission = Permission(), *, - handlers: Optional[list] = None, + handlers: Optional[List[Handler]] = None, temp: bool = False, priority: int = 1, block: bool = False, @@ -50,7 +50,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(), def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(), *, - handlers: Optional[list] = None, + handlers: Optional[List[Handler]] = None, temp: bool = False, priority: int = 1, block: bool = False, @@ -70,7 +70,7 @@ def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(), def on_message(rule: Union[Rule, RuleChecker] = Rule(), permission: Permission = Permission(), *, - handlers: Optional[list] = None, + handlers: Optional[List[Handler]] = None, temp: bool = False, priority: int = 1, block: bool = True, @@ -89,7 +89,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(), def on_notice(rule: Union[Rule, RuleChecker] = Rule(), *, - handlers: Optional[list] = None, + handlers: Optional[List[Handler]] = None, temp: bool = False, priority: int = 1, block: bool = False, @@ -108,7 +108,7 @@ def on_notice(rule: Union[Rule, RuleChecker] = Rule(), def on_request(rule: Union[Rule, RuleChecker] = Rule(), *, - handlers: Optional[list] = None, + handlers: Optional[List[Handler]] = None, temp: bool = False, priority: int = 1, block: bool = False, @@ -149,9 +149,19 @@ def on_command(cmd: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]: if isinstance(cmd, str): cmd = (cmd,) - return on_message(command(cmd) & - rule, permission, **kwargs) if rule else on_message( - command(cmd), permission, **kwargs) + + async def _strip_cmd(bot, event, state: dict): + message = event.message + event.message = message.__class__( + str(message)[len(state["_prefix"]["raw_command"]):].strip()) + + handlers = kwargs.pop("handlers", []) + handlers.insert(0, _strip_cmd) + + return on_message( + command(cmd) & + rule, permission, handlers=handlers, **kwargs) if rule else on_message( + command(cmd), permission, handlers=handlers, **kwargs) def on_regex(pattern: str, @@ -167,12 +177,20 @@ def on_regex(pattern: str, def load_plugin(module_path: str) -> 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" + ) + 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}"') + logger.opt( + colors=True).info(f'Succeeded to import "{module_path}"') return plugin except Exception as e: logger.opt(colors=True, exception=e).error( @@ -189,7 +207,11 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]: continue spec = module_info.module_finder.find_spec(name) - if spec.name in sys.modules: + if spec.name in plugins: + continue + elif spec.name in sys.modules: + logger.warning( + f"Module {spec.name} has been loaded by other plugin! Ignored") continue try: @@ -207,5 +229,9 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]: return loaded_plugins +def load_builtin_plugins(): + return load_plugin("nonebot.plugins.base") + + def get_loaded_plugins() -> Set[Plugin]: return set(plugins.values()) diff --git a/nonebot/plugins/base.py b/nonebot/plugins/base.py new file mode 100644 index 00000000..de743d14 --- /dev/null +++ b/nonebot/plugins/base.py @@ -0,0 +1,10 @@ +from nonebot.rule import to_me +from nonebot.plugin import on_command +from nonebot.typing import Bot, Event + +say = on_command("say", to_me()) + + +@say.handle() +async def repeat(bot: Bot, event: Event, state: dict): + await bot.send(message=event.message, event=event) diff --git a/nonebot/rule.py b/nonebot/rule.py index 34c1c7b4..a992e488 100644 --- a/nonebot/rule.py +++ b/nonebot/rule.py @@ -74,13 +74,21 @@ class TrieRule: suffix = cls.suffix.longest_prefix( message_r.data["text"].rstrip()[::-1]) - state["_prefix"] = {prefix.key: prefix.value} if prefix else {} - state["_suffix"] = {suffix.key: suffix.value} if suffix else {} + state["_prefix"] = { + "raw_command": prefix.key, + "command": prefix.value + } if prefix else {} + state["_suffix"] = { + "raw_command": suffix.key, + "command": suffix.value + } if suffix else {} return ({ - prefix.key: prefix.value + "raw_command": prefix.key, + "command": prefix.value } if prefix else {}, { - suffix.key: suffix.value + "raw_command": suffix.key, + "command": suffix.value } if suffix else {}) @@ -122,7 +130,7 @@ def command(command: Tuple[str, ...]) -> Rule: TrieRule.add_prefix(f"{start}{sep.join(command)}", command) async def _command(bot: Bot, event: Event, state: dict) -> bool: - return command in state["_prefix"].values() + return command == state["_prefix"]["command"] return Rule(_command) diff --git a/tests/bot.py b/tests/bot.py index 095e2eab..31ffb6c8 100644 --- a/tests/bot.py +++ b/tests/bot.py @@ -7,10 +7,22 @@ import sys sys.path.insert(0, os.path.abspath("..")) import nonebot +from nonebot.log import logger, default_format + +# test custom log +logger.add("error.log", + rotation="00:00", + diagnose=False, + level="ERROR", + format=default_format) nonebot.init() app = nonebot.get_asgi() +# load builtin plugin +nonebot.load_plugin("nonebot.plugins.base") + +# load local plugins nonebot.load_plugins("test_plugins") if __name__ == "__main__": diff --git a/tests/test_plugins/test_package/test_command.py b/tests/test_plugins/test_package/test_command.py index 8c811ddc..af060fff 100644 --- a/tests/test_plugins/test_package/test_command.py +++ b/tests/test_plugins/test_package/test_command.py @@ -11,7 +11,7 @@ test_command = on_command("帮助", to_me()) @test_command.handle() async def test_handler(bot: Bot, event: Event, state: dict): - args = str(event.message)[len(list(state["_prefix"].keys())[0]):].strip() + args = str(event.message).strip() print("[!] Command:", state["_prefix"], "Args:", args) if args: state["help"] = args