--- sidebar_position: 5 description: 事件响应器组成与内置响应规则 options: menu: weight: 60 category: advanced --- # 事件响应器进阶 在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。 :::tip 提示 事件响应器允许继承,你可以通过直接继承 `Matcher` 类来创建一个新的事件响应器。 ::: ## 事件响应器组成 ### 事件响应器类型 事件响应器类型 `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") ``` 命令别名 aliases 默认不会添加 `CommandGroup` 设定的前缀,如果需要为 aliases 添加前缀,可以添加 `prefix_aliases=True` 参数: ```python from nonebot import CommandGroup group = CommandGroup("cmd", prefix_aliases=True) cmd = group.command(tuple()) help_cmd = group.command("help", aliases={"帮助"}) ``` 这样就能成功匹配 `/cmd`、`/cmd.help`、`/cmd.帮助` 命令。如果未设置,将默认匹配 `/cmd`、`/cmd.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)