diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 0d5de449..c38ed27d 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -88,6 +88,10 @@ module.exports = context => ({ title: "nonebot.log 模块", path: "log" }, + { + title: "nonebot.rule 模块", + path: "rule" + }, { title: "nonebot.utils 模块", path: "utils" diff --git a/docs/api/README.md b/docs/api/README.md index 8b1247e2..71e3c247 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -19,6 +19,9 @@ * [nonebot.log](log.html) + * [nonebot.rule](rule.html) + + * [nonebot.utils](utils.html) diff --git a/docs/api/nonebot.md b/docs/api/nonebot.md index 8dcd0c20..3c278a5c 100644 --- a/docs/api/nonebot.md +++ b/docs/api/nonebot.md @@ -156,7 +156,7 @@ bots = nonebot.get_bots() * **返回** - * None + * `None` @@ -196,7 +196,7 @@ nonebot.init(database=Database(...)) * **返回** - * None + * `None` diff --git a/docs/api/rule.md b/docs/api/rule.md new file mode 100644 index 00000000..ad53a09f --- /dev/null +++ b/docs/api/rule.md @@ -0,0 +1,90 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.rule 模块 + +## 规则 + +每个 `Matcher` 拥有一个 `Rule` ,其中是 `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。 + +:::tip 提示 +`RuleChecker` 既可以是 async function 也可以是 sync function +::: + + +## _class_ `Rule` + +基类:`object` + + +* **说明** + + `Matcher` 规则类,当事件传递时,在 `Matcher` 运行前进行检查。 + + + +* **示例** + + +```python +Rule(async_function) & sync_function +# 等价于 +from nonebot.utils import run_sync +Rule(async_function, run_sync(sync_function)) +``` + + +### `__init__(*checkers)` + + +* **参数** + + + * `*checkers: Callable[[Bot, Event, dict], Awaitable[bool]]`: **异步** RuleChecker + + + +### `checkers` + + +* **说明** + + 存储 `RuleChecker` + + + +* **类型** + + + * `Set[Callable[[Bot, Event, dict], Awaitable[bool]]]` + + + +### _async_ `__call__(bot, event, state)` + + +* **说明** + + 检查是否符合所有规则 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +* **返回** + + + * `bool` diff --git a/docs/api/typing.md b/docs/api/typing.md index 92eb4fe4..f68bdfcb 100644 --- a/docs/api/typing.md +++ b/docs/api/typing.md @@ -19,7 +19,7 @@ sidebarDepth: 0 * **类型** - BaseDriver + `BaseDriver` @@ -35,7 +35,7 @@ sidebarDepth: 0 * **类型** - BaseWebSocket + `BaseWebSocket` @@ -51,7 +51,7 @@ sidebarDepth: 0 * **类型** - BaseBot + `BaseBot` @@ -67,7 +67,7 @@ sidebarDepth: 0 * **类型** - BaseEvent + `BaseEvent` @@ -83,7 +83,7 @@ sidebarDepth: 0 * **类型** - BaseMessage + `BaseMessage` @@ -99,7 +99,7 @@ sidebarDepth: 0 * **类型** - BaseMessageSegment + `BaseMessageSegment` @@ -115,7 +115,7 @@ sidebarDepth: 0 * **类型** - Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` @@ -131,7 +131,7 @@ sidebarDepth: 0 * **类型** - Matcher + `Matcher` @@ -147,7 +147,7 @@ sidebarDepth: 0 * **类型** - Rule + `Rule` @@ -163,7 +163,7 @@ sidebarDepth: 0 * **类型** - Callable[[Bot, Event, dict], Awaitable[bool]] + `Callable[[Bot, Event, dict], Union[bool, Awaitable[bool]]]` @@ -179,7 +179,7 @@ sidebarDepth: 0 * **类型** - Permission + `Permission` @@ -195,7 +195,7 @@ sidebarDepth: 0 * **类型** - Callable[[Bot, Event], Awaitable[bool]] + `Callable[[Bot, Event], Union[bool, Awaitable[bool]]]` @@ -211,7 +211,7 @@ sidebarDepth: 0 * **类型** - Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` @@ -227,7 +227,7 @@ sidebarDepth: 0 * **类型** - Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] + `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` diff --git a/docs/api/utils.md b/docs/api/utils.md index 5e2db112..7de3ba3f 100644 --- a/docs/api/utils.md +++ b/docs/api/utils.md @@ -25,7 +25,7 @@ sidebarDepth: 0 * **返回** - * Callable[..., Awaitable[Any]] + * `Callable[..., Awaitable[Any]]` @@ -34,35 +34,6 @@ sidebarDepth: 0 基类:`json.encoder.JSONEncoder` -* **类型** - - `json.JSONEncoder` - - - * **说明** - `JSONEncoder` used when encoding `Message` (List of dataclasses) - - - -### `default(o)` - -Implement this method in a subclass such that it returns -a serializable object for `o`, or calls the base implementation -(to raise a `TypeError`). - -For example, to support arbitrary iterators, you could -implement default like this: - -```default -def default(self, o): - try: - iterable = iter(o) - except TypeError: - pass - else: - return list(iterable) - # Let the base class default method raise the TypeError - return JSONEncoder.default(self, o) -``` + 在JSON序列化 `Message` (List[Dataclass]) 时使用的 `JSONEncoder` diff --git a/docs_build/README.rst b/docs_build/README.rst index d33b9a65..6ce28acd 100644 --- a/docs_build/README.rst +++ b/docs_build/README.rst @@ -7,5 +7,6 @@ NoneBot Api Reference - `nonebot.config `_ - `nonebot.sched `_ - `nonebot.log `_ + - `nonebot.rule `_ - `nonebot.utils `_ - `nonebot.exception `_ diff --git a/docs_build/rule.rst b/docs_build/rule.rst new file mode 100644 index 00000000..e182de79 --- /dev/null +++ b/docs_build/rule.rst @@ -0,0 +1,12 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +NoneBot.rule 模块 +==================== + +.. automodule:: nonebot.rule + :members: + :special-members: + :show-inheritance: diff --git a/docs_build/utils.rst b/docs_build/utils.rst index 5fe73c0a..776bbfd0 100644 --- a/docs_build/utils.rst +++ b/docs_build/utils.rst @@ -4,8 +4,9 @@ sidebarDepth: 0 --- NoneBot.utils 模块 -================= +================== -.. automodule:: nonebot.utils - :members: - :show-inheritance: + +.. autodecorator:: nonebot.utils.run_sync +.. autoclass:: nonebot.utils.DataclassEncoder + :show-inheritance: diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 4a15bd6a..68c2a8d2 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -136,7 +136,7 @@ def init(*, _env_file: Optional[str] = None, **kwargs): :返回: - - `None` + - ``None`` :用法: @@ -192,7 +192,7 @@ def run(host: Optional[str] = None, :返回: - - `None` + - ``None`` :用法: diff --git a/nonebot/permission.py b/nonebot/permission.py index fefda749..9cd104bb 100644 --- a/nonebot/permission.py +++ b/nonebot/permission.py @@ -4,14 +4,15 @@ import asyncio from nonebot.utils import run_sync -from nonebot.typing import Bot, Event, Union, NoReturn, PermissionChecker +from nonebot.typing import Bot, Event, Union, NoReturn, Callable, Awaitable, PermissionChecker class Permission: __slots__ = ("checkers",) - def __init__(self, *checkers: PermissionChecker) -> None: - self.checkers = list(checkers) + def __init__(self, *checkers: Callable[[Bot, Event], + Awaitable[bool]]) -> None: + self.checkers = set(checkers) async def __call__(self, bot: Bot, event: Event) -> bool: if not self.checkers: @@ -25,13 +26,13 @@ class Permission: def __or__(self, other: Union["Permission", PermissionChecker]) -> "Permission": - checkers = [*self.checkers] + checkers = self.checkers.copy() if isinstance(other, Permission): - checkers.extend(other.checkers) + checkers |= other.checkers elif asyncio.iscoroutinefunction(other): - checkers.append(other) + checkers.add(other) else: - checkers.append(run_sync(other)) + checkers.add(run_sync(other)) return Permission(*checkers) diff --git a/nonebot/plugin.py b/nonebot/plugin.py index 4dbfcd81..ba526fcf 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -151,8 +151,6 @@ def on_command(cmd: Union[str, Tuple[str, ...]], async def _strip_cmd(bot, event, state: dict): message = event.message - print(message[0].data) - print(state["_prefix"]) event.message = message.__class__( str(message)[len(state["_prefix"]["raw_command"]):].strip()) diff --git a/nonebot/rule.py b/nonebot/rule.py index 15dc1e6a..3f34f511 100644 --- a/nonebot/rule.py +++ b/nonebot/rule.py @@ -1,5 +1,15 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +""" +规则 +==== + +每个 ``Matcher`` 拥有一个 ``Rule`` ,其中是 ``RuleChecker`` 的集合,只有当所有 ``RuleChecker`` 检查结果为 ``True`` 时继续运行。 + +\:\:\:tip 提示 +``RuleChecker`` 既可以是 async function 也可以是 sync function +\:\:\: +""" import re import asyncio @@ -10,28 +20,62 @@ from pygtrie import CharTrie from nonebot import get_driver from nonebot.log import logger from nonebot.utils import run_sync -from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, RuleChecker +from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, Callable, Awaitable, RuleChecker class Rule: + """ + :说明: + ``Matcher`` 规则类,当事件传递时,在 ``Matcher`` 运行前进行检查。 + :示例: + + .. code-block:: python + + Rule(async_function) & sync_function + # 等价于 + from nonebot.utils import run_sync + Rule(async_function, run_sync(sync_function)) + """ __slots__ = ("checkers",) - def __init__(self, *checkers: RuleChecker) -> None: - self.checkers = list(checkers) + def __init__( + self, *checkers: Callable[[Bot, Event, dict], + Awaitable[bool]]) -> None: + """ + :参数: + * ``*checkers: Callable[[Bot, Event, dict], Awaitable[bool]]``: **异步** RuleChecker + """ + self.checkers = set(checkers) + """ + :说明: + 存储 ``RuleChecker`` + :类型: + * ``Set[Callable[[Bot, Event, dict], Awaitable[bool]]]`` + """ async def __call__(self, bot: Bot, event: Event, state: dict) -> bool: + """ + :说明: + 检查是否符合所有规则 + :参数: + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + :返回: + - ``bool`` + """ results = await asyncio.gather( *map(lambda c: c(bot, event, state), self.checkers)) return all(results) def __and__(self, other: Union["Rule", RuleChecker]) -> "Rule": - checkers = [*self.checkers] + checkers = self.checkers.copy() if isinstance(other, Rule): - checkers.extend(other.checkers) + checkers |= other.checkers elif asyncio.iscoroutinefunction(other): - checkers.append(other) + checkers.add(other) # type: ignore else: - checkers.append(run_sync(other)) + checkers.add(run_sync(other)) return Rule(*checkers) def __or__(self, other) -> NoReturn: diff --git a/nonebot/typing.py b/nonebot/typing.py index 73dc62a6..51834a08 100644 --- a/nonebot/typing.py +++ b/nonebot/typing.py @@ -46,7 +46,7 @@ def overrides(InterfaceClass: object): Driver = TypeVar("Driver", bound="BaseDriver") """ -:类型: `BaseDriver` +:类型: ``BaseDriver`` :说明: @@ -54,7 +54,7 @@ Driver = TypeVar("Driver", bound="BaseDriver") """ WebSocket = TypeVar("WebSocket", bound="BaseWebSocket") """ -:类型: `BaseWebSocket` +:类型: ``BaseWebSocket`` :说明: @@ -63,7 +63,7 @@ WebSocket = TypeVar("WebSocket", bound="BaseWebSocket") Bot = TypeVar("Bot", bound="BaseBot") """ -:类型: `BaseBot` +:类型: ``BaseBot`` :说明: @@ -71,7 +71,7 @@ Bot = TypeVar("Bot", bound="BaseBot") """ Event = TypeVar("Event", bound="BaseEvent") """ -:类型: `BaseEvent` +:类型: ``BaseEvent`` :说明: @@ -79,7 +79,7 @@ Event = TypeVar("Event", bound="BaseEvent") """ Message = TypeVar("Message", bound="BaseMessage") """ -:类型: `BaseMessage` +:类型: ``BaseMessage`` :说明: @@ -87,7 +87,7 @@ Message = TypeVar("Message", bound="BaseMessage") """ MessageSegment = TypeVar("MessageSegment", bound="BaseMessageSegment") """ -:类型: `BaseMessageSegment` +:类型: ``BaseMessageSegment`` :说明: @@ -97,7 +97,7 @@ MessageSegment = TypeVar("MessageSegment", bound="BaseMessageSegment") PreProcessor = Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] """ -:类型: `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` +:类型: ``Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` :说明: @@ -106,7 +106,7 @@ PreProcessor = Callable[[Bot, Event, dict], Union[Awaitable[None], Matcher = TypeVar("Matcher", bound="MatcherClass") """ -:类型: `Matcher` +:类型: ``Matcher`` :说明: @@ -114,15 +114,15 @@ Matcher = TypeVar("Matcher", bound="MatcherClass") """ Rule = TypeVar("Rule", bound="RuleClass") """ -:类型: `Rule` +:类型: ``Rule`` :说明: Rule 即判断是否响应事件的处理类。内部存储 RuleChecker ,返回全为 True 则响应事件。 """ -RuleChecker = Callable[[Bot, Event, dict], Awaitable[bool]] +RuleChecker = Callable[[Bot, Event, dict], Union[bool, Awaitable[bool]]] """ -:类型: `Callable[[Bot, Event, dict], Awaitable[bool]]` +:类型: ``Callable[[Bot, Event, dict], Union[bool, Awaitable[bool]]]`` :说明: @@ -130,15 +130,15 @@ RuleChecker = Callable[[Bot, Event, dict], Awaitable[bool]] """ Permission = TypeVar("Permission", bound="PermissionClass") """ -:类型: `Permission` +:类型: ``Permission`` :说明: Permission 即判断是否响应消息的处理类。内部存储 PermissionChecker ,返回只要有一个 True 则响应消息。 """ -PermissionChecker = Callable[[Bot, Event], Awaitable[bool]] +PermissionChecker = Callable[[Bot, Event], Union[bool, Awaitable[bool]]] """ -:类型: `Callable[[Bot, Event], Awaitable[bool]]` +:类型: ``Callable[[Bot, Event], Union[bool, Awaitable[bool]]]`` :说明: @@ -147,7 +147,7 @@ PermissionChecker = Callable[[Bot, Event], Awaitable[bool]] Handler = Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] """ -:类型: `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` +:类型: ``Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` :说明: @@ -156,7 +156,7 @@ Handler = Callable[[Bot, Event, dict], Union[Awaitable[None], ArgsParser = Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] """ -:类型: `Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` +:类型: ``Callable[[Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` :说明: diff --git a/nonebot/utils.py b/nonebot/utils.py index 81e78475..06bdaaa4 100644 --- a/nonebot/utils.py +++ b/nonebot/utils.py @@ -16,7 +16,7 @@ def run_sync(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]: :参数: * ``func: Callable[..., Any]``: 被装饰的同步函数 :返回: - - Callable[..., Awaitable[Any]] + - ``Callable[..., Awaitable[Any]]`` """ @wraps(func) @@ -31,10 +31,8 @@ def run_sync(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]: class DataclassEncoder(json.JSONEncoder): """ - :类型: - ``json.JSONEncoder`` :说明: - ``JSONEncoder`` used when encoding ``Message`` (List of dataclasses) + 在JSON序列化 ``Message`` (List[Dataclass]) 时使用的 ``JSONEncoder`` """ @overrides(json.JSONEncoder)