* 📝 update dependency-injection docs * 🚨 auto fix by pre-commit hooks * 📝 fix some indent * 📝 fix description * 📝 add create callable in DI docs * 🚨 auto fix by pre-commit hooks * 📝 delete unused params in docs * 📝 update di docs * 🚨 auto fix by pre-commit hooks * 📝 update di doc Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: yanyongyu <42488585+yanyongyu@users.noreply.github.com>
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处理。
:::warning
连接之前必须要注册 Adapter
:::
Adapter 处理原始数据
Adapter检查授权许可,并获取self-id作为唯一识别 id 。
:::tip
如果协议端通过 websocket 上报数据,这个步骤只会在建立连接时进行,并在之后运行 on_bot_connect 钩子函数;通过 http 方式连接时,会在协议端每次上报数据时都进行这个步骤。
:::
:::warning
self-id 是帐号的唯一识别 ID ,这意味着不能出现相同的 self-id。
:::
-
根据
self-id实例化Adapter相应的Bot。 -
根据
Event Model将原始数据转化为 NoneBot 可以处理的Event对象。
:::tip
Adapter 在转换数据格式的同时可以进行一系列的特殊操作,例如 OneBot 适配器会对 reply 信息进行提取。
:::
Bot和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会运行后续的handler。 -
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 可以向协议端发送数据,也可以向协议端请求更多的数据。
NoneBot 调用 API 会有如下过程:
-
调用
calling_api_hook预处理钩子。 -
adapter将信息处理为原始数据,并转交driver,driver交给协议端处理。 -
driver接收协议端的结果,交给adapter处理之后将结果反馈给 NoneBot 。 -
调用
called_api_hook后处理钩子。
在调用 API 时同样规定了特殊的异常,叫做 MockApiException 。该异常会由预处理钩子和后处理钩子触发,当预处理钩子触发时,NoneBot 会跳过之后的调用过程,直接执行后处理钩子。
:::tip
不同 adapter 规定了不同的 API,对应的 API 列表请参照协议规范。
:::
一般来说,我们可以用 bot.* 来调用 API(*是 API 的 action 或者 endpoint)。
对于发送消息而言,一方面可以调用既有的 API ;另一方面 NoneBot 实现了两个便捷方法,bot.send(event, message, **kwargs) 方法和可以在 handler 中使用的 Matcher.send(message, **kwargs) 方法,来向事件主体发送消息。
