diff --git a/docs/api/exception.md b/docs/api/exception.md
index 5c89a5f8..0a9876a8 100644
--- a/docs/api/exception.md
+++ b/docs/api/exception.md
@@ -83,23 +83,6 @@ sidebarDepth: 0
-## _exception_ `ExpiredException`
-
-基类:`Exception`
-
-
-* **说明**
-
- 指示 NoneBot 当前 `Matcher` 已失效。
-
-
-
-* **用法**
-
- 当 `Matcher` 运行前检查时抛出。
-
-
-
## _exception_ `StopPropagation`
基类:`Exception`
diff --git a/docs/api/message.md b/docs/api/message.md
index 5fe65f56..c9d7c158 100644
--- a/docs/api/message.md
+++ b/docs/api/message.md
@@ -6,3 +6,138 @@ sidebarDepth: 0
# NoneBot.message 模块
## 事件处理
+
+NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。
+
+
+## `event_preprocessor(func)`
+
+
+* **说明**
+
+ 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。
+
+
+
+* **参数**
+
+ 事件预处理函数接收三个参数。
+
+
+ * `bot: Bot`: Bot 对象
+
+
+ * `event: Event`: Event 对象
+
+
+ * `state: dict`: 当前 State
+
+
+
+## `event_postprocessor(func)`
+
+
+* **说明**
+
+ 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。
+
+
+
+* **参数**
+
+ 事件后处理函数接收三个参数。
+
+
+ * `bot: Bot`: Bot 对象
+
+
+ * `event: Event`: Event 对象
+
+
+ * `state: dict`: 当前事件运行前 State
+
+
+
+## `run_preprocessor(func)`
+
+
+* **说明**
+
+ 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。
+
+
+
+* **参数**
+
+ 运行预处理函数接收四个参数。
+
+
+ * `matcher: Matcher`: 当前要运行的事件响应器
+
+
+ * `bot: Bot`: Bot 对象
+
+
+ * `event: Event`: Event 对象
+
+
+ * `state: dict`: 当前 State
+
+
+
+## `run_postprocessor(func)`
+
+
+* **说明**
+
+ 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。
+
+
+
+* **参数**
+
+ 运行后处理函数接收五个参数。
+
+
+ * `matcher: Matcher`: 运行完毕的事件响应器
+
+
+ * `exception: Optional[Exception]`: 事件响应器运行错误(如果存在)
+
+
+ * `bot: Bot`: Bot 对象
+
+
+ * `event: Event`: Event 对象
+
+
+ * `state: dict`: 当前 State
+
+
+
+## _async_ `handle_event(bot, event)`
+
+
+* **说明**
+
+ 处理一个事件。调用该函数以实现分发事件。
+
+
+
+* **参数**
+
+
+ * `bot: Bot`: Bot 对象
+
+
+ * `event: Event`: Event 对象
+
+
+
+* **示例**
+
+
+```python
+import asyncio
+asyncio.create_task(handle_event(bot, event))
+```
diff --git a/docs/api/typing.md b/docs/api/typing.md
index cdf9f224..8f8a2223 100644
--- a/docs/api/typing.md
+++ b/docs/api/typing.md
@@ -163,13 +163,13 @@ sidebarDepth: 0
* **类型**
- `Callable[[Matcher, List[Any], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`
+ `Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`
* **说明**
- 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数包含运行时产生的错误以及 `ExpiredException`, `StopPropagation` (如果存在)
+ 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在)
diff --git a/nonebot/exception.py b/nonebot/exception.py
index 4d862e41..f16cbcd0 100644
--- a/nonebot/exception.py
+++ b/nonebot/exception.py
@@ -9,12 +9,6 @@
from nonebot.typing import List, Type, Optional
-class _ExceptionContainer(Exception):
-
- def __init__(self, exceptions: List[Type[Exception]]) -> None:
- self.exceptions = exceptions
-
-
class IgnoredException(Exception):
"""
:说明:
@@ -79,19 +73,6 @@ class FinishedException(Exception):
pass
-class ExpiredException(Exception):
- """
- :说明:
-
- 指示 NoneBot 当前 ``Matcher`` 已失效。
-
- :用法:
-
- 当 ``Matcher`` 运行前检查时抛出。
- """
- pass
-
-
class StopPropagation(Exception):
"""
:说明:
diff --git a/nonebot/message.py b/nonebot/message.py
index 49cff307..af0849a5 100644
--- a/nonebot/message.py
+++ b/nonebot/message.py
@@ -1,6 +1,8 @@
"""
事件处理
========
+
+NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。
"""
import asyncio
@@ -9,10 +11,9 @@ from datetime import datetime
from nonebot.log import logger
from nonebot.rule import TrieRule
from nonebot.utils import escape_tag
-from nonebot.matcher import matchers
-from nonebot.exception import IgnoredException, ExpiredException
-from nonebot.exception import StopPropagation, _ExceptionContainer
-from nonebot.typing import Set, Type, Union, NoReturn, Bot, Event, Matcher
+from nonebot.matcher import matchers, Matcher
+from nonebot.typing import Set, Type, Union, Optional, Iterable, NoReturn, Bot, Event
+from nonebot.exception import IgnoredException, StopPropagation
from nonebot.typing import EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor
_event_preprocessors: Set[EventPreProcessor] = set()
@@ -22,39 +23,108 @@ _run_postprocessors: Set[RunPostProcessor] = set()
def event_preprocessor(func: EventPreProcessor) -> EventPreProcessor:
+ """
+ :说明:
+ 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。
+ :参数:
+ 事件预处理函数接收三个参数。
+
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ * ``state: dict``: 当前 State
+ """
_event_preprocessors.add(func)
return func
def event_postprocessor(func: EventPostProcessor) -> EventPostProcessor:
+ """
+ :说明:
+ 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。
+ :参数:
+ 事件后处理函数接收三个参数。
+
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ * ``state: dict``: 当前事件运行前 State
+ """
_event_postprocessors.add(func)
return func
def run_preprocessor(func: RunPreProcessor) -> RunPreProcessor:
+ """
+ :说明:
+ 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。
+ :参数:
+ 运行预处理函数接收四个参数。
+
+ * ``matcher: Matcher``: 当前要运行的事件响应器
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ * ``state: dict``: 当前 State
+ """
_run_preprocessors.add(func)
return func
def run_postprocessor(func: RunPostProcessor) -> RunPostProcessor:
+ """
+ :说明:
+ 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。
+ :参数:
+ 运行后处理函数接收五个参数。
+
+ * ``matcher: Matcher``: 运行完毕的事件响应器
+ * ``exception: Optional[Exception]``: 事件响应器运行错误(如果存在)
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ * ``state: dict``: 当前 State
+ """
_run_postprocessors.add(func)
return func
+async def _check_matcher(priority: int, bot: Bot, event: Event,
+ state: dict) -> Iterable[Type[Matcher]]:
+ current_matchers = matchers[priority].copy()
+
+ async def _check(Matcher: Type[Matcher], bot: Bot, event: Event,
+ state: dict) -> Optional[Type[Matcher]]:
+ try:
+ if await Matcher.check_perm(
+ bot, event) and await Matcher.check_rule(bot, event, state):
+ return Matcher
+ except Exception as e:
+ logger.opt(colors=True, exception=e).error(
+ f"Rule check failed for {Matcher}."
+ )
+ return None
+
+ async def _check_expire(Matcher: Type[Matcher]) -> Optional[Type[Matcher]]:
+ if Matcher.temp or (Matcher.expire_time and
+ datetime.now() > Matcher.expire_time):
+ return Matcher
+ return None
+
+ checking_tasks = [
+ _check(Matcher, bot, event, state) for Matcher in current_matchers
+ ]
+ checking_expire_tasks = [
+ _check_expire(Matcher) for Matcher in current_matchers
+ ]
+ results = await asyncio.gather(*checking_tasks, return_exceptions=True)
+ expired = await asyncio.gather(*checking_expire_tasks)
+ for expired_matcher in filter(lambda x: x and x in results, expired):
+ try:
+ matchers[priority].remove(expired_matcher)
+ except Exception:
+ pass
+ return filter(lambda x: x, results)
+
+
async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
state: dict) -> Union[None, NoReturn]:
- if Matcher.expire_time and datetime.now() > Matcher.expire_time:
- raise _ExceptionContainer([ExpiredException])
-
- try:
- if not await Matcher.check_perm(
- bot, event) or not await Matcher.check_rule(bot, event, state):
- return
- except Exception as e:
- logger.opt(colors=True, exception=e).error(
- f"Rule check failed for {Matcher}.")
- return
-
logger.info(f"Event will be handled by {Matcher}")
matcher = Matcher()
@@ -74,7 +144,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
"Running cancelled!")
return
- exceptions = []
+ exception = None
try:
logger.debug(f"Running matcher {matcher}")
@@ -83,15 +153,10 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
logger.opt(colors=True, exception=e).error(
f"Running matcher {matcher} failed."
)
- exceptions.append(e)
-
- if Matcher.temp:
- exceptions.append(ExpiredException)
- if Matcher.block:
- exceptions.append(StopPropagation)
+ exception = e
coros = list(
- map(lambda x: x(matcher, exceptions, bot, event, state),
+ map(lambda x: x(matcher, exception, bot, event, state),
_run_postprocessors))
if coros:
try:
@@ -101,11 +166,24 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
"Error when running RunPostProcessors"
)
- if exceptions:
- raise _ExceptionContainer(exceptions)
+ if matcher.block:
+ raise StopPropagation
async def handle_event(bot: Bot, event: Event):
+ """
+ :说明:
+ 处理一个事件。调用该函数以实现分发事件。
+ :参数:
+ * ``bot: Bot``: Bot 对象
+ * ``event: Event``: Event 对象
+ :示例:
+
+ .. code-block:: python
+
+ import asyncio
+ asyncio.create_task(handle_event(bot, event))
+ """
show_log = True
log_msg = f"{bot.type.upper()} | {event.self_id} [{event.name}]: "
if event.type == "message":
@@ -153,29 +231,23 @@ async def handle_event(bot: Bot, event: Event):
if break_flag:
break
- pending_tasks = [
- _run_matcher(matcher, bot, event, state.copy())
- for matcher in matchers[priority]
- ]
-
if show_log:
logger.debug(f"Checking for matchers in priority {priority}...")
+
+ run_matchers = await _check_matcher(priority, bot, event, state)
+
+ pending_tasks = [
+ _run_matcher(matcher, bot, event, state.copy())
+ for matcher in run_matchers
+ ]
+
results = await asyncio.gather(*pending_tasks, return_exceptions=True)
- i = 0
- for index, result in enumerate(results):
- if isinstance(result, _ExceptionContainer):
- e_list = result.exceptions
- if StopPropagation in e_list:
- if not break_flag:
- break_flag = True
- logger.debug("Stop event propagation")
- if ExpiredException in e_list:
- logger.debug(
- f"Matcher {matchers[priority][index - i]} will be removed."
- )
- del matchers[priority][index - i]
- i += 1
+ for result in results:
+ if result is StopPropagation:
+ if not break_flag:
+ break_flag = True
+ logger.debug("Stop event propagation")
coros = list(map(lambda x: x(bot, event, state), _event_postprocessors))
if coros:
diff --git a/nonebot/typing.py b/nonebot/typing.py
index da7923bd..09109b37 100644
--- a/nonebot/typing.py
+++ b/nonebot/typing.py
@@ -119,14 +119,14 @@ RunPreProcessor = Callable[["Matcher", Bot, Event, dict],
事件响应器运行前预处理函数 RunPreProcessor 类型
"""
-RunPostProcessor = Callable[["Matcher", List[Any], Bot, Event, dict],
+RunPostProcessor = Callable[["Matcher", Optional[Exception], Bot, Event, dict],
Union[Awaitable[None], Awaitable[NoReturn]]]
"""
-:类型: ``Callable[[Matcher, List[Any], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]``
+:类型: ``Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]``
:说明:
- 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数包含运行时产生的错误以及 ``ExpiredException``, ``StopPropagation`` (如果存在)
+ 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在)
"""
Matcher = TypeVar("Matcher", bound="MatcherClass")