From a39c2e223afd5d68639c386b7336fad0dabf1a0f Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sat, 25 Jul 2020 12:28:30 +0800 Subject: [PATCH] change rule and event handle --- nonebot/adapters/coolq.py | 6 +--- nonebot/drivers/__init__.py | 4 --- nonebot/exception.py | 13 +++++++ nonebot/matcher.py | 7 ++-- nonebot/message.py | 22 +++++++++++- nonebot/plugin.py | 49 +++++++++++++++++++++++-- nonebot/rule.py | 54 +++++++++++++++++++--------- tests/test_plugins/test_metaevent.py | 18 ++++++++++ 8 files changed, 140 insertions(+), 33 deletions(-) create mode 100644 tests/test_plugins/test_metaevent.py diff --git a/nonebot/adapters/coolq.py b/nonebot/adapters/coolq.py index 376e27f7..3de28006 100644 --- a/nonebot/adapters/coolq.py +++ b/nonebot/adapters/coolq.py @@ -63,11 +63,7 @@ class Bot(BaseBot): if "message" in event.keys(): event["message"] = Message(event["message"]) - # TODO: Handle Meta Event - if event.type == "meta_event": - pass - else: - await handle_event(self, event) + await handle_event(self, event) async def call_api(self, api: str, data: dict): # TODO: Call API diff --git a/nonebot/drivers/__init__.py b/nonebot/drivers/__init__.py index e4b88764..dcafe0df 100644 --- a/nonebot/drivers/__init__.py +++ b/nonebot/drivers/__init__.py @@ -45,10 +45,6 @@ class BaseDriver(abc.ABC): async def _handle_ws_reverse(self): raise NotImplementedError - @abc.abstractmethod - async def _handle_http_api(self): - raise NotImplementedError - class BaseWebSocket(object): diff --git a/nonebot/exception.py b/nonebot/exception.py index a0a5ca59..83de121e 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -2,6 +2,19 @@ # -*- coding: utf-8 -*- +class IgnoredException(Exception): + """ + Raised by event_preprocessor indicating that + the bot should ignore the event + """ + + def __init__(self, reason): + """ + :param reason: reason to ignore the event + """ + self.reason = reason + + class PausedException(Exception): """Block a message from further handling and try to receive a new message""" pass diff --git a/nonebot/matcher.py b/nonebot/matcher.py index 89e22b56..a65ba161 100644 --- a/nonebot/matcher.py +++ b/nonebot/matcher.py @@ -62,7 +62,7 @@ class Matcher: return NewMatcher @classmethod - def check_rule(cls, event: Event) -> bool: + def check_rule(cls, bot, event: Event) -> bool: """检查 Matcher 的 Rule 是否成立 Args: @@ -71,7 +71,7 @@ class Matcher: Returns: bool: 条件成立与否 """ - return cls.rule(event) + return cls.rule(bot, event) # @classmethod # def args_parser(cls, func: Callable[[Event, dict], None]): @@ -141,9 +141,6 @@ class Matcher: # 运行handlers async def run(self, bot, event): - if not self.rule(event): - return - try: # if self.parser: # await self.parser(event, state) # type: ignore diff --git a/nonebot/message.py b/nonebot/message.py index 032615b8..81a5778b 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -1,19 +1,39 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import asyncio +from typing import Set, Callable + from nonebot.log import logger from nonebot.event import Event from nonebot.matcher import matchers +from nonebot.exception import IgnoredException + +_event_preprocessors: Set[Callable] = set() + + +def event_preprocessor(func: Callable) -> Callable: + _event_preprocessors.add(func) + return func async def handle_event(bot, event: Event): # TODO: PreProcess + coros = [] + for preprocessor in _event_preprocessors: + coros.append(preprocessor(bot, event)) + if coros: + try: + await asyncio.gather(*coros) + except IgnoredException: + logger.info(f"Event {event} is ignored") + return for priority in sorted(matchers.keys()): for index in range(len(matchers[priority])): Matcher = matchers[priority][index] try: - if not Matcher.check_rule(event): + if not Matcher.check_rule(bot, event): continue except Exception as e: logger.error( diff --git a/nonebot/plugin.py b/nonebot/plugin.py index 5b724763..50576842 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -7,9 +7,9 @@ import importlib from types import ModuleType from typing import Set, Dict, Type, Optional -from nonebot.rule import Rule from nonebot.log import logger from nonebot.matcher import Matcher +from nonebot.rule import Rule, metaevent, message, notice, request plugins: Dict[str, "Plugin"] = {} @@ -26,13 +26,58 @@ class Plugin(object): self.matchers = matchers +def on_metaevent(rule: Rule, + *, + handlers=[], + temp=False, + priority: int = 1, + state={}) -> Type[Matcher]: + matcher = Matcher.new(metaevent() & rule, + temp=temp, + priority=priority, + handlers=handlers, + default_state=state) + _tmp_matchers.add(matcher) + return matcher + + def on_message(rule: Rule, *, handlers=[], temp=False, priority: int = 1, state={}) -> Type[Matcher]: - matcher = Matcher.new(rule, + matcher = Matcher.new(message() & rule, + temp=temp, + priority=priority, + handlers=handlers, + default_state=state) + _tmp_matchers.add(matcher) + return matcher + + +def on_notice(rule: Rule, + *, + handlers=[], + temp=False, + priority: int = 1, + state={}) -> Type[Matcher]: + matcher = Matcher.new(notice() & rule, + temp=temp, + priority=priority, + handlers=handlers, + default_state=state) + _tmp_matchers.add(matcher) + return matcher + + +def on_request(rule: Rule, + *, + handlers=[], + temp=False, + priority: int = 1, + state={}) -> Type[Matcher]: + matcher = Matcher.new(request() & rule, temp=temp, priority=priority, handlers=handlers, diff --git a/nonebot/rule.py b/nonebot/rule.py index 814069b9..7f3f68fa 100644 --- a/nonebot/rule.py +++ b/nonebot/rule.py @@ -9,52 +9,74 @@ from nonebot.event import Event class Rule: - def __init__(self, checker: Optional[Callable[[Event], bool]] = None): - self.checker = checker or (lambda event: True) + def __init__( + self, + checker: Optional[Callable[["BaseBot", Event], # type: ignore + bool]] = None): + self.checker = checker or (lambda bot, event: True) - def __call__(self, event: Event) -> bool: - return self.checker(event) + def __call__(self, bot, event: Event) -> bool: + return self.checker(bot, event) def __and__(self, other: "Rule") -> "Rule": - return Rule(lambda event: self.checker(event) and other.checker(event)) + return Rule(lambda bot, event: self.checker(bot, event) and other. + checker(bot, event)) def __or__(self, other: "Rule") -> "Rule": - return Rule(lambda event: self.checker(event) or other.checker(event)) + return Rule(lambda bot, event: self.checker(bot, event) or other. + checker(bot, event)) def __neg__(self) -> "Rule": - return Rule(lambda event: not self.checker(event)) + return Rule(lambda bot, event: not self.checker(bot, event)) + + +def message() -> Rule: + return Rule(lambda bot, event: event.type == "message") + + +def notice() -> Rule: + return Rule(lambda bot, event: event.type == "notice") + + +def request() -> Rule: + return Rule(lambda bot, event: event.type == "request") + + +def metaevent() -> Rule: + return Rule(lambda bot, event: event.type == "meta_event") def user(*qq: int) -> Rule: - return Rule(lambda event: event.user_id in qq) + return Rule(lambda bot, event: event.user_id in qq) def private() -> Rule: - return Rule(lambda event: event.detail_type == "private") + return Rule(lambda bot, event: event.detail_type == "private") def group(*group: int) -> Rule: - return Rule( - lambda event: event.detail_type == "group" and event.group_id in group) + return Rule(lambda bot, event: event.detail_type == "group" and event. + group_id in group) def discuss(*discuss: int) -> Rule: - return Rule(lambda event: event.detail_type == "discuss" and event. + return Rule(lambda bot, event: event.detail_type == "discuss" and event. discuss_id in discuss) def startswith(msg, start: int = None, end: int = None) -> Rule: - return Rule(lambda event: event.message.startswith(msg, start, end)) + return Rule(lambda bot, event: event.message.startswith(msg, start, end)) def endswith(msg, start: int = None, end: int = None) -> Rule: - return Rule(lambda event: event.message.endswith(msg, start=None, end=None)) + return Rule( + lambda bot, event: event.message.endswith(msg, start=None, end=None)) def has(msg: str) -> Rule: - return Rule(lambda event: msg in event.message) + return Rule(lambda bot, event: msg in event.message) def regex(regex, flags: Union[int, re.RegexFlag] = 0) -> Rule: pattern = re.compile(regex, flags) - return Rule(lambda event: bool(pattern.search(event.message))) + return Rule(lambda bot, event: bool(pattern.search(str(event.message)))) diff --git a/tests/test_plugins/test_metaevent.py b/tests/test_plugins/test_metaevent.py new file mode 100644 index 00000000..3fb7360e --- /dev/null +++ b/tests/test_plugins/test_metaevent.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from nonebot.rule import Rule +from nonebot.event import Event +from nonebot.plugin import on_metaevent + + +def heartbeat(bot, event: Event) -> bool: + return event.detail_type == "heartbeat" + + +test_matcher = on_metaevent(Rule(heartbeat)) + + +@test_matcher.handle() +async def handle_heartbeat(bot, event: Event, state: dict): + print("[i] Heartbeat")