mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-10-06 18:56:45 +00:00
Compare commits
64 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8821218bfd | ||
|
27b9b413df | ||
|
2b67b0f12e | ||
|
d26d7bba55 | ||
|
0761a60443 | ||
|
217b1a5fac | ||
|
ef98a6f23c | ||
|
c6a6dc6e21 | ||
|
f3b77a7f60 | ||
|
f625c34269 | ||
|
ac5c4acf09 | ||
|
9a3464744f | ||
|
91bdfc4704 | ||
|
6f8aa49d0e | ||
|
be57798eac | ||
|
4855e65b1a | ||
|
a3fe3a1ad8 | ||
|
382a9b6e12 | ||
|
24349953e3 | ||
|
85aba9e36f | ||
|
49010bf5b7 | ||
|
b1c7f309f8 | ||
|
458ddaa167 | ||
|
ab97334cf8 | ||
|
112fdf7ed3 | ||
|
b59ff03abf | ||
|
bdd9f5ae30 | ||
|
abcdbc4de9 | ||
|
1715139494 | ||
|
b862c506e4 | ||
|
4fd4fbfb08 | ||
|
6cb9fda53a | ||
|
4f7a033b9c | ||
|
c537841bc1 | ||
|
7d9a8eaf19 | ||
|
86965ee06d | ||
|
496f64f103 | ||
|
9e0862bc97 | ||
|
6b43ad5575 | ||
|
bf7b2a8cbe | ||
|
7232c89292 | ||
|
bc164ca2f2 | ||
|
ad8429e7fa | ||
|
c268e0105d | ||
|
050137fb3f | ||
|
f2d04c598c | ||
|
49307a3df2 | ||
|
ff9f0bc74a | ||
|
f929f25abd | ||
|
6af0a2574d | ||
|
c61a159eff | ||
|
59bdd03b1e | ||
|
35ea1e78ec | ||
|
7db386b752 | ||
|
e6a68feb0c | ||
|
78b0450d66 | ||
|
bd5fb46e0a | ||
|
cc3ed735f1 | ||
|
3f0c3f1f37 | ||
|
e1ff0a7221 | ||
|
2bf8b4d313 | ||
|
188aa8a50a | ||
|
1538d2094a | ||
|
16eb74e88b |
4
.github/workflows/build_docs.yml
vendored
4
.github/workflows/build_docs.yml
vendored
@@ -2,7 +2,7 @@ name: Build API Doc
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [ opened, synchronize, reopened ]
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
- name: Set up dependencies
|
||||
run: |
|
||||
poetry install
|
||||
poetry install -E all
|
||||
|
||||
- name: Build Doc
|
||||
run: poetry run sphinx-build -M markdown ./docs_build ./build
|
||||
|
@@ -75,7 +75,7 @@ NoneBot2 的驱动框架 `Driver` 以及通信协议 `Adapter` 均可**自定义
|
||||
|
||||
- [OneBot(CQHTTP) 协议](https://github.com/howmanybots/onebot/blob/master/README.md) (QQ 等)
|
||||
- [Mirai-API-HTTP 协议](https://github.com/project-mirai/mirai-api-http)
|
||||
- [钉钉](https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p) _开发中_
|
||||
- [钉钉](https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p)
|
||||
- [Telegram](https://core.telegram.org/bots/api) _计划中_
|
||||
|
||||
## 即刻开始
|
||||
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
home: true
|
||||
heroImage: /logo.png
|
||||
tagline: An asynchronous bot framework.
|
||||
tagline: 跨平台 Python 异步 QQ 机器人框架
|
||||
actionText: 开始使用
|
||||
actionLink: guide/
|
||||
features:
|
1
archive/2.0.0a9.post1/advanced/runtime-hook.md
Normal file
1
archive/2.0.0a9.post1/advanced/runtime-hook.md
Normal file
@@ -0,0 +1 @@
|
||||
# 钩子函数
|
@@ -4,7 +4,7 @@
|
||||
|
||||
> Advanced Python Scheduler (APScheduler) is a Python library that lets you schedule your Python code to be executed later, either just once or periodically. You can add new jobs or remove old ones on the fly as you please. If you store your jobs in a database, they will also survive scheduler restarts and maintain their state. When the scheduler is restarted, it will then run all the jobs it should have run while it was offline.
|
||||
|
||||
## 从 v1 迁移
|
||||
## 从 NoneBot v1 迁移
|
||||
|
||||
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/plugin-store.html) 中找到它。
|
||||
|
||||
@@ -21,7 +21,7 @@ nb plugin install nonebot_plugin_apscheduler
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
`nb-cli` 默认通过 `pypi` 安装,你可以使用 `-i [mirror]` 或 `--index [mirror]` 来使用镜像源安装。
|
||||
`nb-cli` 默认通过 `pypi` 安装,你可以添加命令参数 `-i [mirror]` 或 `--index [mirror]` 以使用镜像源安装。
|
||||
:::
|
||||
|
||||
### 通过 poetry
|
||||
@@ -96,10 +96,14 @@ scheduler = require('nonebot_plugin_apscheduler').scheduler
|
||||
|
||||
对于大多数情况,我们需要在 `nonebot2` 项目被启动时启动定时任务,则此处设为 `true`
|
||||
|
||||
##### 在 `.env` 中添加
|
||||
|
||||
```bash
|
||||
APSCHEDULER_AUTOSTART=true
|
||||
```
|
||||
|
||||
##### 在 `bot.py` 中添加
|
||||
|
||||
```python
|
||||
nonebot.init(apscheduler_autostart=True)
|
||||
```
|
||||
@@ -116,10 +120,14 @@ nonebot.init(apscheduler_autostart=True)
|
||||
|
||||
> 官方文档在绝大多数时候能提供最准确和最具时效性的指南
|
||||
|
||||
##### 在 `.env` 中添加
|
||||
|
||||
```bash
|
||||
APSCHEDULER_CONFIG={"apscheduler.timezone": "Asia/Shanghai"}
|
||||
```
|
||||
|
||||
##### 在 `bot.py` 中添加
|
||||
|
||||
```python
|
||||
nonebot.init(apscheduler_config={
|
||||
"apscheduler.timezone": "Asia/Shanghai"
|
@@ -9,6 +9,23 @@ sidebarDepth: 0
|
||||
|
||||
协议详情请看: [CQHTTP](https://github.com/howmanybots/onebot/blob/master/README.md) | [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md)
|
||||
|
||||
# NoneBot.adapters.cqhttp.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
CQHTTP 配置类
|
||||
|
||||
|
||||
* **配置项**
|
||||
|
||||
|
||||
* `access_token` / `cqhttp_access_token`: CQHTTP 协议授权令牌
|
||||
|
||||
|
||||
* `secret` / `cqhttp_secret`: CQHTTP HTTP 上报数据签名口令
|
||||
|
||||
|
||||
# NoneBot.adapters.cqhttp.utils 模块
|
||||
|
||||
|
@@ -9,6 +9,23 @@ sidebarDepth: 0
|
||||
|
||||
协议详情请看: [钉钉文档](https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p)
|
||||
|
||||
# NoneBot.adapters.ding.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
钉钉配置类
|
||||
|
||||
|
||||
* **配置项**
|
||||
|
||||
|
||||
* `access_token` / `ding_access_token`: 钉钉令牌
|
||||
|
||||
|
||||
* `secret` / `ding_secret`: 钉钉 HTTP 上报数据签名口令
|
||||
|
||||
|
||||
# NoneBot.adapters.ding.exception 模块
|
||||
|
||||
|
@@ -21,6 +21,26 @@ Mirai-API-HTTP 的适配器以 [AGPLv3许可](https://opensource.org/licenses/AG
|
||||
这意味着在使用该适配器时需要 **以该许可开源您的完整程序代码**
|
||||
:::
|
||||
|
||||
# NoneBot.adapters.mirai.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
Mirai 配置类
|
||||
|
||||
|
||||
* **必填**
|
||||
|
||||
|
||||
* `auth_key` / `mirai_auth_key`: mirai-api-http 的 auth_key
|
||||
|
||||
|
||||
* `mirai_host`: mirai-api-http 的地址
|
||||
|
||||
|
||||
* `mirai_port`: mirai-api-http 的端口
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.bot 模块
|
||||
|
||||
|
||||
@@ -690,28 +710,6 @@ mirai-api-http 正向 Websocket 协议 Bot 适配。
|
||||
* `qq: int`: 要使用的Bot的QQ号 **注意: 在使用正向Websocket时必须指定该值!**
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
基类:`pydantic.main.BaseModel`
|
||||
|
||||
Mirai 配置类
|
||||
|
||||
|
||||
* **必填**
|
||||
|
||||
|
||||
* `mirai_auth_key`: mirai-api-http的auth_key
|
||||
|
||||
|
||||
* `mirai_host`: mirai-api-http的地址
|
||||
|
||||
|
||||
* `mirai_port`: mirai-api-http的端口
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.message 模块
|
||||
|
||||
|
@@ -120,7 +120,7 @@ Driver 基类。将后端框架封装,以满足适配器使用。
|
||||
|
||||
|
||||
|
||||
### `register_adapter(name, adapter)`
|
||||
### `register_adapter(name, adapter, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
@@ -10,6 +10,58 @@ sidebarDepth: 0
|
||||
后端使用方法请参考: [FastAPI 文档](https://fastapi.tiangolo.com/)
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
基类:`pydantic.env_settings.BaseSettings`
|
||||
|
||||
FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
||||
|
||||
|
||||
### `fastapi_openapi_url`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[str]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
openapi.json 地址,默认为 None 即关闭
|
||||
|
||||
|
||||
|
||||
### `fastapi_docs_url`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[str]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
swagger 地址,默认为 None 即关闭
|
||||
|
||||
|
||||
|
||||
### `fastapi_redoc_url`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[str]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
redoc 地址,默认为 None 即关闭
|
||||
|
||||
|
||||
|
||||
## _class_ `Driver`
|
||||
|
||||
基类:[`nonebot.drivers.Driver`](README.md#nonebot.drivers.Driver)
|
@@ -960,7 +960,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_startswith(msg, rule=None, **kwargs)`
|
||||
### `on_startswith(msg, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1007,7 +1007,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_endswith(msg, rule=None, **kwargs)`
|
||||
### `on_endswith(msg, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1054,7 +1054,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_keyword(keywords, rule=None, **kwargs)`
|
||||
### `on_keyword(keywords, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1101,7 +1101,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_command(cmd, rule=None, aliases=None, **kwargs)`
|
||||
### `on_command(cmd, aliases=None, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1118,12 +1118,12 @@ def something_else():
|
||||
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
|
||||
|
||||
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
|
||||
|
||||
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
|
||||
|
||||
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
|
||||
|
||||
* `permission: Optional[Permission]`: 事件响应权限
|
||||
|
||||
|
||||
@@ -1153,64 +1153,64 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_shell_command(cmd, rule=None, aliases=None, parser=None, **kwargs)`
|
||||
### `on_shell_command(cmd, aliases=None, parser=None, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
|
||||
|
||||
注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
|
||||
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
|
||||
|
||||
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
|
||||
并将用户输入的原始参数列表保存在 `state["argv"]`, `parser` 处理的参数保存在 `state["args"]` 中
|
||||
|
||||
并将用户输入的原始参数列表保存在 `state["argv"]`, `parser` 处理的参数保存在 `state["args"]` 中
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
|
||||
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
|
||||
|
||||
|
||||
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
|
||||
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
|
||||
|
||||
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
* `parser: Optional[ArgumentParser]`: `nonebot.rule.ArgumentParser` 对象
|
||||
|
||||
|
||||
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
|
||||
|
||||
* `parser: Optional[ArgumentParser]`: `nonebot.rule.ArgumentParser` 对象
|
||||
* `permission: Optional[Permission]`: 事件响应权限
|
||||
|
||||
|
||||
* `permission: Optional[Permission]`: 事件响应权限
|
||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
||||
|
||||
|
||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||
|
||||
|
||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||
* `priority: int`: 事件响应器优先级
|
||||
|
||||
|
||||
* `priority: int`: 事件响应器优先级
|
||||
* `block: bool`: 是否阻止事件向更低优先级传递
|
||||
|
||||
|
||||
* `block: bool`: 是否阻止事件向更低优先级传递
|
||||
* `state: Optional[T_State]`: 默认 state
|
||||
|
||||
|
||||
* `state: Optional[T_State]`: 默认 state
|
||||
* `state_factory: Optional[T_StateFactory]`: 默认 state 的工厂函数
|
||||
|
||||
|
||||
* `state_factory: Optional[T_StateFactory]`: 默认 state 的工厂函数
|
||||
|
||||
|
||||
* **返回**
|
||||
|
||||
|
||||
* `Type[Matcher]`
|
||||
|
||||
|
||||
* `Type[Matcher]`
|
||||
|
||||
|
||||
### `on_regex(pattern, flags=0, rule=None, **kwargs)`
|
||||
### `on_regex(pattern, flags=0, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
@@ -8,8 +8,6 @@
|
||||
初次使用时可能会觉得这里的概览过于枯燥,可以先简单略读之后直接前往 [安装](./installation.md) 查看安装方法,并进行后续的基础使用教程。
|
||||
:::
|
||||
|
||||
## 简介
|
||||
|
||||
NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的事件进行解析和处理,并以插件化的形式,按优先级分发给事件所对应的事件响应器,来完成具体的功能。
|
||||
|
||||
除了起到解析事件的作用,NoneBot 还为插件提供了大量实用的预设操作和权限控制机制。对于命令处理,它更是提供了完善且易用的会话机制和内部调用机制,以分别适应命令的连续交互和插件内部功能复用等需求。
|
@@ -2,7 +2,7 @@
|
||||
|
||||
到目前为止我们还在使用 NoneBot 的默认行为,在开始编写自己的插件之前,我们先尝试在配置文件上动动手脚,让 NoneBot 表现出不同的行为。
|
||||
|
||||
在上一章节中,我们创建了默认的项目结构,其中 `.env`, `.env.*` 均为项目的配置文件,下面将介绍几种 NoneBot 配置方式。
|
||||
在上一章节中,我们创建了默认的项目结构,其中 `.env` 和 `.env.*` 均为项目的配置文件,下面将介绍几种 NoneBot 配置方式。
|
||||
|
||||
:::danger 警告
|
||||
请勿将敏感信息写入配置文件并提交至开源仓库!
|
||||
@@ -83,4 +83,4 @@ config.custom_config4 = "new config after init"
|
||||
|
||||
## 优先级
|
||||
|
||||
`bot.py init` > `system env` > `env file`
|
||||
`bot.py` 文件( `nonebot.init` ) > 系统环境变量 > `.env` `.env.*` 文件
|
@@ -34,18 +34,18 @@ AweSome-Bot
|
||||
|
||||
## 启动 Bot
|
||||
|
||||
如果你使用 `nb-cli`
|
||||
通过 `nb-cli`
|
||||
|
||||
```bash
|
||||
nb run [--file=bot.py] [--app=app]
|
||||
```
|
||||
|
||||
或者使用
|
||||
或
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
如果在 bot 入口文件内定义了 asgi server, `nb-cli` 将会为你启动**冷重载模式**
|
||||
如果在 bot 入口文件内定义了 asgi server, `nb-cli` 将会为你启动**冷重载模式**(当文件发生变动时自动重启 NoneBot 实例)
|
||||
:::
|
@@ -1,9 +1,9 @@
|
||||
# 钉钉机器人使用指南
|
||||
|
||||
基于企业机器人的outgoing(回调)机制,用户@机器人之后,钉钉会将消息内容POST到开发者的消息接收地址。开发者解析出消息内容、发送者身份,根据企业的业务逻辑,组装响应的消息内容返回,钉钉会将响应内容发送到群里。
|
||||
基于企业机器人的 outgoing(回调)机制,用户@机器人之后,钉钉会将消息内容 POST 到开发者的消息接收地址。开发者解析出消息内容、发送者身份,根据企业的业务逻辑,组装响应的消息内容返回,钉钉会将响应内容发送到群里。
|
||||
|
||||
::: warning 只有企业内部机器人支持接收消息
|
||||
普通的机器人尚不支持应答机制,该机制指的是群里成员在聊天@机器人的时候,钉钉回调指定的服务地址,即Outgoing机器人。
|
||||
普通的机器人尚不支持应答机制,该机制指的是群里成员在聊天@机器人的时候,钉钉回调指定的服务地址,即 Outgoing 机器人。
|
||||
:::
|
||||
|
||||
首先你需要有钉钉机器人的相关概念,请参阅相关文档:
|
@@ -1,16 +1,16 @@
|
||||
# 开始使用
|
||||
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 NoneBot 实例的准备。
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 NoneBot 实例的准备工作。
|
||||
|
||||
## 最小实例
|
||||
|
||||
如果你已经按照推荐方式安装了 `nb-cli`,使用脚手架创建一个空项目:
|
||||
如果你已经按照推荐方式安装了 `nb-cli`,使用它创建一个空项目:
|
||||
|
||||
```bash
|
||||
nb create
|
||||
```
|
||||
|
||||
根据脚手架引导,将在当前目录下创建一个项目目录,项目目录内包含 `bot.py`。
|
||||
根据引导进行项目配置,完成后会在当前目录下创建一个项目目录,项目目录内包含 `bot.py`。
|
||||
|
||||
如果未安装 `nb-cli`,使用你最熟悉的编辑器或 IDE,创建一个名为 `bot.py` 的文件,内容如下(这里以 CQHTTP 适配器为例):
|
||||
|
@@ -7,7 +7,7 @@
|
||||
:::
|
||||
|
||||
:::warning 注意
|
||||
请在安装 nonebot2 之前卸载 nonebot 1.x
|
||||
请在安装 NoneBot v2 之前卸载 NoneBot v1
|
||||
|
||||
```bash
|
||||
pip uninstall nonebot
|
||||
@@ -15,10 +15,10 @@ pip uninstall nonebot
|
||||
|
||||
:::
|
||||
|
||||
### 通过脚手架安装(推荐安装方式)
|
||||
### (推荐安装方式)通过脚手架安装
|
||||
|
||||
1. (推荐)使用你喜欢的 Python 环境管理工具(如 `poetry`)创建新的虚拟环境。
|
||||
2. 使用 `pip` (或其他包管理工具) 安装 nb-cli,nonebot2 作为其依赖会一起被安装。
|
||||
1. (推荐)使用你喜欢的 Python 环境管理工具(如 `poetry`)创建新的虚拟环境
|
||||
2. 使用 `pip` 或 其他包管理工具 安装 `nb-cli`,`nonebot2` 会作为其依赖被一起安装
|
||||
|
||||
```bash
|
||||
pip install nb-cli
|
||||
@@ -26,17 +26,17 @@ pip uninstall nonebot
|
||||
|
||||
3. 点个 star 吧
|
||||
|
||||
nonebot2: [](https://github.com/nonebot/nonebot2)
|
||||
nonebot2: [](https://github.com/nonebot/nonebot2)
|
||||
|
||||
nb-cli: [](https://github.com/nonebot/nb-cli)
|
||||
|
||||
4. 如果有疑问,可以加群交流 (点击链接直达)
|
||||
4. 如果有疑问,可以加群交流(点击链接直达)
|
||||
|
||||
[](https://jq.qq.com/?_wv=1027&k=5OFifDh)
|
||||
|
||||
[](https://t.me/cqhttp)
|
||||
|
||||
### 不使用脚手架(纯净安装)
|
||||
### (纯净安装)不使用脚手架
|
||||
|
||||
```bash
|
||||
pip install nonebot2
|
||||
@@ -84,8 +84,6 @@ nb plugin install xxx
|
||||
|
||||
### 官方插件
|
||||
|
||||
~~自用插件~~ ~~确信~~
|
||||
|
||||
- [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-APScheduler](https://github.com/nonebot/plugin-apscheduler) 定时任务插件
|
@@ -4,7 +4,7 @@
|
||||
|
||||
Mirai-API-HTTP 的适配现在仍然处于早期阶段, 可能没有进行过充分的测试
|
||||
|
||||
请在生产环境中谨慎使用
|
||||
在生产环境中请谨慎使用
|
||||
|
||||
:::
|
||||
|
||||
@@ -34,7 +34,7 @@ Mirai-API-HTTP 的适配器以 [AGPLv3 许可](https://opensource.org/licenses/A
|
||||
|
||||
> 单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。
|
||||
|
||||
这次, 我们将采用在实现上有别于 onebot<sup>即 CQHTTP</sup>协议的另外一种无头 QQ API 协议, 即 MAH
|
||||
这次, 我们将采用在实现上有别于 OneBot(CQHTTP)协议的另外一种无头 QQ API 协议, 即 MAH
|
||||
|
||||
为了配置 MAH 端, 我们现在需要移步到[MAH 的项目地址](https://github.com/project-mirai/mirai-api-http), 来看看它是如何配置的
|
||||
|
@@ -1 +0,0 @@
|
||||
# 运行时插槽
|
@@ -3,7 +3,7 @@ const path = require("path");
|
||||
module.exports = context => ({
|
||||
base: process.env.VUEPRESS_BASE || "/",
|
||||
title: "NoneBot",
|
||||
description: "基于 酷Q 的 Python 异步 QQ 机器人框架",
|
||||
description: "跨平台 Python 异步 QQ 机器人框架",
|
||||
markdown: {
|
||||
lineNumbers: true
|
||||
},
|
||||
@@ -56,7 +56,7 @@ module.exports = context => ({
|
||||
"/": {
|
||||
lang: "zh-CN",
|
||||
title: "NoneBot",
|
||||
description: "基于 酷Q 的 Python 异步 QQ 机器人框架"
|
||||
description: "跨平台 Python 异步 QQ 机器人框架"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -198,6 +198,10 @@ module.exports = context => ({
|
||||
title: "nonebot.drivers.fastapi 模块",
|
||||
path: "drivers/fastapi"
|
||||
},
|
||||
{
|
||||
title: "nonebot.drivers.quart 模块",
|
||||
path: "drivers/quart"
|
||||
},
|
||||
{
|
||||
title: "nonebot.adapters 模块",
|
||||
path: "adapters/"
|
||||
|
@@ -3,7 +3,7 @@
|
||||
"short_name": "NoneBot",
|
||||
"background-color": "#ffffff",
|
||||
"theme-color": "#ea5252",
|
||||
"description": "An asynchronous python bot framework.",
|
||||
"description": "跨平台 Python 异步 QQ 机器人框架",
|
||||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
|
@@ -88,7 +88,7 @@
|
||||
"repo": "abrahum/nonebot_plugin_simdraw"
|
||||
},
|
||||
{
|
||||
"id": "nonebot-plugin-wordbank",
|
||||
"id": "nonebot_plugin_wordbank",
|
||||
"link": "nonebot-plugin-wordbank",
|
||||
"author": "Joenothing-lst",
|
||||
"desc": "无数据库的轻量问答插件,支持模糊问答",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
[
|
||||
"2.0.0a9",
|
||||
"2.0.0a9.post1",
|
||||
"2.0.0a8.post2",
|
||||
"2.0.0a7"
|
||||
]
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
home: true
|
||||
heroImage: /logo.png
|
||||
tagline: An asynchronous bot framework.
|
||||
tagline: 跨平台 Python 异步 QQ 机器人框架
|
||||
actionText: 开始使用
|
||||
actionLink: guide/
|
||||
features:
|
||||
|
@@ -1 +1,117 @@
|
||||
# 跨插件访问
|
||||
|
||||
由于 `nonebot2` 独特的插件加载机制,在使用 python 原有的 import 机制来进行插件之间的访问时,很可能会有奇怪的或者意料以外的情况发生。为了避免这种情况的发生,您可以有两种方法来实现跨插件访问:
|
||||
|
||||
1. 将插件间的要使用的公共代码剥离出来,作为公共文件或者文件夹,提供给插件加以调用。
|
||||
2. 使用 `nonebot2` 提供的 `export` 和 `require` 机制,来实现插件间的互相调用。
|
||||
|
||||
第一种方法比较容易理解和实现,这里不再赘述,但需要注意的是,请不要将公共文件或者公共文件夹作为**插件**被 `nonebot2` 加载。
|
||||
|
||||
下面将介绍第二种方法—— `export` 和 `require` 机制:
|
||||
|
||||
## 使用 export and require
|
||||
|
||||
现在,假定有两个插件 `pluginA` 和 `pluginB`,需要在 `pluginB` 中调用 `pluginA` 中的一个变量 `varA` 和一个函数 `funcA`。
|
||||
|
||||
在上面的条件中涉及到了两种操作:一种是在 `pluginA` 的 `导出对象` 操作;而另一种是在 `pluginB` 的 `导入对象` 操作。在 `nonebot2` 中,`导出对象` 的操作用 `export` 机制来实现,`导入对象` 的操作用 `require` 机制来实现。下面,我们将逐一进行介绍。
|
||||
|
||||
:::warning 警告
|
||||
|
||||
使用这个方法进行跨插件访问时,**需要先加载`导出对象`的插件,再加载`导入对象`的插件。**
|
||||
|
||||
:::
|
||||
|
||||
### 使用 export
|
||||
|
||||
在 `pluginA` 中,我们调用 `export` 机制 `导出对象`。
|
||||
|
||||
在 `export` 机制调用前,我们需要保证导出的对象已经被定义,比如:
|
||||
|
||||
```python
|
||||
varA = "varA"
|
||||
|
||||
|
||||
def funcA():
|
||||
return "funcA"
|
||||
```
|
||||
|
||||
在确保定义之后,我们可以从 `nonebot.plugin` 导入 `export()` 方法, `export()` 方法会返回一个特殊的字典 `export`:
|
||||
|
||||
```python
|
||||
from nonebot.plugin import export
|
||||
|
||||
export=export()
|
||||
```
|
||||
|
||||
这个字典可以用来装载导出的对象,它的 key 是对象导出后的命名,value 是对象本身,我们可以直接创建新的 `key` - `value` 对导出对象:
|
||||
|
||||
```python
|
||||
export.vA = varA
|
||||
export.fA = funcA
|
||||
```
|
||||
|
||||
除此之外,也支持 `嵌套` 导出对象:
|
||||
|
||||
```python
|
||||
export.sub.vA = varA
|
||||
export.sub.fA = funcA
|
||||
```
|
||||
|
||||
特别地,对于 `函数对象` 而言,`export` 支持用 `装饰器` 的方法来导出,因此,我们可以这样定义 `funcA`:
|
||||
|
||||
```python
|
||||
@export.sub
|
||||
def funcA():
|
||||
return "funcA"
|
||||
```
|
||||
|
||||
或者:
|
||||
|
||||
```python
|
||||
@export
|
||||
def funcA():
|
||||
return "funcA"
|
||||
```
|
||||
|
||||
通过 `装饰器` 的方法导出函数时,命名固定为函数的命名,也就是说,上面的两个例子等同于:
|
||||
|
||||
```python
|
||||
export.sub.funcA = funcA
|
||||
|
||||
export.funcA = funcA
|
||||
```
|
||||
|
||||
这样,我们就成功导出 `varA` 和 `funcA` 对象了。
|
||||
|
||||
下面我们将介绍如何在 `pluginB` 中导入这些对象。
|
||||
|
||||
### 使用 require
|
||||
|
||||
在 `pluginB` 中,我们调用 `require` 机制 `导入对象`。
|
||||
|
||||
:::warning 警告
|
||||
|
||||
在导入来自其他插件的对象时, 请确保导出该对象的插件在引用该对象的插件之前加载。如果该插件并未被加载,则会尝试加载,加载失败则会返回 `None`。
|
||||
|
||||
:::
|
||||
|
||||
我们可以从 `nonebot.plugin` 中导入 `require()` 方法:
|
||||
|
||||
```python
|
||||
from nonebot.plugin import require
|
||||
```
|
||||
|
||||
`require()` 方法的参数是插件名, 它会返回在指定插件中,用 `export()` 方法创建的字典。
|
||||
|
||||
```python
|
||||
require_A = require('pluginA')
|
||||
```
|
||||
|
||||
在之前,这个字典已经存入了 `'vA'` - `varA`, `'fA'` - `funcA` 或 `'funcA'` - `funcA` 这样的 `key` - `value` 对。因此在这里我们直接用 `属性` 的方法来获取导入对象:
|
||||
|
||||
```python
|
||||
varA = require_A.vA
|
||||
funcA = require_A.fA or require_A.funcA
|
||||
```
|
||||
|
||||
这样,我们就在 `pluginB` 中成功导入了 `varA` 和 `funcA` 对象了。
|
||||
|
@@ -1 +1 @@
|
||||
# 运行时插槽
|
||||
# 钩子函数
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
> Advanced Python Scheduler (APScheduler) is a Python library that lets you schedule your Python code to be executed later, either just once or periodically. You can add new jobs or remove old ones on the fly as you please. If you store your jobs in a database, they will also survive scheduler restarts and maintain their state. When the scheduler is restarted, it will then run all the jobs it should have run while it was offline.
|
||||
|
||||
## 从 v1 迁移
|
||||
## 从 NoneBot v1 迁移
|
||||
|
||||
`APScheduler` 作为 `nonebot` v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。`nonebot2` 已将 `APScheduler` 独立为 `nonebot_plugin_apscheduler` 插件,你可以在 [插件广场](https://v2.nonebot.dev/plugin-store.html) 中找到它。
|
||||
|
||||
@@ -21,7 +21,7 @@ nb plugin install nonebot_plugin_apscheduler
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
`nb-cli` 默认通过 `pypi` 安装,你可以使用 `-i [mirror]` 或 `--index [mirror]` 来使用镜像源安装。
|
||||
`nb-cli` 默认通过 `pypi` 安装,你可以添加命令参数 `-i [mirror]` 或 `--index [mirror]` 以使用镜像源安装。
|
||||
:::
|
||||
|
||||
### 通过 poetry
|
||||
@@ -96,10 +96,14 @@ scheduler = require('nonebot_plugin_apscheduler').scheduler
|
||||
|
||||
对于大多数情况,我们需要在 `nonebot2` 项目被启动时启动定时任务,则此处设为 `true`
|
||||
|
||||
##### 在 `.env` 中添加
|
||||
|
||||
```bash
|
||||
APSCHEDULER_AUTOSTART=true
|
||||
```
|
||||
|
||||
##### 在 `bot.py` 中添加
|
||||
|
||||
```python
|
||||
nonebot.init(apscheduler_autostart=True)
|
||||
```
|
||||
@@ -116,10 +120,14 @@ nonebot.init(apscheduler_autostart=True)
|
||||
|
||||
> 官方文档在绝大多数时候能提供最准确和最具时效性的指南
|
||||
|
||||
##### 在 `.env` 中添加
|
||||
|
||||
```bash
|
||||
APSCHEDULER_CONFIG={"apscheduler.timezone": "Asia/Shanghai"}
|
||||
```
|
||||
|
||||
##### 在 `bot.py` 中添加
|
||||
|
||||
```python
|
||||
nonebot.init(apscheduler_config={
|
||||
"apscheduler.timezone": "Asia/Shanghai"
|
||||
|
@@ -43,6 +43,9 @@
|
||||
* [nonebot.drivers.fastapi](drivers/fastapi.html)
|
||||
|
||||
|
||||
* [nonebot.drivers.quart](drivers/quart.html)
|
||||
|
||||
|
||||
* [nonebot.adapters](adapters/)
|
||||
|
||||
|
||||
|
@@ -9,6 +9,23 @@ sidebarDepth: 0
|
||||
|
||||
协议详情请看: [CQHTTP](https://github.com/howmanybots/onebot/blob/master/README.md) | [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md)
|
||||
|
||||
# NoneBot.adapters.cqhttp.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
CQHTTP 配置类
|
||||
|
||||
|
||||
* **配置项**
|
||||
|
||||
|
||||
* `access_token` / `cqhttp_access_token`: CQHTTP 协议授权令牌
|
||||
|
||||
|
||||
* `secret` / `cqhttp_secret`: CQHTTP HTTP 上报数据签名口令
|
||||
|
||||
|
||||
# NoneBot.adapters.cqhttp.utils 模块
|
||||
|
||||
|
||||
|
@@ -9,6 +9,23 @@ sidebarDepth: 0
|
||||
|
||||
协议详情请看: [钉钉文档](https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p)
|
||||
|
||||
# NoneBot.adapters.ding.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
钉钉配置类
|
||||
|
||||
|
||||
* **配置项**
|
||||
|
||||
|
||||
* `access_token` / `ding_access_token`: 钉钉令牌
|
||||
|
||||
|
||||
* `secret` / `ding_secret`: 钉钉 HTTP 上报数据签名口令
|
||||
|
||||
|
||||
# NoneBot.adapters.ding.exception 模块
|
||||
|
||||
|
||||
|
@@ -21,6 +21,26 @@ Mirai-API-HTTP 的适配器以 [AGPLv3许可](https://opensource.org/licenses/AG
|
||||
这意味着在使用该适配器时需要 **以该许可开源您的完整程序代码**
|
||||
:::
|
||||
|
||||
# NoneBot.adapters.mirai.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
Mirai 配置类
|
||||
|
||||
|
||||
* **必填**
|
||||
|
||||
|
||||
* `auth_key` / `mirai_auth_key`: mirai-api-http 的 auth_key
|
||||
|
||||
|
||||
* `mirai_host`: mirai-api-http 的地址
|
||||
|
||||
|
||||
* `mirai_port`: mirai-api-http 的端口
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.bot 模块
|
||||
|
||||
|
||||
@@ -690,28 +710,6 @@ mirai-api-http 正向 Websocket 协议 Bot 适配。
|
||||
* `qq: int`: 要使用的Bot的QQ号 **注意: 在使用正向Websocket时必须指定该值!**
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.config 模块
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
基类:`pydantic.main.BaseModel`
|
||||
|
||||
Mirai 配置类
|
||||
|
||||
|
||||
* **必填**
|
||||
|
||||
|
||||
* `mirai_auth_key`: mirai-api-http的auth_key
|
||||
|
||||
|
||||
* `mirai_host`: mirai-api-http的地址
|
||||
|
||||
|
||||
* `mirai_port`: mirai-api-http的端口
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.message 模块
|
||||
|
||||
|
||||
@@ -967,15 +965,40 @@ CQHTTP 协议 MessageSegment 适配。具体方法参考 [mirai-api-http 消息
|
||||
|
||||
基类:[`nonebot.adapters.Message`](README.md#nonebot.adapters.Message)
|
||||
|
||||
Mirai 协议 Messaqge 适配
|
||||
Mirai 协议 Message 适配
|
||||
|
||||
由于Mirai协议的Message实现较为特殊, 故使用MessageChain命名
|
||||
|
||||
|
||||
### `reduce()`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
忽略为空的消息段, 合并相邻的纯文本消息段
|
||||
|
||||
|
||||
|
||||
### `export()`
|
||||
|
||||
导出为可以被正常json序列化的数组
|
||||
|
||||
|
||||
### `extract_first(*type)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
弹出该消息链的第一个消息
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
|
||||
* \*type: MessageType: 指定的消息类型, 当指定后如类型不匹配不弹出
|
||||
|
||||
|
||||
# NoneBot.adapters.mirai.utils 模块
|
||||
|
||||
|
||||
@@ -1072,20 +1095,6 @@ mirai-api-http 协议事件,字段与 mirai-api-http 一致。各事件字段
|
||||
> * `MEMBER`: 普通群成员
|
||||
|
||||
|
||||
## _class_ `MessageChain`
|
||||
|
||||
基类:[`nonebot.adapters.Message`](README.md#nonebot.adapters.Message)
|
||||
|
||||
Mirai 协议 Messaqge 适配
|
||||
|
||||
由于Mirai协议的Message实现较为特殊, 故使用MessageChain命名
|
||||
|
||||
|
||||
### `export()`
|
||||
|
||||
导出为可以被正常json序列化的数组
|
||||
|
||||
|
||||
## _class_ `MessageEvent`
|
||||
|
||||
基类:`nonebot.adapters.mirai.event.base.Event`
|
||||
|
@@ -120,7 +120,7 @@ Driver 基类。将后端框架封装,以满足适配器使用。
|
||||
|
||||
|
||||
|
||||
### `register_adapter(name, adapter)`
|
||||
### `register_adapter(name, adapter, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
@@ -10,6 +10,58 @@ sidebarDepth: 0
|
||||
后端使用方法请参考: [FastAPI 文档](https://fastapi.tiangolo.com/)
|
||||
|
||||
|
||||
## _class_ `Config`
|
||||
|
||||
基类:`pydantic.env_settings.BaseSettings`
|
||||
|
||||
FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
||||
|
||||
|
||||
### `fastapi_openapi_url`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[str]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
openapi.json 地址,默认为 None 即关闭
|
||||
|
||||
|
||||
|
||||
### `fastapi_docs_url`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[str]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
swagger 地址,默认为 None 即关闭
|
||||
|
||||
|
||||
|
||||
### `fastapi_redoc_url`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[str]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
redoc 地址,默认为 None 即关闭
|
||||
|
||||
|
||||
|
||||
## _class_ `Driver`
|
||||
|
||||
基类:[`nonebot.drivers.Driver`](README.md#nonebot.drivers.Driver)
|
||||
|
62
docs/api/drivers/quart.md
Normal file
62
docs/api/drivers/quart.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
contentSidebar: true
|
||||
sidebarDepth: 0
|
||||
---
|
||||
|
||||
# NoneBot.drivers.quart 模块
|
||||
|
||||
## Quart 驱动适配
|
||||
|
||||
后端使用方法请参考: [Quart 文档](https://pgjones.gitlab.io/quart/index.html)
|
||||
|
||||
|
||||
## _class_ `Driver`
|
||||
|
||||
基类:[`nonebot.drivers.Driver`](README.md#nonebot.drivers.Driver)
|
||||
|
||||
Quart 驱动框架
|
||||
|
||||
|
||||
* **上报地址**
|
||||
|
||||
|
||||
* `/{adapter name}/http`: HTTP POST 上报
|
||||
|
||||
|
||||
* `/{adapter name}/ws`: WebSocket 上报
|
||||
|
||||
|
||||
|
||||
### _property_ `type`
|
||||
|
||||
驱动名称: `quart`
|
||||
|
||||
|
||||
### _property_ `server_app`
|
||||
|
||||
`Quart` 对象
|
||||
|
||||
|
||||
### _property_ `asgi`
|
||||
|
||||
`Quart` 对象
|
||||
|
||||
|
||||
### _property_ `logger`
|
||||
|
||||
fastapi 使用的 logger
|
||||
|
||||
|
||||
### `on_startup(func)`
|
||||
|
||||
参考文档: [Startup and Shutdown](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)
|
||||
|
||||
|
||||
### `on_shutdown(func)`
|
||||
|
||||
参考文档: [Startup and Shutdown](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)
|
||||
|
||||
|
||||
### `run(host=None, port=None, *, app=None, **kwargs)`
|
||||
|
||||
使用 `uvicorn` 启动 Quart
|
@@ -960,7 +960,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_startswith(msg, rule=None, **kwargs)`
|
||||
### `on_startswith(msg, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1007,7 +1007,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_endswith(msg, rule=None, **kwargs)`
|
||||
### `on_endswith(msg, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1054,7 +1054,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_keyword(keywords, rule=None, **kwargs)`
|
||||
### `on_keyword(keywords, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1101,7 +1101,7 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_command(cmd, rule=None, aliases=None, **kwargs)`
|
||||
### `on_command(cmd, aliases=None, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@@ -1118,12 +1118,12 @@ def something_else():
|
||||
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
|
||||
|
||||
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
|
||||
|
||||
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
|
||||
|
||||
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
|
||||
|
||||
* `permission: Optional[Permission]`: 事件响应权限
|
||||
|
||||
|
||||
@@ -1153,64 +1153,64 @@ def something_else():
|
||||
|
||||
|
||||
|
||||
### `on_shell_command(cmd, rule=None, aliases=None, parser=None, **kwargs)`
|
||||
### `on_shell_command(cmd, aliases=None, parser=None, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
|
||||
|
||||
注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
|
||||
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
|
||||
|
||||
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
|
||||
并将用户输入的原始参数列表保存在 `state["argv"]`, `parser` 处理的参数保存在 `state["args"]` 中
|
||||
|
||||
并将用户输入的原始参数列表保存在 `state["argv"]`, `parser` 处理的参数保存在 `state["args"]` 中
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
|
||||
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
|
||||
|
||||
|
||||
* `cmd: Union[str, Tuple[str, ...]]`: 指定命令内容
|
||||
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
|
||||
|
||||
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
* `parser: Optional[ArgumentParser]`: `nonebot.rule.ArgumentParser` 对象
|
||||
|
||||
|
||||
* `aliases: Optional[Set[Union[str, Tuple[str, ...]]]]`: 命令别名
|
||||
* `rule: Optional[Union[Rule, T_RuleChecker]]`: 事件响应规则
|
||||
|
||||
|
||||
* `parser: Optional[ArgumentParser]`: `nonebot.rule.ArgumentParser` 对象
|
||||
* `permission: Optional[Permission]`: 事件响应权限
|
||||
|
||||
|
||||
* `permission: Optional[Permission]`: 事件响应权限
|
||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
||||
|
||||
|
||||
* `handlers: Optional[List[T_Handler]]`: 事件处理函数列表
|
||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||
|
||||
|
||||
* `temp: bool`: 是否为临时事件响应器(仅执行一次)
|
||||
* `priority: int`: 事件响应器优先级
|
||||
|
||||
|
||||
* `priority: int`: 事件响应器优先级
|
||||
* `block: bool`: 是否阻止事件向更低优先级传递
|
||||
|
||||
|
||||
* `block: bool`: 是否阻止事件向更低优先级传递
|
||||
* `state: Optional[T_State]`: 默认 state
|
||||
|
||||
|
||||
* `state: Optional[T_State]`: 默认 state
|
||||
* `state_factory: Optional[T_StateFactory]`: 默认 state 的工厂函数
|
||||
|
||||
|
||||
* `state_factory: Optional[T_StateFactory]`: 默认 state 的工厂函数
|
||||
|
||||
|
||||
* **返回**
|
||||
|
||||
|
||||
* `Type[Matcher]`
|
||||
|
||||
|
||||
* `Type[Matcher]`
|
||||
|
||||
|
||||
### `on_regex(pattern, flags=0, rule=None, **kwargs)`
|
||||
### `on_regex(pattern, flags=0, **kwargs)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
@@ -8,8 +8,6 @@
|
||||
初次使用时可能会觉得这里的概览过于枯燥,可以先简单略读之后直接前往 [安装](./installation.md) 查看安装方法,并进行后续的基础使用教程。
|
||||
:::
|
||||
|
||||
## 简介
|
||||
|
||||
NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的事件进行解析和处理,并以插件化的形式,按优先级分发给事件所对应的事件响应器,来完成具体的功能。
|
||||
|
||||
除了起到解析事件的作用,NoneBot 还为插件提供了大量实用的预设操作和权限控制机制。对于命令处理,它更是提供了完善且易用的会话机制和内部调用机制,以分别适应命令的连续交互和插件内部功能复用等需求。
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
到目前为止我们还在使用 NoneBot 的默认行为,在开始编写自己的插件之前,我们先尝试在配置文件上动动手脚,让 NoneBot 表现出不同的行为。
|
||||
|
||||
在上一章节中,我们创建了默认的项目结构,其中 `.env`, `.env.*` 均为项目的配置文件,下面将介绍几种 NoneBot 配置方式。
|
||||
在上一章节中,我们创建了默认的项目结构,其中 `.env` 和 `.env.*` 均为项目的配置文件,下面将介绍几种 NoneBot 配置方式。
|
||||
|
||||
:::danger 警告
|
||||
请勿将敏感信息写入配置文件并提交至开源仓库!
|
||||
@@ -83,4 +83,4 @@ config.custom_config4 = "new config after init"
|
||||
|
||||
## 优先级
|
||||
|
||||
`bot.py init` > `system env` > `env file`
|
||||
`bot.py` 文件( `nonebot.init` ) > 系统环境变量 > `.env` `.env.*` 文件
|
||||
|
@@ -123,7 +123,7 @@ async def async_checker(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
def sync_checker(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
return True
|
||||
|
||||
def check(arg1, args2):
|
||||
def check(arg1, arg2):
|
||||
|
||||
async def _checker(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
return bool(arg1 + arg2)
|
||||
|
@@ -34,18 +34,18 @@ AweSome-Bot
|
||||
|
||||
## 启动 Bot
|
||||
|
||||
如果你使用 `nb-cli`
|
||||
通过 `nb-cli`
|
||||
|
||||
```bash
|
||||
nb run [--file=bot.py] [--app=app]
|
||||
```
|
||||
|
||||
或者使用
|
||||
或
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
如果在 bot 入口文件内定义了 asgi server, `nb-cli` 将会为你启动**冷重载模式**
|
||||
如果在 bot 入口文件内定义了 asgi server, `nb-cli` 将会为你启动**冷重载模式**(当文件发生变动时自动重启 NoneBot 实例)
|
||||
:::
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# 钉钉机器人使用指南
|
||||
|
||||
基于企业机器人的outgoing(回调)机制,用户@机器人之后,钉钉会将消息内容POST到开发者的消息接收地址。开发者解析出消息内容、发送者身份,根据企业的业务逻辑,组装响应的消息内容返回,钉钉会将响应内容发送到群里。
|
||||
基于企业机器人的 outgoing(回调)机制,用户@机器人之后,钉钉会将消息内容 POST 到开发者的消息接收地址。开发者解析出消息内容、发送者身份,根据企业的业务逻辑,组装响应的消息内容返回,钉钉会将响应内容发送到群里。
|
||||
|
||||
::: warning 只有企业内部机器人支持接收消息
|
||||
普通的机器人尚不支持应答机制,该机制指的是群里成员在聊天@机器人的时候,钉钉回调指定的服务地址,即Outgoing机器人。
|
||||
普通的机器人尚不支持应答机制,该机制指的是群里成员在聊天@机器人的时候,钉钉回调指定的服务地址,即 Outgoing 机器人。
|
||||
:::
|
||||
|
||||
首先你需要有钉钉机器人的相关概念,请参阅相关文档:
|
||||
|
@@ -1,16 +1,16 @@
|
||||
# 开始使用
|
||||
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 NoneBot 实例的准备。
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 NoneBot 实例的准备工作。
|
||||
|
||||
## 最小实例
|
||||
|
||||
如果你已经按照推荐方式安装了 `nb-cli`,使用脚手架创建一个空项目:
|
||||
如果你已经按照推荐方式安装了 `nb-cli`,使用它创建一个空项目:
|
||||
|
||||
```bash
|
||||
nb create
|
||||
```
|
||||
|
||||
根据脚手架引导,将在当前目录下创建一个项目目录,项目目录内包含 `bot.py`。
|
||||
根据引导进行项目配置,完成后会在当前目录下创建一个项目目录,项目目录内包含 `bot.py`。
|
||||
|
||||
如果未安装 `nb-cli`,使用你最熟悉的编辑器或 IDE,创建一个名为 `bot.py` 的文件,内容如下(这里以 CQHTTP 适配器为例):
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
:::
|
||||
|
||||
:::warning 注意
|
||||
请在安装 nonebot2 之前卸载 nonebot 1.x
|
||||
请在安装 NoneBot v2 之前卸载 NoneBot v1
|
||||
|
||||
```bash
|
||||
pip uninstall nonebot
|
||||
@@ -15,10 +15,10 @@ pip uninstall nonebot
|
||||
|
||||
:::
|
||||
|
||||
### 通过脚手架安装(推荐安装方式)
|
||||
### (推荐安装方式)通过脚手架安装
|
||||
|
||||
1. (推荐)使用你喜欢的 Python 环境管理工具(如 `poetry`)创建新的虚拟环境。
|
||||
2. 使用 `pip` (或其他包管理工具) 安装 nb-cli,nonebot2 作为其依赖会一起被安装。
|
||||
1. (推荐)使用你喜欢的 Python 环境管理工具(如 `poetry`)创建新的虚拟环境
|
||||
2. 使用 `pip` 或 其他包管理工具 安装 `nb-cli`,`nonebot2` 会作为其依赖被一起安装
|
||||
|
||||
```bash
|
||||
pip install nb-cli
|
||||
@@ -26,17 +26,17 @@ pip uninstall nonebot
|
||||
|
||||
3. 点个 star 吧
|
||||
|
||||
nonebot2: [](https://github.com/nonebot/nonebot2)
|
||||
nonebot2: [](https://github.com/nonebot/nonebot2)
|
||||
|
||||
nb-cli: [](https://github.com/nonebot/nb-cli)
|
||||
|
||||
4. 如果有疑问,可以加群交流 (点击链接直达)
|
||||
4. 如果有疑问,可以加群交流(点击链接直达)
|
||||
|
||||
[](https://jq.qq.com/?_wv=1027&k=5OFifDh)
|
||||
|
||||
[](https://t.me/cqhttp)
|
||||
|
||||
### 不使用脚手架(纯净安装)
|
||||
### (纯净安装)不使用脚手架
|
||||
|
||||
```bash
|
||||
pip install nonebot2
|
||||
@@ -84,8 +84,6 @@ nb plugin install xxx
|
||||
|
||||
### 官方插件
|
||||
|
||||
~~自用插件~~ ~~确信~~
|
||||
|
||||
- [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-APScheduler](https://github.com/nonebot/plugin-apscheduler) 定时任务插件
|
||||
|
@@ -6,12 +6,15 @@
|
||||
|
||||
在 `bot.py` 文件中添加以下行:
|
||||
|
||||
```python{5}
|
||||
```python{8}
|
||||
import nonebot
|
||||
from nonebot.adapters.cqhttp import Bot
|
||||
|
||||
nonebot.init()
|
||||
# 加载 nonebot 内置插件
|
||||
nonebot.load_builtin_plugins()
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter("cqhttp", Bot) # 注册 CQHTTP 的 Adapter
|
||||
nonebot.load_builtin_plugins() # 加载 nonebot 内置插件
|
||||
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
@@ -19,6 +22,12 @@ if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
::: warning
|
||||
目前, 内建插件仅支持 CQHTTP 的 Adapter
|
||||
|
||||
如果您使用的是其他 Adapter, 请移步该 Adapter 相应的文档
|
||||
:::
|
||||
|
||||
这将会加载 nonebot 内置的插件,它包含:
|
||||
|
||||
- 命令 `say`:可由**superuser**使用,可以将消息内容由特殊纯文本转为富文本
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
Mirai-API-HTTP 的适配现在仍然处于早期阶段, 可能没有进行过充分的测试
|
||||
|
||||
请在生产环境中谨慎使用
|
||||
在生产环境中请谨慎使用
|
||||
|
||||
:::
|
||||
|
||||
@@ -34,7 +34,7 @@ Mirai-API-HTTP 的适配器以 [AGPLv3 许可](https://opensource.org/licenses/A
|
||||
|
||||
> 单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。
|
||||
|
||||
这次, 我们将采用在实现上有别于 onebot<sup>即 CQHTTP</sup>协议的另外一种无头 QQ API 协议, 即 MAH
|
||||
这次, 我们将采用在实现上有别于 OneBot(CQHTTP)协议的另外一种无头 QQ API 协议, 即 MAH
|
||||
|
||||
为了配置 MAH 端, 我们现在需要移步到[MAH 的项目地址](https://github.com/project-mirai/mirai-api-http), 来看看它是如何配置的
|
||||
|
||||
@@ -193,3 +193,36 @@ Mirai-API-HTTP 的适配器以 [AGPLv3 许可](https://opensource.org/licenses/A
|
||||
```
|
||||
|
||||
恭喜你, 你的配置已经成功!
|
||||
|
||||
现在, 我们可以写一个简单的插件来测试一下
|
||||
|
||||
```python
|
||||
from nonebot.plugin import on_keyword, on_command
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.adapters.mirai import Bot, MessageEvent
|
||||
|
||||
message_test = on_keyword({'reply'}, rule=to_me())
|
||||
|
||||
|
||||
@message_test.handle()
|
||||
async def _message(bot: Bot, event: MessageEvent):
|
||||
text = event.get_plaintext()
|
||||
await bot.send(event, text, at_sender=True)
|
||||
|
||||
|
||||
command_test = on_command('miecho')
|
||||
|
||||
|
||||
@command_test.handle()
|
||||
async def _echo(bot: Bot, event: MessageEvent):
|
||||
text = event.get_plaintext()
|
||||
await bot.send(event, text, at_sender=True)
|
||||
```
|
||||
|
||||
它具有两种行为
|
||||
|
||||
- 在指定机器人,即私聊、群聊内@机器人、群聊内称呼机器人昵称的情况下 (即 [Rule: to_me](../api/rule.md#to-me)), 如果消息内包含 `reply` 字段, 则该消息会被机器人重复一次
|
||||
|
||||
- 在执行指令`miecho xxx`时, 机器人会发送回参数`xxx`
|
||||
|
||||
至此, 你已经初步掌握了如何使用 Mirai Adapter
|
||||
|
@@ -15,6 +15,7 @@ NoneBot Api Reference
|
||||
- `nonebot.exception <exception.html>`_
|
||||
- `nonebot.drivers <drivers/>`_
|
||||
- `nonebot.drivers.fastapi <drivers/fastapi.html>`_
|
||||
- `nonebot.drivers.quart <drivers/quart.html>`_
|
||||
- `nonebot.adapters <adapters/>`_
|
||||
- `nonebot.adapters.cqhttp <adapters/cqhttp.html>`_
|
||||
- `nonebot.adapters.ding <adapters/ding.html>`_
|
||||
|
@@ -8,6 +8,12 @@ NoneBot.adapters.cqhttp 模块
|
||||
|
||||
.. automodule:: nonebot.adapters.cqhttp
|
||||
|
||||
NoneBot.adapters.cqhttp.config 模块
|
||||
===================================
|
||||
|
||||
.. automodule:: nonebot.adapters.cqhttp.config
|
||||
:members:
|
||||
|
||||
NoneBot.adapters.cqhttp.utils 模块
|
||||
===================================
|
||||
|
||||
|
@@ -8,6 +8,12 @@ NoneBot.adapters.ding 模块
|
||||
|
||||
.. automodule:: nonebot.adapters.ding
|
||||
|
||||
NoneBot.adapters.ding.config 模块
|
||||
===================================
|
||||
|
||||
.. automodule:: nonebot.adapters.ding.config
|
||||
:members:
|
||||
|
||||
NoneBot.adapters.ding.exception 模块
|
||||
=====================================
|
||||
|
||||
|
@@ -8,6 +8,12 @@ NoneBot.adapters.mirai 模块
|
||||
|
||||
.. automodule:: nonebot.adapters.mirai
|
||||
|
||||
NoneBot.adapters.mirai.config 模块
|
||||
==================================
|
||||
|
||||
.. automodule:: nonebot.adapters.mirai.config
|
||||
:members:
|
||||
|
||||
NoneBot.adapters.mirai.bot 模块
|
||||
===============================
|
||||
|
||||
@@ -22,13 +28,6 @@ NoneBot.adapters.mirai.bot_ws 模块
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
NoneBot.adapters.mirai.config 模块
|
||||
==================================
|
||||
|
||||
.. automodule:: nonebot.adapters.mirai.config
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
NoneBot.adapters.mirai.message 模块
|
||||
===================================
|
||||
|
||||
@@ -83,4 +82,4 @@ NoneBot.adapters.mirai.event.request 模块
|
||||
|
||||
.. automodule:: nonebot.adapters.mirai.event.request
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:show-inheritance:
|
||||
|
12
docs_build/drivers/quart.rst
Normal file
12
docs_build/drivers/quart.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
contentSidebar: true
|
||||
sidebarDepth: 0
|
||||
---
|
||||
|
||||
NoneBot.drivers.quart 模块
|
||||
==========================
|
||||
|
||||
.. automodule:: nonebot.drivers.quart
|
||||
:members:
|
||||
:private-members:
|
||||
:show-inheritance:
|
@@ -425,7 +425,8 @@ class Bot(BaseBot):
|
||||
- ``NetworkError``: 网络错误
|
||||
- ``ActionFailed``: API 调用失败
|
||||
"""
|
||||
message = escape(message) if isinstance(message, str) else message
|
||||
message = escape(message, escape_comma=False) if isinstance(
|
||||
message, str) else message
|
||||
msg = message if isinstance(message, Message) else Message(message)
|
||||
|
||||
at_sender = at_sender and getattr(event, "user_id", None)
|
||||
|
@@ -5,6 +5,14 @@ from pydantic import Field, BaseModel
|
||||
|
||||
# priority: alias > origin
|
||||
class Config(BaseModel):
|
||||
"""
|
||||
CQHTTP 配置类
|
||||
|
||||
:配置项:
|
||||
|
||||
- ``access_token`` / ``cqhttp_access_token``: CQHTTP 协议授权令牌
|
||||
- ``secret`` / ``cqhttp_secret``: CQHTTP HTTP 上报数据签名口令
|
||||
"""
|
||||
access_token: Optional[str] = Field(default=None,
|
||||
alias="cqhttp_access_token")
|
||||
secret: Optional[str] = Field(default=None, alias="cqhttp_secret")
|
||||
|
@@ -4,6 +4,14 @@ from pydantic import Field, BaseModel
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
"""
|
||||
钉钉配置类
|
||||
|
||||
:配置项:
|
||||
|
||||
- ``access_token`` / ``ding_access_token``: 钉钉令牌
|
||||
- ``secret`` / ``ding_secret``: 钉钉 HTTP 上报数据签名口令
|
||||
"""
|
||||
secret: Optional[str] = Field(default=None, alias="ding_secret")
|
||||
access_token: Optional[str] = Field(default=None, alias="ding_access_token")
|
||||
|
||||
|
@@ -1,8 +1,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
from functools import wraps
|
||||
from io import BytesIO
|
||||
from ipaddress import IPv4Address
|
||||
from typing import (Any, Dict, List, NoReturn, Optional, Tuple, Union)
|
||||
from typing import Any, Dict, List, NoReturn, Optional, Tuple, Union
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -10,15 +9,12 @@ from nonebot.adapters import Bot as BaseBot
|
||||
from nonebot.config import Config
|
||||
from nonebot.drivers import Driver, WebSocket
|
||||
from nonebot.exception import ApiNotAvailable, RequestDenied
|
||||
from nonebot.log import logger
|
||||
from nonebot.message import handle_event
|
||||
from nonebot.typing import overrides
|
||||
from nonebot.utils import escape_tag
|
||||
|
||||
from .config import Config as MiraiConfig
|
||||
from .event import Event, FriendMessage, GroupMessage, TempMessage
|
||||
from .message import MessageChain, MessageSegment
|
||||
from .utils import catch_network_error, argument_validation, check_tome, Log
|
||||
from .utils import Log, argument_validation, catch_network_error, process_event
|
||||
|
||||
|
||||
class SessionManager:
|
||||
@@ -212,20 +208,15 @@ class Bot(BaseBot):
|
||||
async def handle_message(self, message: dict):
|
||||
Log.debug(f'received message {message}')
|
||||
try:
|
||||
await handle_event(
|
||||
await process_event(
|
||||
bot=self,
|
||||
event=await check_tome(
|
||||
bot=self,
|
||||
event=Event.new({
|
||||
**message,
|
||||
'self_id': self.self_id,
|
||||
}),
|
||||
),
|
||||
event=Event.new({
|
||||
**message,
|
||||
'self_id': self.self_id,
|
||||
}),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).exception(
|
||||
'Failed to handle message '
|
||||
f'<r>{escape_tag(str(message))}</r>: ')
|
||||
Log.error(f'Failed to handle message: {message}', e)
|
||||
|
||||
@overrides(BaseBot)
|
||||
async def call_api(self, api: str, **data) -> NoReturn:
|
||||
@@ -262,10 +253,8 @@ class Bot(BaseBot):
|
||||
* ``message: Union[MessageChain, MessageSegment, str]``: 要发送的消息
|
||||
* ``at_sender: bool``: 是否 @ 事件主体
|
||||
"""
|
||||
if isinstance(message, MessageSegment):
|
||||
if not isinstance(message, MessageChain):
|
||||
message = MessageChain(message)
|
||||
elif isinstance(message, str):
|
||||
message = MessageChain(MessageSegment.plain(message))
|
||||
if isinstance(event, FriendMessage):
|
||||
return await self.send_friend_message(target=event.sender.id,
|
||||
message_chain=message)
|
||||
|
@@ -10,9 +10,9 @@ class Config(BaseModel):
|
||||
|
||||
:必填:
|
||||
|
||||
- ``mirai_auth_key``: mirai-api-http的auth_key
|
||||
- ``mirai_host``: mirai-api-http的地址
|
||||
- ``mirai_port``: mirai-api-http的端口
|
||||
- ``auth_key`` / ``mirai_auth_key``: mirai-api-http 的 auth_key
|
||||
- ``mirai_host``: mirai-api-http 的地址
|
||||
- ``mirai_port``: mirai-api-http 的端口
|
||||
"""
|
||||
auth_key: Optional[str] = Field(None, alias='mirai_auth_key')
|
||||
host: Optional[IPv4Address] = Field(None, alias='mirai_host')
|
||||
|
@@ -13,7 +13,7 @@ from .request import *
|
||||
|
||||
__all__ = [
|
||||
'Event', 'GroupChatInfo', 'GroupInfo', 'PrivateChatInfo', 'UserPermission',
|
||||
'MessageChain', 'MessageEvent', 'GroupMessage', 'FriendMessage',
|
||||
'MessageSource', 'MessageEvent', 'GroupMessage', 'FriendMessage',
|
||||
'TempMessage', 'NoticeEvent', 'MuteEvent', 'BotMuteEvent', 'BotUnmuteEvent',
|
||||
'MemberMuteEvent', 'MemberUnmuteEvent', 'BotJoinGroupEvent',
|
||||
'BotLeaveEventActive', 'BotLeaveEventKick', 'MemberJoinEvent',
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from nonebot.typing import overrides
|
||||
|
||||
@@ -8,9 +9,15 @@ from ..message import MessageChain
|
||||
from .base import Event, GroupChatInfo, PrivateChatInfo
|
||||
|
||||
|
||||
class MessageSource(BaseModel):
|
||||
id: int
|
||||
time: datetime
|
||||
|
||||
|
||||
class MessageEvent(Event):
|
||||
"""消息事件基类"""
|
||||
message_chain: MessageChain = Field(alias='messageChain')
|
||||
source: Optional[MessageSource] = None
|
||||
sender: Any
|
||||
|
||||
@overrides(Event)
|
||||
|
@@ -44,8 +44,9 @@ class MessageSegment(BaseMessageSegment):
|
||||
|
||||
@overrides(BaseMessageSegment)
|
||||
def __str__(self) -> str:
|
||||
if self.is_text():
|
||||
return self.data.get('text', '')
|
||||
return self.data['text'] if self.is_text() else repr(self)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return '[mirai:%s]' % ','.join([
|
||||
self.type.value,
|
||||
*map(
|
||||
@@ -267,18 +268,20 @@ class MessageSegment(BaseMessageSegment):
|
||||
|
||||
class MessageChain(BaseMessage):
|
||||
"""
|
||||
Mirai 协议 Messaqge 适配
|
||||
Mirai 协议 Message 适配
|
||||
|
||||
由于Mirai协议的Message实现较为特殊, 故使用MessageChain命名
|
||||
"""
|
||||
|
||||
@overrides(BaseMessage)
|
||||
def __init__(self, message: Union[List[Dict[str, Any]],
|
||||
Iterable[MessageSegment], MessageSegment],
|
||||
**kwargs):
|
||||
def __init__(self, message: Union[List[Dict[str,
|
||||
Any]], Iterable[MessageSegment],
|
||||
MessageSegment, str], **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if isinstance(message, MessageSegment):
|
||||
self.append(message)
|
||||
elif isinstance(message, str):
|
||||
self.append(MessageSegment.plain(text=message))
|
||||
elif isinstance(message, Iterable):
|
||||
self.extend(self._construct(message))
|
||||
else:
|
||||
@@ -286,6 +289,19 @@ class MessageChain(BaseMessage):
|
||||
f'Type {type(message).__name__} is not supported in mirai adapter.'
|
||||
)
|
||||
|
||||
@overrides(BaseMessage)
|
||||
def reduce(self):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
忽略为空的消息段, 合并相邻的纯文本消息段
|
||||
"""
|
||||
for index, segment in enumerate(self):
|
||||
segment: MessageSegment
|
||||
if segment.is_text() and not str(segment).strip():
|
||||
self.pop(index)
|
||||
super().reduce()
|
||||
|
||||
@overrides(BaseMessage)
|
||||
def _construct(
|
||||
self, message: Union[List[Dict[str, Any]], Iterable[MessageSegment]]
|
||||
@@ -306,5 +322,22 @@ class MessageChain(BaseMessage):
|
||||
*map(lambda segment: segment.as_dict(), self.copy()) # type: ignore
|
||||
]
|
||||
|
||||
def extract_first(self, *type: MessageType) -> Optional[MessageSegment]:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
弹出该消息链的第一个消息
|
||||
|
||||
:参数:
|
||||
|
||||
* `*type: MessageType`: 指定的消息类型, 当指定后如类型不匹配不弹出
|
||||
"""
|
||||
if not len(self):
|
||||
return None
|
||||
first: MessageSegment = self[0]
|
||||
if (not type) or (first.type in type):
|
||||
return self.pop(0)
|
||||
return None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<{self.__class__.__name__} {[*self.copy()]}>'
|
||||
|
@@ -7,10 +7,11 @@ from pydantic import Extra, ValidationError, validate_arguments
|
||||
|
||||
import nonebot.exception as exception
|
||||
from nonebot.log import logger
|
||||
from nonebot.message import handle_event
|
||||
from nonebot.utils import escape_tag, logger_wrapper
|
||||
|
||||
from .event import Event, GroupMessage
|
||||
from .message import MessageSegment, MessageType
|
||||
from .event import Event, GroupMessage, MessageEvent, MessageSource
|
||||
from .message import MessageType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .bot import Bot
|
||||
@@ -20,23 +21,28 @@ _AnyCallable = TypeVar("_AnyCallable", bound=Callable)
|
||||
|
||||
|
||||
class Log:
|
||||
_log = logger_wrapper('MIRAI')
|
||||
|
||||
@staticmethod
|
||||
def log(level: str, message: str, exception: Optional[Exception] = None):
|
||||
logger = logger_wrapper('MIRAI')
|
||||
message = '<e>' + escape_tag(message) + '</e>'
|
||||
logger(level=level.upper(), message=message, exception=exception)
|
||||
|
||||
@classmethod
|
||||
def info(cls, message: Any):
|
||||
cls._log('INFO', str(message))
|
||||
cls.log('INFO', str(message))
|
||||
|
||||
@classmethod
|
||||
def debug(cls, message: Any):
|
||||
cls._log('DEBUG', str(message))
|
||||
cls.log('DEBUG', str(message))
|
||||
|
||||
@classmethod
|
||||
def warn(cls, message: Any):
|
||||
cls._log('WARNING', str(message))
|
||||
cls.log('WARNING', str(message))
|
||||
|
||||
@classmethod
|
||||
def error(cls, message: Any, exception: Optional[Exception] = None):
|
||||
cls._log('ERROR', str(message), exception=exception)
|
||||
cls.log('ERROR', str(message), exception=exception)
|
||||
|
||||
|
||||
class ActionFailed(exception.ActionFailed):
|
||||
@@ -118,39 +124,55 @@ def argument_validation(function: _AnyCallable) -> _AnyCallable:
|
||||
return wrapper # type: ignore
|
||||
|
||||
|
||||
async def check_tome(bot: "Bot", event: "Event") -> "Event":
|
||||
if not isinstance(event, GroupMessage):
|
||||
return event
|
||||
|
||||
def _is_at(event: GroupMessage) -> bool:
|
||||
for segment in event.message_chain:
|
||||
segment: MessageSegment
|
||||
if segment.type != MessageType.AT:
|
||||
continue
|
||||
if segment.data['target'] == event.self_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_nick(event: GroupMessage) -> bool:
|
||||
text = event.get_plaintext()
|
||||
if not text:
|
||||
return False
|
||||
nick_regex = '|'.join(
|
||||
{i.strip() for i in bot.config.nickname if i.strip()})
|
||||
matched = re.search(rf"^({nick_regex})([\s,,]*|$)", text, re.IGNORECASE)
|
||||
if matched is None:
|
||||
return False
|
||||
Log.info(f'User is calling me {matched.group(1)}')
|
||||
return True
|
||||
|
||||
def _is_reply(event: GroupMessage) -> bool:
|
||||
for segment in event.message_chain:
|
||||
segment: MessageSegment
|
||||
if segment.type != MessageType.QUOTE:
|
||||
continue
|
||||
if segment.data['senderId'] == event.self_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
event.to_me = any([_is_at(event), _is_reply(event), _is_nick(event)])
|
||||
def process_source(bot: "Bot", event: MessageEvent) -> MessageEvent:
|
||||
source = event.message_chain.extract_first(MessageType.SOURCE)
|
||||
if source is not None:
|
||||
event.source = MessageSource.parse_obj(source.data)
|
||||
return event
|
||||
|
||||
|
||||
def process_at(bot: "Bot", event: GroupMessage) -> GroupMessage:
|
||||
at = event.message_chain.extract_first(MessageType.AT)
|
||||
if at is not None:
|
||||
if at.data['target'] == event.self_id:
|
||||
event.to_me = True
|
||||
else:
|
||||
event.message_chain.insert(0, at)
|
||||
return event
|
||||
|
||||
|
||||
def process_nick(bot: "Bot", event: GroupMessage) -> GroupMessage:
|
||||
plain = event.message_chain.extract_first(MessageType.PLAIN)
|
||||
if plain is not None:
|
||||
text = str(plain)
|
||||
nick_regex = '|'.join(filter(lambda x: x, bot.config.nickname))
|
||||
matched = re.search(rf"^({nick_regex})([\s,,]*|$)", text, re.IGNORECASE)
|
||||
if matched is not None:
|
||||
event.to_me = True
|
||||
nickname = matched.group(1)
|
||||
Log.info(f'User is calling me {nickname}')
|
||||
plain.data['text'] = text[matched.end():]
|
||||
event.message_chain.insert(0, plain)
|
||||
return event
|
||||
|
||||
|
||||
def process_reply(bot: "Bot", event: GroupMessage) -> GroupMessage:
|
||||
reply = event.message_chain.extract_first(MessageType.QUOTE)
|
||||
if reply is not None:
|
||||
if reply.data['senderId'] == event.self_id:
|
||||
event.to_me = True
|
||||
else:
|
||||
event.message_chain.insert(0, reply)
|
||||
return event
|
||||
|
||||
|
||||
async def process_event(bot: "Bot", event: Event) -> None:
|
||||
if isinstance(event, MessageEvent):
|
||||
event.message_chain.reduce()
|
||||
Log.debug(event.message_chain)
|
||||
event = process_source(bot, event)
|
||||
if isinstance(event, GroupMessage):
|
||||
event = process_nick(bot, event)
|
||||
event = process_at(bot, event)
|
||||
event = process_reply(bot, event)
|
||||
await handle_event(bot, event)
|
@@ -62,7 +62,7 @@ class Driver(abc.ABC):
|
||||
:说明: 已连接的 Bot
|
||||
"""
|
||||
|
||||
def register_adapter(self, name: str, adapter: Type["Bot"]):
|
||||
def register_adapter(self, name: str, adapter: Type["Bot"], **kwargs):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@@ -74,7 +74,7 @@ class Driver(abc.ABC):
|
||||
* ``adapter: Type[Bot]``: 适配器 Class
|
||||
"""
|
||||
self._adapters[name] = adapter
|
||||
adapter.register(self, self.config)
|
||||
adapter.register(self, self.config, **kwargs)
|
||||
logger.opt(
|
||||
colors=True).debug(f'Succeeded to load adapter "<y>{name}</y>"')
|
||||
|
||||
|
@@ -28,9 +28,39 @@ from nonebot.drivers import Driver as BaseDriver, WebSocket as BaseWebSocket
|
||||
|
||||
|
||||
class Config(BaseSettings):
|
||||
"""
|
||||
FastAPI 驱动框架设置,详情参考 FastAPI 文档
|
||||
"""
|
||||
fastapi_openapi_url: Optional[str] = None
|
||||
"""
|
||||
:类型:
|
||||
|
||||
``Optional[str]``
|
||||
|
||||
:说明:
|
||||
|
||||
`openapi.json` 地址,默认为 `None` 即关闭
|
||||
"""
|
||||
fastapi_docs_url: Optional[str] = None
|
||||
"""
|
||||
:类型:
|
||||
|
||||
``Optional[str]``
|
||||
|
||||
:说明:
|
||||
|
||||
`swagger` 地址,默认为 `None` 即关闭
|
||||
"""
|
||||
fastapi_redoc_url: Optional[str] = None
|
||||
"""
|
||||
:类型:
|
||||
|
||||
``Optional[str]``
|
||||
|
||||
:说明:
|
||||
|
||||
`redoc` 地址,默认为 `None` 即关闭
|
||||
"""
|
||||
|
||||
class Config:
|
||||
extra = "ignore"
|
||||
|
240
nonebot/drivers/quart.py
Normal file
240
nonebot/drivers/quart.py
Normal file
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Quart 驱动适配
|
||||
================
|
||||
|
||||
后端使用方法请参考: `Quart 文档`_
|
||||
|
||||
.. _Quart 文档:
|
||||
https://pgjones.gitlab.io/quart/index.html
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from json.decoder import JSONDecodeError
|
||||
from typing import Any, Callable, Coroutine, Dict, Optional, Type, TypeVar
|
||||
|
||||
import uvicorn
|
||||
|
||||
from nonebot.config import Config as NoneBotConfig
|
||||
from nonebot.config import Env
|
||||
from nonebot.drivers import Driver as BaseDriver
|
||||
from nonebot.drivers import WebSocket as BaseWebSocket
|
||||
from nonebot.exception import RequestDenied
|
||||
from nonebot.log import logger
|
||||
from nonebot.typing import overrides
|
||||
|
||||
try:
|
||||
from quart import Quart, Request, Response
|
||||
from quart import Websocket as QuartWebSocket
|
||||
from quart import exceptions
|
||||
from quart import request as _request
|
||||
from quart import websocket as _websocket
|
||||
except ImportError:
|
||||
raise ValueError(
|
||||
'Please install Quart by using `pip install nonebot2[quart]`')
|
||||
|
||||
_AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine])
|
||||
|
||||
|
||||
class Driver(BaseDriver):
|
||||
"""
|
||||
Quart 驱动框架
|
||||
|
||||
:上报地址:
|
||||
|
||||
* ``/{adapter name}/http``: HTTP POST 上报
|
||||
* ``/{adapter name}/ws``: WebSocket 上报
|
||||
"""
|
||||
|
||||
@overrides(BaseDriver)
|
||||
def __init__(self, env: Env, config: NoneBotConfig):
|
||||
super().__init__(env, config)
|
||||
|
||||
self._server_app = Quart(self.__class__.__qualname__)
|
||||
self._server_app.add_url_rule('/<adapter>/http',
|
||||
methods=['POST'],
|
||||
view_func=self._handle_http)
|
||||
self._server_app.add_websocket('/<adapter>/ws',
|
||||
view_func=self._handle_ws_reverse)
|
||||
|
||||
@property
|
||||
@overrides(BaseDriver)
|
||||
def type(self) -> str:
|
||||
"""驱动名称: ``quart``"""
|
||||
return 'quart'
|
||||
|
||||
@property
|
||||
@overrides(BaseDriver)
|
||||
def server_app(self) -> Quart:
|
||||
"""``Quart`` 对象"""
|
||||
return self._server_app
|
||||
|
||||
@property
|
||||
@overrides(BaseDriver)
|
||||
def asgi(self):
|
||||
"""``Quart`` 对象"""
|
||||
return self._server_app
|
||||
|
||||
@property
|
||||
@overrides(BaseDriver)
|
||||
def logger(self):
|
||||
"""fastapi 使用的 logger"""
|
||||
return self._server_app.logger
|
||||
|
||||
@overrides(BaseDriver)
|
||||
def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:
|
||||
"""参考文档: `Startup and Shutdown <https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html>`_"""
|
||||
return self.server_app.before_serving(func) # type: ignore
|
||||
|
||||
@overrides(BaseDriver)
|
||||
def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:
|
||||
"""参考文档: `Startup and Shutdown <https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html>`_"""
|
||||
return self.server_app.after_serving(func) # type: ignore
|
||||
|
||||
@overrides(BaseDriver)
|
||||
def run(self,
|
||||
host: Optional[str] = None,
|
||||
port: Optional[int] = None,
|
||||
*,
|
||||
app: Optional[str] = None,
|
||||
**kwargs):
|
||||
"""使用 ``uvicorn`` 启动 Quart"""
|
||||
super().run(host, port, app, **kwargs)
|
||||
LOGGING_CONFIG = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"default": {
|
||||
"class": "nonebot.log.LoguruHandler",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"uvicorn.error": {
|
||||
"handlers": ["default"],
|
||||
"level": "INFO"
|
||||
},
|
||||
"uvicorn.access": {
|
||||
"handlers": ["default"],
|
||||
"level": "INFO",
|
||||
},
|
||||
},
|
||||
}
|
||||
uvicorn.run(app or self.server_app,
|
||||
host=host or str(self.config.host),
|
||||
port=port or self.config.port,
|
||||
reload=bool(app) and self.config.debug,
|
||||
debug=self.config.debug,
|
||||
log_config=LOGGING_CONFIG,
|
||||
**kwargs)
|
||||
|
||||
@overrides(BaseDriver)
|
||||
async def _handle_http(self, adapter: str):
|
||||
request: Request = _request
|
||||
|
||||
try:
|
||||
data: Dict[str, Any] = await request.get_json()
|
||||
except Exception as e:
|
||||
raise exceptions.BadRequest()
|
||||
|
||||
if adapter not in self._adapters:
|
||||
logger.warning(f'Unknown adapter {adapter}. '
|
||||
'Please register the adapter before use.')
|
||||
raise exceptions.NotFound()
|
||||
|
||||
BotClass = self._adapters[adapter]
|
||||
headers = {k: v for k, v in request.headers.items(lower=True)}
|
||||
|
||||
try:
|
||||
self_id = await BotClass.check_permission(self, 'http', headers,
|
||||
data)
|
||||
except RequestDenied as e:
|
||||
raise exceptions.HTTPException(status_code=e.status_code,
|
||||
description=e.reason,
|
||||
name='Request Denied')
|
||||
if self_id in self._clients:
|
||||
logger.warning("There's already a reverse websocket connection,"
|
||||
"so the event may be handled twice.")
|
||||
bot = BotClass('http', self_id)
|
||||
asyncio.create_task(bot.handle_message(data))
|
||||
return Response('', 204)
|
||||
|
||||
@overrides(BaseDriver)
|
||||
async def _handle_ws_reverse(self, adapter: str):
|
||||
websocket: QuartWebSocket = _websocket
|
||||
if adapter not in self._adapters:
|
||||
logger.warning(
|
||||
f'Unknown adapter {adapter}. Please register the adapter before use.'
|
||||
)
|
||||
raise exceptions.NotFound()
|
||||
|
||||
BotClass = self._adapters[adapter]
|
||||
headers = {k: v for k, v in websocket.headers.items(lower=True)}
|
||||
try:
|
||||
self_id = await BotClass.check_permission(self, 'websocket',
|
||||
headers, None)
|
||||
except RequestDenied as e:
|
||||
print(e.reason)
|
||||
raise exceptions.HTTPException(status_code=e.status_code,
|
||||
description=e.reason,
|
||||
name='Request Denied')
|
||||
if self_id in self._clients:
|
||||
logger.warning("There's already a reverse websocket connection,"
|
||||
"so the event may be handled twice.")
|
||||
ws = WebSocket(websocket)
|
||||
bot = BotClass('websocket', self_id, websocket=ws)
|
||||
await ws.accept()
|
||||
logger.opt(colors=True).info(
|
||||
f"WebSocket Connection from <y>{adapter.upper()} "
|
||||
f"Bot {self_id}</y> Accepted!")
|
||||
self._bot_connect(bot)
|
||||
|
||||
try:
|
||||
while not ws.closed:
|
||||
data = await ws.receive()
|
||||
if data is None:
|
||||
continue
|
||||
asyncio.create_task(bot.handle_message(data))
|
||||
finally:
|
||||
self._bot_disconnect(bot)
|
||||
|
||||
|
||||
class WebSocket(BaseWebSocket):
|
||||
|
||||
@overrides(BaseWebSocket)
|
||||
def __init__(self, websocket: QuartWebSocket):
|
||||
super().__init__(websocket)
|
||||
self._closed = False
|
||||
|
||||
@property
|
||||
@overrides(BaseWebSocket)
|
||||
def websocket(self) -> QuartWebSocket:
|
||||
return self._websocket
|
||||
|
||||
@property
|
||||
@overrides(BaseWebSocket)
|
||||
def closed(self):
|
||||
return self._closed
|
||||
|
||||
@overrides(BaseWebSocket)
|
||||
async def accept(self):
|
||||
await self.websocket.accept()
|
||||
self._closed = False
|
||||
|
||||
@overrides(BaseWebSocket)
|
||||
async def close(self):
|
||||
self._closed = True
|
||||
|
||||
@overrides(BaseWebSocket)
|
||||
async def receive(self) -> Optional[Dict[str, Any]]:
|
||||
data: Optional[Dict[str, Any]] = None
|
||||
try:
|
||||
data = await self.websocket.receive_json()
|
||||
except JSONDecodeError:
|
||||
logger.warning('Received an invalid json message.')
|
||||
except asyncio.CancelledError:
|
||||
self._closed = True
|
||||
logger.warning('WebSocket disconnected by peer.')
|
||||
return data
|
||||
|
||||
@overrides(BaseWebSocket)
|
||||
async def send(self, data: dict):
|
||||
await self.websocket.send_json(data)
|
@@ -7,7 +7,7 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Set, Type, Optional, Iterable, TYPE_CHECKING
|
||||
from typing import Set, Type, TYPE_CHECKING
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.rule import TrieRule
|
||||
@@ -115,6 +115,7 @@ async def _check_matcher(priority: int, Matcher: Type[Matcher], bot: "Bot",
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
f"<r><bg #f8bbd0>Rule check failed for {Matcher}.</bg #f8bbd0></r>")
|
||||
return
|
||||
|
||||
if Matcher.temp:
|
||||
try:
|
||||
|
@@ -22,7 +22,7 @@ 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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.adapters import Bot, Event, MessageSegment
|
||||
|
||||
plugins: Dict[str, "Plugin"] = {}
|
||||
"""
|
||||
@@ -745,11 +745,7 @@ class MatcherGroup:
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_startswith(self,
|
||||
msg: str,
|
||||
rule: Optional[Optional[Union[Rule,
|
||||
T_RuleChecker]]] = None,
|
||||
**kwargs) -> Type[Matcher]:
|
||||
def on_startswith(self, msg: str, **kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@@ -771,12 +767,14 @@ class MatcherGroup:
|
||||
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
return self.on_message(rule=startswith(msg) & rule, **kwargs)
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_startswith(msg, **final_kwargs)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_endswith(self,
|
||||
msg: str,
|
||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
|
||||
**kwargs) -> Type[Matcher]:
|
||||
def on_endswith(self, msg: str, **kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@@ -798,12 +796,14 @@ class MatcherGroup:
|
||||
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
return self.on_message(rule=endswith(msg) & rule, **kwargs)
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_endswith(msg, **final_kwargs)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_keyword(self,
|
||||
keywords: Set[str],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
**kwargs) -> Type[Matcher]:
|
||||
def on_keyword(self, keywords: Set[str], **kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@@ -825,11 +825,15 @@ class MatcherGroup:
|
||||
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
return self.on_message(rule=keyword(*keywords) & rule, **kwargs)
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_keyword(keywords, **final_kwargs)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_command(self,
|
||||
cmd: Union[str, Tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
|
||||
**kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
@@ -842,8 +846,8 @@ class MatcherGroup:
|
||||
:参数:
|
||||
|
||||
* ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
|
||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||
* ``permission: Optional[Permission]``: 事件响应权限
|
||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||
@@ -856,27 +860,15 @@ class MatcherGroup:
|
||||
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
|
||||
async def _strip_cmd(bot: "Bot", event: "Event", state: T_State):
|
||||
message = event.get_message()
|
||||
segment = message.pop(0)
|
||||
new_message = message.__class__(
|
||||
str(segment)
|
||||
[len(state["_prefix"]["raw_command"]):].strip()) # type: ignore
|
||||
for new_segment in reversed(new_message):
|
||||
message.insert(0, new_segment)
|
||||
|
||||
handlers = kwargs.pop("handlers", [])
|
||||
handlers.insert(0, _strip_cmd)
|
||||
|
||||
commands = set([cmd]) | (aliases or set())
|
||||
return self.on_message(rule=command(*commands) & rule,
|
||||
handlers=handlers,
|
||||
**kwargs)
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_command(cmd, aliases=aliases, **final_kwargs)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_shell_command(self,
|
||||
cmd: Union[str, Tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
aliases: Optional[Set[Union[str, Tuple[str,
|
||||
...]]]] = None,
|
||||
parser: Optional[ArgumentParser] = None,
|
||||
@@ -884,53 +876,43 @@ class MatcherGroup:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
注册一个支持 ``shell_like`` 解析参数的命令消息事件响应器。
|
||||
注册一个支持 ``shell_like`` 解析参数的命令消息事件响应器。
|
||||
|
||||
与普通的 ``on_command`` 不同的是,在添加 ``parser`` 参数时, 响应器会自动处理消息。
|
||||
与普通的 ``on_command`` 不同的是,在添加 ``parser`` 参数时, 响应器会自动处理消息。
|
||||
|
||||
并将用户输入的原始参数列表保存在 ``state["argv"]``, ``parser`` 处理的参数保存在 ``state["args"]`` 中
|
||||
并将用户输入的原始参数列表保存在 ``state["argv"]``, ``parser`` 处理的参数保存在 ``state["args"]`` 中
|
||||
|
||||
:参数:
|
||||
|
||||
* ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
|
||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
||||
* ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
|
||||
* ``permission: Optional[Permission]``: 事件响应权限
|
||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||
* ``priority: int``: 事件响应器优先级
|
||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||
* ``state: Optional[T_State]``: 默认 state
|
||||
* ``state_factory: Optional[T_StateFactory]``: 默认 state 的工厂函数
|
||||
* ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
|
||||
* ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
|
||||
* ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
|
||||
* ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
|
||||
* ``permission: Optional[Permission]``: 事件响应权限
|
||||
* ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
|
||||
* ``temp: bool``: 是否为临时事件响应器(仅执行一次)
|
||||
* ``priority: int``: 事件响应器优先级
|
||||
* ``block: bool``: 是否阻止事件向更低优先级传递
|
||||
* ``state: Optional[T_State]``: 默认 state
|
||||
* ``state_factory: Optional[T_StateFactory]``: 默认 state 的工厂函数
|
||||
|
||||
:返回:
|
||||
|
||||
- ``Type[Matcher]``
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
|
||||
async def _strip_cmd(bot: "Bot", event: "Event", state: T_State):
|
||||
message = event.get_message()
|
||||
segment = message.pop(0)
|
||||
new_message = message.__class__(
|
||||
str(segment)
|
||||
[len(state["_prefix"]["raw_command"]):].strip()) # type: ignore
|
||||
for new_segment in reversed(new_message):
|
||||
message.insert(0, new_segment)
|
||||
|
||||
handlers = kwargs.pop("handlers", [])
|
||||
handlers.insert(0, _strip_cmd)
|
||||
|
||||
commands = set([cmd]) | (aliases or set())
|
||||
return self.on_message(rule=shell_command(*commands, parser=parser) &
|
||||
rule,
|
||||
handlers=handlers,
|
||||
**kwargs)
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_shell_command(cmd,
|
||||
aliases=aliases,
|
||||
parser=parser,
|
||||
**final_kwargs)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_regex(self,
|
||||
pattern: str,
|
||||
flags: Union[int, re.RegexFlag] = 0,
|
||||
rule: Optional[Rule] = None,
|
||||
**kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
:说明:
|
||||
@@ -956,7 +938,12 @@ class MatcherGroup:
|
||||
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
return self.on_message(rule=regex(pattern, flags) & rule, **kwargs)
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_regex(pattern, flags=flags, **final_kwargs)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
|
||||
def load_plugin(module_path: str) -> Optional[Plugin]:
|
||||
|
@@ -320,8 +320,8 @@ class MatcherGroup:
|
||||
|
||||
def on_startswith(
|
||||
self,
|
||||
*,
|
||||
msg: str,
|
||||
*,
|
||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||
permission: Optional[Permission] = ...,
|
||||
handlers: Optional[List[T_Handler]] = ...,
|
||||
@@ -334,8 +334,8 @@ class MatcherGroup:
|
||||
|
||||
def on_endswith(
|
||||
self,
|
||||
*,
|
||||
msg: str,
|
||||
*,
|
||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||
permission: Optional[Permission] = ...,
|
||||
handlers: Optional[List[T_Handler]] = ...,
|
||||
@@ -348,8 +348,8 @@ class MatcherGroup:
|
||||
|
||||
def on_keyword(
|
||||
self,
|
||||
*,
|
||||
keywords: Set[str],
|
||||
*,
|
||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||
permission: Optional[Permission] = ...,
|
||||
handlers: Optional[List[T_Handler]] = ...,
|
||||
@@ -362,10 +362,10 @@ class MatcherGroup:
|
||||
|
||||
def on_command(
|
||||
self,
|
||||
*,
|
||||
cmd: Union[str, Tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||
*,
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||
permission: Optional[Permission] = ...,
|
||||
handlers: Optional[List[T_Handler]] = ...,
|
||||
temp: bool = ...,
|
||||
@@ -377,11 +377,11 @@ class MatcherGroup:
|
||||
|
||||
def on_shell_command(
|
||||
self,
|
||||
*,
|
||||
cmd: Union[str, Tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||
parser: Optional[ArgumentParser] = ...,
|
||||
*,
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||
permission: Optional[Permission] = ...,
|
||||
handlers: Optional[List[T_Handler]] = ...,
|
||||
temp: bool = ...,
|
||||
@@ -393,9 +393,9 @@ class MatcherGroup:
|
||||
|
||||
def on_regex(
|
||||
self,
|
||||
*,
|
||||
pattern: str,
|
||||
flags: Union[int, re.RegexFlag] = 0,
|
||||
*,
|
||||
rule: Optional[Rule] = ...,
|
||||
permission: Optional[Permission] = ...,
|
||||
handlers: Optional[List[T_Handler]] = ...,
|
||||
|
@@ -25,7 +25,7 @@ from nonebot.exception import ParserExit
|
||||
from nonebot.typing import T_State, T_RuleChecker
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.adapters import Bot, Event, MessageSegment
|
||||
|
||||
|
||||
class Rule:
|
||||
|
264
poetry.lock
generated
264
poetry.lock
generated
@@ -1,3 +1,11 @@
|
||||
[[package]]
|
||||
name = "aiofiles"
|
||||
version = "0.6.0"
|
||||
description = "File support for asyncio."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "alabaster"
|
||||
version = "0.7.12"
|
||||
@@ -17,6 +25,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
[package.dependencies]
|
||||
pytz = ">=2015.7"
|
||||
|
||||
[[package]]
|
||||
name = "blinker"
|
||||
version = "1.4"
|
||||
description = "Fast, simple object-to-object and broadcast signaling"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2020.12.5"
|
||||
@@ -25,14 +41,6 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "4.0.0"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "7.1.2"
|
||||
@@ -83,6 +91,26 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "4.0.0"
|
||||
description = "HTTP/2 State-Machine based protocol implementation"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
hpack = ">=4.0,<5"
|
||||
hyperframe = ">=6.0,<7"
|
||||
|
||||
[[package]]
|
||||
name = "hpack"
|
||||
version = "4.0.0"
|
||||
description = "Pure-Python HPACK header compression"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[[package]]
|
||||
name = "html2text"
|
||||
version = "2020.1.16"
|
||||
@@ -135,13 +163,43 @@ sniffio = "*"
|
||||
brotli = ["brotlipy (>=0.7.0,<0.8.0)"]
|
||||
http2 = ["h2 (>=3.0.0,<4.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "hypercorn"
|
||||
version = "0.11.2"
|
||||
description = "A ASGI Server based on Hyper libraries and inspired by Gunicorn."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
h11 = "*"
|
||||
h2 = ">=3.1.0"
|
||||
priority = "*"
|
||||
toml = "*"
|
||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
||||
wsproto = ">=0.14.0"
|
||||
|
||||
[package.extras]
|
||||
h3 = ["aioquic (>=0.9.0,<1.0)"]
|
||||
tests = ["hypothesis", "mock", "pytest", "pytest-asyncio", "pytest-cov", "pytest-trio", "trio"]
|
||||
trio = ["trio (>=0.11.0)"]
|
||||
uvloop = ["uvloop"]
|
||||
|
||||
[[package]]
|
||||
name = "hyperframe"
|
||||
version = "6.0.0"
|
||||
description = "HTTP/2 framing layer for Python"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "2.10"
|
||||
version = "3.1"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = ">=3.4"
|
||||
|
||||
[[package]]
|
||||
name = "imagesize"
|
||||
@@ -151,11 +209,19 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "1.1.0"
|
||||
description = "Various helpers to pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "2.11.3"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
@@ -184,7 +250,7 @@ dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3
|
||||
name = "markupsafe"
|
||||
version = "1.1.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||
|
||||
@@ -199,6 +265,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
|
||||
[[package]]
|
||||
name = "priority"
|
||||
version = "1.3.0"
|
||||
description = "A pure-Python implementation of the HTTP/2 priority tree"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "1.7.3"
|
||||
@@ -270,22 +344,38 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "quart"
|
||||
version = "0.14.1"
|
||||
description = "A Python ASGI web microframework with the same API as Flask"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.7.0"
|
||||
|
||||
[package.dependencies]
|
||||
aiofiles = "*"
|
||||
blinker = "*"
|
||||
click = "*"
|
||||
hypercorn = ">=0.7.0"
|
||||
itsdangerous = "*"
|
||||
jinja2 = "*"
|
||||
toml = "*"
|
||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
||||
werkzeug = ">=1.0.0"
|
||||
|
||||
[package.extras]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.25.1"
|
||||
version = "2.15.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
chardet = ">=3.0.2,<5"
|
||||
idna = ">=2.5,<3"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||
security = ["cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
|
||||
[[package]]
|
||||
@@ -453,6 +543,14 @@ python-versions = ">=3.6"
|
||||
[package.extras]
|
||||
full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "ujson"]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "3.7.4.3"
|
||||
@@ -480,19 +578,6 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.3"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.11.8"
|
||||
@@ -527,6 +612,18 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "1.0.1"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"]
|
||||
watchdog = ["watchdog"]
|
||||
|
||||
[[package]]
|
||||
name = "win32-setctime"
|
||||
version = "1.0.3"
|
||||
@@ -538,6 +635,17 @@ python-versions = ">=3.5"
|
||||
[package.extras]
|
||||
dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
||||
|
||||
[[package]]
|
||||
name = "wsproto"
|
||||
version = "1.0.0"
|
||||
description = "WebSockets state-machine based protocol implementation"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
h11 = ">=0.9.0,<1"
|
||||
|
||||
[[package]]
|
||||
name = "yapf"
|
||||
version = "0.30.0"
|
||||
@@ -546,12 +654,20 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[extras]
|
||||
all = ["Quart"]
|
||||
quart = ["Quart"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "9aa4fde8078788e6a12866ba4eb5d17ec6237355c663d6ea74040b6e165cdcf1"
|
||||
content-hash = "11273401518ba0c93c5e381c6f0c1be02d60106bcda715c7ee7a06a78a8871d5"
|
||||
|
||||
[metadata.files]
|
||||
aiofiles = [
|
||||
{file = "aiofiles-0.6.0-py3-none-any.whl", hash = "sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6e0f9d123c27"},
|
||||
{file = "aiofiles-0.6.0.tar.gz", hash = "sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478adae50ca092"},
|
||||
]
|
||||
alabaster = [
|
||||
{file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
|
||||
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
|
||||
@@ -560,14 +676,13 @@ babel = [
|
||||
{file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"},
|
||||
{file = "Babel-2.9.0.tar.gz", hash = "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"},
|
||||
]
|
||||
blinker = [
|
||||
{file = "blinker-1.4.tar.gz", hash = "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
||||
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
|
||||
]
|
||||
chardet = [
|
||||
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
|
||||
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||
@@ -588,6 +703,14 @@ h11 = [
|
||||
{file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"},
|
||||
{file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"},
|
||||
]
|
||||
h2 = [
|
||||
{file = "h2-4.0.0-py3-none-any.whl", hash = "sha256:ac9e293a1990b339d5d71b19c5fe630e3dd4d768c620d1730d355485323f1b25"},
|
||||
{file = "h2-4.0.0.tar.gz", hash = "sha256:bb7ac7099dd67a857ed52c815a6192b6b1f5ba6b516237fc24a085341340593d"},
|
||||
]
|
||||
hpack = [
|
||||
{file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"},
|
||||
{file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"},
|
||||
]
|
||||
html2text = [
|
||||
{file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"},
|
||||
{file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"},
|
||||
@@ -614,14 +737,26 @@ httpx = [
|
||||
{file = "httpx-0.16.1-py3-none-any.whl", hash = "sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"},
|
||||
{file = "httpx-0.16.1.tar.gz", hash = "sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537"},
|
||||
]
|
||||
hypercorn = [
|
||||
{file = "Hypercorn-0.11.2-py3-none-any.whl", hash = "sha256:8007c10f81566920f8ae12c0e26e146f94ca70506da964b5a727ad610aa1d821"},
|
||||
{file = "Hypercorn-0.11.2.tar.gz", hash = "sha256:5ba1e719c521080abd698ff5781a2331e34ef50fc1c89a50960538115a896a9a"},
|
||||
]
|
||||
hyperframe = [
|
||||
{file = "hyperframe-6.0.0-py3-none-any.whl", hash = "sha256:a51026b1591cac726fc3d0b7994fbc7dc5efab861ef38503face2930fd7b2d34"},
|
||||
{file = "hyperframe-6.0.0.tar.gz", hash = "sha256:742d2a4bc3152a340a49d59f32e33ec420aa8e7054c1444ef5c7efff255842f1"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||
{file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
|
||||
{file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"},
|
||||
]
|
||||
imagesize = [
|
||||
{file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"},
|
||||
{file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
|
||||
]
|
||||
itsdangerous = [
|
||||
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
|
||||
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"},
|
||||
{file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"},
|
||||
@@ -649,45 +784,30 @@ markupsafe = [
|
||||
{file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
|
||||
{file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
|
||||
{file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"},
|
||||
{file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"},
|
||||
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
|
||||
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
|
||||
]
|
||||
priority = [
|
||||
{file = "priority-1.3.0-py2.py3-none-any.whl", hash = "sha256:be4fcb94b5e37cdeb40af5533afe6dd603bd665fe9c8b3052610fc1001d5d1eb"},
|
||||
{file = "priority-1.3.0.tar.gz", hash = "sha256:6bc1961a6d7fcacbfc337769f1a382c8e746566aaa365e78047abe9f66b2ffbe"},
|
||||
]
|
||||
pydantic = [
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"},
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"},
|
||||
@@ -735,9 +855,13 @@ pytz = [
|
||||
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
||||
{file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
|
||||
]
|
||||
quart = [
|
||||
{file = "Quart-0.14.1-py3-none-any.whl", hash = "sha256:7b13786e07541cc9ce1466fdc6a6ccd5f36eb39118edd25a42d617593cd17707"},
|
||||
{file = "Quart-0.14.1.tar.gz", hash = "sha256:429c5b4ff27e1d2f9ca0aacc38f6aba0ff49b38b815448bf24b613d3de12ea02"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
||||
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
||||
{file = "requests-2.15.1-py2.py3-none-any.whl", hash = "sha256:ff753b2196cd18b1bbeddc9dcd5c864056599f7a7d9a4fb5677e723efa2b7fb9"},
|
||||
{file = "requests-2.15.1.tar.gz", hash = "sha256:e5659b9315a0610505e050bb7190bf6fa2ccee1ac295f2b760ef9d8a03ebbb2e"},
|
||||
]
|
||||
rfc3986 = [
|
||||
{file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
|
||||
@@ -784,6 +908,10 @@ starlette = [
|
||||
{file = "starlette-0.13.6-py3-none-any.whl", hash = "sha256:bd2ffe5e37fb75d014728511f8e68ebf2c80b0fa3d04ca1479f4dc752ae31ac9"},
|
||||
{file = "starlette-0.13.6.tar.gz", hash = "sha256:ebe8ee08d9be96a3c9f31b2cb2a24dbdf845247b745664bd8a3f9bd0c977fdbc"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
|
||||
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
|
||||
@@ -795,10 +923,6 @@ unify = [
|
||||
untokenize = [
|
||||
{file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
|
||||
{file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
|
||||
]
|
||||
uvicorn = [
|
||||
{file = "uvicorn-0.11.8-py3-none-any.whl", hash = "sha256:4b70ddb4c1946e39db9f3082d53e323dfd50634b95fd83625d778729ef1730ef"},
|
||||
{file = "uvicorn-0.11.8.tar.gz", hash = "sha256:46a83e371f37ea7ff29577d00015f02c942410288fb57def6440f2653fff1d26"},
|
||||
@@ -838,10 +962,18 @@ websockets = [
|
||||
{file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"},
|
||||
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
|
||||
]
|
||||
werkzeug = [
|
||||
{file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"},
|
||||
{file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"},
|
||||
]
|
||||
win32-setctime = [
|
||||
{file = "win32_setctime-1.0.3-py3-none-any.whl", hash = "sha256:dc925662de0a6eb987f0b01f599c01a8236cb8c62831c22d9cada09ad958243e"},
|
||||
{file = "win32_setctime-1.0.3.tar.gz", hash = "sha256:4e88556c32fdf47f64165a2180ba4552f8bb32c1103a2fafd05723a0bd42bd4b"},
|
||||
]
|
||||
wsproto = [
|
||||
{file = "wsproto-1.0.0-py3-none-any.whl", hash = "sha256:d8345d1808dd599b5ffb352c25a367adb6157e664e140dbecba3f9bc007edb9f"},
|
||||
{file = "wsproto-1.0.0.tar.gz", hash = "sha256:868776f8456997ad0d9720f7322b746bbe9193751b5b290b7f924659377c8c38"},
|
||||
]
|
||||
yapf = [
|
||||
{file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"},
|
||||
{file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "nonebot2"
|
||||
version = "2.0.0-alpha.9"
|
||||
version = "2.0.0a10"
|
||||
description = "An asynchronous python bot framework."
|
||||
authors = ["yanyongyu <yanyongyu_1@126.com>"]
|
||||
license = "MIT"
|
||||
@@ -31,12 +31,17 @@ fastapi = "^0.63.0"
|
||||
uvicorn = "^0.11.5"
|
||||
websockets = "^8.1"
|
||||
pydantic = {extras = ["dotenv", "typing_extensions"], version = "^1.7.3"}
|
||||
Quart = {version = "^0.14.1", optional = true}
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
yapf = "^0.30.0"
|
||||
sphinx = "^3.4.1"
|
||||
sphinx-markdown-builder = { git = "https://github.com/nonebot/sphinx-markdown-builder.git" }
|
||||
|
||||
[tool.poetry.extras]
|
||||
quart = ["quart"]
|
||||
all = ["quart"]
|
||||
|
||||
# [[tool.poetry.source]]
|
||||
# name = "aliyun"
|
||||
# url = "https://mirrors.aliyun.com/pypi/simple/"
|
||||
|
@@ -14,4 +14,4 @@ CUSTOM_CONFIG3=
|
||||
|
||||
MIRAI_AUTH_KEY=12345678
|
||||
MIRAI_HOST=127.0.0.1
|
||||
MIRAI_PORT=8080
|
||||
MIRAI_PORT=8080
|
||||
|
@@ -1,13 +1,20 @@
|
||||
from nonebot.plugin import on_message
|
||||
from nonebot.plugin import on_keyword, on_command
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.adapters.mirai import Bot, MessageEvent
|
||||
|
||||
message_test = on_message()
|
||||
message_test = on_keyword({'reply'}, rule=to_me())
|
||||
|
||||
|
||||
@message_test.handle()
|
||||
async def _message(bot: Bot, event: MessageEvent):
|
||||
text = event.get_plaintext()
|
||||
if not text:
|
||||
return
|
||||
reversed_text = ''.join(reversed(text))
|
||||
await bot.send(event, reversed_text, at_sender=True)
|
||||
await bot.send(event, text, at_sender=True)
|
||||
|
||||
|
||||
command_test = on_command('miecho')
|
||||
|
||||
|
||||
@command_test.handle()
|
||||
async def _echo(bot: Bot, event: MessageEvent):
|
||||
text = event.get_plaintext()
|
||||
await bot.send(event, text, at_sender=True)
|
Reference in New Issue
Block a user