nonebot2/website/docs/tutorial/plugin/create-handler.md
Akirami 36d7b44741
Feature: 添加正则匹配文本注入 (#1457)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-12-09 14:42:54 +08:00

530 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
sidebar_position: 4
description: 定义事件处理流程,完成事件响应
options:
menu:
weight: 27
category: guide
---
# 定义事件处理流程
在上一章节中,我们已经定义了事件响应器,在这一章中,我们将会为事件响应器填充处理流程。
## 添加一个处理依赖
在事件响应器中,事件处理流程由一个或多个处理依赖组成,每个处理依赖都是一个 `Dependent`,详情可以参考[进阶 - 依赖注入](../../advanced/di/dependency-injection.md)。下面介绍如何添加一个处理依赖。
### 使用 `handle` 装饰器
```python {3-5}
matcher = on_message()
@matcher.handle()
async def handle_func():
# do something here
```
如上方示例所示,我们使用 `matcher` 响应器的 `handle` 装饰器装饰了一个函数 `handle_func` 。`handle_func` 函数会被自动转换为 `Dependent` 对象,并被添加到 `matcher` 的事件处理流程中。
在 `handle_func` 函数中,我们可以编写任何事件响应逻辑,如:操作数据库,发送消息等。上下文信息可以通过依赖注入的方式获取,参考:[获取上下文信息](#获取上下文信息)。发送消息可以通过[事件响应器操作](./matcher-operation.md)或者直接调用 Bot 的方法( API 等,由协议适配器决定)。
:::warning 注意
`handle_func` 函数虽然会被装饰器自动转换为 `Dependent` 对象,但 `handle_func` 仍然为原本的函数,因此 `handle_func` 函数可以进行复用。如:
```python
matcher1 = on_message()
matcher2 = on_message()
@matcher1.handle()
@matcher2.handle()
async def handle_func():
# do something here
```
:::
### 使用 `receive` 装饰器
```python {3-5}
matcher = on_message()
@matcher.receive("id")
async def handle_func(e: Event = Received("id")):
# do something here
```
`receive` 装饰器与 `handle` 装饰器一样,可以装饰一个函数添加到事件响应器的事件处理流程中。但与 `handle` 装饰器不同的是,`receive` 装饰器会中断当前事件处理流程,等待接收一个新的事件,就像是会话状态等待用户一个新的事件。可以接收的新的事件类型取决于事件响应器的 [`type`](./create-matcher.md#事件响应器类型-type) 更新值以及 [`permission`](./create-matcher.md#事件触发权限-permission) 更新值,可以通过自定义更新方法来控制会话响应(如进行非消息交互、多人会话、跨群会话等)。
`receive` 装饰器接受一个可选参数 `id`,用于标识当前需要接收的事件,如果不指定,则默认为空 `""`。
在 `handle_func` 函数中,可以通过依赖注入的方式来获取接收到的事件,参考:[`Received`](#received)、[`LastReceived`](#lastreceived)。
:::important 提示
`receive` 装饰器可以和自身与 `got` 装饰器嵌套使用
:::
:::warning 注意
如果存在多个 `receive` 装饰器,则必须指定不相同的多个 `id`;否则相同的 `id` 将会被跳过接收。
```python
matcher = on_message()
@matcher.receive("id1")
@matcher.receive("id2")
async def handle_func():
# do something here
```
:::
### 使用 `got` 装饰器
```python {3-5}
matcher = on_message()
@matcher.got("key", prompt="Key?")
async def handle_func(key: Message = Arg()):
# do something here
```
`got` 装饰器与 `receive` 装饰器一样,会中断当前事件处理流程,等待接收一个新的事件。但与 `receive` 装饰器不同的是,`got` 装饰器用于接收一条消息,并且可以控制是否向用户发送询问 `prompt` 等,更贴近于对话形式会话。
`got` 装饰器接受一个参数 `key` 和一个可选参数 `prompt`,当 `key` 不存在时,会向用户发送 `prompt` 消息,并等待用户回复。
在 `handle_func` 函数中,可以通过依赖注入的方式来获取接收到的消息,参考:[`Arg`](#arg)、[`ArgStr`](#argstr)、[`ArgPlainText`](#argplaintext)。
:::important 提示
`got` 装饰器可以和自身与 `receive` 装饰器嵌套使用
:::
### 直接添加
```python {2}
matcher = on_message(
handlers=[handle_func, or_dependent]
)
```
:::warning 注意
通过该方法添加的处理依赖将会处于整个事件处理流程的最前,因此,如果再使用 `handle` 等装饰器,则会在其之后。
:::
## 事件处理流程
在一个事件响应器中,事件被添加的处理依赖依次执行,直到所有处理依赖都执行完毕,或者遇到了某个处理依赖需要更多的事件来进行下一步的处理。在下一个事件到来并符合响应要求时,继续执行。更多有关 NoneBot 事件分发与处理流程的详细信息,请参考[进阶 - 深入](../../advanced/README.md)。
## 获取上下文信息
在事件处理流程中,事件响应器具有自己独立的上下文,例如:当前的事件、机器人等信息,可以通过依赖注入的方式来获取。
### Bot
获取当前事件的 Bot 对象。
```python {7-9}
from typing import Union
from nonebot.adapters import Bot
from nonebot.adapters.ding import Bot as DingBot
from nonebot.adapters.onebot.v11 import Bot as OneBotV11Bot
async def _(foo: Bot): ...
async def _(foo: Union[DingBot, OneBotV11Bot]): ...
async def _(bot): ... # 兼容性处理
```
### Event
获取当前事件。
```python {6-8}
from typing import Union
from nonebot.adapters import Event
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
async def _(foo: Event): ...
async def _(foo: Union[PrivateMessageEvent, GroupMessageEvent]): ...
async def _(event): ... # 兼容性处理
```
### EventType
获取当前事件的类型。
```python {3}
from nonebot.params import EventType
async def _(foo: str = EventType()): ...
```
### EventMessage
获取当前事件的消息。
```python {4}
from nonebot.adapters import Message
from nonebot.params import EventMessage
async def _(foo: Message = EventMessage()): ...
```
### EventPlainText
获取当前事件的消息纯文本部分。
```python {3}
from nonebot.params import EventPlainText
async def _(foo: str = EventPlainText()): ...
```
### EventToMe
获取当前事件是否与机器人相关。
```python {3}
from nonebot.params import EventToMe
async def _(foo: bool = EventToMe()): ...
```
### State
获取当前事件处理上下文状态State 为一个字典,用户可以向 State 中添加数据来保存状态等操作。(请注意不要随意覆盖 State 中 NoneBot 的数据)
```python {4}
from nonebot.typing import T_State
async def _(foo: T_State): ...
```
### Command
获取当前命令型消息的元组形式命令名。
```python {7}
from nonebot import on_command
from nonebot.params import Command
matcher = on_command("cmd")
@matcher.handle()
async def _(foo: Tuple[str, ...] = Command()): ...
```
:::tip 提示
命令详情只能在首次接收到命令型消息时获取,如果在事件处理后续流程中获取,则会获取到不同的值。
:::
### RawCommand
获取当前命令型消息的文本形式命令名。
```python {7}
from nonebot import on_command
from nonebot.params import RawCommand
matcher = on_command("cmd")
@matcher.handle()
async def _(foo: str = RawCommand()): ...
```
:::tip 提示
命令详情只能在首次接收到命令型消息时获取,如果在事件处理后续流程中获取,则会获取到不同的值。
:::
### CommandArg
获取命令型消息命令后跟随的参数。
```python {8}
from nonebot import on_command
from nonebot.adapters import Message
from nonebot.params import CommandArg
matcher = on_command("cmd")
@matcher.handle()
async def _(foo: Message = CommandArg()): ...
```
:::tip 提示
命令详情只能在首次接收到命令型消息时获取,如果在事件处理后续流程中获取,则会获取到不同的值。
:::
### CommandStart
获取命令型消息命令前缀。
```python {8}
from nonebot import on_command
from nonebot.adapters import Message
from nonebot.params import CommandStart
matcher = on_command("cmd")
@matcher.handle()
async def _(foo: str = CommandStart()): ...
```
:::tip 提示
命令详情只能在首次接收到命令型消息时获取,如果在事件处理后续流程中获取,则会获取到不同的值。
:::
### ShellCommandArgs
获取 shell 命令解析后的参数,支持 MessageSegment 富文本(如:图片)。
:::tip 提示
如果参数解析失败,则为 [`ParserExit`](../../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。
由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。
:::
```python {8,12}
from nonebot import on_shell_command
from nonebot.params import ShellCommandArgs
from nonebot.rule import Namespace, ArgumentParser
parser = ArgumentParser("demo")
# parser.add_argument ...
matcher = on_shell_command("cmd", parser)
# 解析失败
@matcher.handle()
async def _(foo: ParserExit = ShellCommandArgs()):
if foo.status == 0:
foo.message # help message
else:
foo.message # error message
# 解析成功
@matcher.handle()
async def _(foo: Namespace = ShellCommandArgs()): ...
```
### ShellCommandArgv
获取 shell 命令解析前的参数列表,支持 MessageSegment 富文本(如:图片)。
```python {7}
from nonebot import on_shell_command
from nonebot.params import ShellCommandArgs
matcher = on_shell_command("cmd")
@matcher.handle()
async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
```
### RegexStr
获取正则匹配结果的文本。
```python {7}
from nonebot import on_regex
from nonebot.params import RegexStr
matcher = on_regex("regex")
@matcher.handle()
async def _(foo: str = RegexStr()): ...
```
### RegexGroup
获取正则匹配结果的 group 元组。
```python {7}
from nonebot import on_regex
from nonebot.params import RegexGroup
matcher = on_regex("regex")
@matcher.handle()
async def _(foo: Tuple[Any, ...] = RegexGroup()): ...
```
### RegexDict
获取正则匹配结果的 group 字典。
```python {7}
from nonebot import on_regex
from nonebot.params import RegexDict
matcher = on_regex("regex")
@matcher.handle()
async def _(foo: Dict[str, Any] = RegexDict()): ...
```
### Startswith
获取触发响应器的消息前缀字符串。
```python {7}
from nonebot import on_startswith
from nonebot.params import Startswith
matcher = on_startswith("prefix")
@matcher.handle()
async def _(foo: str = Startswith()): ...
```
### Endswith
获取触发响应器的消息后缀字符串。
```python {7}
from nonebot import on_endswith
from nonebot.params import Endswith
matcher = on_endswith("suffix")
@matcher.handle()
async def _(foo: str = Endswith()): ...
```
### Fullmatch
获取触发响应器的消息字符串。
```python {7}
from nonebot import on_fullmatch
from nonebot.params import Fullmatch
matcher = on_fullmatch("fullmatch")
@matcher.handle()
async def _(foo: str = Fullmatch()): ...
```
### Keyword
获取触发响应器的关键字字符串。
```python {7}
from nonebot import on_keyword
from nonebot.params import Keyword
matcher = on_keyword({"keyword"})
@matcher.handle()
async def _(foo: str = Keyword()): ...
```
### Matcher
获取当前事件响应器实例。
```python {7}
from nonebot import on_message
from nonebot.matcher import Matcher
foo = on_message()
@foo.handle()
async def _(matcher: Matcher): ...
```
### Received
获取某次 `receive` 接收的事件。
```python {8}
from nonebot import on_message
from nonebot.adapters import Event
from nonebot.params import Received
matcher = on_message()
@matcher.receive("id")
async def _(foo: Event = Received("id")): ...
```
### LastReceived
获取最近一次 `receive` 接收的事件。
```python {8}
from nonebot import on_message
from nonebot.adapters import Event
from nonebot.params import LastReceived
matcher = on_message()
@matcher.receive("any")
async def _(foo: Event = LastReceived()): ...
```
### Arg
获取某次 `got` 接收的参数。
```python {8-9}
from nonebot.params import Arg
from nonebot import on_message
from nonebot.adapters import Message
matcher = on_message()
@matcher.got("key")
async def _(key: Message = Arg()): ...
async def _(foo: Message = Arg("key")): ...
```
### ArgStr
获取某次 `got` 接收的参数,并转换为字符串。
```python {7-8}
from nonebot import on_message
from nonebot.params import ArgStr
matcher = on_message()
@matcher.got("key")
async def _(key: str = ArgStr()): ...
async def _(foo: str = ArgStr("key")): ...
```
### ArgPlainText
获取某次 `got` 接收的参数的纯文本部分。
```python {7-8}
from nonebot import on_message
from nonebot.params import ArgPlainText
matcher = on_message()
@matcher.got("key")
async def _(key: str = ArgPlainText()): ...
async def _(foo: str = ArgPlainText("key")): ...
```
### Exception
获取事件响应器运行中抛出的异常。
```python {4}
from nonebot.message import run_postprocessor
@run_postprocessor
async def _(e: Exception): ...
```
### Default
带有默认值的参数,便于复用依赖。
```python {1}
async def _(foo="bar"): ...
```