qq-bot-roost 0.8.6 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -10
- package/package.json +1 -1
- package/src/web/server.js +25 -0
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
**填 AppID + AppSecret 就能连上 QQ 官方机器人。零依赖,插件可写任意语言,自带可视化面板。**
|
|
8
8
|
|
|
9
|
-
<img src="https://img.shields.io/badge/Node.js-22%2B-339933?logo=node.js&logoColor=white" alt="Node.js 22+">
|
|
9
|
+
<img src="https://img.shields.io/badge/Node.js-22.13%2B-339933?logo=node.js&logoColor=white" alt="Node.js 22.13+">
|
|
10
10
|
<img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="零依赖">
|
|
11
|
-
<img src="https://img.shields.io/badge/tests-
|
|
11
|
+
<img src="https://img.shields.io/badge/tests-600%2B%20passed-brightgreen" alt="测试 600+ 项">
|
|
12
12
|
<img src="https://img.shields.io/badge/plugins-JS%20%7C%20Python%20%7C%20Go%20%7C%20any-blue" alt="多语言插件">
|
|
13
|
-
<img src="https://img.shields.io/badge/version-0.
|
|
13
|
+
<img src="https://img.shields.io/badge/version-0.8.7-blueviolet" alt="版本 0.8.7">
|
|
14
14
|
<img src="https://img.shields.io/badge/license-Non--Commercial-red" alt="禁止商用">
|
|
15
15
|
|
|
16
16
|
[快速开始](#3-分钟跑起来) | [写插件](docs/插件开发规范.md) | [外部插件协议](docs/插件协议.md) | [配置参考](docs/配置参考.md) | [常见问题](docs/常见问题.md) | [贡献](CONTRIBUTING.md)
|
|
@@ -106,6 +106,10 @@
|
|
|
106
106
|
- **面板页能读自己的数据**:`ctx.registerRoute()` 注册 REST 接口(`/api/plugins/<id>/r/…`),
|
|
107
107
|
鉴权/CSRF/同源校验全复用面板那套,插件拿不到裸 req/res
|
|
108
108
|
- **元数据能表达兼容性**:`frameworkVersion` 声明需要的框架版本,不满足时**在加载前拦住**并说清原因,而不是跑起来才报「xxx 不是函数」
|
|
109
|
+
- **插件能给 AI 贡献工具(0.8.6+)**:`ctx.registerTool()` 注册一个带 JSON Schema 的工具
|
|
110
|
+
(名字全局唯一,建议带插件前缀),别的插件用 `ctx.tools()` 拿清单、`ctx.callTool()` 直接调用。
|
|
111
|
+
于是「AI 能查斗罗玩家数据」这种事,**AI 插件和斗罗插件两边不用互相认识**——都只认这张工具表。
|
|
112
|
+
工具还能标 `ownerOnly` / `admin` 限权限,`timeoutMs` 限超时,插件卸载时自动注销
|
|
109
113
|
|
|
110
114
|
**统计**
|
|
111
115
|
|
|
@@ -142,16 +146,20 @@
|
|
|
142
146
|
|
|
143
147
|
## 3 分钟跑起来
|
|
144
148
|
|
|
145
|
-
### 1. 装 Node.js 22+
|
|
149
|
+
### 1. 装 Node.js 22.13+
|
|
146
150
|
|
|
147
151
|
```bash
|
|
148
|
-
node -v # 需要 >= 22
|
|
152
|
+
node -v # 需要 >= 22.13
|
|
149
153
|
```
|
|
150
154
|
|
|
151
155
|
> 用到了 Node 22 内置的全局 `WebSocket`,所以不用装 `ws`,也不需要 `npm install`。
|
|
152
156
|
|
|
153
157
|
### 2. 拿凭证
|
|
154
158
|
|
|
159
|
+
**扫码最省事(0.8.4+)**:面板 → 「新建机器人」 → 点「**扫码一键创建**」,
|
|
160
|
+
用手机 QQ 扫一下,AppID / AppSecret **自动填好**,不用去网页复制。
|
|
161
|
+
扫码用不了、或已有机器人时,同一处还有「或者手动填」:
|
|
162
|
+
|
|
155
163
|
1. <https://q.qq.com> → 创建机器人
|
|
156
164
|
2. 「开发设置」里拿到 **AppID** 和 **AppSecret**
|
|
157
165
|
3. **关键**:「功能配置」里开通 **群聊** 能力,否则连接会被拒(关闭码 4014)
|
|
@@ -358,6 +366,8 @@ export default {
|
|
|
358
366
|
| `ctx.storage.get/set/all` | 插件私有存档 |
|
|
359
367
|
| `ctx.config` / `ctx.setConfig()` | 插件配置读写 |
|
|
360
368
|
| `ctx.can` / `ctx.levelOf` / `ctx.scopeOf` | 权限判断 |
|
|
369
|
+
| `ctx.registerTool(工具)` | 注册一个 AI 可调用的工具(跨插件,0.8.6+),返回注销函数 |
|
|
370
|
+
| `ctx.tools()` / `ctx.callTool(名, 参数)` | 列出 / 调用别插件注册的工具 |
|
|
361
371
|
| `msg.reply` / `push` / `replyMarkdown` / `replyImage` / … | 各种发送方式 |
|
|
362
372
|
|
|
363
373
|
---
|
|
@@ -400,6 +410,7 @@ bot/
|
|
|
400
410
|
│ ├── gateway.js WebSocket 网关
|
|
401
411
|
│ ├── webhook.js Webhook 接收服务(多机器人共用一个端口)
|
|
402
412
|
│ ├── sign.js Webhook 回调签名(Ed25519)
|
|
413
|
+
│ ├── qq/ 扫码一键建机器人(qrlogin.js / qrsvg.js)
|
|
403
414
|
│ ├── tarball.js tar.gz 下载解包(不依赖 git)
|
|
404
415
|
│ ├── updater.js 框架自身检查更新与升级
|
|
405
416
|
│ ├── semver.js 版本比较与范围匹配(框架更新/市场/兼容校验共用)
|
|
@@ -412,8 +423,9 @@ bot/
|
|
|
412
423
|
│ ├── web/server.js 面板服务端(REST + SSE)
|
|
413
424
|
│ └── plugin/ 插件加载、配置、依赖、子进程宿主、面板页面注册
|
|
414
425
|
├── test/
|
|
415
|
-
│ ├── selftest.mjs 框架自检(
|
|
416
|
-
│
|
|
426
|
+
│ ├── selftest.mjs 框架自检(474 项)
|
|
427
|
+
│ ├── e2e.mjs 端到端测试(147 项,起真实服务打接口)
|
|
428
|
+
│ └── qrlogin.mjs 扫码建机器人(10 项)
|
|
417
429
|
├── web/ 面板前端(原生 JS/CSS,无构建)
|
|
418
430
|
└── docs/
|
|
419
431
|
```
|
|
@@ -458,9 +470,9 @@ openid 完全不同。共享 `roles.json` 等于所有权限记录全部对不
|
|
|
458
470
|
## 开发
|
|
459
471
|
|
|
460
472
|
```bash
|
|
461
|
-
npm test #
|
|
462
|
-
npm run test:e2e # 端到端
|
|
463
|
-
npm run test:all #
|
|
473
|
+
npm test # 自检 484 项(selftest 474 + qrlogin 10)
|
|
474
|
+
npm run test:e2e # 端到端 147 项(起 mock 服务打全部接口,跑在临时目录)
|
|
475
|
+
npm run test:all # 全量约 631 项:自检 + 扫码 + e2e(真机驱动要真机,没配就跳过)
|
|
464
476
|
npm run check:secrets # 扫硬编码凭据(CI 也跑这个,提交前建议过一遍)
|
|
465
477
|
node src/main.js --mock # 不连 QQ,命令行发假消息调插件
|
|
466
478
|
node src/main.js --mock --serve # 只开面板,调界面
|
package/package.json
CHANGED
package/src/web/server.js
CHANGED
|
@@ -582,6 +582,26 @@ export async function startWebServer({
|
|
|
582
582
|
return out;
|
|
583
583
|
};
|
|
584
584
|
|
|
585
|
+
/**
|
|
586
|
+
* 插件更新 / 卸载之后,把已经生效的 id 从「可更新」缓存里摘掉。
|
|
587
|
+
*
|
|
588
|
+
* 缓存 5 分钟是为了少打几次市场接口,但更新完前端会立刻再拉一次
|
|
589
|
+
* `/api/plugins/updates`——命中旧缓存就继续显示「可更新 x.y.z」和
|
|
590
|
+
* 「N 个插件有新版本」,看着像没更新成功,得等 5 分钟才消失。
|
|
591
|
+
*
|
|
592
|
+
* 插件**代码**是全局共享的,所以每个机器人的缓存都要摘,不只是当前这个。
|
|
593
|
+
*/
|
|
594
|
+
const forgetUpdates = (ids) => {
|
|
595
|
+
const drop = new Set((ids || []).filter(Boolean).map(String));
|
|
596
|
+
if (!drop.size) return;
|
|
597
|
+
for (const [botId, hit] of updateCache) {
|
|
598
|
+
const before = hit.updates || [];
|
|
599
|
+
const left = before.filter((u) => !drop.has(u.id));
|
|
600
|
+
if (left.length === before.length) continue;
|
|
601
|
+
updateCache.set(botId, { ...hit, updates: left });
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
585
605
|
// 启动后过一会儿查一次,别拖慢启动;之后每小时一次。
|
|
586
606
|
// 60 秒是有意的:启动瞬间面板、插件、市场可能都在拉数据,
|
|
587
607
|
// 再叠一个 GitHub 请求容易一起超时。fetchIndex 内部也有并发去重兜底。
|
|
@@ -1339,6 +1359,8 @@ export async function startWebServer({
|
|
|
1339
1359
|
await plugins.forget(id).catch(() => {});
|
|
1340
1360
|
const scanned = await plugins.rescan();
|
|
1341
1361
|
suspendPluginWatch(1000);
|
|
1362
|
+
// 卸载后缓存里还留着它的「可更新」,提示条计数会虚高
|
|
1363
|
+
forgetUpdates([id]);
|
|
1342
1364
|
log.info(`已卸载插件 ${id}(数据目录保留,重装可继续用)`);
|
|
1343
1365
|
return json(res, 200, { ok: true, id, ...scanned });
|
|
1344
1366
|
} catch (e) {
|
|
@@ -1774,6 +1796,8 @@ export async function startWebServer({
|
|
|
1774
1796
|
log.warn(`插件 ${id} 更新后重载失败: ${e.message}`);
|
|
1775
1797
|
});
|
|
1776
1798
|
const scanned = await plugins.rescan();
|
|
1799
|
+
// 版本已经落地,缓存里的「可更新」要跟着消失,否则面板还挂着旧标注
|
|
1800
|
+
forgetUpdates([id]);
|
|
1777
1801
|
return json(res, 200, { ok: true, from: r.updated[0].from, to: r.updated[0].to, ...scanned });
|
|
1778
1802
|
} catch (e) {
|
|
1779
1803
|
return json(res, 400, { error: e.message });
|
|
@@ -1803,6 +1827,7 @@ export async function startWebServer({
|
|
|
1803
1827
|
});
|
|
1804
1828
|
}
|
|
1805
1829
|
const scanned = await plugins.rescan();
|
|
1830
|
+
forgetUpdates(r.updated.map((u) => u.id));
|
|
1806
1831
|
return json(res, 200, { ok: true, ...r, ...scanned });
|
|
1807
1832
|
} catch (e) {
|
|
1808
1833
|
return json(res, 400, { error: e.message });
|