Files
autman-plugins/docs/autman-plugin-rules.md
T

646 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Autman 插件开发规则与仓库约定
> 来源:已参考 BOSS 上传到本仓库的插件示例,以及 autMan 官方论坛教程:<https://bbs.autman.cn/>。重点参考帖子:插件编写规则、插件编程-头注篇、插件编写概论、微服务插件编写、Python 简单插件编写、消息类型说明、实时 IM 消息监听、适配器插件编写指南。
## 1. 仓库用途
本仓库统一存放 BOSS 的 Autman 插件、参考示例、文档和模板。以后新增插件优先按下面目录组织:
```text
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 示例:
```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 示例:
```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 关键词/命令触发
```js
//[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-` 做白名单/黑名单。
示例:
```js
//[rule: ^sim状态$]
//[rule: ^sim短信\s+(.+?)\s+([\s\S]+)$]
//[admin: true]
//[platform: qq,wx,tg,tb]
//[priority: 100]
```
### 3.3 微服务路由触发
微服务插件通过 HTTP 路由触发,新增/修改这类插件后通常需要重启 Autman 才生效。
```js
//[router: /simadmin/status]
//[method: get]
//[method: post]
```
规则:
- `router` 路径必须以 `/` 开头。
- `method` 可写多个,常见为 `get``post`,也可支持 `put/delete`
- GET 参数从 `getRouterParams()` 取。
- POST body 从 `getRouterData()` / `getRouterBody()` 取。
- 最终必须用 `response(obj)` 或兼容方式返回,避免请求挂起。
### 3.4 配参头注
用于让 Autman 后台渲染插件配置项。
```js
//[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 基础信息
```js
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 消息上下文
常见函数/对象:
```js
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 数据桶
```js
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` 时,代码中通常按后半段读取:
```js
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 网络请求
回调风格:
```js
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` 已使用):
```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 推送消息
```js
push({
imType: "qq",
groupCode: "123456",
content: "群消息"
})
push({
imType: "wx",
userID: "user-id",
content: "私聊消息"
})
```
字段:
- 群组:`groupCode` 或部分平台使用 `chatID`
- 私聊:`userID`
- 平台:`imType`
## 6. 微服务插件运行规则
官方示例提到的微服务函数:
```js
getRouterPath()
getRouterMethod()
getRouterHeaders()
getRouterCookies()
getRouterParams()
getRouterBody()
response(obj)
```
BOSS 上传插件和论坛示例中也常见兼容函数名:
```js
var method = getMethod()
var params = getRouterParams() || {}
var data = getRouterData()
```
建议写法:
```js
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 插件必须注意:
1. 先做 token 鉴权,鉴权失败后立即 `return`
2. GET/POST 都支持时,参数读取顺序建议:query 参数 -> POST JSON 覆盖。
3. POST JSON 解析必须 try/catch。
4. 对发短信、重启、开关网络等敏感操作必须管理员或 token 控制。
5. 返回统一 JSON
```json
{"ok":true,"status":"ok","retcode":0,"msg":"success","data":{}}
```
错误:
```json
{"ok":false,"status":"fail","retcode":-1,"msg":"token错误"}
```
## 7. Python 插件规则
Python 插件通过 `middleware` 与 Autman 交互。常见头注:
```python
# [title: 测试]
# [language: python]
# [disable: false]
# [platform: qq,wx]
# [rule: ^测试(.*)$]
# [admin: false]
# [version: 1.0.0]
```
常见初始化:
```python
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()
```
回复/监听:
```python
sender.reply("请输入内容,q退出")
user_input = sender.listen(120000) # 毫秒
if user_input == "q":
sender.reply("退出")
exit(0)
```
数据桶:
```python
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/社交平台。官方建议:
1. 选择不冲突的 `im_type`,尽量 2 个字母,如 `tt`
2. HTTP 回调地址一般为:
```text
http://<autman-host>:<port>/<im_type>/receive
```
3. Autman 与适配器通信的 WS 地址一般为:
```text
http://<autman-host>:<port>/<im_type>/adapter
```
4. 适配器需要把外部平台消息转换为 Autman 消息格式:
```json
{
"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": "消息内容"
}
```
5. Autman 返回动作格式:
```json
{
"aut_action": "send_msg",
"aut_echo": "",
"aut_params": {}
}
```
适配器要把 Autman 动作翻译成外部平台 API 调用,并在需要时回执消息 ID。
## 9. 实时 IM 消息监听
Autman 支持本地 SSE 监听实时 IM 消息,官方接口:
```text
POST /otto/msghook
Accept: text/event-stream
Content-Type: application/json
```
请求 body 示例:
```json
{"imtype":"qq","chatid":"11543756","userid":"282617666"}
```
处理要点:
- 仅限本地访问更安全。
- SSE 数据按行读取,关注 `event:``data:`
- 需要处理断线重连。
- 解析 `data:` 后再做业务逻辑。
## 10. 安全与质量约定
开发新插件时必须遵守:
1. **不要硬编码密钥**token、cookie、API key 放配参、环境变量或本地不提交文件。
2. **外部路由必须鉴权**:尤其是发短信、推送、修改配置、重启、网络开关。
3. **鉴权失败必须 return**:避免只回复错误但继续执行后续逻辑。
4. **敏感操作默认管理员限定**:聊天指令使用 `[admin:true]` 或代码内判断。
5. **统一 JSON 返回**:便于外部系统调用和排错。
6. **请求设置 timeout**:避免 Autman 插件阻塞。
7. **兼容 GET/POST**:微服务接口可同时支持 query 和 JSON body,但 POST JSON 优先。
8. **严格校验参数**:手机号、URL、路径、开关布尔值、枚举值都要校验。
9. **日志不要打印密钥**token/cookie/API key 必须脱敏。
10. **插件说明写清楚**:头注 description 和 README 中都要写指令、参数、示例。
## 11. 新插件开发流程
1. 先在 `plugins/<plugin-name>/` 下写源码和 README。
2. 根据插件类型复制 `templates/` 里的模板。
3. 本地检查头注、鉴权、参数校验和错误返回。
4. 如需适配 BOSS 已有插件风格,优先参考:
- `js/qlck.js`:微服务 API + 配参 + token 鉴权 + request 调外部 API。
- `py/nazha.py`Python middleware 初始化、依赖检测、平台/用户上下文。
5. 提交前确认没有 `.env`、token、cookie、日志、运行时数据。
6. 推送后验证 raw 文件可访问。
## 12. SimAdmin 插件专项约定
后续实现 SimAdmin 插件时建议:
- 配参:
- `otto.simadmin_base_url`
- `otto.simadmin_access_token`
- `otto.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:
```json
// 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`
- 如需兼容旧数据,读顺序建议:
```text
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()`,按顺序尝试:
1. 官方 ES5 全局函数 `GetUserID()`
2. `Sender.GetUserID()`
3. 兼容旧/小写 sender 方法:`sender.getUserID()``sender.getUserId()``sender.getUserid()``sender.getSenderID()``sender.getFromUserId()` 等。
4. 明确的用户全局变量:`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(如 QDReader 用 `1 0,7 * * *`),入口按小时分流:0 点分支只归档上一份批次日志到 `archive_json` 并清空 `last_run_json` / `last_result_json`7 点分支只执行签到/业务动作并写 `last_run_json` / `last_result_json`。如果分钟不同或无法安全按小时分流,再使用多个独立 cron;只有在当前 Autman 运行环境确认不可靠支持单插件多 `[cron]` 时,才退而拆成两个插件文件。查询菜单优先展示当日/当前批次;当日日志为空时回退最近归档,按归档结果反馈成功/失败。
- 普通回复永远不要输出完整 Cookie;只展示脱敏 UID、备注、Cookie key 摘要或长度。
- 高风险的导出/批量导入功能默认不做,除非 BOSS 明确需要。
- Node VM 测试应模拟:`sender.reply``sender.listen``sender.getUserID``get/set``bucketGet/bucketSet``request`
- 测试必须覆盖:快速提交、菜单添加、备注提示、详情改备注、编号选择删除/查询、旧 bucket 兼容、新 bucket 写入、用户隔离、手动执行只跑当前用户、cron 跑全部账号。
## 12. QDReader 账号级任务开关约定
启点/QDReader 这类多账号签到插件的附加任务(广告、抽奖、兑换、章节卡、大咖荐书等)不要再放全局开关里统一控制。BOSS 确认更合适的模型是“直接写进账号里,对每个账号单独管理”。仓库实现约定如下:
- Cookie、用户绑定、备注仍分别保存;附加任务开关保存为 `qdreader_task_pref_json`
- `qdreader_task_pref_json` 结构为 `uid -> { taskKey: boolean }`,例如 `{ "12345": { "adv": true } }`
- `启点账号` 菜单内提供“账号任务配置”,先选择账号,再切换该账号的任务开关;支持 `all on` / `all off` 一键开启或关闭该账号全部可选任务。
- 定时/手动签到执行时按 uid 读取自己的任务配置;一个账号开启广告任务不影响其它账号。
- 删除单个账号或清空当前用户账号时,同步删除该 uid 的任务配置,避免残留配置误作用于重新添加的账号。
- 旧全局开关 key 可留作历史数据,但不要作为运行依据;测试必须覆盖“一个账号开启,另一个账号不执行”的隔离场景。
QDReader 签到结果判定补充:
- 上游返回“今日已签到/已经签到/重复签到”应视为成功,不应把账号计入失败。
- 签名网关返回 `url 不在允许的接口列表内` 说明本地可选任务接口尚未被网关白名单支持;对这类可选任务应显示为“已跳过”,不要让整个账号失败。
- 保留原始错误到任务对象的 `rawMsg` 即可,聊天摘要只显示短的跳过原因,避免刷屏。