@zaofan/dsh-qqbot 1.7.1 → 1.7.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-attach.js","sourceRoot":"","sources":["../../src/session/workspace-attach.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-attach.js","sourceRoot":"","sources":["../../src/session/workspace-attach.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 配置诊断日志。**每次插件启动(每个实例)调用一次即可**。
3
+ *
4
+ * @param opts.enabled - 总开关;不传 = 保持当前值(默认 false)
5
+ * @param opts.maxBytes - 单文件上限字节数(默认 2 MB)
6
+ * @param opts.home - 输出根目录(默认 `DSH_HOME` 或 `~/.dsh`)
7
+ */
8
+ export declare function configureDiag(opts: {
9
+ enabled?: boolean;
10
+ maxBytes?: number;
11
+ home?: string;
12
+ }): void;
13
+ /** 诊断日志现在是开着的吗? */
14
+ export declare function diagEnabled(): boolean;
15
+ /** 某个诊断文件的绝对路径(**不看开关**,面板展示用) */
16
+ export declare function diagPathFor(name: string): string;
17
+ /**
18
+ * 写一行诊断日志(**全插件唯一入口**)。
19
+ *
20
+ * - 开关关着 ⇒ 立刻返回,**零 I/O**。
21
+ * - 写失败(磁盘满/被占用)⇒ 静默吞掉,**绝不抛**。
22
+ *
23
+ * @param name - 日志名(不含 `.log`),如 `qqbot-ext-diag` / `botplay-diag`
24
+ * @param line - 内容(会自动补时间戳与换行)
25
+ */
26
+ export declare function diagWrite(name: string, line: string): void;
27
+ /** 诊断状态(面板/端点展示:开关、上限、各文件当前大小) */
28
+ export declare function diagStatus(): {
29
+ enabled: boolean;
30
+ maxBytes: number;
31
+ root: string;
32
+ files: Array<{
33
+ name: string;
34
+ path: string;
35
+ bytes: number;
36
+ }>;
37
+ };
38
+ /**
39
+ * 给「排查完就删」的调用方一条清理捷径(只删我们自己的诊断日志)。
40
+ * @param names - 要删的日志名(不含 `.log`);不传 = 删 sizes 里已知的全部
41
+ */
42
+ export declare function diagClean(names?: string[]): number;
43
+ //# sourceMappingURL=diag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diag.d.ts","sourceRoot":"","sources":["../../src/shared/diag.ts"],"names":[],"mappings":"AA0DA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUjG;AAED,mBAAmB;AACnB,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,kCAAkC;AAClC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAW1D;AAED,kCAAkC;AAClC,wBAAgB,UAAU,IAAI;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D,CAOA;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAYlD"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * diag.ts — 诊断日志的**唯一底座**(2026-10-06,还债)
3
+ *
4
+ * ── 为什么要收口 ──────────────────────────────────────────────────
5
+ * 以前每个功能都自己写一份诊断日志,于是:
6
+ * · `contextless-trace.log` **没有任何上限**,一路涨到 **25 MB**;
7
+ * · 同一条 trace 被写到**三个地方**(`~/.dsh/`、系统 tmp、数据根);
8
+ * · 还有**两处代码写同一个文件**(`contextless-store.ts` 与 `channel-tools.ts`);
9
+ * · 有些每行还往终端 `console.error` —— 控制台刷屏。
10
+ *
11
+ * ⇒ 本模块把「**开关 / 上限 / 位置 / 轮转**」四件事收成一处,所有诊断日志都必须走它:
12
+ *
13
+ * ```ts
14
+ * diagWrite('qqbot-ext-diag', line); // 默认不写;开了才写;超 2MB 自动轮转
15
+ * ```
16
+ *
17
+ * ── 保证 ────────────────────────────────────────────────────────
18
+ * 1. **默认关**(用户机器上零开销、零垃圾);由配置项 `diagLog` 或环境变量
19
+ * `DSH_QQBOT_DIAG=1` 打开。
20
+ * 2. **单文件上限**(默认 2 MB)+ **轮转 1 份**(`xxx.log` → `xxx.log.1`),**只进不出成为历史**。
21
+ * 3. **只写一个位置**:`{DSH_HOME|~/.dsh}/<name>.log`,不再往 tmp / 数据根各撒一份。
22
+ * 4. **绝不影响主链**:任何写失败都静默吞掉(诊断日志不该拖垮业务)。
23
+ * 5. **尺寸用内存计数**,不每次 `statSync`(只有首次取基线)。
24
+ */
25
+ import { appendFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from 'node:fs';
26
+ import { homedir } from 'node:os';
27
+ import { dirname, join } from 'node:path';
28
+ const DEFAULT_MAX_BYTES = 2 * 1024 * 1024; // 2 MB
29
+ const state = {
30
+ enabled: false,
31
+ maxBytes: DEFAULT_MAX_BYTES,
32
+ root: resolveRoot(),
33
+ };
34
+ /** 各文件当前已知大小(避免每次写都 statSync) */
35
+ const sizes = new Map();
36
+ function resolveRoot() {
37
+ try {
38
+ const env = (process.env.DSH_HOME ?? '').trim();
39
+ if (env)
40
+ return env;
41
+ return join(homedir(), '.dsh');
42
+ }
43
+ catch {
44
+ return '.';
45
+ }
46
+ }
47
+ /**
48
+ * 配置诊断日志。**每次插件启动(每个实例)调用一次即可**。
49
+ *
50
+ * @param opts.enabled - 总开关;不传 = 保持当前值(默认 false)
51
+ * @param opts.maxBytes - 单文件上限字节数(默认 2 MB)
52
+ * @param opts.home - 输出根目录(默认 `DSH_HOME` 或 `~/.dsh`)
53
+ */
54
+ export function configureDiag(opts) {
55
+ try {
56
+ if (opts.home)
57
+ state.root = opts.home;
58
+ else if (!state.root)
59
+ state.root = resolveRoot();
60
+ if (typeof opts.maxBytes === 'number' && opts.maxBytes > 0)
61
+ state.maxBytes = Math.floor(opts.maxBytes);
62
+ if (typeof opts.enabled === 'boolean')
63
+ state.enabled = opts.enabled;
64
+ // 环境变量优先(排查时不想改配置就 `set DSH_QQBOT_DIAG=1` 重启)
65
+ const env = (process.env.DSH_QQBOT_DIAG ?? '').trim();
66
+ if (env === '1' || env.toLowerCase() === 'true')
67
+ state.enabled = true;
68
+ }
69
+ catch { /* 配置失败保持默认(关) */ }
70
+ }
71
+ /** 诊断日志现在是开着的吗? */
72
+ export function diagEnabled() {
73
+ return state.enabled === true;
74
+ }
75
+ /** 某个诊断文件的绝对路径(**不看开关**,面板展示用) */
76
+ export function diagPathFor(name) {
77
+ return join(state.root, `${safeName(name)}.log`);
78
+ }
79
+ /** 文件名消毒:只允许字母/数字/`._-`,防路径逃逸 */
80
+ function safeName(name) {
81
+ const n = String(name ?? '').trim();
82
+ if (!n || !/^[A-Za-z0-9._-]+$/.test(n))
83
+ return 'qqbot-diag';
84
+ return n;
85
+ }
86
+ /** 首次写时取一次基线大小(之后靠内存计数) */
87
+ function knownSize(file) {
88
+ const hit = sizes.get(file);
89
+ if (hit !== undefined)
90
+ return hit;
91
+ let n = 0;
92
+ try {
93
+ n = existsSync(file) ? statSync(file).size : 0;
94
+ }
95
+ catch {
96
+ n = 0;
97
+ }
98
+ sizes.set(file, n);
99
+ return n;
100
+ }
101
+ /** 超上限就轮转:`xxx.log` → `xxx.log.1`(只留 1 份旧档) */
102
+ function rotateIfNeeded(file, incoming) {
103
+ try {
104
+ if (knownSize(file) + incoming <= state.maxBytes)
105
+ return;
106
+ const bak = `${file}.1`;
107
+ try {
108
+ rmSync(bak, { force: true });
109
+ }
110
+ catch { /* ignore */ }
111
+ try {
112
+ renameSync(file, bak);
113
+ }
114
+ catch { /* 文件可能不存在/被占,忽略 */ }
115
+ sizes.set(file, 0);
116
+ }
117
+ catch { /* ignore */ }
118
+ }
119
+ /**
120
+ * 写一行诊断日志(**全插件唯一入口**)。
121
+ *
122
+ * - 开关关着 ⇒ 立刻返回,**零 I/O**。
123
+ * - 写失败(磁盘满/被占用)⇒ 静默吞掉,**绝不抛**。
124
+ *
125
+ * @param name - 日志名(不含 `.log`),如 `qqbot-ext-diag` / `botplay-diag`
126
+ * @param line - 内容(会自动补时间戳与换行)
127
+ */
128
+ export function diagWrite(name, line) {
129
+ if (!state.enabled)
130
+ return;
131
+ try {
132
+ const file = diagPathFor(name);
133
+ const row = `${new Date().toISOString()} ${String(line ?? '')}\n`;
134
+ const bytes = Buffer.byteLength(row, 'utf8');
135
+ try {
136
+ mkdirSync(dirname(file), { recursive: true });
137
+ }
138
+ catch { /* ignore */ }
139
+ rotateIfNeeded(file, bytes);
140
+ appendFileSync(file, row, 'utf8');
141
+ sizes.set(file, knownSize(file) + bytes);
142
+ }
143
+ catch { /* 诊断日志失败绝不影响主链 */ }
144
+ }
145
+ /** 诊断状态(面板/端点展示:开关、上限、各文件当前大小) */
146
+ export function diagStatus() {
147
+ const files = [...sizes.entries()].map(([path, bytes]) => ({
148
+ name: path.split(/[\\/]/).pop() ?? path,
149
+ path,
150
+ bytes,
151
+ }));
152
+ return { enabled: state.enabled, maxBytes: state.maxBytes, root: state.root, files };
153
+ }
154
+ /**
155
+ * 给「排查完就删」的调用方一条清理捷径(只删我们自己的诊断日志)。
156
+ * @param names - 要删的日志名(不含 `.log`);不传 = 删 sizes 里已知的全部
157
+ */
158
+ export function diagClean(names) {
159
+ let removed = 0;
160
+ const targets = names && names.length ? names.map((n) => diagPathFor(n)) : [...sizes.keys()];
161
+ for (const file of targets) {
162
+ try {
163
+ for (const p of [file, `${file}.1`]) {
164
+ if (existsSync(p)) {
165
+ rmSync(p, { force: true });
166
+ removed += 1;
167
+ }
168
+ }
169
+ sizes.set(file, 0);
170
+ }
171
+ catch { /* ignore */ }
172
+ }
173
+ return removed;
174
+ }
175
+ //# sourceMappingURL=diag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diag.js","sourceRoot":"","sources":["../../src/shared/diag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAW1C,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAG,OAAO;AAEpD,MAAM,KAAK,GAAc;IACvB,OAAO,EAAE,KAAK;IACd,QAAQ,EAAE,iBAAiB;IAC3B,IAAI,EAAE,WAAW,EAAE;CACpB,CAAC;AAEF,iCAAiC;AACjC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExC,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC;QACpB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAA6D;IACzF,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;aACjC,IAAI,CAAC,KAAK,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,WAAW,EAAE,CAAC;QACjD,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC;YAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvG,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,SAAS;YAAE,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QACpE,8CAA8C;QAC9C,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,MAAM;YAAE,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAC/B,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,WAAW;IACzB,OAAO,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC;AAChC,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,iCAAiC;AACjC,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,YAAY,CAAC;IAC5D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,2BAA2B;AAC3B,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,CAAC;QAAC,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IACxE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACnB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,+CAA+C;AAC/C,SAAS,cAAc,CAAC,IAAY,EAAE,QAAgB;IACpD,IAAI,CAAC;QACH,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,KAAK,CAAC,QAAQ;YAAE,OAAO;QACzD,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC;QACxB,IAAI,CAAC;YAAC,MAAM,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC5D,IAAI,CAAC;YAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;QAC5D,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO;IAC3B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC;YAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC7E,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5B,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAClC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;AAChC,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,UAAU;IAMxB,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI;QACvC,IAAI;QACJ,KAAK;KACN,CAAC,CAAC,CAAC;IACJ,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AACvF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgB;IACxC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,OAAO,GAAG,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7F,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;gBACpC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;oBAAC,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;oBAAC,OAAO,IAAI,CAAC,CAAC;gBAAC,CAAC;YAClE,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -86,7 +86,8 @@ appSecret: ${hasSecret ? '已配置(位置:设置 → QQ 机器人)' : '
86
86
  ── 要求 ──
87
87
  请按上面的契约写一个可直接用的自定义事件模块(写进 ${extDir || '{{EXT_DIR}}'}),并在 botplay-events.json 里登记对应事件(id/name/file)。
88
88
  写完自己核对一遍语法;可以用 /botplay 事件id 在群里发卡试一下, 或让主人去面板点「🔄 重载模块」后再发卡。
89
- content-type
90
- content-length
91
- cache-controlno-store
92
- accept-rangesbytes
89
+ 另: 插件包里自带一个**可直接跑的示例** —— examples/botplay/checkin-stats.mjs(同目录 README 有用法和说明)。
90
+ 想照猫画虎就抄它: 它演示了三个最容易踩的坑 ——
91
+ ① 按钮级权限只能自己在 onClick 里判(卡片级 perm 做不到按按钮区分)
92
+ ② 别动不动重发整张卡(刷屏 + 撞发卡限频); 只有真要刷新按钮状态才 card().dirty = true
93
+ ③ 状态要按 ctx.cardId 分轮存, 否则多张卡片会串上一轮的名单
@@ -0,0 +1,105 @@
1
+ # 宿主服务清单(写扩展时 `env.ctx.get('…')` 能拿到什么)
2
+
3
+ > 这份清单是**从 dsh 源码里 harvest 的真实服务名**(`ctx.get("…")` 的所有出现),
4
+ > 不是猜的。供 `.qqbot-extensions/` 里的**你自己写的扩展**参考。
5
+ >
6
+ > 拿到 `ctx` 的入口:
7
+ > - 扩展工具 `tools/*.mjs` → `env.ctx`
8
+ > - 自定义命令 `commands/*.mjs` → `env.ctx`(v1.7.1+)
9
+ > - botplay 卡片 `botplay/*.mjs` → `ctx.kernel`(v1.7.1+;因为卡片那个对象自己就叫 `ctx`)
10
+ >
11
+ > ⚠️ 三条代价(详见《用户手册》):① 形状跟随 dsh 版本,升级可能失效 —— **成品能力才是主路,
12
+ > `ctx` 是逃生舱**;② `register`/`on` 返回的 disposer **要自己收尾**,否则热重载会累积;
13
+ > ③ 全权限,别把 `ctx` 转手给不信任的代码。
14
+
15
+ ## 怎么用
16
+
17
+ ```js
18
+ // 探测某个服务在不在(拿不到返回 undefined,不会抛)
19
+ const sessions = env.ctx?.get?.('sessions');
20
+ if (sessions) { /* 用它 */ }
21
+ ```
22
+
23
+ ```js
24
+ // 一眼看清"本机此刻有哪些服务可用"
25
+ const ctx = env.ctx ?? env.kernel;
26
+ for (const n of ['sessions','tools','webServer','workspaceRegistry','llm','skills','jobs']) {
27
+ console.log(n, ctx?.get?.(n) ? '✅' : '❌');
28
+ }
29
+ ```
30
+
31
+ ## 最常用的(写 QQ 扩展时优先看这些)
32
+
33
+ | 服务名 | 大致用途 |
34
+ |---|---|
35
+ | `sessions` | **会话服务**:读/建会话、拿会话对象(`session.append` 等) |
36
+ | `tools` | **工具注册表**:`ctx.get('tools').register(def)` 给 AI 加一个工具 |
37
+ | `webServer` | **注册 HTTP 路由**:`register({ kind:'exact', path, handler })`(面板/网页能点的那种) |
38
+ | `agents` | agent 注册表 |
39
+ | `sessionPersistence` | 会话持久化(列会话、读 header,**做批量体检/统计很有用**) |
40
+ | `sessionQuery` / `sessionTitle` | 会话查询 / 标题 |
41
+ | `llm` | LLM 调用面 |
42
+ | `settings` | 设置服务 |
43
+ | `approval` | 审批流(配合 QQ 远程审批) |
44
+ | `userQuestions` | AI 向用户提问(配合 QQ 答题卡) |
45
+ | `skills` | 技能(skills)注册/查询 |
46
+ | `subagents` | 子代理 |
47
+ | `jobs` | 后台任务 |
48
+ | `workspaces` / `workspaceFiles` | 工作区(侧边栏分组就是它) |
49
+ | `fs` / `shell` / `subprocess` | 文件/终端/子进程(也可以直接用 `node:fs` 等) |
50
+ | `credentials` | 凭据 |
51
+ | `sandbox` / `sandboxPolicy` / `permissionPresets` | 沙箱与权限策略 |
52
+ | `slots` | **UI 槽位**(client 侧用;插件往面板塞自定义区块) |
53
+ | `theme` / `locale` | 主题 / 语言 |
54
+ | `tokenMeter` | token 计量 |
55
+ | `loader` / `modules` / `pluginManager` / `pluginPackages` | 插件体系自身 |
56
+
57
+ ## 完整清单(59 个,harvest 自 dsh 源码)
58
+
59
+ ```
60
+ agentDefaultModel agentPresets agents appExit appReady approval attachments
61
+ chatFileMentions cmdlineArgs commandUi configEditor connection conversation
62
+ credentials deepseekAccount deepseekLlmApiExtensions feedbackUi fs inputTriggers
63
+ jobs launchEnvironment llm loader locale modules permissionPresets pluginManager
64
+ pluginPackages productAnalytics profileContext ptcRuntime sandbox sandboxPolicy
65
+ sessionPersistence sessionProjectionCache sessionProjections sessionQuery sessions
66
+ sessionTitle settings shell sidebarRightTabs skills slots spillStore
67
+ subagentModelSelection subagents subprocess systemPrompt theme tokenMeter
68
+ toolResultPruner tools typertGateway uiConversation uiWorkspace userQuestionPanels
69
+ userQuestions webServer workspaceFiles workspaces
70
+ ```
71
+
72
+ ## 几个上手例子
73
+
74
+ ```js
75
+ // ① 给 AI 新增一个工具(注册后 AI 就能调)
76
+ const dispose = env.ctx.get('tools')?.register({
77
+ name: 'my_tool',
78
+ description: '我自己的工具',
79
+ schema: { type: 'object', additionalProperties: false, properties: { text: { type: 'string' } } },
80
+ async execute(args) { return { ok: true, msg: '收到 ' + args.text }; },
81
+ });
82
+ // ⚠️ dispose 要自己收尾(插件卸载/热重载时调用)
83
+
84
+ // ② 开一个自己的 HTTP 路由(配成面板能点的)
85
+ env.ctx.get('webServer')?.register({
86
+ kind: 'exact', path: '/api/my-ext/ping',
87
+ handler: (req, res) => { res.writeHead(200, {'content-type':'application/json'}); res.end('{"ok":true}'); },
88
+ });
89
+
90
+ // ③ 列最近会话(做统计/体检)
91
+ const list = await env.ctx.get('sessionPersistence')?.list?.();
92
+ ```
93
+
94
+ ## 和「成品能力」怎么选
95
+
96
+ | 你要做的事 | 用哪个 |
97
+ |---|---|
98
+ | 发消息 / 发图 / 发卡片 | **`env.markdown` / `env.image` / `env.markdownCard`**(成品,推荐) |
99
+ | 调官方 QQ 接口 | **`env.api(path)`**(成品,自带 token) |
100
+ | 存一点状态 | **`env.store.load/save`**(成品,升级不丢) |
101
+ | 进 AI 上下文 | **`env.appendSilent` / `env.appendWake`**(成品,带安全守卫) |
102
+ | 重启宿主 | **`env.restart()`**(成品,复用内置 `/bot-restart`) |
103
+ | 上面都没有的事儿 | **`env.ctx`(遥控器)** —— 逃生舱 |
104
+
105
+ > 成品能力**不会因为 dsh 升级而失效**;`ctx` 会。所以能用成品就别动遥控器。
@@ -0,0 +1,56 @@
1
+ # botplay 自定义事件 · 示例模块
2
+
3
+ > 这些是**可以直接跑的示例**,用来当骨架改。它们只依赖 botplay 事件契约里的 `ctx`,
4
+ > 不 import 插件内部任何东西 —— 所以复制到哪儿都能用。
5
+
6
+ ## 用法(三步)
7
+
8
+ ```powershell
9
+ # ① 把示例复制到你的数据根(不确定数据根在哪?面板账号列表里有"数据目录")
10
+ # 典型路径:{数据根}/.qqbot-extensions/botplay/
11
+ Copy-Item .\checkin-stats.mjs "D:\你的数据根\.qqbot-extensions\botplay\"
12
+ ```
13
+
14
+ ```
15
+ # ② 登记一个 botplay 事件(面板「🎮 互动事件」→ 新建 / 或直接改 botplay-events.json)
16
+ { "id": "checkin-stats", "name": "签到人数统计", "perm": { "type": "all" }, "file": "checkin-stats.mjs" }
17
+ ```
18
+
19
+ ```
20
+ # ③ 在群里发卡
21
+ /botplay checkin-stats
22
+ ```
23
+
24
+ > 💡 模块目录在**数据根**下(不是插件包内)⇒ **插件升级/重装永不覆盖它**。
25
+ > 模块自己的状态写在同目录 `data/<模块文件名>.json`,同样不丢。
26
+
27
+ ## 示例清单
28
+
29
+ | 文件 | 教什么 |
30
+ |---|---|
31
+ | **`checkin-stats.mjs`** | 两个按钮(谁都能点 / 只有主人能点)、每人限一次、按 `ctx.cardId` 分轮、结算时 `ctx.emit` 公布 + **`ctx.appendWake` 唤醒 AI**、卡片超时自动结算 |
32
+
33
+ ## `checkin-stats.mjs` 值得学的四点
34
+
35
+ 1. **★ 按钮级权限只能自己判**
36
+ botplay 的 `perm` 是**整张卡片**一个,做不到"按钮 1 谁都能点、按钮 2 只有主人能点"。
37
+ ⇒ 示例的做法:事件 `perm` 设 `all`,细粒度权限**在 `onClick` 里用 `ctx.user.isOwner` /
38
+ `ctx.owners.includes(...)` 自己判**。这是最容易踩的坑。
39
+
40
+ 2. **别动不动重发整张卡**
41
+ 点「签到」只**记录 + 回一句**,不重发卡片(重发既刷屏、又可能撞 QQ 发卡限频);
42
+ 名单在「结束」时统一公布就够。只有真要刷新按钮状态时才 `ctx.card().dirty = true`。
43
+
44
+ 3. **状态按 `ctx.cardId` 分轮**
45
+ 同一模块被多张卡片实例共享(模块级变量只有一份)⇒ 用 `cardId` 做 key 存状态,
46
+ 换卡即换轮,不会串上一轮的名单。
47
+
48
+ 4. **`ctx.appendWake` 要用在刀刃上**
49
+ 它会把一段文本写进会话**并唤醒一次 AI 回合** —— 所以只在**真正出结果的那一刻**调
50
+ (结算 / 超时),高频点击里绝不要调(烧 token)。失败要包 try/catch,别拖累卡片流程。
51
+
52
+ ## 完整契约
53
+
54
+ - 模块钩子与 `ctx` 全量能力:见插件包内 `dist/features/botplay-ext.ts` 头部注释,
55
+ 或在线文档《用户手册》的「🤖 botplay 自定义事件」一章
56
+ - 宿主服务(想用 `ctx.kernel` / `env.ctx` 玩更花的):见 `docs/host-services.md`