id, slug, options
| id | slug | options | ||||||
|---|---|---|---|---|---|---|---|---|
| index | /advanced |
|
深入
:::danger 警告 进阶部分尚未更新完成 :::
它如何工作?
如同概览所言:
NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的事件进行解析和处理,并以插件化的形式,按优先级分发给事件所对应的事件响应器,来完成具体的功能。
Nonebot2 是一个可以对机器人上报的事件进行处理并完成具体功能的机器人框架,在这里,我们将简要讲述它的工作内容。
便捷起见,以下内容对 Nonebot2 会被称为 nonebot,与 Nonebot2 交互的机器人实现会被称为 协议端。
在实际应用中,nonebot 会充当一个高性能,轻量级的 Python 微服务框架。协议端可以通过 http, websocket 等方式与之通信,这个通信往往是双向的:一方面,协议端可以上报数据给 nonebot,nonebot 会处理数据并返回响应给协议端;另一方面,nonebot 可以主动推送数据给协议端。而 nonebot 便是围绕双向通信进行工作的。
在开始工作之前,nonebot 需要进行准备工作:
- 运行
nonebot.init初始化函数,它会读取配置文件,并初始化nonebot和后端驱动driver对象。 - 注册协议适配器
adapter。 - 加载插件。
准备工作完成后,nonebot 会利用 uvicorn 启动,并运行 on_startup 钩子函数。
随后,倘若一个协议端与 nonebot 进行了连接,nonebot 的后端驱动 driver 就会将 adapter 实例化为 bot,nonebot 便会利用 bot 开始工作,它的工作内容分为两个方面:
-
事件处理,
bot会将协议端上报的数据转化为事件(Event),之后nonebot会根据一套既定流程来处理事件。 -
调用
API, 在事件处理的过程中,nonebot可以通过bot调用协议端指定的API来获取更多数据,或者反馈响应给协议端;nonebot也可以通过调用API向协议端主动请求数据或者主动推送数据。
在指南模块, 我们已经叙述了如何配置 nonebot, 如何注册协议适配器,如何加载插件, 在这里便不再赘述。
下面,我们将对事件处理, 调用 API进行说明。
事件处理
我们可以先看事件处理的流程图:
在流程图里,我们可以看到,nonebot 会有三个阶段来处理事件:
- driver 处理上报数据
- adapter 处理原始数据
- nonebot 处理 Event
我们将顺序说明这三个阶段。其中,会将第三个阶段拆分成概念解释,处理 Event,特殊异常处理三个部分来说明。
driver 处理上报数据
-
协议端会通过
websocket或者http等方式与nonebot的后端驱动driver连接,driver会根据之前注册的adapter和配置文件的内容来进行鉴权,从而获得这个连接的唯一识别 idself-id,随后adapter就会利用self-id实例化为bot对象。::: tip 需要注意的是,如果协议端通过
websocket与nonebot连接,这个步骤只会在建立连接时进行,并在之后运行on_bot_connect钩子函数;通过http方式连接时,会在协议端每次上报数据时都进行这个步骤。 :::::: warning 连接之前必须要注册
adapter:::::: warning
self-id是帐号的唯一识别 ID,这意味着不能出现相同的self-id。 ::: -
driver会将接收到的数据转交给bot对象进一步处理。
adapter 处理原始数据
-
bot会利用事先定义好的Event Model对上报的数据进行分析处理,将数据转化为nonebot可以处理的Event对象。::: tip
adapter在转换数据格式的同时可以进行一系列的特殊操作,例如CQHTTP会对reply信息进行提取。 ::: -
Event会传入nonebot做进一步处理。
nonebot 处理 Event
在讲述这个阶段之前,我们需要先对几个概念进行解释。
概念解释
-
hook,或者说钩子函数,它们可以在
nonebot处理Event的不同时刻进行拦截,修改或者扩展,在nonebot中,钩子函数分为事件预处理hook,运行预处理hook,运行后处理hook和事件后处理hook。::: tip 关于
hook的更多信息,可以查阅这里 ::: -
Matcher与matcher,在指南中,我们讲述了如何注册事件响应器,这里的事件响应器或者说
Matcher并不是一个具体的实例instance,而是一个具有特定属性的类class。只有当Matcher响应事件时,才会实例化为具体的instance,也就是matcher。matcher可以认为是nonebot处理Event的基本单位,运行matcher是nonebot工作的主要内容。 -
handler,或者说事件处理函数, 它们可以认为是
nonebot处理Event的最小单位。在不考虑hook的情况下,运行 matcher 就是顺序运行 matcher.handlers,这句话换种表达方式就是,handler只有添加到matcher.handlers时,才可以参与到nonebot的工作中来。::: tip 如何让
handler添加到matcher.handlers?一方面,我们可以参照这里利用装饰器来添加;另一方面,我们在用
on()或者on_*()注册事件响应器时,可以添加handlers=[handler1, handler2, ...]这样的关键词参数来添加。:::
处理 Event
-
执行事件预处理 hook,
nonebot接收到Event后,会传入到事件预处理hook中进行处理。::: warning 需要注意的是,执行多个
事件预处理hook时并无顺序可言,它们是并行运行的。这个原则同样适用于其他的hook。 ::: -
按优先级升序选出同一优先级的 Matcher,
nonebot提供了一个全局字典matchers,这个字典的key是优先级priority,value是一个list,里面存放着同一优先级的Matcher。在注册Matcher时,它和优先级priority会添加到里面。在执行
事件预处理hook后,nonebot会对matchers的key升序排序并选择出当前最小优先级的Matcher。 -
根据 Matcher 定义的 Rule, Permission 判断是否运行,在选出
Matcher后,nonebot会将bot,Event传入到Matcher.check_rule和Matcher.check_perm两个函数中,两个函数分别对 Matcher 定义的 Rule, Permission 进行 check,当 check 通过后,这个Matcher就会响应事件。但是当同一个优先级的所有Matcher均没有响应时,nonebot会返回到上一个步骤,选择出下一优先级的Matcher。 -
实例化 matcher 并执行运行预处理 hook,当
Matcher响应事件后,它便会实例化为matcher,并执行运行预处理hook。 -
顺序运行 matcher 的所有 handlers,
运行预处理hook执行完毕后,便会运行matcher,也就是顺序运行它的handlers。::: tip
matcher运行handlers的顺序是: 先运行该matcher的类Matcher注册时添加的handlers(如果有的话),再按照装饰器装饰顺序运行装饰的handlers。 ::: -
执行运行后处理 hook,
matcher的handlers运行完毕后,会执行运行后处理hook。 -
判断是否停止事件传播,
nonebot会根据当前优先级所有matcher的block参数或者StopPropagation异常判断是否停止传播Event,如果事件没有停止传播,nonebot便会返回到第 2 步, 选择出下一优先级的Matcher。 -
执行事件后处理 hook,在
Event停止传播或执行完所有响应的Matcher后,nonebot会执行事件后处理hook。当
事件后处理hook执行完毕后,当前Event的处理周期就顺利结束了。
特殊异常处理
在这个阶段,nonebot 规定了几个特殊的异常,当 nonebot 捕获到它们时,会用特定的行为来处理它们。
-
IgnoredException
这个异常可以在
事件预处理hook和运行预处理hook抛出。当
事件预处理hook抛出它时,nonebot会忽略当前的Event,不进行处理。当
运行预处理hook抛出它时,nonebot会忽略当前的matcher,结束当前matcher的运行。::: warning 当
hook需要抛出这个异常时,要写明原因。 ::: -
PausedException
这个异常可以在
handler中由Matcher.pause抛出。当
nonebot捕获到它时,会停止运行当前handler并结束当前matcher的运行,并将后续的handler交给一个临时Matcher来响应当前交互用户的下一个消息事件,当临时Matcher响应时,临时Matcher会运行后续的 handlers。 -
RejectedException
这个异常可以在
handler中由Matcher.reject抛出。当
nonebot捕获到它时,会停止运行当前handler并结束当前matcher的运行,并将当前 handler 和后续handler交给一个临时Matcher来响应当前交互用户的下一个消息事件,当临时Matcher响应时,临时Matcher会运行当前handler和后续的handler。 -
FinishedException
这个异常可以在
handler中由Matcher.finish抛出。当
nonebot捕获到它时,会停止运行当前handler并结束当前matcher的运行。 -
StopPropagation
这个异常一般会在执行
运行后处理hook后抛出。当
nonebot捕获到它时, 会停止传播当前Event,不再寻找下一优先级的Matcher,直接执行事件后处理hook。
调用 API
nonebot 可以通过 bot 来调用 API ,API 可以向协议端发送数据,也可以向协议端请求更多的数据。
::: tip
不同 adapter 规定了不同的 API,对应的 API 列表请参照协议规范。
:::
一般来说,我们可以用 bot.* 来调用 API(*是 API 的 action 或者 endpoint)。
对于发送消息而言,一方面可以调用既有的 API;另一方面 nonebot 实现了两个便捷方法,bot.send(event, message, **kwargs) 方法和可以在 handler 中使用的 Matcher.send(message, **kwargs) 方法,来向事件主体发送消息。
