@ -1,60 +0,0 @@
|
|||||||
# 钩子函数
|
|
||||||
|
|
||||||
[`钩子编程`](https://zh.wikipedia.org/wiki/%E9%92%A9%E5%AD%90%E7%BC%96%E7%A8%8B)
|
|
||||||
|
|
||||||
> 钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。
|
|
||||||
|
|
||||||
在 `nonebot2` 中有一系列预定义的钩子函数,这些函数位于 [`nonebot.message`](https://v2.nonebot.dev/api/message.html) 模块下,我们可以以装饰器的形式利用这些函数,进行以下四种操作:
|
|
||||||
|
|
||||||
:::warning 注意
|
|
||||||
1.在钩子函数中,与 `matcher` 运行状态相关的函数将不可用,如 `matcher.finish()`
|
|
||||||
|
|
||||||
2.如果需要在钩子函数中打断整个对话的执行,请参考以下范例:
|
|
||||||
```python
|
|
||||||
from nonebot.exception import IgnoredException
|
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
|
||||||
raise IgnoredException("reason")
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 事件预处理
|
|
||||||
|
|
||||||
```python
|
|
||||||
from nonebot.message import event_preprocessor
|
|
||||||
|
|
||||||
@event_preprocessor
|
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
## 事件后处理
|
|
||||||
|
|
||||||
```python
|
|
||||||
from nonebot.message import event_postprocessor
|
|
||||||
|
|
||||||
@event_postprocessor
|
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
## 运行预处理
|
|
||||||
|
|
||||||
```python
|
|
||||||
from nonebot.message import run_preprocessor
|
|
||||||
|
|
||||||
@run_preprocessor
|
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
## 运行后处理
|
|
||||||
```python
|
|
||||||
from nonebot.message import run_postprocessor
|
|
||||||
|
|
||||||
@run_postprocessor
|
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
|
||||||
pass
|
|
||||||
```
|
|
@ -10,9 +10,17 @@
|
|||||||
|
|
||||||
**便捷起见,以下内容对 `Nonebot2` 会被称为 `nonebot`,与 `Nonebot2` 交互的机器人实现会被称为 `协议端`**。
|
**便捷起见,以下内容对 `Nonebot2` 会被称为 `nonebot`,与 `Nonebot2` 交互的机器人实现会被称为 `协议端`**。
|
||||||
|
|
||||||
在实际应用中,`nonebot` 会充当一个高性能,轻量级的 Python 微服务框架。协议端可以通过 `http`, `websocket` 等方式与之通信,这个通信往往是双向的:一方面,协议端可以上报数据给 `nonebot`,`nonebot` 会处理数据并返回响应给协议端;另一方面,`nonebot` 可以主动推送数据给协议端。而 `nonebot` 便是围绕上述的双向通信进行工作的。
|
在实际应用中,`nonebot` 会充当一个高性能,轻量级的 Python 微服务框架。协议端可以通过 `http`, `websocket` 等方式与之通信,这个通信往往是双向的:一方面,协议端可以上报数据给 `nonebot`,`nonebot` 会处理数据并返回响应给协议端;另一方面,`nonebot` 可以主动推送数据给协议端。而 `nonebot` 便是围绕双向通信进行工作的。
|
||||||
|
|
||||||
在开始工作之前,`nonebot` 会依照**配置文件或初始化配置**启动,并会注册**协议适配器** `adapter`,之后便会加载**插件**, 随后,倘若一个协议端与 `nonebot` 进行了连接,`nonebot` 的后端驱动 `driver` 就会将 `adapter` 实例化为 `bot`,`nonebot` 便会利用 `bot` 开始工作,它的工作内容分为两个方面:
|
在开始工作之前,`nonebot` 需要进行准备工作:
|
||||||
|
|
||||||
|
1. **运行 `nonebot.init` 初始化函数**,它会读取配置文件,并初始化 `nonebot` 和后端驱动 `driver` 对象。
|
||||||
|
2. **注册协议适配器 `adapter`** 。
|
||||||
|
3. **加载插件**。
|
||||||
|
|
||||||
|
准备工作完成后,`nonebot` 会利用 `uvicorn` 启动,并运行 `on_startup` 钩子函数。
|
||||||
|
|
||||||
|
随后,倘若一个协议端与 `nonebot` 进行了连接,`nonebot` 的后端驱动 `driver` 就会将 `adapter` 实例化为 `bot`,`nonebot` 便会利用 `bot` 开始工作,它的工作内容分为两个方面:
|
||||||
|
|
||||||
1. **事件处理**,`bot` 会将协议端上报的数据转化为 `事件`(`Event`),之后 `nonebot` 会根据一套既定流程来处理 `事件`。
|
1. **事件处理**,`bot` 会将协议端上报的数据转化为 `事件`(`Event`),之后 `nonebot` 会根据一套既定流程来处理 `事件`。
|
||||||
|
|
||||||
@ -41,7 +49,7 @@
|
|||||||
1. 协议端会通过 `websocket` 或者 `http` 等方式与 `nonebot` 的后端驱动 `driver` 连接,`driver` 会根据之前注册的 `adapter` 和配置文件的内容来进行鉴权,从而获得这个连接的唯一识别 id `self-id`,随后 `adapter` 就会利用 `self-id` 实例化为 `bot` 对象。
|
1. 协议端会通过 `websocket` 或者 `http` 等方式与 `nonebot` 的后端驱动 `driver` 连接,`driver` 会根据之前注册的 `adapter` 和配置文件的内容来进行鉴权,从而获得这个连接的唯一识别 id `self-id`,随后 `adapter` 就会利用 `self-id` 实例化为 `bot` 对象。
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
需要注意的是,如果协议端通过 `websocket` 与 `nonebot` 连接,这个步骤只会在建立连接时进行;通过 `http` 方式连接时,会在协议端每次上报数据时都进行这个步骤。
|
需要注意的是,如果协议端通过 `websocket` 与 `nonebot` 连接,这个步骤只会在建立连接时进行,并在之后运行 `on_bot_connect` 钩子函数;通过 `http` 方式连接时,会在协议端每次上报数据时都进行这个步骤。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: warning
|
::: warning
|
||||||
@ -142,7 +150,7 @@
|
|||||||
|
|
||||||
这个异常可以在 `handler` 中由 `Matcher.reject` 抛出。
|
这个异常可以在 `handler` 中由 `Matcher.reject` 抛出。
|
||||||
|
|
||||||
当 `nonebot` 捕获到它时,会停止运行当前 `handler` 并结束当前 `matcher` 的运行,并将**当前 handler 和后续 `handler`**交给一个临时 `Matcher` 来响应当前交互用户的下一个消息事件,当临时 `Matcher` 响应时,临时 `Matcher` 会运行当前 `handler` 和后续的 `handler`。
|
当 `nonebot` 捕获到它时,会停止运行当前 `handler` 并结束当前 `matcher` 的运行,并将当前 handler 和后续 `handler` 交给一个临时 `Matcher` 来响应当前交互用户的下一个消息事件,当临时 `Matcher` 响应时,临时 `Matcher` 会运行当前 `handler` 和后续的 `handler`。
|
||||||
|
|
||||||
4. **FinishedException**
|
4. **FinishedException**
|
||||||
|
|
||||||
@ -158,7 +166,7 @@
|
|||||||
|
|
||||||
## 调用 API
|
## 调用 API
|
||||||
|
|
||||||
`nonebot` 可以通过 `bot` 来调用 API,API 可以向协议端发送数据,也可以向协议端请求更多的数据。
|
`nonebot` 可以通过 `bot` 来调用 `API` ,`API` 可以向协议端发送数据,也可以向协议端请求更多的数据。
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
不同 `adapter` 规定了不同的 API,对应的 API 列表请参照协议规范。
|
不同 `adapter` 规定了不同的 API,对应的 API 列表请参照协议规范。
|
151
archive/2.0.0a12/advanced/runtime-hook.md
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# 钩子函数
|
||||||
|
|
||||||
|
[`钩子编程`](https://zh.wikipedia.org/wiki/%E9%92%A9%E5%AD%90%E7%BC%96%E7%A8%8B)
|
||||||
|
|
||||||
|
> 钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。
|
||||||
|
|
||||||
|
在 `nonebot2` 中有一系列预定义的钩子函数,分为两类:`全局钩子函数` 和 `事件钩子函数` ,这些钩子函数可以用装饰器的形式来使用。
|
||||||
|
|
||||||
|
## 全局钩子函数
|
||||||
|
|
||||||
|
全局钩子函数是指 `nonebot2` 针对其本身运行过程的钩子函数。
|
||||||
|
|
||||||
|
这些钩子函数是由其后端驱动 `driver`来运行的,故需要先获得全局 `driver` 对象:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot import get_driver
|
||||||
|
|
||||||
|
|
||||||
|
driver=get_driver()
|
||||||
|
```
|
||||||
|
|
||||||
|
共分为五种函数:
|
||||||
|
|
||||||
|
### 启动准备
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2` 启动时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_startup
|
||||||
|
async def do_something():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 终止处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2` 终止时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_shutdown
|
||||||
|
async def do_something():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### bot 连接处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `bot` 通过 `websocket` 连接到 `nonebot2` 时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_bot_connect
|
||||||
|
async def do_something(bot: Bot):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### bot 断开处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `bot` 断开与 `nonebot2` 的 `websocket` 连接时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_bot_disconnect
|
||||||
|
async def do_something(bot: Bot):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### bot api 调用钩子
|
||||||
|
|
||||||
|
这个钩子函数会在 `Bot` 调用 API 时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.adapters import Bot
|
||||||
|
|
||||||
|
@Bot.on_calling_api
|
||||||
|
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## 事件处理钩子
|
||||||
|
|
||||||
|
这些钩子函数指的是影响 `nonebot2` 进行 `事件处理` 的函数。
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
关于 `事件处理` 的流程,可以在[这里](./README)查阅。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::warning 注意
|
||||||
|
|
||||||
|
1.在事件处理钩子函数中,与 `matcher` 运行状态相关的函数将不可用,如 `matcher.finish()`
|
||||||
|
|
||||||
|
2.如果需要在事件处理钩子函数中打断整个对话的执行,请参考以下范例:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.exception import IgnoredException
|
||||||
|
|
||||||
|
|
||||||
|
@event_preprocessor
|
||||||
|
async def do_something(bot: Bot, event: Event, state: T_State):
|
||||||
|
raise IgnoredException("reason")
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
共分为四种函数:
|
||||||
|
|
||||||
|
### 事件预处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `Event` 上报到 `nonebot2` 时运行
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.message import event_preprocessor
|
||||||
|
|
||||||
|
@event_preprocessor
|
||||||
|
async def do_something(bot: Bot, event: Event, state: T_State):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件后处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2` 处理 `Event` 后运行
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
|
@event_postprocessor
|
||||||
|
async def do_something(bot: Bot, event: Event, state: T_State):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 运行预处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2`运行 `matcher` 前运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.message import run_preprocessor
|
||||||
|
|
||||||
|
@run_preprocessor
|
||||||
|
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 运行后处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2`运行 `matcher` 后运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.message import run_postprocessor
|
||||||
|
|
||||||
|
@run_postprocessor
|
||||||
|
async def do_something(matcher: Matcher, exception: Optional[Exception], bot: Bot, event: Event, state: T_State):
|
||||||
|
pass
|
||||||
|
```
|
@ -19,6 +19,9 @@
|
|||||||
* [nonebot.matcher](matcher.html)
|
* [nonebot.matcher](matcher.html)
|
||||||
|
|
||||||
|
|
||||||
|
* [nonebot.handler](handler.html)
|
||||||
|
|
||||||
|
|
||||||
* [nonebot.rule](rule.html)
|
* [nonebot.rule](rule.html)
|
||||||
|
|
||||||
|
|
@ -27,6 +27,21 @@ Driver 对象
|
|||||||
Config 配置对象
|
Config 配置对象
|
||||||
|
|
||||||
|
|
||||||
|
### `_call_api_hook`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Set[T_CallingAPIHook]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
call_api 时执行的函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract_ `__init__(connection_type, self_id, *, websocket=None)`
|
### _abstract_ `__init__(connection_type, self_id, *, websocket=None)`
|
||||||
|
|
||||||
|
|
||||||
@ -93,7 +108,7 @@ Adapter 类型
|
|||||||
* `headers: dict`: 请求头
|
* `headers: dict`: 请求头
|
||||||
|
|
||||||
|
|
||||||
* `body: Optional[dict]`: 请求数据,WebSocket 连接该部分为空
|
* `body: Optional[bytes]`: 请求数据,WebSocket 连接该部分为 None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -127,7 +142,26 @@ Adapter 类型
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract async_ `call_api(api, **data)`
|
### _abstract async_ `_call_api(api, **data)`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
`adapter` 实际调用 api 的逻辑实现函数,实现该方法以调用 api。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
|
* `**data`: API 数据
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _async_ `call_api(api, **data)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -142,6 +176,9 @@ Adapter 类型
|
|||||||
* `api: str`: API 名称
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
|
* `self_id: Optional[str]`: 指定调用 API 的机器人
|
||||||
|
|
||||||
|
|
||||||
* `**data`: API 数据
|
* `**data`: API 数据
|
||||||
|
|
||||||
|
|
@ -129,6 +129,9 @@ sidebarDepth: 0
|
|||||||
* `api: str`: API 名称
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
|
* `event: Optional[MessageEvent]`: Event 对象
|
||||||
|
|
||||||
|
|
||||||
* `**data: Any`: API 参数
|
* `**data: Any`: API 参数
|
||||||
|
|
||||||
|
|
||||||
@ -150,7 +153,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _async_ `send(event, message, at_sender=False, **kwargs)`
|
### _async_ `send(event, message, at_sender=False, webhook=None, secret=None, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -171,6 +174,12 @@ sidebarDepth: 0
|
|||||||
* `at_sender: bool`: 是否 @ 事件主体
|
* `at_sender: bool`: 是否 @ 事件主体
|
||||||
|
|
||||||
|
|
||||||
|
* `webhook: Optional[str]`: 该条消息将调用的 webhook 地址。不传则将使用 sessionWebhook,若其也不存在,该条消息不发送,使用自定义 webhook 时注意你设置的安全方式,如加关键词,IP地址,加签等等。
|
||||||
|
|
||||||
|
|
||||||
|
* `secret: Optional[str]`: 如果你使用自定义的 webhook 地址,推荐使用加签方式对消息进行验证,将 机器人安全设置页面,加签一栏下面显示的SEC开头的字符串 传入这个参数即可。
|
||||||
|
|
||||||
|
|
||||||
* `**kwargs`: 覆盖默认参数
|
* `**kwargs`: 覆盖默认参数
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
|||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
openapi.json 地址,默认为 None 即关闭
|
`openapi.json` 地址,默认为 `None` 即关闭
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
|||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
swagger 地址,默认为 None 即关闭
|
`swagger` 地址,默认为 `None` 即关闭
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -58,7 +58,22 @@ FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
|||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
|
|
||||||
redoc 地址,默认为 None 即关闭
|
`redoc` 地址,默认为 `None` 即关闭
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `fastapi_reload_dirs`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`List[str]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
`debug` 模式下重载监控文件夹列表,默认为 uvicorn 默认值
|
||||||
|
|
||||||
|
|
||||||
|
|
111
archive/2.0.0a12/api/handler.md
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
contentSidebar: true
|
||||||
|
sidebarDepth: 0
|
||||||
|
---
|
||||||
|
|
||||||
|
# NoneBot.handler 模块
|
||||||
|
|
||||||
|
## 事件处理函数
|
||||||
|
|
||||||
|
该模块实现事件处理函数的封装,以实现动态参数等功能。
|
||||||
|
|
||||||
|
|
||||||
|
## _class_ `Handler`
|
||||||
|
|
||||||
|
基类:`object`
|
||||||
|
|
||||||
|
事件处理函数类
|
||||||
|
|
||||||
|
|
||||||
|
### `__init__(func)`
|
||||||
|
|
||||||
|
装饰事件处理函数以便根据动态参数运行
|
||||||
|
|
||||||
|
|
||||||
|
### `func`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`T_Handler`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `signature`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`inspect.Signature`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数签名
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `bot_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Union[Type["Bot"], inspect.Parameter.empty]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数接受的 Bot 对象类型
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `event_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[Union[Type[Event], inspect.Parameter.empty]]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数接受的 event 类型 / 不需要 event 参数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `state_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[Union[T_State, inspect.Parameter.empty]]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数是否接受 state 参数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `matcher_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[Union[Type["Matcher"], inspect.Parameter.empty]]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数是否接受 matcher 参数
|
@ -202,7 +202,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Optional[T_ArgsParser]`
|
`Optional[T_TypeUpdater]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Optional[T_ArgsParser]`
|
`Optional[T_PermissionUpdater]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`List[T_Handler]`
|
`List[Handler]`
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,38 +25,6 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## _class_ `Export`
|
|
||||||
|
|
||||||
基类:`dict`
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
|
||||||
|
|
||||||
插件导出内容以使得其他插件可以获得。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **示例**
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
nonebot.export().default = "bar"
|
|
||||||
|
|
||||||
@nonebot.export()
|
|
||||||
def some_function():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# this doesn't work before python 3.9
|
|
||||||
# use
|
|
||||||
# export = nonebot.export(); @export.sub
|
|
||||||
# instead
|
|
||||||
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
|
||||||
@nonebot.export().sub
|
|
||||||
def something_else():
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## _class_ `Plugin`
|
## _class_ `Plugin`
|
||||||
|
|
||||||
基类:`object`
|
基类:`object`
|
||||||
@ -82,15 +50,6 @@ def something_else():
|
|||||||
* **说明**: 插件模块对象
|
* **说明**: 插件模块对象
|
||||||
|
|
||||||
|
|
||||||
### `matcher`
|
|
||||||
|
|
||||||
|
|
||||||
* **类型**: `Set[Type[Matcher]]`
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**: 插件内定义的 `Matcher`
|
|
||||||
|
|
||||||
|
|
||||||
### `export`
|
### `export`
|
||||||
|
|
||||||
|
|
||||||
@ -100,6 +59,15 @@ def something_else():
|
|||||||
* **说明**: 插件内定义的导出内容
|
* **说明**: 插件内定义的导出内容
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `matcher`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**: `Set[Type[Matcher]]`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**: 插件内定义的 `Matcher`
|
||||||
|
|
||||||
|
|
||||||
## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None, state_factory=None)`
|
## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None, state_factory=None)`
|
||||||
|
|
||||||
|
|
||||||
@ -121,7 +89,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -162,7 +130,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -206,7 +174,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -247,7 +215,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -288,7 +256,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -335,7 +303,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -382,7 +350,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -429,7 +397,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -481,7 +449,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -538,7 +506,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -590,7 +558,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -767,7 +735,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -808,7 +776,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -852,7 +820,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -893,7 +861,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -934,7 +902,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -981,7 +949,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1028,7 +996,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1075,7 +1043,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1127,7 +1095,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1184,7 +1152,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1236,7 +1204,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1442,22 +1410,6 @@ def something_else():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `export()`
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
|
||||||
|
|
||||||
获取插件的导出内容对象
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **返回**
|
|
||||||
|
|
||||||
|
|
||||||
* `Export`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `require(name)`
|
## `require(name)`
|
||||||
|
|
||||||
|
|
||||||
@ -1478,3 +1430,51 @@ def something_else():
|
|||||||
|
|
||||||
|
|
||||||
* `Optional[Export]`
|
* `Optional[Export]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## _class_ `Export`
|
||||||
|
|
||||||
|
基类:`dict`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
插件导出内容以使得其他插件可以获得。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **示例**
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
nonebot.export().default = "bar"
|
||||||
|
|
||||||
|
@nonebot.export()
|
||||||
|
def some_function():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# this doesn't work before python 3.9
|
||||||
|
# use
|
||||||
|
# export = nonebot.export(); @export.sub
|
||||||
|
# instead
|
||||||
|
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
||||||
|
@nonebot.export().sub
|
||||||
|
def something_else():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `export()`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
获取插件的导出内容对象
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **返回**
|
||||||
|
|
||||||
|
|
||||||
|
* `Export`
|
@ -1,5 +1,11 @@
|
|||||||
# CQHTTP 协议使用指南
|
# CQHTTP 协议使用指南
|
||||||
|
|
||||||
|
## 安装 NoneBot CQHTTP 适配器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install nonebot-adapter-cqhttp
|
||||||
|
```
|
||||||
|
|
||||||
## 配置 CQHTTP 协议端(以 QQ 为例)
|
## 配置 CQHTTP 协议端(以 QQ 为例)
|
||||||
|
|
||||||
单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。
|
单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。
|
@ -11,6 +11,16 @@
|
|||||||
- [群机器人概述](https://developers.dingtalk.com/document/app/overview-of-group-robots)
|
- [群机器人概述](https://developers.dingtalk.com/document/app/overview-of-group-robots)
|
||||||
- [开发企业内部机器人](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots)
|
- [开发企业内部机器人](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots)
|
||||||
|
|
||||||
|
钉钉官方机器人教程(Java):
|
||||||
|
|
||||||
|
- [开发一个钉钉机器人](https://developers.dingtalk.com/document/tutorial/create-a-robot)
|
||||||
|
|
||||||
|
## 安装 NoneBot 钉钉 适配器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install nonebot-adapter-ding
|
||||||
|
```
|
||||||
|
|
||||||
## 关于 DingAdapter 的说明
|
## 关于 DingAdapter 的说明
|
||||||
|
|
||||||
你需要显式的注册 ding 这个适配器:
|
你需要显式的注册 ding 这个适配器:
|
||||||
@ -87,6 +97,58 @@ async def raw_handler(bot: DingBot, event: MessageEvent):
|
|||||||
|
|
||||||
其他消息格式请查看 [钉钉适配器的 MessageSegment](https://github.com/nonebot/nonebot2/blob/dev/nonebot/adapters/ding/message.py#L8),里面封装了很多有关消息的方法,比如 `code`、`image`、`feedCard` 等。
|
其他消息格式请查看 [钉钉适配器的 MessageSegment](https://github.com/nonebot/nonebot2/blob/dev/nonebot/adapters/ding/message.py#L8),里面封装了很多有关消息的方法,比如 `code`、`image`、`feedCard` 等。
|
||||||
|
|
||||||
|
## 发送到特定群聊
|
||||||
|
|
||||||
|
钉钉也支持通过 Webhook 的方式直接将消息推送到某个群聊([参考链接](https://developers.dingtalk.com/document/app/custom-robot-access/title-zob-eyu-qse)),你可以在机器人的设置中看到当前群的 Webhook 地址。
|
||||||
|
|
||||||
|
![机器人所在群的 Webhook 地址](./images/ding/webhook.png)
|
||||||
|
|
||||||
|
获取到Webhook地址后,用户可以向这个地址发起HTTP POST 请求,即可实现给该钉钉群发送消息。
|
||||||
|
|
||||||
|
对于这种通过 Webhook 推送的消息,钉钉需要开发者进行安全方面的设置(目前有3种安全设置方式,请根据需要选择一种),如下:
|
||||||
|
|
||||||
|
1. **自定义关键词:** 最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功。
|
||||||
|
例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。
|
||||||
|
2. **加签:** 发送请求时带上验签的值,可以在机器人设置里看到密钥。
|
||||||
|
![加签密钥](./images/ding/jiaqian.png)
|
||||||
|
3. **IP地址(段):** 设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP地址和IP地址段,暂不支持IPv6地址白名单。
|
||||||
|
|
||||||
|
如果你选择 1/3 两种安全设置,你需要自己确认当前网络和发送的消息能被钉钉接受,然后使用 `bot.send` 的时候将 webhook 地址传入 webhook 参数即可。
|
||||||
|
|
||||||
|
如我设置了 `打卡` 为关键词:
|
||||||
|
|
||||||
|
```python
|
||||||
|
message = MessageSegment.text("打卡成功:XXXXXX")
|
||||||
|
await hello.send(
|
||||||
|
message,
|
||||||
|
webhook=
|
||||||
|
"https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
对于第二种加签方式,你可以在 `bot.send` 的时候把 `secret` 参数传进去,Nonebot 内部会自动帮你计算发送该消息的签名并发送,如:
|
||||||
|
|
||||||
|
这里的 `secret` 参数就是加签选项给出的那个密钥。
|
||||||
|
|
||||||
|
```python
|
||||||
|
message = MessageSegment.raw({
|
||||||
|
"msgtype": "text",
|
||||||
|
"text": {
|
||||||
|
"content": 'hello from webhook,一定要注意安全方式的鉴权哦,否则可能发送失败的'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
message += MessageSegment.atDingtalkIds(event.senderId)
|
||||||
|
await hello.send(
|
||||||
|
message,
|
||||||
|
webhook="https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX",
|
||||||
|
secret="SECXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
然后就可以发送成功了。
|
||||||
|
|
||||||
|
![测试 Webhook 发送](images/ding/test_webhook.png)
|
||||||
|
|
||||||
## 创建机器人并连接
|
## 创建机器人并连接
|
||||||
|
|
||||||
在钉钉官方文档 [「开发企业内部机器人 -> 步骤一:创建机器人应用」](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots/title-ufs-4gh-poh) 中有详细介绍,这里就省去创建的步骤,介绍一下如何连接上程序。
|
在钉钉官方文档 [「开发企业内部机器人 -> 步骤一:创建机器人应用」](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots/title-ufs-4gh-poh) 中有详细介绍,这里就省去创建的步骤,介绍一下如何连接上程序。
|
@ -4,6 +4,5 @@
|
|||||||
|
|
||||||
- 请千万注意事件处理器的优先级设定
|
- 请千万注意事件处理器的优先级设定
|
||||||
- 在匹配规则中请勿使用耗时极长的函数
|
- 在匹配规则中请勿使用耗时极长的函数
|
||||||
- 同一个用户可以**跨群**(**私聊**)继续他的事件处理(除非做出权限限制,将在后续介绍)
|
|
||||||
|
|
||||||
如果「指南」还不能满足你,前往 [进阶](../advanced/README.md) 查看更多的功能信息。
|
如果「指南」还不能满足你,前往 [进阶](../advanced/README.md) 查看更多的功能信息。
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
BIN
archive/2.0.0a12/guide/images/ding/jiaqian.png
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
archive/2.0.0a12/guide/images/ding/test_webhook.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
archive/2.0.0a12/guide/images/ding/webhook.png
Normal file
After Width: | Height: | Size: 100 KiB |
@ -1,6 +1,6 @@
|
|||||||
# 安装
|
# 安装
|
||||||
|
|
||||||
## NoneBot
|
## 安装 NoneBot
|
||||||
|
|
||||||
:::warning 注意
|
:::warning 注意
|
||||||
请确保你的 Python 版本 >= 3.7。
|
请确保你的 Python 版本 >= 3.7。
|
||||||
@ -67,6 +67,19 @@ poetry install --no-dev # 推荐
|
|||||||
pip install . # 不推荐
|
pip install . # 不推荐
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 安装适配器
|
||||||
|
|
||||||
|
适配器可以通过 `nb-cli` 在创建项目时根据你的选择自动安装,也可以自行使用 `pip` 安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install nonebot-adapter-<adapter-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出所有的适配器
|
||||||
|
nb adapter list
|
||||||
|
```
|
||||||
|
|
||||||
## 安装插件
|
## 安装插件
|
||||||
|
|
||||||
插件可以通过 `nb-cli` 进行安装,也可以自行安装并加载插件。
|
插件可以通过 `nb-cli` 进行安装,也可以自行安装并加载插件。
|
||||||
@ -87,6 +100,7 @@ nb plugin install xxx
|
|||||||
- [NoneBot-Plugin-Docs](https://github.com/nonebot/nonebot2/tree/master/packages/nonebot-plugin-docs) 离线文档插件
|
- [NoneBot-Plugin-Docs](https://github.com/nonebot/nonebot2/tree/master/packages/nonebot-plugin-docs) 离线文档插件
|
||||||
- [NoneBot-Plugin-Test](https://github.com/nonebot/plugin-test) 本地机器人测试前端插件
|
- [NoneBot-Plugin-Test](https://github.com/nonebot/plugin-test) 本地机器人测试前端插件
|
||||||
- [NoneBot-Plugin-APScheduler](https://github.com/nonebot/plugin-apscheduler) 定时任务插件
|
- [NoneBot-Plugin-APScheduler](https://github.com/nonebot/plugin-apscheduler) 定时任务插件
|
||||||
|
- [NoneBot-Plugin-LocalStore](https://github.com/nonebot/plugin-localstore) 本地数据文件存储插件
|
||||||
- [NoneBot-Plugin-Sentry](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_sentry) Sentry 在线日志分析插件
|
- [NoneBot-Plugin-Sentry](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_sentry) Sentry 在线日志分析插件
|
||||||
- [NoneBot-Plugin-Status](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status) 服务器状态查看插件
|
- [NoneBot-Plugin-Status](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status) 服务器状态查看插件
|
||||||
|
|
@ -28,6 +28,12 @@ Mirai-API-HTTP 的适配器以 [AGPLv3 许可](https://opensource.org/licenses/A
|
|||||||
|
|
||||||
**为了便捷起见, 以下内容均以缩写 `MAH` 代替 `mirai-api-http`**
|
**为了便捷起见, 以下内容均以缩写 `MAH` 代替 `mirai-api-http`**
|
||||||
|
|
||||||
|
## 安装 NoneBot Mirai 适配器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install nonebot-adapter-mirai
|
||||||
|
```
|
||||||
|
|
||||||
## 配置 MAH 客户端
|
## 配置 MAH 客户端
|
||||||
|
|
||||||
正如你可能刚刚在[CQHTTP 协议使用指南](./cqhttp-guide.md)中所读到的:
|
正如你可能刚刚在[CQHTTP 协议使用指南](./cqhttp-guide.md)中所读到的:
|
@ -119,6 +119,10 @@
|
|||||||
"title": "nonebot.matcher 模块",
|
"title": "nonebot.matcher 模块",
|
||||||
"path": "matcher"
|
"path": "matcher"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "nonebot.handler 模块",
|
||||||
|
"path": "handler"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "nonebot.rule 模块",
|
"title": "nonebot.rule 模块",
|
||||||
"path": "rule"
|
"path": "rule"
|
@ -166,6 +166,10 @@ module.exports = context => ({
|
|||||||
title: "nonebot.matcher 模块",
|
title: "nonebot.matcher 模块",
|
||||||
path: "matcher"
|
path: "matcher"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "nonebot.handler 模块",
|
||||||
|
path: "handler"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "nonebot.rule 模块",
|
title: "nonebot.rule 模块",
|
||||||
path: "rule"
|
path: "rule"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[
|
[
|
||||||
"2.0.0a11",
|
"2.0.0a12",
|
||||||
"2.0.0a10",
|
"2.0.0a10",
|
||||||
"2.0.0a8.post2",
|
"2.0.0a8.post2",
|
||||||
"2.0.0a7"
|
"2.0.0a7"
|
||||||
|
@ -10,9 +10,17 @@
|
|||||||
|
|
||||||
**便捷起见,以下内容对 `Nonebot2` 会被称为 `nonebot`,与 `Nonebot2` 交互的机器人实现会被称为 `协议端`**。
|
**便捷起见,以下内容对 `Nonebot2` 会被称为 `nonebot`,与 `Nonebot2` 交互的机器人实现会被称为 `协议端`**。
|
||||||
|
|
||||||
在实际应用中,`nonebot` 会充当一个高性能,轻量级的 Python 微服务框架。协议端可以通过 `http`, `websocket` 等方式与之通信,这个通信往往是双向的:一方面,协议端可以上报数据给 `nonebot`,`nonebot` 会处理数据并返回响应给协议端;另一方面,`nonebot` 可以主动推送数据给协议端。而 `nonebot` 便是围绕上述的双向通信进行工作的。
|
在实际应用中,`nonebot` 会充当一个高性能,轻量级的 Python 微服务框架。协议端可以通过 `http`, `websocket` 等方式与之通信,这个通信往往是双向的:一方面,协议端可以上报数据给 `nonebot`,`nonebot` 会处理数据并返回响应给协议端;另一方面,`nonebot` 可以主动推送数据给协议端。而 `nonebot` 便是围绕双向通信进行工作的。
|
||||||
|
|
||||||
在开始工作之前,`nonebot` 会依照**配置文件或初始化配置**启动,并会注册**协议适配器** `adapter`,之后便会加载**插件**, 随后,倘若一个协议端与 `nonebot` 进行了连接,`nonebot` 的后端驱动 `driver` 就会将 `adapter` 实例化为 `bot`,`nonebot` 便会利用 `bot` 开始工作,它的工作内容分为两个方面:
|
在开始工作之前,`nonebot` 需要进行准备工作:
|
||||||
|
|
||||||
|
1. **运行 `nonebot.init` 初始化函数**,它会读取配置文件,并初始化 `nonebot` 和后端驱动 `driver` 对象。
|
||||||
|
2. **注册协议适配器 `adapter`** 。
|
||||||
|
3. **加载插件**。
|
||||||
|
|
||||||
|
准备工作完成后,`nonebot` 会利用 `uvicorn` 启动,并运行 `on_startup` 钩子函数。
|
||||||
|
|
||||||
|
随后,倘若一个协议端与 `nonebot` 进行了连接,`nonebot` 的后端驱动 `driver` 就会将 `adapter` 实例化为 `bot`,`nonebot` 便会利用 `bot` 开始工作,它的工作内容分为两个方面:
|
||||||
|
|
||||||
1. **事件处理**,`bot` 会将协议端上报的数据转化为 `事件`(`Event`),之后 `nonebot` 会根据一套既定流程来处理 `事件`。
|
1. **事件处理**,`bot` 会将协议端上报的数据转化为 `事件`(`Event`),之后 `nonebot` 会根据一套既定流程来处理 `事件`。
|
||||||
|
|
||||||
@ -41,7 +49,7 @@
|
|||||||
1. 协议端会通过 `websocket` 或者 `http` 等方式与 `nonebot` 的后端驱动 `driver` 连接,`driver` 会根据之前注册的 `adapter` 和配置文件的内容来进行鉴权,从而获得这个连接的唯一识别 id `self-id`,随后 `adapter` 就会利用 `self-id` 实例化为 `bot` 对象。
|
1. 协议端会通过 `websocket` 或者 `http` 等方式与 `nonebot` 的后端驱动 `driver` 连接,`driver` 会根据之前注册的 `adapter` 和配置文件的内容来进行鉴权,从而获得这个连接的唯一识别 id `self-id`,随后 `adapter` 就会利用 `self-id` 实例化为 `bot` 对象。
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
需要注意的是,如果协议端通过 `websocket` 与 `nonebot` 连接,这个步骤只会在建立连接时进行;通过 `http` 方式连接时,会在协议端每次上报数据时都进行这个步骤。
|
需要注意的是,如果协议端通过 `websocket` 与 `nonebot` 连接,这个步骤只会在建立连接时进行,并在之后运行 `on_bot_connect` 钩子函数;通过 `http` 方式连接时,会在协议端每次上报数据时都进行这个步骤。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: warning
|
::: warning
|
||||||
@ -142,7 +150,7 @@
|
|||||||
|
|
||||||
这个异常可以在 `handler` 中由 `Matcher.reject` 抛出。
|
这个异常可以在 `handler` 中由 `Matcher.reject` 抛出。
|
||||||
|
|
||||||
当 `nonebot` 捕获到它时,会停止运行当前 `handler` 并结束当前 `matcher` 的运行,并将**当前 handler 和后续 `handler`**交给一个临时 `Matcher` 来响应当前交互用户的下一个消息事件,当临时 `Matcher` 响应时,临时 `Matcher` 会运行当前 `handler` 和后续的 `handler`。
|
当 `nonebot` 捕获到它时,会停止运行当前 `handler` 并结束当前 `matcher` 的运行,并将当前 handler 和后续 `handler` 交给一个临时 `Matcher` 来响应当前交互用户的下一个消息事件,当临时 `Matcher` 响应时,临时 `Matcher` 会运行当前 `handler` 和后续的 `handler`。
|
||||||
|
|
||||||
4. **FinishedException**
|
4. **FinishedException**
|
||||||
|
|
||||||
@ -158,7 +166,7 @@
|
|||||||
|
|
||||||
## 调用 API
|
## 调用 API
|
||||||
|
|
||||||
`nonebot` 可以通过 `bot` 来调用 API,API 可以向协议端发送数据,也可以向协议端请求更多的数据。
|
`nonebot` 可以通过 `bot` 来调用 `API` ,`API` 可以向协议端发送数据,也可以向协议端请求更多的数据。
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
不同 `adapter` 规定了不同的 API,对应的 API 列表请参照协议规范。
|
不同 `adapter` 规定了不同的 API,对应的 API 列表请参照协议规范。
|
||||||
|
@ -4,43 +4,131 @@
|
|||||||
|
|
||||||
> 钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。
|
> 钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。
|
||||||
|
|
||||||
在 `nonebot2` 中有一系列预定义的钩子函数,这些函数位于 [`nonebot.message`](https://v2.nonebot.dev/api/message.html) 模块下,我们可以以装饰器的形式利用这些函数,进行以下四种操作:
|
在 `nonebot2` 中有一系列预定义的钩子函数,分为两类:`全局钩子函数` 和 `事件钩子函数` ,这些钩子函数可以用装饰器的形式来使用。
|
||||||
|
|
||||||
|
## 全局钩子函数
|
||||||
|
|
||||||
|
全局钩子函数是指 `nonebot2` 针对其本身运行过程的钩子函数。
|
||||||
|
|
||||||
|
这些钩子函数是由其后端驱动 `driver`来运行的,故需要先获得全局 `driver` 对象:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot import get_driver
|
||||||
|
|
||||||
|
|
||||||
|
driver=get_driver()
|
||||||
|
```
|
||||||
|
|
||||||
|
共分为五种函数:
|
||||||
|
|
||||||
|
### 启动准备
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2` 启动时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_startup
|
||||||
|
async def do_something():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 终止处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2` 终止时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_shutdown
|
||||||
|
async def do_something():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### bot 连接处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `bot` 通过 `websocket` 连接到 `nonebot2` 时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_bot_connect
|
||||||
|
async def do_something(bot: Bot):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### bot 断开处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `bot` 断开与 `nonebot2` 的 `websocket` 连接时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
@driver.on_bot_disconnect
|
||||||
|
async def do_something(bot: Bot):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### bot api 调用钩子
|
||||||
|
|
||||||
|
这个钩子函数会在 `Bot` 调用 API 时运行。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nonebot.adapters import Bot
|
||||||
|
|
||||||
|
@Bot.on_calling_api
|
||||||
|
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## 事件处理钩子
|
||||||
|
|
||||||
|
这些钩子函数指的是影响 `nonebot2` 进行 `事件处理` 的函数。
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
关于 `事件处理` 的流程,可以在[这里](./README)查阅。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
:::warning 注意
|
:::warning 注意
|
||||||
1.在钩子函数中,与 `matcher` 运行状态相关的函数将不可用,如 `matcher.finish()`
|
|
||||||
|
|
||||||
2.如果需要在钩子函数中打断整个对话的执行,请参考以下范例:
|
1.在事件处理钩子函数中,与 `matcher` 运行状态相关的函数将不可用,如 `matcher.finish()`
|
||||||
|
|
||||||
|
2.如果需要在事件处理钩子函数中打断整个对话的执行,请参考以下范例:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
async def do_something(bot: Bot, event: Event, state: T_State):
|
||||||
raise IgnoredException("reason")
|
raise IgnoredException("reason")
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## 事件预处理
|
共分为四种函数:
|
||||||
|
|
||||||
|
### 事件预处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `Event` 上报到 `nonebot2` 时运行
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
async def do_something(bot: Bot, event: Event, state: T_State):
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
## 事件后处理
|
### 事件后处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2` 处理 `Event` 后运行
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
async def do_something(bot: Bot, event: Event, state: T_State):
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
## 运行预处理
|
### 运行预处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2`运行 `matcher` 前运行。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.message import run_preprocessor
|
from nonebot.message import run_preprocessor
|
||||||
@ -50,11 +138,14 @@ async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State)
|
|||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
## 运行后处理
|
### 运行后处理
|
||||||
|
|
||||||
|
这个钩子函数会在 `nonebot2`运行 `matcher` 后运行。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.message import run_postprocessor
|
from nonebot.message import run_postprocessor
|
||||||
|
|
||||||
@run_postprocessor
|
@run_postprocessor
|
||||||
async def do_something(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
async def do_something(matcher: Matcher, exception: Optional[Exception], bot: Bot, event: Event, state: T_State):
|
||||||
pass
|
pass
|
||||||
```
|
```
|
@ -19,6 +19,9 @@
|
|||||||
* [nonebot.matcher](matcher.html)
|
* [nonebot.matcher](matcher.html)
|
||||||
|
|
||||||
|
|
||||||
|
* [nonebot.handler](handler.html)
|
||||||
|
|
||||||
|
|
||||||
* [nonebot.rule](rule.html)
|
* [nonebot.rule](rule.html)
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,6 +27,21 @@ Driver 对象
|
|||||||
Config 配置对象
|
Config 配置对象
|
||||||
|
|
||||||
|
|
||||||
|
### `_call_api_hook`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Set[T_CallingAPIHook]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
call_api 时执行的函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract_ `__init__(connection_type, self_id, *, websocket=None)`
|
### _abstract_ `__init__(connection_type, self_id, *, websocket=None)`
|
||||||
|
|
||||||
|
|
||||||
@ -93,7 +108,7 @@ Adapter 类型
|
|||||||
* `headers: dict`: 请求头
|
* `headers: dict`: 请求头
|
||||||
|
|
||||||
|
|
||||||
* `body: Optional[dict]`: 请求数据,WebSocket 连接该部分为空
|
* `body: Optional[bytes]`: 请求数据,WebSocket 连接该部分为 None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -127,7 +142,26 @@ Adapter 类型
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _abstract async_ `call_api(api, **data)`
|
### _abstract async_ `_call_api(api, **data)`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
`adapter` 实际调用 api 的逻辑实现函数,实现该方法以调用 api。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **参数**
|
||||||
|
|
||||||
|
|
||||||
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
|
* `**data`: API 数据
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _async_ `call_api(api, **data)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -142,6 +176,9 @@ Adapter 类型
|
|||||||
* `api: str`: API 名称
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
|
* `self_id: Optional[str]`: 指定调用 API 的机器人
|
||||||
|
|
||||||
|
|
||||||
* `**data`: API 数据
|
* `**data`: API 数据
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,6 +129,9 @@ sidebarDepth: 0
|
|||||||
* `api: str`: API 名称
|
* `api: str`: API 名称
|
||||||
|
|
||||||
|
|
||||||
|
* `event: Optional[MessageEvent]`: Event 对象
|
||||||
|
|
||||||
|
|
||||||
* `**data: Any`: API 参数
|
* `**data: Any`: API 参数
|
||||||
|
|
||||||
|
|
||||||
@ -150,7 +153,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### _async_ `send(event, message, at_sender=False, **kwargs)`
|
### _async_ `send(event, message, at_sender=False, webhook=None, secret=None, **kwargs)`
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
* **说明**
|
||||||
@ -171,6 +174,12 @@ sidebarDepth: 0
|
|||||||
* `at_sender: bool`: 是否 @ 事件主体
|
* `at_sender: bool`: 是否 @ 事件主体
|
||||||
|
|
||||||
|
|
||||||
|
* `webhook: Optional[str]`: 该条消息将调用的 webhook 地址。不传则将使用 sessionWebhook,若其也不存在,该条消息不发送,使用自定义 webhook 时注意你设置的安全方式,如加关键词,IP地址,加签等等。
|
||||||
|
|
||||||
|
|
||||||
|
* `secret: Optional[str]`: 如果你使用自定义的 webhook 地址,推荐使用加签方式对消息进行验证,将 机器人安全设置页面,加签一栏下面显示的SEC开头的字符串 传入这个参数即可。
|
||||||
|
|
||||||
|
|
||||||
* `**kwargs`: 覆盖默认参数
|
* `**kwargs`: 覆盖默认参数
|
||||||
|
|
||||||
|
|
||||||
|
111
docs/api/handler.md
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
contentSidebar: true
|
||||||
|
sidebarDepth: 0
|
||||||
|
---
|
||||||
|
|
||||||
|
# NoneBot.handler 模块
|
||||||
|
|
||||||
|
## 事件处理函数
|
||||||
|
|
||||||
|
该模块实现事件处理函数的封装,以实现动态参数等功能。
|
||||||
|
|
||||||
|
|
||||||
|
## _class_ `Handler`
|
||||||
|
|
||||||
|
基类:`object`
|
||||||
|
|
||||||
|
事件处理函数类
|
||||||
|
|
||||||
|
|
||||||
|
### `__init__(func)`
|
||||||
|
|
||||||
|
装饰事件处理函数以便根据动态参数运行
|
||||||
|
|
||||||
|
|
||||||
|
### `func`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`T_Handler`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `signature`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`inspect.Signature`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数签名
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `bot_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Union[Type["Bot"], inspect.Parameter.empty]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数接受的 Bot 对象类型
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `event_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[Union[Type[Event], inspect.Parameter.empty]]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数接受的 event 类型 / 不需要 event 参数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `state_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[Union[T_State, inspect.Parameter.empty]]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数是否接受 state 参数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `matcher_type`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**
|
||||||
|
|
||||||
|
`Optional[Union[Type["Matcher"], inspect.Parameter.empty]]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
事件处理函数是否接受 matcher 参数
|
@ -202,7 +202,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Optional[T_ArgsParser]`
|
`Optional[T_TypeUpdater]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`Optional[T_ArgsParser]`
|
`Optional[T_PermissionUpdater]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
* **类型**
|
* **类型**
|
||||||
|
|
||||||
`List[T_Handler]`
|
`List[Handler]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,38 +25,6 @@ sidebarDepth: 0
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## _class_ `Export`
|
|
||||||
|
|
||||||
基类:`dict`
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
|
||||||
|
|
||||||
插件导出内容以使得其他插件可以获得。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **示例**
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
nonebot.export().default = "bar"
|
|
||||||
|
|
||||||
@nonebot.export()
|
|
||||||
def some_function():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# this doesn't work before python 3.9
|
|
||||||
# use
|
|
||||||
# export = nonebot.export(); @export.sub
|
|
||||||
# instead
|
|
||||||
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
|
||||||
@nonebot.export().sub
|
|
||||||
def something_else():
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## _class_ `Plugin`
|
## _class_ `Plugin`
|
||||||
|
|
||||||
基类:`object`
|
基类:`object`
|
||||||
@ -82,15 +50,6 @@ def something_else():
|
|||||||
* **说明**: 插件模块对象
|
* **说明**: 插件模块对象
|
||||||
|
|
||||||
|
|
||||||
### `matcher`
|
|
||||||
|
|
||||||
|
|
||||||
* **类型**: `Set[Type[Matcher]]`
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**: 插件内定义的 `Matcher`
|
|
||||||
|
|
||||||
|
|
||||||
### `export`
|
### `export`
|
||||||
|
|
||||||
|
|
||||||
@ -100,6 +59,15 @@ def something_else():
|
|||||||
* **说明**: 插件内定义的导出内容
|
* **说明**: 插件内定义的导出内容
|
||||||
|
|
||||||
|
|
||||||
|
### _property_ `matcher`
|
||||||
|
|
||||||
|
|
||||||
|
* **类型**: `Set[Type[Matcher]]`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**: 插件内定义的 `Matcher`
|
||||||
|
|
||||||
|
|
||||||
## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None, state_factory=None)`
|
## `on(type='', rule=None, permission=None, *, handlers=None, temp=False, priority=1, block=False, state=None, state_factory=None)`
|
||||||
|
|
||||||
|
|
||||||
@ -121,7 +89,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -162,7 +130,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -206,7 +174,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -247,7 +215,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -288,7 +256,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -335,7 +303,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -382,7 +350,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -429,7 +397,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -481,7 +449,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -538,7 +506,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -590,7 +558,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -767,7 +735,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -808,7 +776,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -852,7 +820,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -893,7 +861,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -934,7 +902,7 @@ def something_else():
|
|||||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -981,7 +949,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1028,7 +996,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1075,7 +1043,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1127,7 +1095,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1184,7 +1152,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1236,7 +1204,7 @@ def something_else():
|
|||||||
* `permission: Optional[Permission]`: 事件响应权限
|
* `permission: Optional[Permission]`: 事件响应权限
|
||||||
|
|
||||||
|
|
||||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
* `handlers: Optional[List[Union[T_Handler, Handler]]]`: 事件处理函数列表
|
||||||
|
|
||||||
|
|
||||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||||
@ -1442,22 +1410,6 @@ def something_else():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `export()`
|
|
||||||
|
|
||||||
|
|
||||||
* **说明**
|
|
||||||
|
|
||||||
获取插件的导出内容对象
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **返回**
|
|
||||||
|
|
||||||
|
|
||||||
* `Export`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `require(name)`
|
## `require(name)`
|
||||||
|
|
||||||
|
|
||||||
@ -1478,3 +1430,51 @@ def something_else():
|
|||||||
|
|
||||||
|
|
||||||
* `Optional[Export]`
|
* `Optional[Export]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## _class_ `Export`
|
||||||
|
|
||||||
|
基类:`dict`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
插件导出内容以使得其他插件可以获得。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **示例**
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
nonebot.export().default = "bar"
|
||||||
|
|
||||||
|
@nonebot.export()
|
||||||
|
def some_function():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# this doesn't work before python 3.9
|
||||||
|
# use
|
||||||
|
# export = nonebot.export(); @export.sub
|
||||||
|
# instead
|
||||||
|
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
||||||
|
@nonebot.export().sub
|
||||||
|
def something_else():
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `export()`
|
||||||
|
|
||||||
|
|
||||||
|
* **说明**
|
||||||
|
|
||||||
|
获取插件的导出内容对象
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **返回**
|
||||||
|
|
||||||
|
|
||||||
|
* `Export`
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
- [群机器人概述](https://developers.dingtalk.com/document/app/overview-of-group-robots)
|
- [群机器人概述](https://developers.dingtalk.com/document/app/overview-of-group-robots)
|
||||||
- [开发企业内部机器人](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots)
|
- [开发企业内部机器人](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots)
|
||||||
|
|
||||||
|
钉钉官方机器人教程(Java):
|
||||||
|
|
||||||
|
- [开发一个钉钉机器人](https://developers.dingtalk.com/document/tutorial/create-a-robot)
|
||||||
|
|
||||||
## 安装 NoneBot 钉钉 适配器
|
## 安装 NoneBot 钉钉 适配器
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -93,6 +97,58 @@ async def raw_handler(bot: DingBot, event: MessageEvent):
|
|||||||
|
|
||||||
其他消息格式请查看 [钉钉适配器的 MessageSegment](https://github.com/nonebot/nonebot2/blob/dev/nonebot/adapters/ding/message.py#L8),里面封装了很多有关消息的方法,比如 `code`、`image`、`feedCard` 等。
|
其他消息格式请查看 [钉钉适配器的 MessageSegment](https://github.com/nonebot/nonebot2/blob/dev/nonebot/adapters/ding/message.py#L8),里面封装了很多有关消息的方法,比如 `code`、`image`、`feedCard` 等。
|
||||||
|
|
||||||
|
## 发送到特定群聊
|
||||||
|
|
||||||
|
钉钉也支持通过 Webhook 的方式直接将消息推送到某个群聊([参考链接](https://developers.dingtalk.com/document/app/custom-robot-access/title-zob-eyu-qse)),你可以在机器人的设置中看到当前群的 Webhook 地址。
|
||||||
|
|
||||||
|
![机器人所在群的 Webhook 地址](./images/ding/webhook.png)
|
||||||
|
|
||||||
|
获取到Webhook地址后,用户可以向这个地址发起HTTP POST 请求,即可实现给该钉钉群发送消息。
|
||||||
|
|
||||||
|
对于这种通过 Webhook 推送的消息,钉钉需要开发者进行安全方面的设置(目前有3种安全设置方式,请根据需要选择一种),如下:
|
||||||
|
|
||||||
|
1. **自定义关键词:** 最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功。
|
||||||
|
例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。
|
||||||
|
2. **加签:** 发送请求时带上验签的值,可以在机器人设置里看到密钥。
|
||||||
|
![加签密钥](./images/ding/jiaqian.png)
|
||||||
|
3. **IP地址(段):** 设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP地址和IP地址段,暂不支持IPv6地址白名单。
|
||||||
|
|
||||||
|
如果你选择 1/3 两种安全设置,你需要自己确认当前网络和发送的消息能被钉钉接受,然后使用 `bot.send` 的时候将 webhook 地址传入 webhook 参数即可。
|
||||||
|
|
||||||
|
如我设置了 `打卡` 为关键词:
|
||||||
|
|
||||||
|
```python
|
||||||
|
message = MessageSegment.text("打卡成功:XXXXXX")
|
||||||
|
await hello.send(
|
||||||
|
message,
|
||||||
|
webhook=
|
||||||
|
"https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
对于第二种加签方式,你可以在 `bot.send` 的时候把 `secret` 参数传进去,Nonebot 内部会自动帮你计算发送该消息的签名并发送,如:
|
||||||
|
|
||||||
|
这里的 `secret` 参数就是加签选项给出的那个密钥。
|
||||||
|
|
||||||
|
```python
|
||||||
|
message = MessageSegment.raw({
|
||||||
|
"msgtype": "text",
|
||||||
|
"text": {
|
||||||
|
"content": 'hello from webhook,一定要注意安全方式的鉴权哦,否则可能发送失败的'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
message += MessageSegment.atDingtalkIds(event.senderId)
|
||||||
|
await hello.send(
|
||||||
|
message,
|
||||||
|
webhook="https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX",
|
||||||
|
secret="SECXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
然后就可以发送成功了。
|
||||||
|
|
||||||
|
![测试 Webhook 发送](images/ding/test_webhook.png)
|
||||||
|
|
||||||
## 创建机器人并连接
|
## 创建机器人并连接
|
||||||
|
|
||||||
在钉钉官方文档 [「开发企业内部机器人 -> 步骤一:创建机器人应用」](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots/title-ufs-4gh-poh) 中有详细介绍,这里就省去创建的步骤,介绍一下如何连接上程序。
|
在钉钉官方文档 [「开发企业内部机器人 -> 步骤一:创建机器人应用」](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots/title-ufs-4gh-poh) 中有详细介绍,这里就省去创建的步骤,介绍一下如何连接上程序。
|
||||||
|
@ -4,6 +4,5 @@
|
|||||||
|
|
||||||
- 请千万注意事件处理器的优先级设定
|
- 请千万注意事件处理器的优先级设定
|
||||||
- 在匹配规则中请勿使用耗时极长的函数
|
- 在匹配规则中请勿使用耗时极长的函数
|
||||||
- 同一个用户可以**跨群**(**私聊**)继续他的事件处理(除非做出权限限制,将在后续介绍)
|
|
||||||
|
|
||||||
如果「指南」还不能满足你,前往 [进阶](../advanced/README.md) 查看更多的功能信息。
|
如果「指南」还不能满足你,前往 [进阶](../advanced/README.md) 查看更多的功能信息。
|
||||||
|
BIN
docs/guide/images/ding/jiaqian.png
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
docs/guide/images/ding/test_webhook.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
docs/guide/images/ding/webhook.png
Normal file
After Width: | Height: | Size: 100 KiB |
@ -68,6 +68,7 @@ pip install . # 不推荐
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 安装适配器
|
## 安装适配器
|
||||||
|
|
||||||
适配器可以通过 `nb-cli` 在创建项目时根据你的选择自动安装,也可以自行使用 `pip` 安装
|
适配器可以通过 `nb-cli` 在创建项目时根据你的选择自动安装,也可以自行使用 `pip` 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -99,6 +100,7 @@ nb plugin install xxx
|
|||||||
- [NoneBot-Plugin-Docs](https://github.com/nonebot/nonebot2/tree/master/packages/nonebot-plugin-docs) 离线文档插件
|
- [NoneBot-Plugin-Docs](https://github.com/nonebot/nonebot2/tree/master/packages/nonebot-plugin-docs) 离线文档插件
|
||||||
- [NoneBot-Plugin-Test](https://github.com/nonebot/plugin-test) 本地机器人测试前端插件
|
- [NoneBot-Plugin-Test](https://github.com/nonebot/plugin-test) 本地机器人测试前端插件
|
||||||
- [NoneBot-Plugin-APScheduler](https://github.com/nonebot/plugin-apscheduler) 定时任务插件
|
- [NoneBot-Plugin-APScheduler](https://github.com/nonebot/plugin-apscheduler) 定时任务插件
|
||||||
|
- [NoneBot-Plugin-LocalStore](https://github.com/nonebot/plugin-localstore) 本地数据文件存储插件
|
||||||
- [NoneBot-Plugin-Sentry](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_sentry) Sentry 在线日志分析插件
|
- [NoneBot-Plugin-Sentry](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_sentry) Sentry 在线日志分析插件
|
||||||
- [NoneBot-Plugin-Status](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status) 服务器状态查看插件
|
- [NoneBot-Plugin-Status](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status) 服务器状态查看插件
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ NoneBot Api Reference
|
|||||||
- `nonebot.plugin <plugin.html>`_
|
- `nonebot.plugin <plugin.html>`_
|
||||||
- `nonebot.message <message.html>`_
|
- `nonebot.message <message.html>`_
|
||||||
- `nonebot.matcher <matcher.html>`_
|
- `nonebot.matcher <matcher.html>`_
|
||||||
|
- `nonebot.handler <handler.html>`_
|
||||||
- `nonebot.rule <rule.html>`_
|
- `nonebot.rule <rule.html>`_
|
||||||
- `nonebot.permission <permission.html>`_
|
- `nonebot.permission <permission.html>`_
|
||||||
- `nonebot.log <log.html>`_
|
- `nonebot.log <log.html>`_
|
||||||
|
13
docs_build/handler.rst
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
contentSidebar: true
|
||||||
|
sidebarDepth: 0
|
||||||
|
---
|
||||||
|
|
||||||
|
NoneBot.handler 模块
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. automodule:: nonebot.handler
|
||||||
|
:members:
|
||||||
|
:private-members:
|
||||||
|
:special-members: __init__
|
||||||
|
:show-inheritance:
|
@ -10,3 +10,8 @@ NoneBot.plugin 模块
|
|||||||
:members:
|
:members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
:special-members: __init__
|
:special-members: __init__
|
||||||
|
|
||||||
|
.. automodule:: nonebot.plugin.export
|
||||||
|
:members:
|
||||||
|
:show-inheritance:
|
||||||
|
:special-members: __init__
|
||||||
|
@ -6,21 +6,30 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
import asyncio
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing_extensions import Literal
|
|
||||||
from functools import reduce, partial
|
from functools import reduce, partial
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Any, Dict, Union, TypeVar, Mapping, Optional, Callable, Iterable, Iterator, Awaitable, TYPE_CHECKING
|
from typing import (Any, Set, Dict, Union, TypeVar, Mapping, Optional, Iterable,
|
||||||
|
Protocol, Awaitable, TYPE_CHECKING)
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from nonebot.log import logger
|
||||||
from nonebot.utils import DataclassEncoder
|
from nonebot.utils import DataclassEncoder
|
||||||
|
from nonebot.typing import T_CallingAPIHook
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from nonebot.config import Config
|
from nonebot.config import Config
|
||||||
from nonebot.drivers import Driver, WebSocket
|
from nonebot.drivers import Driver, WebSocket
|
||||||
|
|
||||||
|
|
||||||
|
class _ApiCall(Protocol):
|
||||||
|
|
||||||
|
def __call__(self, **kwargs: Any) -> Awaitable[Any]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class Bot(abc.ABC):
|
class Bot(abc.ABC):
|
||||||
"""
|
"""
|
||||||
Bot 基类。用于处理上报消息,并提供 API 调用接口。
|
Bot 基类。用于处理上报消息,并提供 API 调用接口。
|
||||||
@ -30,6 +39,11 @@ class Bot(abc.ABC):
|
|||||||
"""Driver 对象"""
|
"""Driver 对象"""
|
||||||
config: "Config"
|
config: "Config"
|
||||||
"""Config 配置对象"""
|
"""Config 配置对象"""
|
||||||
|
_call_api_hook: Set[T_CallingAPIHook] = set()
|
||||||
|
"""
|
||||||
|
:类型: ``Set[T_CallingAPIHook]``
|
||||||
|
:说明: call_api 时执行的函数
|
||||||
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
@ -51,7 +65,7 @@ class Bot(abc.ABC):
|
|||||||
self.websocket = websocket
|
self.websocket = websocket
|
||||||
"""Websocket 连接对象"""
|
"""Websocket 连接对象"""
|
||||||
|
|
||||||
def __getattr__(self, name: str) -> Callable[..., Awaitable[Any]]:
|
def __getattr__(self, name: str) -> _ApiCall:
|
||||||
return partial(self.call_api, name)
|
return partial(self.call_api, name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -73,7 +87,7 @@ class Bot(abc.ABC):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def check_permission(cls, driver: "Driver", connection_type: str,
|
async def check_permission(cls, driver: "Driver", connection_type: str,
|
||||||
headers: dict, body: Optional[dict]) -> str:
|
headers: dict, body: Optional[bytes]) -> str:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -84,7 +98,7 @@ class Bot(abc.ABC):
|
|||||||
* ``driver: Driver``: Driver 对象
|
* ``driver: Driver``: Driver 对象
|
||||||
* ``connection_type: str``: 连接类型
|
* ``connection_type: str``: 连接类型
|
||||||
* ``headers: dict``: 请求头
|
* ``headers: dict``: 请求头
|
||||||
* ``body: Optional[dict]``: 请求数据,WebSocket 连接该部分为空
|
* ``body: Optional[bytes]``: 请求数据,WebSocket 连接该部分为 None
|
||||||
|
|
||||||
:返回:
|
:返回:
|
||||||
|
|
||||||
@ -110,7 +124,20 @@ class Bot(abc.ABC):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def call_api(self, api: str, **data):
|
async def _call_api(self, api: str, **data) -> Any:
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
``adapter`` 实际调用 api 的逻辑实现函数,实现该方法以调用 api。
|
||||||
|
|
||||||
|
:参数:
|
||||||
|
|
||||||
|
* ``api: str``: API 名称
|
||||||
|
* ``**data``: API 数据
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def call_api(self, api: str, **data: Any) -> Any:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -119,6 +146,7 @@ class Bot(abc.ABC):
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``api: str``: API 名称
|
* ``api: str``: API 名称
|
||||||
|
* ``self_id: Optional[str]``: 指定调用 API 的机器人
|
||||||
* ``**data``: API 数据
|
* ``**data``: API 数据
|
||||||
|
|
||||||
:示例:
|
:示例:
|
||||||
@ -128,11 +156,28 @@ class Bot(abc.ABC):
|
|||||||
await bot.call_api("send_msg", message="hello world")
|
await bot.call_api("send_msg", message="hello world")
|
||||||
await bot.send_msg(message="hello world")
|
await bot.send_msg(message="hello world")
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
coros = list(map(lambda x: x(self, api, data), self._call_api_hook))
|
||||||
|
if coros:
|
||||||
|
try:
|
||||||
|
logger.debug("Running CallingAPI hooks...")
|
||||||
|
await asyncio.gather(*coros)
|
||||||
|
except Exception as e:
|
||||||
|
logger.opt(colors=True, exception=e).error(
|
||||||
|
"<r><bg #f8bbd0>Error when running CallingAPI hook. "
|
||||||
|
"Running cancelled!</bg #f8bbd0></r>")
|
||||||
|
|
||||||
|
if "self_id" in data:
|
||||||
|
self_id = data.pop("self_id")
|
||||||
|
if self_id:
|
||||||
|
bot = self.driver.bots[str(self_id)]
|
||||||
|
return await bot._call_api(api, **data)
|
||||||
|
|
||||||
|
return await self._call_api(api, **data)
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def send(self, event: "Event",
|
async def send(self, event: "Event", message: Union[str, "Message",
|
||||||
message: Union[str, "Message", "MessageSegment"], **kwargs):
|
"MessageSegment"],
|
||||||
|
**kwargs) -> Any:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -146,6 +191,11 @@ class Bot(abc.ABC):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def on_calling_api(cls, func: T_CallingAPIHook) -> T_CallingAPIHook:
|
||||||
|
cls._call_api_hook.add(func)
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
T_Message = TypeVar("T_Message", bound="Message")
|
T_Message = TypeVar("T_Message", bound="Message")
|
||||||
T_MessageSegment = TypeVar("T_MessageSegment", bound="MessageSegment")
|
T_MessageSegment = TypeVar("T_MessageSegment", bound="MessageSegment")
|
||||||
|
@ -16,7 +16,7 @@ from typing import List, Optional, Callable
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from pydantic import BaseSettings
|
from pydantic import BaseSettings
|
||||||
from fastapi.responses import Response
|
from fastapi.responses import Response
|
||||||
from fastapi import Body, status, Request, FastAPI, HTTPException
|
from fastapi import status, Request, FastAPI, HTTPException
|
||||||
from starlette.websockets import WebSocketDisconnect, WebSocket as FastAPIWebSocket
|
from starlette.websockets import WebSocketDisconnect, WebSocket as FastAPIWebSocket
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
@ -177,11 +177,11 @@ class Driver(BaseDriver):
|
|||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(BaseDriver)
|
||||||
async def _handle_http(self,
|
async def _handle_http(self, adapter: str, request: Request):
|
||||||
adapter: str,
|
data = await request.body()
|
||||||
request: Request,
|
data_dict = json.loads(data.decode())
|
||||||
data: dict = Body(...)):
|
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data_dict, dict):
|
||||||
logger.warning("Data received is invalid")
|
logger.warning("Data received is invalid")
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ class Driver(BaseDriver):
|
|||||||
|
|
||||||
bot = BotClass("http", x_self_id)
|
bot = BotClass("http", x_self_id)
|
||||||
|
|
||||||
asyncio.create_task(bot.handle_message(data))
|
asyncio.create_task(bot.handle_message(data_dict))
|
||||||
return Response("", 204)
|
return Response("", 204)
|
||||||
|
|
||||||
@overrides(BaseDriver)
|
@overrides(BaseDriver)
|
||||||
@ -234,7 +234,8 @@ class Driver(BaseDriver):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if x_self_id in self._clients:
|
if x_self_id in self._clients:
|
||||||
logger.warning("There's already a reverse websocket connection, "
|
logger.opt(colors=True).warning(
|
||||||
|
"There's already a reverse websocket connection, "
|
||||||
f"<y>{adapter.upper()} Bot {x_self_id}</y> ignored.")
|
f"<y>{adapter.upper()} Bot {x_self_id}</y> ignored.")
|
||||||
await ws.close(code=status.WS_1008_POLICY_VIOLATION)
|
await ws.close(code=status.WS_1008_POLICY_VIOLATION)
|
||||||
return
|
return
|
||||||
|
193
nonebot/handler.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
"""
|
||||||
|
事件处理函数
|
||||||
|
===========
|
||||||
|
|
||||||
|
该模块实现事件处理函数的封装,以实现动态参数等功能。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
from typing import Any, List, Dict, Type, Union, Optional, TYPE_CHECKING
|
||||||
|
from typing import ForwardRef, _eval_type # type: ignore
|
||||||
|
|
||||||
|
from nonebot.log import logger
|
||||||
|
from nonebot.typing import T_Handler, T_State
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from nonebot.matcher import Matcher
|
||||||
|
from nonebot.adapters import Bot, Event
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerMeta(type):
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
func: T_Handler
|
||||||
|
signature: inspect.Signature
|
||||||
|
bot_type: Type["Bot"]
|
||||||
|
event_type: Optional[Type["Event"]]
|
||||||
|
state_type: Optional[T_State]
|
||||||
|
matcher_type: Optional[Type["Matcher"]]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (f"<Handler {self.func.__name__}(bot: {self.bot_type}, "
|
||||||
|
f"event: {self.event_type}, state: {self.state_type}, "
|
||||||
|
f"matcher: {self.matcher_type})>")
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return repr(self)
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(metaclass=HandlerMeta):
|
||||||
|
"""事件处理函数类"""
|
||||||
|
|
||||||
|
def __init__(self, func: T_Handler):
|
||||||
|
"""装饰事件处理函数以便根据动态参数运行"""
|
||||||
|
self.func: T_Handler = func
|
||||||
|
"""
|
||||||
|
:类型: ``T_Handler``
|
||||||
|
:说明: 事件处理函数
|
||||||
|
"""
|
||||||
|
self.signature: inspect.Signature = self.get_signature()
|
||||||
|
"""
|
||||||
|
:类型: ``inspect.Signature``
|
||||||
|
:说明: 事件处理函数签名
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (f"<Handler {self.func.__name__}(bot: {self.bot_type}, "
|
||||||
|
f"event: {self.event_type}, state: {self.state_type}, "
|
||||||
|
f"matcher: {self.matcher_type})>")
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return repr(self)
|
||||||
|
|
||||||
|
async def __call__(self, matcher: "Matcher", bot: "Bot", event: "Event",
|
||||||
|
state: T_State):
|
||||||
|
BotType = ((self.bot_type is not inspect.Parameter.empty) and
|
||||||
|
inspect.isclass(self.bot_type) and self.bot_type)
|
||||||
|
if BotType and not isinstance(bot, BotType):
|
||||||
|
logger.debug(
|
||||||
|
f"Matcher {matcher} bot type {type(bot)} not match annotation {BotType}, ignored"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
EventType = ((self.event_type is not inspect.Parameter.empty) and
|
||||||
|
inspect.isclass(self.event_type) and self.event_type)
|
||||||
|
if EventType and not isinstance(event, EventType):
|
||||||
|
logger.debug(
|
||||||
|
f"Matcher {matcher} event type {type(event)} not match annotation {EventType}, ignored"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
args = {"bot": bot, "event": event, "state": state, "matcher": matcher}
|
||||||
|
await self.func(
|
||||||
|
**{
|
||||||
|
k: v
|
||||||
|
for k, v in args.items()
|
||||||
|
if self.signature.parameters.get(k, None) is not None
|
||||||
|
})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bot_type(self) -> Union[Type["Bot"], inspect.Parameter.empty]:
|
||||||
|
"""
|
||||||
|
:类型: ``Union[Type["Bot"], inspect.Parameter.empty]``
|
||||||
|
:说明: 事件处理函数接受的 Bot 对象类型"""
|
||||||
|
return self.signature.parameters["bot"].annotation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def event_type(
|
||||||
|
self) -> Optional[Union[Type["Event"], inspect.Parameter.empty]]:
|
||||||
|
"""
|
||||||
|
:类型: ``Optional[Union[Type[Event], inspect.Parameter.empty]]``
|
||||||
|
:说明: 事件处理函数接受的 event 类型 / 不需要 event 参数
|
||||||
|
"""
|
||||||
|
if "event" not in self.signature.parameters:
|
||||||
|
return None
|
||||||
|
return self.signature.parameters["event"].annotation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_type(self) -> Optional[Union[T_State, inspect.Parameter.empty]]:
|
||||||
|
"""
|
||||||
|
:类型: ``Optional[Union[T_State, inspect.Parameter.empty]]``
|
||||||
|
:说明: 事件处理函数是否接受 state 参数
|
||||||
|
"""
|
||||||
|
if "state" not in self.signature.parameters:
|
||||||
|
return None
|
||||||
|
return self.signature.parameters["state"].annotation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def matcher_type(
|
||||||
|
self) -> Optional[Union[Type["Matcher"], inspect.Parameter.empty]]:
|
||||||
|
"""
|
||||||
|
:类型: ``Optional[Union[Type["Matcher"], inspect.Parameter.empty]]``
|
||||||
|
:说明: 事件处理函数是否接受 matcher 参数
|
||||||
|
"""
|
||||||
|
if "matcher" not in self.signature.parameters:
|
||||||
|
return None
|
||||||
|
return self.signature.parameters["matcher"].annotation
|
||||||
|
|
||||||
|
def get_signature(self) -> inspect.Signature:
|
||||||
|
wrapped_signature = self._get_typed_signature()
|
||||||
|
signature = self._get_typed_signature(False)
|
||||||
|
self._check_params(signature)
|
||||||
|
self._check_bot_param(signature)
|
||||||
|
self._check_bot_param(wrapped_signature)
|
||||||
|
signature.parameters["bot"].replace(
|
||||||
|
annotation=wrapped_signature.parameters["bot"].annotation)
|
||||||
|
if "event" in wrapped_signature.parameters and "event" in signature.parameters:
|
||||||
|
signature.parameters["event"].replace(
|
||||||
|
annotation=wrapped_signature.parameters["event"].annotation)
|
||||||
|
return signature
|
||||||
|
|
||||||
|
def update_signature(
|
||||||
|
self, **kwargs: Union[None, Type["Bot"], Type["Event"], Type["Matcher"],
|
||||||
|
T_State, inspect.Parameter.empty]
|
||||||
|
) -> None:
|
||||||
|
params: List[inspect.Parameter] = []
|
||||||
|
for param in ["bot", "event", "state", "matcher"]:
|
||||||
|
sig = self.signature.parameters.get(param, None)
|
||||||
|
if param in kwargs:
|
||||||
|
sig = inspect.Parameter(param,
|
||||||
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
||||||
|
annotation=kwargs[param])
|
||||||
|
if sig:
|
||||||
|
params.append(sig)
|
||||||
|
|
||||||
|
self.signature = inspect.Signature(params)
|
||||||
|
|
||||||
|
def _get_typed_signature(self,
|
||||||
|
follow_wrapped: bool = True) -> inspect.Signature:
|
||||||
|
signature = inspect.signature(self.func, follow_wrapped=follow_wrapped)
|
||||||
|
globalns = getattr(self.func, "__globals__", {})
|
||||||
|
typed_params = [
|
||||||
|
inspect.Parameter(
|
||||||
|
name=param.name,
|
||||||
|
kind=param.kind,
|
||||||
|
default=param.default,
|
||||||
|
annotation=param.annotation if follow_wrapped else
|
||||||
|
self._get_typed_annotation(param, globalns),
|
||||||
|
) for param in signature.parameters.values()
|
||||||
|
]
|
||||||
|
typed_signature = inspect.Signature(typed_params)
|
||||||
|
return typed_signature
|
||||||
|
|
||||||
|
def _get_typed_annotation(self, param: inspect.Parameter,
|
||||||
|
globalns: Dict[str, Any]) -> Any:
|
||||||
|
try:
|
||||||
|
if isinstance(param.annotation, str):
|
||||||
|
return _eval_type(ForwardRef(param.annotation), globalns,
|
||||||
|
globalns)
|
||||||
|
else:
|
||||||
|
return param.annotation
|
||||||
|
except Exception:
|
||||||
|
return param.annotation
|
||||||
|
|
||||||
|
def _check_params(self, signature: inspect.Signature):
|
||||||
|
if not set(signature.parameters.keys()) <= {
|
||||||
|
"bot", "event", "state", "matcher"
|
||||||
|
}:
|
||||||
|
raise ValueError(
|
||||||
|
"Handler param names must in `bot`/`event`/`state`/`matcher`")
|
||||||
|
|
||||||
|
def _check_bot_param(self, signature: inspect.Signature):
|
||||||
|
if not any(
|
||||||
|
param.name == "bot" for param in signature.parameters.values()):
|
||||||
|
raise ValueError("Handler missing parameter 'bot'")
|
@ -77,7 +77,7 @@ default_format = (
|
|||||||
"<c><u>{name}</u></c> | "
|
"<c><u>{name}</u></c> | "
|
||||||
# "<c>{function}:{line}</c>| "
|
# "<c>{function}:{line}</c>| "
|
||||||
"{message}")
|
"{message}")
|
||||||
logger.add(sys.stdout,
|
logger_id = logger.add(sys.stdout,
|
||||||
colorize=True,
|
colorize=True,
|
||||||
diagnose=False,
|
diagnose=False,
|
||||||
filter=default_filter,
|
filter=default_filter,
|
||||||
|
@ -5,18 +5,21 @@
|
|||||||
该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行对话 。
|
该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行对话 。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import inspect
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Type, List, Dict, Union, Mapping, Iterable, Callable, Optional, NoReturn, TYPE_CHECKING
|
from typing import (Any, Type, List, Dict, Union, Mapping, Iterable, Callable,
|
||||||
|
Optional, NoReturn, TYPE_CHECKING)
|
||||||
|
|
||||||
from nonebot.rule import Rule
|
from nonebot.rule import Rule
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
|
from nonebot.handler import Handler
|
||||||
from nonebot.permission import Permission, USER
|
from nonebot.permission import Permission, USER
|
||||||
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_ArgsParser, T_TypeUpdater, T_PermissionUpdater
|
from nonebot.typing import (T_State, T_StateFactory, T_Handler, T_ArgsParser,
|
||||||
from nonebot.exception import PausedException, RejectedException, FinishedException, StopPropagation
|
T_TypeUpdater, T_PermissionUpdater)
|
||||||
|
from nonebot.exception import (PausedException, RejectedException,
|
||||||
|
FinishedException, StopPropagation)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from nonebot.adapters import Bot, Event, Message, MessageSegment
|
from nonebot.adapters import Bot, Event, Message, MessageSegment
|
||||||
@ -31,11 +34,21 @@ current_event: ContextVar = ContextVar("current_event")
|
|||||||
|
|
||||||
|
|
||||||
class MatcherMeta(type):
|
class MatcherMeta(type):
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
module: Optional[str]
|
||||||
|
type: str
|
||||||
|
rule: Rule
|
||||||
|
permission: Permission
|
||||||
|
handlers: List[T_Handler]
|
||||||
|
priority: int
|
||||||
|
block: bool
|
||||||
|
temp: bool
|
||||||
|
expire_time: Optional[datetime]
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (f"<Matcher from {self.module or 'unknow'}, " # type: ignore
|
return (f"<Matcher from {self.module or 'unknow'}, "
|
||||||
f"type={self.type}, priority={self.priority}, " # type: ignore
|
f"type={self.type}, priority={self.priority}, "
|
||||||
f"temp={self.temp}>") # type: ignore
|
f"temp={self.temp}>")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return repr(self)
|
return repr(self)
|
||||||
@ -64,9 +77,9 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
:类型: ``Permission``
|
:类型: ``Permission``
|
||||||
:说明: 事件响应器触发权限
|
:说明: 事件响应器触发权限
|
||||||
"""
|
"""
|
||||||
handlers: List[T_Handler] = []
|
handlers: List[Handler] = []
|
||||||
"""
|
"""
|
||||||
:类型: ``List[T_Handler]``
|
:类型: ``List[Handler]``
|
||||||
:说明: 事件响应器拥有的事件处理函数列表
|
:说明: 事件响应器拥有的事件处理函数列表
|
||||||
"""
|
"""
|
||||||
priority: int = 1
|
priority: int = 1
|
||||||
@ -108,12 +121,12 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
"""
|
"""
|
||||||
_default_type_updater: Optional[T_TypeUpdater] = None
|
_default_type_updater: Optional[T_TypeUpdater] = None
|
||||||
"""
|
"""
|
||||||
:类型: ``Optional[T_ArgsParser]``
|
:类型: ``Optional[T_TypeUpdater]``
|
||||||
:说明: 事件响应器类型更新函数
|
:说明: 事件响应器类型更新函数
|
||||||
"""
|
"""
|
||||||
_default_permission_updater: Optional[T_PermissionUpdater] = None
|
_default_permission_updater: Optional[T_PermissionUpdater] = None
|
||||||
"""
|
"""
|
||||||
:类型: ``Optional[T_ArgsParser]``
|
:类型: ``Optional[T_PermissionUpdater]``
|
||||||
:说明: 事件响应器权限更新函数
|
:说明: 事件响应器权限更新函数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -127,14 +140,15 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
f"priority={self.priority}, temp={self.temp}>")
|
f"priority={self.priority}, temp={self.temp}>")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.__repr__()
|
return repr(self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new(cls,
|
def new(cls,
|
||||||
type_: str = "",
|
type_: str = "",
|
||||||
rule: Optional[Rule] = None,
|
rule: Optional[Rule] = None,
|
||||||
permission: Optional[Permission] = None,
|
permission: Optional[Permission] = None,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[Union[List[T_Handler], List[Handler],
|
||||||
|
List[Union[T_Handler, Handler]]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -178,7 +192,9 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
"permission":
|
"permission":
|
||||||
permission or Permission(),
|
permission or Permission(),
|
||||||
"handlers": [
|
"handlers": [
|
||||||
cls.process_handler(handler) for handler in handlers
|
handler
|
||||||
|
if isinstance(handler, Handler) else Handler(handler)
|
||||||
|
for handler in handlers
|
||||||
] if handlers else [],
|
] if handlers else [],
|
||||||
"temp":
|
"temp":
|
||||||
temp,
|
temp,
|
||||||
@ -284,27 +300,11 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
cls._default_permission_updater = func
|
cls._default_permission_updater = func
|
||||||
return func
|
return func
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_handler(handler: T_Handler) -> T_Handler:
|
|
||||||
signature = inspect.signature(handler, follow_wrapped=False)
|
|
||||||
bot = signature.parameters.get("bot")
|
|
||||||
event = signature.parameters.get("event")
|
|
||||||
state = signature.parameters.get("state")
|
|
||||||
matcher = signature.parameters.get("matcher")
|
|
||||||
if not bot:
|
|
||||||
raise ValueError("Handler missing parameter 'bot'")
|
|
||||||
handler.__params__ = {
|
|
||||||
"bot": bot.annotation,
|
|
||||||
"event": event.annotation if event else None,
|
|
||||||
"state": T_State if state else None,
|
|
||||||
"matcher": matcher.annotation if matcher else None
|
|
||||||
}
|
|
||||||
return handler
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def append_handler(cls, handler: T_Handler) -> None:
|
def append_handler(cls, handler: T_Handler) -> Handler:
|
||||||
# Process handler first
|
handler_ = Handler(handler)
|
||||||
cls.handlers.append(cls.process_handler(handler))
|
cls.handlers.append(handler_)
|
||||||
|
return handler_
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle(cls) -> Callable[[T_Handler], T_Handler]:
|
def handle(cls) -> Callable[[T_Handler], T_Handler]:
|
||||||
@ -339,23 +339,19 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
async def _receive(bot: "Bot", event: "Event") -> NoReturn:
|
async def _receive(bot: "Bot", event: "Event") -> NoReturn:
|
||||||
raise PausedException
|
raise PausedException
|
||||||
|
|
||||||
cls.process_handler(_receive)
|
|
||||||
|
|
||||||
if cls.handlers:
|
if cls.handlers:
|
||||||
# 已有前置handlers则接受一条新的消息,否则视为接收初始消息
|
# 已有前置handlers则接受一条新的消息,否则视为接收初始消息
|
||||||
cls.append_handler(_receive)
|
receive_handler = cls.append_handler(_receive)
|
||||||
|
else:
|
||||||
|
receive_handler = None
|
||||||
|
|
||||||
def _decorator(func: T_Handler) -> T_Handler:
|
def _decorator(func: T_Handler) -> T_Handler:
|
||||||
cls.process_handler(func)
|
|
||||||
if not cls.handlers or cls.handlers[-1] is not func:
|
if not cls.handlers or cls.handlers[-1] is not func:
|
||||||
cls.append_handler(func)
|
func_handler = cls.append_handler(func)
|
||||||
|
if receive_handler:
|
||||||
_receive.__params__.update({
|
receive_handler.update_signature(
|
||||||
"bot":
|
bot=func_handler.bot_type,
|
||||||
func.__params__["bot"],
|
event=func_handler.event_type)
|
||||||
"event":
|
|
||||||
func.__params__["event"] or _receive.__params__["event"]
|
|
||||||
})
|
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
@ -416,42 +412,27 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
else:
|
else:
|
||||||
state[state["_current_key"]] = str(event.get_message())
|
state[state["_current_key"]] = str(event.get_message())
|
||||||
|
|
||||||
cls.append_handler(_key_getter)
|
getter_handler = cls.append_handler(_key_getter)
|
||||||
cls.append_handler(_key_parser)
|
parser_handler = cls.append_handler(_key_parser)
|
||||||
|
|
||||||
def _decorator(func: T_Handler) -> T_Handler:
|
def _decorator(func: T_Handler) -> T_Handler:
|
||||||
if not hasattr(cls.handlers[-1], "__wrapped__"):
|
if not hasattr(cls.handlers[-1], "__wrapped__"):
|
||||||
cls.process_handler(func)
|
|
||||||
parser = cls.handlers.pop()
|
parser = cls.handlers.pop()
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
async def wrapper(bot: "Bot", event: "Event", state: T_State,
|
async def wrapper(bot: "Bot", event: "Event", state: T_State,
|
||||||
matcher: Matcher):
|
matcher: Matcher):
|
||||||
await matcher.run_handler(parser, bot, event, state)
|
await parser(matcher, bot, event, state)
|
||||||
await matcher.run_handler(func, bot, event, state)
|
await func_handler(matcher, bot, event, state)
|
||||||
if "_current_key" in state:
|
if "_current_key" in state:
|
||||||
del state["_current_key"]
|
del state["_current_key"]
|
||||||
|
|
||||||
cls.append_handler(wrapper)
|
func_handler = cls.append_handler(wrapper)
|
||||||
|
|
||||||
wrapper.__params__.update({
|
getter_handler.update_signature(bot=func_handler.bot_type,
|
||||||
"bot":
|
event=func_handler.event_type)
|
||||||
func.__params__["bot"],
|
parser_handler.update_signature(bot=func_handler.bot_type,
|
||||||
"event":
|
event=func_handler.event_type)
|
||||||
func.__params__["event"] or wrapper.__params__["event"]
|
|
||||||
})
|
|
||||||
_key_getter.__params__.update({
|
|
||||||
"bot":
|
|
||||||
func.__params__["bot"],
|
|
||||||
"event":
|
|
||||||
func.__params__["event"] or wrapper.__params__["event"]
|
|
||||||
})
|
|
||||||
_key_parser.__params__.update({
|
|
||||||
"bot":
|
|
||||||
func.__params__["bot"],
|
|
||||||
"event":
|
|
||||||
func.__params__["event"] or wrapper.__params__["event"]
|
|
||||||
})
|
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
@ -459,7 +440,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def send(cls, message: Union[str, "Message", "MessageSegment"],
|
async def send(cls, message: Union[str, "Message", "MessageSegment"],
|
||||||
**kwargs):
|
**kwargs) -> Any:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -470,9 +451,9 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
* ``message: Union[str, Message, MessageSegment]``: 消息内容
|
* ``message: Union[str, Message, MessageSegment]``: 消息内容
|
||||||
* ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api
|
* ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api
|
||||||
"""
|
"""
|
||||||
bot = current_bot.get()
|
bot: "Bot" = current_bot.get()
|
||||||
event = current_event.get()
|
event = current_event.get()
|
||||||
await bot.send(event=event, message=message, **kwargs)
|
return await bot.send(event=event, message=message, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def finish(cls,
|
async def finish(cls,
|
||||||
@ -545,32 +526,6 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
"""
|
"""
|
||||||
self.block = True
|
self.block = True
|
||||||
|
|
||||||
async def run_handler(self, handler: T_Handler, bot: "Bot", event: "Event",
|
|
||||||
state: T_State):
|
|
||||||
if not hasattr(handler, "__params__"):
|
|
||||||
self.process_handler(handler)
|
|
||||||
params = getattr(handler, "__params__")
|
|
||||||
|
|
||||||
BotType = ((params["bot"] is not inspect.Parameter.empty) and
|
|
||||||
inspect.isclass(params["bot"]) and params["bot"])
|
|
||||||
if BotType and not isinstance(bot, BotType):
|
|
||||||
logger.debug(
|
|
||||||
f"Matcher {self} bot type {type(bot)} not match annotation {BotType}, ignored"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
EventType = ((params["event"] is not inspect.Parameter.empty) and
|
|
||||||
inspect.isclass(params["event"]) and params["event"])
|
|
||||||
if EventType and not isinstance(event, EventType):
|
|
||||||
logger.debug(
|
|
||||||
f"Matcher {self} event type {type(event)} not match annotation {EventType}, ignored"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
args = {"bot": bot, "event": event, "state": state, "matcher": self}
|
|
||||||
await handler(
|
|
||||||
**{k: v for k, v in args.items() if params[k] is not None})
|
|
||||||
|
|
||||||
# 运行handlers
|
# 运行handlers
|
||||||
async def run(self, bot: "Bot", event: "Event", state: T_State):
|
async def run(self, bot: "Bot", event: "Event", state: T_State):
|
||||||
b_t = current_bot.set(bot)
|
b_t = current_bot.set(bot)
|
||||||
@ -583,20 +538,22 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
for _ in range(len(self.handlers)):
|
for _ in range(len(self.handlers)):
|
||||||
handler = self.handlers.pop(0)
|
handler = self.handlers.pop(0)
|
||||||
await self.run_handler(handler, bot, event, state_)
|
await handler(self, bot, event, state_)
|
||||||
|
|
||||||
except RejectedException:
|
except RejectedException:
|
||||||
self.handlers.insert(0, handler) # type: ignore
|
self.handlers.insert(0, handler) # type: ignore
|
||||||
if self._default_type_updater:
|
updater = self.__class__._default_type_updater
|
||||||
type_ = await self._default_type_updater(
|
if updater:
|
||||||
bot, event, state, self.type)
|
type_ = await updater(bot, event, state, self.type)
|
||||||
else:
|
else:
|
||||||
type_ = "message"
|
type_ = "message"
|
||||||
if self._default_permission_updater:
|
|
||||||
permission = await self._default_permission_updater(
|
updater = self.__class__._default_permission_updater
|
||||||
bot, event, state, self.permission)
|
if updater:
|
||||||
|
permission = await updater(bot, event, state, self.permission)
|
||||||
else:
|
else:
|
||||||
permission = USER(event.get_session_id(), perm=self.permission)
|
permission = USER(event.get_session_id(), perm=self.permission)
|
||||||
|
|
||||||
Matcher.new(type_,
|
Matcher.new(type_,
|
||||||
Rule(),
|
Rule(),
|
||||||
permission,
|
permission,
|
||||||
@ -609,16 +566,18 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
expire_time=datetime.now() +
|
expire_time=datetime.now() +
|
||||||
bot.config.session_expire_timeout)
|
bot.config.session_expire_timeout)
|
||||||
except PausedException:
|
except PausedException:
|
||||||
if self._default_type_updater:
|
updater = self.__class__._default_type_updater
|
||||||
type_ = await self._default_type_updater(
|
if updater:
|
||||||
bot, event, state, self.type)
|
type_ = await updater(bot, event, state, self.type)
|
||||||
else:
|
else:
|
||||||
type_ = "message"
|
type_ = "message"
|
||||||
if self._default_permission_updater:
|
|
||||||
permission = await self._default_permission_updater(
|
updater = self.__class__._default_permission_updater
|
||||||
bot, event, state, self.permission)
|
if updater:
|
||||||
|
permission = await updater(bot, event, state, self.permission)
|
||||||
else:
|
else:
|
||||||
permission = USER(event.get_session_id(), perm=self.permission)
|
permission = USER(event.get_session_id(), perm=self.permission)
|
||||||
|
|
||||||
Matcher.new(type_,
|
Matcher.new(type_,
|
||||||
Rule(),
|
Rule(),
|
||||||
permission,
|
permission,
|
||||||
|
@ -205,6 +205,7 @@ async def handle_event(bot: "Bot", event: "Event"):
|
|||||||
coros = list(map(lambda x: x(bot, event, state), _event_preprocessors))
|
coros = list(map(lambda x: x(bot, event, state), _event_preprocessors))
|
||||||
if coros:
|
if coros:
|
||||||
try:
|
try:
|
||||||
|
if show_log:
|
||||||
logger.debug("Running PreProcessors...")
|
logger.debug("Running PreProcessors...")
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
except IgnoredException:
|
except IgnoredException:
|
||||||
@ -240,10 +241,15 @@ async def handle_event(bot: "Bot", event: "Event"):
|
|||||||
if not break_flag:
|
if not break_flag:
|
||||||
break_flag = True
|
break_flag = True
|
||||||
logger.debug("Stop event propagation")
|
logger.debug("Stop event propagation")
|
||||||
|
elif isinstance(result, Exception):
|
||||||
|
logger.opt(colors=True, exception=result).error(
|
||||||
|
"<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>"
|
||||||
|
)
|
||||||
|
|
||||||
coros = list(map(lambda x: x(bot, event, state), _event_postprocessors))
|
coros = list(map(lambda x: x(bot, event, state), _event_postprocessors))
|
||||||
if coros:
|
if coros:
|
||||||
try:
|
try:
|
||||||
|
if show_log:
|
||||||
logger.debug("Running PostProcessors...")
|
logger.debug("Running PostProcessors...")
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -8,17 +8,20 @@ import re
|
|||||||
import json
|
import json
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from collections import defaultdict
|
||||||
from contextvars import Context, ContextVar, copy_context
|
from contextvars import Context, ContextVar, copy_context
|
||||||
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional, TYPE_CHECKING
|
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import tomlkit
|
import tomlkit
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
|
from nonebot.handler import Handler
|
||||||
from nonebot.permission import Permission
|
from nonebot.permission import Permission
|
||||||
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_RuleChecker
|
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_RuleChecker
|
||||||
from nonebot.rule import Rule, startswith, endswith, keyword, command, shell_command, ArgumentParser, regex
|
from nonebot.rule import Rule, startswith, endswith, keyword, command, shell_command, ArgumentParser, regex
|
||||||
|
|
||||||
from .manager import PluginManager
|
from .export import Export, export, _export
|
||||||
|
from .manager import PluginManager, _current_plugin
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from nonebot.adapters import Bot, Event
|
from nonebot.adapters import Bot, Event
|
||||||
@ -30,52 +33,7 @@ plugins: Dict[str, "Plugin"] = {}
|
|||||||
"""
|
"""
|
||||||
PLUGIN_NAMESPACE = "nonebot.loaded_plugins"
|
PLUGIN_NAMESPACE = "nonebot.loaded_plugins"
|
||||||
|
|
||||||
_export: ContextVar["Export"] = ContextVar("_export")
|
_plugin_matchers: Dict[str, Set[Type[Matcher]]] = defaultdict(set)
|
||||||
_tmp_matchers: ContextVar[Set[Type[Matcher]]] = ContextVar("_tmp_matchers")
|
|
||||||
|
|
||||||
|
|
||||||
class Export(dict):
|
|
||||||
"""
|
|
||||||
:说明:
|
|
||||||
|
|
||||||
插件导出内容以使得其他插件可以获得。
|
|
||||||
|
|
||||||
:示例:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
nonebot.export().default = "bar"
|
|
||||||
|
|
||||||
@nonebot.export()
|
|
||||||
def some_function():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# this doesn't work before python 3.9
|
|
||||||
# use
|
|
||||||
# export = nonebot.export(); @export.sub
|
|
||||||
# instead
|
|
||||||
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
|
||||||
@nonebot.export().sub
|
|
||||||
def something_else():
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __call__(self, func, **kwargs):
|
|
||||||
self[func.__name__] = func
|
|
||||||
self.update(kwargs)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
super().__setitem__(key,
|
|
||||||
Export(value) if isinstance(value, dict) else value)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
self[name] = Export(value) if isinstance(value, dict) else value
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name not in self:
|
|
||||||
self[name] = Export()
|
|
||||||
return self[name]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(eq=False)
|
@dataclass(eq=False)
|
||||||
@ -91,23 +49,31 @@ class Plugin(object):
|
|||||||
- **类型**: ``ModuleType``
|
- **类型**: ``ModuleType``
|
||||||
- **说明**: 插件模块对象
|
- **说明**: 插件模块对象
|
||||||
"""
|
"""
|
||||||
matcher: Set[Type[Matcher]]
|
|
||||||
"""
|
|
||||||
- **类型**: ``Set[Type[Matcher]]``
|
|
||||||
- **说明**: 插件内定义的 ``Matcher``
|
|
||||||
"""
|
|
||||||
export: Export
|
export: Export
|
||||||
"""
|
"""
|
||||||
- **类型**: ``Export``
|
- **类型**: ``Export``
|
||||||
- **说明**: 插件内定义的导出内容
|
- **说明**: 插件内定义的导出内容
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def matcher(self) -> Set[Type[Matcher]]:
|
||||||
|
"""
|
||||||
|
- **类型**: ``Set[Type[Matcher]]``
|
||||||
|
- **说明**: 插件内定义的 ``Matcher``
|
||||||
|
"""
|
||||||
|
return _plugin_matchers[self.name]
|
||||||
|
|
||||||
|
|
||||||
|
def _store_matcher(matcher: Type[Matcher]):
|
||||||
|
plugin_name = matcher.module.split(".", maxsplit=1)[0]
|
||||||
|
_plugin_matchers[plugin_name].add(matcher)
|
||||||
|
|
||||||
|
|
||||||
def on(type: str = "",
|
def on(type: str = "",
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
permission: Optional[Permission] = None,
|
permission: Optional[Permission] = None,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -123,7 +89,7 @@ def on(type: str = "",
|
|||||||
* ``type: str``: 事件响应器类型
|
* ``type: str``: 事件响应器类型
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -141,16 +107,17 @@ def on(type: str = "",
|
|||||||
priority=priority,
|
priority=priority,
|
||||||
block=block,
|
block=block,
|
||||||
handlers=handlers,
|
handlers=handlers,
|
||||||
|
module=_current_plugin.get(),
|
||||||
default_state=state,
|
default_state=state,
|
||||||
default_state_factory=state_factory)
|
default_state_factory=state_factory)
|
||||||
_tmp_matchers.get().add(matcher)
|
_store_matcher(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
def on_metaevent(
|
def on_metaevent(
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -164,7 +131,7 @@ def on_metaevent(
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -182,16 +149,17 @@ def on_metaevent(
|
|||||||
priority=priority,
|
priority=priority,
|
||||||
block=block,
|
block=block,
|
||||||
handlers=handlers,
|
handlers=handlers,
|
||||||
|
module=_current_plugin.get(),
|
||||||
default_state=state,
|
default_state=state,
|
||||||
default_state_factory=state_factory)
|
default_state_factory=state_factory)
|
||||||
_tmp_matchers.get().add(matcher)
|
_store_matcher(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
permission: Optional[Permission] = None,
|
permission: Optional[Permission] = None,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = True,
|
block: bool = True,
|
||||||
@ -206,7 +174,7 @@ def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -224,15 +192,16 @@ def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
priority=priority,
|
priority=priority,
|
||||||
block=block,
|
block=block,
|
||||||
handlers=handlers,
|
handlers=handlers,
|
||||||
|
module=_current_plugin.get(),
|
||||||
default_state=state,
|
default_state=state,
|
||||||
default_state_factory=state_factory)
|
default_state_factory=state_factory)
|
||||||
_tmp_matchers.get().add(matcher)
|
_store_matcher(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -246,7 +215,7 @@ def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -264,15 +233,16 @@ def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
priority=priority,
|
priority=priority,
|
||||||
block=block,
|
block=block,
|
||||||
handlers=handlers,
|
handlers=handlers,
|
||||||
|
module=_current_plugin.get(),
|
||||||
default_state=state,
|
default_state=state,
|
||||||
default_state_factory=state_factory)
|
default_state_factory=state_factory)
|
||||||
_tmp_matchers.get().add(matcher)
|
_store_matcher(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -286,7 +256,7 @@ def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -304,9 +274,10 @@ def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
|||||||
priority=priority,
|
priority=priority,
|
||||||
block=block,
|
block=block,
|
||||||
handlers=handlers,
|
handlers=handlers,
|
||||||
|
module=_current_plugin.get(),
|
||||||
default_state=state,
|
default_state=state,
|
||||||
default_state_factory=state_factory)
|
default_state_factory=state_factory)
|
||||||
_tmp_matchers.get().add(matcher)
|
_store_matcher(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
@ -323,7 +294,7 @@ def on_startswith(msg: str,
|
|||||||
* ``msg: str``: 指定消息开头内容
|
* ``msg: str``: 指定消息开头内容
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -350,7 +321,7 @@ def on_endswith(msg: str,
|
|||||||
* ``msg: str``: 指定消息结尾内容
|
* ``msg: str``: 指定消息结尾内容
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -377,7 +348,7 @@ def on_keyword(keywords: Set[str],
|
|||||||
* ``keywords: Set[str]``: 关键词列表
|
* ``keywords: Set[str]``: 关键词列表
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -408,7 +379,7 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
|
|||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -457,7 +428,7 @@ def on_shell_command(cmd: Union[str, Tuple[str, ...]],
|
|||||||
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
||||||
* ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
|
* ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -489,7 +460,7 @@ def on_shell_command(cmd: Union[str, Tuple[str, ...]],
|
|||||||
|
|
||||||
def on_regex(pattern: str,
|
def on_regex(pattern: str,
|
||||||
flags: Union[int, re.RegexFlag] = 0,
|
flags: Union[int, re.RegexFlag] = 0,
|
||||||
rule: Optional[Rule] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
**kwargs) -> Type[Matcher]:
|
**kwargs) -> Type[Matcher]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
@ -504,7 +475,7 @@ def on_regex(pattern: str,
|
|||||||
* ``flags: Union[int, re.RegexFlag]``: 正则匹配标志
|
* ``flags: Union[int, re.RegexFlag]``: 正则匹配标志
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -619,7 +590,7 @@ class MatcherGroup:
|
|||||||
* ``type: str``: 事件响应器类型
|
* ``type: str``: 事件响应器类型
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -645,7 +616,7 @@ class MatcherGroup:
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -673,7 +644,7 @@ class MatcherGroup:
|
|||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -700,7 +671,7 @@ class MatcherGroup:
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -727,7 +698,7 @@ class MatcherGroup:
|
|||||||
:参数:
|
:参数:
|
||||||
|
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -756,7 +727,7 @@ class MatcherGroup:
|
|||||||
* ``msg: str``: 指定消息开头内容
|
* ``msg: str``: 指定消息开头内容
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -785,7 +756,7 @@ class MatcherGroup:
|
|||||||
* ``msg: str``: 指定消息结尾内容
|
* ``msg: str``: 指定消息结尾内容
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -814,7 +785,7 @@ class MatcherGroup:
|
|||||||
* ``keywords: Set[str]``: 关键词列表
|
* ``keywords: Set[str]``: 关键词列表
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -849,7 +820,7 @@ class MatcherGroup:
|
|||||||
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -889,7 +860,7 @@ class MatcherGroup:
|
|||||||
* ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
|
* ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -927,7 +898,7 @@ class MatcherGroup:
|
|||||||
* ``flags: Union[int, re.RegexFlag]``: 正则匹配标志
|
* ``flags: Union[int, re.RegexFlag]``: 正则匹配标志
|
||||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||||
* ``permission: Optional[Permission]``: 事件响应权限
|
* ``permission: Optional[Permission]``: 事件响应权限
|
||||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
* ``handlers: Optional[List[Union[T_Handler, Handler]]]``: 事件处理函数列表
|
||||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||||
* ``priority: int``: 事件响应器优先级
|
* ``priority: int``: 事件响应器优先级
|
||||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||||
@ -950,18 +921,14 @@ def _load_plugin(manager: PluginManager, plugin_name: str) -> Optional[Plugin]:
|
|||||||
if plugin_name.startswith("_"):
|
if plugin_name.startswith("_"):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
_tmp_matchers.set(set())
|
|
||||||
_export.set(Export())
|
|
||||||
|
|
||||||
if plugin_name in plugins:
|
if plugin_name in plugins:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = manager.load_plugin(plugin_name)
|
module = manager.load_plugin(plugin_name)
|
||||||
|
|
||||||
for m in _tmp_matchers.get():
|
plugin = Plugin(plugin_name, module,
|
||||||
m.module = plugin_name
|
getattr(module, "__export__", Export()))
|
||||||
plugin = Plugin(plugin_name, module, _tmp_matchers.get(), _export.get())
|
|
||||||
plugins[plugin_name] = plugin
|
plugins[plugin_name] = plugin
|
||||||
logger.opt(
|
logger.opt(
|
||||||
colors=True).info(f'Succeeded to import "<y>{plugin_name}</y>"')
|
colors=True).info(f'Succeeded to import "<y>{plugin_name}</y>"')
|
||||||
@ -1032,39 +999,11 @@ def load_all_plugins(module_path: Set[str],
|
|||||||
|
|
||||||
- ``Set[Plugin]``
|
- ``Set[Plugin]``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _load_plugin(plugin_name: str) -> Optional[Plugin]:
|
|
||||||
if plugin_name.startswith("_"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
_tmp_matchers.set(set())
|
|
||||||
_export.set(Export())
|
|
||||||
|
|
||||||
if plugin_name in plugins:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
module = manager.load_plugin(plugin_name)
|
|
||||||
|
|
||||||
for m in _tmp_matchers.get():
|
|
||||||
m.module = plugin_name
|
|
||||||
plugin = Plugin(plugin_name, module, _tmp_matchers.get(),
|
|
||||||
_export.get())
|
|
||||||
plugins[plugin_name] = plugin
|
|
||||||
logger.opt(
|
|
||||||
colors=True).info(f'Succeeded to import "<y>{plugin_name}</y>"')
|
|
||||||
return plugin
|
|
||||||
except Exception as e:
|
|
||||||
logger.opt(colors=True, exception=e).error(
|
|
||||||
f'<r><bg #f8bbd0>Failed to import "{plugin_name}"</bg #f8bbd0></r>'
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
|
|
||||||
loaded_plugins = set()
|
loaded_plugins = set()
|
||||||
manager = PluginManager(PLUGIN_NAMESPACE, module_path, plugin_dir)
|
manager = PluginManager(PLUGIN_NAMESPACE, module_path, plugin_dir)
|
||||||
for plugin_name in manager.list_plugins():
|
for plugin_name in manager.list_plugins():
|
||||||
context: Context = copy_context()
|
context: Context = copy_context()
|
||||||
result = context.run(_load_plugin, plugin_name)
|
result = context.run(_load_plugin, manager, plugin_name)
|
||||||
if result:
|
if result:
|
||||||
loaded_plugins.add(result)
|
loaded_plugins.add(result)
|
||||||
return loaded_plugins
|
return loaded_plugins
|
||||||
@ -1168,19 +1107,6 @@ def get_loaded_plugins() -> Set[Plugin]:
|
|||||||
return set(plugins.values())
|
return set(plugins.values())
|
||||||
|
|
||||||
|
|
||||||
def export() -> Export:
|
|
||||||
"""
|
|
||||||
:说明:
|
|
||||||
|
|
||||||
获取插件的导出内容对象
|
|
||||||
|
|
||||||
:返回:
|
|
||||||
|
|
||||||
- ``Export``
|
|
||||||
"""
|
|
||||||
return _export.get()
|
|
||||||
|
|
||||||
|
|
||||||
def require(name: str) -> Optional[Export]:
|
def require(name: str) -> Optional[Export]:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
@ -4,6 +4,7 @@ from contextvars import ContextVar
|
|||||||
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional
|
from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional
|
||||||
|
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
|
from nonebot.handler import Handler
|
||||||
from nonebot.permission import Permission
|
from nonebot.permission import Permission
|
||||||
from nonebot.rule import Rule, ArgumentParser
|
from nonebot.rule import Rule, ArgumentParser
|
||||||
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_RuleChecker
|
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_RuleChecker
|
||||||
@ -37,7 +38,7 @@ def on(type: str = ...,
|
|||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -49,7 +50,7 @@ def on(type: str = ...,
|
|||||||
def on_metaevent(
|
def on_metaevent(
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -61,7 +62,7 @@ def on_metaevent(
|
|||||||
def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -72,7 +73,7 @@ def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
|||||||
|
|
||||||
def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -83,7 +84,7 @@ def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
|||||||
|
|
||||||
def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -97,7 +98,7 @@ def on_startswith(
|
|||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -110,7 +111,7 @@ def on_endswith(msg: str,
|
|||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -123,7 +124,7 @@ def on_keyword(keywords: Set[str],
|
|||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -137,7 +138,7 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
|
|||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -156,10 +157,10 @@ def on_shell_command(cmd: Union[str, Tuple[str, ...]],
|
|||||||
|
|
||||||
def on_regex(pattern: str,
|
def on_regex(pattern: str,
|
||||||
flags: Union[int, re.RegexFlag] = 0,
|
flags: Union[int, re.RegexFlag] = 0,
|
||||||
rule: Optional[Rule] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
*,
|
*,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -216,7 +217,7 @@ class CommandGroup:
|
|||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
*,
|
*,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -230,7 +231,7 @@ class CommandGroup:
|
|||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -246,7 +247,7 @@ class CommandGroup:
|
|||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||||
parser: Optional[ArgumentParser] = ...,
|
parser: Optional[ArgumentParser] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -262,7 +263,7 @@ class MatcherGroup:
|
|||||||
type: str = ...,
|
type: str = ...,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -274,7 +275,7 @@ class MatcherGroup:
|
|||||||
type: str = ...,
|
type: str = ...,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -286,7 +287,7 @@ class MatcherGroup:
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -299,7 +300,7 @@ class MatcherGroup:
|
|||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
permission: Optional[Permission] = None,
|
permission: Optional[Permission] = None,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = True,
|
block: bool = True,
|
||||||
@ -311,7 +312,7 @@ class MatcherGroup:
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -323,7 +324,7 @@ class MatcherGroup:
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
handlers: Optional[List[T_Handler]] = None,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = None,
|
||||||
temp: bool = False,
|
temp: bool = False,
|
||||||
priority: int = 1,
|
priority: int = 1,
|
||||||
block: bool = False,
|
block: bool = False,
|
||||||
@ -337,7 +338,7 @@ class MatcherGroup:
|
|||||||
*,
|
*,
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -351,7 +352,7 @@ class MatcherGroup:
|
|||||||
*,
|
*,
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -365,7 +366,7 @@ class MatcherGroup:
|
|||||||
*,
|
*,
|
||||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -380,7 +381,7 @@ class MatcherGroup:
|
|||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -396,7 +397,7 @@ class MatcherGroup:
|
|||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
@ -409,9 +410,9 @@ class MatcherGroup:
|
|||||||
pattern: str,
|
pattern: str,
|
||||||
flags: Union[int, re.RegexFlag] = 0,
|
flags: Union[int, re.RegexFlag] = 0,
|
||||||
*,
|
*,
|
||||||
rule: Optional[Rule] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
permission: Optional[Permission] = ...,
|
permission: Optional[Permission] = ...,
|
||||||
handlers: Optional[List[T_Handler]] = ...,
|
handlers: Optional[List[Union[T_Handler, Handler]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
priority: int = ...,
|
priority: int = ...,
|
||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
|
60
nonebot/plugin/export.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from contextvars import ContextVar
|
||||||
|
|
||||||
|
_export: ContextVar["Export"] = ContextVar("_export")
|
||||||
|
|
||||||
|
|
||||||
|
class Export(dict):
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
插件导出内容以使得其他插件可以获得。
|
||||||
|
|
||||||
|
:示例:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
nonebot.export().default = "bar"
|
||||||
|
|
||||||
|
@nonebot.export()
|
||||||
|
def some_function():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# this doesn't work before python 3.9
|
||||||
|
# use
|
||||||
|
# export = nonebot.export(); @export.sub
|
||||||
|
# instead
|
||||||
|
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
||||||
|
@nonebot.export().sub
|
||||||
|
def something_else():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __call__(self, func, **kwargs):
|
||||||
|
self[func.__name__] = func
|
||||||
|
self.update(kwargs)
|
||||||
|
return func
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
super().__setitem__(key,
|
||||||
|
Export(value) if isinstance(value, dict) else value)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
self[name] = Export(value) if isinstance(value, dict) else value
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name not in self:
|
||||||
|
self[name] = Export()
|
||||||
|
return self[name]
|
||||||
|
|
||||||
|
|
||||||
|
def export() -> Export:
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
获取插件的导出内容对象
|
||||||
|
|
||||||
|
:返回:
|
||||||
|
|
||||||
|
- ``Export``
|
||||||
|
"""
|
||||||
|
return _export.get()
|
@ -5,9 +5,15 @@ import importlib
|
|||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
from contextvars import ContextVar
|
||||||
from importlib.abc import MetaPathFinder
|
from importlib.abc import MetaPathFinder
|
||||||
from importlib.machinery import PathFinder
|
|
||||||
from typing import Set, List, Optional, Iterable
|
from typing import Set, List, Optional, Iterable
|
||||||
|
from importlib.machinery import PathFinder, SourceFileLoader
|
||||||
|
|
||||||
|
from .export import _export, Export
|
||||||
|
|
||||||
|
_current_plugin: ContextVar[Optional[str]] = ContextVar("_current_plugin",
|
||||||
|
default=None)
|
||||||
|
|
||||||
_internal_space = ModuleType(__name__ + "._internal")
|
_internal_space = ModuleType(__name__ + "._internal")
|
||||||
_internal_space.__path__ = [] # type: ignore
|
_internal_space.__path__ = [] # type: ignore
|
||||||
@ -138,10 +144,12 @@ class PluginManager:
|
|||||||
|
|
||||||
def load_plugin(self, name) -> ModuleType:
|
def load_plugin(self, name) -> ModuleType:
|
||||||
if name in self.plugins:
|
if name in self.plugins:
|
||||||
|
with self:
|
||||||
return importlib.import_module(name)
|
return importlib.import_module(name)
|
||||||
|
|
||||||
if "." in name:
|
if "." in name:
|
||||||
raise ValueError("Plugin name cannot contain '.'")
|
raise ValueError("Plugin name cannot contain '.'")
|
||||||
|
|
||||||
with self:
|
with self:
|
||||||
return importlib.import_module(f"{self.namespace}.{name}")
|
return importlib.import_module(f"{self.namespace}.{name}")
|
||||||
|
|
||||||
@ -149,14 +157,15 @@ class PluginManager:
|
|||||||
return [self.load_plugin(name) for name in self.list_plugins()]
|
return [self.load_plugin(name) for name in self.list_plugins()]
|
||||||
|
|
||||||
def _rewrite_module_name(self, module_name) -> Optional[str]:
|
def _rewrite_module_name(self, module_name) -> Optional[str]:
|
||||||
if module_name == self.namespace:
|
prefix = f"{self.internal_module.__name__}."
|
||||||
return self.internal_module.__name__
|
if module_name.startswith(self.namespace + "."):
|
||||||
elif module_name.startswith(self.namespace + "."):
|
|
||||||
path = module_name.split(".")
|
path = module_name.split(".")
|
||||||
length = self.namespace.count(".") + 1
|
length = self.namespace.count(".") + 1
|
||||||
return f"{self.internal_module.__name__}.{'.'.join(path[length:])}"
|
return f"{prefix}{'.'.join(path[length:])}"
|
||||||
|
elif module_name in self.plugins or module_name.startswith(prefix):
|
||||||
|
return module_name
|
||||||
elif module_name in self.search_plugins():
|
elif module_name in self.search_plugins():
|
||||||
return f"{self.internal_module.__name__}.{module_name}"
|
return f"{prefix}{module_name}"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -169,13 +178,52 @@ class PluginFinder(MetaPathFinder):
|
|||||||
manager = _manager_stack[index]
|
manager = _manager_stack[index]
|
||||||
newname = manager._rewrite_module_name(fullname)
|
newname = manager._rewrite_module_name(fullname)
|
||||||
if newname:
|
if newname:
|
||||||
spec = PathFinder.find_spec(newname,
|
spec = PathFinder.find_spec(
|
||||||
list(manager.search_path),
|
newname, [*manager.search_path, *(path or [])], target)
|
||||||
target)
|
|
||||||
if spec:
|
if spec:
|
||||||
|
spec.loader = PluginLoader(manager, newname,
|
||||||
|
spec.origin)
|
||||||
return spec
|
return spec
|
||||||
index -= 1
|
index -= 1
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class PluginLoader(SourceFileLoader):
|
||||||
|
|
||||||
|
def __init__(self, manager: PluginManager, fullname: str, path) -> None:
|
||||||
|
self.manager = manager
|
||||||
|
self.loaded = False
|
||||||
|
self._plugin_token = None
|
||||||
|
self._export_token = None
|
||||||
|
super().__init__(fullname, path)
|
||||||
|
|
||||||
|
def create_module(self, spec) -> Optional[ModuleType]:
|
||||||
|
if self.name in sys.modules:
|
||||||
|
self.loaded = True
|
||||||
|
return sys.modules[self.name]
|
||||||
|
prefix = self.manager.internal_module.__name__
|
||||||
|
plugin_name = self.name[len(prefix):] if self.name.startswith(
|
||||||
|
prefix) else self.name
|
||||||
|
self._plugin_token = _current_plugin.set(plugin_name.lstrip("."))
|
||||||
|
self._export_token = _export.set(Export())
|
||||||
|
# return None to use default module creation
|
||||||
|
return super().create_module(spec)
|
||||||
|
|
||||||
|
def exec_module(self, module: ModuleType) -> None:
|
||||||
|
if self.loaded:
|
||||||
|
return
|
||||||
|
# really need?
|
||||||
|
# setattr(module, "__manager__", self.manager)
|
||||||
|
if self._export_token:
|
||||||
|
setattr(module, "__export__", _export.get())
|
||||||
|
|
||||||
|
super().exec_module(module)
|
||||||
|
|
||||||
|
if self._plugin_token:
|
||||||
|
_current_plugin.reset(self._plugin_token)
|
||||||
|
if self._export_token:
|
||||||
|
_export.reset(self._export_token)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
sys.meta_path.insert(0, PluginFinder())
|
sys.meta_path.insert(0, PluginFinder())
|
||||||
|
@ -17,7 +17,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
|||||||
current_event_id = id(event)
|
current_event_id = id(event)
|
||||||
event_id = _running_matcher.get(session_id, None)
|
event_id = _running_matcher.get(session_id, None)
|
||||||
if event_id and event_id != current_event_id:
|
if event_id and event_id != current_event_id:
|
||||||
raise IgnoredException("Annother matcher running")
|
raise IgnoredException("Another matcher running")
|
||||||
|
|
||||||
_running_matcher[session_id] = current_event_id
|
_running_matcher[session_id] = current_event_id
|
||||||
|
|
||||||
|
@ -287,10 +287,15 @@ class ArgumentParser(ArgParser):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def _print_message(self, message, file=None):
|
def _print_message(self, message, file=None):
|
||||||
pass
|
old_message: str = getattr(self, "message", "")
|
||||||
|
if old_message:
|
||||||
|
old_message += "\n"
|
||||||
|
old_message += message
|
||||||
|
setattr(self, "message", old_message)
|
||||||
|
|
||||||
def exit(self, status=0, message=None):
|
def exit(self, status=0, message=None):
|
||||||
raise ParserExit(status=status, message=message)
|
raise ParserExit(status=status,
|
||||||
|
message=message or getattr(self, "message", None))
|
||||||
|
|
||||||
def parse_args(self,
|
def parse_args(self,
|
||||||
args: Optional[Sequence[str]] = None,
|
args: Optional[Sequence[str]] = None,
|
||||||
|
@ -71,6 +71,7 @@ T_WebSocketDisconnectionHook = Callable[["Bot"], Awaitable[None]]
|
|||||||
|
|
||||||
WebSocket 连接断开时执行的函数
|
WebSocket 连接断开时执行的函数
|
||||||
"""
|
"""
|
||||||
|
T_CallingAPIHook = Callable[["Bot", str, Dict[str, Any]], Awaitable[None]]
|
||||||
|
|
||||||
T_EventPreProcessor = Callable[["Bot", "Event", T_State], Awaitable[None]]
|
T_EventPreProcessor = Callable[["Bot", "Event", T_State], Awaitable[None]]
|
||||||
"""
|
"""
|
||||||
|
@ -52,8 +52,12 @@ async def _check_reply(bot: "Bot", event: "Event"):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
msg_seg = event.message[index]
|
msg_seg = event.message[index]
|
||||||
|
try:
|
||||||
event.reply = Reply.parse_obj(await
|
event.reply = Reply.parse_obj(await
|
||||||
bot.get_msg(message_id=msg_seg.data["id"]))
|
bot.get_msg(message_id=msg_seg.data["id"]
|
||||||
|
))
|
||||||
|
except Exception as e:
|
||||||
|
log("WARNING", f"Error when getting message reply info: {repr(e)}", e)
|
||||||
# ensure string comparation
|
# ensure string comparation
|
||||||
if str(event.reply.sender.user_id) == str(event.self_id):
|
if str(event.reply.sender.user_id) == str(event.self_id):
|
||||||
event.to_me = True
|
event.to_me = True
|
||||||
@ -244,7 +248,7 @@ class Bot(BaseBot):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def check_permission(cls, driver: "Driver", connection_type: str,
|
async def check_permission(cls, driver: "Driver", connection_type: str,
|
||||||
headers: dict, body: Optional[dict]) -> str:
|
headers: dict, body: Optional[bytes]) -> str:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -271,14 +275,13 @@ class Bot(BaseBot):
|
|||||||
if not x_signature:
|
if not x_signature:
|
||||||
log("WARNING", "Missing Signature Header")
|
log("WARNING", "Missing Signature Header")
|
||||||
raise RequestDenied(401, "Missing Signature")
|
raise RequestDenied(401, "Missing Signature")
|
||||||
sig = hmac.new(secret.encode("utf-8"),
|
sig = hmac.new(secret.encode("utf-8"), body, "sha1").hexdigest()
|
||||||
json.dumps(body).encode(), "sha1").hexdigest()
|
|
||||||
if x_signature != "sha1=" + sig:
|
if x_signature != "sha1=" + sig:
|
||||||
log("WARNING", "Signature Header is invalid")
|
log("WARNING", "Signature Header is invalid")
|
||||||
raise RequestDenied(403, "Signature is invalid")
|
raise RequestDenied(403, "Signature is invalid")
|
||||||
|
|
||||||
access_token = cqhttp_config.access_token
|
access_token = cqhttp_config.access_token
|
||||||
if access_token and access_token != token:
|
if access_token and access_token != token and connection_type == "websocket":
|
||||||
log(
|
log(
|
||||||
"WARNING", "Authorization Header is invalid"
|
"WARNING", "Authorization Header is invalid"
|
||||||
if token else "Missing Authorization Header")
|
if token else "Missing Authorization Header")
|
||||||
@ -329,32 +332,7 @@ class Bot(BaseBot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def call_api(self, api: str, **data) -> Any:
|
async def _call_api(self, api: str, **data) -> Any:
|
||||||
"""
|
|
||||||
:说明:
|
|
||||||
|
|
||||||
调用 CQHTTP 协议 API
|
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``api: str``: API 名称
|
|
||||||
* ``**data: Any``: API 参数
|
|
||||||
|
|
||||||
:返回:
|
|
||||||
|
|
||||||
- ``Any``: API 调用返回数据
|
|
||||||
|
|
||||||
:异常:
|
|
||||||
|
|
||||||
- ``NetworkError``: 网络错误
|
|
||||||
- ``ActionFailed``: API 调用失败
|
|
||||||
"""
|
|
||||||
if "self_id" in data:
|
|
||||||
self_id = data.pop("self_id")
|
|
||||||
if self_id:
|
|
||||||
bot = self.driver.bots[str(self_id)]
|
|
||||||
return await bot.call_api(api, **data)
|
|
||||||
|
|
||||||
log("DEBUG", f"Calling API <y>{api}</y>")
|
log("DEBUG", f"Calling API <y>{api}</y>")
|
||||||
if self.connection_type == "websocket":
|
if self.connection_type == "websocket":
|
||||||
seq = ResultStore.get_seq()
|
seq = ResultStore.get_seq()
|
||||||
@ -397,6 +375,29 @@ class Bot(BaseBot):
|
|||||||
except httpx.HTTPError:
|
except httpx.HTTPError:
|
||||||
raise NetworkError("HTTP request failed")
|
raise NetworkError("HTTP request failed")
|
||||||
|
|
||||||
|
@overrides(BaseBot)
|
||||||
|
async def call_api(self, api: str, **data) -> Any:
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
调用 CQHTTP 协议 API
|
||||||
|
|
||||||
|
:参数:
|
||||||
|
|
||||||
|
* ``api: str``: API 名称
|
||||||
|
* ``**data: Any``: API 参数
|
||||||
|
|
||||||
|
:返回:
|
||||||
|
|
||||||
|
- ``Any``: API 调用返回数据
|
||||||
|
|
||||||
|
:异常:
|
||||||
|
|
||||||
|
- ``NetworkError``: 网络错误
|
||||||
|
- ``ActionFailed``: API 调用失败
|
||||||
|
"""
|
||||||
|
return await super().call_api(api, **data)
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def send(self,
|
async def send(self,
|
||||||
event: Event,
|
event: Event,
|
||||||
|
@ -68,7 +68,8 @@ class Bot(BaseBot):
|
|||||||
async def handle_message(self, message: dict):
|
async def handle_message(self, message: dict):
|
||||||
...
|
...
|
||||||
|
|
||||||
async def call_api(self, api: str, **data) -> Any:
|
async def call_api(self, api: str, *, self_id: Optional[str],
|
||||||
|
**data) -> Any:
|
||||||
...
|
...
|
||||||
|
|
||||||
async def send(self, event: Event, message: Union[str, Message,
|
async def send(self, event: Event, message: Union[str, Message,
|
||||||
|
@ -19,3 +19,4 @@ class Config(BaseModel):
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
extra = "ignore"
|
extra = "ignore"
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
@ -199,6 +199,10 @@ class GroupMessageEvent(MessageEvent):
|
|||||||
if x.is_text() else f"<le>{escape_tag(str(x))}</le>",
|
if x.is_text() else f"<le>{escape_tag(str(x))}</le>",
|
||||||
self.message)) + '"')
|
self.message)) + '"')
|
||||||
|
|
||||||
|
@overrides(MessageEvent)
|
||||||
|
def get_session_id(self) -> str:
|
||||||
|
return f"group_{self.group_id}_{self.user_id}"
|
||||||
|
|
||||||
|
|
||||||
# Notice Events
|
# Notice Events
|
||||||
class NoticeEvent(Event):
|
class NoticeEvent(Event):
|
||||||
|
@ -38,7 +38,8 @@ class MessageSegment(BaseMessageSegment):
|
|||||||
|
|
||||||
@overrides(BaseMessageSegment)
|
@overrides(BaseMessageSegment)
|
||||||
def __radd__(self, other) -> "Message":
|
def __radd__(self, other) -> "Message":
|
||||||
return Message(other) + self
|
return (MessageSegment.text(other)
|
||||||
|
if isinstance(other, str) else Message(other)) + self
|
||||||
|
|
||||||
@overrides(BaseMessageSegment)
|
@overrides(BaseMessageSegment)
|
||||||
def is_text(self) -> bool:
|
def is_text(self) -> bool:
|
||||||
@ -211,6 +212,11 @@ class Message(BaseMessage):
|
|||||||
CQHTTP 协议 Message 适配。
|
CQHTTP 协议 Message 适配。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __radd__(self, other: Union[str, MessageSegment,
|
||||||
|
"Message"]) -> "Message":
|
||||||
|
result = MessageSegment.text(other) if isinstance(other, str) else other
|
||||||
|
return super(Message, self).__radd__(result)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@overrides(BaseMessage)
|
@overrides(BaseMessage)
|
||||||
def _construct(
|
def _construct(
|
||||||
|
20
packages/nonebot-adapter-cqhttp/poetry.lock
generated
@ -111,7 +111,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpx"
|
name = "httpx"
|
||||||
version = "0.17.0"
|
version = "0.17.1"
|
||||||
description = "The next generation HTTP client."
|
description = "The next generation HTTP client."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -119,7 +119,7 @@ python-versions = ">=3.6"
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
certifi = "*"
|
certifi = "*"
|
||||||
httpcore = ">=0.12.0,<0.13.0"
|
httpcore = ">=0.12.1,<0.13"
|
||||||
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||||
sniffio = "*"
|
sniffio = "*"
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot2"
|
name = "nonebot2"
|
||||||
version = "2.0.0-alpha.11"
|
version = "2.0.0-alpha.12"
|
||||||
description = "An asynchronous python bot framework."
|
description = "An asynchronous python bot framework."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -226,8 +226,8 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
description = "Add .env support to your django/flask apps in development and deployments"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
@ -418,7 +418,7 @@ reference = "aliyun"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7.3"
|
python-versions = "^3.7.3"
|
||||||
content-hash = "8110a56337b3ca1557700161e09ff2d74720b5897f0fac09c4ac5038495194d9"
|
content-hash = "b8ec196a78675b4098ab7509cbdbd311ffcbcf1ce8b625c589f1e95596801c71"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
certifi = [
|
certifi = [
|
||||||
@ -460,8 +460,8 @@ httptools = [
|
|||||||
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
||||||
]
|
]
|
||||||
httpx = [
|
httpx = [
|
||||||
{file = "httpx-0.17.0-py3-none-any.whl", hash = "sha256:fe19522f7b0861a1f6ac83306360bb5b7fb1ed64633a1a04a33f04102a1bea60"},
|
{file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"},
|
||||||
{file = "httpx-0.17.0.tar.gz", hash = "sha256:4f7ab2fef7f929c5531abd4f413b41ce2c820e3202f2eeee498f2d92b6849f8d"},
|
{file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"},
|
||||||
]
|
]
|
||||||
idna = [
|
idna = [
|
||||||
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
||||||
@ -500,8 +500,8 @@ pygtrie = [
|
|||||||
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
||||||
]
|
]
|
||||||
python-dotenv = [
|
python-dotenv = [
|
||||||
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
|
{file = "python-dotenv-0.16.0.tar.gz", hash = "sha256:9fa413c37d4652d3fa02fea0ff465c384f5db75eab259c4fc5d0c5b8bf20edd4"},
|
||||||
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
|
{file = "python_dotenv-0.16.0-py2.py3-none-any.whl", hash = "sha256:31d752f5b748f4e292448c9a0cac6a08ed5e6f4cefab85044462dcad56905cec"},
|
||||||
]
|
]
|
||||||
pyyaml = [
|
pyyaml = [
|
||||||
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "nonebot-adapter-cqhttp"
|
name = "nonebot-adapter-cqhttp"
|
||||||
version = "2.0.0a11.post2"
|
version = "2.0.0-alpha.12"
|
||||||
description = "OneBot(CQHTTP) adapter for nonebot2"
|
description = "OneBot(CQHTTP) adapter for nonebot2"
|
||||||
authors = ["yanyongyu <yyy@nonebot.dev>"]
|
authors = ["yanyongyu <yyy@nonebot.dev>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -25,7 +25,7 @@ exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"]
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7.3"
|
python = "^3.7.3"
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
nonebot2 = "^2.0.0-alpha.11"
|
nonebot2 = "^2.0.0-alpha.12"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
nonebot2 = { path = "../../", develop = true }
|
nonebot2 = { path = "../../", develop = true }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import hmac
|
import json
|
||||||
import base64
|
import urllib.parse
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import time
|
||||||
from typing import Any, Union, Optional, TYPE_CHECKING
|
from typing import Any, Union, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@ -10,7 +12,7 @@ from nonebot.message import handle_event
|
|||||||
from nonebot.adapters import Bot as BaseBot
|
from nonebot.adapters import Bot as BaseBot
|
||||||
from nonebot.exception import RequestDenied
|
from nonebot.exception import RequestDenied
|
||||||
|
|
||||||
from .utils import log
|
from .utils import calc_hmac_base64, log
|
||||||
from .config import Config as DingConfig
|
from .config import Config as DingConfig
|
||||||
from .message import Message, MessageSegment
|
from .message import Message, MessageSegment
|
||||||
from .exception import NetworkError, ApiNotAvailable, ActionFailed, SessionExpired
|
from .exception import NetworkError, ApiNotAvailable, ActionFailed, SessionExpired
|
||||||
@ -20,7 +22,7 @@ if TYPE_CHECKING:
|
|||||||
from nonebot.config import Config
|
from nonebot.config import Config
|
||||||
from nonebot.drivers import Driver
|
from nonebot.drivers import Driver
|
||||||
|
|
||||||
SEND_BY_SESSION_WEBHOOK = "send_by_sessionWebhook"
|
SEND = "send"
|
||||||
|
|
||||||
|
|
||||||
class Bot(BaseBot):
|
class Bot(BaseBot):
|
||||||
@ -48,7 +50,7 @@ class Bot(BaseBot):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def check_permission(cls, driver: "Driver", connection_type: str,
|
async def check_permission(cls, driver: "Driver", connection_type: str,
|
||||||
headers: dict, body: Optional[dict]) -> str:
|
headers: dict, body: Optional[bytes]) -> str:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
|
|
||||||
@ -72,15 +74,13 @@ class Bot(BaseBot):
|
|||||||
if not sign:
|
if not sign:
|
||||||
log("WARNING", "Missing Signature Header")
|
log("WARNING", "Missing Signature Header")
|
||||||
raise RequestDenied(400, "Missing `sign` Header")
|
raise RequestDenied(400, "Missing `sign` Header")
|
||||||
string_to_sign = f"{timestamp}\n{secret}"
|
sign_base64 = calc_hmac_base64(str(timestamp), secret)
|
||||||
sig = hmac.new(secret.encode("utf-8"),
|
if sign != sign_base64.decode('utf-8'):
|
||||||
string_to_sign.encode("utf-8"), "sha256").digest()
|
|
||||||
if sign != base64.b64encode(sig).decode("utf-8"):
|
|
||||||
log("WARNING", "Signature Header is invalid")
|
log("WARNING", "Signature Header is invalid")
|
||||||
raise RequestDenied(403, "Signature is invalid")
|
raise RequestDenied(403, "Signature is invalid")
|
||||||
else:
|
else:
|
||||||
log("WARNING", "Ding signature check ignored!")
|
log("WARNING", "Ding signature check ignored!")
|
||||||
return body["chatbotUserId"]
|
return json.loads(body.decode())["chatbotUserId"]
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def handle_message(self, message: dict):
|
async def handle_message(self, message: dict):
|
||||||
@ -109,48 +109,37 @@ class Bot(BaseBot):
|
|||||||
return
|
return
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def call_api(self,
|
async def _call_api(self,
|
||||||
api: str,
|
api: str,
|
||||||
event: Optional[MessageEvent] = None,
|
event: Optional[MessageEvent] = None,
|
||||||
**data) -> Any:
|
**data) -> Any:
|
||||||
"""
|
|
||||||
:说明:
|
|
||||||
|
|
||||||
调用 钉钉 协议 API
|
|
||||||
|
|
||||||
:参数:
|
|
||||||
|
|
||||||
* ``api: str``: API 名称
|
|
||||||
* ``**data: Any``: API 参数
|
|
||||||
|
|
||||||
:返回:
|
|
||||||
|
|
||||||
- ``Any``: API 调用返回数据
|
|
||||||
|
|
||||||
:异常:
|
|
||||||
|
|
||||||
- ``NetworkError``: 网络错误
|
|
||||||
- ``ActionFailed``: API 调用失败
|
|
||||||
"""
|
|
||||||
if self.connection_type != "http":
|
if self.connection_type != "http":
|
||||||
log("ERROR", "Only support http connection.")
|
log("ERROR", "Only support http connection.")
|
||||||
return
|
return
|
||||||
if "self_id" in data:
|
|
||||||
self_id = data.pop("self_id")
|
|
||||||
if self_id:
|
|
||||||
bot = self.driver.bots[str(self_id)]
|
|
||||||
return await bot.call_api(api, **data)
|
|
||||||
|
|
||||||
log("DEBUG", f"Calling API <y>{api}</y>")
|
log("DEBUG", f"Calling API <y>{api}</y>")
|
||||||
|
params = {}
|
||||||
|
# 传入参数有 webhook,则使用传入的 webhook
|
||||||
|
webhook = data.get("webhook")
|
||||||
|
|
||||||
if api == SEND_BY_SESSION_WEBHOOK:
|
if webhook:
|
||||||
|
secret = data.get("secret")
|
||||||
|
if secret:
|
||||||
|
# 有这个参数的时候再计算加签的值
|
||||||
|
timestamp = str(round(time.time() * 1000))
|
||||||
|
params["timestamp"] = timestamp
|
||||||
|
hmac_code_base64 = calc_hmac_base64(timestamp, secret)
|
||||||
|
sign = urllib.parse.quote_plus(hmac_code_base64)
|
||||||
|
params["sign"] = sign
|
||||||
|
else:
|
||||||
|
# webhook 不存在则使用 event 中的 sessionWebhook
|
||||||
if event:
|
if event:
|
||||||
# 确保 sessionWebhook 没有过期
|
# 确保 sessionWebhook 没有过期
|
||||||
if int(datetime.now().timestamp()) > int(
|
if int(datetime.now().timestamp()) > int(
|
||||||
event.sessionWebhookExpiredTime / 1000):
|
event.sessionWebhookExpiredTime / 1000):
|
||||||
raise SessionExpired
|
raise SessionExpired
|
||||||
|
|
||||||
target = event.sessionWebhook
|
webhook = event.sessionWebhook
|
||||||
else:
|
else:
|
||||||
raise ApiNotAvailable
|
raise ApiNotAvailable
|
||||||
|
|
||||||
@ -160,9 +149,8 @@ class Bot(BaseBot):
|
|||||||
raise ValueError("Message not found")
|
raise ValueError("Message not found")
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(headers=headers) as client:
|
async with httpx.AsyncClient(headers=headers) as client:
|
||||||
response = await client.post(
|
response = await client.post(webhook,
|
||||||
target,
|
params=params,
|
||||||
params={"access_token": self.ding_config.access_token},
|
|
||||||
json=message._produce(),
|
json=message._produce(),
|
||||||
timeout=self.config.api_timeout)
|
timeout=self.config.api_timeout)
|
||||||
|
|
||||||
@ -180,11 +168,40 @@ class Bot(BaseBot):
|
|||||||
except httpx.HTTPError:
|
except httpx.HTTPError:
|
||||||
raise NetworkError("HTTP request failed")
|
raise NetworkError("HTTP request failed")
|
||||||
|
|
||||||
|
@overrides(BaseBot)
|
||||||
|
async def call_api(self,
|
||||||
|
api: str,
|
||||||
|
event: Optional[MessageEvent] = None,
|
||||||
|
**data) -> Any:
|
||||||
|
"""
|
||||||
|
:说明:
|
||||||
|
|
||||||
|
调用 钉钉 协议 API
|
||||||
|
|
||||||
|
:参数:
|
||||||
|
|
||||||
|
* ``api: str``: API 名称
|
||||||
|
* ``event: Optional[MessageEvent]``: Event 对象
|
||||||
|
* ``**data: Any``: API 参数
|
||||||
|
|
||||||
|
:返回:
|
||||||
|
|
||||||
|
- ``Any``: API 调用返回数据
|
||||||
|
|
||||||
|
:异常:
|
||||||
|
|
||||||
|
- ``NetworkError``: 网络错误
|
||||||
|
- ``ActionFailed``: API 调用失败
|
||||||
|
"""
|
||||||
|
return await super().call_api(api, event=event, **data)
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def send(self,
|
async def send(self,
|
||||||
event: MessageEvent,
|
event: MessageEvent,
|
||||||
message: Union[str, "Message", "MessageSegment"],
|
message: Union[str, "Message", "MessageSegment"],
|
||||||
at_sender: bool = False,
|
at_sender: bool = False,
|
||||||
|
webhook: Optional[str] = None,
|
||||||
|
secret: Optional[str] = None,
|
||||||
**kwargs) -> Any:
|
**kwargs) -> Any:
|
||||||
"""
|
"""
|
||||||
:说明:
|
:说明:
|
||||||
@ -196,6 +213,8 @@ class Bot(BaseBot):
|
|||||||
* ``event: Event``: Event 对象
|
* ``event: Event``: Event 对象
|
||||||
* ``message: Union[str, Message, MessageSegment]``: 要发送的消息
|
* ``message: Union[str, Message, MessageSegment]``: 要发送的消息
|
||||||
* ``at_sender: bool``: 是否 @ 事件主体
|
* ``at_sender: bool``: 是否 @ 事件主体
|
||||||
|
* ``webhook: Optional[str]``: 该条消息将调用的 webhook 地址。不传则将使用 sessionWebhook,若其也不存在,该条消息不发送,使用自定义 webhook 时注意你设置的安全方式,如加关键词,IP地址,加签等等。
|
||||||
|
* ``secret: Optional[str]``: 如果你使用自定义的 webhook 地址,推荐使用加签方式对消息进行验证,将 `机器人安全设置页面,加签一栏下面显示的SEC开头的字符串` 传入这个参数即可。
|
||||||
* ``**kwargs``: 覆盖默认参数
|
* ``**kwargs``: 覆盖默认参数
|
||||||
|
|
||||||
:返回:
|
:返回:
|
||||||
@ -213,6 +232,9 @@ class Bot(BaseBot):
|
|||||||
at_sender = at_sender and bool(event.senderId)
|
at_sender = at_sender and bool(event.senderId)
|
||||||
params = {}
|
params = {}
|
||||||
params["event"] = event
|
params["event"] = event
|
||||||
|
if webhook:
|
||||||
|
params["webhook"] = webhook
|
||||||
|
params["secret"] = secret
|
||||||
params.update(kwargs)
|
params.update(kwargs)
|
||||||
|
|
||||||
if at_sender and event.conversationType != ConversationType.private:
|
if at_sender and event.conversationType != ConversationType.private:
|
||||||
@ -222,4 +244,4 @@ class Bot(BaseBot):
|
|||||||
else:
|
else:
|
||||||
params["message"] = msg
|
params["message"] = msg
|
||||||
|
|
||||||
return await self.call_api(SEND_BY_SESSION_WEBHOOK, **params)
|
return await self.call_api(SEND, **params)
|
||||||
|
@ -17,3 +17,4 @@ class Config(BaseModel):
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
extra = "ignore"
|
extra = "ignore"
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
@ -143,3 +143,7 @@ class GroupMessageEvent(MessageEvent):
|
|||||||
@overrides(MessageEvent)
|
@overrides(MessageEvent)
|
||||||
def is_tome(self) -> bool:
|
def is_tome(self) -> bool:
|
||||||
return self.isInAtList
|
return self.isInAtList
|
||||||
|
|
||||||
|
@overrides(MessageEvent)
|
||||||
|
def get_session_id(self) -> str:
|
||||||
|
return f"group_{self.conversationId}_{self.senderId}"
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
import hmac
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from nonebot.utils import logger_wrapper
|
from nonebot.utils import logger_wrapper
|
||||||
|
|
||||||
log = logger_wrapper("DING")
|
log = logger_wrapper("DING")
|
||||||
|
|
||||||
|
|
||||||
|
def calc_hmac_base64(timestamp: str, secret: str):
|
||||||
|
secret_enc = secret.encode('utf-8')
|
||||||
|
string_to_sign = '{}\n{}'.format(timestamp, secret)
|
||||||
|
string_to_sign_enc = string_to_sign.encode('utf-8')
|
||||||
|
hmac_code = hmac.new(secret_enc,
|
||||||
|
string_to_sign_enc,
|
||||||
|
digestmod=hashlib.sha256).digest()
|
||||||
|
return base64.b64encode(hmac_code)
|
||||||
|
20
packages/nonebot-adapter-ding/poetry.lock
generated
@ -111,7 +111,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpx"
|
name = "httpx"
|
||||||
version = "0.17.0"
|
version = "0.17.1"
|
||||||
description = "The next generation HTTP client."
|
description = "The next generation HTTP client."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -119,7 +119,7 @@ python-versions = ">=3.6"
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
certifi = "*"
|
certifi = "*"
|
||||||
httpcore = ">=0.12.0,<0.13.0"
|
httpcore = ">=0.12.1,<0.13"
|
||||||
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||||
sniffio = "*"
|
sniffio = "*"
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot2"
|
name = "nonebot2"
|
||||||
version = "2.0.0-alpha.11"
|
version = "2.0.0-alpha.12"
|
||||||
description = "An asynchronous python bot framework."
|
description = "An asynchronous python bot framework."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -226,8 +226,8 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
description = "Add .env support to your django/flask apps in development and deployments"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
@ -418,7 +418,7 @@ reference = "aliyun"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7.3"
|
python-versions = "^3.7.3"
|
||||||
content-hash = "8110a56337b3ca1557700161e09ff2d74720b5897f0fac09c4ac5038495194d9"
|
content-hash = "b8ec196a78675b4098ab7509cbdbd311ffcbcf1ce8b625c589f1e95596801c71"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
certifi = [
|
certifi = [
|
||||||
@ -460,8 +460,8 @@ httptools = [
|
|||||||
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
||||||
]
|
]
|
||||||
httpx = [
|
httpx = [
|
||||||
{file = "httpx-0.17.0-py3-none-any.whl", hash = "sha256:fe19522f7b0861a1f6ac83306360bb5b7fb1ed64633a1a04a33f04102a1bea60"},
|
{file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"},
|
||||||
{file = "httpx-0.17.0.tar.gz", hash = "sha256:4f7ab2fef7f929c5531abd4f413b41ce2c820e3202f2eeee498f2d92b6849f8d"},
|
{file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"},
|
||||||
]
|
]
|
||||||
idna = [
|
idna = [
|
||||||
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
||||||
@ -500,8 +500,8 @@ pygtrie = [
|
|||||||
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
||||||
]
|
]
|
||||||
python-dotenv = [
|
python-dotenv = [
|
||||||
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
|
{file = "python-dotenv-0.16.0.tar.gz", hash = "sha256:9fa413c37d4652d3fa02fea0ff465c384f5db75eab259c4fc5d0c5b8bf20edd4"},
|
||||||
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
|
{file = "python_dotenv-0.16.0-py2.py3-none-any.whl", hash = "sha256:31d752f5b748f4e292448c9a0cac6a08ed5e6f4cefab85044462dcad56905cec"},
|
||||||
]
|
]
|
||||||
pyyaml = [
|
pyyaml = [
|
||||||
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "nonebot-adapter-ding"
|
name = "nonebot-adapter-ding"
|
||||||
version = "2.0.0a11.post2"
|
version = "2.0.0-alpha.12"
|
||||||
description = "Ding adapter for nonebot2"
|
description = "Ding adapter for nonebot2"
|
||||||
authors = ["Artin <lengthmin@gmail.com>", "yanyongyu <yyy@nonebot.dev>"]
|
authors = ["Artin <lengthmin@gmail.com>", "yanyongyu <yyy@nonebot.dev>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -25,7 +25,7 @@ exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"]
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7.3"
|
python = "^3.7.3"
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
nonebot2 = "^2.0.0-alpha.11"
|
nonebot2 = "^2.0.0-alpha.12"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
nonebot2 = { path = "../../", develop = true }
|
nonebot2 = { path = "../../", develop = true }
|
||||||
|
@ -178,7 +178,7 @@ class Bot(BaseBot):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def check_permission(cls, driver: "Driver", connection_type: str,
|
async def check_permission(cls, driver: "Driver", connection_type: str,
|
||||||
headers: dict, body: Optional[dict]) -> str:
|
headers: dict, body: Optional[bytes]) -> str:
|
||||||
if connection_type == 'ws':
|
if connection_type == 'ws':
|
||||||
raise RequestDenied(
|
raise RequestDenied(
|
||||||
status_code=501,
|
status_code=501,
|
||||||
@ -218,6 +218,10 @@ class Bot(BaseBot):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
Log.error(f'Failed to handle message: {message}', e)
|
Log.error(f'Failed to handle message: {message}', e)
|
||||||
|
|
||||||
|
@overrides(BaseBot)
|
||||||
|
async def _call_api(self, api: str, **data) -> NoReturn:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def call_api(self, api: str, **data) -> NoReturn:
|
async def call_api(self, api: str, **data) -> NoReturn:
|
||||||
"""
|
"""
|
||||||
|
@ -116,7 +116,8 @@ class WebsocketBot(Bot):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@overrides(Bot)
|
@overrides(Bot)
|
||||||
async def check_permission(cls, driver: "Driver", connection_type: str,
|
async def check_permission(cls, driver: "Driver", connection_type: str,
|
||||||
headers: dict, body: Optional[dict]) -> NoReturn:
|
headers: dict,
|
||||||
|
body: Optional[bytes]) -> NoReturn:
|
||||||
raise RequestDenied(
|
raise RequestDenied(
|
||||||
status_code=501,
|
status_code=501,
|
||||||
reason=f'Connection {connection_type} not implented')
|
reason=f'Connection {connection_type} not implented')
|
||||||
|
@ -20,3 +20,4 @@ class Config(BaseModel):
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
extra = Extra.ignore
|
extra = Extra.ignore
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
@ -11,7 +11,7 @@ from nonebot.message import handle_event
|
|||||||
from nonebot.utils import escape_tag, logger_wrapper
|
from nonebot.utils import escape_tag, logger_wrapper
|
||||||
|
|
||||||
from .event import Event, GroupMessage, MessageEvent, MessageSource
|
from .event import Event, GroupMessage, MessageEvent, MessageSource
|
||||||
from .message import MessageType
|
from .message import MessageType, MessageSegment
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
@ -138,6 +138,8 @@ def process_at(bot: "Bot", event: GroupMessage) -> GroupMessage:
|
|||||||
event.to_me = True
|
event.to_me = True
|
||||||
else:
|
else:
|
||||||
event.message_chain.insert(0, at)
|
event.message_chain.insert(0, at)
|
||||||
|
if not event.message_chain:
|
||||||
|
event.message_chain.append(MessageSegment.plain(''))
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
20
packages/nonebot-adapter-mirai/poetry.lock
generated
@ -111,7 +111,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpx"
|
name = "httpx"
|
||||||
version = "0.17.0"
|
version = "0.17.1"
|
||||||
description = "The next generation HTTP client."
|
description = "The next generation HTTP client."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -119,7 +119,7 @@ python-versions = ">=3.6"
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
certifi = "*"
|
certifi = "*"
|
||||||
httpcore = ">=0.12.0,<0.13.0"
|
httpcore = ">=0.12.1,<0.13"
|
||||||
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||||
sniffio = "*"
|
sniffio = "*"
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot2"
|
name = "nonebot2"
|
||||||
version = "2.0.0-alpha.11"
|
version = "2.0.0-alpha.12"
|
||||||
description = "An asynchronous python bot framework."
|
description = "An asynchronous python bot framework."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -226,8 +226,8 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
description = "Add .env support to your django/flask apps in development and deployments"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
@ -418,7 +418,7 @@ reference = "aliyun"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7.3"
|
python-versions = "^3.7.3"
|
||||||
content-hash = "9eb7c46cddf1245508a34c00f5709d21415d02d7f8c356733cc20ad187f431f9"
|
content-hash = "5b8b3e27eccd897aa33fea94ba813b7d601c5656d52efed4401b398f6d68a677"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
certifi = [
|
certifi = [
|
||||||
@ -460,8 +460,8 @@ httptools = [
|
|||||||
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
||||||
]
|
]
|
||||||
httpx = [
|
httpx = [
|
||||||
{file = "httpx-0.17.0-py3-none-any.whl", hash = "sha256:fe19522f7b0861a1f6ac83306360bb5b7fb1ed64633a1a04a33f04102a1bea60"},
|
{file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"},
|
||||||
{file = "httpx-0.17.0.tar.gz", hash = "sha256:4f7ab2fef7f929c5531abd4f413b41ce2c820e3202f2eeee498f2d92b6849f8d"},
|
{file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"},
|
||||||
]
|
]
|
||||||
idna = [
|
idna = [
|
||||||
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
||||||
@ -500,8 +500,8 @@ pygtrie = [
|
|||||||
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
||||||
]
|
]
|
||||||
python-dotenv = [
|
python-dotenv = [
|
||||||
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
|
{file = "python-dotenv-0.16.0.tar.gz", hash = "sha256:9fa413c37d4652d3fa02fea0ff465c384f5db75eab259c4fc5d0c5b8bf20edd4"},
|
||||||
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
|
{file = "python_dotenv-0.16.0-py2.py3-none-any.whl", hash = "sha256:31d752f5b748f4e292448c9a0cac6a08ed5e6f4cefab85044462dcad56905cec"},
|
||||||
]
|
]
|
||||||
pyyaml = [
|
pyyaml = [
|
||||||
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "nonebot-adapter-mirai"
|
name = "nonebot-adapter-mirai"
|
||||||
version = "2.0.0a11.post2"
|
version = "2.0.0-alpha.12"
|
||||||
description = "Mirai Api HTTP adapter for nonebot2"
|
description = "Mirai Api HTTP adapter for nonebot2"
|
||||||
authors = ["Mix <admin@yami.im>", "yanyongyu <yyy@nonebot.dev>"]
|
authors = ["Mix <admin@yami.im>", "yanyongyu <yyy@nonebot.dev>"]
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
@ -26,7 +26,7 @@ exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"]
|
|||||||
python = "^3.7.3"
|
python = "^3.7.3"
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
websockets = "^8.1"
|
websockets = "^8.1"
|
||||||
nonebot2 = "^2.0.0-alpha.11"
|
nonebot2 = "^2.0.0-alpha.12"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
nonebot2 = { path = "../../", develop = true }
|
nonebot2 = { path = "../../", develop = true }
|
||||||
|
@ -4,6 +4,17 @@ sidebar: auto
|
|||||||
|
|
||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
|
## v2.0.0a12
|
||||||
|
|
||||||
|
- 分离 `handler` 与 `matcher`
|
||||||
|
- 修复 `cqhttp` secret 校验出错
|
||||||
|
- 修复 `pydantic 1.8` 导致的 `alias` 问题
|
||||||
|
- 修改 `cqhttp` `ding` `session id`,不再允许跨群
|
||||||
|
- 修改 `shell_command` 存储 message
|
||||||
|
- 修复 `cqhttp` 检查 reply 失败退出
|
||||||
|
- 新增 `call_api` hook 接口
|
||||||
|
- 优化 `import hook`
|
||||||
|
|
||||||
## v2.0.0a11
|
## v2.0.0a11
|
||||||
|
|
||||||
- 修改 `nonebot` 项目结构,分离所有 `adapter`
|
- 修改 `nonebot` 项目结构,分离所有 `adapter`
|
||||||
|
150
poetry.lock
generated
@ -3,7 +3,7 @@ name = "aiofiles"
|
|||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
description = "File support for asyncio."
|
description = "File support for asyncio."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
@ -235,7 +235,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpx"
|
name = "httpx"
|
||||||
version = "0.17.0"
|
version = "0.17.1"
|
||||||
description = "The next generation HTTP client."
|
description = "The next generation HTTP client."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -243,7 +243,7 @@ python-versions = ">=3.6"
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
certifi = "*"
|
certifi = "*"
|
||||||
httpcore = ">=0.12.0,<0.13.0"
|
httpcore = ">=0.12.1,<0.13"
|
||||||
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||||
sniffio = "*"
|
sniffio = "*"
|
||||||
|
|
||||||
@ -389,7 +389,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot-adapter-cqhttp"
|
name = "nonebot-adapter-cqhttp"
|
||||||
version = "2.0.0-alpha.11"
|
version = "2.0.0-alpha.12"
|
||||||
description = "OneBot(CQHTTP) adapter for nonebot2"
|
description = "OneBot(CQHTTP) adapter for nonebot2"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -398,7 +398,7 @@ develop = true
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
nonebot2 = "^2.0.0-alpha.11"
|
nonebot2 = "^2.0.0-alpha.12"
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "directory"
|
type = "directory"
|
||||||
@ -406,7 +406,7 @@ url = "packages/nonebot-adapter-cqhttp"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot-adapter-ding"
|
name = "nonebot-adapter-ding"
|
||||||
version = "2.0.0-alpha.11"
|
version = "2.0.0-alpha.12"
|
||||||
description = "Ding adapter for nonebot2"
|
description = "Ding adapter for nonebot2"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -415,7 +415,7 @@ develop = true
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
nonebot2 = "^2.0.0-alpha.11"
|
nonebot2 = "^2.0.0-alpha.12"
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "directory"
|
type = "directory"
|
||||||
@ -423,7 +423,7 @@ url = "packages/nonebot-adapter-ding"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonebot-adapter-mirai"
|
name = "nonebot-adapter-mirai"
|
||||||
version = "2.0.0-alpha.11"
|
version = "2.0.0-alpha.12"
|
||||||
description = "Mirai Api HTTP adapter for nonebot2"
|
description = "Mirai Api HTTP adapter for nonebot2"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -432,13 +432,31 @@ develop = true
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
httpx = "^0.17.0"
|
httpx = "^0.17.0"
|
||||||
nonebot2 = "^2.0.0-alpha.11"
|
nonebot2 = "^2.0.0-alpha.12"
|
||||||
websockets = "^8.1"
|
websockets = "^8.1"
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "directory"
|
type = "directory"
|
||||||
url = "packages/nonebot-adapter-mirai"
|
url = "packages/nonebot-adapter-mirai"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nonebot-plugin-test"
|
||||||
|
version = "0.2.0"
|
||||||
|
description = "Test frontend for nonebot v2+"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7,<4.0"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
aiofiles = ">=0.6.0,<0.7.0"
|
||||||
|
nonebot2 = ">=2.0.0-alpha.9,<3.0.0"
|
||||||
|
python-socketio = ">=4.6.1,<5.0.0"
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "legacy"
|
||||||
|
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||||
|
reference = "aliyun"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "20.9"
|
version = "20.9"
|
||||||
@ -491,14 +509,14 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydash"
|
name = "pydash"
|
||||||
version = "4.9.3"
|
version = "5.0.0"
|
||||||
description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library."
|
description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["coverage", "docformatter", "flake8", "invoke", "mock", "pylint", "pytest", "pytest-cov", "pytest-flake8", "pytest-pylint", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel", "black", "flake8-black", "flake8-bugbear", "flake8-isort", "isort"]
|
dev = ["black", "coverage", "docformatter", "flake8", "flake8-black", "flake8-bugbear", "flake8-isort", "invoke", "isort", "pylint", "pytest", "pytest-cov", "pytest-flake8", "pytest-pylint", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel"]
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "legacy"
|
type = "legacy"
|
||||||
@ -507,7 +525,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
description = "Pygments is a syntax highlighting package written in Python."
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -546,8 +564,8 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
description = "Add .env support to your django/flask apps in development and deployments"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
@ -560,6 +578,47 @@ type = "legacy"
|
|||||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||||
reference = "aliyun"
|
reference = "aliyun"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-engineio"
|
||||||
|
version = "3.14.2"
|
||||||
|
description = "Engine.IO server"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
six = ">=1.9.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
asyncio_client = ["aiohttp (>=3.4)"]
|
||||||
|
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "legacy"
|
||||||
|
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||||
|
reference = "aliyun"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-socketio"
|
||||||
|
version = "4.6.1"
|
||||||
|
description = "Socket.IO server"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
python-engineio = ">=3.13.0,<4"
|
||||||
|
six = ">=1.9.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
asyncio_client = ["aiohttp (>=3.4)", "websockets (>=7.0)"]
|
||||||
|
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "legacy"
|
||||||
|
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||||
|
reference = "aliyun"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2021.1"
|
version = "2021.1"
|
||||||
@ -655,6 +714,19 @@ type = "legacy"
|
|||||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||||
reference = "aliyun"
|
reference = "aliyun"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.15.0"
|
||||||
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "legacy"
|
||||||
|
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||||
|
reference = "aliyun"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -683,7 +755,7 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinx"
|
name = "sphinx"
|
||||||
version = "3.5.1"
|
version = "3.5.3"
|
||||||
description = "Python documentation generator"
|
description = "Python documentation generator"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -926,16 +998,16 @@ reference = "aliyun"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "1.26.3"
|
version = "1.26.4"
|
||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
brotli = ["brotlipy (>=0.6.0)"]
|
|
||||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
brotli = ["brotlipy (>=0.6.0)"]
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "legacy"
|
type = "legacy"
|
||||||
@ -1083,7 +1155,7 @@ quart = ["Quart"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7.3"
|
python-versions = "^3.7.3"
|
||||||
content-hash = "f4e49d25ac5c37b3c7527935b52acf5d0d6d0261a645f4247120f9e28c5ca282"
|
content-hash = "9907ba758206e19d2c36493252f8b026db796ff4dbc67142824d86da31763919"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiofiles = [
|
aiofiles = [
|
||||||
@ -1160,8 +1232,8 @@ httptools = [
|
|||||||
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
||||||
]
|
]
|
||||||
httpx = [
|
httpx = [
|
||||||
{file = "httpx-0.17.0-py3-none-any.whl", hash = "sha256:fe19522f7b0861a1f6ac83306360bb5b7fb1ed64633a1a04a33f04102a1bea60"},
|
{file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"},
|
||||||
{file = "httpx-0.17.0.tar.gz", hash = "sha256:4f7ab2fef7f929c5531abd4f413b41ce2c820e3202f2eeee498f2d92b6849f8d"},
|
{file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"},
|
||||||
]
|
]
|
||||||
hypercorn = [
|
hypercorn = [
|
||||||
{file = "Hypercorn-0.11.2-py3-none-any.whl", hash = "sha256:8007c10f81566920f8ae12c0e26e146f94ca70506da964b5a727ad610aa1d821"},
|
{file = "Hypercorn-0.11.2-py3-none-any.whl", hash = "sha256:8007c10f81566920f8ae12c0e26e146f94ca70506da964b5a727ad610aa1d821"},
|
||||||
@ -1248,6 +1320,10 @@ markupsafe = [
|
|||||||
nonebot-adapter-cqhttp = []
|
nonebot-adapter-cqhttp = []
|
||||||
nonebot-adapter-ding = []
|
nonebot-adapter-ding = []
|
||||||
nonebot-adapter-mirai = []
|
nonebot-adapter-mirai = []
|
||||||
|
nonebot-plugin-test = [
|
||||||
|
{file = "nonebot-plugin-test-0.2.0.tar.gz", hash = "sha256:c9ee997c5c96160de4af02d10a7c6301b3fc4e942df7e70906df0534606ea23b"},
|
||||||
|
{file = "nonebot_plugin_test-0.2.0-py3-none-any.whl", hash = "sha256:75cd18cc282815a03250bb86c7d2a8d6a66a5064ac335bedc9a3e268a1e7dd13"},
|
||||||
|
]
|
||||||
packaging = [
|
packaging = [
|
||||||
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
|
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
|
||||||
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
|
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
|
||||||
@ -1281,12 +1357,12 @@ pydantic = [
|
|||||||
{file = "pydantic-1.8.1.tar.gz", hash = "sha256:26cf3cb2e68ec6c0cfcb6293e69fb3450c5fd1ace87f46b64f678b0d29eac4c3"},
|
{file = "pydantic-1.8.1.tar.gz", hash = "sha256:26cf3cb2e68ec6c0cfcb6293e69fb3450c5fd1ace87f46b64f678b0d29eac4c3"},
|
||||||
]
|
]
|
||||||
pydash = [
|
pydash = [
|
||||||
{file = "pydash-4.9.3-py2.py3-none-any.whl", hash = "sha256:7851a2d749e70c02585ae4803b01c5e0f47b7ec9df9b84ccb16aac38cad2fdd2"},
|
{file = "pydash-5.0.0-py3-none-any.whl", hash = "sha256:0d87f879a3df4ad9389ab6d63c69eea078517d41541ddd5744cfcff3396e8543"},
|
||||||
{file = "pydash-4.9.3.tar.gz", hash = "sha256:d709e57b537b1aaf118f188da3ec6242a665090ecd7839b66f857ee3dc2bb006"},
|
{file = "pydash-5.0.0.tar.gz", hash = "sha256:845262df83b5411742e5f7f7dbfa5ed4d0ddac6d7d0a13c4375c6a3c40d4e8f4"},
|
||||||
]
|
]
|
||||||
pygments = [
|
pygments = [
|
||||||
{file = "Pygments-2.8.0-py3-none-any.whl", hash = "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"},
|
{file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"},
|
||||||
{file = "Pygments-2.8.0.tar.gz", hash = "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0"},
|
{file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"},
|
||||||
]
|
]
|
||||||
pygtrie = [
|
pygtrie = [
|
||||||
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
|
||||||
@ -1296,8 +1372,16 @@ pyparsing = [
|
|||||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||||
]
|
]
|
||||||
python-dotenv = [
|
python-dotenv = [
|
||||||
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
|
{file = "python-dotenv-0.16.0.tar.gz", hash = "sha256:9fa413c37d4652d3fa02fea0ff465c384f5db75eab259c4fc5d0c5b8bf20edd4"},
|
||||||
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
|
{file = "python_dotenv-0.16.0-py2.py3-none-any.whl", hash = "sha256:31d752f5b748f4e292448c9a0cac6a08ed5e6f4cefab85044462dcad56905cec"},
|
||||||
|
]
|
||||||
|
python-engineio = [
|
||||||
|
{file = "python-engineio-3.14.2.tar.gz", hash = "sha256:eab4553f2804c1ce97054c8b22cf0d5a9ab23128075248b97e1a5b2f29553085"},
|
||||||
|
{file = "python_engineio-3.14.2-py2.py3-none-any.whl", hash = "sha256:5a9e6086d192463b04a1428ff1f85b6ba631bbb19d453b144ffc04f530542b84"},
|
||||||
|
]
|
||||||
|
python-socketio = [
|
||||||
|
{file = "python-socketio-4.6.1.tar.gz", hash = "sha256:cd1f5aa492c1eb2be77838e837a495f117e17f686029ebc03d62c09e33f4fa10"},
|
||||||
|
{file = "python_socketio-4.6.1-py2.py3-none-any.whl", hash = "sha256:5a21da53fdbdc6bb6c8071f40e13d100e0b279ad997681c2492478e06f370523"},
|
||||||
]
|
]
|
||||||
pytz = [
|
pytz = [
|
||||||
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
||||||
@ -1338,6 +1422,10 @@ rfc3986 = [
|
|||||||
{file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
|
{file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
|
||||||
{file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
|
{file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
|
||||||
]
|
]
|
||||||
|
six = [
|
||||||
|
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
|
||||||
|
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
|
||||||
|
]
|
||||||
sniffio = [
|
sniffio = [
|
||||||
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
|
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
|
||||||
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
||||||
@ -1347,8 +1435,8 @@ snowballstemmer = [
|
|||||||
{file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"},
|
{file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"},
|
||||||
]
|
]
|
||||||
sphinx = [
|
sphinx = [
|
||||||
{file = "Sphinx-3.5.1-py3-none-any.whl", hash = "sha256:e90161222e4d80ce5fc811ace7c6787a226b4f5951545f7f42acf97277bfc35c"},
|
{file = "Sphinx-3.5.3-py3-none-any.whl", hash = "sha256:3f01732296465648da43dec8fb40dc451ba79eb3e2cc5c6d79005fd98197107d"},
|
||||||
{file = "Sphinx-3.5.1.tar.gz", hash = "sha256:11d521e787d9372c289472513d807277caafb1684b33eb4f08f7574c405893a9"},
|
{file = "Sphinx-3.5.3.tar.gz", hash = "sha256:ce9c228456131bab09a3d7d10ae58474de562a6f79abb3dc811ae401cf8c1abc"},
|
||||||
]
|
]
|
||||||
sphinx-markdown-builder = []
|
sphinx-markdown-builder = []
|
||||||
sphinxcontrib-applehelp = [
|
sphinxcontrib-applehelp = [
|
||||||
@ -1399,8 +1487,8 @@ untokenize = [
|
|||||||
{file = "untokenize-0.1.1.tar.gz", hash = "md5:50d325dff09208c624cc603fad33bb0d"},
|
{file = "untokenize-0.1.1.tar.gz", hash = "md5:50d325dff09208c624cc603fad33bb0d"},
|
||||||
]
|
]
|
||||||
urllib3 = [
|
urllib3 = [
|
||||||
{file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
|
{file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
|
||||||
{file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
|
{file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
|
||||||
]
|
]
|
||||||
uvicorn = [
|
uvicorn = [
|
||||||
{file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"},
|
{file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"},
|
||||||
|