了解 NcatBot 插件
约 2233 字大约 7 分钟
2025-02-08
NcatBot 插件
在了解 NcatBot 插件之前, 请至少先阅读快速开始和开发指南的两部分内容.
NcatBot 的插件位于工作目录下 plugins
文件夹中, 每个插件都是一个独立的文件夹.
使用 bot.run()
启动 NcatBot 时, 会自动加载所有插件.
NcatBot 提供一个简易的 CLI 用于下载和管理插件, 详见 CLI
NcatBot 插件系统小故事
凌晨三点的机房里,鱼鱼面前的四块显示器泛着幽幽蓝光。她机械地敲击着键盘, console 里不断刷新的报错信息在视网膜上投下跳动的残影。
“为什么就是抓不到这个事件触发时机......”鱼鱼扯下挂在脖子上的工牌甩在桌上,金属链子撞到咖啡杯发出清脆响声。ncatbot的插件系统像座迷宫,每个API接口都藏着意想不到的陷阱。五天前就该完成的天气插件,此刻仍卡在事件订阅的泥潭里。
冷风突然掀动窗帘,鱼鱼下意识缩了缩脖子。带着松木香气的保温杯轻轻落在手边,蒸腾的热气在显示器的代码注释区晕开一片水雾。
“异步回调里嵌套同步方法,不卡死才怪。”熟悉的声音让鱼鱼猛地转头,三个月未见的彭彭正俯身查看她的屏幕,苍白的指尖点在某个await关键字上,“这里需要加个 Promise.resolve 做缓冲层。”
鱼鱼怔怔看着好友从帆布包里掏出那台贴满电路板贴纸的 ThinkPad ,十指翻飞地调出半个月前的 git 提交记录。“你看,上周重构事件总线的时候,是不是把生命周期钩子的执行顺序改了?”彭彭的呼吸声里混着细微的杂音,像是老旧的通风管道。
他们并排而坐的姿势与大学时代别无二致。那时彭彭总能在鱼鱼卡壳时,用他特有的“五层分析法”把问题拆解成漂亮的思维导图。此刻他正用vscode的调试器下断点,控制台突然跳出的内存警告却让动作顿住。
“你的肝酶指标...”鱼鱼瞥见彭彭袖口下露出的住院手环,话音被剧烈的咳嗽声打断。零散的药片从彭彭口袋里滚落,在机械键盘的缝隙间闪着微光。
彭彭却已重新聚焦在屏幕上:“还记得我们给开源社区写的那个中间件吗?把它的发布-订阅模式移植过来,事件触发延迟能降低70%。”他苍白的脸上泛起病态的红晕,手指在触摸板上划出流畅的轨迹,“看,用RxJS重构事件流,再配合...咳咳...配合装饰器语法做插件注册...”
当晨曦爬上窗棂时,编译成功的提示框终于弹出。鱼鱼看着单元测试全部通过的绿色标记,突然发现彭彭的呼吸不知何时变得平稳绵长。那些散落的头孢克肟药片旁,静静躺着一本翻旧的《分布式系统设计模式》,扉页上是他们毕业时在樱花树下的合影。
“下周的CT复查...”鱼鱼轻声开口,却被彭彭截住话头。好友正在给 README.MD 添加最后一行文档,光标在“特别鸣谢”后欢快地跳动:“就说感谢某位不愿透露姓名的架构师——就像我们给 Linux 内核提交 Patch 时那样。”
晨光中,两个影子在满墙的架构图上交错重叠。鱼鱼突然想起三年前那个暴雨夜,当她的毕业设计因硬盘损坏即将泡汤时,是彭彭连夜用数据恢复工具从物理坏道中抢救出源码。此刻他们面前的屏幕上,NcatBot 的天气插件正在测试群里弹出今日的朝阳预报,而窗外真正的曙光正漫过彭彭不再颤抖的指尖。
TO BE CONTINUED...
一个插件包含以下文件:
- your_plugin
- folder1
- subfolder1-1
- file1-1-1.yaml
- file1-1-2.py
- subfolder1-2
- file1-2-1.py
- file1-2-2.py
- subfolder1-1
- folder2
- file2-2.py
- main.py
- __init__.py
- requirements.txt
- README.md
- .gitignore
- folder1
其中, __init__.py
用于声明插件, 是必须的, 其余文件都是可选的.
your_plugin/
称为插件文件夹, 字符串 your_plugin
称为插件文件夹名, 一个插件文件夹包含且仅包含一个插件.
一个插件所需要的全部资源, 都必须放入插件文件夹中. 插件不应该读写插件文件夹之外的任何内容.
提示
推荐使用 __file__
参数来定位代码的路径.
开发插件时可以在同一个文件下开发多个插件, 文件结构只推荐如下结构:
- launch.py
- plugins
- your_plugin1
- __init__.py
- main.py
- .gitignore
- README.md
- your_plugin2
- __init__.py
- main.py
- .gitignore
- README.md
- your_plugin1
几个提示的点:
.gitignore
文件用于忽略不需要上传的文件, 每个插件文件夹独立- 两个插件完全独立, 例如
your_plugin1
不能使用plugins/your_plugin2/
下任何资源, 只能使用plugins/your_plugin1/
下资源.
创建插件
声明插件
__init__.py
中, 需要声明插件, 并且 __init__.py
必须放在插件文件夹根目录下:
from .main import MyPlugin
__all__ = ["MyPlugin"]
- 第一行: 导入插件类.
- 第二行: 空行, 好看.
- 第三行: 明确被导入的插件类.
NcatBot 不对工作目录做任何保证, 插件项目内部只建议使用相对导入.
定义插件
一般在 main.py
中定义插件, name
称为插件名, 派生类类名(即 MyPlugin.__name__
的值)称为插件类名.
from ncatbot.plugin import BasePlugin, CompatibleEnrollment
class MyPlugin(BasePlugin):
name = "MyPlugin" # 插件名
version = "0.0.1" # 插件版本
命名要求
插件名和插件类名必须和插件文件夹名一致, 例如 MyPlugin
插件, 插件文件夹名为 MyPlugin
, 插件类名为 MyPlugin
, 插件名为 MyPlugin
.
最小示例
文件结构:
- plugins/MyPlugin
- main.py
- __init__.py
- launch.py
__init__.py
插件声明程序, 用于声明插件.
# __init__.py
from .main import MyPlugin
__all__ = ["MyPlugin"]
main.py
插件主程序, 用于定义插件.
# main.py
import os
from ncatbot.plugin import BasePlugin, CompatibleEnrollment
from ncatbot.core.message import GroupMessage
bot = CompatibleEnrollment # 兼容回调函数注册器
class MyPlugin(BasePlugin):
name = "MyPlugin" # 插件名称
version = "0.0.1" # 插件版本
@bot.group_event()
async def on_group_event(self, msg: GroupMessage):
# 定义的回调函数
if msg.raw_message == "测试":
await self.api.post_group_msg(msg.group_id, text="Ncatbot 插件测试成功喵")
async def on_load(self):
# 插件加载时执行的操作, 可缺省
print(f"{self.name} 插件已加载")
print(f"插件版本: {self.version}")
launch.py
引导程序, 用于启动 NcatBot.
from ncatbot.core import BotClient
from ncatbot.utils.config import config
config.set_bot_uin("123456") # 设置 bot qq 号 (必填)
config.set_ws_uri("ws://localhost:3001") # 设置 napcat websocket server 地址
config.set_token("") # 设置 token (napcat 服务器的 token)
bot = BotClient()
if __name__ == "__main__":
bot.run()
代码拆解
导入
from ncatbot.plugin import BasePlugin, CompatibleEnrollment
from ncatbot.core.message import GroupMessage
BasePlugin
: 插件基类. 所有的插件必须是BasePlugin
的派生类, 否则无法被正常加载.CompatibleEnrollment
: 兼容回调函数注册器, 用于注册 BotClient 风格的回调函数. 插件项目和 BotClient 项目采用两种不同的事件触发机制, 为了方便迁移, 提供了这个注册器.
定义插件
bot = CompatibleEnrollment # 兼容回调函数注册器
class MyPlugin(BasePlugin): # 插件类名 为 `MyPlugin`
name = "MyPlugin" # 插件名 为 `MyPlugin` 必须和插件类名一致
version = "0.0.1" # 插件版本
name = "MyPlugin"
: 插件名, 命名规范见上version = "0.0.1"
: 插件版本号.
回调函数
@bot.group_event()
async def on_group_event(self, msg: GroupMessage):
# 定义的回调函数
if msg.raw_message == "测试":
await self.api.post_group_msg(msg.group_id, text="Ncatbot 插件测试成功喵")
BasePlugin.api
是一个 BotAPI
对象, 参考 API 调用 一节使用.
运行插件
环境要求
- NcatBot 已经安装到 Python 环境中.
- 工作目录的文件结构正确, 即如 最小示例 所示. 工作目录下存在
plugins/
文件夹, 所有的插件文件夹均放入plugins/
文件夹中.
直接运行
在工作目录中直接运行 python launch.py
即可.
使用 CLI 运行
参阅.
BotClient 项目和插件项目比较
- 直接使用
NcatBot
时, API 通过bot.api
调用, 而插件系统通过self.api
调用. - 直接使用
NcatBot
时, 回调函数通过@bot.group_event()
注册,bot
是一个BotClient
实例, 而插件系统需要在MyPlugin
里用@bot.group_event()
注册,bot
是CompatibleEnrollment
兼容注册器. - 使用插件系统时, 引导程序中无需注册回调函数, 只需要填写配置项, 创建
BotClient
实例, 运行BotClient
实例.
版权所有
版权归属:huan-yp