banner
NEWS LETTER

Bot插件依赖注意事项

Scroll down

参考: Nonebot2文档

配置自定义插件的存储位置

在项目的pyproject.toml中:

1
2
3
4
5
6
[tool.nonebot]
plugin_dirs = ["path/to/your/plugins"]

[tool.nonebot.plugins]
"@local" = ["path.to.your.plugin"] # 本地插件等非插件商店来源的插件
"nonebot-plugin-someplugin" = ["nonebot_plugin_someplugin"] # 插件商店来源的插件

插件配置

插件格式:

1
2
3
📂 <plugin name>
├── 📜 __init__.py
└── 📜 config.py

插件的配置项会由插件模块的config.py进行读取,对应.env文件的配置

配置项解析

.env文件中的配置值使用 JSON 进行解析。如果配置项值无法被解析,将作为字符串处理。例如:

1
2
3
4
5
6
7
8
9
10
11
12
STRING_CONFIG=some string
LIST_CONFIG=[1, 2, 3]
DICT_CONFIG={"key": "value"}
MULTILINE_CONFIG='
[
{
"item_key": "item_value"
}
]
'
EMPTY_CONFIG=
NULL_CONFIG

将被解析为:

1
2
3
4
5
6
7
8
dotenv_config = {
"string_config": "some string",
"list_config": [1, 2, 3],
"dict_config": {"key": "value"},
"multiline_config": [{"item_key": "item_value"}],
"empty_config": "",
"null_config": None
}

特别的,NoneBot 支持使用 env_nested_delimiter 配置嵌套字典,在层与层之间使用 __ 分隔即可:

1
2
3
4
DICT={"k1": "v1", "k2": null}
DICT__K2=v2
DICT__K3=v3
DICT__INNER__K4=v4

将被解析为:

1
2
3
4
5
6
7
8
9
10
dotenv_config = {
"dict": {
"k1": "v1",
"k2": "v2",
"k3": "v3",
"inner": {
"k4": "v4"
}
}
}

文件存储

需要插件nonebot-plugin-localstore

同时代码中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from nonebot import require
require("nonebot_plugin_localstore")
import nonebot_plugin_localstore as store

# 获取插件缓存目录
cache_dir = store.get_plugin_cache_dir()
# 获取插件缓存文件
cache_file = store.get_plugin_cache_file("file_name")
# 获取插件数据目录
data_dir = store.get_plugin_data_dir()
# 获取插件数据文件
data_file = store.get_plugin_data_file("file_name")
# 获取插件配置目录
config_dir = store.get_plugin_config_dir()
# 获取插件配置文件
config_file = store.get_plugin_config_file("file_name")

这些目录均为Pathlib.Path类型

自定义存储位置:

1
2
3
4
# 重定向 localstore 插件的存储路径,便于后续迁移 Bot
LOCALSTORE_CACHE_DIR=data/nonebot/cache
LOCALSTORE_CONFIG_DIR=data/nonebot/config
LOCALSTORE_DATA_DIR=data/nonebot/data

配置数据库

需要插件nonebot-plugin-orm

需要另行安装数据库驱动:

pdm add "nonebot-plugin-orm[sqlite]"

默认情况下,数据库文件为 <data path>/nonebot-plugin-orm/db.sqlite3(数据目录由 nonebot-plugin-localstore 提供)。 或者,你可以通过配置 SQLALCHEMY_DATABASE_URL 来指定数据库文件路径:
SQLALCHEMY_DATABASE_URL=sqlite+aiosqlite:///file_path

使用时,当有新的使用数据库的插件时,执行同步数据库(需要迁移脚本)

1
nb orm upgrade

之后,检查一下:

1
nb orm check

如果输出是 没有检测到新的升级操作,那么恭喜你,数据库已经迁移完成了,你可以启动机器人了。

定义模型

首先,我们需要设计存储的数据的结构。

例:

1
2
3
4
5
6
7
from nonebot_plugin_orm import Model
from sqlalchemy.orm import Mapped, mapped_column

class Weather(Model):
    __tablename__ = "weather" #自定义表名
location: Mapped[str] = mapped_column(primary_key=True)
weather: Mapped[str]

结构定义完成之后,不用创建表,nonebot会自动帮我们创建

创建迁移脚本

原来是我们定义了模型,但是数据库中并没有对应的表,这会导致插件不能正常运行。 所以,我们需要迁移数据库。

首先,我们需要创建一个迁移脚本:

1
nb orm revision -m "first revision" --branch-label weather

其中,-m 参数是迁移脚本的描述,--branch-label 参数是迁移脚本的分支,一般为插件模块名。 执行命令过后,出现了一个 /migrations 目录,其中有一个 xxxxxxxxxxxx_first_revision.py 文件。

然后就可以用nb orm upgrade进行数据库迁移了。

APScheduler配置

当使用用APScheduler进行计划任务的插件,未在计划时间内执行则可能会导致bot进程退出

你可以设置一个容错时间misfire_grace_time让它可以在迟到多少秒后依然可以执行。

1
APSCHEDULER_CONFIG='{"apscheduler.timezone": "Asia/Shanghai", "apscheduler.job_defaults.misfire_grace_time": 40}'

Discord Slash(斜杠)命令注册

当安装了Discord适配器时,就可以适配Discord。但,Discord有个很坑人的设置:消息内容意图开关。机器人启动后需要约45分钟才能真正收到Discord消息的内容,否则只会收到空消息。为了规避这一点,可以利用Discord的斜杠命令系统。

以下为Discord斜杠命令示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import asyncio
from typing import Optional

from nonebot.adapters.discord.api import (
IntegerOption,
NumberOption,
StringOption,
SubCommandOption,
User,
UserOption,
)
from nonebot.adapters.discord.commands import (
CommandOption,
on_slash_command,
)

# 其余的如block,rule,priority等参数和普通的响应器区别不大
matcher = on_slash_command(
name="permission", # 命令名
description="权限管理", # Discord 显示的命令描述
block=True,
rule=rule,
options=[
SubCommandOption( # 次级命令
name="add",
description="添加",
options=[
StringOption( # 字符串选项
name="plugin",
description="插件名",
required=True, #是否必需
),
IntegerOption( # 整数选项
name="priority",
description="优先级",
required=False,
),
],
),
SubCommandOption( # 次级命令
name="remove",
description="移除",
options=[
StringOption(name="plugin", description="插件名", required=True),
NumberOption(name="time", description="时长", required=False),
],
),
SubCommandOption(
name="ban",
description="禁用",
options=[
UserOption(name="user", description="用户", required=False),
],
),
],
)

对斜杠命令的响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import asyncio
from typing import Optional

@matcher.handle_sub_command("add") #对于次级命令的响应
async def handle_user_add(
plugin: CommandOption[str], priority: CommandOption[Optional[int]] #要与上面的name对上
):
await matcher.send_deferred_response() # 若处理指令需要时间,一定要加,否则Discord回应的超时时间仅为5秒,之后直接回复APP无响应
await asyncio.sleep(2)
await matcher.edit_response(f"你添加了插件 {plugin},优先级 {priority}")
await asyncio.sleep(2)
fm = await matcher.send_followup_msg(
f"你添加了插件 {plugin},优先级 {priority} (新消息)"
)
await asyncio.sleep(2)
await matcher.edit_followup_msg(
fm.id, f"你添加了插件 {plugin},优先级 {priority} (新消息修改后)"
)


# 对主命令的响应用普通的handle()即可
其他文章