@tencent-connect/dsh-qqbot 0.2.0 → 0.3.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/CHANGELOG.md +39 -0
- package/dist/config.d.ts +4 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- 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/gateway/bootstrap.d.ts +12 -0
- package/dist/gateway/bootstrap.d.ts.map +1 -0
- package/dist/gateway/bootstrap.js +71 -0
- package/dist/gateway/bootstrap.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 +80 -0
- package/dist/gateway/middleware-setup.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -128
- package/dist/index.js.map +1 -1
- 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/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +4 -5
- package/dist/session/session-manager.js.map +1 -1
- package/dist/setup.js +6 -6
- package/dist/setup.js.map +1 -1
- 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/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 +1 -1
- package/dist/transport/inbound.d.ts.map +1 -1
- package/dist/transport/inbound.js +39 -14
- package/dist/transport/inbound.js.map +1 -1
- package/dist/transport/outbound-buffer.d.ts +12 -7
- package/dist/transport/outbound-buffer.d.ts.map +1 -1
- package/dist/transport/outbound-buffer.js +20 -22
- package/dist/transport/outbound-buffer.js.map +1 -1
- package/dist/transport/outbound.d.ts +7 -6
- package/dist/transport/outbound.d.ts.map +1 -1
- package/dist/transport/outbound.js +116 -111
- package/dist/transport/outbound.js.map +1 -1
- 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/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本文件从产品视角记录 dsh-qqbot 的重要更新,聚焦用户可感知的变化。
|
|
4
|
+
|
|
5
|
+
格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - 2026-08-15
|
|
8
|
+
|
|
9
|
+
### 新功能
|
|
10
|
+
|
|
11
|
+
- **文件理解**:给机器人发送文件,它会自动下载并理解文件内容,支持解压、读取、分析等操作。
|
|
12
|
+
- **流式回复**:回复内容实时逐段显示,无需等待全部生成,体验更流畅自然。
|
|
13
|
+
- **过程可见**:机器人执行任务(运行命令、读写文件)时,用户可以看到它在做什么,不再是一个"黑盒"。
|
|
14
|
+
- **出错提示**:任务异常结束时,机器人会明确告知错误原因,不再出现"半截回复"或"毫无反应"。
|
|
15
|
+
|
|
16
|
+
### 体验优化
|
|
17
|
+
|
|
18
|
+
- 群聊中正确显示发言者昵称,不再显示一串看不懂的字符。
|
|
19
|
+
- 群聊回复后自动清理历史上下文,避免多轮对话相互干扰。
|
|
20
|
+
- 语音消息识别后只保留文字,不再附带无效链接。
|
|
21
|
+
- 机器人现在会"真正动手"了:面对「下载、解压、安装」这类请求,会用工具实际执行,而不是只复述操作步骤。
|
|
22
|
+
|
|
23
|
+
### 问题修复
|
|
24
|
+
|
|
25
|
+
- 修复机器人无法读写文件的问题(此前工作目录配置错误)。
|
|
26
|
+
- 修复流式回复中可能出现的重复内容、分片过少等显示问题。
|
|
27
|
+
|
|
28
|
+
## [0.1.0] - 2026-08-14
|
|
29
|
+
|
|
30
|
+
### 新功能
|
|
31
|
+
|
|
32
|
+
- **首个版本**:QQ 机器人接入 AI 助手,支持私聊与群聊对话。
|
|
33
|
+
- **模型切换**:支持切换不同的 AI 模型。
|
|
34
|
+
- **常用指令**:内置帮助、状态查询、会话管理等指令。
|
|
35
|
+
- **扫码绑定**:首次使用扫码即可完成机器人绑定,配置简单。
|
|
36
|
+
|
|
37
|
+
### 问题修复
|
|
38
|
+
|
|
39
|
+
- 修复配置解析问题。
|
package/dist/config.d.ts
CHANGED
|
@@ -23,8 +23,8 @@ export interface ImQQBotConfig {
|
|
|
23
23
|
model?: string;
|
|
24
24
|
/** Agent preset id */
|
|
25
25
|
preset?: string;
|
|
26
|
-
/** Agent
|
|
27
|
-
cwd
|
|
26
|
+
/** Agent 工作目录(缺省回落到进程 cwd) */
|
|
27
|
+
cwd?: string;
|
|
28
28
|
/** 是否启用群消息 @mention 门控 */
|
|
29
29
|
requireMention: boolean;
|
|
30
30
|
/** 群聊额外 system prompt */
|
|
@@ -33,6 +33,8 @@ export interface ImQQBotConfig {
|
|
|
33
33
|
directPrompt?: string;
|
|
34
34
|
/** 单条消息最大长度(QQ 限制约 5000 字符) */
|
|
35
35
|
textChunkLimit: number;
|
|
36
|
+
/** 是否启用流式输出(群聊始终不启用) */
|
|
37
|
+
streaming: boolean;
|
|
36
38
|
/** 每会话最大闲置时长(ms),超时自动回收 */
|
|
37
39
|
sessionIdleTimeout: number;
|
|
38
40
|
/** 并发队列最大长度 */
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,eAAe;IACf,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa;IACb,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC7C,0BAA0B;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,eAAe;IACf,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa;IACb,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC7C,0BAA0B;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,2BAA2B;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,MAAM,EAAE,mBAAmB,CAAC;IAC5B,6BAA6B;IAC7B,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW;IACX,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,aAAa,CA6B7C,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -8,11 +8,12 @@ export const ConfigSchema = Schema.object({
|
|
|
8
8
|
provider: Schema.string().description('LLM provider name'),
|
|
9
9
|
model: Schema.string().description('Model name'),
|
|
10
10
|
preset: Schema.string().description('Agent preset id'),
|
|
11
|
-
cwd: Schema.string().
|
|
11
|
+
cwd: Schema.string().description('Agent working directory'),
|
|
12
12
|
requireMention: Schema.boolean().default(true).description('群聊是否需要@bot触发'),
|
|
13
13
|
groupPrompt: Schema.string().description('群聊额外system prompt'),
|
|
14
14
|
directPrompt: Schema.string().description('私聊额外system prompt'),
|
|
15
15
|
textChunkLimit: Schema.number().default(4500).description('单条消息最大字符数'),
|
|
16
|
+
streaming: Schema.boolean().default(true).description('是否启用流式输出(群聊始终不启用)'),
|
|
16
17
|
sessionIdleTimeout: Schema.number().default(30 * 60 * 1000).description('会话闲置超时(ms)'),
|
|
17
18
|
maxQueue: Schema.number().default(20).description('并发队列最大长度'),
|
|
18
19
|
processingTimeoutMs: Schema.number().default(120000).description('处理超时(ms)'),
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HistoryStore } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
/** 获取共享历史存储(单例) */
|
|
3
|
+
export declare function getHistoryStore(): HistoryStore;
|
|
4
|
+
/** 用 appId 前缀隔离群历史(单账号下等价于 groupOpenid,保留多账号扩展) */
|
|
5
|
+
export declare function historyGroupKey(appId: string, groupId: string): string;
|
|
6
|
+
/** 清空群历史(回复后调用,避免下次 @ 时重复组包) */
|
|
7
|
+
export declare function clearGroupHistory(appId: string, groupId: string): void;
|
|
8
|
+
//# sourceMappingURL=history-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../../src/features/history-store.ts"],"names":[],"mappings":"AAQA,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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 群历史存储共享模块
|
|
3
|
+
*
|
|
4
|
+
* historyBuffer 中间件需要一个跨调用可访问的 HistoryStore 实例,
|
|
5
|
+
* 以便在回复完成后清空某群的历史缓存(避免下次 @ 时重复组包)。
|
|
6
|
+
* 对齐 openclaw-qqbot 的 features/history-store。
|
|
7
|
+
*/
|
|
8
|
+
import { MemoryHistoryStore } from '@tencent-connect/qqbot-nodejs';
|
|
9
|
+
let _store = null;
|
|
10
|
+
/** 获取共享历史存储(单例) */
|
|
11
|
+
export function getHistoryStore() {
|
|
12
|
+
if (!_store)
|
|
13
|
+
_store = new MemoryHistoryStore();
|
|
14
|
+
return _store;
|
|
15
|
+
}
|
|
16
|
+
/** 用 appId 前缀隔离群历史(单账号下等价于 groupOpenid,保留多账号扩展) */
|
|
17
|
+
export function historyGroupKey(appId, groupId) {
|
|
18
|
+
return `${appId}:${groupId}`;
|
|
19
|
+
}
|
|
20
|
+
/** 清空群历史(回复后调用,避免下次 @ 时重复组包) */
|
|
21
|
+
export function clearGroupHistory(appId, groupId) {
|
|
22
|
+
_store?.clear?.(historyGroupKey(appId, groupId));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=history-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-store.js","sourceRoot":"","sources":["../../src/features/history-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;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,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 网关组装 — 创建 bot、编排中间件、注册事件、出站、生命周期
|
|
3
|
+
*
|
|
4
|
+
* 将 QQ 消息平台作为 dsh 前端协议驱动:入站消息 → handleInbound → dsh agent,
|
|
5
|
+
* dsh session/event → createOutboundHandler → QQ 出站。
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import { type DshAgentRegistry } from '../session/index.js';
|
|
9
|
+
import type { ImQQBotConfig } from '../config.js';
|
|
10
|
+
import type { Logger } from '../types.js';
|
|
11
|
+
export declare function bootstrapGateway(ctx: Context, agents: DshAgentRegistry, config: ImQQBotConfig, logger: Logger): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +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;AAK5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAyEf"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { QQBot } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import { SessionManager } from '../session/index.js';
|
|
3
|
+
import { handleInbound, createOutboundHandler } from '../transport/index.js';
|
|
4
|
+
import { buildUserAgent } from '../shared/index.js';
|
|
5
|
+
import { setupMiddlewares } from './middleware-setup.js';
|
|
6
|
+
export async function bootstrapGateway(ctx, agents, config, logger) {
|
|
7
|
+
const manager = new SessionManager(ctx, agents, config, logger);
|
|
8
|
+
// ── 初始化 QQ Bot SDK ──
|
|
9
|
+
const userAgent = buildUserAgent();
|
|
10
|
+
const bot = new QQBot({
|
|
11
|
+
appId: config.appId,
|
|
12
|
+
appSecret: config.appSecret,
|
|
13
|
+
transport: 'websocket',
|
|
14
|
+
userAgent,
|
|
15
|
+
logger,
|
|
16
|
+
});
|
|
17
|
+
logger.info(`QQBot SDK initialized (UA: ${userAgent})`);
|
|
18
|
+
// ── 中间件链 ──
|
|
19
|
+
setupMiddlewares(bot, config, manager, logger);
|
|
20
|
+
// ── 入站:经过中间件链后的消息交给 dsh agent ──
|
|
21
|
+
bot.on('message', async (mCtx) => {
|
|
22
|
+
const msg = mCtx.message;
|
|
23
|
+
if (config.debug) {
|
|
24
|
+
logger.debug(`← message (post-middleware): ${JSON.stringify(msg, null, 2).slice(0, 500)}`);
|
|
25
|
+
}
|
|
26
|
+
await handleInbound(msg, manager, config, logger, mCtx.state);
|
|
27
|
+
});
|
|
28
|
+
// ── 出站:dsh session/event → QQ 消息 ──
|
|
29
|
+
// 获取 tools 服务(工具结果结构化展示,参考 dsh-TUI presentResult),可选
|
|
30
|
+
let toolsRegistry;
|
|
31
|
+
try {
|
|
32
|
+
toolsRegistry = ctx.get('tools');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
toolsRegistry = undefined;
|
|
36
|
+
}
|
|
37
|
+
// 发送适配器:将 QQBot 实例适配为 QQBotSender(openStream 参数形态不同)
|
|
38
|
+
const sender = {
|
|
39
|
+
sendMarkdown: (target, content) => bot.sendMarkdown(target, content),
|
|
40
|
+
openStream: (target) => bot.openStream({
|
|
41
|
+
target: {
|
|
42
|
+
scope: target.scope,
|
|
43
|
+
targetId: target.targetId,
|
|
44
|
+
msgId: target.msgId,
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
const outboundHandler = createOutboundHandler(manager, sender, config, logger, toolsRegistry);
|
|
49
|
+
ctx
|
|
50
|
+
.on('session/event', outboundHandler);
|
|
51
|
+
bot.on('error', (err) => {
|
|
52
|
+
logger.error(`bot error: ${err instanceof Error ? err.message : String(err)}`);
|
|
53
|
+
});
|
|
54
|
+
bot.on('ready', () => {
|
|
55
|
+
console.log(`[im-qqbot] Bot ready! appId=${config.appId}`);
|
|
56
|
+
});
|
|
57
|
+
// ── 生命周期 ──
|
|
58
|
+
ctx
|
|
59
|
+
.effect(() => {
|
|
60
|
+
logger.info(`Starting bot (appId=${config.appId})`);
|
|
61
|
+
bot.start().catch((err) => {
|
|
62
|
+
logger.error(`Bot start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
63
|
+
});
|
|
64
|
+
return async () => {
|
|
65
|
+
logger.info('Shutting down');
|
|
66
|
+
await manager.disposeAll();
|
|
67
|
+
bot.stop();
|
|
68
|
+
};
|
|
69
|
+
}, 'im-qqbot.lifecycle');
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/gateway/bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -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;AAe9E,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;AAK1C,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,KAAK,EACV,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAqFN"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { errorHandler, messageFilter, accessPolicy, mentionGate, contentSanitizer, rateLimiter, slashCommand, concurrencyGuard, typingIndicator, quoteRef, historyBuffer, envelopeFormatter, } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import { buildCommandList } from '../commands/index.js';
|
|
3
|
+
import { attachmentProcessor } from '../middleware/attachment.js';
|
|
4
|
+
import { getHistoryStore, historyGroupKey } from '../features/history-store.js';
|
|
5
|
+
export function setupMiddlewares(bot, config, manager, logger) {
|
|
6
|
+
// 1. 错误兜底(最外层洋葱皮)
|
|
7
|
+
bot.use(errorHandler());
|
|
8
|
+
// 2. 消息过滤:bot 回声 + 消息去重
|
|
9
|
+
bot.use(messageFilter({ skipSelfEcho: false }));
|
|
10
|
+
// 3. 访问控制(白名单/开放/禁用)
|
|
11
|
+
bot.use(accessPolicy({
|
|
12
|
+
c2c: {
|
|
13
|
+
mode: config.access.c2cMode,
|
|
14
|
+
allow: config.access.c2cAllow.length > 0 ? config.access.c2cAllow : ['*'],
|
|
15
|
+
},
|
|
16
|
+
group: {
|
|
17
|
+
mode: config.access.groupMode,
|
|
18
|
+
allow: config.access.groupAllow.length > 0 ? config.access.groupAllow : ['*'],
|
|
19
|
+
},
|
|
20
|
+
onBlock: (_mCtx, reason) => {
|
|
21
|
+
if (config.debug) {
|
|
22
|
+
logger.debug(`Access blocked: ${reason}`);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
// 4. 群历史缓冲 — 放在门控之前,确保所有消息(含未 @bot)都计入上下文
|
|
27
|
+
// store 走共享单例(getHistoryStore),groupKey 带 appId 前缀,供回复后清空
|
|
28
|
+
bot.use(historyBuffer({
|
|
29
|
+
limit: config.historyLimit,
|
|
30
|
+
store: getHistoryStore(),
|
|
31
|
+
recordOnSkip: true,
|
|
32
|
+
groupKey: (ctx) => {
|
|
33
|
+
const gid = ctx.message.groupOpenid;
|
|
34
|
+
if (ctx.message.kind !== 'group' || !gid)
|
|
35
|
+
return undefined;
|
|
36
|
+
return historyGroupKey(config.appId, gid);
|
|
37
|
+
},
|
|
38
|
+
}));
|
|
39
|
+
// 5. 群聊 @bot 门控
|
|
40
|
+
bot.use(mentionGate({
|
|
41
|
+
requireMentionInGroup: config.requireMention,
|
|
42
|
+
}));
|
|
43
|
+
// 6. 内容清洗(去 @marker、表情标签、多余空白)
|
|
44
|
+
bot.use(contentSanitizer({
|
|
45
|
+
parseFaceTags: true,
|
|
46
|
+
}));
|
|
47
|
+
// 7. 三层限流(sender / group / global)
|
|
48
|
+
bot.use(rateLimiter());
|
|
49
|
+
// 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
|
|
50
|
+
const slash = slashCommand({
|
|
51
|
+
autoHelp: true,
|
|
52
|
+
commands: buildCommandList({ manager, config }),
|
|
53
|
+
});
|
|
54
|
+
bot.use(slash.middleware);
|
|
55
|
+
// 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
|
|
56
|
+
bot.use(concurrencyGuard({
|
|
57
|
+
strategy: 'merge',
|
|
58
|
+
maxQueue: config.maxQueue,
|
|
59
|
+
maxProcessingMs: config.processingTimeoutMs,
|
|
60
|
+
urgentPredicate: (mCtx) => {
|
|
61
|
+
return (mCtx.message.content ?? '').trim() === '/bot-stop';
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
64
|
+
// 10. C2C 输入状态指示
|
|
65
|
+
bot.use(typingIndicator());
|
|
66
|
+
// 11. 引用消息解析(记录 + 解析被引用原文)
|
|
67
|
+
bot.use(quoteRef({
|
|
68
|
+
maxSize: 500,
|
|
69
|
+
preferMsgElements: true,
|
|
70
|
+
}));
|
|
71
|
+
// 11.5. 附件下载(file 附件下载到本地,供 @提及 / 工具访问)
|
|
72
|
+
bot.use(attachmentProcessor(config, logger));
|
|
73
|
+
// 12. 上下文组装(将 history + quote + sender 组成 envelope)
|
|
74
|
+
bot.use(envelopeFormatter({
|
|
75
|
+
historyLimit: config.historyLimit,
|
|
76
|
+
includeQuote: true,
|
|
77
|
+
includeSender: true,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
//# 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
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* dsh-im-qqbot — QQ Bot IM channel plugin for deepseek-harness
|
|
3
3
|
*
|
|
4
4
|
* Cordis 插件入口。将 QQ 消息平台作为 dsh 的前端协议驱动。
|
|
5
|
-
*
|
|
5
|
+
* 网关组装(中间件编排 + 事件 + 出站 + 生命周期)见 src/gateway/。
|
|
6
6
|
*/
|
|
7
7
|
import type { Context } from '@deepseek-ai/cordis';
|
|
8
8
|
import { type ImQQBotConfig } from './config.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/D,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,CAmC9E"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { QQBot, errorHandler, messageFilter, accessPolicy, mentionGate, contentSanitizer, rateLimiter, slashCommand, concurrencyGuard, typingIndicator, quoteRef, historyBuffer, envelopeFormatter, MemoryHistoryStore, } from '@tencent-connect/qqbot-nodejs';
|
|
2
1
|
import { ConfigSchema } from './config.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { buildCommandList } from './commands/index.js';
|
|
6
|
-
import { getProfileDir, resolveEnv, buildUserAgent } from './shared/index.js';
|
|
2
|
+
import { bootstrapGateway } from './gateway/index.js';
|
|
3
|
+
import { getProfileDir, resolveEnv } from './shared/index.js';
|
|
7
4
|
import { runQrSetup, persistCredentialsToProfile } from './setup.js';
|
|
8
5
|
// ── Cordis 插件元数据 ──
|
|
9
6
|
export const name = 'im-qqbot';
|
|
@@ -35,128 +32,6 @@ export async function apply(ctx, config) {
|
|
|
35
32
|
return;
|
|
36
33
|
}
|
|
37
34
|
const resolvedConfig = { ...config, appId, appSecret };
|
|
38
|
-
await
|
|
39
|
-
}
|
|
40
|
-
async function bootstrap(ctx, agents, config, logger) {
|
|
41
|
-
const manager = new SessionManager(ctx, agents, config, logger);
|
|
42
|
-
// ── 初始化 QQ Bot SDK ──
|
|
43
|
-
const userAgent = buildUserAgent();
|
|
44
|
-
const bot = new QQBot({
|
|
45
|
-
appId: config.appId,
|
|
46
|
-
appSecret: config.appSecret,
|
|
47
|
-
transport: 'websocket',
|
|
48
|
-
userAgent,
|
|
49
|
-
logger,
|
|
50
|
-
});
|
|
51
|
-
logger.info(`QQBot SDK initialized (UA: ${userAgent})`);
|
|
52
|
-
// ══════════════════════════════════════════════════════════════
|
|
53
|
-
// SDK 中间件链(洋葱模型,按执行顺序编排)
|
|
54
|
-
// ══════════════════════════════════════════════════════════════
|
|
55
|
-
// 1. 错误兜底(最外层洋葱皮)
|
|
56
|
-
bot.use(errorHandler());
|
|
57
|
-
// 2. 消息过滤:bot 回声 + 消息去重
|
|
58
|
-
bot.use(messageFilter({ skipSelfEcho: false }));
|
|
59
|
-
// 3. 访问控制(白名单/开放/禁用)
|
|
60
|
-
bot.use(accessPolicy({
|
|
61
|
-
c2c: {
|
|
62
|
-
mode: config.access.c2cMode,
|
|
63
|
-
allow: config.access.c2cAllow.length > 0 ? config.access.c2cAllow : ['*'],
|
|
64
|
-
},
|
|
65
|
-
group: {
|
|
66
|
-
mode: config.access.groupMode,
|
|
67
|
-
allow: config.access.groupAllow.length > 0 ? config.access.groupAllow : ['*'],
|
|
68
|
-
},
|
|
69
|
-
onBlock: (_mCtx, reason) => {
|
|
70
|
-
if (config.debug) {
|
|
71
|
-
logger.debug(`Access blocked: ${reason}`);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
}));
|
|
75
|
-
// 4. 群历史缓冲 — 放在门控之前,确保所有消息(含未 @bot)都计入上下文
|
|
76
|
-
const historyStore = new MemoryHistoryStore();
|
|
77
|
-
bot.use(historyBuffer({
|
|
78
|
-
limit: config.historyLimit,
|
|
79
|
-
store: historyStore,
|
|
80
|
-
recordOnSkip: true,
|
|
81
|
-
}));
|
|
82
|
-
// 5. 群聊 @bot 门控
|
|
83
|
-
bot.use(mentionGate({
|
|
84
|
-
requireMentionInGroup: config.requireMention,
|
|
85
|
-
}));
|
|
86
|
-
// 6. 内容清洗(去 @marker、表情标签、多余空白)
|
|
87
|
-
bot.use(contentSanitizer({
|
|
88
|
-
parseFaceTags: true,
|
|
89
|
-
}));
|
|
90
|
-
// 7. 三层限流(sender / group / global)
|
|
91
|
-
bot.use(rateLimiter());
|
|
92
|
-
// 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
|
|
93
|
-
const slash = slashCommand({
|
|
94
|
-
autoHelp: true,
|
|
95
|
-
commands: buildCommandList({ manager, config }),
|
|
96
|
-
});
|
|
97
|
-
bot.use(slash.middleware);
|
|
98
|
-
// 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
|
|
99
|
-
bot.use(concurrencyGuard({
|
|
100
|
-
strategy: 'merge',
|
|
101
|
-
maxQueue: config.maxQueue,
|
|
102
|
-
maxProcessingMs: config.processingTimeoutMs,
|
|
103
|
-
urgentPredicate: (mCtx) => {
|
|
104
|
-
return (mCtx.message.content ?? '').trim() === '/bot-stop';
|
|
105
|
-
},
|
|
106
|
-
}));
|
|
107
|
-
// 10. C2C 输入状态指示
|
|
108
|
-
bot.use(typingIndicator());
|
|
109
|
-
// 11. 引用消息解析(记录 + 解析被引用原文)
|
|
110
|
-
bot.use(quoteRef({
|
|
111
|
-
maxSize: 500,
|
|
112
|
-
preferMsgElements: true,
|
|
113
|
-
}));
|
|
114
|
-
// 12. 上下文组装(将 history + quote + sender 组成 envelope)
|
|
115
|
-
bot.use(envelopeFormatter({
|
|
116
|
-
historyLimit: config.historyLimit,
|
|
117
|
-
includeQuote: true,
|
|
118
|
-
includeSender: true,
|
|
119
|
-
}));
|
|
120
|
-
// ══════════════════════════════════════════════════════════════
|
|
121
|
-
// 最终处理:经过中间件链后的消息交给 dsh agent
|
|
122
|
-
// ══════════════════════════════════════════════════════════════
|
|
123
|
-
bot.on('message', async (mCtx) => {
|
|
124
|
-
const msg = mCtx.message;
|
|
125
|
-
if (config.debug) {
|
|
126
|
-
logger.debug(`← message (post-middleware): ${JSON.stringify(msg, null, 2).slice(0, 500)}`);
|
|
127
|
-
}
|
|
128
|
-
await handleInbound(msg, manager, config, logger, mCtx.state);
|
|
129
|
-
});
|
|
130
|
-
// ── 出站 ──
|
|
131
|
-
// 获取 tools 服务(工具结果结构化展示,参考 dsh-TUI presentResult),可选
|
|
132
|
-
let toolsRegistry;
|
|
133
|
-
try {
|
|
134
|
-
toolsRegistry = ctx.get('tools');
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
toolsRegistry = undefined;
|
|
138
|
-
}
|
|
139
|
-
const outboundHandler = createOutboundHandler(manager, bot, config, logger, toolsRegistry);
|
|
140
|
-
ctx
|
|
141
|
-
.on('session/event', outboundHandler);
|
|
142
|
-
bot.on('error', (err) => {
|
|
143
|
-
logger.error(`bot error: ${err instanceof Error ? err.message : String(err)}`);
|
|
144
|
-
});
|
|
145
|
-
bot.on('ready', () => {
|
|
146
|
-
console.log(`[im-qqbot] Bot ready! appId=${config.appId}`);
|
|
147
|
-
});
|
|
148
|
-
// ── 生命周期 ──
|
|
149
|
-
ctx
|
|
150
|
-
.effect(() => {
|
|
151
|
-
logger.info(`Starting bot (appId=${config.appId})`);
|
|
152
|
-
bot.start().catch((err) => {
|
|
153
|
-
logger.error(`Bot start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
154
|
-
});
|
|
155
|
-
return async () => {
|
|
156
|
-
logger.info('Shutting down');
|
|
157
|
-
await manager.disposeAll();
|
|
158
|
-
bot.stop();
|
|
159
|
-
};
|
|
160
|
-
}, 'im-qqbot.lifecycle');
|
|
35
|
+
await bootstrapGateway(ctx, agents, resolvedConfig, logger);
|
|
161
36
|
}
|
|
162
37
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAGrE,qBAAqB;AACrB,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC;AAC/B,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC;AAInC,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqB;IAC7D,MAAM,MAAM,GAAI,GAA0C,CAAC,MAA0B,CAAC;IACtF,MAAM,MAAM,GAAa,GAA0C,CAAC,MAAiB,IAAI,OAAO,CAAC;IAEjG,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAEzC,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACpD,IAAI,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAE7D,oBAAoB;IACpB,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC/B,MAAM,WAAW,GAAG,MAAM,UAAU,EAAE,CAAC;QAEvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QAED,kBAAkB;QAClB,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;QAE/E,0BAA0B;QAC1B,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC;QAEjD,4CAA4C;QAC5C,oBAAoB;QACpB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IAED,MAAM,cAAc,GAAkB,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAEtE,MAAM,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 附件下载中间件 — 在中间件链中下载 file 附件
|
|
3
|
+
*
|
|
4
|
+
* 结果写入 ctx.state.downloadedFiles,供 handleInbound 读取。
|
|
5
|
+
* 下载失败不阻断消息(记录告警后放行),确保附件异常不影响正常文本处理。
|
|
6
|
+
*/
|
|
7
|
+
import type { MiddlewareContext } from '@tencent-connect/qqbot-nodejs';
|
|
8
|
+
import type { ImQQBotConfig } from '../config.js';
|
|
9
|
+
import type { Logger } from '../types.js';
|
|
10
|
+
export declare function attachmentProcessor(config: ImQQBotConfig, logger: Logger): (ctx: MiddlewareContext, next: () => Promise<void>) => Promise<void>;
|
|
11
|
+
//# sourceMappingURL=attachment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment.d.ts","sourceRoot":"","sources":["../../src/middleware/attachment.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAC;AAEzD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IACzD,KAAK,iBAAiB,EAAE,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC,CAchF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { downloadFileAttachments } from '../transport/attachment.js';
|
|
2
|
+
export function attachmentProcessor(config, logger) {
|
|
3
|
+
return async (ctx, next) => {
|
|
4
|
+
const msg = ctx.message;
|
|
5
|
+
const cwd = config.cwd || process.cwd();
|
|
6
|
+
const messageId = msg.messageId ?? 'unknown';
|
|
7
|
+
try {
|
|
8
|
+
const downloaded = await downloadFileAttachments(msg.attachments, cwd, messageId, logger);
|
|
9
|
+
ctx.state.downloadedFiles = downloaded;
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
logger.warn(`im-qqbot: attachment download failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
13
|
+
}
|
|
14
|
+
await next();
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=attachment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment.js","sourceRoot":"","sources":["../../src/middleware/attachment.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAIrE,MAAM,UAAU,mBAAmB,CAAC,MAAqB,EAAE,MAAc;IACvE,OAAO,KAAK,EAAE,GAAsB,EAAE,IAAyB,EAAiB,EAAE;QAChF,MAAM,GAAG,GAAG,GAAG,CAAC,OAAgE,CAAC;QACjF,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACxC,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,SAAS,CAAC;QAE7C,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;YAC1F,GAAG,CAAC,KAAK,CAAC,eAAe,GAAG,UAAU,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,yCAAyC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3G,CAAC;QAED,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,KAAK,EAEV,QAAQ,EAGR,gBAAgB,EAGhB,aAAa,EACb,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAMvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAG3B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM;IAgBjC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI3E;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA4D1F,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAM1D,mBAAmB,IAAI,UAAU,EAAE;IAInC,aAAa,IAAI,MAAM,EAAE;IAMzB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI7E,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa;IAe1D,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe;IAkBhE,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAsBxD,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,gBAAgB;YAIV,aAAa;IA2B3B,wCAAwC;IAClC,WAAW,CACf,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,KAAK,EAEV,QAAQ,EAGR,gBAAgB,EAGhB,aAAa,EACb,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAMvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAG3B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM;IAgBjC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI3E;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA4D1F,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAM1D,mBAAmB,IAAI,UAAU,EAAE;IAInC,aAAa,IAAI,MAAM,EAAE;IAMzB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI7E,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa;IAe1D,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe;IAkBhE,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAsBxD,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,gBAAgB;YAIV,aAAa;IA2B3B,wCAAwC;IAClC,WAAW,CACf,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,aAAa,CAAC;IAqEzB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAO7D,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,aAAa,GAAG,SAAS;IAOjD,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -198,7 +198,7 @@ export class SessionManager {
|
|
|
198
198
|
async composePreset(presetId) {
|
|
199
199
|
let presets;
|
|
200
200
|
try {
|
|
201
|
-
presets = this.ctx.agentPresets;
|
|
201
|
+
presets = this.ctx.get('agentPresets');
|
|
202
202
|
}
|
|
203
203
|
catch {
|
|
204
204
|
// agentPresets 服务未注入,降级跳过
|
|
@@ -241,9 +241,10 @@ export class SessionManager {
|
|
|
241
241
|
this.logger.info(`reusing live agent: key=${key}`);
|
|
242
242
|
}
|
|
243
243
|
else {
|
|
244
|
+
// preset 只解析一次:resume/create 共用同一组合,避免重复 resolve/mount 目录
|
|
245
|
+
const composed = await this.composePreset(this.config.preset);
|
|
246
|
+
agentPreset = composed.agentPreset;
|
|
244
247
|
try {
|
|
245
|
-
const composed = await this.composePreset(this.config.preset);
|
|
246
|
-
agentPreset = composed.agentPreset;
|
|
247
248
|
const resumeRoute = this.modelResolver.getResumeRoute(key);
|
|
248
249
|
const resumed = await this.agents.resume({
|
|
249
250
|
resumeSessionId: sessionId,
|
|
@@ -255,8 +256,6 @@ export class SessionManager {
|
|
|
255
256
|
this.logger.info(`resumed session: key=${key} preset=${agentPreset ?? 'none'} route=${resumeRoute ? `${resumeRoute.provider}/${resumeRoute.model}` : 'session-own'}`);
|
|
256
257
|
}
|
|
257
258
|
catch {
|
|
258
|
-
const composed = await this.composePreset(this.config.preset);
|
|
259
|
-
agentPreset = composed.agentPreset;
|
|
260
259
|
const created = await this.agents.create({
|
|
261
260
|
sessionId,
|
|
262
261
|
meta: {
|