写插件
插件是跑在 Aeria 同一个进程里的 Python 代码,通过宿主给的接口做事:收命令、发消息、存自己的数据、在后台跑任务。这一篇从零写一个能用的小插件,后面几篇逐项细讲。
要写的东西
一个插件是一个目录,放在插件目录里(默认 data/plugins/<插件编号>/),里面两样:
plugin.json:它是谁、入口在哪、要哪些能力(3.8);- 入口文件(默认
plugin.py):导出一个叫plugin的对象,宿主启用它时调await plugin.setup(context),停用时调await plugin.teardown()。
例子:每天早上在一个频道里说句早安
data/plugins/morning-note/plugin.json:
{
"id": "morning-note",
"name": "早安",
"version": "1.0.0",
"description": "每天早上在指定频道发一句早安",
"entrypoint": "plugin.py",
"permissions": ["discord.channel.send", "webui.plugin_panel"]
}
data/plugins/morning-note/plugin.py:
import asyncio
from datetime import datetime
class MorningNote:
async def setup(self, context):
self.context = context
# 托管的后台任务:崩了宿主按退避重启,停用、重载时一起停
context.spawn("morning", self._loop)
async def teardown(self):
return None
def panel_schema(self):
# 网页后台「插件中心 → 设置」里画成一张表单
return {"sections": [{"title": "发到哪", "fields": [
{"path": "channel_id", "type": "string", "label": "频道编号"},
]}]}
async def _loop(self):
await self.context.discord.wait_until_ready()
last_day = None
while True:
now = datetime.now()
channel_id = self.context.config().get("channel_id")
if channel_id and now.hour == 8 and now.date() != last_day:
await self.context.discord.send_channel(int(channel_id), content="早安")
last_day = now.date()
await asyncio.sleep(60)
plugin = MorningNote()
装上、确认、启用
- 把这个目录放进插件目录,重启运行时。
- 网页后台「插件中心」里找到它,点「确认能力」:列出来的就是
permissions里写的那几项。 - 点「设置」,填频道编号,保存;然后「启用」。
接下来
内置插件
随 Aeria 一起发的插件不用联网下载,在插件中心里直接装。现在有一个:「对话指令」。它本身不干活,加载着就表示「人格可以把对话里冲着它的请求交给模型判断是不是指令」;不装,人格只聊天。服务器管理、放歌这些自然语言的请求都要它在。
新装默认不装,要用就在插件中心装上。
对应的代码
- 宿主:
src/aeria/plugins.py的PluginManager(安装、确认能力、启用、停用、健康)、PluginManifest(plugin.json的格式)、PluginContext。 - Discord 的接口:
src/aeria/plugin_discord.py的PluginDiscordFacade(wait_until_ready、send_channel……)。 - 设置表单:
webui_frontend/src/routes/PluginSettings.tsx(字段类型string、integer、boolean、id_list、string_list、json)。 - 能力的完整说明:
docs/PLUGIN_PERMISSIONS.md。 - 内置插件「对话指令」:
src/aeria/builtin_plugins/persona_commands/;宿主那边看它在不在:bot.py的_persona_commands_enabled;升级时自动装上:PluginManager.CARRY_OVER_BUILTINS。 - 测试:
tests/test_4_1_plugin_host.py、tests/test_plugin_qq.py、tests/test_plugin_download_limits.py;tests/test_persona_commands_plugin.py的「没装对话指令插件,对话里的话不交给指令判断,只是聊天」。
没做到的
- 没有沙箱:插件和人格在同一个进程里,只能靠接口边界和装的人的确认。
- 「对话指令」现在只是一个开关:判断和执行还在宿主里,还没有做成插件能调的接口。
- 还没有官方的插件源:要装外面的插件,得在配置里加插件源(
[plugins].market_sources)或者自己把目录放进去。