@zaofan/dsh-qqbot 0.6.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/LICENSE +21 -0
- package/README.md +308 -0
- package/README_EN.md +273 -0
- package/client/qqbot-settings.js +1463 -0
- package/cordis.patch.yml +36 -0
- package/dist/api/group-admin.d.ts +112 -0
- package/dist/api/group-admin.d.ts.map +1 -0
- package/dist/api/group-admin.js +179 -0
- package/dist/api/group-admin.js.map +1 -0
- package/dist/channel-tools.d.ts +14 -0
- package/dist/channel-tools.d.ts.map +1 -0
- package/dist/channel-tools.js +880 -0
- package/dist/channel-tools.js.map +1 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +21 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +13 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +29 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/misc.d.ts +14 -0
- package/dist/commands/misc.d.ts.map +1 -0
- package/dist/commands/misc.js +39 -0
- package/dist/commands/misc.js.map +1 -0
- package/dist/commands/model.d.ts +7 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +58 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/session.d.ts +10 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +26 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +24 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/types.d.ts +12 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config.d.ts +205 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +203 -0
- package/dist/config.js.map +1 -0
- package/dist/features/chat-ledger.d.ts +53 -0
- package/dist/features/chat-ledger.d.ts.map +1 -0
- package/dist/features/chat-ledger.js +185 -0
- package/dist/features/chat-ledger.js.map +1 -0
- package/dist/features/face-tags.d.ts +17 -0
- package/dist/features/face-tags.d.ts.map +1 -0
- package/dist/features/face-tags.js +79 -0
- package/dist/features/face-tags.js.map +1 -0
- package/dist/features/group-join-request.d.ts +65 -0
- package/dist/features/group-join-request.d.ts.map +1 -0
- package/dist/features/group-join-request.js +190 -0
- package/dist/features/group-join-request.js.map +1 -0
- package/dist/features/history-store.d.ts +8 -0
- package/dist/features/history-store.d.ts.map +1 -0
- package/dist/features/history-store.js +24 -0
- package/dist/features/history-store.js.map +1 -0
- package/dist/features/qq-approval.d.ts +78 -0
- package/dist/features/qq-approval.d.ts.map +1 -0
- package/dist/features/qq-approval.js +165 -0
- package/dist/features/qq-approval.js.map +1 -0
- package/dist/features/schedule-store.d.ts +62 -0
- package/dist/features/schedule-store.d.ts.map +1 -0
- package/dist/features/schedule-store.js +253 -0
- package/dist/features/schedule-store.js.map +1 -0
- package/dist/features/scheduler.d.ts +9 -0
- package/dist/features/scheduler.d.ts.map +1 -0
- package/dist/features/scheduler.js +146 -0
- package/dist/features/scheduler.js.map +1 -0
- package/dist/features/sticker-gate.d.ts +90 -0
- package/dist/features/sticker-gate.d.ts.map +1 -0
- package/dist/features/sticker-gate.js +391 -0
- package/dist/features/sticker-gate.js.map +1 -0
- package/dist/features/sticker-store.d.ts +158 -0
- package/dist/features/sticker-store.d.ts.map +1 -0
- package/dist/features/sticker-store.js +676 -0
- package/dist/features/sticker-store.js.map +1 -0
- package/dist/features/sticker-tagger.d.ts +10 -0
- package/dist/features/sticker-tagger.d.ts.map +1 -0
- package/dist/features/sticker-tagger.js +139 -0
- package/dist/features/sticker-tagger.js.map +1 -0
- package/dist/gateway/bootstrap.d.ts +12 -0
- package/dist/gateway/bootstrap.d.ts.map +1 -0
- package/dist/gateway/bootstrap.js +346 -0
- package/dist/gateway/bootstrap.js.map +1 -0
- package/dist/gateway/debounce.d.ts +40 -0
- package/dist/gateway/debounce.d.ts.map +1 -0
- package/dist/gateway/debounce.js +262 -0
- package/dist/gateway/debounce.js.map +1 -0
- package/dist/gateway/index.d.ts +3 -0
- package/dist/gateway/index.d.ts.map +1 -0
- package/dist/gateway/index.js +3 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/middleware-setup.d.ts +12 -0
- package/dist/gateway/middleware-setup.d.ts.map +1 -0
- package/dist/gateway/middleware-setup.js +156 -0
- package/dist/gateway/middleware-setup.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/attachment.d.ts +11 -0
- package/dist/middleware/attachment.d.ts.map +1 -0
- package/dist/middleware/attachment.js +17 -0
- package/dist/middleware/attachment.js.map +1 -0
- package/dist/middleware/media-history.d.ts +30 -0
- package/dist/middleware/media-history.d.ts.map +1 -0
- package/dist/middleware/media-history.js +54 -0
- package/dist/middleware/media-history.js.map +1 -0
- package/dist/middleware/sticker-capture.d.ts +17 -0
- package/dist/middleware/sticker-capture.d.ts.map +1 -0
- package/dist/middleware/sticker-capture.js +97 -0
- package/dist/middleware/sticker-capture.js.map +1 -0
- package/dist/model/index.d.ts +10 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +9 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/model-resolver.d.ts +88 -0
- package/dist/model/model-resolver.d.ts.map +1 -0
- package/dist/model/model-resolver.js +151 -0
- package/dist/model/model-resolver.js.map +1 -0
- package/dist/model/prefs-store.d.ts +24 -0
- package/dist/model/prefs-store.d.ts.map +1 -0
- package/dist/model/prefs-store.js +126 -0
- package/dist/model/prefs-store.js.map +1 -0
- package/dist/model/settings-reader.d.ts +19 -0
- package/dist/model/settings-reader.d.ts.map +1 -0
- package/dist/model/settings-reader.js +76 -0
- package/dist/model/settings-reader.js.map +1 -0
- package/dist/model/types.d.ts +15 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +5 -0
- package/dist/model/types.js.map +1 -0
- package/dist/session/idle-evictor.d.ts +18 -0
- package/dist/session/idle-evictor.d.ts.map +1 -0
- package/dist/session/idle-evictor.js +31 -0
- package/dist/session/idle-evictor.js.map +1 -0
- package/dist/session/index.d.ts +9 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +8 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/session-manager.d.ts +97 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +624 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +130 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/session/workspace-attach.d.ts +22 -0
- package/dist/session/workspace-attach.d.ts.map +1 -0
- package/dist/session/workspace-attach.js +21 -0
- package/dist/session/workspace-attach.js.map +1 -0
- package/dist/setup.d.ts +25 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +198 -0
- package/dist/setup.js.map +1 -0
- package/dist/shared/index.d.ts +7 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +7 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/scope.d.ts +14 -0
- package/dist/shared/scope.d.ts.map +1 -0
- package/dist/shared/scope.js +12 -0
- package/dist/shared/scope.js.map +1 -0
- package/dist/shared/send-helper.d.ts +11 -0
- package/dist/shared/send-helper.d.ts.map +1 -0
- package/dist/shared/send-helper.js +13 -0
- package/dist/shared/send-helper.js.map +1 -0
- package/dist/shared/utils.d.ts +28 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/shared/utils.js +143 -0
- package/dist/shared/utils.js.map +1 -0
- package/dist/transport/attachment.d.ts +18 -0
- package/dist/transport/attachment.d.ts.map +1 -0
- package/dist/transport/attachment.js +106 -0
- package/dist/transport/attachment.js.map +1 -0
- package/dist/transport/chunker.d.ts +14 -0
- package/dist/transport/chunker.d.ts.map +1 -0
- package/dist/transport/chunker.js +78 -0
- package/dist/transport/chunker.js.map +1 -0
- package/dist/transport/events.d.ts +80 -0
- package/dist/transport/events.d.ts.map +1 -0
- package/dist/transport/events.js +62 -0
- package/dist/transport/events.js.map +1 -0
- package/dist/transport/inbound.d.ts +8 -0
- package/dist/transport/inbound.d.ts.map +1 -0
- package/dist/transport/inbound.js +292 -0
- package/dist/transport/inbound.js.map +1 -0
- package/dist/transport/index.d.ts +10 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +10 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/inject-rules.d.ts +24 -0
- package/dist/transport/inject-rules.d.ts.map +1 -0
- package/dist/transport/inject-rules.js +72 -0
- package/dist/transport/inject-rules.js.map +1 -0
- package/dist/transport/outbound-buffer.d.ts +56 -0
- package/dist/transport/outbound-buffer.d.ts.map +1 -0
- package/dist/transport/outbound-buffer.js +131 -0
- package/dist/transport/outbound-buffer.js.map +1 -0
- package/dist/transport/outbound.d.ts +30 -0
- package/dist/transport/outbound.d.ts.map +1 -0
- package/dist/transport/outbound.js +139 -0
- package/dist/transport/outbound.js.map +1 -0
- package/dist/transport/rich-media.d.ts +41 -0
- package/dist/transport/rich-media.d.ts.map +1 -0
- package/dist/transport/rich-media.js +104 -0
- package/dist/transport/rich-media.js.map +1 -0
- package/dist/transport/streaming-writer.d.ts +47 -0
- package/dist/transport/streaming-writer.d.ts.map +1 -0
- package/dist/transport/streaming-writer.js +110 -0
- package/dist/transport/streaming-writer.js.map +1 -0
- package/dist/transport/tool-presenter.d.ts +38 -0
- package/dist/transport/tool-presenter.d.ts.map +1 -0
- package/dist/transport/tool-presenter.js +121 -0
- package/dist/transport/tool-presenter.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +106 -0
- package/settings-host.js +927 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* debounce.ts — 延迟聚合回复(2026-09-05 新增; 与"群冷却"融合的终版)
|
|
3
|
+
*
|
|
4
|
+
* 要解决的问题: 用户连发 N 句, AI 只回第一句 / 上下文顺序倒置。
|
|
5
|
+
*
|
|
6
|
+
* 链路位置: 插在群冷却中间件**之前**(middleware-setup 第6.6步)。所有群消息(普通/@)与私聊
|
|
7
|
+
* 先聚合进 per-peer 窗口(peerKey = group:<groupOpenid> | c2c:<senderId>), 冷却中间件收不到被吞的消息。
|
|
8
|
+
*
|
|
9
|
+
* 窗口/触发: 有新消息(按 QQ 服务器时间戳更新)就重置计时; 静默 X 秒 或 攒满 Y 条 → 尝试派发。
|
|
10
|
+
*
|
|
11
|
+
* 派发判定(与群冷却融合):
|
|
12
|
+
* - 窗口含 @: @ 无视冷却, 随时整批派发(只多等 debounce 静默)。
|
|
13
|
+
* - 群普通(无@): 距上次普通派发不足 freeIntervalSec → 窗口保留继续攒, 冷却结束再整批
|
|
14
|
+
* 按服务器时间序一次综合回(两次"批派发"仍 ≥60s, 防刷屏语义保留; 冷却是 60s 级的、聚合是 3s 级的, 不再打架)。
|
|
15
|
+
* - 私聊: 无冷却, 直接派发。
|
|
16
|
+
*
|
|
17
|
+
* 派发内容(防倒序): 以 store(mediaHistoryBuffer 记录的全量, 含被吞消息)为权威,
|
|
18
|
+
* 与窗口合并去重后按服务器时间戳升序: current = 时间序最后一条; 窗口内更早的 @ 消息在 history 里补
|
|
19
|
+
* " (@you)" 标注(与 current 同款 @ 事实标注, 回不回由 AI 按守则判, 插件不注入回复指令);
|
|
20
|
+
* 其余按序进 [Chat history]。私聊无 store → 窗口文本按序拼接+合并附件成合成消息直连。
|
|
21
|
+
*
|
|
22
|
+
* 斜杠命令(以 / 开头)不聚合直放行: 保 /approve /bot-stop 等命令的即时性。
|
|
23
|
+
* debounce.enabled=false 或 @ 秒回(mentionDelayed=false) → 直放行, 交回下方群冷却中间件与既有链路。
|
|
24
|
+
*
|
|
25
|
+
* 配置: config.behavior.debounce { enabled, silenceSec, maxMsgs, mentionDelayed } + behavior.freeIntervalSec, 每次现读(live 热更)。
|
|
26
|
+
* ⚠️ 本地手改功能: 同步纪律同 middleware-setup.ts 内群冷却中间件(改完保持 src 与部署 dist 一致)。
|
|
27
|
+
*/
|
|
28
|
+
import type { Middleware } from '@tencent-connect/qqbot-nodejs';
|
|
29
|
+
import type { ImQQBotConfig } from '../config.js';
|
|
30
|
+
import type { SessionManager } from '../session/index.js';
|
|
31
|
+
import type { Logger } from '../types.js';
|
|
32
|
+
/**
|
|
33
|
+
* 构造延迟聚合中间件(与群冷却融合, 2026-09-05 终版)。
|
|
34
|
+
* 插在群冷却中间件**之前**: 所有群消息(普通/@)与私聊先聚合进 per-peer 窗口;
|
|
35
|
+
* flush 时群普通消息仍受 60s 冷却约束(未过则窗口保留等冷却结束整批按时间序派发),
|
|
36
|
+
* @ 消息无视冷却随时派发。
|
|
37
|
+
* cooldownAt: 与 middleware-setup 群冷却中间件共享的 lastDispatchAt(groupOpenid -> 上次普通派发 ms)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function debounceLayer(config: ImQQBotConfig, manager: SessionManager, logger: Logger, cooldownAt: Map<string, number>): Middleware;
|
|
40
|
+
//# sourceMappingURL=debounce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../src/gateway/debounce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,UAAU,EAAmC,MAAM,+BAA+B,CAAC;AACjG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAyC1C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,UAAU,CAuPZ"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { handleInbound } from '../transport/inbound.js';
|
|
2
|
+
import { getHistoryStore, historyGroupKey } from '../features/history-store.js';
|
|
3
|
+
import { appendFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const DEFAULTS = { enabled: true, silenceSec: 3, maxMsgs: 10, mentionDelayed: true };
|
|
6
|
+
/** 解析消息服务器时间戳(ISO 字符串或 ms 数字; 解析失败回落"到达时刻"兜底) */
|
|
7
|
+
function msgTs(msg) {
|
|
8
|
+
const raw = msg.timestamp;
|
|
9
|
+
const n = typeof raw === 'number' ? raw : Date.parse(String(raw ?? ''));
|
|
10
|
+
return Number.isFinite(n) ? n : Date.now();
|
|
11
|
+
}
|
|
12
|
+
function num(v, d) {
|
|
13
|
+
const n = Number(v);
|
|
14
|
+
return Number.isFinite(n) ? n : d;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 构造延迟聚合中间件(与群冷却融合, 2026-09-05 终版)。
|
|
18
|
+
* 插在群冷却中间件**之前**: 所有群消息(普通/@)与私聊先聚合进 per-peer 窗口;
|
|
19
|
+
* flush 时群普通消息仍受 60s 冷却约束(未过则窗口保留等冷却结束整批按时间序派发),
|
|
20
|
+
* @ 消息无视冷却随时派发。
|
|
21
|
+
* cooldownAt: 与 middleware-setup 群冷却中间件共享的 lastDispatchAt(groupOpenid -> 上次普通派发 ms)。
|
|
22
|
+
*/
|
|
23
|
+
export function debounceLayer(config, manager, logger, cooldownAt) {
|
|
24
|
+
const windows = new Map();
|
|
25
|
+
/** 调序调试日志(临时, 定位官方帧序用; 排查完删除): {cwd}/.qqbot/debounce-dbg.log */
|
|
26
|
+
const dbgFile = join(config.cwd || process.cwd(), '.qqbot', 'debounce-dbg.log');
|
|
27
|
+
function dbg(line) {
|
|
28
|
+
try {
|
|
29
|
+
appendFileSync(dbgFile, `${new Date().toISOString()} ${line}\n`, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
catch { /* ignore */ }
|
|
32
|
+
}
|
|
33
|
+
const cfg = () => {
|
|
34
|
+
const d = config.behavior?.debounce;
|
|
35
|
+
return d ?? DEFAULTS;
|
|
36
|
+
};
|
|
37
|
+
function clearTimer(w) {
|
|
38
|
+
if (w.timer) {
|
|
39
|
+
clearTimeout(w.timer);
|
|
40
|
+
w.timer = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** 尝试派发一批(窗口内全部消息 → AI 一次综合回复)。
|
|
44
|
+
* 群普通(无@)受 60s 冷却约束: 未过冷却 → 窗口保留、重排计时器, 冷却结束再整批按时间序派发。
|
|
45
|
+
* @ 消息(窗口含@)/私聊: 随时派发。 */
|
|
46
|
+
async function flush(key, w) {
|
|
47
|
+
const pre = w.entries;
|
|
48
|
+
if (pre.length === 0) {
|
|
49
|
+
clearTimer(w);
|
|
50
|
+
windows.delete(key);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const f0 = pre[0];
|
|
54
|
+
if (!f0) {
|
|
55
|
+
clearTimer(w);
|
|
56
|
+
windows.delete(key);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const fKind = String(f0.msg.kind ?? '');
|
|
60
|
+
const hasMention = pre.some(e => e.wasMentioned);
|
|
61
|
+
if (fKind === 'group' && !hasMention) {
|
|
62
|
+
// 群普通消息: 距上次普通派发不足 freeIntervalSec → 冷却中, 窗口保留继续攒
|
|
63
|
+
const gid = String(f0.msg.groupOpenid ?? f0.msg.senderId ?? '');
|
|
64
|
+
const freeSec = Math.max(0, num(config.behavior?.freeIntervalSec, 0));
|
|
65
|
+
if (gid && freeSec > 0) {
|
|
66
|
+
const nowMs = Date.now();
|
|
67
|
+
const lastAt = cooldownAt.get(gid) ?? 0;
|
|
68
|
+
const passedMs = nowMs - lastAt;
|
|
69
|
+
if (passedMs < freeSec * 1000) {
|
|
70
|
+
clearTimer(w);
|
|
71
|
+
const retryMs = Math.max(500, freeSec * 1000 - passedMs);
|
|
72
|
+
w.timer = setTimeout(() => void flush(key, w), retryMs);
|
|
73
|
+
w.timer.unref?.();
|
|
74
|
+
dbg(`flush 冷却中 defer ${Math.round(retryMs / 1000)}s key=${key} n=${pre.length}`);
|
|
75
|
+
return; // 窗口保留, 不派发
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
clearTimer(w);
|
|
80
|
+
windows.delete(key);
|
|
81
|
+
const entries = w.entries;
|
|
82
|
+
if (entries.length === 0)
|
|
83
|
+
return;
|
|
84
|
+
// QQ 官方推送可能乱序: 派发前统一按服务器时间戳升序排(旧→新)
|
|
85
|
+
entries.sort((a, b) => a.ts - b.ts);
|
|
86
|
+
// 触发消息形状(仅取 kind/gid 参考; 真正的 current 在群分支按时间序从 merged 里取)
|
|
87
|
+
const trigger = entries[entries.length - 1];
|
|
88
|
+
if (!trigger)
|
|
89
|
+
return;
|
|
90
|
+
const kind = String(trigger.msg.kind ?? '');
|
|
91
|
+
dbg(`flush key=${key} n=${entries.length} sorted-order=[${entries.map(e => JSON.stringify(String(e.msg.content ?? '').slice(0, 24))).join(',')}] hasMention=${hasMention}`);
|
|
92
|
+
try {
|
|
93
|
+
if (kind === 'group') {
|
|
94
|
+
const gid = String(trigger.msg.groupOpenid ?? trigger.msg.senderId ?? '');
|
|
95
|
+
if (!gid)
|
|
96
|
+
return;
|
|
97
|
+
// 群冷却吞掉的消息也已被 mediaHistoryBuffer(链第4步, 冷却/debounce 之前)记录进 store。
|
|
98
|
+
// 因此以 store 为权威全量: 与窗口合并去重、按服务器时间序排列后,
|
|
99
|
+
// current 取"时间上真正最后一条"(窗口含@时取最后一条被@的), 其余按序进 history ——
|
|
100
|
+
// 避免 current 取成"群冷却放行的第一条"导致上下文整体倒序。
|
|
101
|
+
let storeHist = [];
|
|
102
|
+
try {
|
|
103
|
+
storeHist = await getHistoryStore().list(historyGroupKey(config.appId, gid), num(config.historyLimit, 20));
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
logger.warn?.(`[debounce] 拉群历史失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
107
|
+
}
|
|
108
|
+
dbg(` store-raw=[${storeHist.map(h => JSON.stringify(String(h.content).slice(0, 24))).join(',')}]`);
|
|
109
|
+
const merged = storeHist.map(h => ({
|
|
110
|
+
messageId: h.messageId,
|
|
111
|
+
ts: h.timestamp || 0,
|
|
112
|
+
content: h.content,
|
|
113
|
+
senderId: h.senderId,
|
|
114
|
+
senderName: h.senderName,
|
|
115
|
+
}));
|
|
116
|
+
for (const e of entries) {
|
|
117
|
+
const id = String(e.msg.messageId ?? '');
|
|
118
|
+
const hit = merged.find(m => m.messageId === id);
|
|
119
|
+
if (hit) {
|
|
120
|
+
if (e.wasMentioned)
|
|
121
|
+
hit.wasMentioned = true;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
merged.push({ messageId: id, ts: e.ts, msg: e.msg, wasMentioned: e.wasMentioned });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
merged.sort((a, b) => a.ts - b.ts);
|
|
128
|
+
// current 恒取时间序最后一条(纯时间序, 上下文永不倒置)。
|
|
129
|
+
const cur = merged[merged.length - 1];
|
|
130
|
+
if (!cur)
|
|
131
|
+
return;
|
|
132
|
+
// 忠实还原 store 原文进 history; 窗口内曾 @ 机器人的消息(时间上早于 current)
|
|
133
|
+
// 补 " (@you)" 标注 —— 与 current 的 (@you) 同款格式, 还原"这条@了bot"的事实,
|
|
134
|
+
// 由 AI 按自身守则决定是否开口(插件不注入回复指令, 保持通用)。
|
|
135
|
+
const hist = merged
|
|
136
|
+
.filter(m => m.messageId !== cur.messageId)
|
|
137
|
+
.map(m => {
|
|
138
|
+
const baseContent = m.content ?? String(m.msg?.content ?? '');
|
|
139
|
+
return {
|
|
140
|
+
senderId: m.senderId ?? '',
|
|
141
|
+
senderName: m.senderName,
|
|
142
|
+
content: m.wasMentioned ? `${baseContent} (@you)` : baseContent,
|
|
143
|
+
timestamp: m.ts,
|
|
144
|
+
messageId: m.messageId,
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
// current 消息: 窗口条目有完整 msg; store 来源的只有 HistoryEntry 字段,
|
|
148
|
+
// 从群窗口消息继承 kind/groupOpenid/attachments 等再补齐本人字段。
|
|
149
|
+
const curMsg = cur.msg ?? {
|
|
150
|
+
...(entries[entries.length - 1] ?? trigger).msg,
|
|
151
|
+
messageId: cur.messageId,
|
|
152
|
+
content: cur.content ?? '',
|
|
153
|
+
senderId: cur.senderId,
|
|
154
|
+
senderName: cur.senderName,
|
|
155
|
+
timestamp: new Date(cur.ts).toISOString(),
|
|
156
|
+
};
|
|
157
|
+
const state = { history: hist };
|
|
158
|
+
if (cur.wasMentioned) {
|
|
159
|
+
// current 本身就是 @ 消息 → 走正常 mention, AI 见 (@you)
|
|
160
|
+
state.mention = { wasMentioned: true };
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
state.batchDispatch = true;
|
|
164
|
+
}
|
|
165
|
+
dbg(` merged-order=[${merged.map(m => JSON.stringify(String(m.content ?? m.msg?.content ?? '').slice(0, 16))).join(',')}] cur=${JSON.stringify(String(cur.content ?? cur.msg?.content ?? '').slice(0, 16))} hasMention=${hasMention}`);
|
|
166
|
+
// 群普通(无@)批派发 → 记冷却(与群冷却中间件共享 lastDispatchAt, 防刷屏语义保留)
|
|
167
|
+
if (!hasMention)
|
|
168
|
+
cooldownAt.set(gid, Date.now());
|
|
169
|
+
logger.info(`[debounce] flush(group ${gid}) 窗口${entries.length}条/store${storeHist.length}条 → handleInbound`);
|
|
170
|
+
await handleInbound(curMsg, manager, config, logger, state);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// 私聊: 无群历史缓冲, 窗口自缓冲 → 文本按序拼接 + 合并附件成一条合成消息
|
|
174
|
+
const textLines = entries
|
|
175
|
+
.map(e => String(e.msg.content ?? '').trim())
|
|
176
|
+
.filter(Boolean);
|
|
177
|
+
const allAtts = [];
|
|
178
|
+
for (const e of entries) {
|
|
179
|
+
const a = e.msg.attachments;
|
|
180
|
+
if (Array.isArray(a) && a.length > 0)
|
|
181
|
+
allAtts.push(...a);
|
|
182
|
+
}
|
|
183
|
+
const lastEntry = entries[entries.length - 1];
|
|
184
|
+
const base = (lastEntry ?? trigger).msg;
|
|
185
|
+
const merged = {
|
|
186
|
+
...base,
|
|
187
|
+
content: textLines.join('\n'),
|
|
188
|
+
attachments: allAtts.length > 0 ? allAtts : undefined,
|
|
189
|
+
};
|
|
190
|
+
logger.info(`[debounce] flush(c2c ${String(base.senderId ?? '')}) ${entries.length}条 → handleInbound`);
|
|
191
|
+
await handleInbound(merged, manager, config, logger, undefined);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
logger.error(`[debounce] flush 失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return async (ctx, next) => {
|
|
199
|
+
const c = cfg();
|
|
200
|
+
if (!c.enabled)
|
|
201
|
+
return next();
|
|
202
|
+
const msg = ctx.message;
|
|
203
|
+
const kind = String(msg.kind ?? '');
|
|
204
|
+
if (kind !== 'group' && kind !== 'c2c')
|
|
205
|
+
return next();
|
|
206
|
+
// 斜杠命令不聚合: 保 /approve、/bot-stop、/help 等即时响应
|
|
207
|
+
const content = String(msg.content ?? '').trim();
|
|
208
|
+
if (content.startsWith('/'))
|
|
209
|
+
return next();
|
|
210
|
+
const wasMentioned = ctx.state?.mention?.wasMentioned === true;
|
|
211
|
+
// @ 可配置为不走延迟(秒回)
|
|
212
|
+
if (wasMentioned && c.mentionDelayed === false)
|
|
213
|
+
return next();
|
|
214
|
+
const peerId = kind === 'group'
|
|
215
|
+
? (msg.groupOpenid ?? msg.senderId)
|
|
216
|
+
: msg.senderId;
|
|
217
|
+
if (!peerId)
|
|
218
|
+
return next();
|
|
219
|
+
const key = `${kind}:${String(peerId)}`;
|
|
220
|
+
const silenceMs = Math.max(0, num(c.silenceSec, DEFAULTS.silenceSec)) * 1000;
|
|
221
|
+
const maxMsgs = Math.max(1, Math.floor(num(c.maxMsgs, DEFAULTS.maxMsgs)));
|
|
222
|
+
if (silenceMs <= 0)
|
|
223
|
+
return next(); // 0=不停顿: 保持原链路立即派发
|
|
224
|
+
let w = windows.get(key);
|
|
225
|
+
if (!w) {
|
|
226
|
+
w = { entries: [], timer: null };
|
|
227
|
+
windows.set(key, w);
|
|
228
|
+
}
|
|
229
|
+
// 防御: 窗口保留等冷却期间可能持续进消息 → 超上限丢最老(只留最近 100 条)
|
|
230
|
+
if (w.entries.length >= 100) {
|
|
231
|
+
w.entries.splice(0, w.entries.length - 80);
|
|
232
|
+
}
|
|
233
|
+
const ts = msgTs(msg);
|
|
234
|
+
const arrivedAt = Date.now();
|
|
235
|
+
// 到达日志: 记录"到达本中间件的时刻"+"服务器时间戳", 用于对比官方帧序与真实发送序
|
|
236
|
+
dbg(`arrive key=${key} at=${arrivedAt} ts=${String(msg.timestamp ?? '')} parsedTs=${ts} id=${String(msg.messageId ?? '').slice(0, 14)} mention=${wasMentioned} content=${JSON.stringify(content.slice(0, 30))}`);
|
|
237
|
+
// 只有"比窗口现有消息更新"的消息才算最近说话者继续开口 → 重置静默计时;
|
|
238
|
+
// 乱序补到的旧消息(服务器时间更早)只入窗口, 不延长等待。
|
|
239
|
+
let maxTs = 0;
|
|
240
|
+
for (let i = 0; i < w.entries.length; i++) {
|
|
241
|
+
const e = w.entries[i];
|
|
242
|
+
if (e && e.ts > maxTs)
|
|
243
|
+
maxTs = e.ts;
|
|
244
|
+
}
|
|
245
|
+
const isNewer = ts >= maxTs;
|
|
246
|
+
w.entries.push({ msg: { ...msg }, wasMentioned, ts });
|
|
247
|
+
dbg(` win=${w.entries.length} new=${isNewer} order=[${w.entries.map(e => JSON.stringify(String(e.msg.content ?? '').slice(0, 16))).join(',')}]`);
|
|
248
|
+
if (isNewer) {
|
|
249
|
+
// 最近说话者(按服务器时间)刚又开口 → 重新等 ta 停口 silenceMs
|
|
250
|
+
clearTimer(w);
|
|
251
|
+
w.timer = setTimeout(() => void flush(key, w), silenceMs);
|
|
252
|
+
w.timer.unref?.();
|
|
253
|
+
}
|
|
254
|
+
// 攒满上限立即发, 不等对方停
|
|
255
|
+
if (w.entries.length >= maxMsgs) {
|
|
256
|
+
void flush(key, w);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// 吞掉本消息(不调 next): 群消息已被链第4步 mediaHistoryBuffer 记录, 延迟到 flush 直连派发
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.js","sourceRoot":"","sources":["../../src/gateway/debounce.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAUjC,MAAM,QAAQ,GAA0B,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;AAe5G,iDAAiD;AACjD,SAAS,KAAK,CAAC,GAA4B;IACzC,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,GAAG,CAAC,CAAU,EAAE,CAAS;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAqB,EACrB,OAAuB,EACvB,MAAc,EACd,UAA+B;IAE/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD,gEAAgE;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IAChF,SAAS,GAAG,CAAC,IAAY;QACvB,IAAI,CAAC;YACH,cAAc,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3E,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,GAAG,GAAG,GAAgB,EAAE;QAC5B,MAAM,CAAC,GAAI,MAAM,CAAC,QAAuC,EAAE,QAAQ,CAAC;QACpE,OAAO,CAAC,IAAI,QAAQ,CAAC;IACvB,CAAC,CAAC;IAEF,SAAS,UAAU,CAAC,CAAiB;QACnC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IAED;;+BAE2B;IAC3B,KAAK,UAAU,KAAK,CAAC,GAAW,EAAE,CAAiB;QACjD,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC;QACtB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,UAAU,CAAC,CAAC,CAAC,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,UAAU,CAAC,CAAC,CAAC,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,KAAK,KAAK,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;YACrC,kDAAkD;YAClD,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YAChE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAE,MAAM,CAAC,QAA0C,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC;YACzG,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACzB,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACxC,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;gBAChC,IAAI,QAAQ,GAAG,OAAO,GAAG,IAAI,EAAE,CAAC;oBAC9B,UAAU,CAAC,CAAC,CAAC,CAAC;oBACd,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,QAAQ,CAAC,CAAC;oBACzD,CAAC,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;oBACxD,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;oBAClB,GAAG,CAAC,mBAAmB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;oBACjF,OAAO,CAAC,YAAY;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QACD,UAAU,CAAC,CAAC,CAAC,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpB,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC1B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEjC,oCAAoC;QACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QAEpC,0DAA0D;QAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC5C,GAAG,CAAC,aAAa,GAAG,MAAM,OAAO,CAAC,MAAM,kBAAkB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,UAAU,EAAE,CAAC,CAAC;QAE5K,IAAI,CAAC;YACH,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACrB,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;gBAC1E,IAAI,CAAC,GAAG;oBAAE,OAAO;gBACjB,iEAAiE;gBACjE,uCAAuC;gBACvC,wDAAwD;gBACxD,qCAAqC;gBACrC,IAAI,SAAS,GAAmB,EAAE,CAAC;gBACnC,IAAI,CAAC;oBACH,SAAS,GAAG,MAAM,eAAe,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC7G,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,EAAE,CAAC,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC1F,CAAC;gBACD,GAAG,CAAC,gBAAgB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAWrG,MAAM,MAAM,GAAiB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAC/C,SAAS,EAAE,CAAC,CAAC,SAAS;oBACtB,EAAE,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC;oBACpB,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;oBACpB,UAAU,EAAE,CAAC,CAAC,UAAU;iBACzB,CAAC,CAAC,CAAC;gBACJ,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;oBACzC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC;oBACjD,IAAI,GAAG,EAAE,CAAC;wBACR,IAAI,CAAC,CAAC,YAAY;4BAAE,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC;oBAC9C,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;oBACrF,CAAC;gBACH,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBAEnC,oCAAoC;gBACpC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBACtC,IAAI,CAAC,GAAG;oBAAE,OAAO;gBAEjB,uDAAuD;gBACvD,6DAA6D;gBAC7D,qCAAqC;gBACrC,MAAM,IAAI,GAAmB,MAAM;qBAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,SAAS,CAAC;qBAC1C,GAAG,CAAC,CAAC,CAAC,EAAE;oBACP,MAAM,WAAW,GAAG,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;oBAC9D,OAAO;wBACL,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE;wBAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;wBACxB,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,WAAW,SAAS,CAAC,CAAC,CAAC,WAAW;wBAC/D,SAAS,EAAE,CAAC,CAAC,EAAE;wBACf,SAAS,EAAE,CAAC,CAAC,SAAS;qBACvB,CAAC;gBACJ,CAAC,CAAC,CAAC;gBAEL,wDAAwD;gBACxD,kDAAkD;gBAClD,MAAM,MAAM,GAA4B,GAAG,CAAC,GAAG,IAAI;oBACjD,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG;oBAC/C,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE;oBAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE;iBAC1C,CAAC;gBACF,MAAM,KAAK,GAA4B,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBACzD,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;oBACrB,+CAA+C;oBAC/C,KAAK,CAAC,OAAO,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;gBACzC,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC7B,CAAC;gBACD,GAAG,CAAC,mBAAmB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,UAAU,EAAE,CAAC,CAAC;gBACxO,sDAAsD;gBACtD,IAAI,CAAC,UAAU;oBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,0BAA0B,GAAG,OAAO,OAAO,CAAC,MAAM,UAAU,SAAS,CAAC,MAAM,mBAAmB,CAAC,CAAC;gBAC7G,MAAM,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;YAC9D,CAAC;iBAAM,CAAC;gBACN,2CAA2C;gBAC3C,MAAM,SAAS,GAAG,OAAO;qBACtB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;qBAC5C,MAAM,CAAC,OAAO,CAAC,CAAC;gBACnB,MAAM,OAAO,GAAc,EAAE,CAAC;gBAC9B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,MAAM,CAAC,GAAI,CAAC,CAAC,GAAmC,CAAC,WAAW,CAAC;oBAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;wBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3D,CAAC;gBACD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC9C,MAAM,IAAI,GAAG,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC;gBACxC,MAAM,MAAM,GAA4B;oBACtC,GAAG,IAAI;oBACP,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;oBAC7B,WAAW,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;iBACtD,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,mBAAmB,CAAC,CAAC;gBACvG,MAAM,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,OAAO,KAAK,EAAE,GAAsB,EAAE,IAAyB,EAAiB,EAAE;QAChF,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC,CAAC,OAAO;YAAE,OAAO,IAAI,EAAE,CAAC;QAE9B,MAAM,GAAG,GAAG,GAAG,CAAC,OAA6C,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACpC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,EAAE,CAAC;QAEtD,4CAA4C;QAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAE3C,MAAM,YAAY,GACf,GAAG,CAAC,KAAkD,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;QAC1F,iBAAiB;QACjB,IAAI,YAAY,IAAI,CAAC,CAAC,cAAc,KAAK,KAAK;YAAE,OAAO,IAAI,EAAE,CAAC;QAE9D,MAAM,MAAM,GAAG,IAAI,KAAK,OAAO;YAC7B,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,QAAQ,CAAC;YACnC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;QACjB,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAExC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC;QAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,SAAS,IAAI,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC,CAAC,mBAAmB;QAEtD,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACtB,CAAC;QACD,4CAA4C;QAC5C,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YAC5B,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,+CAA+C;QAC/C,GAAG,CAAC,cAAc,GAAG,OAAO,SAAS,OAAO,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,aAAa,EAAE,OAAO,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,YAAY,YAAY,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACjN,wCAAwC;QACxC,gCAAgC;QAChC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,KAAK;gBAAE,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QACtC,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,IAAI,KAAK,CAAC;QAC5B,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QACtD,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,QAAQ,OAAO,WAAW,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClJ,IAAI,OAAO,EAAE,CAAC;YACZ,0CAA0C;YAC1C,UAAU,CAAC,CAAC,CAAC,CAAC;YACd,CAAC,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,GAAG,EAAE,CAAmB,CAAC,EAAE,SAAS,CAAC,CAAC;YAC5E,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QACpB,CAAC;QAED,iBAAiB;QACjB,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;YAChC,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QACD,kEAAkE;IACpE,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK 中间件编排
|
|
3
|
+
*
|
|
4
|
+
* 根据插件配置组装 SDK 内置中间件链(洋葱模型)。
|
|
5
|
+
* 中间件负责过滤与上下文富化;最终消息统一由 bot.on('message') 处理转发。
|
|
6
|
+
*/
|
|
7
|
+
import type { QQBot } from '@tencent-connect/qqbot-nodejs';
|
|
8
|
+
import type { ImQQBotConfig } from '../config.js';
|
|
9
|
+
import type { SessionManager } from '../session/index.js';
|
|
10
|
+
import type { Logger } from '../types.js';
|
|
11
|
+
export declare function setupMiddlewares(bot: QQBot, config: ImQQBotConfig, manager: SessionManager, logger: Logger): void;
|
|
12
|
+
//# sourceMappingURL=middleware-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-setup.d.ts","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,+BAA+B,CAAC;AAa9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAY1C,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,KAAK,EACV,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAgKN"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { errorHandler, messageFilter, accessPolicy, mentionGate, contentSanitizer, slashCommand, concurrencyGuard, typingIndicator, quoteRef, envelopeFormatter, } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import { buildCommandList } from '../commands/index.js';
|
|
3
|
+
import { attachmentProcessor } from '../middleware/attachment.js';
|
|
4
|
+
import { mediaHistoryBuffer } from '../middleware/media-history.js';
|
|
5
|
+
import { stickerCapture } from '../middleware/sticker-capture.js';
|
|
6
|
+
import { getHistoryStore, historyGroupKey } from '../features/history-store.js';
|
|
7
|
+
import { stickerActivityRecorder } from '../features/sticker-gate.js';
|
|
8
|
+
import { chatLedgerRecorder } from '../features/chat-ledger.js';
|
|
9
|
+
import { debounceLayer } from './debounce.js';
|
|
10
|
+
import { faceTagResolver } from '../features/face-tags.js';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
export function setupMiddlewares(bot, config, manager, logger) {
|
|
13
|
+
// 1. 错误兜底(最外层洋葱皮)
|
|
14
|
+
bot.use(errorHandler());
|
|
15
|
+
// 1.2 表情标签名字解析(本地实现, 2026-09-06): 必须挂在**最顶层**(任何历史/缓冲之前)——
|
|
16
|
+
// mediaHistoryBuffer(第4步)会把 content 原文存进群历史, AI 上下文读的是历史里的文本,
|
|
17
|
+
// 若解析放后面, 历史里存的就是 <faceType=...> 原文, AI 永远看不到名字。
|
|
18
|
+
// 这里把 <faceType=N,faceId="X",ext="b64"> / [<face,id=N/>] 转成【表情: 名字】:
|
|
19
|
+
// ext.text 优先(官方名字最准), 空则按 faceId 查 QQ 经典表情名表(0=微笑…160+),
|
|
20
|
+
// SDK contentSanitizer(parseFaceTags) 只解 ext.text, text 空会丢 faceId → 本层先吃掉标签。
|
|
21
|
+
bot.use(faceTagResolver());
|
|
22
|
+
// 1.5 消息串行闸(本地手改, 2026-09-05):
|
|
23
|
+
// SDK 收帧处 `void Promise.resolve(onMessage(...))` 不等待 → 快速连发时多条消息的
|
|
24
|
+
// 中间件链并发执行、在各 await 处交错 → 到达下游(历史缓冲/debounce/派发)的顺序
|
|
25
|
+
// ≠ 官方推送顺序(即真实发送序)。这里把每条消息的整条下游链排队串行:
|
|
26
|
+
// 前一条消息处理完才放行下一条, 恢复官方帧序。零 SDK 改动, 只作用于本 bot 实例。
|
|
27
|
+
// ⚠️ 本地手改功能, 同步纪律同群冷却中间件。
|
|
28
|
+
// ── 群普通消息"上次派发时刻"(冷却中间件与 debounce 融合层共享) ──
|
|
29
|
+
const lastDispatchAt = new Map(); // groupOpenid -> 上次普通派发时间(ms)
|
|
30
|
+
let inboundTail = Promise.resolve();
|
|
31
|
+
bot.use(async (_ctx, next) => {
|
|
32
|
+
const run = inboundTail.then(() => next());
|
|
33
|
+
inboundTail = run.catch(() => undefined);
|
|
34
|
+
await run;
|
|
35
|
+
});
|
|
36
|
+
// 2. 消息过滤:bot 回声 + 消息去重
|
|
37
|
+
bot.use(messageFilter({ skipSelfEcho: false }));
|
|
38
|
+
// 3. 访问控制(白名单/开放/禁用)
|
|
39
|
+
bot.use(accessPolicy({
|
|
40
|
+
c2c: {
|
|
41
|
+
mode: config.access.c2cMode,
|
|
42
|
+
allow: config.access.c2cAllow.length > 0 ? config.access.c2cAllow : ['*'],
|
|
43
|
+
},
|
|
44
|
+
group: {
|
|
45
|
+
mode: config.access.groupMode,
|
|
46
|
+
allow: config.access.groupAllow.length > 0 ? config.access.groupAllow : ['*'],
|
|
47
|
+
},
|
|
48
|
+
onBlock: (_mCtx, reason) => {
|
|
49
|
+
if (config.debug) {
|
|
50
|
+
logger.debug(`Access blocked: ${reason}`);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
}));
|
|
54
|
+
// 4. 群历史缓冲 — 放在门控之前,确保所有消息(含未 @bot)都计入上下文
|
|
55
|
+
// store 走共享单例(getHistoryStore),groupKey 带 appId 前缀,供回复后清空
|
|
56
|
+
// ⚠️ 用增强版 mediaHistoryBuffer(本地手改,替代 SDK historyBuffer):
|
|
57
|
+
// 记历史时把带 URL 的附件折叠进 content,冷却期群友发的图/媒体在历史里也带链接
|
|
58
|
+
bot.use(mediaHistoryBuffer({
|
|
59
|
+
limit: config.historyLimit,
|
|
60
|
+
store: getHistoryStore(),
|
|
61
|
+
recordOnSkip: true,
|
|
62
|
+
groupKey: (ctx) => {
|
|
63
|
+
const gid = ctx.message.groupOpenid;
|
|
64
|
+
if (ctx.message.kind !== 'group' || !gid)
|
|
65
|
+
return undefined;
|
|
66
|
+
return historyGroupKey(config.appId, gid);
|
|
67
|
+
},
|
|
68
|
+
}));
|
|
69
|
+
// 4.5. 表情包自动收藏(P0):群图片 → 本地图库(fire-and-forget,不阻塞主链)
|
|
70
|
+
bot.use(stickerCapture(config, logger));
|
|
71
|
+
// 4.6. 表情包闸门活性计数(P1):记录所有群消息时间戳 → RingBuffer,供"热闹才发"判定。
|
|
72
|
+
// ⚠️ 必须在 mentionGate(第5步) 之前:未@消息被门控截断也照样计数。
|
|
73
|
+
// 多账号: 每实例传自己 dataDir(各号各闸门状态)。
|
|
74
|
+
bot.use(stickerActivityRecorder(config.sticker.dataDir || join(config.cwd || process.cwd(), '表情包')));
|
|
75
|
+
// 4.7. 聊天台账(M3):记录机器人见过的群/私聊(带昵称) → known-chats.jsonl,
|
|
76
|
+
// 供 Web ④区定时唤醒"点选聊天对象"(不用抄 openid)。也在 mentionGate 之前。
|
|
77
|
+
const ledgerDataDir = config.sticker.dataDir || join(config.cwd || process.cwd(), '表情包');
|
|
78
|
+
bot.use(chatLedgerRecorder(ledgerDataDir));
|
|
79
|
+
// 5. 群聊 @bot 门控
|
|
80
|
+
bot.use(mentionGate({
|
|
81
|
+
requireMentionInGroup: config.requireMention,
|
|
82
|
+
}));
|
|
83
|
+
// 6. 内容清洗(去 @marker、表情标签、多余空白)
|
|
84
|
+
bot.use(contentSanitizer({
|
|
85
|
+
parseFaceTags: true,
|
|
86
|
+
}));
|
|
87
|
+
// 6.6 延迟聚合(debounce)融合层(本地手改, 2026-09-05):在群冷却**之前**拦截。
|
|
88
|
+
// 消息(群普通/@ + 私聊)先进 per-peer 窗口攒着(不进冷却中间件), 最近说话者停口 X 秒
|
|
89
|
+
// 或攒满 Y 条 → 尝试派发一次: 群普通受 60s 冷却约束(未过则窗口保留继续等, 到点整批
|
|
90
|
+
// 按服务器时间序综合回, 不再出现"current 早于 history"的倒序); @ 消息无视冷却随时派发。
|
|
91
|
+
// debounce.enabled=false 或 @ 秒回(mentionDelayed=false)/斜杠命令 → 直放行, 交下方群冷却中间件
|
|
92
|
+
// 与既有链路处理(原 60s 冷却语义完整保留)。配置 config.behavior.debounce(live 热更)。
|
|
93
|
+
// ⚠️ 本地手改功能, 同步纪律同下方群冷却中间件。
|
|
94
|
+
bot.use(debounceLayer(config, manager, logger, lastDispatchAt));
|
|
95
|
+
// 7. 回复冷却调度(配置化, 替代写死 REPLY_COOLDOWN_MS=60000):照常接收并记录所有群消息
|
|
96
|
+
// (mediaHistoryBuffer 已在上方记录), 但每群按 config.behavior 间隔才真正派发一次给 AI。
|
|
97
|
+
// 注: debounce 开启时群消息被其拦截, 此中间件仅兜底 debounce 直放行的消息(命令/@秒回/功能关闭)。
|
|
98
|
+
// ⚠️ 本地手改功能(曾被重编译冲掉),改完务必保持 src 与部署 dist 同步。
|
|
99
|
+
// ⚠️ 每次调用现读 config.behavior(勿外层解构)——支持 Web settings live 热更新。
|
|
100
|
+
bot.use(async (ctx, next) => {
|
|
101
|
+
// 私聊/无群号:不纳入调度,直接放行
|
|
102
|
+
if (ctx.message.kind !== 'group' || !ctx.message.groupOpenid) {
|
|
103
|
+
return next();
|
|
104
|
+
}
|
|
105
|
+
const { freeIntervalSec, mentionIntervalSec } = config.behavior;
|
|
106
|
+
const gid = ctx.message.groupOpenid;
|
|
107
|
+
const wasMentioned = ctx.state.mention?.wasMentioned === true;
|
|
108
|
+
const intervalSec = wasMentioned ? mentionIntervalSec : freeIntervalSec;
|
|
109
|
+
// 适用间隔=0:不冷却直接放行(@立即回;无@放行后是否到 AI 由 requireMention 门控决定)
|
|
110
|
+
if (intervalSec <= 0) {
|
|
111
|
+
return next();
|
|
112
|
+
}
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
const last = lastDispatchAt.get(gid) ?? 0;
|
|
115
|
+
if (now - last < intervalSec * 1000) {
|
|
116
|
+
// 冷却期内:消息已被 mediaHistoryBuffer 记进群历史,本次不派发 → 不调 next()
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// 冷却结束:派发当前消息。用独立的 batchDispatch 标记,让下游把累积的群历史一起打包
|
|
120
|
+
//(不伪装成 @you,避免出现假 (@you) 标签误导 AI)
|
|
121
|
+
lastDispatchAt.set(gid, now);
|
|
122
|
+
ctx.state.batchDispatch = true;
|
|
123
|
+
return next();
|
|
124
|
+
});
|
|
125
|
+
// 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
|
|
126
|
+
const slash = slashCommand({
|
|
127
|
+
autoHelp: true,
|
|
128
|
+
commands: buildCommandList({ manager, config }),
|
|
129
|
+
});
|
|
130
|
+
bot.use(slash.middleware);
|
|
131
|
+
// 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
|
|
132
|
+
bot.use(concurrencyGuard({
|
|
133
|
+
strategy: 'merge',
|
|
134
|
+
maxQueue: config.maxQueue,
|
|
135
|
+
maxProcessingMs: config.processingTimeoutMs,
|
|
136
|
+
urgentPredicate: (mCtx) => {
|
|
137
|
+
return (mCtx.message.content ?? '').trim() === '/bot-stop';
|
|
138
|
+
},
|
|
139
|
+
}));
|
|
140
|
+
// 10. C2C 输入状态指示
|
|
141
|
+
bot.use(typingIndicator());
|
|
142
|
+
// 11. 引用消息解析(记录 + 解析被引用原文)
|
|
143
|
+
bot.use(quoteRef({
|
|
144
|
+
maxSize: 500,
|
|
145
|
+
preferMsgElements: true,
|
|
146
|
+
}));
|
|
147
|
+
// 11.5. 附件下载(file 附件下载到本地,供 @提及 / 工具访问)
|
|
148
|
+
bot.use(attachmentProcessor(config, logger));
|
|
149
|
+
// 12. 上下文组装(将 history + quote + sender 组成 envelope)
|
|
150
|
+
bot.use(envelopeFormatter({
|
|
151
|
+
historyLimit: config.historyLimit,
|
|
152
|
+
includeQuote: true,
|
|
153
|
+
includeSender: true,
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=middleware-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-setup.js","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-im-qqbot — QQ Bot IM channel plugin for deepseek-harness
|
|
3
|
+
*
|
|
4
|
+
* Cordis 插件入口。将 QQ 消息平台作为 dsh 的前端协议驱动。
|
|
5
|
+
* 网关组装(中间件编排 + 事件 + 出站 + 生命周期)见 src/gateway/。
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import { type ImQQBotConfig } from './config.js';
|
|
9
|
+
export declare const name = "im-qqbot";
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
export declare const Config: import("@deepseek-ai/schemastery").default<ImQQBotConfig>;
|
|
12
|
+
export type { ImQQBotConfig } from './config.js';
|
|
13
|
+
export declare function apply(ctx: Context, config: ImQQBotConfig): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAA2D,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAe1G,eAAO,MAAM,IAAI,aAAa,CAAC;AAC/B,eAAO,MAAM,MAAM,UAAa,CAAC;AACjC,eAAO,MAAM,MAAM,2DAAe,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA4E9E"}
|