事件响应器进阶
在指南与深入中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。
事件响应器组成
事件响应器类型
事件响应器类型 type
即是该响应器所要响应的事件类型,只有在接收到的事件类型与该响应器的类型相同时,才会触发该响应器。如果类型为空字符串 ""
,则响应器将会响应所有类型的事件。事件响应器类型的检查在所有其他检查(权限控制、响应规则)之前进行。
NoneBot 内置了四种常用事件类型:meta_event
、message
、notice
、request
,分别对应元事件、消息、通知、请求。通常情况下,协议适配器会将事件合理地分类至这四种类型中。如果有其他类型的事件需要响应,可以自行定义新的类型。
事件触发权限
事件触发权限 permission
是一个 Permission
对象,这在权限控制一节中已经介绍过。事件触发权限会在事件响应器的类型检查通过后进行检查,如果权限检查通过,则执行响应规则检查。
事件响应规则
事件响应规则 rule
是一个 Rule
对象,这在响应规则一节中已经介绍过。事件响应器的响应规则会在事件响应器的权限检查通过后进行匹配,如果响应规则检查通过,则触发该响应器。
响应优先级
响应优先级 priority
是一个正整数,用于指定响应器的优先级。响应器的优先级越小,越先被触发。如果响应器的优先级相同,则按照响应器的注册顺序进行触发。
阻断
阻断 block
是一个布尔值,用于指定响应器是否阻断事件的传播。如果阻断为 True
,则在该响应器被触发后,事件将不会再传播给其他下一优先级的响应器。
NoneBot 内置的事件响应器中,所有非 command
规则的 message
类型的事件响应器都会阻断事件传递,其他则不会。
在部分情况中,可以使用 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
。
例如,我们可以创建一个匹配消息开头为 !
或者 /
的规则:
from nonebot.rule import startswith
rule = startswith(("!", "/"), ignorecase=False)
也可以直接使用辅助函数新建一个响应器:
from nonebot import on_startswith
matcher = on_startswith(("!", "/"), ignorecase=False)
endswith
endswith
响应规则用于匹配消息纯文本部分的结尾是否与指定字符串(或一系列字符串)相同。可选参数 ignorecase
用于指定是否忽略大小写,默认为 False
。
例如,我们可以创建一个匹配消息结尾为 .
或者 。
的规则:
from nonebot.rule import endswith
rule = endswith((".", "。"), ignorecase=False)
也可以直接使用辅助函数新建一个响应器:
from nonebot import on_endswith
matcher = on_endswith((".", "。"), ignorecase=False)
fullmatch
fullmatch
响应规则用于匹配消息纯文本部分是否与指定字符串(或一系列字符串)完全相同。可选参数 ignorecase
用于指定是否忽略大小写,默认为 False
。
例如,我们可以创建一个匹配消息为 ping
或者 pong
的规则:
from nonebot.rule import fullmatch
rule = fullmatch(("ping", "pong"), ignorecase=False)
也可以直接使用辅助函数新建一个响应器:
from nonebot import on_fullmatch
matcher = on_fullmatch(("ping", "pong"), ignorecase=False)
keyword
keyword
响应规则用于匹配消息纯文本部分是否包含指定字符串(或一系列字符串)。
例如,我们可以创建一个匹配消息中包含 hello
或者 hi
的规则:
from nonebot.rule import keyword
rule = keyword("hello", "hi")
也可以直接使用辅助函数新建一个响应器:
from nonebot import on_keyword
matcher = on_keyword("hello", "hi")
command
command
是最常用的响应规则,它用于匹配消息是否为命令。它会根据配置中的 Command Start 和 Command Separator 来判断消息是否为命令。
例如,当我们配置了 Command Start
为 /
,Command Separator
为 .
时:
from nonebot.rule import command
# 匹配 "/help" 或者 "/帮助" 开头的消息
rule = command("help", "帮助")
# 匹配 "/help.cmd" 开头的消息
rule = command(("help", "cmd"))
也可以直接使用辅助函数新建一个响应器:
from nonebot import on_command
matcher = on_command("help", aliases={"帮助"})
此外,command
响应规则默认允许消息命令与参数间不加空格,如果需要严格匹配命令与参数间的空白符,可以使用 command
函数的 force_whitespace
参数。force_whitespace
参数可以是 bool 类型或者具体的字符串,默认为 False
。如果为 True
,则命令与参数间必须有任意个数的空白符;如果为字符串,则命令与参数间必须有且与给定字符串一致的空白符。
rule = command("help", force_whitespace=True)
rule = command("help", force_whitespace=" ")
命令解析后的结果可以通过 Command
、RawCommand
、CommandArg
、CommandStart
、CommandWhitespace
依赖注入获取。
shell_command
shell_command
响应规则用于匹配类 shell 命令形式的消息。它首先与 command
响应规则一样进行命令匹配,如果匹配成功,则会进行进一步的参数解析。参数解析采用 argparse
标准库进行,在此基础上添加了消息序列 Message
支持。
例如,我们可以创建一个匹配 /cmd
命令并且带有 -v
选项与默认 -h
帮助选项的规则:
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 文档。我们也可以选择不提供 parser
参数,这样 shell_command
将不会解析参数,但会提供参数列表 argv
。
直接使用辅助函数新建一个响应器:
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
、ShellCommandArgs
依赖注入获取。
regex
regex
响应规则用于匹配消息是否与指定正则表达式匹配。
提示
正则表达式匹配使用 search 而非 match,如需从头匹配请使用 r"^xxx"
模式来确保匹配开头。
例如,我们可以创建一个匹配消息中包含字母并且忽略大小写的规则:
from nonebot.rule import regex
rule = regex(r"[a-z]+", flags=re.IGNORECASE)
也可以直接使用辅助函数新建一个响应器:
from nonebot import on_regex
matcher = on_regex(r"[a-z]+", flags=re.IGNORECASE)
正则匹配后的结果可以通过 RegexStr
、RegexGroup
、RegexDict
依赖注入获取。
to_me
to_me
响应规则用于匹配事件是否与机器人相关。
例如:
from nonebot.rule import to_me
rule = to_me()
is_type
is_type
响应规则用于匹配事件类型是否为指定类型(或者一系列类型)。
例如,我们可以创建一个匹配 OneBot v11 私聊和群聊消息事件的规则:
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
三个命令,他们具有相同的优先级:
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
可以用于管理一系列具有相同属性的响应器。
例如,我们创建一个具有相同响应规则的响应器组:
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
是一类提供了拓展响应规则的插件。
该插件使用 Alconna 作为命令解析器,
是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
特点包括:
- 高效
- 直观的命令组件创建方式
- 强大的类型解析与类型转换功能
- 自定义的帮助信息格式
- 多语言支持
- 易用的快捷命令创建与使用
- 可创建命令补全会话, 以实现多轮连续的补全提示
- 可嵌套的多级子命令
- 正则匹配支持
该插件提供了一类新的事件响应器辅助函数 on_alconna
,以及 AlconnaResult
等依赖注入函数。
同时,基于 Annotated 支持, 添加了两类注解 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
中。
插件安装
nb plugin install nonebot-plugin-alconna
或
pip install nonebot-plugin-alconna
示例
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
中的 aliasescomp_config: CompConfig | None = None
: 补全会话配置, 不传入则不启用补全会话
AlconnaMatches
是一个依赖注入函数,可注入 Alconna
命令解析结果。
参考
插件文档: 📦 这里
官方文档: 👉 指路
QQ 交流群: 🔗 链接
友链: 📚 文档