diff --git a/nonebot/adapters/cqhttp/message.py b/nonebot/adapters/cqhttp/message.py index 18cbfea9..6ecc73a3 100644 --- a/nonebot/adapters/cqhttp/message.py +++ b/nonebot/adapters/cqhttp/message.py @@ -213,7 +213,7 @@ class Message(BaseMessage): for cqcode in re.finditer( r"\[CQ:(?P[a-zA-Z0-9-_.]+)" r"(?P" - r"(?:,[a-zA-Z0-9-_.]+=[^,\]]+))*" + r"(?:,[a-zA-Z0-9-_.]+=[^,\]]+)*" r"),?\]", msg): yield "text", unescape(msg[text_begin:cqcode.pos + cqcode.start()]) diff --git a/nonebot/matcher.py b/nonebot/matcher.py index af4448e2..16676359 100644 --- a/nonebot/matcher.py +++ b/nonebot/matcher.py @@ -221,6 +221,26 @@ class Matcher(metaclass=MatcherMeta): cls._default_parser = func return func + @staticmethod + def process_handler(handler: Handler) -> Handler: + signature = inspect.signature(handler, follow_wrapped=False) + bot = signature.parameters.get("bot") + event = signature.parameters.get("event") + state = signature.parameters.get("state") + if not bot: + raise ValueError("Handler missing parameter 'bot'") + handler.__params__ = { + "bot": bot.annotation, + "event": event.annotation if event else None, + "state": State if state else None + } + return handler + + @classmethod + def append_handler(cls, handler: Handler) -> None: + # Process handler first + cls.handlers.append(cls.process_handler(handler)) + @classmethod def handle(cls) -> Callable[[Handler], Handler]: """ @@ -234,7 +254,7 @@ class Matcher(metaclass=MatcherMeta): """ def _decorator(func: Handler) -> Handler: - cls.handlers.append(func) + cls.append_handler(func) return func return _decorator @@ -257,11 +277,11 @@ class Matcher(metaclass=MatcherMeta): if cls.handlers: # 已有前置handlers则接受一条新的消息,否则视为接收初始消息 - cls.handlers.append(_receive) + cls.append_handler(_receive) def _decorator(func: Handler) -> Handler: if not cls.handlers or cls.handlers[-1] is not func: - cls.handlers.append(func) + cls.append_handler(func) return func @@ -306,21 +326,22 @@ class Matcher(metaclass=MatcherMeta): else: state[state["_current_key"]] = str(event.get_message()) - cls.handlers.append(_key_getter) - cls.handlers.append(_key_parser) + cls.append_handler(_key_getter) + cls.append_handler(_key_parser) def _decorator(func: Handler) -> Handler: if not hasattr(cls.handlers[-1], "__wrapped__"): + cls.process_handler(func) parser = cls.handlers.pop() @wraps(func) async def wrapper(bot: "Bot", event: "Event", state: State): await parser(bot, event, state) - await func(bot, event, state) + await cls.run_handler(func, bot, event, state) if "_current_key" in state: del state["_current_key"] - cls.handlers.append(wrapper) + cls.append_handler(wrapper) return func @@ -406,6 +427,23 @@ class Matcher(metaclass=MatcherMeta): await bot.send(event=event, message=prompt, **kwargs) raise RejectedException + @classmethod + async def run_handler(cls, handler: Handler, bot: "Bot", event: "Event", + state: State): + if not hasattr(handler, "__params__"): + cls.process_handler(handler) + params = getattr(handler, "__params__") + BotType = ((params["bot"] is not inspect.Parameter.empty) and + inspect.isclass(params["bot"]) and params["bot"]) + EventType = ((params["event"] is not inspect.Parameter.empty) and + inspect.isclass(params["event"]) and params["event"]) + if (BotType and not isinstance(bot, BotType)) or ( + EventType and not isinstance(event, EventType)): + return + args = {"bot": bot, "event": event, "state": state} + await handler( + **{k: v for k, v in args.items() if params[k] is not None}) + # 运行handlers async def run(self, bot: "Bot", event: "Event", state: State): b_t = current_bot.set(bot) @@ -416,12 +454,7 @@ class Matcher(metaclass=MatcherMeta): for _ in range(len(self.handlers)): handler = self.handlers.pop(0) - signature = inspect.signature(handler) - BotType = signature.parameters.get("bot").annotation - if BotType is not inspect.Parameter.empty and inspect.isclass( - BotType) and not isinstance(bot, BotType): - continue - await handler(bot, event, self.state) + await self.run_handler(handler, bot, event, self.state) except RejectedException: self.handlers.insert(0, handler) # type: ignore diff --git a/poetry.lock b/poetry.lock index 027d0f07..8cd5e301 100644 --- a/poetry.lock +++ b/poetry.lock @@ -158,7 +158,7 @@ reference = "aliyun" [[package]] name = "certifi" -version = "2020.11.8" +version = "2020.12.5" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -250,14 +250,14 @@ reference = "aliyun" [[package]] name = "cryptography" -version = "3.2.1" +version = "3.3.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "dev" optional = true -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" [package.dependencies] -cffi = ">=1.8,<1.11.3 || >1.11.3" +cffi = ">=1.12" six = ">=1.4.1" [package.extras] @@ -518,13 +518,14 @@ reference = "aliyun" [[package]] name = "importlib-metadata" -version = "3.1.1" +version = "3.3.0" description = "Read metadata from Python packages" category = "dev" optional = true python-versions = ">=3.6" [package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] @@ -687,7 +688,7 @@ reference = "aliyun" [[package]] name = "packaging" -version = "20.7" +version = "20.8" description = "Core utilities for Python packages" category = "dev" optional = false @@ -792,14 +793,14 @@ reference = "aliyun" [[package]] name = "pydash" -version = "4.9.0" +version = "4.9.1" description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library." category = "dev" optional = false python-versions = "*" [package.extras] -dev = ["coverage", "flake8", "mock", "pylint", "pytest", "pytest-cov", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel"] +dev = ["coverage", "docformatter", "flake8", "invoke", "mock", "pylint", "pytest", "pytest-cov", "pytest-flake8", "pytest-pylint", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel", "black", "flake8-black", "flake8-bugbear", "flake8-isort", "isort"] [package.source] type = "legacy" @@ -821,7 +822,7 @@ reference = "aliyun" [[package]] name = "pygments" -version = "2.7.2" +version = "2.7.3" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -1584,8 +1585,8 @@ cached-property = [ {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] certifi = [ - {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"}, - {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] cffi = [ {file = "cffi-1.14.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775"}, @@ -1640,28 +1641,20 @@ cookiecutter = [ {file = "cookiecutter-1.7.2.tar.gz", hash = "sha256:efb6b2d4780feda8908a873e38f0e61778c23f6a2ea58215723bcceb5b515dac"}, ] cryptography = [ - {file = "cryptography-3.2.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:6dc59630ecce8c1f558277ceb212c751d6730bd12c80ea96b4ac65637c4f55e7"}, - {file = "cryptography-3.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:75e8e6684cf0034f6bf2a97095cb95f81537b12b36a8fedf06e73050bb171c2d"}, - {file = "cryptography-3.2.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4e7268a0ca14536fecfdf2b00297d4e407da904718658c1ff1961c713f90fd33"}, - {file = "cryptography-3.2.1-cp27-cp27m-win32.whl", hash = "sha256:7117319b44ed1842c617d0a452383a5a052ec6aa726dfbaffa8b94c910444297"}, - {file = "cryptography-3.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:a733671100cd26d816eed39507e585c156e4498293a907029969234e5e634bc4"}, - {file = "cryptography-3.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:a75f306a16d9f9afebfbedc41c8c2351d8e61e818ba6b4c40815e2b5740bb6b8"}, - {file = "cryptography-3.2.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5849d59358547bf789ee7e0d7a9036b2d29e9a4ddf1ce5e06bb45634f995c53e"}, - {file = "cryptography-3.2.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:bd717aa029217b8ef94a7d21632a3bb5a4e7218a4513d2521c2a2fd63011e98b"}, - {file = "cryptography-3.2.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:efe15aca4f64f3a7ea0c09c87826490e50ed166ce67368a68f315ea0807a20df"}, - {file = "cryptography-3.2.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:32434673d8505b42c0de4de86da8c1620651abd24afe91ae0335597683ed1b77"}, - {file = "cryptography-3.2.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:7b8d9d8d3a9bd240f453342981f765346c87ade811519f98664519696f8e6ab7"}, - {file = "cryptography-3.2.1-cp35-cp35m-win32.whl", hash = "sha256:d3545829ab42a66b84a9aaabf216a4dce7f16dbc76eb69be5c302ed6b8f4a29b"}, - {file = "cryptography-3.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:a4e27ed0b2504195f855b52052eadcc9795c59909c9d84314c5408687f933fc7"}, - {file = "cryptography-3.2.1-cp36-abi3-win32.whl", hash = "sha256:13b88a0bd044b4eae1ef40e265d006e34dbcde0c2f1e15eb9896501b2d8f6c6f"}, - {file = "cryptography-3.2.1-cp36-abi3-win_amd64.whl", hash = "sha256:07ca431b788249af92764e3be9a488aa1d39a0bc3be313d826bbec690417e538"}, - {file = "cryptography-3.2.1-cp36-cp36m-win32.whl", hash = "sha256:a035a10686532b0587d58a606004aa20ad895c60c4d029afa245802347fab57b"}, - {file = "cryptography-3.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:d26a2557d8f9122f9bf445fc7034242f4375bd4e95ecda007667540270965b13"}, - {file = "cryptography-3.2.1-cp37-cp37m-win32.whl", hash = "sha256:545a8550782dda68f8cdc75a6e3bf252017aa8f75f19f5a9ca940772fc0cb56e"}, - {file = "cryptography-3.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:55d0b896631412b6f0c7de56e12eb3e261ac347fbaa5d5e705291a9016e5f8cb"}, - {file = "cryptography-3.2.1-cp38-cp38-win32.whl", hash = "sha256:3cd75a683b15576cfc822c7c5742b3276e50b21a06672dc3a800a2d5da4ecd1b"}, - {file = "cryptography-3.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:d25cecbac20713a7c3bc544372d42d8eafa89799f492a43b79e1dfd650484851"}, - {file = "cryptography-3.2.1.tar.gz", hash = "sha256:d3d5e10be0cf2a12214ddee45c6bd203dab435e3d83b4560c03066eda600bfe3"}, + {file = "cryptography-3.3.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:c366df0401d1ec4e548bebe8f91d55ebcc0ec3137900d214dd7aac8427ef3030"}, + {file = "cryptography-3.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9f6b0492d111b43de5f70052e24c1f0951cb9e6022188ebcb1cc3a3d301469b0"}, + {file = "cryptography-3.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a69bd3c68b98298f490e84519b954335154917eaab52cf582fa2c5c7efc6e812"}, + {file = "cryptography-3.3.1-cp27-cp27m-win32.whl", hash = "sha256:84ef7a0c10c24a7773163f917f1cb6b4444597efd505a8aed0a22e8c4780f27e"}, + {file = "cryptography-3.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:594a1db4511bc4d960571536abe21b4e5c3003e8750ab8365fafce71c5d86901"}, + {file = "cryptography-3.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0003a52a123602e1acee177dc90dd201f9bb1e73f24a070db7d36c588e8f5c7d"}, + {file = "cryptography-3.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:83d9d2dfec70364a74f4e7c70ad04d3ca2e6a08b703606993407bf46b97868c5"}, + {file = "cryptography-3.3.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:dc42f645f8f3a489c3dd416730a514e7a91a59510ddaadc09d04224c098d3302"}, + {file = "cryptography-3.3.1-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:788a3c9942df5e4371c199d10383f44a105d67d401fb4304178020142f020244"}, + {file = "cryptography-3.3.1-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:69e836c9e5ff4373ce6d3ab311c1a2eed274793083858d3cd4c7d12ce20d5f9c"}, + {file = "cryptography-3.3.1-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:9e21301f7a1e7c03dbea73e8602905a4ebba641547a462b26dd03451e5769e7c"}, + {file = "cryptography-3.3.1-cp36-abi3-win32.whl", hash = "sha256:b4890d5fb9b7a23e3bf8abf5a8a7da8e228f1e97dc96b30b95685df840b6914a"}, + {file = "cryptography-3.3.1-cp36-abi3-win_amd64.whl", hash = "sha256:0e85aaae861d0485eb5a79d33226dd6248d2a9f133b81532c8f5aae37de10ff7"}, + {file = "cryptography-3.3.1.tar.gz", hash = "sha256:7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6"}, ] distro = [ {file = "distro-1.5.0-py2.py3-none-any.whl", hash = "sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799"}, @@ -1728,8 +1721,8 @@ imagesize = [ {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.1.1-py3-none-any.whl", hash = "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013"}, - {file = "importlib_metadata-3.1.1.tar.gz", hash = "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"}, + {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, + {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, ] jinja2 = [ {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, @@ -1795,8 +1788,8 @@ nonebot-plugin-test = [ {file = "nonebot_plugin_test-0.1.0-py3-none-any.whl", hash = "sha256:a0ae2e5c546efcb7ac83e96af46962ed5ea032cb16ca2b468fadd7e04143f34d"}, ] packaging = [ - {file = "packaging-20.7-py2.py3-none-any.whl", hash = "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"}, - {file = "packaging-20.7.tar.gz", hash = "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236"}, + {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, + {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, ] paramiko = [ {file = "paramiko-2.7.2-py2.py3-none-any.whl", hash = "sha256:4f3e316fef2ac628b05097a637af35685183111d4bc1b5979bd397c2ab7b5898"}, @@ -1840,16 +1833,16 @@ pydantic = [ {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"}, ] pydash = [ - {file = "pydash-4.9.0-py2.py3-none-any.whl", hash = "sha256:a743212a586f92980ee093fdec4a984cb97d38fe6d5dadd3c4eb1de57bc5fb4a"}, - {file = "pydash-4.9.0.tar.gz", hash = "sha256:44f7217669511901bf234628231a8d8fccd89a53c23b54c35759ee0838a67ba7"}, + {file = "pydash-4.9.1-py2.py3-none-any.whl", hash = "sha256:f328ce27a87a3d3cea4566fa4c9d13748a8af45d9c738fe49a80490bc46fd271"}, + {file = "pydash-4.9.1.tar.gz", hash = "sha256:edd2e4cc964372337b0b881e5a18fc4d55b232c66e1200e26bc8dbb92bcf6bba"}, ] pyfiglet = [ {file = "pyfiglet-0.8.post1-py2.py3-none-any.whl", hash = "sha256:d555bcea17fbeaf70eaefa48bb119352487e629c9b56f30f383e2c62dd67a01c"}, {file = "pyfiglet-0.8.post1.tar.gz", hash = "sha256:c6c2321755d09267b438ec7b936825a4910fec696292139e664ca8670e103639"}, ] pygments = [ - {file = "Pygments-2.7.2-py3-none-any.whl", hash = "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"}, - {file = "Pygments-2.7.2.tar.gz", hash = "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"}, + {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, + {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, ] pygtrie = [ {file = "pygtrie-2.4.1.tar.gz", hash = "sha256:4367b87d92eaf475107421dce0295a9d4d72156702908c96c430a426b654aee7"}, diff --git a/tests/test_plugins/test_processor.py b/tests/test_plugins/test_processor.py index bfd030e5..bcb0dfe5 100644 --- a/tests/test_plugins/test_processor.py +++ b/tests/test_plugins/test_processor.py @@ -7,7 +7,7 @@ from nonebot.message import event_preprocessor, run_preprocessor @event_preprocessor async def handle(bot: Bot, event: Event, state: State): state["preprocessed"] = True - print(event) + print(type(event), event) @run_preprocessor diff --git a/tests/test_plugins/test_weather.py b/tests/test_plugins/test_weather.py index 73f8f5c2..493ce0b4 100644 --- a/tests/test_plugins/test_weather.py +++ b/tests/test_plugins/test_weather.py @@ -8,14 +8,14 @@ weather = on_command("天气", rule=to_me(), priority=1) @weather.handle() async def handle_first_receive(bot: Bot, event: Event, state: State): - args = str(event.message).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海 + args = str(event.get_message()).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海 print(f"==={args}===") if args: state["city"] = args # 如果用户发送了参数则直接赋值 @weather.got("city", prompt="你想查询哪个城市的天气呢?") -async def handle_city(bot: Bot, event: Event, state: State): +async def handle_city(bot: Bot, state: State): city = state["city"] if city not in ["上海", "北京"]: await weather.reject("你想查询的城市暂不支持,请重新输入!")