mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-25 01:25:04 +08:00
421 lines
16 KiB
Markdown
421 lines
16 KiB
Markdown
---
|
||
sidebar_position: 5
|
||
description: 事件响应器组成与内置响应规则
|
||
|
||
options:
|
||
menu:
|
||
weight: 60
|
||
category: advanced
|
||
---
|
||
|
||
# 事件响应器进阶
|
||
|
||
在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。
|
||
|
||
## 事件响应器组成
|
||
|
||
### 事件响应器类型
|
||
|
||
事件响应器类型 `type` 即是该响应器所要响应的事件类型,只有在接收到的事件类型与该响应器的类型相同时,才会触发该响应器。如果类型为空字符串 `""`,则响应器将会响应所有类型的事件。事件响应器类型的检查在所有其他检查(权限控制、响应规则)之前进行。
|
||
|
||
NoneBot 内置了四种常用事件类型:`meta_event`、`message`、`notice`、`request`,分别对应元事件、消息、通知、请求。通常情况下,协议适配器会将事件合理地分类至这四种类型中。如果有其他类型的事件需要响应,可以自行定义新的类型。
|
||
|
||
### 事件触发权限
|
||
|
||
事件触发权限 `permission` 是一个 `Permission` 对象,这在[权限控制](../appendices/permission.mdx)一节中已经介绍过。事件触发权限会在事件响应器的类型检查通过后进行检查,如果权限检查通过,则执行响应规则检查。
|
||
|
||
### 事件响应规则
|
||
|
||
事件响应规则 `rule` 是一个 `Rule` 对象,这在[响应规则](../appendices/rule.md)一节中已经介绍过。事件响应器的响应规则会在事件响应器的权限检查通过后进行匹配,如果响应规则检查通过,则触发该响应器。
|
||
|
||
### 响应优先级
|
||
|
||
响应优先级 `priority` 是一个正整数,用于指定响应器的优先级。响应器的优先级越小,越先被触发。如果响应器的优先级相同,则按照响应器的注册顺序进行触发。
|
||
|
||
### 阻断
|
||
|
||
阻断 `block` 是一个布尔值,用于指定响应器是否阻断事件的传播。如果阻断为 `True`,则在该响应器被触发后,事件将不会再传播给其他下一优先级的响应器。
|
||
|
||
NoneBot 内置的事件响应器中,所有非 `command` 规则的 `message` 类型的事件响应器都会阻断事件传递,其他则不会。
|
||
|
||
在部分情况中,可以使用 [`stop_propagation`](../appendices/session-control.mdx#stop_propagation) 方法动态阻止事件传播,该方法需要 handler 在参数中获取 matcher 实例后调用方法。
|
||
|
||
### 有效期
|
||
|
||
事件响应器的有效期分为 `temp` 和 `expire_time` 。`temp` 是一个布尔值,用于指定响应器是否为临时响应器。如果为 `True`,则该响应器在被触发后会被自动销毁。`expire_time` 是一个 `datetime` 对象,用于指定响应器的过期时间。如果 `expire_time` 不为 `None`,则在该时间点后,该响应器会被自动销毁。
|
||
|
||
### 默认状态
|
||
|
||
事件响应器的默认状态 `default_state` 是一个 `dict` 对象,用于指定响应器的默认状态。在响应器被触发时,响应器将会初始化默认状态然后开始执行事件处理流程。
|
||
|
||
## 基本辅助函数
|
||
|
||
NoneBot 为四种类型的事件响应器提供了五个基本的辅助函数:
|
||
|
||
- `on`:创建任何类型的事件响应器。
|
||
- `on_metaevent`:创建元事件响应器。
|
||
- `on_message`:创建消息事件响应器。
|
||
- `on_request`:创建请求事件响应器。
|
||
- `on_notice`:创建通知事件响应器。
|
||
|
||
除了 `on` 函数具有一个 `type` 参数外,其余参数均相同:
|
||
|
||
- `rule`:响应规则,可以是 `Rule` 对象或者 `RuleChecker` 函数。
|
||
- `permission`:事件触发权限,可以是 `Permission` 对象或者 `PermissionChecker` 函数。
|
||
- `handlers`:事件处理函数列表。
|
||
- `temp`:是否为临时响应器。
|
||
- `expire_time`:响应器的过期时间。
|
||
- `priority`:响应器的优先级。
|
||
- `block`:是否阻断事件传播。
|
||
- `state`:响应器的默认状态。
|
||
|
||
在消息类型的事件响应器的基础上,NoneBot 还内置了一些常用的响应规则,并结合为辅助函数来方便我们快速创建指定功能的响应器。下面我们逐个介绍。
|
||
|
||
## 内置响应规则
|
||
|
||
### `startswith`
|
||
|
||
`startswith` 响应规则用于匹配消息纯文本部分的开头是否与指定字符串(或一系列字符串)相同。可选参数 `ignorecase` 用于指定是否忽略大小写,默认为 `False`。
|
||
|
||
例如,我们可以创建一个匹配消息开头为 `!` 或者 `/` 的规则:
|
||
|
||
```python
|
||
from nonebot.rule import startswith
|
||
|
||
rule = startswith(("!", "/"), ignorecase=False)
|
||
```
|
||
|
||
也可以直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_startswith
|
||
|
||
matcher = on_startswith(("!", "/"), ignorecase=False)
|
||
```
|
||
|
||
### `endswith`
|
||
|
||
`endswith` 响应规则用于匹配消息纯文本部分的结尾是否与指定字符串(或一系列字符串)相同。可选参数 `ignorecase` 用于指定是否忽略大小写,默认为 `False`。
|
||
|
||
例如,我们可以创建一个匹配消息结尾为 `.` 或者 `。` 的规则:
|
||
|
||
```python
|
||
from nonebot.rule import endswith
|
||
|
||
rule = endswith((".", "。"), ignorecase=False)
|
||
```
|
||
|
||
也可以直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_endswith
|
||
|
||
matcher = on_endswith((".", "。"), ignorecase=False)
|
||
```
|
||
|
||
### `fullmatch`
|
||
|
||
`fullmatch` 响应规则用于匹配消息纯文本部分是否与指定字符串(或一系列字符串)完全相同。可选参数 `ignorecase` 用于指定是否忽略大小写,默认为 `False`。
|
||
|
||
例如,我们可以创建一个匹配消息为 `ping` 或者 `pong` 的规则:
|
||
|
||
```python
|
||
from nonebot.rule import fullmatch
|
||
|
||
rule = fullmatch(("ping", "pong"), ignorecase=False)
|
||
```
|
||
|
||
也可以直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_fullmatch
|
||
|
||
matcher = on_fullmatch(("ping", "pong"), ignorecase=False)
|
||
```
|
||
|
||
### `keyword`
|
||
|
||
`keyword` 响应规则用于匹配消息纯文本部分是否包含指定字符串(或一系列字符串)。
|
||
|
||
例如,我们可以创建一个匹配消息中包含 `hello` 或者 `hi` 的规则:
|
||
|
||
```python
|
||
from nonebot.rule import keyword
|
||
|
||
rule = keyword("hello", "hi")
|
||
```
|
||
|
||
也可以直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_keyword
|
||
|
||
matcher = on_keyword("hello", "hi")
|
||
```
|
||
|
||
### `command`
|
||
|
||
`command` 是最常用的响应规则,它用于匹配消息是否为命令。它会根据配置中的 [Command Start 和 Command Separator](../appendices/config.mdx#command-start-和-command-separator) 来判断消息是否为命令。
|
||
|
||
例如,当我们配置了 `Command Start` 为 `/`,`Command Separator` 为 `.` 时:
|
||
|
||
```python
|
||
from nonebot.rule import command
|
||
|
||
# 匹配 "/help" 或者 "/帮助" 开头的消息
|
||
rule = command("help", "帮助")
|
||
# 匹配 "/help.cmd" 开头的消息
|
||
rule = command(("help", "cmd"))
|
||
```
|
||
|
||
也可以直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_command
|
||
|
||
matcher = on_command("help", aliases={"帮助"})
|
||
```
|
||
|
||
此外,`command` 响应规则默认允许消息命令与参数间不加空格,如果需要严格匹配命令与参数间的空白符,可以使用 `command` 函数的 `force_whitespace` 参数。`force_whitespace` 参数可以是 bool 类型或者具体的字符串,默认为 `False`。如果为 `True`,则命令与参数间必须有任意个数的空白符;如果为字符串,则命令与参数间必须有且与给定字符串一致的空白符。
|
||
|
||
```python
|
||
rule = command("help", force_whitespace=True)
|
||
rule = command("help", force_whitespace=" ")
|
||
```
|
||
|
||
命令解析后的结果可以通过 [`Command`](./dependency.mdx#command)、[`RawCommand`](./dependency.mdx#rawcommand)、[`CommandArg`](./dependency.mdx#commandarg)、[`CommandStart`](./dependency.mdx#commandstart)、[`CommandWhitespace`](./dependency.mdx#commandwhitespace) 依赖注入获取。
|
||
|
||
### `shell_command`
|
||
|
||
`shell_command` 响应规则用于匹配类 shell 命令形式的消息。它首先与 [`command`](#command) 响应规则一样进行命令匹配,如果匹配成功,则会进行进一步的参数解析。参数解析采用 `argparse` 标准库进行,在此基础上添加了消息序列 `Message` 支持。
|
||
|
||
例如,我们可以创建一个匹配 `/cmd` 命令并且带有 `-v` 选项与默认 `-h` 帮助选项的规则:
|
||
|
||
```python
|
||
from nonebot.rule import shell_command, ArgumentParser
|
||
|
||
parser = ArgumentParser()
|
||
parser.add_argument("-v", "--verbose", action="store_true")
|
||
|
||
rule = shell_command("cmd", parser=parser)
|
||
```
|
||
|
||
更多关于 `argparse` 的使用方法请参考 [argparse 文档](https://docs.python.org/zh-cn/3/library/argparse.html)。我们也可以选择不提供 `parser` 参数,这样 `shell_command` 将不会解析参数,但会提供参数列表 `argv`。
|
||
|
||
直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_shell_command
|
||
from nonebot.rule import ArgumentParser
|
||
|
||
parser = ArgumentParser()
|
||
parser.add_argument("-v", "--verbose", action="store_true")
|
||
|
||
matcher = on_shell_command("cmd", parser=parser)
|
||
```
|
||
|
||
参数解析后的结果可以通过 [`ShellCommandArgv`](./dependency.mdx#shellcommandargv)、[`ShellCommandArgs`](./dependency.mdx#shellcommandargs) 依赖注入获取。
|
||
|
||
### `regex`
|
||
|
||
`regex` 响应规则用于匹配消息是否与指定正则表达式匹配。
|
||
|
||
:::tip 提示
|
||
正则表达式匹配使用 search 而非 match,如需从头匹配请使用 `r"^xxx"` 模式来确保匹配开头。
|
||
:::
|
||
|
||
例如,我们可以创建一个匹配消息中包含字母并且忽略大小写的规则:
|
||
|
||
```python
|
||
from nonebot.rule import regex
|
||
|
||
rule = regex(r"[a-z]+", flags=re.IGNORECASE)
|
||
```
|
||
|
||
也可以直接使用辅助函数新建一个响应器:
|
||
|
||
```python
|
||
from nonebot import on_regex
|
||
|
||
matcher = on_regex(r"[a-z]+", flags=re.IGNORECASE)
|
||
```
|
||
|
||
正则匹配后的结果可以通过 [`RegexStr`](./dependency.mdx#regexstr)、[`RegexGroup`](./dependency.mdx#regexgroup)、[`RegexDict`](./dependency.mdx#regexdict) 依赖注入获取。
|
||
|
||
### `to_me`
|
||
|
||
`to_me` 响应规则用于匹配事件是否与机器人相关。
|
||
|
||
例如:
|
||
|
||
```python
|
||
from nonebot.rule import to_me
|
||
|
||
rule = to_me()
|
||
```
|
||
|
||
### `is_type`
|
||
|
||
`is_type` 响应规则用于匹配事件类型是否为指定类型(或者一系列类型)。
|
||
|
||
例如,我们可以创建一个匹配 OneBot v11 私聊和群聊消息事件的规则:
|
||
|
||
```python
|
||
from nonebot.rule import is_type
|
||
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
||
|
||
rule = is_type(PrivateMessageEvent, GroupMessageEvent)
|
||
```
|
||
|
||
## 响应器组
|
||
|
||
为了更方便的管理一系列功能相近的响应器,NoneBot 提供了两种响应器组,它们可以帮助我们进行响应器的统一管理。
|
||
|
||
### `CommandGroup`
|
||
|
||
`CommandGroup` 可以用于管理一系列具有相同前置命令的子命令响应器。
|
||
|
||
例如,我们创建 `/cmd`、`/cmd.sub`、`/cmd.help` 三个命令,他们具有相同的优先级:
|
||
|
||
```python
|
||
from nonebot import CommandGroup
|
||
|
||
group = CommandGroup("cmd", priority=10)
|
||
|
||
cmd = group.command(tuple())
|
||
sub_cmd = group.command("sub")
|
||
help_cmd = group.command("help")
|
||
```
|
||
|
||
### `MatcherGroup`
|
||
|
||
`MatcherGroup` 可以用于管理一系列具有相同属性的响应器。
|
||
|
||
例如,我们创建一个具有相同响应规则的响应器组:
|
||
|
||
```python
|
||
from nonebot.rule import to_me
|
||
from nonebot import MatcherGroup
|
||
|
||
group = MatcherGroup(rule=to_me())
|
||
|
||
matcher1 = group.on_message()
|
||
matcher2 = group.on_message()
|
||
```
|
||
|
||
## 第三方响应规则
|
||
|
||
### Alconna
|
||
|
||
[`nonebot-plugin-alconna`](https://github.com/ArcletProject/nonebot-plugin-alconna) 是一类提供了拓展响应规则的插件。
|
||
该插件使用 [Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器,
|
||
是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
|
||
|
||
特点包括:
|
||
|
||
- 高效
|
||
- 直观的命令组件创建方式
|
||
- 强大的类型解析与类型转换功能
|
||
- 自定义的帮助信息格式
|
||
- 多语言支持
|
||
- 易用的快捷命令创建与使用
|
||
- 可创建命令补全会话, 以实现多轮连续的补全提示
|
||
- 可嵌套的多级子命令
|
||
- 正则匹配支持
|
||
|
||
该插件提供了一类新的事件响应器辅助函数 `on_alconna`,以及 `AlconnaResult` 等依赖注入函数。
|
||
|
||
同时,基于 [Annotated 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了两类注解 `AlcMatches` 与 `AlcResult`
|
||
|
||
该插件还可以通过 `handle(parameterless)` 来控制一个具体的响应函数是否在不满足条件时跳过响应:
|
||
|
||
- `pip.handle([Check(assign("add.name", "nb"))])` 表示仅在命令为 `role-group add` 并且 name 为 `nb` 时响应
|
||
- `pip.handle([Check(assign("list"))])` 表示仅在命令为 `role-group list` 时响应
|
||
- `pip.handle([Check(assign("add"))])` 表示仅在命令为 `role-group add` 时响应
|
||
|
||
基于 `Alconna` 的特性,该插件同时提供了一系列便捷的消息段标注。
|
||
标注可用于在 `Alconna` 中匹配消息中除 text 外的其他消息段,也可用于快速创建各适配器下的消息段。所有标注位于 `nonebot_plugin_alconna.adapters` 中。
|
||
|
||
#### 插件安装
|
||
|
||
```shell
|
||
nb plugin install nonebot-plugin-alconna
|
||
```
|
||
|
||
或
|
||
|
||
```shell
|
||
pip install nonebot-plugin-alconna
|
||
```
|
||
|
||
#### 示例
|
||
|
||
```python
|
||
from nonebot_plugin_alconna.adapters import At
|
||
from nonebot.adapters.onebot.v12 import Message
|
||
from nonebot_plugin_alconna.adapters.onebot12 import Image
|
||
from nonebot_plugin_alconna import AlconnaMatches, on_alconna
|
||
from nonebot.adapters.onebot.v12 import MessageSegment as Ob12MS
|
||
from arclet.alconna import Args, Option, Alconna, Arparma, MultiVar, Subcommand
|
||
|
||
alc = Alconna(
|
||
"role-group",
|
||
Subcommand(
|
||
"add",
|
||
Args["name", str],
|
||
Option("member", Args["target", MultiVar(At)]),
|
||
),
|
||
Option("list"),
|
||
)
|
||
rg = on_alconna(alc, auto_send_output=True)
|
||
|
||
|
||
@rg.handle()
|
||
async def _(result: Arparma = AlconnaMatches()):
|
||
if result.find("list"):
|
||
img = await gen_role_group_list_image()
|
||
await rg.finish(Message([Image(img)]))
|
||
if result.find("add"):
|
||
group = await create_role_group(result["add.name"])
|
||
if result.find("add.member"):
|
||
ats: tuple[Ob12MS, ...] = result["add.member.target"]
|
||
group.extend(member.data["user_id"] for member in ats)
|
||
await rg.finish("添加成功")
|
||
```
|
||
|
||
我们可以看到主要的两大组件:`Option` 与 `Subcommand`。
|
||
|
||
`Option` 可以传入一组别名,如 `Option("--foo|-F|--FOO|-f")` 或 `Option("--foo", alias=["-F"]`
|
||
|
||
`Subcommand` 则可以传入自己的 `Option` 与 `Subcommand`:
|
||
|
||
他们拥有如下共同参数:
|
||
|
||
- `help_text`: 传入该组件的帮助信息
|
||
- `dest`: 被指定为解析完成时标注匹配结果的标识符,不传入时默认为选项或子命令的名称 (name)
|
||
- `requires`: 一段指定顺序的字符串列表,作为唯一的前置序列与命令嵌套替换
|
||
- `default`: 默认值,在该组件未被解析时使用使用该值替换。
|
||
|
||
其次使用了消息段标注,其中 `At` 属于通用标注,而 `Image` 属于 `onebot12` 适配器下的标注。
|
||
|
||
`on_alconna` 的所有参数如下:
|
||
|
||
- `command: Alconna | str`: Alconna 命令
|
||
- `skip_for_unmatch: bool = True`: 是否在命令不匹配时跳过该响应
|
||
- `auto_send_output: bool = False`: 是否自动发送输出信息并跳过响应
|
||
- `output_converter: TConvert | None = None`: 输出信息字符串转换为消息序列方法
|
||
- `aliases: set[str | tuple[str, ...]] | None = None`: 命令别名, 作用类似于 `on_command` 中的 aliases
|
||
- `comp_config: CompConfig | None = None`: 补全会话配置, 不传入则不启用补全会话
|
||
|
||
`AlconnaMatches` 是一个依赖注入函数,可注入 `Alconna` 命令解析结果。
|
||
|
||
#### 参考
|
||
|
||
插件文档: [📦 这里](https://github.com/ArcletProject/nonebot-plugin-alconna/blob/master/docs.md)
|
||
|
||
官方文档: [👉 指路](https://arclet.top/)
|
||
|
||
QQ 交流群: [🔗 链接](https://jq.qq.com/?_wv=1027&k=PUPOnCSH)
|
||
|
||
友链: [📚 文档](https://graiax.cn/guide/message_parser/alconna.html)
|