20 KiB
Autman 插件开发规则与仓库约定
来源:已参考 BOSS 上传到本仓库的插件示例,以及 autMan 官方论坛教程:https://bbs.autman.cn/。重点参考帖子:插件编写规则、插件编程-头注篇、插件编写概论、微服务插件编写、Python 简单插件编写、消息类型说明、实时 IM 消息监听、适配器插件编写指南。
1. 仓库用途
本仓库统一存放 BOSS 的 Autman 插件、参考示例、文档和模板。以后新增插件优先按下面目录组织:
js/ # ECMAScript5 / 内置 JS 插件,通常放 autMan plugin/replies
py/ # Python 插件,通常放 autMan plugin/scripts
plugins/ # 我们新写的正式插件;按功能再分目录或命名
examples/ # 调用示例、配置示例
templates/ # 可复制的插件模板
docs/ # 规则、接口说明、研究记录
已有历史插件可保留原路径。新开发插件建议优先放到 plugins/,稳定后可按 Autman 实际导入目录复制到 js/ 或 py/。
2. 插件类型
Autman 支持多种插件/脚本形态:
| 类型 | 说明 | 常见位置/用途 |
|---|---|---|
| HTML 插件 | 由 Autman Web 服务托管页面 | plugin/web,主要自用 |
| ES5 插件 | Autman 内置 ES5 环境运行,内置大量函数 | plugin/replies,适合关键词、路由、轻量集成 |
| Python/Node/TS/PHP/Shell/Go 等外部脚本 | Autman 调用宿主系统环境运行,通过 middleware 与 Autman 交互 |
plugin/scripts,适合复杂逻辑、依赖第三方库 |
| 微服务路由插件 | 使用 router / method 头注暴露 HTTP 接口 |
对外接收 Webhook/API 调用 |
| 适配器插件 | 对接新的社交媒体/IM,将外部消息转换为 Autman 消息格式 | 需要处理 HTTP/WS 通信和消息格式转换 |
注意:除 HTML 插件外,其他插件运行通常要求 Autman 授权有效。
3. 头注规则
头注必须放在插件文件头部,用于生成插件元数据和触发条件。
3.1 市场/基础元数据
JS 示例:
//[open_source: true]
//[create_at: 2026-01-01 12:00:00]
//[title: 插件名称]
//[author: 作者]
//[service: 售后联系方式]
//[class: 工具类]
//[version: 1.0.0]
//[platform: qq,wx,tg,tb,web]
//[public: false]
//[price: 0]
//[disable: false]
//[description: 插件说明,尽量写清楚用法]
//[icon: https://example.com/icon.png]
Python 示例:
# [title: 插件名称]
# [language: python]
# [service: 售后联系方式]
# [disable: false]
# [admin: true]
# [rule: ^指令$]
# [platform: qq,wx,tg,tb]
# [open_source: true]
# [version: 1.0.0]
# [public: false]
# [price: 0]
# [description: 插件说明]
字段说明:
| 字段 | 说明 |
|---|---|
title |
插件标题 |
author |
作者 |
service |
售后/联系方式;上架通常必填 |
class |
分类,如工具类、查询类、娱乐类、生活类、图片类等 |
platform |
适用平台,多个用英文逗号分隔 |
public |
是否公开显示到市场;开发测试建议 false |
disable |
true 禁用,false 启用 |
version |
版本号 |
price |
价格 |
description |
说明,建议写清楚指令、参数、注意事项 |
icon |
图标 URL,建议 48px 正方形,支持 http/https |
3.2 关键词/命令触发
//[rule: ^菜单$]
//[rule: ^菜单管理$]
//[admin: true]
//[priority: 300]
//[cron: 0 7 * * *]
//[bypass: false]
规则:
rule支持正则,可写多行表示多个触发规则。cron支持 5 位或 6 位 cron 表达式。admin: true表示仅管理员可执行。priority数字越大优先级越高。bypass: true常用于全拦截规则,例如[rule: ?],表示旁路并行处理。- 可用
imType+/imType-、userId+/userId-、groupId+/groupId-做白名单/黑名单。
示例:
//[rule: ^sim状态$]
//[rule: ^sim短信\s+(.+?)\s+([\s\S]+)$]
//[admin: true]
//[platform: qq,wx,tg,tb]
//[priority: 100]
3.3 微服务路由触发
微服务插件通过 HTTP 路由触发,新增/修改这类插件后通常需要重启 Autman 才生效。
//[router: /simadmin/status]
//[method: get]
//[method: post]
规则:
router路径必须以/开头。method可写多个,常见为get、post,也可支持put/delete。- GET 参数从
getRouterParams()取。 - POST body 从
getRouterData()/getRouterBody()取。 - 最终必须用
response(obj)或兼容方式返回,避免请求挂起。
3.4 配参头注
用于让 Autman 后台渲染插件配置项。
//[param: {"required":true,"key":"otto.simadmin_base_url","bool":false,"placeholder":"http://192.168.1.10:3000","name":"SimAdmin地址","desc":"不要以 / 结尾"}]
//[param: {"required":true,"key":"otto.simadmin_token","bool":false,"placeholder":"访问Token","name":"接口鉴权Token","desc":"外部请求必须携带 access_token"}]
//[param: {"required":false,"key":"otto.simadmin_push_master","bool":true,"placeholder":"","name":"是否推送管理员","desc":""}]
//[param: {"spliter":true}]
约定:
key建议使用统一前缀,例如otto.simadmin_xxx或otto.<插件名>_xxx。- 密钥/token 不要写死在源码里,放到配参或环境变量。
bool: true用于开关项。required只表示配置 UI 要求,不等于运行期一定存在;代码里仍需校验。
4. 常见平台/消息类型
论坛中整理的常见类型:
| 类型 | 含义 |
|---|---|
qq |
QQ 框架 |
qb |
QQ 频道机器人 |
wx |
微信外接框架 |
wb |
内置微信机器人 |
tg |
Telegram 客户端 |
tb |
Telegram 官方机器人 |
wm |
微信订阅号 |
wv |
微信服务号 |
wk |
微信客服 |
dd |
钉钉机器人 |
dc |
Discord 机器人 |
sk |
Slack 机器人 |
fs |
飞书机器人 |
kk |
Kook 机器人 |
qh |
群晖 Synology Chat |
we |
页面对话 |
rt |
router 消息 / 微信服务插件消息 |
terminal |
交互模式终端对话 |
5. ES5 插件内置函数与用法
5.1 基础信息
var name = call("name")()
var port = call("port")()
var version = call("version")()
var machineId = call("machineId")()
var id = call("uuid")()
var sign = call("md5")("text")
var now = call("timeFormat")("yyyy-MM-dd hh:mm:ss")
var textTime = call("unixTimeFormat")(second, nanosecond, "yyyy-MM-dd hh:mm:ss")
var ok = cancall("someFunc")
var coffeeOk = coffee()
5.2 消息上下文
常见函数/对象:
var content = GetContent()
var is_admin = isAdmin()
sendText("回复文本")
var msg = input() // 等待用户输入,适合交互式插件
sender.reply({ok:true, msg:"success"})
Debug("调试日志")
console.log("普通日志")
建议:
- 管理类指令必须先判断
isAdmin()。 input()应处理null/undefined/空字符串/q退出。- JSON 返回优先用对象,便于 Web/API 调用方解析。
5.3 数据桶
bucketSet("bucket", "key", "value")
var value = bucketGet("bucket", "key")
bucketDel("bucket", "key")
var keys = bucketKeys("bucket")
约定:
- 桶名和 key 统一前缀,避免和其他插件冲突。
- 从
bucketGet读出的复杂对象需要JSON.parse,必须 try/catch。 - 写入复杂对象用
JSON.stringify。
5.4 获取配置参数
配参 key 形如 otto.simadmin_base_url 时,代码中通常按后半段读取:
var baseUrl = get("simadmin_base_url")
var token = get("simadmin_token")
实际以 BOSS 上传的插件为准:qlck.js 中 key: "otto.vhook_qlck_token",读取为 get("vhook_qlck_token")。
5.5 网络请求
回调风格:
request({
useproxy: false,
url: "https://example.com/api",
headers: {"Content-Type":"application/json"},
method: "post",
dataType: "json",
body: {a: 1},
timeOut: 10000
}, function(error, response, header, body) {
// 处理结果
})
同步/直接返回风格(BOSS 上传的 js/qlck.js 已使用):
var resp = request({
url: baseUrl + "/api/health",
method: "get",
dataType: "json",
timeOut: 10000
})
约定:
- HTTP API 插件优先使用
dataType: "json"。 - 外部服务业务失败时不一定是 HTTP 非 200,要检查返回 JSON 内的状态字段。
- 请求外部服务前清理 URL 尾部
/:baseUrl.replace(/\/+$/, "")。 - 超时必须设置,避免插件卡死。
5.6 推送消息
push({
imType: "qq",
groupCode: "123456",
content: "群消息"
})
push({
imType: "wx",
userID: "user-id",
content: "私聊消息"
})
字段:
- 群组:
groupCode或部分平台使用chatID。 - 私聊:
userID。 - 平台:
imType。
6. 微服务插件运行规则
官方示例提到的微服务函数:
getRouterPath()
getRouterMethod()
getRouterHeaders()
getRouterCookies()
getRouterParams()
getRouterBody()
response(obj)
BOSS 上传插件和论坛示例中也常见兼容函数名:
var method = getMethod()
var params = getRouterParams() || {}
var data = getRouterData()
建议写法:
function jsonResponse(obj) {
if (typeof response === "function") {
response(obj)
} else if (typeof sender !== "undefined" && sender.reply) {
sender.reply(obj)
} else {
sendText(JSON.stringify(obj))
}
}
外部 API 插件必须注意:
- 先做 token 鉴权,鉴权失败后立即
return。 - GET/POST 都支持时,参数读取顺序建议:query 参数 -> POST JSON 覆盖。
- POST JSON 解析必须 try/catch。
- 对发短信、重启、开关网络等敏感操作必须管理员或 token 控制。
- 返回统一 JSON:
{"ok":true,"status":"ok","retcode":0,"msg":"success","data":{}}
错误:
{"ok":false,"status":"fail","retcode":-1,"msg":"token错误"}
7. Python 插件规则
Python 插件通过 middleware 与 Autman 交互。常见头注:
# [title: 测试]
# [language: python]
# [disable: false]
# [platform: qq,wx]
# [rule: ^测试(.*)$]
# [admin: false]
# [version: 1.0.0]
常见初始化:
import middleware
sender_id = middleware.getSenderID()
sender = middleware.Sender(sender_id)
message = sender.getMessage()
user_id = sender.getUserID()
user_name = sender.getUserName()
im_type = sender.getImtype()
chat_id = sender.getChatID()
is_admin = sender.isAdmin()
回复/监听:
sender.reply("请输入内容,q退出")
user_input = sender.listen(120000) # 毫秒
if user_input == "q":
sender.reply("退出")
exit(0)
数据桶:
middleware.bucketSet("bucket", user_id, "value")
value = middleware.bucketGet("bucket", user_id)
middleware.bucketDel("bucket", user_id)
依赖:
- Python 插件可自动安装依赖,但要谨慎,避免每次运行都安装。
- 建议在代码里先
importlib.import_module检测,再提示管理员安装或自动安装。 - 网络请求可用
requests/aiohttp,但要设置 timeout。
8. 适配器插件要点
适配器用于接入新 IM/社交平台。官方建议:
- 选择不冲突的
im_type,尽量 2 个字母,如tt。 - HTTP 回调地址一般为:
http://<autman-host>:<port>/<im_type>/receive
- Autman 与适配器通信的 WS 地址一般为:
http://<autman-host>:<port>/<im_type>/adapter
- 适配器需要把外部平台消息转换为 Autman 消息格式:
{
"event": "message",
"event_data": {},
"guild_id": "",
"guild_name": "",
"bot_id": "",
"bot_name": "",
"chat_id": "",
"chat_name": "",
"user_id": "",
"user_name": "",
"im_type": "tt",
"message_id": "",
"content": "消息内容"
}
- Autman 返回动作格式:
{
"aut_action": "send_msg",
"aut_echo": "",
"aut_params": {}
}
适配器要把 Autman 动作翻译成外部平台 API 调用,并在需要时回执消息 ID。
9. 实时 IM 消息监听
Autman 支持本地 SSE 监听实时 IM 消息,官方接口:
POST /otto/msghook
Accept: text/event-stream
Content-Type: application/json
请求 body 示例:
{"imtype":"qq","chatid":"11543756","userid":"282617666"}
处理要点:
- 仅限本地访问更安全。
- SSE 数据按行读取,关注
event:和data:。 - 需要处理断线重连。
- 解析
data:后再做业务逻辑。
10. 安全与质量约定
开发新插件时必须遵守:
- 不要硬编码密钥:token、cookie、API key 放配参、环境变量或本地不提交文件。
- 外部路由必须鉴权:尤其是发短信、推送、修改配置、重启、网络开关。
- 鉴权失败必须 return:避免只回复错误但继续执行后续逻辑。
- 敏感操作默认管理员限定:聊天指令使用
[admin:true]或代码内判断。 - 统一 JSON 返回:便于外部系统调用和排错。
- 请求设置 timeout:避免 Autman 插件阻塞。
- 兼容 GET/POST:微服务接口可同时支持 query 和 JSON body,但 POST JSON 优先。
- 严格校验参数:手机号、URL、路径、开关布尔值、枚举值都要校验。
- 日志不要打印密钥:token/cookie/API key 必须脱敏。
- 插件说明写清楚:头注 description 和 README 中都要写指令、参数、示例。
11. 新插件开发流程
- 先在
plugins/<plugin-name>/下写源码和 README。 - 根据插件类型复制
templates/里的模板。 - 本地检查头注、鉴权、参数校验和错误返回。
- 如需适配 BOSS 已有插件风格,优先参考:
js/qlck.js:微服务 API + 配参 + token 鉴权 + request 调外部 API。py/nazha.py:Python middleware 初始化、依赖检测、平台/用户上下文。
- 提交前确认没有
.env、token、cookie、日志、运行时数据。 - 推送后验证 raw 文件可访问。
12. SimAdmin 插件专项约定
后续实现 SimAdmin 插件时建议:
- 配参:
otto.simadmin_base_urlotto.simadmin_access_tokenotto.simadmin_timeout_ms
- 路由:
/simadmin/status/simadmin/sms/send/simadmin/sms/list
- 聊天指令:
sim状态sim短信 <手机号> <内容>
- SimAdmin API 业务失败要检查 JSON 内
status === "error"。 - 发送短信必须 token 或管理员控制。
- Base URL 默认不带尾部
/,代码内统一 trim。
13. 账号/Cookie 类插件菜单设计经验(QDReader 复盘)
最近制作 plugins/qdreader/qdreader_sign_autman.js 时沉淀出一套适用于账号/Cookie 管理类插件的交互与数据规则,后续新插件优先复用:
13.1 交互结构
- 优先做“一级菜单 + 二级编号选择”,不要堆很多平铺子命令。
- 账号管理和执行查询分离:
xxx账号:只做账号列表、添加/更新、详情/备注、删除、清空。xxx查询:只做最后执行日志/最近记录、单账号最近记录、立即执行等执行侧动作;除非明确实现实时接口,否则不要伪装成实时查询。
- 高频输入可以保留快速通道,例如
xxxck <cookie>,但快速通道只做快速提交,不再混入查询/导出/批量导入。 - 用户选择账号时展示编号列表,让用户回复编号;不要让用户手动复制/输入 UID。
- 添加/更新账号成功后立刻询问是否添加备注;备注修改放在账号详情里,不单独做一个割裂的备注菜单。
13.2 数据结构
账号/Cookie 类插件不要把 Cookie、用户绑定、备注、执行状态混在一起,建议拆成独立 map:
// cookie_json
{ "uid": "full cookie" }
// user_bind_json
{ "uid": "sender.getUserID()" }
// remark_json
{ "uid": "主号" }
// last_result_json:单账号最后执行结果
{ "uid": "{\"ok\":true,\"msg\":\"成功\",\"time\":\"...\",\"source\":\"cron\",\"runId\":\"...\"}" }
// last_run_json:最近一次执行批次日志
{ "source": "cron", "finishedAt": "...", "okCount": 1, "failCount": 0, "total": 1, "items": [] }
// archive_json:按日期归档后的批次日志,用于每日结算/历史判断
{ "2026-05-18": { "source": "cron", "finishedAt": "...", "okCount": 1, "failCount": 0, "total": 1, "items": [] } }
这样删除、过滤、展示和权限校验更安全。
13.3 数据桶
- 新插件应创建自己的 bucket,例如
hermes_qidian,不要所有插件都塞进otto。 - 如需兼容旧数据,读顺序建议:
bucketGet("插件专属bucket", key)
↓ fallback
get(key)
↓ fallback
bucketGet("otto", key)
- 写入新数据时写到专属 bucket,可同时写普通
set(key)保持兼容。
13.4 多用户隔离
- 保存账号时绑定当前用户 ID。
- 账号列表、详情、删除、手动执行只处理当前用户绑定的账号。
删除全部只删除当前用户账号,不清空全局数据。- cron/系统触发通常没有用户上下文,是否执行全部账号要显式传
allUsers参数,并在代码和测试里说明。
13.5 Autman JS 用户身份与历史兜底绑定(QDReader 复盘)
QDReader 这次踩坑说明:账号 ID 识别成功,不代表 Autman 会话用户绑定一定正确。JS 插件里不能只照 Python 示例写 sender.getUserID(),也不能在取不到用户时随便兜底。
建议统一写 currentUserId(),按顺序尝试:
- 官方 ES5 全局函数
GetUserID()。 Sender.GetUserID()。- 兼容旧/小写 sender 方法:
sender.getUserID()、sender.getUserId()、sender.getUserid()、sender.getSenderID()、sender.getFromUserId()等。 - 明确的用户全局变量:
userId、userID、senderID、senderId、fromUserId、fromUserID。
不要把 GetChatID()、sender.getChatID()、chatId 当用户 owner。群聊里这是群 ID,会让多个成员绑成同一个 owner,只能用于诊断输出。
保存 CK 时的安全策略:
- 获取不到真实用户 ID:拒绝保存,并输出脱敏诊断,不写
cookie_json/user_bind_json。 cookie_json[uid]不存在但user_bind_json[uid]还存在:这是手动删桶后留下的孤儿绑定,应清理旧绑定后允许重新保存。cookie_json[uid]和user_bind_json[uid]都存在,但 owner 是历史兜底标识(如早期__qdreader_default_owner__或chickliu这类非数字短标识):管理员触发时可自动修复为当前真实用户,并继续更新 CK。cookie_json[uid]和真实其他用户绑定都存在:必须拒绝覆盖,防止抢绑。
这类修复要补回归测试:
- 官方
GetUserID()可正常绑定。 - 旧 sender 字段可兼容绑定。
- 无用户 ID 时拒绝写入。
- CK 被手动删除但绑定残留时可重新添加。
- 管理员可修复历史兜底 owner。
- 非管理员不可修复历史兜底 owner。
- 真实其他用户绑定仍不可覆盖。
13.6 安全与测试
xxx查询的数据来源必须写清楚:如果没有实时接口,就命名为“最近签到记录/最后执行日志”,读取last_run_json和last_result_json,不要让用户误以为是实时查询。- 执行动作要同时写批次日志和单账号日志;成功、业务失败、请求异常都要落日志,异常不能只显示在当次回复里。
- 如果插件需要按天判断执行情况,不要把归档和运行任务放在同一个 cron 入口。优先在同一个插件内声明两个独立 cron,并在入口根据触发时间/触发标识分流:业务时间 cron(如
15 7 * * *)只执行签到/业务动作并写last_run_json/last_result_json;零点归档 cron(如0 0 * * *)只把上一份批次日志按日期写入archive_json,再清空last_run_json/last_result_json,绝不执行签到/业务动作。若只能按当前本机时间识别系统触发,归档窗口要覆盖合理调度延迟风险(例如 00:00-00:04 都归档),避免 00:00 cron 短延迟后误走业务签到,同时不要让窗口过长影响业务语义。只有在当前 Autman 运行环境确认不可靠支持单插件多[cron]时,才退而拆成两个插件文件。查询菜单优先展示当日/当前批次;当日日志为空时回退最近归档,按归档结果反馈成功/失败。 - 普通回复永远不要输出完整 Cookie;只展示脱敏 UID、备注、Cookie key 摘要或长度。
- 高风险的导出/批量导入功能默认不做,除非 BOSS 明确需要。
- Node VM 测试应模拟:
sender.reply、sender.listen、sender.getUserID、get/set、bucketGet/bucketSet、request。 - 测试必须覆盖:快速提交、菜单添加、备注提示、详情改备注、编号选择删除/查询、旧 bucket 兼容、新 bucket 写入、用户隔离、手动执行只跑当前用户、cron 跑全部账号。