mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-07-27 16:21:28 +00:00
🔖 Release 2.4.1
Some checks failed
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.11) (push) Failing after 2s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.12) (push) Failing after 6m59s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.10) (push) Failing after 7m7s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.10) (push) Failing after 5m14s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.9) (push) Failing after 5m42s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.11) (push) Failing after 4m51s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.12) (push) Failing after 7m12s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.9) (push) Failing after 6m33s
Pyright Lint / Pyright Lint (pydantic-v1) (push) Failing after 8m12s
Ruff Lint / Ruff Lint (push) Successful in 43s
Pyright Lint / Pyright Lint (pydantic-v2) (push) Failing after 6m56s
Site Deploy / publish (push) Failing after 7m21s
Some checks failed
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.11) (push) Failing after 2s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.12) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.9) (push) Waiting to run
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.12) (push) Failing after 6m59s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.10) (push) Failing after 7m7s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.10) (push) Failing after 5m14s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.9) (push) Failing after 5m42s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.11) (push) Failing after 4m51s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.12) (push) Failing after 7m12s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.9) (push) Failing after 6m33s
Pyright Lint / Pyright Lint (pydantic-v1) (push) Failing after 8m12s
Ruff Lint / Ruff Lint (push) Successful in 43s
Pyright Lint / Pyright Lint (pydantic-v2) (push) Failing after 6m56s
Site Deploy / publish (push) Failing after 7m21s
This commit is contained in:
110
website/versioned_docs/version-2.4.1/tutorial/application.md
Normal file
110
website/versioned_docs/version-2.4.1/tutorial/application.md
Normal file
@ -0,0 +1,110 @@
|
||||
---
|
||||
sidebar_position: 0
|
||||
description: 创建一个 NoneBot 项目
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 20
|
||||
---
|
||||
|
||||
# 手动创建项目
|
||||
|
||||
在[快速上手](../quick-start.mdx)中,我们已经介绍了如何安装和使用 `nb-cli` 创建一个项目。在本章节中,我们将简要介绍如何在不使用 `nb-cli` 的方式创建一个机器人项目的**最小实例**并启动。如果你想要了解 NoneBot 的启动流程,也可以阅读本章节。
|
||||
|
||||
:::caution 警告
|
||||
我们十分不推荐直接创建机器人项目,请优先考虑使用 nb-cli 进行项目创建。
|
||||
:::
|
||||
|
||||
一个机器人项目的**最小实例**中**至少**需要包含以下内容:
|
||||
|
||||
- 入口文件:初始化并运行机器人的 Python 文件
|
||||
- 配置文件:存储机器人启动所需的配置
|
||||
- 插件:为机器人提供具体的功能
|
||||
|
||||
下面我们创建一个项目文件夹,来存放项目所需文件,以下步骤均在该文件夹中进行。
|
||||
|
||||
## 安装依赖
|
||||
|
||||
在创建项目前,我们首先需要将项目所需依赖安装至环境中。
|
||||
|
||||
1. (可选)创建虚拟环境,以 venv 为例
|
||||
|
||||
```bash
|
||||
python -m venv .venv --prompt nonebot2
|
||||
# windows
|
||||
.venv\Scripts\activate
|
||||
# linux/macOS
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
2. 安装 nonebot2 以及驱动器
|
||||
|
||||
```bash
|
||||
pip install 'nonebot2[fastapi]'
|
||||
```
|
||||
|
||||
驱动器包名可以在 [驱动器商店](/store/drivers) 中找到。
|
||||
|
||||
3. 安装适配器
|
||||
|
||||
```bash
|
||||
pip install nonebot-adapter-console
|
||||
```
|
||||
|
||||
适配器包名可以在 [适配器商店](/store/adapters) 中找到。
|
||||
|
||||
## 创建配置文件
|
||||
|
||||
配置文件用于存放 NoneBot 运行所需要的配置项,使用 [`pydantic`](https://docs.pydantic.dev/) 以及 [`python-dotenv`](https://saurabh-kumar.com/python-dotenv/) 来读取配置。配置项需符合 dotenv 格式,复杂类型数据需使用 JSON 格式填写。具体可选配置方式以及配置项详情参考[配置](../appendices/config.mdx)。
|
||||
|
||||
在**项目文件夹**中创建一个 `.env` 文本文件,并写入以下内容:
|
||||
|
||||
```bash title=.env
|
||||
HOST=0.0.0.0 # 配置 NoneBot 监听的 IP / 主机名
|
||||
PORT=8080 # 配置 NoneBot 监听的端口
|
||||
COMMAND_START=["/"] # 配置命令起始字符
|
||||
COMMAND_SEP=["."] # 配置命令分割字符
|
||||
```
|
||||
|
||||
## 创建入口文件
|
||||
|
||||
入口文件( Entrypoint )顾名思义,是用来初始化并运行机器人的 Python 文件。入口文件需要完成框架的初始化、注册适配器、加载插件等工作。
|
||||
|
||||
:::tip 提示
|
||||
如果你使用 `nb-cli` 创建项目,入口文件不会被创建,该文件功能会被 `nb run` 命令代替。
|
||||
:::
|
||||
|
||||
在**项目文件夹**中创建一个 `bot.py` 文件,并写入以下内容:
|
||||
|
||||
```python title=bot.py
|
||||
import nonebot
|
||||
from nonebot.adapters.console import Adapter as ConsoleAdapter # 避免重复命名
|
||||
|
||||
# 初始化 NoneBot
|
||||
nonebot.init()
|
||||
|
||||
# 注册适配器
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(ConsoleAdapter)
|
||||
|
||||
# 在这里加载插件
|
||||
nonebot.load_builtin_plugins("echo") # 内置插件
|
||||
# nonebot.load_plugin("thirdparty_plugin") # 第三方插件
|
||||
# nonebot.load_plugins("awesome_bot/plugins") # 本地插件
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
我们暂时不需要了解其中内容的含义,这些将会在稍后的章节中逐一介绍。在创建完成以上文件并确认已安装所需适配器和插件后,即可运行机器人。
|
||||
|
||||
## 运行机器人
|
||||
|
||||
在**项目文件夹**中,使用配置好环境的 Python 解释器运行入口文件(如果使用虚拟环境,请先激活虚拟环境):
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
如果你后续使用了 `nb-cli` ,你仍可以使用 `nb run` 命令来运行机器人,`nb-cli` 会自动检测入口文件 `bot.py` 是否存在并运行。同时,你也可以使用 `nb run --reload` 来自动检测代码的更改并自动重新运行入口文件。
|
226
website/versioned_docs/version-2.4.1/tutorial/create-plugin.md
Normal file
226
website/versioned_docs/version-2.4.1/tutorial/create-plugin.md
Normal file
@ -0,0 +1,226 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
description: 创建并加载自定义插件
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 50
|
||||
---
|
||||
|
||||
# 插件编写准备
|
||||
|
||||
在正式编写插件之前,我们需要先了解一下插件的概念。
|
||||
|
||||
## 插件结构
|
||||
|
||||
在 NoneBot 中,插件即是 Python 的一个[模块(module)](https://docs.python.org/zh-cn/3/glossary.html#term-module)。NoneBot 会在导入时对这些模块做一些特殊的处理使得他们成为一个插件。插件间应尽量减少耦合,可以进行有限制的相互调用,NoneBot 能够正确解析插件间的依赖关系。
|
||||
|
||||
### 单文件插件
|
||||
|
||||
一个普通的 `.py` 文件即可以作为一个插件,例如创建一个 `foo.py` 文件:
|
||||
|
||||
```tree title=Project
|
||||
📂 plugins
|
||||
└── 📜 foo.py
|
||||
```
|
||||
|
||||
这个时候模块 `foo` 已经可以被称为一个插件了,尽管它还什么都没做。
|
||||
|
||||
### 包插件
|
||||
|
||||
一个包含 `__init__.py` 的文件夹即是一个常规 Python [包 `package`](https://docs.python.org/zh-cn/3/glossary.html#term-regular-package),例如创建一个 `foo` 文件夹:
|
||||
|
||||
```tree title=Project
|
||||
📂 plugins
|
||||
└── 📂 foo
|
||||
└── 📜 __init__.py
|
||||
```
|
||||
|
||||
这个时候包 `foo` 同样是一个合法的插件,插件内容可以在 `__init__.py` 文件中编写。
|
||||
|
||||
## 创建插件
|
||||
|
||||
:::caution 注意
|
||||
如果在之前的[快速上手](../quick-start.mdx)章节中已经使用 `bootstrap` 模板创建了项目,那么你需要做出如下修改:
|
||||
|
||||
1. 在项目目录中创建一个两层文件夹 `awesome_bot/plugins`
|
||||
|
||||
```tree title=Project
|
||||
📦 awesome-bot
|
||||
├── 📂 awesome_bot
|
||||
│ └── 📂 plugins
|
||||
├── 📜 pyproject.toml
|
||||
└── 📜 README.md
|
||||
```
|
||||
|
||||
2. 修改 `pyproject.toml` 文件中的 `nonebot` 配置项,在 `plugin_dirs` 中添加 `awesome_bot/plugins`
|
||||
|
||||
```toml title=pyproject.toml
|
||||
[tool.nonebot]
|
||||
plugin_dirs = ["awesome_bot/plugins"]
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution 注意
|
||||
如果在之前的[创建项目](./application.md)章节中手动创建了相关文件,那么你需要做出如下修改:
|
||||
|
||||
1. 在项目目录中创建一个两层文件夹 `awesome_bot/plugins`
|
||||
|
||||
```tree title=Project
|
||||
📦 awesome-bot
|
||||
├── 📂 awesome_bot
|
||||
│ └── 📂 plugins
|
||||
└── 📜 bot.py
|
||||
```
|
||||
|
||||
2. 修改 `bot.py` 文件中的加载插件部分,取消注释或者添加如下代码
|
||||
|
||||
```python title=bot.py
|
||||
# 在这里加载插件
|
||||
nonebot.load_builtin_plugins("echo") # 内置插件
|
||||
nonebot.load_plugins("awesome_bot/plugins") # 本地插件
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
创建插件可以通过 `nb-cli` 命令从完整模板创建,也可以手动新建空白文件。通过以下命令创建一个名为 `weather` 的插件:
|
||||
|
||||
```bash
|
||||
$ nb plugin create
|
||||
[?] 插件名称: weather
|
||||
[?] 使用嵌套插件? (y/N) N
|
||||
[?] 输出目录: awesome_bot/plugins
|
||||
```
|
||||
|
||||
`nb-cli` 会在 `awesome_bot/plugins` 目录下创建一个名为 `weather` 的文件夹,其中包含的文件将在稍后章节中用到。
|
||||
|
||||
```tree title=Project
|
||||
📦 awesome-bot
|
||||
├── 📂 awesome_bot
|
||||
│ └── 📂 plugins
|
||||
| └── 📂 foo
|
||||
| ├── 📜 __init__.py
|
||||
| └── 📜 config.py
|
||||
├── 📜 pyproject.toml
|
||||
└── 📜 README.md
|
||||
```
|
||||
|
||||
## 加载插件
|
||||
|
||||
:::danger 警告
|
||||
请勿在插件被加载前 `import` 插件模块,这会导致 NoneBot 无法将其转换为插件而出现意料之外的情况。
|
||||
:::
|
||||
|
||||
加载插件是在机器人入口文件中完成的,需要在框架初始化之后,运行之前进行。
|
||||
|
||||
请注意,加载的插件模块名称(插件文件名或文件夹名)**不能相同**,且每一个插件**只能被加载一次**,重复加载将会导致异常。
|
||||
|
||||
如果你使用 `nb-cli` 管理插件,那么你可以跳过这一节,`nb-cli` 将会自动处理加载。
|
||||
|
||||
如果你**使用自定义的入口文件** `bot.py`,那么你需要在 `bot.py` 中加载插件。
|
||||
|
||||
```python {5} title=bot.py
|
||||
import nonebot
|
||||
|
||||
nonebot.init()
|
||||
|
||||
# 加载插件
|
||||
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
加载插件的方式有多种,但在底层的加载逻辑是一致的。以下是为加载插件提供的几种方式:
|
||||
|
||||
### `load_plugin`
|
||||
|
||||
通过点分割模块名称或使用 [`pathlib`](https://docs.python.org/zh-cn/3/library/pathlib.html) 的 `Path` 对象来加载插件。通常用于加载第三方插件或者项目插件。例如:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
nonebot.load_plugin("path.to.your.plugin") # 加载第三方插件
|
||||
nonebot.load_plugin(Path("./path/to/your/plugin.py")) # 加载项目插件
|
||||
```
|
||||
|
||||
:::caution 注意
|
||||
请注意,本地插件的路径应该为相对机器人 **入口文件(通常为 bot.py)** 可导入的,例如在项目 `plugins` 目录下。
|
||||
:::
|
||||
|
||||
### `load_plugins`
|
||||
|
||||
加载传入插件目录中的所有插件,通常用于加载一系列本地编写的项目插件。例如:
|
||||
|
||||
```python
|
||||
nonebot.load_plugins("src/plugins", "path/to/your/plugins")
|
||||
```
|
||||
|
||||
:::caution 注意
|
||||
请注意,插件目录应该为相对机器人 **入口文件(通常为 bot.py)** 可导入的,例如在项目 `plugins` 目录下。
|
||||
:::
|
||||
|
||||
### `load_all_plugins`
|
||||
|
||||
这种加载方式是以上两种方式的混合,加载所有传入的插件模块名称,以及所有给定目录下的插件。例如:
|
||||
|
||||
```python
|
||||
nonebot.load_all_plugins(["path.to.your.plugin"], ["path/to/your/plugins"])
|
||||
```
|
||||
|
||||
### `load_from_json`
|
||||
|
||||
通过 JSON 文件加载插件,是 [`load_all_plugins`](#load_all_plugins) 的 JSON 变种。通过读取 JSON 文件中的 `plugins` 字段和 `plugin_dirs` 字段进行加载。例如:
|
||||
|
||||
```json title=plugin_config.json
|
||||
{
|
||||
"plugins": ["path.to.your.plugin"],
|
||||
"plugin_dirs": ["path/to/your/plugins"]
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
nonebot.load_from_json("plugin_config.json", encoding="utf-8")
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
如果 JSON 配置文件中的字段无法满足你的需求,可以使用 [`load_all_plugins`](#load_all_plugins) 方法自行读取配置来加载插件。
|
||||
:::
|
||||
|
||||
### `load_from_toml`
|
||||
|
||||
通过 TOML 文件加载插件,是 [`load_all_plugins`](#load_all_plugins) 的 TOML 变种。通过读取 TOML 文件中的 `[tool.nonebot]` Table 中的 `plugins` 和 `plugin_dirs` Array 进行加载。例如:
|
||||
|
||||
```toml title=plugin_config.toml
|
||||
[tool.nonebot]
|
||||
plugins = ["path.to.your.plugin"]
|
||||
plugin_dirs = ["path/to/your/plugins"]
|
||||
```
|
||||
|
||||
```python
|
||||
nonebot.load_from_toml("plugin_config.toml", encoding="utf-8")
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
如果 TOML 配置文件中的字段无法满足你的需求,可以使用 [`load_all_plugins`](#load_all_plugins) 方法自行读取配置来加载插件。
|
||||
:::
|
||||
|
||||
### `load_builtin_plugin`
|
||||
|
||||
加载一个内置插件,传入的插件名必须为 NoneBot 内置插件。该方法是 [`load_plugin`](#load_plugin) 的封装。例如:
|
||||
|
||||
```python
|
||||
nonebot.load_builtin_plugin("echo")
|
||||
```
|
||||
|
||||
### `load_builtin_plugins`
|
||||
|
||||
加载传入插件列表中的所有内置插件。例如:
|
||||
|
||||
```python
|
||||
nonebot.load_builtin_plugins("echo", "single_session")
|
||||
```
|
||||
|
||||
### 其他加载方式
|
||||
|
||||
有关其他插件加载的方式,可参考[跨插件访问](../advanced/requiring.md)和[嵌套插件](../advanced/plugin-nesting.md)。
|
65
website/versioned_docs/version-2.4.1/tutorial/event-data.mdx
Normal file
65
website/versioned_docs/version-2.4.1/tutorial/event-data.mdx
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
description: 通过依赖注入获取所需事件信息
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 80
|
||||
---
|
||||
|
||||
# 获取事件信息
|
||||
|
||||
import Messenger from "@site/src/components/Messenger";
|
||||
|
||||
在 NoneBot 事件处理流程中,获取事件信息并做出对应的操作是非常常见的场景。本章节中我们将介绍如何通过**依赖注入**获取事件信息。
|
||||
|
||||
## 认识依赖注入
|
||||
|
||||
在事件处理流程中,事件响应器具有自己独立的上下文,例如:当前响应的事件、收到事件的机器人或者其他处理流程中新增的信息等。这些数据可以根据我们的需求,通过依赖注入的方式,在执行事件处理流程中注入到事件处理函数中。
|
||||
|
||||
相对于传统的信息获取方法,通过依赖注入获取信息的最大特色在于**按需获取**。如果该事件处理函数不需要任何额外信息即可运行,那么可以不进行依赖注入。如果事件处理函数需要额外的数据,可以通过依赖注入的方式灵活的标注出需要的依赖,在函数运行时便会被按需注入。
|
||||
|
||||
## 使用依赖注入
|
||||
|
||||
使用依赖注入获取上下文信息的方法十分简单,我们仅需要在函数的参数中声明所需的依赖,并正确的将函数添加为事件处理依赖即可。在 NoneBot 中,我们可以直接使用 `nonebot.params` 模块中定义的参数类型来声明依赖。
|
||||
|
||||
例如,我们可以继续改进上一章节中的 `weather` 插件,使其可以获取到 `天气` 命令的地名参数,并根据地名返回天气信息。
|
||||
|
||||
```python {9,11} title=weather/__init__.py
|
||||
from nonebot import on_command
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.adapters import Message
|
||||
from nonebot.params import CommandArg
|
||||
|
||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
||||
|
||||
@weather.handle()
|
||||
async def handle_function(args: Message = CommandArg()):
|
||||
# 提取参数纯文本作为地名,并判断是否有效
|
||||
if location := args.extract_plain_text():
|
||||
await weather.finish(f"今天{location}的天气是...")
|
||||
else:
|
||||
await weather.finish("请输入地名")
|
||||
```
|
||||
|
||||
如上方示例所示,我们使用了 `args` 作为注入参数名,注入的内容为 `CommandArg()`,也就是**消息命令后跟随的内容**。在这个示例中,我们获得的参数会被检查是否有效,对无效参数则会结束事件。
|
||||
|
||||
:::tip 提示
|
||||
命令与参数之间可以不需要空格,`CommandArg()` 获取的信息为命令后跟随的内容并去除了头部空白符。例如:`/天气 上海` 消息的参数为 `上海`。
|
||||
:::
|
||||
|
||||
:::tip 提示
|
||||
`:=` 是 Python 3.8 引入的新语法 [Assignment Expressions](https://docs.python.org/zh-cn/3/reference/expressions.html#assignment-expressions),也称为海象表达式,可以在表达式中直接赋值。
|
||||
:::
|
||||
|
||||
<Messenger
|
||||
msgs={[
|
||||
{ position: "right", msg: "/天气" },
|
||||
{ position: "left", msg: "请输入地名" },
|
||||
{ position: "right", msg: "/天气 上海" },
|
||||
{ position: "left", msg: "今天上海的天气是..." },
|
||||
]}
|
||||
/>
|
||||
|
||||
NoneBot 提供了多种依赖注入类型,可以获取不同的信息,具体内容可参考[依赖注入](../advanced/dependency.mdx)。
|
@ -0,0 +1,24 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
description: NoneBot 机器人构成及基本使用
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 30
|
||||
---
|
||||
|
||||
# 机器人的构成
|
||||
|
||||
了解机器人的基本构成有助于你更好地使用 NoneBot,本章节将介绍 NoneBot 中的基本组成部分,稍后的文档中将会使用到这些概念。
|
||||
|
||||
使用 NoneBot 框架搭建的机器人具有以下几个基本组成部分:
|
||||
|
||||
1. NoneBot 机器人框架主体:负责连接各个组成部分,提供基本的机器人功能
|
||||
2. 驱动器 `Driver`:客户端/服务端的功能实现,负责接收和发送消息(通常为 HTTP 通信)
|
||||
3. 适配器 `Adapter`:驱动器的上层,负责将**平台消息**与 NoneBot 事件/操作系统的消息格式相互转换
|
||||
4. 插件 `Plugin`:机器人的功能实现,通常为负责处理事件并进行一系列的操作
|
||||
|
||||
除 NoneBot 机器人框架主体外,其他部分均可按需选择、互相搭配,但由于平台的兼容性问题,部分插件可能仅在某些特定平台上可用(这由插件编写者决定)。
|
||||
|
||||
在接下来的章节中,我们将重点介绍机器人功能实现,即插件 `Plugin` 部分。
|
87
website/versioned_docs/version-2.4.1/tutorial/handler.mdx
Normal file
87
website/versioned_docs/version-2.4.1/tutorial/handler.mdx
Normal file
@ -0,0 +1,87 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
description: 处理接收到的特定事件
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 70
|
||||
---
|
||||
|
||||
# 事件处理
|
||||
|
||||
import Messenger from "@site/src/components/Messenger";
|
||||
|
||||
在我们收到事件,并被某个事件响应器正确响应后,便正式开启了对于这个事件的**处理流程**。
|
||||
|
||||
## 认识事件处理流程
|
||||
|
||||
就像我们在解决问题时需要遵循流程一样,处理一个事件也需要一套流程。在事件响应器对一个事件进行响应之后,会依次执行一系列的**事件处理依赖**(通常是函数)。简单来说,事件处理流程并不是一个函数、一个对象或一个方法,而是一整套由开发者设计的流程。
|
||||
|
||||
在这个流程中,我们**目前**只需要了解两个概念:函数形式的“事件处理依赖”(下称“事件处理函数”)和“事件响应器操作”。
|
||||
|
||||
## 事件处理函数
|
||||
|
||||
在事件响应器中,事件处理流程可以由一个或多个“事件处理函数”组成,这些事件处理函数将会按照顺序依次对事件进行处理,直到全部执行完成或被中断。我们可以采用事件响应器的“事件处理函数装饰器”来添加这些“事件处理函数”。
|
||||
|
||||
顾名思义,“事件处理函数装饰器”是一个[装饰器(decorator)](https://docs.python.org/zh-cn/3/glossary.html#term-decorator),那么它的使用方法也同[函数定义](https://docs.python.org/zh-cn/3/reference/compound_stmts.html#function-definitions)中所展示的包装用法相同。例如:
|
||||
|
||||
```python {6-8} title=weather/__init__.py
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.plugin import on_command
|
||||
|
||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
||||
|
||||
@weather.handle()
|
||||
async def handle_function():
|
||||
pass # do something here
|
||||
```
|
||||
|
||||
如上方示例所示,我们使用 `weather` 响应器的 `handle` 装饰器装饰了一个函数 `handle_function`。`handle_function` 函数会被添加到 `weather` 的事件处理流程中。在 `weather` 响应器被触发之后,将会依次调用 `weather` 响应器的事件处理函数,即 `handle_function` 来对事件进行处理。
|
||||
|
||||
## 事件响应器操作
|
||||
|
||||
在事件处理流程中,我们可以使用事件响应器操作来进行一些交互或改变事件处理流程,例如向机器人用户发送消息或提前结束事件处理流程等。
|
||||
|
||||
事件响应器操作与事件处理函数装饰器类似,通常作为事件响应器 `Matcher` 的[类方法](https://docs.python.org/zh-cn/3/library/functions.html#classmethod)存在,因此事件响应器操作的调用方法也是 `Matcher.func()` 的形式。不过不同的是,事件响应器操作并不是装饰器,因此并不需要@进行标注。
|
||||
|
||||
```python {8,9} title=weather/__init__.py
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.plugin import on_command
|
||||
|
||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
||||
|
||||
@weather.handle()
|
||||
async def handle_function():
|
||||
# await weather.send("天气是...")
|
||||
await weather.finish("天气是...")
|
||||
```
|
||||
|
||||
如上方示例所示,我们使用 `weather` 响应器的 `finish` 操作方法向机器人用户回复了 `天气是...` 并结束了事件处理流程。效果如下:
|
||||
|
||||
<Messenger
|
||||
msgs={[
|
||||
{ position: "right", msg: "/天气" },
|
||||
{ position: "left", msg: "天气是..." },
|
||||
]}
|
||||
/>
|
||||
|
||||
值得注意的是,在执行 `finish` 方法时,NoneBot 会在向机器人用户发送消息内容后抛出 `FinishedException` 异常来结束事件响应流程。也就是说,在 `finish` 被执行后,后续的程序是不会被执行的。如果你需要回复机器人用户消息但不想事件处理流程结束,可以使用注释的部分中展示的 `send` 方法。
|
||||
|
||||
:::danger 警告
|
||||
由于 `finish` 是通过抛出 `FinishedException` 异常来结束事件的,因此异常可能会被未加限制的 `try-except` 捕获,影响事件处理流程正确处理,导致无法正常结束此事件。请务必在异常捕获中指定错误类型或排除所有 [MatcherException](../api/exception.md#MatcherException) 类型的异常(如下所示),或将 `finish` 移出捕获范围进行使用。
|
||||
|
||||
```python
|
||||
from nonebot.exception import MatcherException
|
||||
|
||||
try:
|
||||
await weather.finish("天气是...")
|
||||
except MatcherException:
|
||||
raise
|
||||
except Exception as e:
|
||||
pass # do something here
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
目前 NoneBot 提供了多种事件响应器操作,其中包括用于机器人用户交互与流程控制两大类,进阶使用方法可以查看[会话控制](../appendices/session-control.mdx)。
|
58
website/versioned_docs/version-2.4.1/tutorial/matcher.md
Normal file
58
website/versioned_docs/version-2.4.1/tutorial/matcher.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
description: 响应接收到的特定事件
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 60
|
||||
---
|
||||
|
||||
# 事件响应器
|
||||
|
||||
事件响应器(Matcher)是对接收到的事件进行响应的基本单元,所有的事件响应器都继承自 `Matcher` 基类。
|
||||
|
||||
在 NoneBot 中,事件响应器可以通过一系列特定的规则**筛选**出**具有某种特征的事件**,并按照**特定的流程**交由**预定义的事件处理依赖**进行处理。例如,在[快速上手](../quick-start.mdx)中,我们使用了内置插件 `echo` ,它定义的事件响应器能响应机器人用户发送的“/echo hello world”消息,提取“hello world”信息并作为回复消息发送。
|
||||
|
||||
## 事件响应器辅助函数
|
||||
|
||||
NoneBot 中所有事件响应器均继承自 `Matcher` 基类,但直接使用 `Matcher.new()` 方法创建事件响应器过于繁琐且不能记录插件信息。因此,NoneBot 中提供了一系列“事件响应器辅助函数”(下称“辅助函数”)来辅助我们用**最简的方式**创建**带有不同规则预设**的事件响应器,提高代码可读性和书写效率。通常情况下,我们只需要使用辅助函数即可完成事件响应器的创建。
|
||||
|
||||
在 NoneBot 中,辅助函数以 `on()` 或 `on_<type/rule>()` 形式出现(例如 `on_command()`),调用后根据不同的参数返回一个 `Type[Matcher]` 类型的新事件响应器。
|
||||
|
||||
目前 NoneBot 提供了多种功能各异的辅助函数、具有共同命令名称前缀的命令组以及具有共同参数的响应器组,均可以从 `nonebot` 模块直接导入使用,具体内容参考[事件响应器进阶](../advanced/matcher.md)。
|
||||
|
||||
## 创建事件响应器
|
||||
|
||||
在上一节[创建插件](./create-plugin.md#创建插件)中,我们创建了一个 `weather` 插件,现在我们来实现他的功能。
|
||||
|
||||
我们直接使用 `on_command()` 辅助函数来创建一个事件响应器:
|
||||
|
||||
```python {3} title=weather/__init__.py
|
||||
from nonebot import on_command
|
||||
|
||||
weather = on_command("天气")
|
||||
```
|
||||
|
||||
这样,我们就获得一个名为 `weather` 的事件响应器了,这个事件响应器会对 `/天气` 开头的消息进行响应。
|
||||
|
||||
:::tip 提示
|
||||
如果一条消息中包含“@机器人”或以“机器人的昵称”开始,例如 `@bot /天气` 时,协议适配器会将 `event.is_tome()` 判断为 `True` ,同时也会自动去除 `@bot`,即事件响应器收到的信息内容为 `/天气`,方便进行命令匹配。
|
||||
:::
|
||||
|
||||
### 为事件响应器添加参数
|
||||
|
||||
在辅助函数中,我们可以添加一些参数来对事件响应器进行更加精细的调整,例如事件响应器的优先级、匹配规则等。例如:
|
||||
|
||||
```python {4} title=weather/__init__.py
|
||||
from nonebot import on_command
|
||||
from nonebot.rule import to_me
|
||||
|
||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
||||
```
|
||||
|
||||
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
||||
|
||||
:::tip 提示
|
||||
需要注意的是,不同的辅助函数有不同的可选参数,在使用之前可以参考[事件响应器进阶 - 基本辅助函数](../advanced/matcher.md#基本辅助函数)或 [API 文档](../api/plugin/on.md#on)。
|
||||
:::
|
349
website/versioned_docs/version-2.4.1/tutorial/message.md
Normal file
349
website/versioned_docs/version-2.4.1/tutorial/message.md
Normal file
@ -0,0 +1,349 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
description: 处理消息序列与消息段
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 90
|
||||
---
|
||||
|
||||
# 处理消息
|
||||
|
||||
在不同平台中,一条消息可能会有承载有各种不同的表现形式,它可能是一段纯文本、一张图片、一段语音、一篇富文本文章,也有可能是多种类型的组合等等。
|
||||
|
||||
在 NoneBot 中,为确保消息的正常处理与跨平台兼容性,采用了扁平化的消息序列形式,即 `Message` 对象。消息序列是 NoneBot 中的消息载体,无论是接收还是发送的消息,都采用消息序列的形式进行处理。
|
||||
|
||||
## 认识消息类型
|
||||
|
||||
### 消息序列 `Message`
|
||||
|
||||
在 NoneBot 中,消息序列 `Message` 的主要作用是用于表达“一串消息”。由于消息序列继承自 `List[MessageSegment]`,所以 `Message` 的本质是由若干消息段所组成的序列。因此,消息序列的使用方法与 `List` 有很多相似之处,例如切片、索引、拼接等。
|
||||
|
||||
在上一节的[使用依赖注入](./event-data.mdx#使用依赖注入)中,我们已经通过依赖注入 `CommandArg()` 获取了命令的参数,它的类型即是消息序列。我们使用了消息序列的 `extract_plain_text()` 方法来获取消息序列中的纯文本内容。
|
||||
|
||||
### 消息段 `MessageSegment`
|
||||
|
||||
顾名思义,消息段 `MessageSegment` 是一段消息。由于消息序列的本质是由若干消息段所组成的序列,消息段可以被认为是构成消息序列的最小单位。简单来说,消息序列类似于一个自然段,而消息段则是组成自然段的一句话。同时,作为特殊消息载体的存在,绝大多数的平台都有着**独特的消息类型**,这些独特的内容均需要由对应的**协议适配器**所提供,以适应不同平台中的消息模式。**这也意味着,你需要导入对应的协议适配器中的消息序列和消息段后才能使用其特殊的工厂方法。**
|
||||
|
||||
:::caution 注意
|
||||
消息段的类型是由协议适配器提供的,因此你需要参考协议适配器的文档并导入对应的消息段后才能使用其特殊的消息类型。
|
||||
|
||||
在上一节的[使用依赖注入](./event-data.mdx#使用依赖注入)中,我们导入的为 `nonebot.adapters.Message` 抽象基类,因此我们无法使用平台特有的消息类型。仅能使用 `str` 作为纯文本消息回复。
|
||||
:::
|
||||
|
||||
## 使用消息序列
|
||||
|
||||
:::caution 注意
|
||||
在以下的示例中,为了更好的理解多种类型的消息组成方式,我们将使用 `Console` 协议适配器来演示消息序列的使用方法。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。
|
||||
:::
|
||||
|
||||
通常情况下,适配器在接收到消息时,会将消息转换为消息序列,可以通过依赖注入 [`EventMessage`](../advanced/dependency.mdx#eventmessage),或者使用 `event.get_message()` 获取。
|
||||
|
||||
由于消息序列是 `List[MessageSegment]` 的子类,所以你总是可以用和操作 `List` 类似的方式来处理消息序列。例如:
|
||||
|
||||
```python
|
||||
>>> from nonebot.adapters.console import Message, MessageSegment
|
||||
>>> message = Message([
|
||||
MessageSegment(type="text", data={"text":"hello"}),
|
||||
MessageSegment(type="markdown", data={"markup":"**world**"}),
|
||||
])
|
||||
>>> for segment in message:
|
||||
... print(segment.type, segment.data)
|
||||
...
|
||||
text {'text': 'hello'}
|
||||
markdown {'markup': '**world**'}
|
||||
>>> len(message)
|
||||
2
|
||||
```
|
||||
|
||||
### 构造消息序列
|
||||
|
||||
在使用事件响应器操作发送消息时,既可以使用 `str` 作为消息,也可以使用 `Message`、`MessageSegment` 或者 `MessageTemplate`。那么,我们就需要先构造一个消息序列。消息序列可以通过多种方式构造:
|
||||
|
||||
#### 直接构造
|
||||
|
||||
`Message` 类可以直接实例化,支持 `str`、`MessageSegment`、`Iterable[MessageSegment]` 或适配器自定义类型的参数。
|
||||
|
||||
```python
|
||||
from nonebot.adapters.console import Message, MessageSegment
|
||||
|
||||
# str
|
||||
Message("Hello, world!")
|
||||
# MessageSegment
|
||||
Message(MessageSegment.text("Hello, world!"))
|
||||
# List[MessageSegment]
|
||||
Message([MessageSegment.text("Hello, world!")])
|
||||
```
|
||||
|
||||
#### 运算构造
|
||||
|
||||
`Message` 对象可以通过 `str`、`MessageSegment` 相加构造,详情请参考[拼接消息](#拼接消息)。
|
||||
|
||||
#### 从字典数组构造
|
||||
|
||||
`Message` 对象支持 Pydantic 自定义类型构造,可以使用 Pydantic 的 `TypeAdapter` 方法进行构造。
|
||||
|
||||
```python
|
||||
from pydantic import TypeAdapter
|
||||
from nonebot.adapters.console import Message, MessageSegment
|
||||
|
||||
# 由字典构造消息段
|
||||
TypeAdapter(MessageSegment).validate_python(
|
||||
{"type": "text", "data": {"text": "text"}}
|
||||
) == MessageSegment.text("text")
|
||||
|
||||
# 由字典数组构造消息序列
|
||||
TypeAdapter(Message).validate_python(
|
||||
[MessageSegment.text("text"), {"type": "text", "data": {"text": "text"}}],
|
||||
) == Message([MessageSegment.text("text"), MessageSegment.text("text")])
|
||||
```
|
||||
|
||||
### 获取消息纯文本
|
||||
|
||||
由于消息中存在各种类型的消息段,因此 `str(message)` 通常**不能得到消息的纯文本**,而是一个消息序列的字符串表示。
|
||||
|
||||
NoneBot 为消息段定义了一个方法 `is_text()` ,可以用于判断消息段是否为纯文本;也可以使用 `message.extract_plain_text()` 方法获取消息纯文本。
|
||||
|
||||
```python
|
||||
from nonebot.adapters.console import Message, MessageSegment
|
||||
|
||||
# 判断消息段是否为纯文本
|
||||
MessageSegment.text("text").is_text() == True
|
||||
|
||||
# 提取消息纯文本字符串
|
||||
Message(
|
||||
[MessageSegment.text("text"), MessageSegment.markdown("**markup**")]
|
||||
).extract_plain_text() == "text"
|
||||
```
|
||||
|
||||
### 遍历
|
||||
|
||||
消息序列继承自 `List[MessageSegment]` ,因此可以使用 `for` 循环遍历消息段。
|
||||
|
||||
```python
|
||||
for segment in message:
|
||||
...
|
||||
```
|
||||
|
||||
### 比较
|
||||
|
||||
消息和消息段都可以使用 `==` 或 `!=` 运算符比较是否相同。
|
||||
|
||||
```python
|
||||
MessageSegment.text("text") != MessageSegment.text("foo")
|
||||
|
||||
some_message == Message([MessageSegment.text("text")])
|
||||
```
|
||||
|
||||
### 检查消息段
|
||||
|
||||
我们可以通过 `in` 运算符或消息序列的 `has` 方法来:
|
||||
|
||||
```python
|
||||
# 是否存在消息段
|
||||
MessageSegment.text("text") in message
|
||||
# 是否存在指定类型的消息段
|
||||
"text" in message
|
||||
```
|
||||
|
||||
我们还可以使用消息序列的 `only` 方法来检查消息中是否仅包含指定的消息段。
|
||||
|
||||
```python
|
||||
# 是否都为指定消息段
|
||||
message.only(MessageSegment.text("test"))
|
||||
# 是否仅包含指定类型的消息段
|
||||
message.only("text")
|
||||
```
|
||||
|
||||
### 过滤、索引与切片
|
||||
|
||||
消息序列对列表的索引与切片进行了增强,在原有列表 `int` 索引与 `slice` 切片的基础上,支持 `type` 过滤索引与切片。
|
||||
|
||||
```python
|
||||
from nonebot.adapters.console import Message, MessageSegment
|
||||
|
||||
message = Message(
|
||||
[
|
||||
MessageSegment.text("test"),
|
||||
MessageSegment.markdown("test2"),
|
||||
MessageSegment.markdown("test3"),
|
||||
MessageSegment.text("test4"),
|
||||
]
|
||||
)
|
||||
# 索引
|
||||
message[0] == MessageSegment.text("test")
|
||||
# 切片
|
||||
message[0:2] == Message(
|
||||
[MessageSegment.text("test"), MessageSegment.markdown("test2")]
|
||||
)
|
||||
# 类型过滤
|
||||
message["markdown"] == Message(
|
||||
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
||||
)
|
||||
# 类型索引
|
||||
message["markdown", 0] == MessageSegment.markdown("test2")
|
||||
# 类型切片
|
||||
message["markdown", 0:2] == Message(
|
||||
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
||||
)
|
||||
```
|
||||
|
||||
我们也可以通过消息序列的 `include`、`exclude` 方法进行类型过滤。
|
||||
|
||||
```python
|
||||
message.include("text", "markdown")
|
||||
message.exclude("text")
|
||||
```
|
||||
|
||||
同样的,消息序列对列表的 `index`、`count` 方法也进行了增强,可以用于索引指定类型的消息段。
|
||||
|
||||
```python
|
||||
# 指定类型首个消息段索引
|
||||
message.index("markdown") == 1
|
||||
# 指定类型消息段数量
|
||||
message.count("markdown") == 2
|
||||
```
|
||||
|
||||
此外,消息序列添加了一个 `get` 方法,可以用于获取指定类型指定个数的消息段。
|
||||
|
||||
```python
|
||||
# 获取指定类型指定个数的消息段
|
||||
message.get("markdown", 1) == Message([MessageSegment.markdown("test2")])
|
||||
```
|
||||
|
||||
### 拼接消息
|
||||
|
||||
`str`、`Message`、`MessageSegment` 对象之间可以直接相加,相加均会返回一个新的 `Message` 对象。
|
||||
|
||||
```python
|
||||
# 消息序列与消息段相加
|
||||
Message([MessageSegment.text("text")]) + MessageSegment.text("text")
|
||||
# 消息序列与字符串相加
|
||||
Message([MessageSegment.text("text")]) + "text"
|
||||
# 消息序列与消息序列相加
|
||||
Message([MessageSegment.text("text")]) + Message([MessageSegment.text("text")])
|
||||
# 字符串与消息序列相加
|
||||
"text" + Message([MessageSegment.text("text")])
|
||||
# 消息段与消息段相加
|
||||
MessageSegment.text("text") + MessageSegment.text("text")
|
||||
# 消息段与字符串相加
|
||||
MessageSegment.text("text") + "text"
|
||||
# 消息段与消息序列相加
|
||||
MessageSegment.text("text") + Message([MessageSegment.text("text")])
|
||||
# 字符串与消息段相加
|
||||
"text" + MessageSegment.text("text")
|
||||
```
|
||||
|
||||
如果需要在当前消息序列后直接拼接新的消息段,可以使用 `Message.append`、`Message.extend` 方法,或者使用自加。
|
||||
|
||||
```python
|
||||
msg = Message([MessageSegment.text("text")])
|
||||
# 自加
|
||||
msg += "text"
|
||||
msg += MessageSegment.text("text")
|
||||
msg += Message([MessageSegment.text("text")])
|
||||
# 附加
|
||||
msg.append("text")
|
||||
msg.append(MessageSegment.text("text"))
|
||||
# 扩展
|
||||
msg.extend([MessageSegment.text("text")])
|
||||
```
|
||||
|
||||
我们也可以通过消息段或消息序列的 `join` 方法来拼接一串消息:
|
||||
|
||||
```python
|
||||
seg = MessageSegment.text("text")
|
||||
msg = seg.join(
|
||||
[
|
||||
MessageSegment.text("first"),
|
||||
Message(
|
||||
[
|
||||
MessageSegment.text("second"),
|
||||
MessageSegment.text("third"),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
msg == Message(
|
||||
[
|
||||
MessageSegment.text("first"),
|
||||
MessageSegment.text("text"),
|
||||
MessageSegment.text("second"),
|
||||
MessageSegment.text("third"),
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### 使用消息模板
|
||||
|
||||
为了提供安全可靠的跨平台模板字符,我们提供了一个消息模板功能来构建消息序列
|
||||
|
||||
它在以下常见场景中尤其有用:
|
||||
|
||||
- 多行富文本编排(包含图片,文字以及表情等)
|
||||
- 客制化(由 Bot 最终用户提供消息模板时)
|
||||
|
||||
在事实上,它的用法和 `str.format` 极为相近,所以你在使用的时候,总是可以参考[Python 文档](https://docs.python.org/zh-cn/3/library/stdtypes.html#str.format)来达到你想要的效果,这里给出几个简单的例子。
|
||||
|
||||
默认情况下,消息模板采用 `str` 纯文本形式的格式化:
|
||||
|
||||
```python title=基础格式化用法
|
||||
>>> from nonebot.adapters import MessageTemplate
|
||||
>>> MessageTemplate("{} {}").format("hello", "world")
|
||||
'hello world'
|
||||
```
|
||||
|
||||
如果 `Message.template` 构建消息模板,那么消息模板将采用消息序列形式的格式化,此时的消息将会是平台特定的:
|
||||
|
||||
:::caution 注意
|
||||
使用 `Message.template` 构建消息模板时,应注意消息序列为平台适配器提供的类型,不能使用 `nonebot.adapters.Message` 基类作为模板构建。使用基类构建模板与使用 `str` 构建模板的效果是一样的,因此请使用上述的 `MessageTemplate` 类直接构建模板。:
|
||||
:::
|
||||
|
||||
```python title=平台格式化用法
|
||||
>>> from nonebot.adapters.console import Message, MessageSegment
|
||||
>>> Message.template("{} {}").format("hello", "world")
|
||||
Message(
|
||||
MessageSegment.text("hello"),
|
||||
MessageSegment.text(" "),
|
||||
MessageSegment.text("world")
|
||||
)
|
||||
```
|
||||
|
||||
消息模板支持使用消息段进行格式化:
|
||||
|
||||
```python title=对消息段进行安全的拼接
|
||||
>>> from nonebot.adapters.console import Message, MessageSegment
|
||||
>>> Message.template("{}{}").format(MessageSegment.markdown("**markup**"), "world")
|
||||
Message(
|
||||
MessageSegment(type='markdown', data={'markup': '**markup**'}),
|
||||
MessageSegment(type='text', data={'text': 'world'})
|
||||
)
|
||||
```
|
||||
|
||||
消息模板同样支持使用消息序列作为模板:
|
||||
|
||||
```python title=以消息对象作为模板
|
||||
>>> from nonebot.adapters.console import Message, MessageSegment
|
||||
>>> Message.template(
|
||||
... MessageSegment.text("{user_id}") + MessageSegment.emoji("tada") +
|
||||
... MessageSegment.text("{message}")
|
||||
... ).format_map({"user_id": 123456, "message": "hello world"})
|
||||
Message(
|
||||
MessageSegment(type='text', data={'text': '123456'}),
|
||||
MessageSegment(type='emoji', data={'emoji': 'tada'}),
|
||||
MessageSegment(type='text', data={'text': 'hello world'})
|
||||
)
|
||||
```
|
||||
|
||||
:::caution 注意
|
||||
只有消息序列中的文本类型消息段才能被格式化,其他类型的消息段将会原样添加。
|
||||
:::
|
||||
|
||||
消息模板支持使用拓展控制符来控制消息段类型:
|
||||
|
||||
```python title=使用消息段的拓展控制符
|
||||
>>> from nonebot.adapters.console import Message, MessageSegment
|
||||
>>> Message.template("{name:emoji}").format(name='tada')
|
||||
Message(MessageSegment(type='emoji', data={'name': 'tada'}))
|
||||
```
|
267
website/versioned_docs/version-2.4.1/tutorial/store.mdx
Normal file
267
website/versioned_docs/version-2.4.1/tutorial/store.mdx
Normal file
@ -0,0 +1,267 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
description: 从商店安装适配器和插件
|
||||
|
||||
options:
|
||||
menu:
|
||||
- category: tutorial
|
||||
weight: 40
|
||||
---
|
||||
|
||||
# 获取商店内容
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
import Asciinema from "@site/src/components/Asciinema";
|
||||
|
||||
:::tip 提示
|
||||
|
||||
如果你暂时没有获取商店内容的需求,可以跳过本章节。
|
||||
|
||||
:::
|
||||
|
||||
NoneBot 提供了一个[商店](/store/plugins),商店内容均由社区开发者贡献。你可以在商店中查找你需要的适配器和插件等,进行安装或者参考其文档等。
|
||||
|
||||
商店中每个内容的卡片都包含了其名称和简介等信息,点击**卡片右上角**链接图标即可跳转到其主页。
|
||||
|
||||
## 安装插件
|
||||
|
||||
<Asciinema
|
||||
url="https://asciinema.org/a/569650.cast"
|
||||
options={{ theme: "monokai", poster: "npt:16.8" }}
|
||||
/>
|
||||
|
||||
在商店插件页面中,点击你需要安装的插件下方的 `点击复制安装命令` 按钮,即可复制 `nb-cli` 命令。
|
||||
|
||||
请在你的**项目目录**下执行该命令。`nb-cli` 会自动安装插件并将其添加到加载列表中。
|
||||
|
||||
<Tabs groupId="cli-install">
|
||||
<TabItem value="cli" label="使用命令" default>
|
||||
|
||||
```bash
|
||||
nb plugin install <插件名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="interactive" label="交互式安装">
|
||||
|
||||
```bash
|
||||
$ nb plugin install
|
||||
[?] 想要安装的插件名称: <插件名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```bash
|
||||
pip install <插件包名>
|
||||
```
|
||||
|
||||
插件包名可以在商店插件卡片中找到,或者使用 `nb-cli` 搜索插件显示的详情中找到。安装完成后,需要参考[加载插件章节](./create-plugin.md#加载插件)自行加载。
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
如果想要查看插件列表,可以使用以下命令
|
||||
|
||||
```bash
|
||||
# 列出商店所有插件
|
||||
nb plugin list
|
||||
# 搜索商店插件
|
||||
nb plugin search [可选关键词]
|
||||
```
|
||||
|
||||
升级和卸载插件可以使用以下命令
|
||||
|
||||
<Tabs groupId="cli-install">
|
||||
<TabItem value="cli" label="使用命令" default>
|
||||
|
||||
```bash
|
||||
nb plugin update <插件名称>
|
||||
nb plugin uninstall <插件名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="interactive" label="交互式安装">
|
||||
|
||||
```bash
|
||||
$ nb plugin update
|
||||
[?] 想要安装的插件名称: <插件名称>
|
||||
$ nb plugin uninstall
|
||||
[?] 想要卸载的插件名称: <插件名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```bash
|
||||
pip install --upgrade <插件包名>
|
||||
pip uninstall <插件包名>
|
||||
```
|
||||
|
||||
插件包名可以在商店插件卡片中找到,或者使用 `nb-cli` 搜索插件显示的详情中找到。卸载完成后,需要自行移除插件加载。
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 安装适配器
|
||||
|
||||
<Asciinema
|
||||
url="https://asciinema.org/a/569664.cast"
|
||||
options={{ theme: "monokai", poster: "npt:12.0" }}
|
||||
/>
|
||||
|
||||
安装适配器与安装插件类似,只是将命令换为 `nb adapter`,这里就不再赘述。
|
||||
|
||||
请在你的**项目目录**下执行该命令。`nb-cli` 会自动安装适配器并将其添加到注册列表中。
|
||||
|
||||
<Tabs groupId="cli-install">
|
||||
<TabItem value="cli" label="使用命令" default>
|
||||
|
||||
```bash
|
||||
nb adapter install <适配器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="interactive" label="交互式安装">
|
||||
|
||||
```bash
|
||||
$ nb adapter install
|
||||
[?] 想要安装的适配器名称: <适配器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```bash
|
||||
pip install <适配器包名>
|
||||
```
|
||||
|
||||
适配器包名可以在商店适配器卡片中找到,或者使用 `nb-cli` 搜索适配器显示的详情中找到。安装完成后,需要参考[注册适配器章节](../advanced/adapter.md#注册适配器)自行注册。
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
如果想要查看适配器列表,可以使用以下命令
|
||||
|
||||
```bash
|
||||
# 列出商店所有适配器
|
||||
nb adapter list
|
||||
# 搜索商店适配器
|
||||
nb adapter search [可选关键词]
|
||||
```
|
||||
|
||||
升级和卸载适配器可以使用以下命令
|
||||
|
||||
<Tabs groupId="cli-install">
|
||||
<TabItem value="cli" label="使用命令" default>
|
||||
|
||||
```bash
|
||||
nb adapter update <适配器名称>
|
||||
nb adapter uninstall <适配器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="interactive" label="交互式安装">
|
||||
|
||||
```bash
|
||||
$ nb adapter update
|
||||
[?] 想要安装的适配器名称: <适配器名称>
|
||||
$ nb adapter uninstall
|
||||
[?] 想要卸载的适配器名称: <适配器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```bash
|
||||
pip install --upgrade <适配器包名>
|
||||
pip uninstall <适配器包名>
|
||||
```
|
||||
|
||||
适配器包名可以在商店适配器卡片中找到,或者使用 `nb-cli` 搜索适配器显示的详情中找到。卸载完成后,需要自行移除适配器加载。
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 安装驱动器
|
||||
|
||||
<Asciinema
|
||||
url="https://asciinema.org/a/569665.cast"
|
||||
options={{ theme: "monokai", poster: "npt:14.0" }}
|
||||
/>
|
||||
|
||||
安装驱动器与安装插件同样类似,只是将命令换为 `nb driver`,这里就不再赘述。
|
||||
|
||||
如果你使用了虚拟环境,请在你的**项目目录**下执行该命令,`nb-cli` 会自动安装驱动器到虚拟环境中。
|
||||
|
||||
请注意 `nb-cli` 并不会在安装驱动器后修改项目所使用的驱动器,请自行参考[配置方法](../appendices/config.mdx)章节以及 [`DRIVER` 配置项](../appendices/config.mdx#driver)修改驱动器。
|
||||
|
||||
<Tabs groupId="cli-install">
|
||||
<TabItem value="cli" label="使用命令" default>
|
||||
|
||||
```bash
|
||||
nb driver install <驱动器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="interactive" label="交互式安装">
|
||||
|
||||
```bash
|
||||
$ nb driver install
|
||||
[?] 想要安装的驱动器名称: <驱动器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```bash
|
||||
pip install <驱动器包名>
|
||||
```
|
||||
|
||||
驱动器包名可以在商店驱动器卡片中找到,或者使用 `nb-cli` 搜索驱动器显示的详情中找到。
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
如果想要查看驱动器列表,可以使用以下命令
|
||||
|
||||
```bash
|
||||
# 列出商店所有驱动器
|
||||
nb driver list
|
||||
# 搜索商店驱动器
|
||||
nb driver search [可选关键词]
|
||||
```
|
||||
|
||||
升级和卸载驱动器可以使用以下命令
|
||||
|
||||
<Tabs groupId="cli-install">
|
||||
<TabItem value="cli" label="使用命令" default>
|
||||
|
||||
```bash
|
||||
nb driver update <驱动器名称>
|
||||
nb driver uninstall <驱动器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="interactive" label="交互式安装">
|
||||
|
||||
```bash
|
||||
$ nb driver update
|
||||
[?] 想要安装的驱动器名称: <驱动器名称>
|
||||
$ nb driver uninstall
|
||||
[?] 想要卸载的驱动器名称: <驱动器名称>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```bash
|
||||
pip install --upgrade <驱动器包名>
|
||||
pip uninstall <驱动器包名>
|
||||
```
|
||||
|
||||
驱动器包名可以在商店驱动器卡片中找到,或者使用 `nb-cli` 搜索驱动器显示的详情中找到。卸载完成后,需要自行移除适配器加载。
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
Reference in New Issue
Block a user