- 后端
- 即时通讯
【免费下载链接】nonebot2
跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python
导读
在 NoneBot2 中,机器人会接收到来自各种适配器(QQ、微信、Telegram、Discord 等)的多种事件,而响应规则(Rule)正是控制“哪些事件应该被哪个事件响应器处理”的核心机制。本指南以 NoneBot2 官方文档《响应规则》为骨架,结合 nonebot/internal/rule.py 与 nonebot/rule.py 的源码实现,系统讲解RuleChecker依赖注入、Rule并发检查原理、&运算符合并规则、主动调用规则判定事件,以及全部内置响应规则的用法。读完本文,你将能编写插件级开关规则、黑名单规则,并熟练组合出符合业务需求的响应条件。
响应规则的作用:为事件处理把关
机器人在实际应用中,往往会接收到多种多样的事件类型:普通消息、群成员变动、加好友请求、元事件等。NoneBot 通过响应规则来控制事件的处理——只有通过规则检查的事件,才会交给对应的事件响应器(Matcher)执行后续逻辑。
在指南中,我们为weather命令添加了一个rule=to_me()参数,这个参数就是一个响应规则,确保只有在私聊或者@bot时才会响应。这就是响应规则最典型的应用场景:把“该事件是否与我相关”这类判断从业务逻辑中抽离出来,交给框架在事件分发阶段统一完成。
从源码结构看,每个事件响应器都拥有一个Rule对象,事件传递时会先经过规则检查再运行处理函数(参见 nonebot/internal/rule.py 中Rule的类文档)。响应规则是一个Rule对象,它由一系列的RuleChecker函数组成,每个RuleChecker函数都会检查事件是否符合条件,如果所有的检查都通过,则事件会被处理。
RuleChecker:可依赖注入的判定函数
RuleChecker是一个返回值为bool类型的依赖函数,即RuleChecker支持依赖注入。在 nonebot/typing.py 中,其类型别名定义为_DependentCallable[bool],文档明确说明“RuleChecker 即判断是否响应事件的处理函数”,且支持依赖参数。
这意味着我们可以直接在RuleChecker的参数列表中声明Bot、Event、State,甚至声明其他依赖(如Depend)和配置项。框架会像处理事件处理函数一样,对RuleChecker进行依赖解析后再调用。
我们可以根据配置项在weather插件目录中编写一个响应规则:
from nonebot import get_plugin_config from .config import Config plugin_config = get_plugin_config(Config) async def is_enable() -> bool: return plugin_config.weather_plugin_enabled weather = on_command("天气", rule=is_enable)在上面的代码中,我们定义了一个函数is_enable,它会检查配置项weather_plugin_enabled是否为True。这个函数is_enable即为一个RuleChecker。这里传入rule=的参数既可以是一个Rule对象,也可以直接是一个RuleChecker函数——从 nonebot/internal/rule.py 可以看到,Rule.__init__会对传入的每个 checker 做归一化处理:如果传入的是Dependent实例则直接使用,否则调用Dependent[bool].parse(call=checker, allow_types=...)将其解析为依赖对象,并存入self.checkers集合中。
得益于依赖注入,RuleChecker还可以声明事件参数,例如检查用户是否在某个黑名单中:
from nonebot.adapters import Event BLACKLIST: set[str] = set() async def is_blacklisted(event: Event) -> bool: return event.get_user_id() not in BLACKLISTRuleChecker支持的所有依赖参数类型在源码中有明确约束:Rule.HANDLER_PARAM_TYPES声明了DependParam、BotParam、EventParam、StateParam、DefaultParam五种(见 nonebot/internal/rule.py),与事件处理函数的依赖参数范围一致。
Rule:多个 RuleChecker 的并发集合
Rule是若干个RuleChecker的集合,它会并发调用每个RuleChecker,只有当所有RuleChecker检查通过时匹配成功。例如:我们可以组合两个RuleChecker,一个用于检查插件是否启用,一个用于检查用户是否在黑名单中:
from nonebot.rule import Rule from nonebot.adapters import Event async def is_enable() -> bool: return plugin_config.weather_plugin_enabled async def is_blacklisted(event: Event) -> bool: return event.get_user_id() not in BLACKLIST rule = Rule(is_enable, is_blacklisted) weather = on_command("天气", rule=rule)并发检查的底层实现
“并发调用每个RuleChecker”并非泛泛而谈,源码实现位于 nonebot/internal/rule.py 的Rule.__call__:
- 当
self.checkers为空时直接返回True(空规则恒匹配); - 使用
anyio.create_task_group()创建任务组,通过tg.start_soon(_run_checker, checker)为每个 checker 启动一个并发任务; - 每个
_run_checker将检查结果与最终结果做result &= is_passed累加,先计算再累加以避免数据竞争; - 若某个 checker 抛出
SkippedException(被catch捕获),则整体结果置为False,即“跳过”同样视为未通过。
因此,Rule的语义是严格的AND(与):任意一个 checker 返回False或抛出SkippedException,整个规则就不通过。测试用例 tests/test_rule.py 对此有直接验证:await Rule(truthy, falsy)(bot, event, {}) is False、await Rule(truthy, skipped)(bot, event, {}) is False。
空 Rule 的行为
从上述实现可以推断:Rule()不包含任何 checker,__call__直接返回True,即不附加任何约束。这在动态构建规则链时很实用,例如根据配置条件决定是否追加额外的 checker。
合并响应规则:&运算符与 None 忽略
在定义响应规则时,我们可以将规则进行细分,来更好地复用规则。而在使用时,我们需要合并多个规则。除了使用Rule对象来组合多个RuleChecker外,我们还可以对Rule对象进行合并。在原weather插件中,我们可以将rule=to_me()与rule=is_enable使用&运算符合并:
from nonebot.rule import to_me from nonebot import get_plugin_config from .config import Config plugin_config = get_plugin_config(Config) async def is_enable() -> bool: return plugin_config.weather_plugin_enabled weather = on_command( "天气", rule=to_me() & is_enable, aliases={"weather", "查天气"}, priority=plugin_config.weather_command_priority, block=True, )这样,weather命令就只会在插件启用且在私聊或者@bot时才会响应。注意to_me() & is_enable中&左侧是Rule对象、右侧是普通函数,这种混合合并是允许的。
支持的所有合并形式
合并响应规则可以有多种形式,例如:
rule1 = Rule(foo_checker) rule2 = Rule(bar_checker) rule = rule1 & rule2 rule = rule1 & bar_checker rule = foo_checker & rule2源码中的__and__与__rand__(见 nonebot/internal/rule.py)保证了这些写法全部合法:
Rule & Rule:把两个规则的 checker 集合合并成新Rule;Rule & RuleChecker:把单个 checker 追加进原规则;RuleChecker & Rule:通过__rand__将 checker 置于规则之前;- 合并操作不会修改原
Rule对象,而是返回新的Rule(因为checkers是新建的 set)。
合并 None 值的安全保证
同时,我们也无需担心合并了一个None值,Rule会忽略None值:
assert (rule & None) is rule__and__与__rand__的第一分支就是if other is None: return self——注意这里返回的是原对象本身,因此不仅是语义上忽略,连对象引用都保持不变,assert成立。测试用例 tests/test_rule.py 也覆盖了Rule(truthy) & None、None & Rule(truthy)、Rule(truthy) & falsy、truthy & Rule(falsy)四种组合。这一设计在“按配置项条件性追加规则”的场景中非常顺手:
rule = to_me() if plugin_config.enable_blacklist: rule = rule & is_not_blacklisted无需担心if分支外的规则变量为None导致崩溃。
注意:
Rule只支持&(AND)合并,源码中__or__被显式定义为直接抛出RuntimeError("Or operation between rules is not allowed."),即不存在|或运算,请勿混用。
主动使用响应规则:程序化判定事件
除了在事件响应器中使用响应规则外,我们也可以主动使用响应规则来判断事件是否符合条件。例如:
rule = Rule(some_checker) result: bool = await rule(bot, event, state)我们只需要传入Bot对象、事件和会话状态,Rule会并发调用所有RuleChecker进行检查,并返回结果。
从 nonebot/internal/rule.py 的签名看,Rule.__call__的完整参数为(bot, event, state, stack=None, dependency_cache=None)。其中stack(异步上下文栈)与dependency_cache(依赖缓存)为可选参数,在事件响应器内部调用时由框架自动注入;手动调用时通常只需提供前三个参数。这种方式非常适合在自定义分发逻辑、消息预处理管道或二次开发框架能力时复用现成的规则。
内置响应规则:开箱即用的规则库
NoneBot 内置了一些常用的响应规则,可以直接通过事件响应器辅助函数或者自行合并其他规则使用。内置响应规则列表可以参考事件响应器进阶。
全部内置规则定义在 nonebot/rule.py 中,每个规则都同时提供规则类(如CommandRule)与工厂函数(如command(...)),工厂函数返回Rule对象。下面分类展开:
命令类规则
command(*cmds, force_whitespace=None)—— 根据全局配置command_start(命令起始符,默认/)与command_sep(命令分隔符,默认.)判断消息是否为命令,对应实现CommandRule(nonebot/rule.py):
# 匹配 "/test" 开头的消息 rule = command("test") # 匹配 "/test.sub" 开头的消息 rule = command("test", "sub") # force_whitespace=True 时,命令后必须有空白才匹配(如 "/test xxx") rule = command("test", force_whitespace=True)- 命令内容与后续消息之间无需空格;
- 可通过
Command()、RawCommand()、CommandArg()参数在处理器中获取匹配到的命令元组、原始命令文本和参数部分; - 底层还会向
TrieRule前缀树注册命令前缀,用于消息的快速预匹配(见 nonebot/rule.py),注册重复前缀时会输出Duplicated prefix rule警告。
shell_command(*cmds, parser=None)—— shell 风格的命令匹配,支持用ArgumentParser解析参数,对应ShellCommandRule(nonebot/rule.py):
from nonebot.rule import ArgumentParser parser = ArgumentParser() parser.add_argument("-a", action="store_true") rule = shell_command("ls", parser=parser)- 解析前可通过
ShellCommandArgv()获取原始参数列表,解析后通过ShellCommandArgs()获取参数字典; - 若参数解析失败,
ShellCommandArgs()返回的将是ParserExit异常对象; parser必须是nonebot.rule.ArgumentParser实例,否则抛出TypeError。
消息文本类规则
startswith(msg, ignorecase=False)/endswith(msg, ignorecase=False)—— 匹配消息纯文本的开头 / 结尾(nonebot/rule.py):
rule = startswith("今天", ignorecase=True) rule = endswith(("吗", "呢"))匹配成功后,匹配到的字符串会被写入state(键分别为_prefix、_suffix对应的常量)。
fullmatch(msg, ignorecase=False)—— 消息纯文本与指定内容完全一致才匹配(nonebot/rule.py):
rule = fullmatch("签到")ignorecase=True时使用str.casefold()做大小写无关比较。
keyword(*keywords)—— 消息纯文本包含任意指定关键词即匹配(nonebot/rule.py):
rule = keyword("天气", "气象")按关键词在文本中出现的顺序取第一个命中写入state。
regex(regex, flags=0)—— 用正则表达式匹配消息字符串(注意是str(EventMessage)而非纯文本,nonebot/rule.py):
rule = regex(r"^天气(\d+)$", flags=re.I)- 使用
re.search而非re.match,如需从头匹配请用r"^xxx"; - 可通过
RegexStr()、RegexGroup()、RegexDict()获取匹配字符串、分组元组与分组字典。
事件类规则
to_me()—— 匹配与机器人有关的事件(私聊或@bot),对应ToMeRule(nonebot/rule.py)。它内部通过EventToMe()参数判断,是文档示例中最常用的规则之一。
is_type(*types)—— 检查事件是否为指定类型,对应IsTypeRule(nonebot/rule.py):
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent rule = is_type(GroupMessageEvent, PrivateMessageEvent)事件响应器辅助函数
多数内置规则都有对应的on_*快捷注册函数(定义于 nonebot/plugin/on.py),每个函数都接受可选的rule参数用于追加额外规则:
| 辅助函数 | 对应规则 | 说明 |
|---|---|---|
on_command(cmd, aliases=None, force_whitespace=None) | command | 命令触发(nonebot/plugin/on.py) |
on_shell_command(cmd, aliases=None, parser=None) | shell_command | shell 风格命令触发(nonebot/plugin/on.py) |
on_startswith(msg, ignorecase=False) | startswith | 文本开头触发(nonebot/plugin/on.py) |
on_endswith(msg, ignorecase=False) | endswith | 文本结尾触发(nonebot/plugin/on.py) |
on_fullmatch(msg, ignorecase=False) | fullmatch | 文本全匹配触发(nonebot/plugin/on.py) |
on_keyword(keywords) | keyword | 关键词触发(nonebot/plugin/on.py) |
on_regex(pattern, flags=0) | regex | 正则触发(nonebot/plugin/on.py) |
on_message()/on_notice()/on_request()/on_metaevent() | is_type等 | 按事件类型触发 |
例如:
from nonebot import on_command weather = on_command( "天气", aliases={"weather", "查天气"}, rule=to_me(), priority=10, block=True, )这里的rule=参数即可接收Rule对象或单个RuleChecker,且可以与&合并的结果配合使用。
实战示例:插件开关 + 黑名单 + 私聊限定
将本文所有知识点串起来,一个完整的 weather 插件响应规则定义如下:
from nonebot import get_plugin_config, on_command from nonebot.adapters import Event from nonebot.rule import Rule, to_me from .config import Config plugin_config = get_plugin_config(Config) BLACKLIST = {"10001", "10002"} async def is_enable() -> bool: # 插件开关,来自插件配置项 return plugin_config.weather_plugin_enabled async def is_not_blacklisted(event: Event) -> bool: # 黑名单检查,来自事件依赖注入 return event.get_user_id() not in BLACKLIST # 组合三个 RuleChecker:插件启用、不在黑名单、私聊或 @bot weather = on_command( "天气", rule=Rule(is_enable, is_not_blacklisted) & to_me(), aliases={"weather", "查天气"}, priority=plugin_config.weather_command_priority, block=True, )规则检查会在事件分发阶段并发执行:任一条件不满足(插件未启用、用户在黑名单、既非私聊也未被 @)都会导致该事件不进入weather的处理流程。这正体现了响应规则的设计意图——把“是否响应”的决定权从业务代码中彻底剥离,以声明式、可组合、可复用的方式表达。
小结
RuleChecker是支持依赖注入的bool判定函数,可声明Bot、Event、State等依赖参数;Rule是RuleChecker的集合,通过anyio任务组并发执行并做 AND 累加,任一失败(含SkippedException)即整体不通过;- 使用
&运算符合并规则,支持Rule & Rule、Rule & checker、checker & Rule三种形式,且自动忽略None值(返回原对象); - 可通过
await rule(bot, event, state)主动调用规则判定任意事件; - 内置
command、shell_command、startswith、endswith、fullmatch、keyword、regex、to_me、is_type九类规则,并有对应on_*快捷注册函数。
响应规则与事件响应器进阶、权限系统共同构成了 NoneBot2 事件分发的完整控制体系,是编写高质量插件时最值得熟练掌握的框架能力之一。
- 后端
- 即时通讯
【免费下载链接】nonebot2
跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python
相关推荐
NoneBot 响应规则(Rule)完全指南:从 RuleChecker 组合到内置规则实战
NoneBot 响应规则(Rule)完全指南:从 RuleChecker 组合到内置规则实战 NoneBot 作为跨平台异步聊天机器人框架,通过"响应规则"来决
后端即时通讯NoneBot2 响应规则(Rule)全解析:从 RuleChecker 依赖注入到内置规则与主动调用
NoneBot2 响应规则(Rule)全解析:从 RuleChecker 依赖注入到内置规则与主动调用 事件响应器(Matcher)是 NoneBot2 处理消
后端即时通讯NoneBot2 事件响应器进阶指南:响应器组成、内置规则与响应器组实战
NoneBot2 事件响应器进阶指南:响应器组成、内置规则与响应器组实战 本篇技术指南以 NoneBot2 的事件响应器(Matcher)进阶用法为核心,系统讲
后端即时通讯
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考