mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-07-27 16:21:28 +00:00
🏗️ change doc theme
This commit is contained in:
4
website/docs/guide/coding/_category_.json
Normal file
4
website/docs/guide/coding/_category_.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "编写插件",
|
||||
"position": 2
|
||||
}
|
206
website/docs/guide/coding/creating-a-handler.md
Normal file
206
website/docs/guide/coding/creating-a-handler.md
Normal file
@ -0,0 +1,206 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 140
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 事件处理
|
||||
|
||||
在上一章中,我们已经注册了事件响应器,现在我们可以正式编写事件处理逻辑了!
|
||||
|
||||
## [事件处理函数](../api/typing.md#handler)
|
||||
|
||||
```python{1,2,8,9}
|
||||
@weather.handle()
|
||||
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
|
||||
args = str(event.get_message()).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海
|
||||
if args:
|
||||
state["city"] = args # 如果用户发送了参数则直接赋值
|
||||
|
||||
|
||||
@weather.got("city", prompt="你想查询哪个城市的天气呢?")
|
||||
async def handle_city(bot: Bot, event: Event, state: T_State):
|
||||
city = state["city"]
|
||||
if city not in ["上海", "北京"]:
|
||||
await weather.reject("你想查询的城市暂不支持,请重新输入!")
|
||||
city_weather = await get_weather(city)
|
||||
await weather.finish(city_weather)
|
||||
```
|
||||
|
||||
在之前的样例中,我们定义了两个函数 `handle_first_receive`, `handle_city`,他们被事件响应器的装饰器装饰从而成为事件响应器的事件处理函数。
|
||||
|
||||
:::tip 提示
|
||||
在事件响应器中,事件处理函数是**顺序**执行的!
|
||||
:::
|
||||
|
||||
### 添加一个事件处理函数
|
||||
|
||||
事件响应器提供了三种装饰事件处理函数的装饰器,分别是:
|
||||
|
||||
1. [handle()](../api/matcher.md#classmethod-handle)
|
||||
2. [receive()](../api/matcher.md#classmethod-receive)
|
||||
3. [got(key, prompt, args_parser)](../api/matcher.md#classmethod-got-key-prompt-none-args-parser-none)
|
||||
|
||||
#### handle()
|
||||
|
||||
简单的为事件响应器添加一个事件处理函数,这个函数将会在上一个处理函数正常返回执行完毕后立即执行。
|
||||
|
||||
#### receive()
|
||||
|
||||
指示 NoneBot 接收一条新的用户消息后继续执行该处理函数。此时函数将会接收到新的消息而非前一条消息,之前相关信息可以存储在 state 中。
|
||||
|
||||
特别地,当装饰的函数前没有其他事件处理函数,那么 `receive()` 不会接收一条新的消息而是直接使用第一条接收到的消息。
|
||||
|
||||
#### got(key, prompt, args_parser)
|
||||
|
||||
指示 NoneBot 当 `state` 中不存在 `key` 时向用户发送 `prompt` 等待用户回复并赋值给 `state[key]`。
|
||||
|
||||
`prompt` 可以为 `str`, `Message`, `MessageSegment`,若为空则不会向用户发送,若不为空则会在 format 之后发送,即 `prompt.format(**state)`,注意对 `{}` 进行转义。示例:
|
||||
|
||||
```python
|
||||
@matcher.receive()
|
||||
async def handle(bot: Bot, event: Event, state: T_State):
|
||||
state["key"] = "hello"
|
||||
|
||||
|
||||
@matcher.got("key2", prompt="{key}!")
|
||||
async def handle2(bot: Bot, event: Event, state: T_State):
|
||||
pass
|
||||
```
|
||||
|
||||
`args_parser` 为参数处理函数,在这里传入一个新的函数以覆盖默认的参数处理。详情参照 [args_parser](#参数处理函数-args-parser)
|
||||
|
||||
特别的,这些装饰器都可以套娃使用:
|
||||
|
||||
```python
|
||||
@matcher.got("key1")
|
||||
@matcher.got("key2")
|
||||
async def handle(bot: Bot, event: Event, state: T_State):
|
||||
pass
|
||||
```
|
||||
|
||||
### 事件处理函数参数
|
||||
|
||||
事件处理函数类型为:
|
||||
|
||||
- `Callable[[Bot, Event, T_State, Matcher], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot, Event, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot, Event, Matcher], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot, T_State, Matcher], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot, Event], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot, Matcher], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
- `Callable[[Bot], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
|
||||
简单说就是:除了 `bot` 参数,其他都是可选的。
|
||||
|
||||
以下函数都是合法的事件处理函数(仅列举常用的):
|
||||
|
||||
```python
|
||||
async def handle(bot: Bot, event: Event, state: T_State):
|
||||
pass
|
||||
|
||||
async def handle(bot: Bot, event: Event, state: T_State, matcher: Matcher):
|
||||
pass
|
||||
|
||||
async def handle(bot: Bot, event: Event):
|
||||
pass
|
||||
|
||||
async def handle(bot: Bot, state: T_State):
|
||||
pass
|
||||
|
||||
async def handle(bot: Bot):
|
||||
pass
|
||||
```
|
||||
|
||||
:::danger 警告
|
||||
函数的参数名固定不能修改!
|
||||
:::
|
||||
|
||||
参数分别为:
|
||||
|
||||
1. [nonebot.adapters.Bot](../api/adapters/#class-bot): 即事件上报连接对应的 Bot 对象,为 BaseBot 的子类。特别注意,此处的类型注释可以替换为指定的 Bot 类型,例如:`nonebot.adapters.cqhttp.Bot`,只有在上报事件的 Bot 类型与类型注释相符时才会执行该处理函数!可用于多平台进行不同的处理。
|
||||
2. [nonebot.adapters.Event](../api/adapters/#class-event): 即上报事件对象,可以获取到上报的所有信息。
|
||||
3. [state](../api/typing.md#t-state): 状态字典,可以存储任意的信息,其中还包含一些特殊的值以获取 NoneBot 内部处理时的一些信息,如:
|
||||
|
||||
- `state["_current_key"]`: 存储当前 `got` 获取的参数名
|
||||
- `state["_prefix"]`, `state["_suffix"]`: 存储当前 TRIE 匹配的前缀/后缀,可以通过该值获取用户命令的原始命令
|
||||
|
||||
:::tip 提示
|
||||
NoneBot 会对不同类型的参数进行不同的操作,详情查看 [事件处理函数重载](../advanced/overloaded-handlers.md)
|
||||
:::
|
||||
|
||||
### 参数处理函数 args_parser
|
||||
|
||||
在使用 `got` 获取用户输入参数时,需要对用户的消息进行处理以转换为我们所需要的信息。在默认情况下,NoneBot 会把用户的消息字符串原封不动的赋值给 `state[key]` 。可以通过以下两种方式修改默认处理逻辑:
|
||||
|
||||
- `@matcher.args_parser` 装饰器:直接装饰一个函数作为参数处理器
|
||||
- `got(key, prompt, args_parser)`:直接把函数作为参数传入
|
||||
|
||||
参数处理函数类型为:`Callable[[Bot, Event, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]`,即:
|
||||
|
||||
```python
|
||||
async def parser(bot: Bot, event: Event, state: T_State):
|
||||
state[state["_current_key"]] = str(event.get_message())
|
||||
```
|
||||
|
||||
特别的,`state["_current_key"]` 中存储了当前获取的参数名
|
||||
|
||||
### 逻辑控制
|
||||
|
||||
NoneBot 也为事件处理函数提供了一些便捷的逻辑控制函数:
|
||||
|
||||
#### `matcher.send`
|
||||
|
||||
这个函数用于发送一条消息给当前交互的用户。~~其实这并不是一个逻辑控制函数,只是不知道放在哪里……~~
|
||||
|
||||
#### `matcher.pause`
|
||||
|
||||
这个函数用于结束当前事件处理函数,强制接收一条新的消息再运行**下一个消息处理函数**。
|
||||
|
||||
#### `matcher.reject`
|
||||
|
||||
这个函数用于结束当前事件处理函数,强制接收一条新的消息再**再次运行当前消息处理函数**。常用于用户输入信息不符合预期。
|
||||
|
||||
#### `matcher.finish`
|
||||
|
||||
这个函数用于直接结束当前事件处理。
|
||||
|
||||
以上三个函数都拥有一个参数 `message` / `prompt`,用于向用户发送一条消息。以及 `**kwargs` 直接传递给 `bot.send` 的额外参数。
|
||||
|
||||
## 常用事件处理结构
|
||||
|
||||
```python
|
||||
matcher = on_command("test")
|
||||
|
||||
# 修改默认参数处理
|
||||
@matcher.args_parser
|
||||
async def parse(bot: Bot, event: Event, state: T_State):
|
||||
print(state["_current_key"], ":", str(event.get_message()))
|
||||
state[state["_current_key"]] = str(event.get_message())
|
||||
|
||||
@matcher.handle()
|
||||
async def first_receive(bot: Bot, event: Event, state: T_State):
|
||||
# 获取用户原始命令,如:/test
|
||||
print(state["_prefix"]["raw_command"])
|
||||
# 处理用户输入参数,如:/test arg1 arg2
|
||||
raw_args = str(event.get_message()).strip()
|
||||
if raw_args:
|
||||
arg_list = raw_args.split()
|
||||
# 将参数存入state以阻止后续再向用户询问参数
|
||||
state["arg1"] = arg_list[0]
|
||||
|
||||
|
||||
@matcher.got("arg1", prompt="参数?")
|
||||
async def arg_handle(bot: Bot, event: Event, state: T_State):
|
||||
# 在这里对参数进行验证
|
||||
if state["arg1"] not in ["allow", "list"]:
|
||||
await matcher.reject("参数不正确!请重新输入")
|
||||
# 发送一些信息
|
||||
await bot.send(event, "message")
|
||||
await matcher.send("message")
|
||||
await matcher.finish("message")
|
||||
```
|
157
website/docs/guide/coding/creating-a-matcher.md
Normal file
157
website/docs/guide/coding/creating-a-matcher.md
Normal file
@ -0,0 +1,157 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 130
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 注册事件响应器
|
||||
|
||||
好了,现在插件已经创建完毕,我们可以开始编写实际代码了,下面将以一个简易单文件天气查询插件为例。
|
||||
|
||||
在插件目录下 `weather.py` 中添加如下代码:
|
||||
|
||||
```python
|
||||
from nonebot import on_command
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters import Bot, Event
|
||||
|
||||
weather = on_command("天气", rule=to_me(), priority=5)
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
|
||||
args = str(event.get_message()).strip() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海
|
||||
if args:
|
||||
state["city"] = args # 如果用户发送了参数则直接赋值
|
||||
|
||||
|
||||
@weather.got("city", prompt="你想查询哪个城市的天气呢?")
|
||||
async def handle_city(bot: Bot, event: Event, state: T_State):
|
||||
city = state["city"]
|
||||
if city not in ["上海", "北京"]:
|
||||
await weather.reject("你想查询的城市暂不支持,请重新输入!")
|
||||
city_weather = await get_weather(city)
|
||||
await weather.finish(city_weather)
|
||||
|
||||
|
||||
async def get_weather(city: str):
|
||||
return f"{city}的天气是..."
|
||||
```
|
||||
|
||||
为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API。
|
||||
|
||||
接下来我们来说明这段代码是如何工作的。
|
||||
|
||||
:::tip 提示
|
||||
从这里开始,你需要对 Python 的 asyncio 编程有所了解,因为 NoneBot 是完全基于 asyncio 的,具体可以参考 [廖雪峰的 Python 教程](https://www.liaoxuefeng.com/wiki/1016959663602400/1017959540289152)
|
||||
:::
|
||||
|
||||
## [事件响应器](../../api/matcher.md)
|
||||
|
||||
```python{5}
|
||||
from nonebot import on_command
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.permission import Permission
|
||||
|
||||
weather = on_command("天气", rule=to_me(), permission=Permission(), priority=5)
|
||||
```
|
||||
|
||||
在上方代码中,我们注册了一个事件响应器 `Matcher`,它由几个部分组成:
|
||||
|
||||
1. `on_command` 注册一个消息类型的命令处理器
|
||||
2. `"天气"` 指定 command 参数 - 命令名
|
||||
3. `rule` 补充事件响应器的匹配规则
|
||||
4. `priority` 事件响应器优先级
|
||||
5. `block` 是否阻止事件传递
|
||||
|
||||
其他详细配置可以参考 API 文档,下面我们详细说明各个部分:
|
||||
|
||||
### 事件响应器类型 type
|
||||
|
||||
事件响应器类型其实就是对应事件的类型 `Event.get_type()` ,NoneBot 提供了一个基础类型事件响应器 `on()` 以及一些其他内置的事件响应器。
|
||||
|
||||
以下所有类型的事件响应器都是由 `on(type, rule)` 的形式进行了简化封装。
|
||||
|
||||
- `on("事件类型")`: 基础事件响应器,第一个参数为事件类型,空字符串表示不限
|
||||
- `on_metaevent()` ~ `on("meta_event")`: 元事件响应器
|
||||
- `on_message()` ~ `on("message")`: 消息事件响应器
|
||||
- `on_request()` ~ `on("request")`: 请求事件响应器
|
||||
- `on_notice()` ~ `on("notice")`: 通知事件响应器
|
||||
- `on_startswith(str)` ~ `on("message", startswith(str))`: 消息开头匹配响应器,参考 [startswith](../../api/rule.md#startswith-msg)
|
||||
- `on_endswith(str)` ~ `on("message", endswith(str))`: 消息结尾匹配响应器,参考 [endswith](../../api/rule.md#endswith-msg)
|
||||
- `on_keyword(set)` ~ `on("message", keyword(str))`: 消息关键词匹配响应器,参考 [keyword](../../api/rule.md#keyword-keywords)
|
||||
- `on_command(str|tuple)` ~ `on("message", command(str|tuple))`: 命令响应器,参考 [command](../../api/rule.md#command-cmds)
|
||||
- `on_regex(pattern_str)` ~ `on("message", regex(pattern_str))`: 正则匹配处理器,参考 [regex](../../api/rule.md#regex-regex-flags-0)
|
||||
|
||||
### 匹配规则 rule
|
||||
|
||||
事件响应器的匹配规则即 `Rule`,详细内容在下方介绍。[直达](#自定义-rule)
|
||||
|
||||
### 优先级 priority
|
||||
|
||||
事件响应器的优先级代表事件响应器的执行顺序,同一优先级的事件响应器会 **同时执行!**,优先级数字**越小**越先响应!优先级请从 `1` 开始排序!
|
||||
|
||||
:::tip 提示
|
||||
使用 `nonebot-plugin-test` 可以在网页端查看当前所有事件响应器的执行流程,有助理解事件响应流程!
|
||||
|
||||
```bash
|
||||
nb plugin install nonebot_plugin_test
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 阻断 block
|
||||
|
||||
当有任意事件响应器发出了阻止事件传递信号时,该事件将不再会传递给下一优先级,直接结束处理。
|
||||
|
||||
NoneBot 内置的事件响应器中,所有 `message` 类的事件响应器默认会阻断事件传递,其他则不会。
|
||||
|
||||
在部分情况中,可以使用 `matcher.stop_propagation()` 方法动态阻止事件传播,该方法需要 `handler` 在参数中获取 `matcher` 实例后调用方法。
|
||||
|
||||
## 自定义 rule
|
||||
|
||||
rule 的出现使得 nonebot 对事件的响应可以非常自由,nonebot 内置了一些规则:
|
||||
|
||||
- [startswith(msg)](../../api/rule.md#startswith-msg)
|
||||
- [endswith(msg)](../../api/rule.md#endswith-msg)
|
||||
- [keyword(\*keywords)](../../api/rule.md#keyword-keywords)
|
||||
- [command(\*cmds)](../../api/rule.md#command-cmds)
|
||||
- [regex(regex, flag)](../../api/rule.md#regex-regex-flags-0)
|
||||
|
||||
以上规则都是返回类型为 `Rule` 的函数,`Rule` 由非负个 `RuleChecker` 组成,当所有 `RuleChecker` 返回 `True` 时匹配成功。这些 `Rule`, `RuleChecker` 的形式如下:
|
||||
|
||||
```python
|
||||
from nonebot.rule import Rule
|
||||
from nonebot.typing import T_State
|
||||
|
||||
async def async_checker(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
return True
|
||||
|
||||
def sync_checker(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
return True
|
||||
|
||||
def check(arg1, arg2):
|
||||
|
||||
async def _checker(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
return bool(arg1 + arg2)
|
||||
|
||||
return Rule(_checker)
|
||||
```
|
||||
|
||||
`Rule` 和 `RuleChecker` 之间可以使用 `与 &` 互相组合:
|
||||
|
||||
```python
|
||||
from nonebot.rule import Rule
|
||||
|
||||
Rule(async_checker1) & sync_checker & async_checker2
|
||||
```
|
||||
|
||||
**_请勿将事件处理的逻辑写入 `rule` 中,这会使得事件处理返回奇怪的响应。_**
|
||||
|
||||
:::danger 警告
|
||||
`Rule(*checkers)` 只接受 async function,或使用 `nonebot.utils.run_sync` 自行包裹 sync function。在使用 `与 &` 时,NoneBot 会自动包裹 sync function
|
||||
:::
|
128
website/docs/guide/coding/creating-a-plugin.md
Normal file
128
website/docs/guide/coding/creating-a-plugin.md
Normal file
@ -0,0 +1,128 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 120
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 创建插件
|
||||
|
||||
如果之前使用 `nb-cli` 生成了项目结构,那我们已经有了一个空的插件目录 `Awesome-Bot/awesome_bot/plugins`,并且它已在 `bot.py` 中被加载,我们现在可以开始创建插件了!
|
||||
|
||||
使用 `nb-cli` 创建包形式插件,或自行创建文件(夹)
|
||||
|
||||
```bash
|
||||
nb plugin new
|
||||
```
|
||||
|
||||
下面分别对两种通常的插件形式做具体介绍
|
||||
|
||||
## 单文件形式
|
||||
|
||||
在插件目录下创建名为 `foo.py` 的 Python 文件,暂时留空,此时目录结构如下:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
:::vue
|
||||
AweSome-Bot
|
||||
├── awesome_bot
|
||||
│ └── plugins
|
||||
│ └── `foo.py`
|
||||
├── .env
|
||||
├── .env.dev
|
||||
├── .env.prod
|
||||
├── .gitignore
|
||||
├── bot.py
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
├── pyproject.toml
|
||||
└── README.md
|
||||
:::
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
这个时候它已经可以被称为一个插件了,尽管它还什么都没做。
|
||||
|
||||
## 包形式(推荐)
|
||||
|
||||
在插件目录下创建文件夹 `foo`,并在该文件夹下创建文件 `__init__.py`,此时目录结构如下:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
:::vue
|
||||
AweSome-Bot
|
||||
├── awesome_bot
|
||||
│ └── plugins
|
||||
│ └── `foo`
|
||||
│ └── `__init__.py`
|
||||
├── .env
|
||||
├── .env.dev
|
||||
├── .env.prod
|
||||
├── .gitignore
|
||||
├── bot.py
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
├── pyproject.toml
|
||||
└── README.md
|
||||
:::
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
这个时候 `foo` 就是一个合法的 Python 包了,同时也是合法的 NoneBot 插件,插件内容可以在 `__init__.py` 中编写。
|
||||
|
||||
### 推荐结构(仅供参考)
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
:::vue
|
||||
foo
|
||||
├── `__init__.py`
|
||||
├── `config.py`
|
||||
├── `data_source.py`
|
||||
└── `model.py`
|
||||
:::
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
#### \_\_init\_\_.py
|
||||
|
||||
在该文件中编写各类事件响应及处理逻辑。
|
||||
|
||||
#### config.py
|
||||
|
||||
在该文件中使用 `pydantic` 定义插件所需要的配置项以及类型。
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
from pydantic import BaseSettings
|
||||
|
||||
|
||||
class Config(BaseSettings):
|
||||
|
||||
# plugin custom config
|
||||
plugin_setting: str = "default"
|
||||
|
||||
class Config:
|
||||
extra = "ignore"
|
||||
```
|
||||
|
||||
并在 `__init__.py` 文件中添加以下行
|
||||
|
||||
```python
|
||||
import nonebot
|
||||
from .config import Config
|
||||
|
||||
global_config = nonebot.get_driver().config
|
||||
plugin_config = Config(**global_config.dict())
|
||||
```
|
||||
|
||||
此时就可以通过 `plugin_config.plugin_setting` 获取到插件所需要的配置项了。
|
||||
|
||||
#### data_source.py
|
||||
|
||||
在该文件中编写数据获取函数。
|
||||
|
||||
:::warning 警告
|
||||
数据获取应尽量使用**异步**处理!例如使用 [httpx](https://www.python-httpx.org/) 而非 [requests](https://requests.readthedocs.io/en/master/)
|
||||
:::
|
||||
|
||||
#### model.py
|
||||
|
||||
在该文件中编写数据库模型。
|
12
website/docs/guide/coding/end-or-start.md
Normal file
12
website/docs/guide/coding/end-or-start.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# 结语
|
||||
|
||||
至此,相信你已经能够写出一个基础的插件了。这里给出几个小提示:
|
||||
|
||||
- 请千万注意事件处理器的优先级设定
|
||||
- 在匹配规则中请勿使用耗时极长的函数
|
||||
|
||||
如果「指南」还不能满足你,前往 [进阶](../advanced/README.md) 查看更多的功能信息。
|
193
website/docs/guide/coding/loading-a-plugin.md
Normal file
193
website/docs/guide/coding/loading-a-plugin.md
Normal file
@ -0,0 +1,193 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 110
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 加载插件
|
||||
|
||||
在 [创建一个完整的项目](creating-a-project) 一章节中,我们已经创建了插件目录 `awesome_bot/plugins`,现在我们在机器人入口文件中加载它。当然,你也可以单独加载一个插件。
|
||||
|
||||
## 加载内置插件
|
||||
|
||||
在 `bot.py` 文件中添加以下行:
|
||||
|
||||
```python{8}
|
||||
import nonebot
|
||||
from nonebot.adapters.cqhttp import Bot
|
||||
|
||||
nonebot.init()
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter("cqhttp", Bot) # 注册 CQHTTP 的 Adapter
|
||||
nonebot.load_builtin_plugins() # 加载 nonebot 内置插件
|
||||
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
::: warning
|
||||
目前, 内建插件仅支持 CQHTTP 的 Adapter
|
||||
|
||||
如果您使用的是其他 Adapter, 请移步该 Adapter 相应的文档
|
||||
:::
|
||||
|
||||
这将会加载 nonebot 内置的插件,它包含:
|
||||
|
||||
- 命令 `say`:可由**superuser**使用,可以将消息内容由特殊纯文本转为富文本
|
||||
- 命令 `echo`:可由任何人使用,将消息原样返回
|
||||
|
||||
以上命令均需要指定机器人,即私聊、群聊内@机器人、群聊内称呼机器人昵称。参考 [Rule: to_me](../../api/rule.md#to-me)
|
||||
|
||||
## 加载插件目录
|
||||
|
||||
在 `bot.py` 文件中添加以下行:
|
||||
|
||||
```python{6}
|
||||
import nonebot
|
||||
|
||||
nonebot.init()
|
||||
|
||||
# 加载插件目录,该目录下为各插件,以下划线开头的插件将不会被加载
|
||||
nonebot.load_plugins("awesome_bot/plugins")
|
||||
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
加载插件目录时,目录下以 `_` 下划线开头的插件将不会被加载!
|
||||
:::
|
||||
|
||||
:::warning 提示
|
||||
**不能存在相同名称的插件!**
|
||||
:::
|
||||
|
||||
:::danger 警告
|
||||
插件间不应该存在过多的耦合,如果确实需要导入某个插件内的数据,可以参考 [进阶-跨插件访问](../../advanced/export-and-require.md)
|
||||
:::
|
||||
|
||||
## 加载单个插件
|
||||
|
||||
在 `bot.py` 文件中添加以下行:
|
||||
|
||||
```python{6,8}
|
||||
import nonebot
|
||||
|
||||
nonebot.init()
|
||||
|
||||
# 加载一个 pip 安装的插件
|
||||
nonebot.load_plugin("nonebot_plugin_status")
|
||||
# 加载本地的单独插件
|
||||
nonebot.load_plugin("awesome_bot.plugins.xxx")
|
||||
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
## 从 json 文件中加载插件
|
||||
|
||||
在 `bot.py` 文件中添加以下行:
|
||||
|
||||
```python{6}
|
||||
import nonebot
|
||||
|
||||
nonebot.init()
|
||||
|
||||
# 从 plugin.json 加载插件
|
||||
nonebot.load_from_json("plugin.json")
|
||||
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
**json 文件示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": ["nonebot_plugin_status", "awesome_bot.plugins.xxx"],
|
||||
"plugin_dirs": ["awesome_bot/plugins"]
|
||||
}
|
||||
```
|
||||
|
||||
## 从 toml 文件中加载插件
|
||||
|
||||
在 `bot.py` 文件中添加以下行:
|
||||
|
||||
```python{6}
|
||||
import nonebot
|
||||
|
||||
nonebot.init()
|
||||
|
||||
# 从 pyproject.toml 加载插件
|
||||
nonebot.load_from_toml("pyproject.toml")
|
||||
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
**toml 文件示例:**
|
||||
|
||||
```toml
|
||||
[nonebot.plugins]
|
||||
plugins = ["nonebot_plugin_status", "awesome_bot.plugins.xxx"]
|
||||
plugin_dirs = ["awesome_bot/plugins"]
|
||||
```
|
||||
|
||||
::: tip
|
||||
nb-cli 默认使用 `pyproject.toml` 加载插件。
|
||||
:::
|
||||
|
||||
## 子插件(嵌套插件)
|
||||
|
||||
在插件中同样可以加载子插件,例如如下插件目录结构:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
:::vue
|
||||
foo_plugin
|
||||
├── `plugins`
|
||||
│ ├── `sub_plugin1`
|
||||
│ │ └── \_\_init\_\_.py
|
||||
│ └── `sub_plugin2.py`
|
||||
├── `__init__.py`
|
||||
└── config.py
|
||||
:::
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
在插件目录下的 `__init__.py` 中添加如下代码:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
import nonebot
|
||||
|
||||
# store all subplugins
|
||||
_sub_plugins = set()
|
||||
# load sub plugins
|
||||
_sub_plugins |= nonebot.load_plugins(
|
||||
str((Path(__file__).parent / "plugins").resolve()))
|
||||
```
|
||||
|
||||
插件将会被加载并存储于 `_sub_plugins` 中。
|
||||
|
||||
## 运行结果
|
||||
|
||||
尝试运行 `nb run` 或者 `python bot.py`,可以看到日志输出了类似如下内容:
|
||||
|
||||
```plain
|
||||
09-19 21:51:59 [INFO] nonebot | Succeeded to import "nonebot.plugins.base"
|
||||
09-19 21:51:59 [INFO] nonebot | Succeeded to import "plugin_in_folder"
|
||||
```
|
117
website/docs/guide/cqhttp-guide.md
Normal file
117
website/docs/guide/cqhttp-guide.md
Normal file
@ -0,0 +1,117 @@
|
||||
# CQHTTP 协议使用指南
|
||||
|
||||
## 安装 NoneBot CQHTTP 适配器
|
||||
|
||||
```bash
|
||||
pip install nonebot-adapter-cqhttp
|
||||
```
|
||||
|
||||
## 配置 CQHTTP 协议端(以 QQ 为例)
|
||||
|
||||
单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。
|
||||
|
||||
QQ 协议端举例:
|
||||
|
||||
- [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) (基于 [MiraiGo](https://github.com/Mrs4s/MiraiGo))
|
||||
- [onebot-kotlin](https://github.com/yyuueexxiinngg/onebot-kotlin)
|
||||
- [Mirai](https://github.com/mamoe/mirai) + [onebot-mirai](https://github.com/yyuueexxiinngg/onebot-kotlin)
|
||||
- [Mirai](https://github.com/mamoe/mirai) + [Mirai Native](https://github.com/iTXTech/mirai-native) + [CQHTTP](https://github.com/richardchien/coolq-http-api)
|
||||
- [node-onebot](https://github.com/takayama-lily/node-onebot) (基于 [abot](https://github.com/takayama-lily/abot), [OICQ](https://github.com/takayama-lily/oicq))
|
||||
|
||||
这里以 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 为例
|
||||
|
||||
1. 下载 go-cqhttp 对应平台的 release 文件,[点此前往](https://github.com/Mrs4s/go-cqhttp/releases)
|
||||
2. 运行 exe 文件或者使用 `./go-cqhttp` 启动
|
||||
3. 生成默认配置文件并修改默认配置
|
||||
|
||||
### 选项 1 反向 WebSocket 连接
|
||||
|
||||
```yml{2,3,6,10}
|
||||
account:
|
||||
uin: 机器人QQ号
|
||||
password: "机器人密码"
|
||||
|
||||
message:
|
||||
post-format: array
|
||||
|
||||
servers:
|
||||
- ws-reverse:
|
||||
universal: ws://127.0.0.1:8080/cqhttp/ws
|
||||
```
|
||||
|
||||
其中 `ws://127.0.0.1:8080/cqhttp/ws` 中的 `127.0.0.1` 和 `8080` 应分别对应 nonebot 配置的 HOST 和 PORT。
|
||||
|
||||
`cqhttp` 是前述 `register_adapter` 时传入的第一个参数,代表设置的 `CQHTTPBot` 适配器的路径,你可以对不同的适配器设置不同路径以作区别。
|
||||
|
||||
### 选项 2 HTTP POST 上报
|
||||
|
||||
```yml{2,3,6,11}
|
||||
account:
|
||||
uin: 机器人QQ号
|
||||
password: "机器人密码"
|
||||
|
||||
message:
|
||||
post-format: array
|
||||
|
||||
servers:
|
||||
- http:
|
||||
post:
|
||||
- url: "http://127.0.0.1:8080/cqhttp/http"
|
||||
secret: ""
|
||||
```
|
||||
|
||||
其中 `ws://127.0.0.1:8080/cqhttp/http` 中的 `127.0.0.1` 和 `8080` 应分别对应 nonebot 配置的 HOST 和 PORT。
|
||||
|
||||
`cqhttp` 是前述 `register_adapter` 时传入的第一个参数,代表设置的 `CQHTTPBot` 适配器的路径,你可以对不同的适配器设置不同路径以作区别。
|
||||
|
||||
### 选项 3 正向 WebSocket 连接
|
||||
|
||||
```yml{2,3,6,10,11}
|
||||
account:
|
||||
uin: 机器人QQ号
|
||||
password: "机器人密码"
|
||||
|
||||
message:
|
||||
post-format: array
|
||||
|
||||
servers:
|
||||
- ws:
|
||||
host: 127.0.0.1
|
||||
port: 6700
|
||||
```
|
||||
|
||||
NoneBot 配置
|
||||
|
||||
```dotenv
|
||||
CQHTTP_WS_URLS={"机器人QQ号": "ws://127.0.0.1:6700/"}
|
||||
```
|
||||
|
||||
其中 `ws://127.0.0.1:6700/` 中的 `127.0.0.1` 和 `6700` 应分别对应 go-cqhttp 配置的 HOST 和 PORT。
|
||||
|
||||
正向连接可以选择支持客户端连接方式的 `Driver` 来进行连接,请根据需求进行选择:
|
||||
|
||||
- `nonebot.drivers.fastapi`: 同时支持正向和反向
|
||||
- `nonebot.drivers.aiohttp`: 仅支持正向
|
||||
|
||||
## 历史性的第一次对话
|
||||
|
||||
一旦新的配置文件正确生效之后,NoneBot 所在的控制台(如果正在运行的话)应该会输出类似下面的内容(两条访问日志):
|
||||
|
||||
```default
|
||||
09-14 21:31:16 [INFO] uvicorn | ('127.0.0.1', 12345) - "WebSocket /cqhttp/ws" [accepted]
|
||||
09-14 21:31:16 [INFO] nonebot | WebSocket Connection from CQHTTP Bot 你的QQ号 Accepted!
|
||||
```
|
||||
|
||||
这表示 CQHTTP 协议端已经成功地使用 CQHTTP 协议连接上了 NoneBot。
|
||||
|
||||
现在,尝试向你的机器人账号发送如下内容:
|
||||
|
||||
```default
|
||||
/echo 你好,世界
|
||||
```
|
||||
|
||||
到这里如果一切 OK,你应该会收到机器人给你回复了 `你好,世界`。这一历史性的对话标志着你已经成功地运行了一个 NoneBot 的最小实例,开始了编写更强大的 QQ 机器人的创意之旅!
|
||||
|
||||
<!-- <ClientOnly>
|
||||
<Messenger :messages="[{ position: 'right', msg: '/echo 你好,世界' }, { position: 'left', msg: '你好,世界' }]"/>
|
||||
</ClientOnly> -->
|
181
website/docs/guide/ding-guide.md
Normal file
181
website/docs/guide/ding-guide.md
Normal file
@ -0,0 +1,181 @@
|
||||
# 钉钉机器人使用指南
|
||||
|
||||
基于企业机器人的 outgoing(回调)机制,用户@机器人之后,钉钉会将消息内容 POST 到开发者的消息接收地址。开发者解析出消息内容、发送者身份,根据企业的业务逻辑,组装响应的消息内容返回,钉钉会将响应内容发送到群里。
|
||||
|
||||
::: warning 只有企业内部机器人支持接收消息
|
||||
普通的机器人尚不支持应答机制,该机制指的是群里成员在聊天@机器人的时候,钉钉回调指定的服务地址,即 Outgoing 机器人。
|
||||
:::
|
||||
|
||||
首先你需要有钉钉机器人的相关概念,请参阅相关文档:
|
||||
|
||||
- [群机器人概述](https://developers.dingtalk.com/document/app/overview-of-group-robots)
|
||||
- [开发企业内部机器人](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots)
|
||||
|
||||
钉钉官方机器人教程(Java):
|
||||
|
||||
- [开发一个钉钉机器人](https://developers.dingtalk.com/document/tutorial/create-a-robot)
|
||||
|
||||
## 安装 NoneBot 钉钉 适配器
|
||||
|
||||
```bash
|
||||
pip install nonebot-adapter-ding
|
||||
```
|
||||
|
||||
## 关于 DingAdapter 的说明
|
||||
|
||||
你需要显式的注册 ding 这个适配器:
|
||||
|
||||
```python{2,6}
|
||||
import nonebot
|
||||
from nonebot.adapters.ding import Bot as DingBot
|
||||
|
||||
nonebot.init()
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter("ding", DingBot)
|
||||
nonebot.load_builtin_plugins()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
注册适配器的目的是将 `/ding` 这个路径挂载到程序上,并且和 DingBot 适配器关联起来。之后钉钉把收到的消息回调到 `http://xx.xxx.xxx.xxx:{port}/ding` 时,Nonebot 才知道要用什么适配器去处理该消息。
|
||||
|
||||
## 第一个命令
|
||||
|
||||
因为 Nonebot 可以根据你的命令处理函数的类型注解来选择使用什么 Adapter 进行处理,所以你如果需要使用钉钉相关的功能,你的 handler 中 `bot` 类型的注解需要是 DingBot 及其父类。
|
||||
|
||||
对于 Event 来说也是如此,Event 也可以根据标注来判断,比如一个 handler 的 event 标注位 `PrivateMessageEvent`,那这个 handler 只会处理私聊消息。
|
||||
|
||||
举个栗子:
|
||||
|
||||
```python
|
||||
test = on_command("test", to_me())
|
||||
|
||||
|
||||
@test.handle()
|
||||
async def test_handler1(bot: DingBot, event: PrivateMessageEvent):
|
||||
await test.finish("PrivateMessageEvent")
|
||||
|
||||
|
||||
@test.handle()
|
||||
async def test_handler2(bot: DingBot, event: GroupMessageEvent):
|
||||
await test.finish("GroupMessageEvent")
|
||||
```
|
||||
|
||||
这样 Nonebot 就会根据不同的类型注解使用不同的 handler 来处理消息。
|
||||
|
||||
可以查看 Nonebot 官方的这个例子:<https://github.com/nonebot/nonebot2/tree/dev/tests>,更详细的了解一个 Bot 的结构。
|
||||
|
||||
## 多种消息格式
|
||||
|
||||
发送 markdown 消息:
|
||||
|
||||
```python
|
||||
@markdown.handle()
|
||||
async def markdown_handler(bot: DingBot):
|
||||
message = MessageSegment.markdown(
|
||||
"Hello, This is NoneBot",
|
||||
"#### NoneBot \n> Nonebot 是一款高性能的 Python 机器人框架\n> \n> [GitHub 仓库地址](https://github.com/nonebot/nonebot2) \n"
|
||||
)
|
||||
await markdown.finish(message)
|
||||
```
|
||||
|
||||
可以按自己的需要发送原生的格式消息(需要使用 `MessageSegment` 包裹,可以很方便的实现 @ 等操作):
|
||||
|
||||
```python
|
||||
@raw.handle()
|
||||
async def raw_handler(bot: DingBot, event: MessageEvent):
|
||||
message = MessageSegment.raw({
|
||||
"msgtype": "text",
|
||||
"text": {
|
||||
"content": f"@{event.senderId},你好"
|
||||
},
|
||||
})
|
||||
message += MessageSegment.atDingtalkIds(event.senderId)
|
||||
await raw.send(message)
|
||||
```
|
||||
|
||||
其他消息格式请查看 [钉钉适配器的 MessageSegment](https://github.com/nonebot/nonebot2/blob/dev/nonebot/adapters/ding/message.py#L8),里面封装了很多有关消息的方法,比如 `code`、`image`、`feedCard` 等。
|
||||
|
||||
## 发送到特定群聊
|
||||
|
||||
钉钉也支持通过 Webhook 的方式直接将消息推送到某个群聊([参考链接](https://developers.dingtalk.com/document/app/custom-robot-access/title-zob-eyu-qse)),你可以在机器人的设置中看到当前群的 Webhook 地址。
|
||||
|
||||

|
||||
|
||||
获取到 Webhook 地址后,用户可以向这个地址发起 HTTP POST 请求,即可实现给该钉钉群发送消息。
|
||||
|
||||
对于这种通过 Webhook 推送的消息,钉钉需要开发者进行安全方面的设置(目前有 3 种安全设置方式,请根据需要选择一种),如下:
|
||||
|
||||
1. **自定义关键词:** 最多可以设置 10 个关键词,消息中至少包含其中 1 个关键词才可以发送成功。
|
||||
例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。
|
||||
2. **加签:** 发送请求时带上验签的值,可以在机器人设置里看到密钥。
|
||||

|
||||
3. **IP 地址(段):** 设定后,只有来自 IP 地址范围内的请求才会被正常处理。支持两种设置方式:IP 地址和 IP 地址段,暂不支持 IPv6 地址白名单。
|
||||
|
||||
如果你选择 1/3 两种安全设置,你需要自己确认当前网络和发送的消息能被钉钉接受,然后使用 `bot.send` 的时候将 webhook 地址传入 webhook 参数即可。
|
||||
|
||||
如我设置了 `打卡` 为关键词:
|
||||
|
||||
```python
|
||||
message = MessageSegment.text("打卡成功:XXXXXX")
|
||||
await hello.send(
|
||||
message,
|
||||
webhook=
|
||||
"https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX",
|
||||
)
|
||||
```
|
||||
|
||||
对于第二种加签方式,你可以在 `bot.send` 的时候把 `secret` 参数传进去,Nonebot 内部会自动帮你计算发送该消息的签名并发送,如:
|
||||
|
||||
这里的 `secret` 参数就是加签选项给出的那个密钥。
|
||||
|
||||
```python
|
||||
message = MessageSegment.raw({
|
||||
"msgtype": "text",
|
||||
"text": {
|
||||
"content": 'hello from webhook,一定要注意安全方式的鉴权哦,否则可能发送失败的'
|
||||
},
|
||||
})
|
||||
message += MessageSegment.atDingtalkIds(event.senderId)
|
||||
await hello.send(
|
||||
message,
|
||||
webhook="https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX",
|
||||
secret="SECXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
)
|
||||
```
|
||||
|
||||
然后就可以发送成功了。
|
||||
|
||||

|
||||
|
||||
## 创建机器人并连接
|
||||
|
||||
在钉钉官方文档 [「开发企业内部机器人 -> 步骤一:创建机器人应用」](https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots/title-ufs-4gh-poh) 中有详细介绍,这里就省去创建的步骤,介绍一下如何连接上程序。
|
||||
|
||||
### 本地开发机器人
|
||||
|
||||
在本地开发机器人的时候可能没有公网 IP,钉钉官方给我们提供一个 [内网穿透工具](https://developers.dingtalk.com/document/resourcedownload/http-intranet-penetration?pnamespace=app),方便开发测试。
|
||||
|
||||
::: tip
|
||||
究其根源这是一个魔改版的 ngrok,钉钉提供了一个服务器。
|
||||
|
||||
本工具不保证稳定性,仅适用于开发测试阶段,禁止当作公网域名使用。如线上应用使用本工具造成稳定性问题,后果由自己承担。如使用本工具传播违法不良信息,钉钉将追究法律责任。
|
||||
:::
|
||||
|
||||
官方文档里已经讲了如何使用。我们再以 Windows(终端使用 Powershell) 为例,来演示一下。
|
||||
|
||||
1. 将仓库 clone 到本地,打开 `windows_64` 文件夹。
|
||||
2. 执行 `.\ding.exe -config="./ding.cfg" -subdomain=rcnb 8080` 就可以将 8080 端口暴露到公网中。
|
||||
你访问 <http://rcnb.vaiwan.com/xxxxx> 都会映射到 <http://127.0.0.1:8080/xxxxx>。
|
||||
|
||||
假设我们的机器人监听的端口是 `2333`,并且已经注册了钉钉适配器。那我们就执行 `.\ding.exe -config="./ding.cfg" -subdomain=rcnb 2333`,然后在机器人的后台设置 POST 的地址:`http://rcnb.vaiwan.com/ding`。
|
||||
这样钉钉接收到消息之后就会 POST 消息到 `http://rcnb.vaiwan.com/ding` 上,然后这个服务会把消息再转发到我们本地的开发服务器上。
|
||||
|
||||
### 生产模式
|
||||
|
||||
生产模式你的机器需要有一个公网 IP,然后到机器人的后台设置 POST 的地址就好了。
|
||||
|
||||
## 示例
|
||||
|
||||
关于钉钉机器人能做啥,你可以查看 `https://github.com/nonebot/nonebot2/blob/dev/tests/test_plugins/test_ding.py`,里面有一些例子。
|
102
website/docs/guide/feishu-guide.md
Normal file
102
website/docs/guide/feishu-guide.md
Normal file
@ -0,0 +1,102 @@
|
||||
# 飞书机器人使用指南
|
||||
|
||||
基于飞书开放平台事件回调与 API 进行机器人适配,目前仅适配企业自建应用。
|
||||
|
||||
## 安装 NoneBot 飞书 适配器
|
||||
|
||||
```bash
|
||||
pip install nonebot-adapter-feishu
|
||||
```
|
||||
|
||||
## 创建应用与启用应用“机器人”能力
|
||||
|
||||
::: tip
|
||||
此部分可参考[飞书开放平台-快速开发机器人-创建应用](https://open.feishu.cn/document/home/develop-a-bot-in-5-minutes/create-an-app)部分的文档。
|
||||
|
||||
:::
|
||||
|
||||
## 开启应用权限
|
||||
|
||||
应用拥有所需权限后,才能调用飞书接口获取相关信息。如果需要用到所有飞书平台的 API,请开启所有应用权限。
|
||||
|
||||
在仅群聊功能的情况下,需要为应用开启用户、消息、通讯录和群聊权限组所有权限。
|
||||
|
||||
## 配置飞书事件订阅
|
||||
|
||||
::: tip
|
||||
|
||||
在添加事件订阅时请注意,带有**(历史版本)**字样的事件的格式为**不受支持的旧版事件格式**,请使用对应的**新版事件(不带历史版本字样)作为替代**。
|
||||
|
||||
:::
|
||||
|
||||
目前,飞书适配器支持以下事件:
|
||||
| 事件名称 | 事件描述|
|
||||
| ---- | ---- |
|
||||
|接收消息|机器人接收到用户发送的消息。|
|
||||
|消息已读|用户阅读机器人发送的单聊消息。|
|
||||
|群解散|群组被解散。|
|
||||
|群配置更改|群组配置被修改后触发此事件,包含:群主转移、群基本信息修改、群权限修改。|
|
||||
|机器人进群|机器人被添加至群聊。|
|
||||
|机器人被移出群|机器人被移出群聊。|
|
||||
|用户进群|新用户进群。|
|
||||
|撤销拉用户进群|撤销拉用户进群。|
|
||||
|用户被移出群|用户主动退群或被移出群聊。|
|
||||
|
||||
## 在 NoneBot 配置中添加相应配置
|
||||
|
||||
在 `.env` 文件中添加以下配置
|
||||
|
||||
```
|
||||
APP_ID=<yourAppId>
|
||||
APP_SECRET=<yourAppSecret>
|
||||
VERIFICATION_TOKEN=<yourVerificationToken>
|
||||
```
|
||||
|
||||
复制所创建应用**“凭证和基础信息”**中的 **App ID** 、 **App Secret** 和 **“事件订阅”** 中的 **Verification Token** ,替换上面相应的配置的值。
|
||||
|
||||
此外,对于飞书平台的事件订阅加密机制,飞书适配器也提供 `ENCRYPT_KEY` 配置项。
|
||||
|
||||
```
|
||||
ENCRYPT_KEY=<yourEncryptKey>
|
||||
```
|
||||
|
||||
当此项不为空时,飞书适配器会认为用户启用了加密机制,并对事件上报中的密文进行解密。
|
||||
|
||||
对于[Lark(飞书平台海外版)](https://www.larksuite.com) 的用户,飞书适配器也提供**实验性**支持,仅需要在配置文件中添加 `IS_LARK=true` 即可。
|
||||
|
||||
```
|
||||
IS_LARK=true
|
||||
```
|
||||
|
||||
## 注册飞书适配器
|
||||
|
||||
在 `bot.py` 中添加:
|
||||
|
||||
```python
|
||||
from nonebot.adapters.feishu import Bot as FeishuBot
|
||||
|
||||
driver.register_adapter("feishu", FeishuBot)
|
||||
```
|
||||
|
||||
## 编写一个适用于飞书适配器的插件并加载
|
||||
|
||||
插件代码范例:
|
||||
|
||||
```python
|
||||
from nonebot.plugin import on_command
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.feishu import Bot as FeishuBot, MessageEvent
|
||||
|
||||
helper = on_command("say")
|
||||
|
||||
|
||||
@helper.handle()
|
||||
async def feishu_helper(bot: FeishuBot, event: MessageEvent, state: T_State):
|
||||
message = event.get_message()
|
||||
await helper.finish(message, at_sender=True)
|
||||
```
|
||||
|
||||
以上代码注册了一个对飞书平台适用的`say`指令,并会提取`/say`之后的内容发送到事件所对应的群或私聊。
|
||||
|
||||
大功告成!现在可以试试向机器人发送类似`/say Hello, Feishu!`的消息进行测试了。
|
||||
|
BIN
website/docs/guide/images/Handle-Event.png
Normal file
BIN
website/docs/guide/images/Handle-Event.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 KiB |
BIN
website/docs/guide/images/ding/jiaqian.png
Normal file
BIN
website/docs/guide/images/ding/jiaqian.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
BIN
website/docs/guide/images/ding/test_webhook.png
Normal file
BIN
website/docs/guide/images/ding/test_webhook.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
BIN
website/docs/guide/images/ding/webhook.png
Normal file
BIN
website/docs/guide/images/ding/webhook.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
253
website/docs/guide/mirai-guide.md
Normal file
253
website/docs/guide/mirai-guide.md
Normal file
@ -0,0 +1,253 @@
|
||||
# Mirai-API-HTTP 协议使用指南
|
||||
|
||||
::: warning
|
||||
|
||||
Mirai-API-HTTP 的适配现在仍然处于早期阶段, 可能没有进行过充分的测试
|
||||
|
||||
在生产环境中请谨慎使用
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
|
||||
为了你的使用之旅更加顺畅, 我们建议您在配置之前具有以下的前置知识
|
||||
|
||||
- 对服务端/客户端(C/S)模型的基本了解
|
||||
- 对 Web 服务配置基础的认知
|
||||
- 对`YAML`语法的一点点了解
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
Mirai-API-HTTP 的适配器以 [AGPLv3 许可](https://opensource.org/licenses/AGPL-3.0) 单独开源
|
||||
|
||||
这意味着在使用该适配器时需要 **以该许可开源您的完整程序代码**
|
||||
|
||||
:::
|
||||
|
||||
**为了便捷起见, 以下内容均以缩写 `MAH` 代替 `mirai-api-http`**
|
||||
|
||||
## 安装 NoneBot Mirai 适配器
|
||||
|
||||
```bash
|
||||
pip install nonebot-adapter-mirai
|
||||
```
|
||||
|
||||
## 配置 MAH 客户端
|
||||
|
||||
正如你可能刚刚在[CQHTTP 协议使用指南](./cqhttp-guide.md)中所读到的:
|
||||
|
||||
> 单纯运行 NoneBot 实例并不会产生任何效果,因为此刻 QQ 这边还不知道 NoneBot 的存在,也就无法把消息发送给它,因此现在需要使用一个无头 QQ 来把消息等事件上报给 NoneBot。
|
||||
|
||||
这次, 我们将采用在实现上有别于 OneBot(CQHTTP)协议的另外一种无头 QQ API 协议, 即 MAH
|
||||
|
||||
为了配置 MAH 端, 我们现在需要移步到[MAH 的项目地址](https://github.com/project-mirai/mirai-api-http), 来看看它是如何配置的
|
||||
|
||||
根据[项目提供的 README](https://github.com/project-mirai/mirai-api-http/blob/056beedba31d6ad06426997a1d3fde861a7f8ba3/README.md),配置 MAH 大概需要以下几步
|
||||
|
||||
1. 下载并安装 Java 运行环境, 你可以有以下几种选择:
|
||||
|
||||
- [由 Oracle 提供的 Java 运行环境](https://java.com/zh-CN/download/manual.jsp) **在没有特殊需求的情况下推荐**
|
||||
- [由 Zulu 编译的 OpenJRE 环境](https://www.azul.com/downloads/zulu-community/?version=java-8-lts&architecture=x86-64-bit&package=jre)
|
||||
|
||||
2. 下载[Mirai Console Loader](https://github.com/iTXTech/mirai-console-loader)
|
||||
|
||||
- 请按照文档 README 中的步骤下载并安装
|
||||
|
||||
3. 安装 MAH:
|
||||
|
||||
- 在 Mirai Console Loader 目录下执行该指令
|
||||
|
||||
- ```shell
|
||||
./mcl --update-package net.mamoe:mirai-api-http --channel stable --type plugin
|
||||
```
|
||||
|
||||
注意: 该指令的前缀`./mcl`可能根据操作系统以及使用 java 环境的不同而变化
|
||||
|
||||
4. 修改配置文件
|
||||
|
||||
::: tip
|
||||
|
||||
在此之前, 你可能需要了解我们为 MAH 设计的两种通信方式
|
||||
|
||||
- 正向 Websocket
|
||||
- NoneBot 作为纯粹的客户端,通过 websocket 监听事件下发
|
||||
- 优势
|
||||
1. 网络配置简单, 特别是在使用 Docker 等网络隔离的容器时
|
||||
2. 在初步测试中连接性较好
|
||||
- 劣势
|
||||
1. 与 NoneBot 本身的架构不同, 可能稳定性较差
|
||||
2. 需要在注册 adapter 时显式指定 qq, 对于需要开源的程序来讲不利
|
||||
- POST 消息上报
|
||||
- NoneBot 在接受消息上报时作为服务端, 发送消息时作为客户端
|
||||
- 优势
|
||||
1. 与 NoneBot 本身架构相符, 性能和稳定性较强
|
||||
2. 无需在任何地方指定 QQ, 即插即用
|
||||
- 劣势
|
||||
1. 由于同时作为客户端和服务端, 配置较为复杂
|
||||
2. 在测试中网络连接性较差 (未确认原因)
|
||||
|
||||
:::
|
||||
|
||||
- 这是当使用正向 Websocket 时的配置举例
|
||||
|
||||
正向连接可以选择支持客户端连接方式的 `Driver` 来进行连接,请根据需求进行选择:
|
||||
|
||||
- `nonebot.drivers.fastapi`: 同时支持正向和反向
|
||||
- `nonebot.drivers.aiohttp`: 仅支持正向
|
||||
|
||||
::: warning
|
||||
|
||||
在默认情况下, NoneBot 和 MAH 会同时监听 8080 端口, 这会导致端口冲突的错误
|
||||
请确保二者配置不在同一端口下
|
||||
|
||||
:::
|
||||
|
||||
- MAH 的`setting.yml`文件
|
||||
|
||||
- ```yaml
|
||||
# 省略了部分无需修改的部分
|
||||
|
||||
host: "0.0.0.0" # 监听地址
|
||||
port: 8080 # 监听端口
|
||||
authKey: 1234567890 # 访问密钥, 最少八位
|
||||
enableWebsocket: true # 必须为true
|
||||
```
|
||||
|
||||
- `.env`文件
|
||||
|
||||
- ```shell
|
||||
PORT=2333
|
||||
|
||||
MIRAI_AUTH_KEY=1234567890
|
||||
MIRAI_HOST=127.0.0.1 # 当MAH运行在本机时
|
||||
MIRAI_PORT=8080 # MAH的监听端口
|
||||
PORT=2333 # 防止与MAH接口冲突
|
||||
```
|
||||
|
||||
- `bot.py`文件
|
||||
|
||||
- ```python
|
||||
import nonebot
|
||||
from nonebot.adapters.mirai import Bot
|
||||
|
||||
nonebot.init()
|
||||
nonebot.get_driver().register_adapter('mirai',
|
||||
Bot,
|
||||
qq=12345678)
|
||||
# qq参数需要填在mah中登录的qq, 如果需要多个帐号, 可以填写类似于 [123456,789100] 的数组形式
|
||||
|
||||
nonebot.load_builtin_plugins() # 加载 nonebot 内置插件
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
- 这是当使用 POST 消息上报时的配置文件
|
||||
|
||||
- MAH 的`setting.yml`文件
|
||||
|
||||
- ```yaml
|
||||
# 省略了部分无需修改的部分
|
||||
|
||||
host: '0.0.0.0' # 监听地址
|
||||
port: 8080 # 监听端口
|
||||
authKey: 1234567890 # 访问密钥, 最少八位
|
||||
|
||||
## 消息上报
|
||||
report:
|
||||
enable: true # 必须为true
|
||||
groupMessage:
|
||||
report: true # 群消息上报
|
||||
friendMessage:
|
||||
report: true # 好友消息上报
|
||||
tempMessage:
|
||||
report: true # 临时会话上报
|
||||
eventMessage:
|
||||
report: true # 事件上报
|
||||
destinations:
|
||||
- 'http://127.0.0.1:2333/mirai/http' #上报地址, 请按照实际情况修改
|
||||
# 上报时的额外Header
|
||||
extraHeaders: {}
|
||||
```
|
||||
|
||||
- `.env`文件
|
||||
|
||||
- ```shell
|
||||
HOST=127.0.0.1 # 当MAH运行在本机时
|
||||
PORT=2333 # 防止与MAH接口冲突
|
||||
|
||||
MIRAI_AUTH_KEY=1234567890
|
||||
MIRAI_HOST=127.0.0.1 # 当MAH运行在本机时
|
||||
MIRAI_PORT=8080 # MAH的监听端口
|
||||
```
|
||||
|
||||
- `bot.py`文件
|
||||
|
||||
- ```python
|
||||
import nonebot
|
||||
from nonebot.adapters.mirai import Bot
|
||||
|
||||
nonebot.init()
|
||||
nonebot.get_driver().register_adapter('mirai', Bot)
|
||||
nonebot.load_builtin_plugins() # 加载 nonebot 内置插件
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
## 历史性的第一次对话
|
||||
|
||||
现在, 先启动 NoneBot, 再启动 MAH
|
||||
|
||||
如果你的配置文件一切正常, 你将在控制台看到类似于下列的日志
|
||||
|
||||
```log
|
||||
02-01 18:25:12 [INFO] nonebot | NoneBot is initializing...
|
||||
02-01 18:25:12 [INFO] nonebot | Current Env: prod
|
||||
02-01 18:25:12 [DEBUG] nonebot | Loaded Config: {'driver': 'nonebot.drivers.fastapi', 'host': IPv4Address('127.0.0.1'), 'port': 8080, 'debug': True, 'api_root': {}, 'api_timeout': 30.0, 'access_token': None, 'secret': None, 'superusers': set(), 'nickname': set(), 'command_start': {'/'}, 'command_sep': {'.'}, 'session_expire_timeout': datetime.timedelta(seconds=120), 'mirai_port': 8080, 'environment': 'prod', 'mirai_auth_key': 12345678, 'mirai_host': '127.0.0.1'}
|
||||
02-01 18:25:12 [DEBUG] nonebot | Succeeded to load adapter "mirai"
|
||||
02-01 18:25:12 [INFO] nonebot | Succeeded to import "nonebot.plugins.echo"
|
||||
02-01 18:25:12 [INFO] nonebot | Running NoneBot...
|
||||
02-01 18:25:12 [DEBUG] nonebot | Loaded adapters: mirai
|
||||
02-01 18:25:12 [INFO] uvicorn | Started server process [183155]
|
||||
02-01 18:25:12 [INFO] uvicorn | Waiting for application startup.
|
||||
02-01 18:25:12 [INFO] uvicorn | Application startup complete.
|
||||
02-01 18:25:12 [INFO] uvicorn | Uvicorn running on http://127.0.0.1:2333 (Press CTRL+C to quit)
|
||||
02-01 18:25:14 [INFO] uvicorn | 127.0.0.1:37794 - "POST /mirai/http HTTP/1.1" 204
|
||||
02-01 18:25:14 [DEBUG] nonebot | MIRAI | received message {'type': 'BotOnlineEvent', 'qq': 1234567}
|
||||
02-01 18:25:14 [INFO] nonebot | MIRAI 1234567 | [BotOnlineEvent]: {'self_id': 1234567, 'type': 'BotOnlineEvent', 'qq': 1234567}
|
||||
02-01 18:25:14 [DEBUG] nonebot | Checking for matchers in priority 1...
|
||||
```
|
||||
|
||||
恭喜你, 你的配置已经成功!
|
||||
|
||||
现在, 我们可以写一个简单的插件来测试一下
|
||||
|
||||
```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
|
42
website/docs/guide/start/README.md
Normal file
42
website/docs/guide/start/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
id: guide
|
||||
slug: /guide
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 10
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 概览
|
||||
|
||||
<!-- :::tip 提示
|
||||
如果在阅读本文档时遇到难以理解的词汇,请随时查阅 [术语表](../glossary.md) 或使用 [Google 搜索](https://www.google.com/)。
|
||||
::: -->
|
||||
|
||||
:::tip 提示
|
||||
初次使用时可能会觉得这里的概览过于枯燥,可以先简单略读之后直接前往 [安装](./installation.md) 查看安装方法,并进行后续的基础使用教程。
|
||||
:::
|
||||
|
||||
NoneBot2 是一个可扩展的 Python 异步机器人框架,它会对机器人收到的事件进行解析和处理,并以插件化的形式,按优先级分发给事件所对应的事件响应器,来完成具体的功能。
|
||||
|
||||
除了起到解析事件的作用,NoneBot 还为插件提供了大量实用的预设操作和权限控制机制。对于命令处理,它更是提供了完善且易用的会话机制和内部调用机制,以分别适应命令的连续交互和插件内部功能复用等需求。
|
||||
|
||||
得益于 Python 的 [asyncio](https://docs.python.org/3/library/asyncio.html) 机制,NoneBot 处理事件的吞吐量有了很大的保障,再配合 WebSocket 通信方式(也是最建议的通信方式),NoneBot 的性能可以达到 HTTP 通信方式的两倍以上,相较于传统同步 I/O 的 HTTP 通信,更是有质的飞跃。
|
||||
|
||||
需要注意的是,NoneBot 仅支持 **Python 3.7.3 以上版本**
|
||||
|
||||
## 特色
|
||||
|
||||
NoneBot2 的驱动框架 `Driver` 以及通信协议 `Adapter` 均可**自定义**,并且可以作为插件进行**替换/添加**!
|
||||
|
||||
- 提供使用简易的脚手架
|
||||
- 提供丰富的官方插件
|
||||
- 提供可添加/替换的驱动以及协议选项
|
||||
- 基于异步 I/O
|
||||
- 同时支持 HTTP 和反向 WebSocket 通信方式
|
||||
- 支持多个机器人账号负载均衡
|
||||
- 提供直观的交互式会话接口
|
||||
- 提供可自定义的权限控制机制
|
||||
- 多种方式渲染要发送的消息内容,使对话足够自然
|
4
website/docs/guide/start/_category_.json
Normal file
4
website/docs/guide/start/_category_.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "开始",
|
||||
"position": 1
|
||||
}
|
95
website/docs/guide/start/basic-configuration.md
Normal file
95
website/docs/guide/start/basic-configuration.md
Normal file
@ -0,0 +1,95 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 50
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 基本配置
|
||||
|
||||
到目前为止我们还在使用 NoneBot 的默认行为,在开始编写自己的插件之前,我们先尝试在配置文件上动动手脚,让 NoneBot 表现出不同的行为。
|
||||
|
||||
在上一章节中,我们创建了默认的项目结构,其中 `.env` 和 `.env.*` 均为项目的配置文件,下面将介绍几种 NoneBot 配置方式。
|
||||
|
||||
:::danger 警告
|
||||
请勿将敏感信息写入配置文件并提交至开源仓库!
|
||||
:::
|
||||
|
||||
## .env 文件
|
||||
|
||||
NoneBot 在启动时将会从系统环境变量或者 `.env` 文件中寻找变量 `ENVIRONMENT` (大小写不敏感),默认值为 `prod`。
|
||||
这将引导 NoneBot 从系统环境变量或者 `.env.{ENVIRONMENT}` 文件中进一步加载具体配置。
|
||||
|
||||
`.env` 文件是基础环境配置文件,该文件中的配置项在不同环境下都会被加载,但会被 `.env.{ENVIRONMENT}` 文件中的配置所覆盖。
|
||||
|
||||
现在,我们在 `.env` 文件中写入当前环境信息:
|
||||
|
||||
```bash
|
||||
# .env
|
||||
ENVIRONMENT=dev
|
||||
CUSTOM_CONFIG=common config # 这个配置项在任何环境中都会被加载
|
||||
```
|
||||
|
||||
如你所想,之后 NoneBot 就会从 `.env.dev` 文件中加载环境变量。
|
||||
|
||||
## .env.\* 文件
|
||||
|
||||
NoneBot 使用 [pydantic](https://pydantic-docs.helpmanual.io/) 进行配置管理,会从 `.env.{ENVIRONMENT}` 文件中获悉环境配置。
|
||||
|
||||
可以在 NoneBot 初始化时指定加载某个环境配置文件: `nonebot.init(_env_file=".env.dev")`,这将忽略你在 `.env` 中设置的 `ENVIRONMENT` 。
|
||||
|
||||
:::warning 提示
|
||||
由于 `pydantic` 使用 JSON 解析配置项,请确保配置项值为 JSON 格式的数据。如:
|
||||
|
||||
```bash
|
||||
list=["123456789", "987654321", 1]
|
||||
test={"hello": "world"}
|
||||
```
|
||||
|
||||
如果配置项值解析失败将作为字符串处理。
|
||||
:::
|
||||
|
||||
示例及说明:
|
||||
|
||||
```bash
|
||||
HOST=0.0.0.0 # 配置 NoneBot 监听的 IP/主机名
|
||||
PORT=8080 # 配置 NoneBot 监听的端口
|
||||
DEBUG=true # 开启 debug 模式 **请勿在生产环境开启**
|
||||
SUPERUSERS=["123456789", "987654321"] # 配置 NoneBot 超级用户
|
||||
NICKNAME=["awesome", "bot"] # 配置机器人的昵称
|
||||
COMMAND_START=["/", ""] # 配置命令起始字符
|
||||
COMMAND_SEP=["."] # 配置命令分割字符
|
||||
|
||||
# Custom Configs
|
||||
CUSTOM_CONFIG1="config in env file"
|
||||
CUSTOM_CONFIG2= # 留空则从系统环境变量读取,如不存在则为空字符串
|
||||
```
|
||||
|
||||
详细的配置项参考 [Config Reference](../api/config.md) 。
|
||||
|
||||
## 系统环境变量
|
||||
|
||||
如果在系统环境变量中定义了配置,则一样会被读取。
|
||||
|
||||
## bot.py 文件
|
||||
|
||||
配置项也可以在 NoneBot 初始化时传入。此处可以传入任意合法 Python 变量。当然也可以在初始化完成后修改或新增。
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
# bot.py
|
||||
import nonebot
|
||||
|
||||
nonebot.init(custom_config3="config on init")
|
||||
|
||||
config = nonebot.get_driver().config
|
||||
config.custom_config3 = "changed after init"
|
||||
config.custom_config4 = "new config after init"
|
||||
```
|
||||
|
||||
## 优先级
|
||||
|
||||
`bot.py` 文件( `nonebot.init` ) > 系统环境变量 > `.env` `.env.*` 文件
|
67
website/docs/guide/start/creating-a-project.md
Normal file
67
website/docs/guide/start/creating-a-project.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 40
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 创建一个完整的项目
|
||||
|
||||
上一章中我们已经运行了一个简单的 NoneBot 实例,在这一章,我们将从零开始一个完整的项目。
|
||||
|
||||
## 目录结构
|
||||
|
||||
可以使用 `nb-cli` 或者自行创建完整的项目目录:
|
||||
|
||||
```bash
|
||||
nb create
|
||||
```
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
:::vue
|
||||
AweSome-Bot
|
||||
├── `awesome_bot` _(**或是 src**)_
|
||||
│ └── `plugins`
|
||||
├── `.env` _(**可选的**)_
|
||||
├── `.env.dev` _(**可选的**)_
|
||||
├── `.env.prod` _(**可选的**)_
|
||||
├── .gitignore
|
||||
├── `bot.py`
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
├── `pyproject.toml`
|
||||
└── README.md
|
||||
:::
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
- `awesome_bot/plugins` 或 `src/plugins`: 用于存放编写的 bot 插件
|
||||
- `.env`, `.env.dev`, `.env.prod`: 各环境配置文件
|
||||
- `bot.py`: bot 入口文件
|
||||
- `pyproject.toml`: 项目依赖管理文件,默认使用 [poetry](https://python-poetry.org/)
|
||||
|
||||
## 启动 Bot
|
||||
|
||||
:::warning 提示
|
||||
如果您使用如 `VSCode` / `PyCharm` 等 IDE 启动 nonebot,请检查 IDE 当前工作空间目录是否与当前侧边栏打开目录一致。
|
||||
|
||||
- 注意:在二者不一致的环境下可能导致 nonebot 读取配置文件和插件等不符合预期
|
||||
|
||||
:::
|
||||
|
||||
通过 `nb-cli`
|
||||
|
||||
```bash
|
||||
nb run [--file=bot.py] [--app=app]
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
如果在 bot 入口文件内定义了 asgi server, `nb-cli` 将会为你启动**冷重载模式**(当文件发生变动时自动重启 NoneBot 实例)
|
||||
:::
|
96
website/docs/guide/start/getting-started.md
Normal file
96
website/docs/guide/start/getting-started.md
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 30
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 开始使用
|
||||
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 NoneBot 实例的准备工作。
|
||||
|
||||
## 最小实例
|
||||
|
||||
如果你已经按照推荐方式安装了 `nb-cli`,使用它创建一个空项目:
|
||||
|
||||
```bash
|
||||
nb create
|
||||
```
|
||||
|
||||
根据引导进行项目配置,完成后会在当前目录下创建一个项目目录,项目目录内包含 `bot.py`。
|
||||
|
||||
如果未安装 `nb-cli`,使用你最熟悉的编辑器或 IDE,创建一个名为 `bot.py` 的文件,内容如下(这里以 CQHTTP 适配器为例):
|
||||
|
||||
```python{4,6,7,10}
|
||||
import nonebot
|
||||
from nonebot.adapters.cqhttp import Bot as CQHTTPBot
|
||||
|
||||
nonebot.init()
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter("cqhttp", CQHTTPBot)
|
||||
nonebot.load_builtin_plugins()
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run()
|
||||
```
|
||||
|
||||
## 解读
|
||||
|
||||
在上方 `bot.py` 中,这几行高亮代码将依次:
|
||||
|
||||
1. 使用默认配置初始化 NoneBot
|
||||
2. 加载 NoneBot 内置的 CQHTTP 协议适配组件
|
||||
`register_adapter` 的第一个参数我们传入了一个字符串,该字符串将会在后文 [配置 CQHTTP 协议端](#配置-cqhttp-协议端-以-qq-为例) 时使用。
|
||||
3. 加载 NoneBot 内置的插件
|
||||
4. 在地址 `127.0.0.1:8080` 运行 NoneBot
|
||||
|
||||
在命令行使用如下命令即可运行这个 NoneBot 实例:
|
||||
|
||||
```bash
|
||||
# nb-cli
|
||||
nb run
|
||||
# 其他
|
||||
python bot.py
|
||||
```
|
||||
|
||||
运行后会产生如下日志:
|
||||
|
||||
```plain
|
||||
09-14 21:02:00 [INFO] nonebot | Succeeded to import "nonebot.plugins.base"
|
||||
09-14 21:02:00 [INFO] nonebot | Running NoneBot...
|
||||
09-14 21:02:00 [INFO] uvicorn | Started server process [1234]
|
||||
09-14 21:02:00 [INFO] uvicorn | Waiting for application startup.
|
||||
09-14 21:02:00 [INFO] uvicorn | Application startup complete.
|
||||
09-14 21:02:00 [INFO] uvicorn | Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
## 配置协议端上报
|
||||
|
||||
在 `bot.py` 文件中使用 `register_adapter` 注册协议适配之后即可配置协议端来完成与 NoneBot 的通信,详细配置方法参考:
|
||||
|
||||
- [配置 CQHTTP](./cqhttp-guide.md)
|
||||
- [配置钉钉](./ding-guide.md)
|
||||
- [配置 mirai-api-http](./mirai-guide.md)
|
||||
|
||||
NoneBot 接受的上报地址与 `Driver` 有关,默认使用的 `FastAPI Driver` 所接受的上报地址有:
|
||||
|
||||
- `/{adapter name}/`: HTTP POST 上报
|
||||
- `/{adapter name}/http/`: HTTP POST 上报
|
||||
- `/{adapter name}/ws`: WebSocket 上报
|
||||
- `/{adapter name}/ws/`: WebSocket 上报
|
||||
|
||||
:::warning 注意
|
||||
如果到这一步你没有在 NoneBot 看到连接成功日志,比较常见的出错点包括:
|
||||
|
||||
- NoneBot 监听 `0.0.0.0`,然后在协议端上报配置中填了 `ws://0.0.0.0:8080/***/ws`
|
||||
- 在 Docker 容器内运行协议端,并通过 `127.0.0.1` 访问宿主机上的 NoneBot
|
||||
- 想从公网访问,但没有修改云服务商的安全组策略或系统防火墙
|
||||
- NoneBot 所监听的端口存在冲突,已被其它程序占用
|
||||
- 弄混了 NoneBot 的 `host`、`port` 参数与协议端上报配置中的 `host`、`port` 参数
|
||||
- `ws://` 错填为 `http://`
|
||||
- 协议端或 NoneBot 启动时遭到外星武器干扰
|
||||
|
||||
请尝试重启协议端 NoneBot、更换端口、修改防火墙、重启系统、仔细阅读前面的文档及提示、更新协议端 和 NoneBot 到最新版本等方式来解决。
|
||||
:::
|
124
website/docs/guide/start/installation.md
Normal file
124
website/docs/guide/start/installation.md
Normal file
@ -0,0 +1,124 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
|
||||
options:
|
||||
menu:
|
||||
weight: 20
|
||||
catogory: guide
|
||||
---
|
||||
|
||||
# 安装
|
||||
|
||||
## 安装 NoneBot
|
||||
|
||||
:::warning 注意
|
||||
请确保你的 Python 版本 >= 3.7。
|
||||
:::
|
||||
|
||||
:::warning 注意
|
||||
请在安装 NoneBot v2 之前卸载 NoneBot v1
|
||||
|
||||
```bash
|
||||
pip uninstall nonebot
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### (推荐安装方式)通过脚手架安装
|
||||
|
||||
1. (推荐)使用你喜欢的 Python 环境管理工具(如 `poetry`)创建新的虚拟环境
|
||||
2. 使用 `pip` 或 其他包管理工具 安装 `nb-cli`,`nonebot2` 会作为其依赖被一起安装
|
||||
|
||||
```bash
|
||||
pip install nb-cli
|
||||
```
|
||||
|
||||
3. 点个 star 吧
|
||||
|
||||
nonebot2: [](https://github.com/nonebot/nonebot2)
|
||||
|
||||
nb-cli: [](https://github.com/nonebot/nb-cli)
|
||||
|
||||
4. 如果有疑问,可以加群交流(点击链接直达)
|
||||
|
||||
[](https://jq.qq.com/?_wv=1027&k=5OFifDh)
|
||||
|
||||
[](https://t.me/cqhttp)
|
||||
|
||||
### (纯净安装)不使用脚手架
|
||||
|
||||
```bash
|
||||
pip install nonebot2
|
||||
# 也可以通过 poetry 安装
|
||||
poetry add nonebot2
|
||||
```
|
||||
|
||||
如果你需要使用最新的(可能**尚未发布**的)特性,可以直接从 GitHub 仓库安装:
|
||||
|
||||
:::warning 注意
|
||||
直接从 Github 仓库中安装意味着你将使用最新提交的代码,它们并没有进行充分的稳定性测试
|
||||
在任何情况下请不要将其应用于生产环境!
|
||||
:::
|
||||
|
||||
```bash
|
||||
# master分支
|
||||
poetry add git+https://github.com/nonebot/nonebot2.git#master
|
||||
# dev分支
|
||||
poetry add git+https://github.com/nonebot/nonebot2.git#dev
|
||||
```
|
||||
|
||||
或者在克隆 Git 仓库后手动安装:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/nonebot/nonebot2.git
|
||||
cd nonebot2
|
||||
poetry install --no-dev # 推荐
|
||||
pip install . # 不推荐
|
||||
```
|
||||
|
||||
## 安装适配器
|
||||
|
||||
适配器可以通过 `nb-cli` 在创建项目时根据你的选择自动安装,也可以自行使用 `pip` 安装
|
||||
|
||||
```bash
|
||||
pip install <adapter-name>
|
||||
```
|
||||
|
||||
```bash
|
||||
# 列出所有的适配器
|
||||
nb adapter list
|
||||
```
|
||||
|
||||
## 安装插件
|
||||
|
||||
插件可以通过 `nb-cli` 进行安装,也可以自行安装并加载插件。
|
||||
|
||||
```bash
|
||||
# 列出所有的插件
|
||||
nb plugin list
|
||||
# 搜索插件
|
||||
nb plugin search <plugin-name>
|
||||
# 安装插件
|
||||
nb plugin install <plugin-name>
|
||||
```
|
||||
|
||||
如果急于上线 Bot 或想要使用现成的插件,以下插件可作为参考:
|
||||
|
||||
### 官方插件
|
||||
|
||||
- [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) 定时任务插件
|
||||
- [NoneBot-Plugin-LocalStore](https://github.com/nonebot/plugin-localstore) 本地数据文件存储插件
|
||||
- [NoneBot-Plugin-Sentry](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_sentry) Sentry 在线日志分析插件
|
||||
- [NoneBot-Plugin-Status](https://github.com/cscs181/QQ-GitHub-Bot/tree/master/src/plugins/nonebot_plugin_status) 服务器状态查看插件
|
||||
|
||||
### 其他插件
|
||||
|
||||
还有更多的插件在 [这里](/store.html) 等着你发现~
|
||||
|
||||
## 安装开发环境(可选)
|
||||
|
||||
NoneBot v2 全程使用 `VSCode` 搭配 `Pylance` 的开发环境进行开发,在严格的类型检查下,NoneBot v2 具有完善的类型设计与声明。
|
||||
|
||||
在围绕 NoneBot v2 进行开发时,使用 `VSCode` 搭配 `Pylance` 进行类型检查是非常推荐的。这有利于统一代码风格及避免低级错误的发生。
|
Reference in New Issue
Block a user