@zaofan/dsh-qqbot 1.6.9 → 1.7.1
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/dist/channel-tools.d.ts.map +1 -1
- package/dist/channel-tools.js +37 -0
- package/dist/channel-tools.js.map +1 -1
- package/dist/features/botplay-ext.d.ts +48 -0
- package/dist/features/botplay-ext.d.ts.map +1 -1
- package/dist/features/botplay-ext.js +7 -0
- package/dist/features/botplay-ext.js.map +1 -1
- package/dist/features/botplay.d.ts +3 -0
- package/dist/features/botplay.d.ts.map +1 -1
- package/dist/features/botplay.js +40 -0
- package/dist/features/botplay.js.map +1 -1
- package/dist/features/ext-capabilities.d.ts +151 -0
- package/dist/features/ext-capabilities.d.ts.map +1 -0
- package/dist/features/ext-capabilities.js +308 -0
- package/dist/features/ext-capabilities.js.map +1 -0
- package/dist/gateway/bootstrap.d.ts.map +1 -1
- package/dist/gateway/bootstrap.js +3 -1
- package/dist/gateway/bootstrap.js.map +1 -1
- package/dist/gateway/middleware-setup.d.ts +3 -1
- package/dist/gateway/middleware-setup.d.ts.map +1 -1
- package/dist/gateway/middleware-setup.js +50 -2
- package/dist/gateway/middleware-setup.js.map +1 -1
- package/docs/USER-GUIDE.md +30 -1
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { Logger } from '../types.js';
|
|
2
|
+
/** 媒体类型(与 sender.sendMedia 的 kind 对齐) */
|
|
3
|
+
export type ExtMediaKind = 'image' | 'voice' | 'video' | 'file';
|
|
4
|
+
/** 媒体源:URL 或本机绝对路径(二选一) */
|
|
5
|
+
export interface ExtMediaSource {
|
|
6
|
+
url?: string;
|
|
7
|
+
localPath?: string;
|
|
8
|
+
}
|
|
9
|
+
/** 宿主 sender 里我们用到的子集(全部 duck-typing + 缺失即降级) */
|
|
10
|
+
export interface ExtSenderLike {
|
|
11
|
+
sendText?: (target: unknown, text: string) => Promise<unknown>;
|
|
12
|
+
sendMarkdown?: (target: unknown, content: string) => Promise<unknown>;
|
|
13
|
+
sendMarkdownWithKeyboard?: (target: unknown, content: string, keyboard?: unknown) => Promise<unknown>;
|
|
14
|
+
sendMedia?: (target: unknown, kind: string, source: ExtMediaSource) => Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** 官方 API 调用结果(与 GroupAdminClient.apiCall 同形) */
|
|
17
|
+
export type ExtApiResult = {
|
|
18
|
+
ok: true;
|
|
19
|
+
data: unknown;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
err: {
|
|
23
|
+
code: string;
|
|
24
|
+
human: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/** 构造能力包的依赖(能给的都给,给不了的自动降级为「调用返回 false」) */
|
|
28
|
+
export interface ExtCapsDeps {
|
|
29
|
+
/** 数据根(store 落盘用)。空 = store 不可用 */
|
|
30
|
+
dataRoot?: string;
|
|
31
|
+
logger?: Logger;
|
|
32
|
+
/** bot 凭证(api / appId / appSecret 用) */
|
|
33
|
+
appId?: string;
|
|
34
|
+
appSecret?: string;
|
|
35
|
+
/** 主人 openid 列表(user.isOwner / owners) */
|
|
36
|
+
owners?: string[];
|
|
37
|
+
/** 当前会话的发送器与目标(没有 = 发送类能力不可用) */
|
|
38
|
+
sender?: ExtSenderLike;
|
|
39
|
+
replyTarget?: unknown;
|
|
40
|
+
/** 当前会话的 agent(appendSilent / appendWake 用) */
|
|
41
|
+
agent?: {
|
|
42
|
+
session?: unknown;
|
|
43
|
+
followup?: (msg: unknown) => unknown;
|
|
44
|
+
} | undefined;
|
|
45
|
+
/** 当前会话标识 + 称呼(peer / user 展示用) */
|
|
46
|
+
scope?: string;
|
|
47
|
+
peerId?: string;
|
|
48
|
+
/** 触发者(命令的发送者 / 工具的调用会话所属人) */
|
|
49
|
+
actorOpenid?: string;
|
|
50
|
+
actorName?: string;
|
|
51
|
+
/** 带 token 的官方 API 调用(注入了就优先用它,否则用本模块自带的 token 管理) */
|
|
52
|
+
apiCall?: (method: 'GET' | 'POST' | 'PATCH' | 'DELETE', path: string, body?: unknown) => Promise<ExtApiResult>;
|
|
53
|
+
/** 扩展名(store 文件名 / 日志前缀) */
|
|
54
|
+
selfName?: string;
|
|
55
|
+
/** 扩展种类:tools / commands(决定 store 落在哪个子目录) */
|
|
56
|
+
kind?: string;
|
|
57
|
+
/**
|
|
58
|
+
* ★ 插件 ctx("遥控器")—— 见 {@link ExtCapabilities.kernel}。
|
|
59
|
+
* 由调用方把插件的 cordis Context 原样传进来;不传 = 没有这个能力。
|
|
60
|
+
*/
|
|
61
|
+
kernel?: unknown;
|
|
62
|
+
/**
|
|
63
|
+
* 自重启实现(仅供测试注入;生产走内置 `selfRestart`)。
|
|
64
|
+
* ⚠️ 真跑它会 spawn 一个助手去 kill 宿主进程 —— 测试里**必须**用这个接缝替换。
|
|
65
|
+
*/
|
|
66
|
+
restartImpl?: (delayMs?: number) => boolean;
|
|
67
|
+
}
|
|
68
|
+
/** 能力包对外形状(写扩展时看到的就是这个) */
|
|
69
|
+
export interface ExtCapabilities {
|
|
70
|
+
appId: string;
|
|
71
|
+
appSecret: string;
|
|
72
|
+
at(id: string): string;
|
|
73
|
+
text(s: string): Promise<boolean>;
|
|
74
|
+
markdown(s: string): Promise<boolean>;
|
|
75
|
+
markdownCard(s: string, keyboard?: unknown): Promise<boolean>;
|
|
76
|
+
media(kind: ExtMediaKind, source: ExtMediaSource): Promise<boolean>;
|
|
77
|
+
image(source: ExtMediaSource | string): Promise<boolean>;
|
|
78
|
+
voice(source: ExtMediaSource | string): Promise<boolean>;
|
|
79
|
+
video(source: ExtMediaSource | string): Promise<boolean>;
|
|
80
|
+
file(source: ExtMediaSource | string): Promise<boolean>;
|
|
81
|
+
api(path: string, opts?: {
|
|
82
|
+
method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
|
83
|
+
body?: unknown;
|
|
84
|
+
}): Promise<ExtApiResult>;
|
|
85
|
+
store: {
|
|
86
|
+
load<T = unknown>(): T | undefined;
|
|
87
|
+
save(value: unknown): boolean;
|
|
88
|
+
path: string;
|
|
89
|
+
};
|
|
90
|
+
appendSilent(text: string): Promise<boolean>;
|
|
91
|
+
appendWake(text: string): Promise<boolean>;
|
|
92
|
+
canAppendSilent(): boolean;
|
|
93
|
+
user: {
|
|
94
|
+
openid: string;
|
|
95
|
+
name: string;
|
|
96
|
+
isOwner: boolean;
|
|
97
|
+
};
|
|
98
|
+
owners: string[];
|
|
99
|
+
peer: {
|
|
100
|
+
scope: string;
|
|
101
|
+
peerId: string;
|
|
102
|
+
};
|
|
103
|
+
log(...args: unknown[]): void;
|
|
104
|
+
/**
|
|
105
|
+
* ★ **内核句柄("遥控器")**= 本插件自己的 cordis Context(`ctx`)。
|
|
106
|
+
*
|
|
107
|
+
* 拿到它就能做**受控能力包做不到的事**:
|
|
108
|
+
* ```js
|
|
109
|
+
* const ctx = env.kernel;
|
|
110
|
+
* ctx.get('sessions') // 读/建会话
|
|
111
|
+
* ctx.get('tools') // 工具注册表
|
|
112
|
+
* ctx.compaction.compactNow(a, s, id) // 调宿主的上下文压缩
|
|
113
|
+
* ctx.on('session/event', fn) // 订阅宿主事件
|
|
114
|
+
* ctx.webServer.register({ path, handler }) // 开一个 HTTP 路由
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* ⚠️ **三条代价(用之前先读)**:
|
|
118
|
+
* 1. **跟随宿主版本**:`ctx` 的形状由 dsh 决定,宿主升级可能让这段代码失效 ——
|
|
119
|
+
* 所以「成品能力」才是主路,`kernel` 是**逃生舱**。
|
|
120
|
+
* 2. **注册要自己收尾**:`ctx.tools.register()` / `ctx.on()` / `ctx.webServer.register()`
|
|
121
|
+
* 都返回 disposer;不 dispose 的话,热重载会**不断累积**(本插件自己也踩过这个坑,
|
|
122
|
+
* 见维护手册铁律 8:disposer 要存到跨模块实例可见的地方)。
|
|
123
|
+
* 3. **全权限**:它等价于"把遥控器交出去"。这是**你自己机器上、你自己写的代码**,
|
|
124
|
+
* 所以本来就是你的权利 —— 但别把它转手给不信任的代码。
|
|
125
|
+
*/
|
|
126
|
+
kernel: unknown;
|
|
127
|
+
/**
|
|
128
|
+
* 触发宿主自重启(等价于内置的 `/bot-restart`)。
|
|
129
|
+
*
|
|
130
|
+
* 实现直接复用插件自己的 `selfRestart()` —— 它已经填平了所有坑
|
|
131
|
+
* (动态识别启动命令、**强制补 `--no-open`**、助手写系统 tmpdir 避开中文路径、
|
|
132
|
+
* detached+unref 保证宿主被杀后照样能拉起)。**别自己 spawn,很容易写错。**
|
|
133
|
+
*
|
|
134
|
+
* @param opts.requireOwner - 默认 `true`:只有主人(`env.user.isOwner`)触发才生效,
|
|
135
|
+
* 防止 AI 被群友一句话钓去重启机器人。**你自己的场景要谁都能重启,就传 `false`。**
|
|
136
|
+
* @param opts.delayMs - 延迟多久开始(默认 1600ms,给回执留时间)
|
|
137
|
+
* @returns 是否已触发(不代表重启成功,只代表助手已脱手启动)
|
|
138
|
+
*/
|
|
139
|
+
restart(opts?: {
|
|
140
|
+
requireOwner?: boolean;
|
|
141
|
+
delayMs?: number;
|
|
142
|
+
}): boolean;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 造一份扩展能力包。**永远不会抛**:拿不到的依赖 → 对应能力返回 false / 空值。
|
|
146
|
+
*
|
|
147
|
+
* @param deps - 见 {@link ExtCapsDeps}
|
|
148
|
+
* @returns 能力包(见 {@link ExtCapabilities})
|
|
149
|
+
*/
|
|
150
|
+
export declare function makeExtCaps(deps: ExtCapsDeps): ExtCapabilities;
|
|
151
|
+
//# sourceMappingURL=ext-capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ext-capabilities.d.ts","sourceRoot":"","sources":["../../src/features/ext-capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ext-capabilities.ts — 给「用户扩展」用的统一能力包(2026-10-06)
|
|
3
|
+
*
|
|
4
|
+
* ── 为什么有这个东西 ───────────────────────────────────────────────
|
|
5
|
+
* 插件有三套用户扩展:`commands/`(自定义斜杠命令)、`tools/`(自定义 QQ 工具)、
|
|
6
|
+
* `botplay/`(卡片事件模块)。三套的能力面原本**严重不均**:
|
|
7
|
+
*
|
|
8
|
+
* · botplay 卡片:`card()` / `emit` / `markdown` / `image` / `at` / `store` /
|
|
9
|
+
* `api()` / **`appId`+`appSecret`** / `appendWake` —— 精装版
|
|
10
|
+
* · 扩展工具 :只有裸 `{cwd, manager, sender, replyTarget, exec, ctx, logger}`
|
|
11
|
+
* —— 想发张卡得自己 `sender.sendMarkdown(target, text)`,
|
|
12
|
+
* 以至于主人不得不专门写一个 `send_md.mjs` 来干这一件事
|
|
13
|
+
* · 自定义命令:**最窄**,只有一个消息 ctx
|
|
14
|
+
*
|
|
15
|
+
* 本模块把「大家都该有」的那部分做成**一套**,三处注入同一个面:
|
|
16
|
+
* 让**第三方作者**写扩展时不用再自己接生。
|
|
17
|
+
*
|
|
18
|
+
* ── 拿到什么(写扩展时直接 `env.xxx` / `caps.xxx`)───────────────────
|
|
19
|
+
* 发送: text(s) / markdown(s) / markdownCard(s) / image(src) / voice(src) /
|
|
20
|
+
* video(src) / file(src) / media(kind, src) / at(openid)
|
|
21
|
+
* 官方 API: appId / appSecret / api(path, {method, body})
|
|
22
|
+
* —— ★ 自带 token 与 2 小时缓存,**不依赖 groupAdmin 开关**
|
|
23
|
+
* 持久化: store.load() / store.save(obj) (落数据根,插件升级不丢)
|
|
24
|
+
* 上下文: appendSilent(text) / appendWake(text) ← 见下面「安全」
|
|
25
|
+
* 身份: user.{openid,name,isOwner} / owners / peer.{scope,peerId}
|
|
26
|
+
* 杂项: log(...)
|
|
27
|
+
*
|
|
28
|
+
* ── ★ 安全:appendSilent 必须过表面守卫 ─────────────────────────────
|
|
29
|
+
* dsh 的 v4 会话格式要求 `system/message`(人设)是 surface 的**第一个节点**
|
|
30
|
+
* (见 `session/surface-guard.ts` 的事故记录)。全新会话里人设还没落,
|
|
31
|
+
* 此时静默 append 会**把整份会话日志写废**。
|
|
32
|
+
* ⇒ 本模块的 `appendSilent` **一律先过 `canSilentlyAppend()`**,不安全就**拒绝执行**
|
|
33
|
+
* 并返回 false(内容不丢:调用方可以改走 `appendWake`)。
|
|
34
|
+
* `appendWake` 走的是正常回合(dsh 会先补人设),所以**天然安全**。
|
|
35
|
+
*
|
|
36
|
+
* ── 设计取舍 ───────────────────────────────────────────────────
|
|
37
|
+
* 不直接复用 `botplay-ext.ts` 的 `makeExtContext()`:那个 ctx 带 `card()` /
|
|
38
|
+
* `clickCount` / `reloadSelf` 等**卡片专属**成员,给工具/命令用只会让作者困惑。
|
|
39
|
+
* 这里只做「三套共有」的那一层,实现上仍是同一批底层原语(sender / session / token)。
|
|
40
|
+
*/
|
|
41
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
42
|
+
import { dirname, join } from 'node:path';
|
|
43
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
44
|
+
import { canSilentlyAppend } from '../session/surface-guard.js';
|
|
45
|
+
import { selfRestart } from '../commands/exit.js';
|
|
46
|
+
// ── 自带 token 管理(不依赖 groupAdmin 开关,第三方用户也能用)──────────────
|
|
47
|
+
// key = appId;value = { token, expireAt }。token 官方有效期 ~7200s,这里按 60s 余量提前过期。
|
|
48
|
+
const tokenCache = new Map();
|
|
49
|
+
async function fetchAccessToken(appId, appSecret) {
|
|
50
|
+
if (!appId || !appSecret)
|
|
51
|
+
return '';
|
|
52
|
+
const hit = tokenCache.get(appId);
|
|
53
|
+
if (hit && Date.now() < hit.expireAt)
|
|
54
|
+
return hit.token;
|
|
55
|
+
const res = await fetch('https://bots.qq.com/app/getAppAccessToken', {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { 'content-type': 'application/json' },
|
|
58
|
+
body: JSON.stringify({ appId, clientSecret: appSecret }),
|
|
59
|
+
});
|
|
60
|
+
const data = (await res.json());
|
|
61
|
+
const token = String(data?.access_token ?? '');
|
|
62
|
+
if (!token)
|
|
63
|
+
throw new Error('拿 access_token 失败(检查 appId/appSecret)');
|
|
64
|
+
const ttl = Number(data?.expires_in ?? 7200);
|
|
65
|
+
tokenCache.set(appId, { token, expireAt: Date.now() + Math.max(60, ttl - 60) * 1000 });
|
|
66
|
+
return token;
|
|
67
|
+
}
|
|
68
|
+
/** 「url 或本地路径」→ 媒体源对象(第三方作者常直接传字符串) */
|
|
69
|
+
function toMediaSource(src) {
|
|
70
|
+
if (typeof src === 'string') {
|
|
71
|
+
return /^https?:\/\//i.test(src) ? { url: src } : { localPath: src };
|
|
72
|
+
}
|
|
73
|
+
return src ?? {};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 造一份扩展能力包。**永远不会抛**:拿不到的依赖 → 对应能力返回 false / 空值。
|
|
77
|
+
*
|
|
78
|
+
* @param deps - 见 {@link ExtCapsDeps}
|
|
79
|
+
* @returns 能力包(见 {@link ExtCapabilities})
|
|
80
|
+
*/
|
|
81
|
+
export function makeExtCaps(deps) {
|
|
82
|
+
const logger = deps.logger;
|
|
83
|
+
const self = String(deps.selfName ?? 'ext');
|
|
84
|
+
const kind = String(deps.kind ?? 'ext');
|
|
85
|
+
const owners = Array.isArray(deps.owners) ? deps.owners.map(String) : [];
|
|
86
|
+
const appId = String(deps.appId ?? '');
|
|
87
|
+
const appSecret = String(deps.appSecret ?? '');
|
|
88
|
+
const actorOpenid = String(deps.actorOpenid ?? '');
|
|
89
|
+
const storeDir = deps.dataRoot ? join(deps.dataRoot, '.qqbot-extensions', kind, 'data') : '';
|
|
90
|
+
const storeFile = storeDir ? join(storeDir, `${self.replace(/\.(mjs?|cjs|js)$/i, '')}.json`) : '';
|
|
91
|
+
const warn = (what, err) => {
|
|
92
|
+
try {
|
|
93
|
+
logger?.warn?.(`[ext-caps] ${self} ${what} 失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
94
|
+
}
|
|
95
|
+
catch { /* 日志失败忽略 */ }
|
|
96
|
+
};
|
|
97
|
+
const send = async (what, fn) => {
|
|
98
|
+
try {
|
|
99
|
+
await fn();
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
warn(what, err);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
appId,
|
|
109
|
+
appSecret,
|
|
110
|
+
// ── @人 ────────────────────────────────────────────────────────
|
|
111
|
+
at: (id) => `<@${String(id ?? '')}>`,
|
|
112
|
+
// ── 发送 ───────────────────────────────────────────────────────
|
|
113
|
+
text: (s) => {
|
|
114
|
+
const t = String(s ?? '');
|
|
115
|
+
// ⚠️ 用 trim 判"空",但发出去的仍是原文(不裁剪内容语义)
|
|
116
|
+
if (!t.trim() || !deps.sender?.sendText)
|
|
117
|
+
return Promise.resolve(false);
|
|
118
|
+
return send('text', () => deps.sender.sendText(deps.replyTarget, t));
|
|
119
|
+
},
|
|
120
|
+
markdown: (s) => {
|
|
121
|
+
const t = String(s ?? '');
|
|
122
|
+
if (!t.trim() || !deps.sender?.sendMarkdown)
|
|
123
|
+
return Promise.resolve(false);
|
|
124
|
+
return send('markdown', () => deps.sender.sendMarkdown(deps.replyTarget, t));
|
|
125
|
+
},
|
|
126
|
+
markdownCard: (s, keyboard) => {
|
|
127
|
+
const t = String(s ?? '');
|
|
128
|
+
if (!t.trim())
|
|
129
|
+
return Promise.resolve(false);
|
|
130
|
+
if (deps.sender?.sendMarkdownWithKeyboard) {
|
|
131
|
+
return send('markdownCard', () => deps.sender.sendMarkdownWithKeyboard(deps.replyTarget, t, keyboard));
|
|
132
|
+
}
|
|
133
|
+
if (deps.sender?.sendMarkdown)
|
|
134
|
+
return send('markdownCard→markdown', () => deps.sender.sendMarkdown(deps.replyTarget, t));
|
|
135
|
+
return Promise.resolve(false);
|
|
136
|
+
},
|
|
137
|
+
media: (k, source) => {
|
|
138
|
+
if (!deps.sender?.sendMedia)
|
|
139
|
+
return Promise.resolve(false);
|
|
140
|
+
return send(`media(${k})`, () => deps.sender.sendMedia(deps.replyTarget, k, toMediaSource(source)));
|
|
141
|
+
},
|
|
142
|
+
image: (source) => {
|
|
143
|
+
if (!deps.sender?.sendMedia)
|
|
144
|
+
return Promise.resolve(false);
|
|
145
|
+
return send('image', () => deps.sender.sendMedia(deps.replyTarget, 'image', toMediaSource(source)));
|
|
146
|
+
},
|
|
147
|
+
voice: (source) => {
|
|
148
|
+
if (!deps.sender?.sendMedia)
|
|
149
|
+
return Promise.resolve(false);
|
|
150
|
+
return send('voice', () => deps.sender.sendMedia(deps.replyTarget, 'voice', toMediaSource(source)));
|
|
151
|
+
},
|
|
152
|
+
video: (source) => {
|
|
153
|
+
if (!deps.sender?.sendMedia)
|
|
154
|
+
return Promise.resolve(false);
|
|
155
|
+
return send('video', () => deps.sender.sendMedia(deps.replyTarget, 'video', toMediaSource(source)));
|
|
156
|
+
},
|
|
157
|
+
file: (source) => {
|
|
158
|
+
if (!deps.sender?.sendMedia)
|
|
159
|
+
return Promise.resolve(false);
|
|
160
|
+
return send('file', () => deps.sender.sendMedia(deps.replyTarget, 'file', toMediaSource(source)));
|
|
161
|
+
},
|
|
162
|
+
// ── 官方 API(自带 token;注入了 apiCall 就优先用注入的)──────────
|
|
163
|
+
api: async (path, opts) => {
|
|
164
|
+
const method = (opts?.method ?? 'GET');
|
|
165
|
+
const p = String(path ?? '');
|
|
166
|
+
if (!p)
|
|
167
|
+
return { ok: false, err: { code: 'BAD_PATH', human: 'path 不能为空' } };
|
|
168
|
+
if (typeof deps.apiCall === 'function') {
|
|
169
|
+
try {
|
|
170
|
+
return await deps.apiCall(method, p, opts?.body);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
warn('api(injected)', err);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const token = await fetchAccessToken(appId, appSecret);
|
|
178
|
+
if (!token)
|
|
179
|
+
return { ok: false, err: { code: 'NO_CREDENTIALS', human: '未配置 appId/appSecret' } };
|
|
180
|
+
const res = await fetch(`https://api.bot.qq.com${p.startsWith('/') ? p : `/${p}`}`, {
|
|
181
|
+
method,
|
|
182
|
+
headers: { authorization: `QQBot ${token}`, 'content-type': 'application/json' },
|
|
183
|
+
...(opts?.body === undefined ? {} : { body: JSON.stringify(opts.body) }),
|
|
184
|
+
});
|
|
185
|
+
const text = await res.text();
|
|
186
|
+
let data = text;
|
|
187
|
+
try {
|
|
188
|
+
data = JSON.parse(text);
|
|
189
|
+
}
|
|
190
|
+
catch { /* 非 JSON 原样返回 */ }
|
|
191
|
+
if (!res.ok)
|
|
192
|
+
return { ok: false, err: { code: String(res.status), human: `HTTP ${res.status}: ${text.slice(0, 200)}` } };
|
|
193
|
+
return { ok: true, data };
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
return { ok: false, err: { code: 'THROW', human: err instanceof Error ? err.message : String(err) } };
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
// ── 持久化(落数据根,插件升级/重装不丢)─────────────────────────
|
|
200
|
+
store: {
|
|
201
|
+
path: storeFile,
|
|
202
|
+
load() {
|
|
203
|
+
if (!storeFile)
|
|
204
|
+
return undefined;
|
|
205
|
+
try {
|
|
206
|
+
if (!existsSync(storeFile))
|
|
207
|
+
return undefined;
|
|
208
|
+
return JSON.parse(readFileSync(storeFile, 'utf8'));
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
warn('store.load', err);
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
save(value) {
|
|
216
|
+
if (!storeFile)
|
|
217
|
+
return false;
|
|
218
|
+
try {
|
|
219
|
+
mkdirSync(dirname(storeFile), { recursive: true });
|
|
220
|
+
writeFileSync(storeFile, JSON.stringify(value ?? null, null, 2), 'utf8');
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
warn('store.save', err);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
// ── 上下文(★ appendSilent 必须过表面守卫)───────────────────────
|
|
230
|
+
appendSilent: async (text) => {
|
|
231
|
+
const t = String(text ?? '').trim();
|
|
232
|
+
if (!t)
|
|
233
|
+
return false;
|
|
234
|
+
const session = deps.agent?.session;
|
|
235
|
+
if (!session || typeof session.append !== 'function')
|
|
236
|
+
return false;
|
|
237
|
+
if (!canSilentlyAppend(session)) {
|
|
238
|
+
try {
|
|
239
|
+
logger?.debug?.('[ext-caps] appendSilent 被拒:该会话人设尚未落盘(surface 首节点不是 system/message),硬写会报废会话日志');
|
|
240
|
+
}
|
|
241
|
+
catch { /* ignore */ }
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
session.append('user/message', createUserMessage({ content: [{ type: 'text', text: t }], source: { kind: 'user' } }), { surfaceOp: 'append' });
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
warn('appendSilent', err);
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
appendWake: async (text) => {
|
|
254
|
+
const t = String(text ?? '').trim();
|
|
255
|
+
if (!t)
|
|
256
|
+
return false;
|
|
257
|
+
const agent = deps.agent;
|
|
258
|
+
// 走正常回合:dsh 会先补人设 → 天然安全,不需要表面守卫
|
|
259
|
+
if (agent && typeof agent.followup === 'function') {
|
|
260
|
+
try {
|
|
261
|
+
agent.followup(createUserMessage({ content: [{ type: 'text', text: t }], source: { kind: 'user' } }));
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
warn('appendWake', err);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// 没有 followup 能力 ⇒ 退回静默 append(仍受守卫保护)
|
|
270
|
+
return false;
|
|
271
|
+
},
|
|
272
|
+
canAppendSilent: () => canSilentlyAppend(deps.agent?.session),
|
|
273
|
+
// ── 身份 ───────────────────────────────────────────────────────
|
|
274
|
+
user: {
|
|
275
|
+
openid: actorOpenid,
|
|
276
|
+
name: String(deps.actorName ?? ''),
|
|
277
|
+
isOwner: actorOpenid !== '' && owners.includes(actorOpenid),
|
|
278
|
+
},
|
|
279
|
+
owners,
|
|
280
|
+
peer: { scope: String(deps.scope ?? ''), peerId: String(deps.peerId ?? '') },
|
|
281
|
+
log: (...args) => {
|
|
282
|
+
try {
|
|
283
|
+
logger?.info?.(`[ext:${self}] ${args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ')}`);
|
|
284
|
+
}
|
|
285
|
+
catch { /* ignore */ }
|
|
286
|
+
},
|
|
287
|
+
// ── 内核句柄(遥控器)────────────────────────────────────────────
|
|
288
|
+
kernel: deps.kernel,
|
|
289
|
+
// ── 宿主自重启(复用内置 /bot-restart 的实现,别自己 spawn)──────
|
|
290
|
+
restart: (opts) => {
|
|
291
|
+
const requireOwner = opts?.requireOwner !== false; // 默认:仅主人
|
|
292
|
+
const isOwner = actorOpenid !== '' && owners.includes(actorOpenid);
|
|
293
|
+
if (requireOwner && !isOwner) {
|
|
294
|
+
try {
|
|
295
|
+
logger?.warn?.(`[ext-caps] ${self} restart 被拒:仅主人可触发(想放开就传 { requireOwner: false })`);
|
|
296
|
+
}
|
|
297
|
+
catch { /* ignore */ }
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
logger?.warn?.(`[ext-caps] ${self} 触发了宿主自重启(owner=${isOwner} actor=${actorOpenid || '(未知)'})`);
|
|
302
|
+
}
|
|
303
|
+
catch { /* ignore */ }
|
|
304
|
+
return (deps.restartImpl ?? selfRestart)(opts?.delayMs);
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=ext-capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ext-capabilities.js","sourceRoot":"","sources":["../../src/features/ext-capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/gateway/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5E,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAqC1C,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/gateway/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5E,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAqC1C,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAqnBf"}
|
|
@@ -127,7 +127,7 @@ export async function bootstrapGateway(ctx, agents, config, logger) {
|
|
|
127
127
|
});
|
|
128
128
|
logger.info(`QQBot SDK initialized (UA: ${userAgent})${watchJoinRequests ? ' [watchJoinRequests: GROUP_MEMBER_EVENT 已订阅]' : ''}`);
|
|
129
129
|
// ── 中间件链 ──
|
|
130
|
-
await setupMiddlewares(bot, config, manager, logger);
|
|
130
|
+
await setupMiddlewares(bot, config, manager, logger, ctx);
|
|
131
131
|
// ── 入站:经过中间件链后的消息交给 dsh agent ──
|
|
132
132
|
bot.on('message', async (mCtx) => {
|
|
133
133
|
const msg = mCtx.message;
|
|
@@ -466,6 +466,8 @@ export async function bootstrapGateway(ctx, agents, config, logger) {
|
|
|
466
466
|
// ⚠️ 必须与事件文件同源(dataRootOf), 否则面板报的路径与实际加载的不是一处, 主人改错文件还找不到原因
|
|
467
467
|
() => dataRootOf(config));
|
|
468
468
|
registerBotplayController(myNs, botplayController);
|
|
469
|
+
// ★ 内核句柄:把插件 ctx 交给 botplay(卡片 ctx.kernel / 与扩展工具·命令对齐)
|
|
470
|
+
botplayController.setKernel(ctx);
|
|
469
471
|
// 昵称兜底(2026-10-05 主人定: "兜底的时候用 api,其余的时候用台账"):
|
|
470
472
|
// 台账/会话壳都查不到时, 后台调官方「获取群成员信息」接口拿 username。
|
|
471
473
|
// ⚠️ 该接口官方标注「内邀接入中」(11253=无权限) ⇒ 失败静默、只记一行日志。
|