@zaofan/dsh-qqbot 1.9.2 → 1.10.0
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 +646 -554
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/features/ext-capabilities.d.ts +9 -0
- package/dist/features/ext-capabilities.d.ts.map +1 -1
- package/dist/features/ext-capabilities.js +2 -0
- package/dist/features/ext-capabilities.js.map +1 -1
- package/dist/features/ext-tool-interaction.d.ts.map +1 -1
- package/dist/features/ext-tool-interaction.js +37 -5
- package/dist/features/ext-tool-interaction.js.map +1 -1
- package/dist/features/extension-store.d.ts.map +1 -1
- package/dist/features/extension-store.js +8 -0
- package/dist/features/extension-store.js.map +1 -1
- package/dist/gateway/middleware-setup.d.ts.map +1 -1
- package/dist/gateway/middleware-setup.js +6 -0
- package/dist/gateway/middleware-setup.js.map +1 -1
- package/docs/extension-contract.md +187 -0
- package/examples/ext-commands//345/255/220/344/274/232/350/257/235.mjs +598 -0
- package/examples/ext-hooks/session-watch-hook.mjs +53 -119
- package/examples/ext-tools/lib/session_watch_lib.mjs +767 -350
- package/examples/ext-tools/qq_send.mjs +201 -138
- package/examples/ext-tools/session_children.mjs +99 -0
- package/examples/ext-tools/session_create.mjs +219 -201
- package/examples/ext-tools/session_watch.mjs +139 -93
- package/examples/ext-tools/sessions_list.mjs +180 -180
- package/package.json +142 -141
|
@@ -1,119 +1,53 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* session-watch-hook.mjs —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* ⚠️
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const off = env.ctx?.on?.('session/event', () => {
|
|
55
|
-
state.events += 1;
|
|
56
|
-
if (state.events % 10 === 0) {
|
|
57
|
-
env.logger.info(`[ext-hook:session-watch] 已观察到 ${state.events} 个 session/event`);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
if (typeof off === 'function') state.disposers.push(off);
|
|
61
|
-
else env.logger.warn('[ext-hook:session-watch] ctx.on 没返回 disposer —— 跳过订阅(宿主版本差异?)');
|
|
62
|
-
} catch (err) {
|
|
63
|
-
// 钩子里的失败**不要往外抛**:抛了也只是被引擎记成"失败",不如自己降级
|
|
64
|
-
env.logger.warn(`[ext-hook:session-watch] 订阅 session/event 失败(已降级): ${err?.message ?? err}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ── ② 定时器(记得 unref:不该因为一个统计定时器让宿主进程退不掉) ──
|
|
68
|
-
state.timer = setInterval(() => {
|
|
69
|
-
if (state.cancelled) return;
|
|
70
|
-
env.logger.info(
|
|
71
|
-
`[ext-hook:session-watch] 心跳:本次启动已观察 ${state.events} 个事件,` +
|
|
72
|
-
`存活 ${Math.round((Date.now() - state.startAt) / 1000)}s`,
|
|
73
|
-
);
|
|
74
|
-
}, TICK_MS);
|
|
75
|
-
state.timer.unref?.();
|
|
76
|
-
|
|
77
|
-
// ── ③ 登记清理(同时用 registerDispose 展示这条通路) ──
|
|
78
|
-
env.registerDispose(() => {
|
|
79
|
-
// 这里只是再打一行日志;真正的清理由下面 cleanup() 统一做(避免两处清理重复)
|
|
80
|
-
env.logger.info('[ext-hook:session-watch] registerDispose 回调执行');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// ── 竞态兜底:如果 run 还没跑完,dispose 就已经被调用了,那就**立刻**收尾 ──
|
|
84
|
-
if (state.cancelled) {
|
|
85
|
-
env.logger.warn('[ext-hook:session-watch] 启动与卸载撞上了,立即收尾');
|
|
86
|
-
cleanup(env);
|
|
87
|
-
return { cancelled: true };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return { starts: state.starts };
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
/** 模块级 dispose:引擎在插件卸载 / 宿主退出 / 重建网关时调用(逆序、逐个 try/catch) */
|
|
94
|
-
async dispose(env) {
|
|
95
|
-
if (state) state.cancelled = true; // 先立标志:run 若还在后面跑,它会自己收尾
|
|
96
|
-
cleanup(env);
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/** 幂等收尾:disposer 逐个吞错 + 定时器清掉 + 计数字段落盘 */
|
|
101
|
-
function cleanup(env) {
|
|
102
|
-
const s = state;
|
|
103
|
-
state = null;
|
|
104
|
-
if (!s) return;
|
|
105
|
-
s.cancelled = true;
|
|
106
|
-
for (const off of s.disposers.splice(0).reverse()) {
|
|
107
|
-
try { off(); } catch (err) {
|
|
108
|
-
env.logger.warn(`[ext-hook:session-watch] 取消订阅失败(已忽略): ${err?.message ?? err}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (s.timer) { clearInterval(s.timer); s.timer = undefined; }
|
|
112
|
-
env.store.save({
|
|
113
|
-
starts: s.starts,
|
|
114
|
-
lastEvents: s.events,
|
|
115
|
-
lastRunMs: Date.now() - s.startAt,
|
|
116
|
-
lastStopAt: new Date().toISOString(),
|
|
117
|
-
});
|
|
118
|
-
env.logger.info(`[ext-hook:session-watch] 已收尾(观察 ${s.events} 个事件,监听与定时器都已归还)`);
|
|
119
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* session-watch-hook.mjs — 启动钩子:**重启后把「回合结束回灌」接回来**
|
|
3
|
+
*
|
|
4
|
+
* 背景(2026-10-13 主人定「做成持久化的本地存档,不要只靠内存」):
|
|
5
|
+
* · **盯梢名单本身就以存档为准**:
|
|
6
|
+
* `{数据根}/.qqbot-extensions/tools/data/session-watch-watching.json`
|
|
7
|
+
* (和 `vote.json` 同一个 data 目录)—— register / 改档 / 取消都会落盘,
|
|
8
|
+
* 所以**重启后名单不会丢**;
|
|
9
|
+
* · **但监听器是内存态**:回灌靠 `root.on('session/event', …)` 这个订阅,
|
|
10
|
+
* 它随插件进程一起消失 ⇒ 重启后"名单还在、却没人听了"。
|
|
11
|
+
* 这就是之前那个「重启后引用子会话没用了」的根因。
|
|
12
|
+
* ⇒ 所以本钩子只做一件事:`rehydrate(env.ctx)` = **读回存档 + 重装监听器**。
|
|
13
|
+
*
|
|
14
|
+
* 为什么写在这里:hooks/ 是插件**启动期**跑的(每次启动 / 重建网关都会走一遍),
|
|
15
|
+
* 正是"把内存态重新接上"的唯一时机。
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ **改这个文件要重启宿主才生效**(hooks 是启动期加载,不走 /tools-reload)。
|
|
18
|
+
*/
|
|
19
|
+
export const name = 'session-watch-hook';
|
|
20
|
+
|
|
21
|
+
/** 本次 run 的现场(重建网关会有新的一份) */
|
|
22
|
+
let state = null;
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
name,
|
|
26
|
+
|
|
27
|
+
async run(env) {
|
|
28
|
+
state = { restored: 0, hooked: false };
|
|
29
|
+
try {
|
|
30
|
+
// 同一份库(tools/lib/);不带 ?v= —— 名单走 globalThis 共享,无需新实例
|
|
31
|
+
const lib = await import('../tools/lib/session_watch_lib.mjs');
|
|
32
|
+
const r = await lib.rehydrate(env.ctx);
|
|
33
|
+
state.restored = Number(r?.restored ?? 0);
|
|
34
|
+
state.hooked = !!r?.hooked;
|
|
35
|
+
env.logger.info(
|
|
36
|
+
`[ext-hook:session-watch] 启动恢复:读回 ${state.restored} 条盯梢,` +
|
|
37
|
+
`回灌监听器${state.hooked ? '已装' : '未能装上'}` +
|
|
38
|
+
`(名单存档 tools/data/session-watch-watching.json)`,
|
|
39
|
+
);
|
|
40
|
+
return { restored: state.restored, hooked: state.hooked };
|
|
41
|
+
} catch (err) {
|
|
42
|
+
// 钩子里的失败不要往外抛:抛了也只是被引擎记成"失败",不如降级 + 留日志
|
|
43
|
+
const msg = String(err?.message ?? err);
|
|
44
|
+
env.logger.warn(`[ext-hook:session-watch] 启动恢复失败(不影响其它功能): ${msg}`);
|
|
45
|
+
return { restored: 0, hooked: false, error: msg };
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/** 引擎在插件卸载 / 宿主退出 / 重建网关时调用(本钩子没有要归还的资源) */
|
|
50
|
+
async dispose() {
|
|
51
|
+
state = null;
|
|
52
|
+
},
|
|
53
|
+
};
|