@tencent-connect/dsh-qqbot 0.4.0 → 0.5.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/.orange-ci.yml +2 -68
- package/CHANGELOG.md +56 -0
- package/CHANGELOG_EN.md +95 -0
- package/README.md +119 -18
- package/README_EN.md +107 -17
- package/cordis.patch.yml +1 -1
- package/cordis.yml +46 -3
- package/dist/commands/help.d.ts +6 -7
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +23 -7
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/index.d.ts +7 -5
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +12 -12
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/misc.d.ts +10 -8
- package/dist/commands/misc.d.ts.map +1 -1
- package/dist/commands/misc.js +45 -8
- package/dist/commands/misc.js.map +1 -1
- package/dist/commands/model.d.ts +3 -4
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +8 -7
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/preset.d.ts +8 -0
- package/dist/commands/preset.d.ts.map +1 -0
- package/dist/commands/preset.js +52 -0
- package/dist/commands/preset.js.map +1 -0
- package/dist/commands/session.d.ts +11 -7
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +29 -13
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/status.d.ts +3 -4
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/types.d.ts +9 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -9
- package/dist/config.js.map +1 -1
- package/dist/features/answer-parser.d.ts +9 -0
- package/dist/features/answer-parser.d.ts.map +1 -0
- package/dist/features/answer-parser.js +23 -0
- package/dist/features/answer-parser.js.map +1 -0
- package/dist/features/approval-channel.d.ts +76 -0
- package/dist/features/approval-channel.d.ts.map +1 -0
- package/dist/features/approval-channel.js +142 -0
- package/dist/features/approval-channel.js.map +1 -0
- package/dist/features/approval-renderer.d.ts +18 -0
- package/dist/features/approval-renderer.d.ts.map +1 -0
- package/dist/features/approval-renderer.js +52 -0
- package/dist/features/approval-renderer.js.map +1 -0
- package/dist/features/button-utils.d.ts +33 -0
- package/dist/features/button-utils.d.ts.map +1 -0
- package/dist/features/button-utils.js +27 -0
- package/dist/features/button-utils.js.map +1 -0
- package/dist/features/history-store.d.ts.map +1 -1
- package/dist/features/history-store.js +0 -1
- package/dist/features/history-store.js.map +1 -1
- package/dist/features/question-channel.d.ts +96 -0
- package/dist/features/question-channel.d.ts.map +1 -0
- package/dist/features/question-channel.js +234 -0
- package/dist/features/question-channel.js.map +1 -0
- package/dist/features/question-renderer.d.ts +20 -0
- package/dist/features/question-renderer.d.ts.map +1 -0
- package/dist/features/question-renderer.js +70 -0
- package/dist/features/question-renderer.js.map +1 -0
- package/dist/gateway/bootstrap.d.ts +3 -3
- package/dist/gateway/bootstrap.d.ts.map +1 -1
- package/dist/gateway/bootstrap.js +84 -8
- package/dist/gateway/bootstrap.js.map +1 -1
- package/dist/gateway/index.d.ts +2 -2
- package/dist/gateway/index.js +2 -2
- package/dist/gateway/middleware-setup.d.ts +3 -3
- package/dist/gateway/middleware-setup.d.ts.map +1 -1
- package/dist/gateway/middleware-setup.js +9 -7
- package/dist/gateway/middleware-setup.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -25
- package/dist/index.js.map +1 -1
- package/dist/media/media-cleaner.d.ts +19 -0
- package/dist/media/media-cleaner.d.ts.map +1 -0
- package/dist/media/media-cleaner.js +81 -0
- package/dist/media/media-cleaner.js.map +1 -0
- package/dist/media/send-file-tool.d.ts +35 -0
- package/dist/media/send-file-tool.d.ts.map +1 -0
- package/dist/media/send-file-tool.js +150 -0
- package/dist/media/send-file-tool.js.map +1 -0
- package/dist/media/vision-tool.d.ts +16 -0
- package/dist/media/vision-tool.d.ts.map +1 -0
- package/dist/media/vision-tool.js +241 -0
- package/dist/media/vision-tool.js.map +1 -0
- package/dist/middleware/attachment.d.ts +3 -3
- package/dist/middleware/attachment.d.ts.map +1 -1
- package/dist/middleware/attachment.js +17 -4
- package/dist/middleware/attachment.js.map +1 -1
- package/dist/middleware/question-answer.d.ts +10 -0
- package/dist/middleware/question-answer.d.ts.map +1 -0
- package/dist/middleware/question-answer.js +19 -0
- package/dist/middleware/question-answer.js.map +1 -0
- package/dist/model/index.d.ts +4 -4
- package/dist/model/index.js +3 -3
- package/dist/model/model-resolver.d.ts +27 -13
- package/dist/model/model-resolver.d.ts.map +1 -1
- package/dist/model/model-resolver.js +82 -27
- package/dist/model/model-resolver.js.map +1 -1
- package/dist/model/prefs-store.d.ts +6 -1
- package/dist/model/prefs-store.d.ts.map +1 -1
- package/dist/model/prefs-store.js +25 -1
- package/dist/model/prefs-store.js.map +1 -1
- package/dist/model/settings-reader.d.ts +1 -1
- package/dist/session/idle-evictor.d.ts +2 -2
- package/dist/session/idle-evictor.d.ts.map +1 -1
- package/dist/session/idle-evictor.js +1 -1
- package/dist/session/idle-evictor.js.map +1 -1
- package/dist/session/index.d.ts +3 -3
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +2 -2
- package/dist/session/session-manager.d.ts +67 -9
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +216 -20
- package/dist/session/session-manager.js.map +1 -1
- package/dist/session/types.d.ts +47 -1
- package/dist/session/types.d.ts.map +1 -1
- package/dist/setup/bind-routes.d.ts +78 -0
- package/dist/setup/bind-routes.d.ts.map +1 -0
- package/dist/setup/bind-routes.js +311 -0
- package/dist/setup/bind-routes.js.map +1 -0
- package/dist/setup/binding-service.d.ts +86 -0
- package/dist/setup/binding-service.d.ts.map +1 -0
- package/dist/setup/binding-service.js +175 -0
- package/dist/setup/binding-service.js.map +1 -0
- package/dist/setup/index.d.ts +50 -0
- package/dist/setup/index.d.ts.map +1 -0
- package/dist/setup/index.js +98 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/setup/persist.d.ts +41 -0
- package/dist/setup/persist.d.ts.map +1 -0
- package/dist/setup/persist.js +201 -0
- package/dist/setup/persist.js.map +1 -0
- package/dist/setup/presenters.d.ts +40 -0
- package/dist/setup/presenters.d.ts.map +1 -0
- package/dist/setup/presenters.js +84 -0
- package/dist/setup/presenters.js.map +1 -0
- package/dist/setup/qr-svg.d.ts +8 -0
- package/dist/setup/qr-svg.d.ts.map +1 -0
- package/dist/setup/qr-svg.js +20 -0
- package/dist/setup/qr-svg.js.map +1 -0
- package/dist/shared/bind-protocol.d.ts +42 -0
- package/dist/shared/bind-protocol.d.ts.map +1 -0
- package/dist/shared/bind-protocol.js +19 -0
- package/dist/shared/bind-protocol.js.map +1 -0
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +3 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/send-helper.js +1 -1
- package/dist/shared/utils.d.ts +10 -3
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +47 -6
- package/dist/shared/utils.js.map +1 -1
- package/dist/transport/attachment.d.ts +18 -7
- package/dist/transport/attachment.d.ts.map +1 -1
- package/dist/transport/attachment.js +77 -30
- package/dist/transport/attachment.js.map +1 -1
- package/dist/transport/inbound.d.ts +5 -4
- package/dist/transport/inbound.d.ts.map +1 -1
- package/dist/transport/inbound.js +120 -80
- package/dist/transport/inbound.js.map +1 -1
- package/dist/transport/index.d.ts +4 -4
- package/dist/transport/index.js +4 -4
- package/dist/transport/msgid-cache.d.ts +23 -0
- package/dist/transport/msgid-cache.d.ts.map +1 -0
- package/dist/transport/msgid-cache.js +57 -0
- package/dist/transport/msgid-cache.js.map +1 -0
- package/dist/transport/outbound-buffer.d.ts +9 -6
- package/dist/transport/outbound-buffer.d.ts.map +1 -1
- package/dist/transport/outbound-buffer.js +5 -5
- package/dist/transport/outbound-buffer.js.map +1 -1
- package/dist/transport/outbound.d.ts +9 -9
- package/dist/transport/outbound.d.ts.map +1 -1
- package/dist/transport/outbound.js +7 -7
- package/dist/transport/outbound.js.map +1 -1
- package/dist/transport/reply-limiter.d.ts +31 -0
- package/dist/transport/reply-limiter.d.ts.map +1 -0
- package/dist/transport/reply-limiter.js +50 -0
- package/dist/transport/reply-limiter.js.map +1 -0
- package/dist/transport/reply-target.d.ts +29 -0
- package/dist/transport/reply-target.d.ts.map +1 -0
- package/dist/transport/reply-target.js +55 -0
- package/dist/transport/reply-target.js.map +1 -0
- package/dist/transport/streaming-writer.d.ts +3 -3
- package/dist/transport/streaming-writer.d.ts.map +1 -1
- package/dist/transport/streaming-writer.js +1 -1
- package/dist/transport/streaming-writer.js.map +1 -1
- package/dist/transport/tool-presenter.d.ts +0 -1
- package/dist/transport/tool-presenter.d.ts.map +1 -1
- package/dist/transport/tool-presenter.js +0 -1
- package/dist/transport/tool-presenter.js.map +1 -1
- package/dist/types.d.ts +9 -15
- package/dist/types.d.ts.map +1 -1
- package/docs/assets/qqchannel.jpg +0 -0
- package/docs/assets/qqgroup.jpg +0 -0
- package/docs/assets/qrcode.png +0 -0
- package/lib/client.js +996 -0
- package/lib/client.js.map +1 -0
- package/package.json +44 -7
- package/pnpm-workspace.yaml +1 -0
- package/tsconfig.client.json +19 -0
- package/tsdown.client.config.ts +58 -0
- package/dist/setup.d.ts +0 -24
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js +0 -170
- package/dist/setup.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** QQ 按钮 label 有长度限制,超长截断(正文保留完整文本;多列布局下阈值更短) */
|
|
2
|
+
export const BUTTON_LABEL_MAX = 10;
|
|
3
|
+
/** 单选按钮每行个数(多列布局,减少选项多时的刷屏) */
|
|
4
|
+
export const BUTTONS_PER_ROW = 2;
|
|
5
|
+
/** 截断按钮 label,超长补省略号 */
|
|
6
|
+
export function buttonLabel(text) {
|
|
7
|
+
const s = String(text ?? '').trim();
|
|
8
|
+
return s.length > BUTTON_LABEL_MAX ? s.slice(0, BUTTON_LABEL_MAX - 1) + '…' : s;
|
|
9
|
+
}
|
|
10
|
+
/** 编码 button_data(JSON 字符串) */
|
|
11
|
+
export function encodeButtonData(data) {
|
|
12
|
+
return JSON.stringify(data);
|
|
13
|
+
}
|
|
14
|
+
/** 解码 button_data;非法 JSON 或缺少合法 `t` 时返回 undefined */
|
|
15
|
+
export function decodeButtonData(raw) {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
if (parsed !== null && typeof parsed === 'object' && (parsed.t === 'question' || parsed.t === 'approval')) {
|
|
19
|
+
return parsed;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// fall through
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=button-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button-utils.js","sourceRoot":"","sources":["../../src/features/button-utils.ts"],"names":[],"mappings":"AASA,iDAAiD;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAEnC,+BAA+B;AAC/B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,wBAAwB;AACxB,MAAM,UAAU,WAAW,CAAC,IAAwB;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO,CAAC,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC;AAmBD,+BAA+B;AAC/B,MAAM,UAAU,gBAAgB,CAAC,IAAgB;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QACtD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,UAAU,IAAI,MAAM,CAAC,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC;YAC1G,OAAO,MAAoB,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../../src/features/history-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../../src/features/history-store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,mBAAmB;AACnB,wBAAgB,eAAe,IAAI,YAAY,CAG9C;AAED,mDAAmD;AACnD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,gCAAgC;AAChC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAEtE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-store.js","sourceRoot":"","sources":["../../src/features/history-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"history-store.js","sourceRoot":"","sources":["../../src/features/history-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,IAAI,MAAM,GAAwB,IAAI,CAAC;AAEvC,mBAAmB;AACnB,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,MAAM;QAAE,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,OAAe;IAC5D,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,OAAe;IAC9D,MAAM,EAAE,KAAK,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuestionChannel — 把 dsh 的 ask_user_question 桥接到 QQ 通道。
|
|
3
|
+
*
|
|
4
|
+
* 接入方式:包装 `userQuestions.ask`(而非 registerProvider),按 `request.agent.id`
|
|
5
|
+
* 路由到 QQ 会话;非 QQ 会话委托宿主默认 provider(如 Web 弹出框),多通道共存。
|
|
6
|
+
*
|
|
7
|
+
* 每会话一个显式状态机,三态语义:
|
|
8
|
+
* - 待答(pending 中):等待当前题的回答
|
|
9
|
+
* - 逐题推进:多问题时一次问一题,答完一题再问下一题
|
|
10
|
+
* - 完成 / 中断(abort / 超时 / 会话回收):reject(ASK_ABORTED)
|
|
11
|
+
*/
|
|
12
|
+
import type { InlineKeyboard, InteractionEvent } from '@tencent-connect/qqbot-nodejs';
|
|
13
|
+
import type { ChatScope, Logger, ReplyTarget } from '../types.ts';
|
|
14
|
+
export interface UserQuestionOption {
|
|
15
|
+
label: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface UserQuestion {
|
|
19
|
+
id: string;
|
|
20
|
+
header?: string;
|
|
21
|
+
question: string;
|
|
22
|
+
options?: UserQuestionOption[];
|
|
23
|
+
multiSelect?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface UserQuestionAnswer {
|
|
26
|
+
id: string;
|
|
27
|
+
selected: string[];
|
|
28
|
+
custom?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface UserQuestionRequest {
|
|
31
|
+
questions: UserQuestion[];
|
|
32
|
+
agent?: {
|
|
33
|
+
id?: string;
|
|
34
|
+
};
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}
|
|
37
|
+
export interface UserQuestionResult {
|
|
38
|
+
answers: UserQuestionAnswer[];
|
|
39
|
+
}
|
|
40
|
+
export interface UserQuestionsServiceLike {
|
|
41
|
+
ask: (request: UserQuestionRequest) => Promise<UserQuestionResult>;
|
|
42
|
+
__qqQuestionPatched?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface QuestionSessionRecordLike {
|
|
45
|
+
sessionKey: string;
|
|
46
|
+
scope: ChatScope;
|
|
47
|
+
replyTarget: ReplyTarget;
|
|
48
|
+
}
|
|
49
|
+
export interface QuestionChannelManagerLike {
|
|
50
|
+
findBySessionId(sessionId: string): QuestionSessionRecordLike | undefined;
|
|
51
|
+
getSessionRecord(scope: ChatScope, peerId: string): QuestionSessionRecordLike | undefined;
|
|
52
|
+
}
|
|
53
|
+
export interface QuestionChannelSenderLike {
|
|
54
|
+
sendMarkdown(target: ReplyTarget, content: string, opts?: {
|
|
55
|
+
keyboard?: InlineKeyboard;
|
|
56
|
+
}): Promise<unknown>;
|
|
57
|
+
}
|
|
58
|
+
export interface QuestionChannelConfigLike {
|
|
59
|
+
requireMention: boolean;
|
|
60
|
+
askTimeoutMs: number;
|
|
61
|
+
}
|
|
62
|
+
export declare class QuestionChannel {
|
|
63
|
+
private readonly pending;
|
|
64
|
+
private uq?;
|
|
65
|
+
private origAsk?;
|
|
66
|
+
private readonly manager;
|
|
67
|
+
private readonly sender;
|
|
68
|
+
private readonly config;
|
|
69
|
+
private readonly logger;
|
|
70
|
+
constructor(manager: QuestionChannelManagerLike, sender: QuestionChannelSenderLike, config: QuestionChannelConfigLike, logger: Logger);
|
|
71
|
+
/**
|
|
72
|
+
* 包装 userQuestions.ask:QQ 会话走 QQ 通道,其余委托原实现。
|
|
73
|
+
* 幂等(已 patch 则跳过);服务缺失或 ask 非函数时优雅禁用。
|
|
74
|
+
*/
|
|
75
|
+
install(ctx: {
|
|
76
|
+
get(name: string): unknown;
|
|
77
|
+
}): void;
|
|
78
|
+
uninstall(): void;
|
|
79
|
+
/** 会话回收时清理其待答问题,避免 pending 悬挂导致 Promise 与闭包泄漏 */
|
|
80
|
+
cancelPending(sessionKey: string): void;
|
|
81
|
+
/** 入站文本作答:该会话若有待答问题且本条消息有文本,解析为答案并推进 */
|
|
82
|
+
tryAnswer(scope: ChatScope, peerId: string, text: string): boolean;
|
|
83
|
+
/** 按钮点击作答:解码 button_data 选项下标,推进状态机 */
|
|
84
|
+
handleInteraction(event: InteractionEvent): boolean;
|
|
85
|
+
/** 走 QQ 通道:发送首题 + 挂起 Promise,等待逐题作答 */
|
|
86
|
+
private askViaQQ;
|
|
87
|
+
/** 推进状态机:收集答案 → 有下一题则发送,否则 resolve 全部答案 */
|
|
88
|
+
private advance;
|
|
89
|
+
/** 发送单个问题:优先带按钮,按钮发送失败降级纯文本 */
|
|
90
|
+
private sendQuestion;
|
|
91
|
+
private onTimeout;
|
|
92
|
+
private resetTimer;
|
|
93
|
+
private clearTimer;
|
|
94
|
+
private removeAbort;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=question-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-channel.d.ts","sourceRoot":"","sources":["../../src/features/question-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { parseAnswer } from "./answer-parser.js";
|
|
2
|
+
import { buildKeyboard, formatQuestion } from "./question-renderer.js";
|
|
3
|
+
import { decodeButtonData } from "./button-utils.js";
|
|
4
|
+
// ── 错误语义(对齐 dsh-user-questions 的 UserQuestionError code 约定) ──
|
|
5
|
+
/** 问答中断错误码:上层(如 plan-mode)按 code 区分「用户未作答」与普通发送/网络错误 */
|
|
6
|
+
const ASK_ABORTED = 'ASK_ABORTED';
|
|
7
|
+
class QuestionChannelError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(message, code) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.name = 'QuestionChannelError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class QuestionChannel {
|
|
16
|
+
pending = new Map();
|
|
17
|
+
uq;
|
|
18
|
+
origAsk;
|
|
19
|
+
manager;
|
|
20
|
+
sender;
|
|
21
|
+
config;
|
|
22
|
+
logger;
|
|
23
|
+
constructor(manager, sender, config, logger) {
|
|
24
|
+
this.manager = manager;
|
|
25
|
+
this.sender = sender;
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 包装 userQuestions.ask:QQ 会话走 QQ 通道,其余委托原实现。
|
|
31
|
+
* 幂等(已 patch 则跳过);服务缺失或 ask 非函数时优雅禁用。
|
|
32
|
+
*/
|
|
33
|
+
install(ctx) {
|
|
34
|
+
const uq = ctx.get('userQuestions');
|
|
35
|
+
if (!uq || uq.__qqQuestionPatched)
|
|
36
|
+
return;
|
|
37
|
+
if (typeof uq.ask !== 'function') {
|
|
38
|
+
this.logger.warn('im-qqbot: userQuestions service lacks ask() — QQ question channel disabled');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const self = this;
|
|
42
|
+
this.origAsk = uq.ask.bind(uq);
|
|
43
|
+
uq.ask = async function qqRoutedAsk(request) {
|
|
44
|
+
const sessionId = request?.agent?.id;
|
|
45
|
+
const record = sessionId ? self.manager.findBySessionId(sessionId) : undefined;
|
|
46
|
+
if (record)
|
|
47
|
+
return self.askViaQQ(record, request);
|
|
48
|
+
return self.origAsk(request);
|
|
49
|
+
};
|
|
50
|
+
Object.defineProperty(uq, '__qqQuestionPatched', { value: true, configurable: true, enumerable: false });
|
|
51
|
+
this.uq = uq;
|
|
52
|
+
this.logger.info('im-qqbot: QQ question channel installed');
|
|
53
|
+
}
|
|
54
|
+
uninstall() {
|
|
55
|
+
if (this.uq && this.origAsk) {
|
|
56
|
+
this.uq.ask = this.origAsk;
|
|
57
|
+
delete this.uq.__qqQuestionPatched;
|
|
58
|
+
this.uq = undefined;
|
|
59
|
+
this.origAsk = undefined;
|
|
60
|
+
}
|
|
61
|
+
for (const [, entry] of this.pending) {
|
|
62
|
+
this.clearTimer(entry);
|
|
63
|
+
this.removeAbort(entry);
|
|
64
|
+
entry.reject(new QuestionChannelError('QQ question channel was unloaded before the user answered', ASK_ABORTED));
|
|
65
|
+
}
|
|
66
|
+
this.pending.clear();
|
|
67
|
+
}
|
|
68
|
+
/** 会话回收时清理其待答问题,避免 pending 悬挂导致 Promise 与闭包泄漏 */
|
|
69
|
+
cancelPending(sessionKey) {
|
|
70
|
+
const entry = this.pending.get(sessionKey);
|
|
71
|
+
if (!entry)
|
|
72
|
+
return;
|
|
73
|
+
this.pending.delete(sessionKey);
|
|
74
|
+
this.clearTimer(entry);
|
|
75
|
+
this.removeAbort(entry);
|
|
76
|
+
entry.reject(new QuestionChannelError('ask_user_question was cancelled before the user answered', ASK_ABORTED));
|
|
77
|
+
}
|
|
78
|
+
/** 入站文本作答:该会话若有待答问题且本条消息有文本,解析为答案并推进 */
|
|
79
|
+
tryAnswer(scope, peerId, text) {
|
|
80
|
+
const record = this.manager.getSessionRecord(scope, peerId);
|
|
81
|
+
if (!record)
|
|
82
|
+
return false;
|
|
83
|
+
const key = record.sessionKey;
|
|
84
|
+
const entry = this.pending.get(key);
|
|
85
|
+
if (!entry)
|
|
86
|
+
return false;
|
|
87
|
+
if (!text)
|
|
88
|
+
return false; // 纯图片/表情等:继续等待,消息走常规流程
|
|
89
|
+
const current = entry.request.questions[entry.index];
|
|
90
|
+
if (!current)
|
|
91
|
+
return false;
|
|
92
|
+
const answer = parseAnswer(current, text);
|
|
93
|
+
this.advance(entry, key, answer);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
/** 按钮点击作答:解码 button_data 选项下标,推进状态机 */
|
|
97
|
+
handleInteraction(event) {
|
|
98
|
+
const peerId = event.group_openid ?? event.user_openid;
|
|
99
|
+
if (!peerId)
|
|
100
|
+
return false;
|
|
101
|
+
const scope = event.group_openid ? 'group' : 'c2c';
|
|
102
|
+
const record = this.manager.getSessionRecord(scope, peerId);
|
|
103
|
+
if (!record)
|
|
104
|
+
return false;
|
|
105
|
+
const entry = this.pending.get(record.sessionKey);
|
|
106
|
+
if (!entry)
|
|
107
|
+
return false;
|
|
108
|
+
const data = event.data?.resolved?.button_data;
|
|
109
|
+
if (!data)
|
|
110
|
+
return false;
|
|
111
|
+
const button = decodeButtonData(data);
|
|
112
|
+
if (!button || button.t !== 'question')
|
|
113
|
+
return false;
|
|
114
|
+
const current = entry.request.questions[entry.index];
|
|
115
|
+
if (!current)
|
|
116
|
+
return false;
|
|
117
|
+
// 校验按钮归属题:旧题按钮在新题推进后被点击(误点/延迟事件),
|
|
118
|
+
// 其 q 不等于当前题 id,直接忽略,避免错位映射到当前题选项。
|
|
119
|
+
if (button.q !== current.id)
|
|
120
|
+
return false;
|
|
121
|
+
const idx = button.i;
|
|
122
|
+
const opts = current.options ?? [];
|
|
123
|
+
if (idx < 0 || idx >= opts.length)
|
|
124
|
+
return false;
|
|
125
|
+
this.advance(entry, record.sessionKey, { id: current.id, selected: [opts[idx].label] });
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/** 走 QQ 通道:发送首题 + 挂起 Promise,等待逐题作答 */
|
|
129
|
+
async askViaQQ(record, request) {
|
|
130
|
+
const key = record.sessionKey;
|
|
131
|
+
// 会话回收/取消可能在发送前发生:signal 已 abort 则直接失败,避免悬挂
|
|
132
|
+
if (request.signal?.aborted) {
|
|
133
|
+
throw new QuestionChannelError('ask_user_question was aborted before delivery', ASK_ABORTED);
|
|
134
|
+
}
|
|
135
|
+
// 同会话已有待答问题(理论上不会发生:回合阻塞在第一个问题上)→ 拒绝旧的
|
|
136
|
+
const prev = this.pending.get(key);
|
|
137
|
+
if (prev) {
|
|
138
|
+
this.pending.delete(key);
|
|
139
|
+
this.clearTimer(prev);
|
|
140
|
+
this.removeAbort(prev);
|
|
141
|
+
prev.reject(new QuestionChannelError('superseded by a new question', ASK_ABORTED));
|
|
142
|
+
}
|
|
143
|
+
const first = request.questions[0];
|
|
144
|
+
if (!first)
|
|
145
|
+
throw new Error('ask_user_question with empty questions');
|
|
146
|
+
await this.sendQuestion(record, first);
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
const entry = { record, request, resolve, reject, index: 0, collected: [] };
|
|
149
|
+
if (request.signal) {
|
|
150
|
+
const onAbort = () => {
|
|
151
|
+
if (this.pending.get(key) !== entry)
|
|
152
|
+
return;
|
|
153
|
+
this.pending.delete(key);
|
|
154
|
+
this.clearTimer(entry);
|
|
155
|
+
reject(new QuestionChannelError('ask_user_question was aborted before the user answered', ASK_ABORTED));
|
|
156
|
+
};
|
|
157
|
+
entry.onAbort = onAbort;
|
|
158
|
+
request.signal.addEventListener('abort', onAbort, { once: true });
|
|
159
|
+
}
|
|
160
|
+
entry.timer = setTimeout(() => this.onTimeout(key), this.config.askTimeoutMs);
|
|
161
|
+
this.pending.set(key, entry);
|
|
162
|
+
this.logger.info(`im-qqbot: question sent to QQ, waiting for answer key=${key}`);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** 推进状态机:收集答案 → 有下一题则发送,否则 resolve 全部答案 */
|
|
166
|
+
advance(entry, key, answer) {
|
|
167
|
+
entry.collected.push(answer);
|
|
168
|
+
entry.index += 1;
|
|
169
|
+
if (entry.index < entry.request.questions.length) {
|
|
170
|
+
const next = entry.request.questions[entry.index];
|
|
171
|
+
this.resetTimer(entry, key);
|
|
172
|
+
void this.sendQuestion(entry.record, next).catch((err) => {
|
|
173
|
+
this.pending.delete(key);
|
|
174
|
+
this.clearTimer(entry);
|
|
175
|
+
this.removeAbort(entry);
|
|
176
|
+
entry.reject(err instanceof Error ? err : new Error(String(err)));
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.pending.delete(key);
|
|
181
|
+
this.clearTimer(entry);
|
|
182
|
+
this.removeAbort(entry);
|
|
183
|
+
this.logger.info(`im-qqbot: question answered via QQ key=${key}`);
|
|
184
|
+
entry.resolve({ answers: entry.collected });
|
|
185
|
+
}
|
|
186
|
+
/** 发送单个问题:优先带按钮,按钮发送失败降级纯文本 */
|
|
187
|
+
async sendQuestion(record, question) {
|
|
188
|
+
const hint = record.scope === 'group' && this.config.requireMention === true;
|
|
189
|
+
const keyboard = buildKeyboard(question);
|
|
190
|
+
try {
|
|
191
|
+
if (keyboard) {
|
|
192
|
+
try {
|
|
193
|
+
await this.sender.sendMarkdown(record.replyTarget, formatQuestion(question, hint, true), { keyboard });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
this.logger.warn(`im-qqbot: keyboard send failed, fallback to text: ${err instanceof Error ? err.message : String(err)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
await this.sender.sendMarkdown(record.replyTarget, formatQuestion(question, hint, false));
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
204
|
+
this.logger.error(`im-qqbot: question send failed key=${record.sessionKey}: ${msg}`);
|
|
205
|
+
throw new Error(`failed to deliver question to QQ: ${msg}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
onTimeout(key) {
|
|
209
|
+
const entry = this.pending.get(key);
|
|
210
|
+
if (!entry)
|
|
211
|
+
return;
|
|
212
|
+
this.pending.delete(key);
|
|
213
|
+
this.removeAbort(entry);
|
|
214
|
+
entry.timer = undefined;
|
|
215
|
+
void this.sender.sendMarkdown(entry.record.replyTarget, '⚠️ 提问已超时,未收到回答。').catch(() => { });
|
|
216
|
+
entry.reject(new QuestionChannelError('ask_user_question timed out before the user answered', ASK_ABORTED));
|
|
217
|
+
}
|
|
218
|
+
resetTimer(entry, key) {
|
|
219
|
+
if (entry.timer)
|
|
220
|
+
clearTimeout(entry.timer);
|
|
221
|
+
entry.timer = setTimeout(() => this.onTimeout(key), this.config.askTimeoutMs);
|
|
222
|
+
}
|
|
223
|
+
clearTimer(entry) {
|
|
224
|
+
if (entry.timer)
|
|
225
|
+
clearTimeout(entry.timer);
|
|
226
|
+
entry.timer = undefined;
|
|
227
|
+
}
|
|
228
|
+
removeAbort(entry) {
|
|
229
|
+
if (entry.onAbort && entry.request.signal) {
|
|
230
|
+
entry.request.signal.removeEventListener('abort', entry.onAbort);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=question-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-channel.js","sourceRoot":"","sources":["../../src/features/question-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QQ 问答的文本渲染与按钮键盘构建(纯函数,便于单测)。
|
|
3
|
+
*
|
|
4
|
+
* 逐题渲染:一次只渲染一个问题(多题时由状态机逐题推进)。
|
|
5
|
+
*/
|
|
6
|
+
import type { InlineKeyboard } from '@tencent-connect/qqbot-nodejs';
|
|
7
|
+
import type { UserQuestion } from './question-channel.ts';
|
|
8
|
+
/**
|
|
9
|
+
* 为「单选、带选项」的问题构建内联键盘;不适用时返回 undefined。
|
|
10
|
+
*
|
|
11
|
+
* 多行多列布局:每行 BUTTONS_PER_ROW 个按钮,减少选项多时的刷屏。
|
|
12
|
+
*
|
|
13
|
+
* button_data 编码 `{"t":"question","q":<问题id>,"i":<选项下标>}`,由分发器解码。
|
|
14
|
+
* `t` 顶层判别字段供 interaction 统一路由;`q` 关联「当前正在问的题」:逐题
|
|
15
|
+
* 推进时旧题按钮在新题发出后被点击,其 q 不等于当前题 id,可被识别并忽略。
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildKeyboard(question: UserQuestion): InlineKeyboard | undefined;
|
|
18
|
+
/** 把单个问题渲染成 QQ 文本 */
|
|
19
|
+
export declare function formatQuestion(question: UserQuestion, requireMentionHint: boolean, withButtons: boolean): string;
|
|
20
|
+
//# sourceMappingURL=question-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-renderer.d.ts","sourceRoot":"","sources":["../../src/features/question-renderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,CA6BhF;AAED,qBAAqB;AACrB,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,kBAAkB,EAAE,OAAO,EAC3B,WAAW,EAAE,OAAO,GACnB,MAAM,CA6BR"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BUTTONS_PER_ROW, buttonLabel, encodeButtonData } from "./button-utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* 为「单选、带选项」的问题构建内联键盘;不适用时返回 undefined。
|
|
4
|
+
*
|
|
5
|
+
* 多行多列布局:每行 BUTTONS_PER_ROW 个按钮,减少选项多时的刷屏。
|
|
6
|
+
*
|
|
7
|
+
* button_data 编码 `{"t":"question","q":<问题id>,"i":<选项下标>}`,由分发器解码。
|
|
8
|
+
* `t` 顶层判别字段供 interaction 统一路由;`q` 关联「当前正在问的题」:逐题
|
|
9
|
+
* 推进时旧题按钮在新题发出后被点击,其 q 不等于当前题 id,可被识别并忽略。
|
|
10
|
+
*/
|
|
11
|
+
export function buildKeyboard(question) {
|
|
12
|
+
const opts = question.options ?? [];
|
|
13
|
+
if (opts.length === 0 || question.multiSelect)
|
|
14
|
+
return undefined;
|
|
15
|
+
const rows = [];
|
|
16
|
+
for (let i = 0; i < opts.length; i += BUTTONS_PER_ROW) {
|
|
17
|
+
rows.push({
|
|
18
|
+
buttons: opts.slice(i, i + BUTTONS_PER_ROW).map((o, offset) => {
|
|
19
|
+
const idx = i + offset;
|
|
20
|
+
return {
|
|
21
|
+
id: `q-${question.id}-opt-${idx}`,
|
|
22
|
+
render_data: {
|
|
23
|
+
label: buttonLabel(o.label),
|
|
24
|
+
visited_label: '✓ 已选',
|
|
25
|
+
style: 1,
|
|
26
|
+
},
|
|
27
|
+
action: {
|
|
28
|
+
type: 1,
|
|
29
|
+
permission: { type: 2 },
|
|
30
|
+
click_limit: 1,
|
|
31
|
+
data: encodeButtonData({ t: 'question', q: question.id, i: idx }),
|
|
32
|
+
},
|
|
33
|
+
// 同一题的选项共享分组:点一个后其余变灰(单选互斥)
|
|
34
|
+
group_id: `q-${question.id}`,
|
|
35
|
+
};
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return { content: { rows } };
|
|
40
|
+
}
|
|
41
|
+
/** 把单个问题渲染成 QQ 文本 */
|
|
42
|
+
export function formatQuestion(question, requireMentionHint, withButtons) {
|
|
43
|
+
const lines = [];
|
|
44
|
+
if (question.header)
|
|
45
|
+
lines.push(`**${question.header}**`);
|
|
46
|
+
const opts = question.options ?? [];
|
|
47
|
+
const multi = question.multiSelect === true;
|
|
48
|
+
// 问题行:多选标记前置,用户第一眼就知道作答方式
|
|
49
|
+
lines.push(multi ? `${question.question}(可多选)` : question.question);
|
|
50
|
+
// 选项列表:保留完整 label + description(按钮 label 截断时 description 靠正文展示)
|
|
51
|
+
opts.forEach((o, i) => {
|
|
52
|
+
lines.push(`${i + 1}. ${o.label}${o.description ? ` · ${o.description}` : ''}`);
|
|
53
|
+
});
|
|
54
|
+
// 底部引导:用引用块(>)与正文区分;mention 放在「回复」动作后(点按钮无需 @)
|
|
55
|
+
if (opts.length > 0) {
|
|
56
|
+
lines.push('');
|
|
57
|
+
const mention = requireMentionHint ? '(需 @机器人)' : '';
|
|
58
|
+
if (withButtons) {
|
|
59
|
+
lines.push(`> 👇 点击下方按钮选择,或回复编号${mention}`);
|
|
60
|
+
}
|
|
61
|
+
else if (multi) {
|
|
62
|
+
lines.push(`> 💡 回复多个编号即可多选(如 1,3)${mention},或直接输入你的想法`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
lines.push(`> 💡 回复编号选择${mention},或直接输入你的想法`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=question-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-renderer.js","sourceRoot":"","sources":["../../src/features/question-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* dsh session/event → createOutboundHandler → QQ 出站。
|
|
6
6
|
*/
|
|
7
7
|
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
-
import { type DshAgentRegistry } from '../session/index.
|
|
9
|
-
import type { ImQQBotConfig } from '../config.
|
|
10
|
-
import type { Logger } from '../types.
|
|
8
|
+
import { type DshAgentRegistry } from '../session/index.ts';
|
|
9
|
+
import type { ImQQBotConfig } from '../config.ts';
|
|
10
|
+
import type { Logger } from '../types.ts';
|
|
11
11
|
export declare function bootstrapGateway(ctx: Context, agents: DshAgentRegistry, config: ImQQBotConfig, logger: Logger): Promise<void>;
|
|
12
12
|
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -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;AAGnD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,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;AAGnD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAW5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,aAAa,CAAC;AAqBrD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAmIf"}
|