mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-28 02:55:18 +08:00
change Matcher class
This commit is contained in:
parent
06b7ef2a45
commit
761d725aed
124
nonebot/event.py
Normal file
124
nonebot/event.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class Event(dict):
|
||||||
|
"""
|
||||||
|
封装从 CQHTTP 收到的事件数据对象(字典),提供属性以获取其中的字段。
|
||||||
|
|
||||||
|
除 `type` 和 `detail_type` 属性对于任何事件都有效外,其它属性存在与否(不存在则返回
|
||||||
|
`None`)依事件不同而不同。
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_payload(payload: Dict[str, Any]) -> Optional["Event"]:
|
||||||
|
"""
|
||||||
|
从 CQHTTP 事件数据构造 `Event` 对象。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
e = Event(payload)
|
||||||
|
_ = e.type, e.detail_type
|
||||||
|
return e
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> str:
|
||||||
|
"""
|
||||||
|
事件类型,有 ``message``、``notice``、``request``、``meta_event`` 等。
|
||||||
|
"""
|
||||||
|
return self['post_type']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def detail_type(self) -> str:
|
||||||
|
"""
|
||||||
|
事件具体类型,依 `type` 的不同而不同,以 ``message`` 类型为例,有
|
||||||
|
``private``、``group``、``discuss`` 等。
|
||||||
|
"""
|
||||||
|
return self[f'{self.type}_type']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sub_type(self) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
事件子类型,依 `detail_type` 不同而不同,以 ``message.private`` 为例,有
|
||||||
|
``friend``、``group``、``discuss``、``other`` 等。
|
||||||
|
"""
|
||||||
|
return self.get('sub_type')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
事件名,对于有 `sub_type` 的事件,为 ``{type}.{detail_type}.{sub_type}``,否则为
|
||||||
|
``{type}.{detail_type}``。
|
||||||
|
"""
|
||||||
|
n = self.type + '.' + self.detail_type
|
||||||
|
if self.sub_type:
|
||||||
|
n += '.' + self.sub_type
|
||||||
|
return n
|
||||||
|
|
||||||
|
@property
|
||||||
|
def self_id(self) -> int:
|
||||||
|
"""机器人自身 ID。"""
|
||||||
|
return self['self_id']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_id(self) -> Optional[int]:
|
||||||
|
"""用户 ID。"""
|
||||||
|
return self.get('user_id')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def operator_id(self) -> Optional[int]:
|
||||||
|
"""操作者 ID。"""
|
||||||
|
return self.get('operator_id')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_id(self) -> Optional[int]:
|
||||||
|
"""群 ID。"""
|
||||||
|
return self.get('group_id')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def discuss_id(self) -> Optional[int]:
|
||||||
|
"""讨论组 ID。"""
|
||||||
|
return self.get('discuss_id')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message_id(self) -> Optional[int]:
|
||||||
|
"""消息 ID。"""
|
||||||
|
return self.get('message_id')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self) -> Optional[Any]:
|
||||||
|
"""消息。"""
|
||||||
|
return self.get('message')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw_message(self) -> Optional[str]:
|
||||||
|
"""未经 CQHTTP 处理的原始消息。"""
|
||||||
|
return self.get('raw_message')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sender(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""消息发送者信息。"""
|
||||||
|
return self.get('sender')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def anonymous(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""匿名信息。"""
|
||||||
|
return self.get('anonymous')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def file(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""文件信息。"""
|
||||||
|
return self.get('file')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comment(self) -> Optional[str]:
|
||||||
|
"""请求验证消息。"""
|
||||||
|
return self.get('comment')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flag(self) -> Optional[str]:
|
||||||
|
"""请求标识。"""
|
||||||
|
return self.get('flag')
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'<Event, {super().__repr__()}>'
|
@ -1,8 +1,13 @@
|
|||||||
class BlockedException(Exception):
|
class PausedException(Exception):
|
||||||
"""Block a message from further handling"""
|
"""Block a message from further handling and try to receive a new message"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RejectedException(Exception):
|
class RejectedException(Exception):
|
||||||
"""Reject a message and return current handler back"""
|
"""Reject a message and return current handler back"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FinishedException(Exception):
|
||||||
|
"""Finish handling a message"""
|
||||||
|
pass
|
||||||
|
@ -1,102 +1,163 @@
|
|||||||
import re
|
import re
|
||||||
import copy
|
import copy
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Union, Optional
|
from typing import Type, Union, Optional, Callable
|
||||||
|
|
||||||
from .rule import Rule, startswith, regex, user
|
from .event import Event
|
||||||
from .typing import Scope, Handler
|
from .typing import Scope, Handler
|
||||||
from .exception import BlockedException, RejectedException
|
from .rule import Rule, startswith, regex, user
|
||||||
|
from .exception import PausedException, RejectedException, FinishedException
|
||||||
|
|
||||||
|
|
||||||
class Matcher:
|
class Matcher:
|
||||||
|
|
||||||
def __init__(self,
|
rule: Rule = Rule()
|
||||||
rule: Rule,
|
scope: Scope = "ALL"
|
||||||
|
permission: str = "ALL"
|
||||||
|
block: bool = True
|
||||||
|
handlers: list = []
|
||||||
|
temp: bool = False
|
||||||
|
|
||||||
|
_default_state: dict = {}
|
||||||
|
_default_parser: Optional[Callable[[Event, dict], None]] = None
|
||||||
|
_args_parser: Optional[Callable[[Event, dict], None]] = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.handlers = self.handlers.copy()
|
||||||
|
self.state = self._default_state.copy()
|
||||||
|
self.parser = self._args_parser or self._default_parser
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls,
|
||||||
|
rule: Rule = Rule(),
|
||||||
scope: Scope = "ALL",
|
scope: Scope = "ALL",
|
||||||
permission: str = "ALL",
|
permission: str = "ALL",
|
||||||
block: bool = True,
|
block: bool = True,
|
||||||
*,
|
|
||||||
handlers: list = [],
|
handlers: list = [],
|
||||||
state: dict = {},
|
temp: bool = False,
|
||||||
temp: bool = False):
|
*,
|
||||||
self.rule = rule
|
default_state: dict = {},
|
||||||
self.scope = scope
|
default_parser: Optional[Callable[[Event, dict], None]] = None,
|
||||||
self.permission = permission
|
args_parser: Optional[Callable[[Event, dict], None]] = None):
|
||||||
self.block = block
|
|
||||||
self.handlers = handlers
|
|
||||||
self.state = state
|
|
||||||
self.temp = temp
|
|
||||||
|
|
||||||
def _default_parser(event: "Event", state: dict):
|
# class NewMatcher(cls):
|
||||||
state[state.pop("_current_arg")] = event.message
|
# rule: Rule = rule
|
||||||
|
# scope: Scope = scope
|
||||||
|
# permission: str = permission
|
||||||
|
# block: bool = block
|
||||||
|
# handlers: list = handlers
|
||||||
|
# temp: bool = temp
|
||||||
|
|
||||||
self._args_parser = _default_parser
|
# _default_state = default_state
|
||||||
|
|
||||||
def __call__(self, func: Handler) -> Handler:
|
NewMatcher = type(
|
||||||
self.handlers.append(func)
|
"Matcher", (cls,), {
|
||||||
|
"rule": rule,
|
||||||
|
"scope": scope,
|
||||||
|
"permission": permission,
|
||||||
|
"block": block,
|
||||||
|
"handlers": handlers,
|
||||||
|
"temp": temp,
|
||||||
|
"_default_state": default_state,
|
||||||
|
"_default_parser": default_parser,
|
||||||
|
"_args_parser": args_parser,
|
||||||
|
})
|
||||||
|
|
||||||
# TODO: export some functions
|
return NewMatcher
|
||||||
func.args_parser = self.args_parser
|
|
||||||
func.receive = self.receive
|
|
||||||
func.got = self.got
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def args_parser(cls, func: Callable[[Event, dict], None]):
|
||||||
|
cls._default_parser = func
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def args_parser(self, func):
|
@classmethod
|
||||||
self._args_parser = func
|
def receive(cls):
|
||||||
return func
|
|
||||||
|
|
||||||
def receive(self):
|
|
||||||
|
|
||||||
def _decorator(func: Handler) -> Handler:
|
def _decorator(func: Handler) -> Handler:
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def _handler(event: "Event", state: dict):
|
def _handler(event: Event, state: dict):
|
||||||
# TODO: add tmp matcher to matcher tree
|
raise PausedException
|
||||||
matcher = Matcher(user(event.user_id) & self.rule,
|
|
||||||
scope=self.scope,
|
|
||||||
permission=self.permission,
|
|
||||||
block=self.block,
|
|
||||||
handlers=self.handlers,
|
|
||||||
state=state,
|
|
||||||
temp=True)
|
|
||||||
matcher.args_parser(self._args_parser)
|
|
||||||
raise BlockedException
|
|
||||||
|
|
||||||
self.handlers.append(_handler)
|
cls.handlers.append(_handler)
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return _decorator
|
return _decorator
|
||||||
|
|
||||||
def got(self, key, args_parser=None):
|
@classmethod
|
||||||
|
def got(cls,
|
||||||
|
key: str,
|
||||||
|
args_parser: Optional[Callable[[Event, dict], None]] = None):
|
||||||
|
|
||||||
def _decorator(func: Handler) -> Handler:
|
def _decorator(func: Handler) -> Handler:
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def _handler(event: "Event", state: dict):
|
def _handler(event: Event, state: dict):
|
||||||
if key not in state:
|
if key not in state:
|
||||||
state["_current_arg"] = key
|
if state.get("__current_arg__", None) == key:
|
||||||
|
state[key] = event.message
|
||||||
# TODO: add tmp matcher to matcher tree
|
del state["__current_arg__"]
|
||||||
matcher = copy.copy(self)
|
return func(event, state)
|
||||||
|
state["__current_arg__"] = key
|
||||||
|
cls._args_parser = args_parser
|
||||||
raise RejectedException
|
raise RejectedException
|
||||||
|
|
||||||
return func(event, state)
|
return func(event, state)
|
||||||
|
|
||||||
self.handlers.append(_handler)
|
cls.handlers.append(_handler)
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return _decorator
|
return _decorator
|
||||||
|
|
||||||
def finish(self):
|
@classmethod
|
||||||
# BlockedException用于阻止后续handler继续执行
|
def finish(cls):
|
||||||
raise BlockedException
|
raise FinishedException
|
||||||
|
|
||||||
def reject(self):
|
@classmethod
|
||||||
# RejectedException用于阻止后续handler继续执行并将当前handler放回队列
|
def reject(cls):
|
||||||
raise RejectedException
|
raise RejectedException
|
||||||
|
|
||||||
|
async def run(self, event):
|
||||||
|
if not self.rule(event):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.parser:
|
||||||
|
await self.parser(event, state) # type: ignore
|
||||||
|
|
||||||
|
for _ in range(len(self.handlers)):
|
||||||
|
handler = self.handlers.pop(0)
|
||||||
|
await handler(event, self.state)
|
||||||
|
except RejectedException:
|
||||||
|
# TODO: add tmp matcher to matcher tree
|
||||||
|
self.handlers.insert(handler, 0)
|
||||||
|
matcher = Matcher.new(self.rule,
|
||||||
|
self.scope,
|
||||||
|
self.permission,
|
||||||
|
self.block,
|
||||||
|
self.handlers,
|
||||||
|
temp=True,
|
||||||
|
default_state=self.state,
|
||||||
|
default_parser=self._default_parser,
|
||||||
|
args_parser=self._args_parser)
|
||||||
|
return
|
||||||
|
except PausedException:
|
||||||
|
# TODO: add tmp matcher to matcher tree
|
||||||
|
matcher = Matcher.new(self.rule,
|
||||||
|
self.scope,
|
||||||
|
self.permission,
|
||||||
|
self.block,
|
||||||
|
self.handlers,
|
||||||
|
temp=True,
|
||||||
|
default_state=self.state,
|
||||||
|
default_parser=self._default_parser,
|
||||||
|
args_parser=self._args_parser)
|
||||||
|
return
|
||||||
|
except FinishedException:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def on_message(rule: Rule,
|
def on_message(rule: Rule,
|
||||||
scope="ALL",
|
scope="ALL",
|
||||||
@ -104,23 +165,23 @@ def on_message(rule: Rule,
|
|||||||
block=True,
|
block=True,
|
||||||
*,
|
*,
|
||||||
handlers=[],
|
handlers=[],
|
||||||
state={},
|
temp=False,
|
||||||
temp=False) -> Matcher:
|
state={}) -> Type[Matcher]:
|
||||||
# TODO: add matcher to matcher tree
|
# TODO: add matcher to matcher tree
|
||||||
return Matcher(rule,
|
return Matcher.new(rule,
|
||||||
scope,
|
scope,
|
||||||
permission,
|
permission,
|
||||||
block,
|
block,
|
||||||
handlers=handlers,
|
handlers=handlers,
|
||||||
state=state,
|
temp=temp,
|
||||||
temp=temp)
|
default_state=state)
|
||||||
|
|
||||||
|
|
||||||
def on_startswith(msg,
|
def on_startswith(msg,
|
||||||
start: int = None,
|
start: int = None,
|
||||||
end: int = None,
|
end: int = None,
|
||||||
rule: Optional[Rule] = None,
|
rule: Optional[Rule] = None,
|
||||||
**kwargs) -> Matcher:
|
**kwargs) -> Type[Matcher]:
|
||||||
return on_message(startswith(msg, start, end) &
|
return on_message(startswith(msg, start, end) &
|
||||||
rule, **kwargs) if rule else on_message(
|
rule, **kwargs) if rule else on_message(
|
||||||
startswith(msg, start, end), **kwargs)
|
startswith(msg, start, end), **kwargs)
|
||||||
@ -129,7 +190,7 @@ def on_startswith(msg,
|
|||||||
def on_regex(pattern,
|
def on_regex(pattern,
|
||||||
flags: Union[int, re.RegexFlag] = 0,
|
flags: Union[int, re.RegexFlag] = 0,
|
||||||
rule: Optional[Rule] = None,
|
rule: Optional[Rule] = None,
|
||||||
**kwargs) -> Matcher:
|
**kwargs) -> Type[Matcher]:
|
||||||
return on_message(regex(pattern, flags) &
|
return on_message(regex(pattern, flags) &
|
||||||
rule, **kwargs) if rule else on_message(
|
rule, **kwargs) if rule else on_message(
|
||||||
regex(pattern, flags), **kwargs)
|
regex(pattern, flags), **kwargs)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import re
|
import re
|
||||||
from typing import Union, Callable
|
from typing import Union, Callable, Optional
|
||||||
|
|
||||||
|
from .event import Event
|
||||||
|
|
||||||
|
|
||||||
class Rule:
|
class Rule:
|
||||||
|
|
||||||
def __init__(self, checker: Callable[["Event"], bool]):
|
def __init__(self, checker: Optional[Callable[[Event], bool]] = None):
|
||||||
self.checker = checker
|
self.checker = checker or (lambda event: True)
|
||||||
|
|
||||||
def __call__(self, event):
|
def __call__(self, event):
|
||||||
return self.checker(event)
|
return self.checker(event)
|
||||||
|
Loading…
Reference in New Issue
Block a user