@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.
Files changed (216) hide show
  1. package/.orange-ci.yml +2 -68
  2. package/CHANGELOG.md +56 -0
  3. package/CHANGELOG_EN.md +95 -0
  4. package/README.md +119 -18
  5. package/README_EN.md +107 -17
  6. package/cordis.patch.yml +1 -1
  7. package/cordis.yml +46 -3
  8. package/dist/commands/help.d.ts +6 -7
  9. package/dist/commands/help.d.ts.map +1 -1
  10. package/dist/commands/help.js +23 -7
  11. package/dist/commands/help.js.map +1 -1
  12. package/dist/commands/index.d.ts +7 -5
  13. package/dist/commands/index.d.ts.map +1 -1
  14. package/dist/commands/index.js +12 -12
  15. package/dist/commands/index.js.map +1 -1
  16. package/dist/commands/misc.d.ts +10 -8
  17. package/dist/commands/misc.d.ts.map +1 -1
  18. package/dist/commands/misc.js +45 -8
  19. package/dist/commands/misc.js.map +1 -1
  20. package/dist/commands/model.d.ts +3 -4
  21. package/dist/commands/model.d.ts.map +1 -1
  22. package/dist/commands/model.js +8 -7
  23. package/dist/commands/model.js.map +1 -1
  24. package/dist/commands/preset.d.ts +8 -0
  25. package/dist/commands/preset.d.ts.map +1 -0
  26. package/dist/commands/preset.js +52 -0
  27. package/dist/commands/preset.js.map +1 -0
  28. package/dist/commands/session.d.ts +11 -7
  29. package/dist/commands/session.d.ts.map +1 -1
  30. package/dist/commands/session.js +29 -13
  31. package/dist/commands/session.js.map +1 -1
  32. package/dist/commands/status.d.ts +3 -4
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +2 -1
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/commands/types.d.ts +9 -2
  37. package/dist/commands/types.d.ts.map +1 -1
  38. package/dist/config.d.ts +40 -0
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +45 -9
  41. package/dist/config.js.map +1 -1
  42. package/dist/features/answer-parser.d.ts +9 -0
  43. package/dist/features/answer-parser.d.ts.map +1 -0
  44. package/dist/features/answer-parser.js +23 -0
  45. package/dist/features/answer-parser.js.map +1 -0
  46. package/dist/features/approval-channel.d.ts +76 -0
  47. package/dist/features/approval-channel.d.ts.map +1 -0
  48. package/dist/features/approval-channel.js +142 -0
  49. package/dist/features/approval-channel.js.map +1 -0
  50. package/dist/features/approval-renderer.d.ts +18 -0
  51. package/dist/features/approval-renderer.d.ts.map +1 -0
  52. package/dist/features/approval-renderer.js +52 -0
  53. package/dist/features/approval-renderer.js.map +1 -0
  54. package/dist/features/button-utils.d.ts +33 -0
  55. package/dist/features/button-utils.d.ts.map +1 -0
  56. package/dist/features/button-utils.js +27 -0
  57. package/dist/features/button-utils.js.map +1 -0
  58. package/dist/features/history-store.d.ts.map +1 -1
  59. package/dist/features/history-store.js +0 -1
  60. package/dist/features/history-store.js.map +1 -1
  61. package/dist/features/question-channel.d.ts +96 -0
  62. package/dist/features/question-channel.d.ts.map +1 -0
  63. package/dist/features/question-channel.js +234 -0
  64. package/dist/features/question-channel.js.map +1 -0
  65. package/dist/features/question-renderer.d.ts +20 -0
  66. package/dist/features/question-renderer.d.ts.map +1 -0
  67. package/dist/features/question-renderer.js +70 -0
  68. package/dist/features/question-renderer.js.map +1 -0
  69. package/dist/gateway/bootstrap.d.ts +3 -3
  70. package/dist/gateway/bootstrap.d.ts.map +1 -1
  71. package/dist/gateway/bootstrap.js +84 -8
  72. package/dist/gateway/bootstrap.js.map +1 -1
  73. package/dist/gateway/index.d.ts +2 -2
  74. package/dist/gateway/index.js +2 -2
  75. package/dist/gateway/middleware-setup.d.ts +3 -3
  76. package/dist/gateway/middleware-setup.d.ts.map +1 -1
  77. package/dist/gateway/middleware-setup.js +9 -7
  78. package/dist/gateway/middleware-setup.js.map +1 -1
  79. package/dist/index.d.ts +4 -3
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +44 -25
  82. package/dist/index.js.map +1 -1
  83. package/dist/media/media-cleaner.d.ts +19 -0
  84. package/dist/media/media-cleaner.d.ts.map +1 -0
  85. package/dist/media/media-cleaner.js +81 -0
  86. package/dist/media/media-cleaner.js.map +1 -0
  87. package/dist/media/send-file-tool.d.ts +35 -0
  88. package/dist/media/send-file-tool.d.ts.map +1 -0
  89. package/dist/media/send-file-tool.js +150 -0
  90. package/dist/media/send-file-tool.js.map +1 -0
  91. package/dist/media/vision-tool.d.ts +16 -0
  92. package/dist/media/vision-tool.d.ts.map +1 -0
  93. package/dist/media/vision-tool.js +241 -0
  94. package/dist/media/vision-tool.js.map +1 -0
  95. package/dist/middleware/attachment.d.ts +3 -3
  96. package/dist/middleware/attachment.d.ts.map +1 -1
  97. package/dist/middleware/attachment.js +17 -4
  98. package/dist/middleware/attachment.js.map +1 -1
  99. package/dist/middleware/question-answer.d.ts +10 -0
  100. package/dist/middleware/question-answer.d.ts.map +1 -0
  101. package/dist/middleware/question-answer.js +19 -0
  102. package/dist/middleware/question-answer.js.map +1 -0
  103. package/dist/model/index.d.ts +4 -4
  104. package/dist/model/index.js +3 -3
  105. package/dist/model/model-resolver.d.ts +27 -13
  106. package/dist/model/model-resolver.d.ts.map +1 -1
  107. package/dist/model/model-resolver.js +82 -27
  108. package/dist/model/model-resolver.js.map +1 -1
  109. package/dist/model/prefs-store.d.ts +6 -1
  110. package/dist/model/prefs-store.d.ts.map +1 -1
  111. package/dist/model/prefs-store.js +25 -1
  112. package/dist/model/prefs-store.js.map +1 -1
  113. package/dist/model/settings-reader.d.ts +1 -1
  114. package/dist/session/idle-evictor.d.ts +2 -2
  115. package/dist/session/idle-evictor.d.ts.map +1 -1
  116. package/dist/session/idle-evictor.js +1 -1
  117. package/dist/session/idle-evictor.js.map +1 -1
  118. package/dist/session/index.d.ts +3 -3
  119. package/dist/session/index.d.ts.map +1 -1
  120. package/dist/session/index.js +2 -2
  121. package/dist/session/session-manager.d.ts +67 -9
  122. package/dist/session/session-manager.d.ts.map +1 -1
  123. package/dist/session/session-manager.js +216 -20
  124. package/dist/session/session-manager.js.map +1 -1
  125. package/dist/session/types.d.ts +47 -1
  126. package/dist/session/types.d.ts.map +1 -1
  127. package/dist/setup/bind-routes.d.ts +78 -0
  128. package/dist/setup/bind-routes.d.ts.map +1 -0
  129. package/dist/setup/bind-routes.js +311 -0
  130. package/dist/setup/bind-routes.js.map +1 -0
  131. package/dist/setup/binding-service.d.ts +86 -0
  132. package/dist/setup/binding-service.d.ts.map +1 -0
  133. package/dist/setup/binding-service.js +175 -0
  134. package/dist/setup/binding-service.js.map +1 -0
  135. package/dist/setup/index.d.ts +50 -0
  136. package/dist/setup/index.d.ts.map +1 -0
  137. package/dist/setup/index.js +98 -0
  138. package/dist/setup/index.js.map +1 -0
  139. package/dist/setup/persist.d.ts +41 -0
  140. package/dist/setup/persist.d.ts.map +1 -0
  141. package/dist/setup/persist.js +201 -0
  142. package/dist/setup/persist.js.map +1 -0
  143. package/dist/setup/presenters.d.ts +40 -0
  144. package/dist/setup/presenters.d.ts.map +1 -0
  145. package/dist/setup/presenters.js +84 -0
  146. package/dist/setup/presenters.js.map +1 -0
  147. package/dist/setup/qr-svg.d.ts +8 -0
  148. package/dist/setup/qr-svg.d.ts.map +1 -0
  149. package/dist/setup/qr-svg.js +20 -0
  150. package/dist/setup/qr-svg.js.map +1 -0
  151. package/dist/shared/bind-protocol.d.ts +42 -0
  152. package/dist/shared/bind-protocol.d.ts.map +1 -0
  153. package/dist/shared/bind-protocol.js +19 -0
  154. package/dist/shared/bind-protocol.js.map +1 -0
  155. package/dist/shared/index.d.ts +3 -3
  156. package/dist/shared/index.d.ts.map +1 -1
  157. package/dist/shared/index.js +3 -3
  158. package/dist/shared/index.js.map +1 -1
  159. package/dist/shared/send-helper.js +1 -1
  160. package/dist/shared/utils.d.ts +10 -3
  161. package/dist/shared/utils.d.ts.map +1 -1
  162. package/dist/shared/utils.js +47 -6
  163. package/dist/shared/utils.js.map +1 -1
  164. package/dist/transport/attachment.d.ts +18 -7
  165. package/dist/transport/attachment.d.ts.map +1 -1
  166. package/dist/transport/attachment.js +77 -30
  167. package/dist/transport/attachment.js.map +1 -1
  168. package/dist/transport/inbound.d.ts +5 -4
  169. package/dist/transport/inbound.d.ts.map +1 -1
  170. package/dist/transport/inbound.js +120 -80
  171. package/dist/transport/inbound.js.map +1 -1
  172. package/dist/transport/index.d.ts +4 -4
  173. package/dist/transport/index.js +4 -4
  174. package/dist/transport/msgid-cache.d.ts +23 -0
  175. package/dist/transport/msgid-cache.d.ts.map +1 -0
  176. package/dist/transport/msgid-cache.js +57 -0
  177. package/dist/transport/msgid-cache.js.map +1 -0
  178. package/dist/transport/outbound-buffer.d.ts +9 -6
  179. package/dist/transport/outbound-buffer.d.ts.map +1 -1
  180. package/dist/transport/outbound-buffer.js +5 -5
  181. package/dist/transport/outbound-buffer.js.map +1 -1
  182. package/dist/transport/outbound.d.ts +9 -9
  183. package/dist/transport/outbound.d.ts.map +1 -1
  184. package/dist/transport/outbound.js +7 -7
  185. package/dist/transport/outbound.js.map +1 -1
  186. package/dist/transport/reply-limiter.d.ts +31 -0
  187. package/dist/transport/reply-limiter.d.ts.map +1 -0
  188. package/dist/transport/reply-limiter.js +50 -0
  189. package/dist/transport/reply-limiter.js.map +1 -0
  190. package/dist/transport/reply-target.d.ts +29 -0
  191. package/dist/transport/reply-target.d.ts.map +1 -0
  192. package/dist/transport/reply-target.js +55 -0
  193. package/dist/transport/reply-target.js.map +1 -0
  194. package/dist/transport/streaming-writer.d.ts +3 -3
  195. package/dist/transport/streaming-writer.d.ts.map +1 -1
  196. package/dist/transport/streaming-writer.js +1 -1
  197. package/dist/transport/streaming-writer.js.map +1 -1
  198. package/dist/transport/tool-presenter.d.ts +0 -1
  199. package/dist/transport/tool-presenter.d.ts.map +1 -1
  200. package/dist/transport/tool-presenter.js +0 -1
  201. package/dist/transport/tool-presenter.js.map +1 -1
  202. package/dist/types.d.ts +9 -15
  203. package/dist/types.d.ts.map +1 -1
  204. package/docs/assets/qqchannel.jpg +0 -0
  205. package/docs/assets/qqgroup.jpg +0 -0
  206. package/docs/assets/qrcode.png +0 -0
  207. package/lib/client.js +996 -0
  208. package/lib/client.js.map +1 -0
  209. package/package.json +44 -7
  210. package/pnpm-workspace.yaml +1 -0
  211. package/tsconfig.client.json +19 -0
  212. package/tsdown.client.config.ts +58 -0
  213. package/dist/setup.d.ts +0 -24
  214. package/dist/setup.d.ts.map +0 -1
  215. package/dist/setup.js +0 -170
  216. package/dist/setup.js.map +0 -1
@@ -1,8 +1,35 @@
1
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';
2
+ import { SessionManager } from "../session/index.js";
3
+ import { handleInbound, createOutboundHandler } from "../transport/index.js";
4
+ import { ReplyLimiter } from "../transport/reply-limiter.js";
5
+ import { cacheMsgId, cacheEventId } from "../transport/msgid-cache.js";
6
+ import { resolveReplyTarget, sendResolvedMarkdown } from "../transport/reply-target.js";
7
+ import { QuestionChannel } from "../features/question-channel.js";
8
+ import { ApprovalChannel } from "../features/approval-channel.js";
9
+ import { decodeButtonData } from "../features/button-utils.js";
10
+ import { buildUserAgent } from "../shared/index.js";
11
+ import { setupMiddlewares } from "./middleware-setup.js";
12
+ import { startMediaCleanup } from "../media/media-cleaner.js";
13
+ import { ensureVisionInputModal, registerDescribeImageTool } from "../media/vision-tool.js";
14
+ import { registerSendFileTool } from "../media/send-file-tool.js";
15
+ /**
16
+ * interaction 统一分发器:按 button_data 的顶层 `t` 判别字段,路由到对应
17
+ * channel(question / approval)。QQ 只有一个 interaction 入口,集中路由
18
+ * 避免多通道各自 try 导致误判。
19
+ */
20
+ function dispatchInteraction(event, manager) {
21
+ const raw = event.data?.resolved?.button_data;
22
+ if (!raw)
23
+ return false;
24
+ const button = decodeButtonData(raw);
25
+ if (!button)
26
+ return false;
27
+ if (button.t === 'question')
28
+ return manager.questionChannel?.handleInteraction(event) ?? false;
29
+ if (button.t === 'approval')
30
+ return manager.approvalChannel?.handleInteraction(event) ?? false;
31
+ return false;
32
+ }
6
33
  export async function bootstrapGateway(ctx, agents, config, logger) {
7
34
  const manager = new SessionManager(ctx, agents, config, logger);
8
35
  // ── 初始化 QQ Bot SDK ──
@@ -22,13 +49,15 @@ export async function bootstrapGateway(ctx, agents, config, logger) {
22
49
  // ── 入站:经过中间件链后的消息交给 dsh agent ──
23
50
  bot.on('message', async (mCtx) => {
24
51
  const msg = mCtx.message;
52
+ // 记录最近 msgId,供无上下文的出站发送(文件发送显式 target、主动推送等)获取被动回复目标
53
+ cacheMsgId(mCtx.replyTarget.scope, mCtx.replyTarget.targetId, mCtx.replyTarget.msgId);
25
54
  if (config.debug) {
26
55
  logger.debug(`← message (post-middleware): ${JSON.stringify(msg, null, 2).slice(0, 500)}`);
27
56
  }
28
- await handleInbound(msg, manager, config, logger, mCtx.state);
57
+ await handleInbound(mCtx, manager, config, logger);
29
58
  });
30
59
  // ── 出站:dsh session/event → QQ 消息 ──
31
- // 获取 tools 服务(工具结果结构化展示,参考 dsh-TUI presentResult),可选
60
+ // 获取 tools 服务(工具结果结构化展示),可选
32
61
  let toolsRegistry;
33
62
  try {
34
63
  toolsRegistry = ctx.get('tools');
@@ -36,9 +65,11 @@ export async function bootstrapGateway(ctx, agents, config, logger) {
36
65
  catch {
37
66
  toolsRegistry = undefined;
38
67
  }
39
- // 发送适配器:将 QQBot 实例适配为 QQBotSender(openStream 参数形态不同)
68
+ // 发送适配器:将 QQBot 实例适配为 QQBotSender(openStream 参数形态不同)。
69
+ // sendMarkdown 统一做被动回复限额管控:同一 msgId 超限/过期时降级主动推送。
70
+ const replyLimiter = new ReplyLimiter({ limit: 4 });
40
71
  const sender = {
41
- sendMarkdown: (target, content) => bot.sendMarkdown(target, content),
72
+ sendMarkdown: (target, content, opts) => sendResolvedMarkdown(bot, resolveReplyTarget(target, replyLimiter, true), content, opts),
42
73
  openStream: (target) => bot.openStream({
43
74
  target: {
44
75
  scope: target.scope,
@@ -50,12 +81,57 @@ export async function bootstrapGateway(ctx, agents, config, logger) {
50
81
  const outboundHandler = createOutboundHandler(manager, sender, config, logger, toolsRegistry);
51
82
  ctx
52
83
  .on('session/event', outboundHandler);
84
+ // ── 问答通道(ask_user_question → QQ 文本 + 按钮) ──
85
+ const questionChannel = new QuestionChannel(manager, sender, config, logger);
86
+ manager.questionChannel = questionChannel;
87
+ questionChannel.install(ctx);
88
+ // ── 审批通道(approval/request → QQ 两按钮审批) ──
89
+ const approvalChannel = new ApprovalChannel(manager, sender, logger);
90
+ manager.approvalChannel = approvalChannel;
91
+ approvalChannel.install(ctx);
92
+ // ── 按钮点击回调(interaction):统一分发到问答/审批通道 ──
93
+ bot.on('interaction', async (_iCtx, event) => {
94
+ // scope/targetId 推导对齐消息事件(inbound.ts):按 scene 判定类型,群用 group_openid,私聊用 user_openid
95
+ const scope = event.scene === 'group' ? 'group' : 'c2c';
96
+ const targetId = scope === 'group' ? (event.group_openid ?? event.user_openid) : event.user_openid;
97
+ // 记录互动事件 id,作为后续被动回复候选(event_id)
98
+ cacheEventId(scope, targetId, event.id);
99
+ const matched = dispatchInteraction(event, manager);
100
+ await bot.acknowledgeInteraction(event.id, matched ? 0 : 3).catch(() => { });
101
+ });
53
102
  bot.on('error', (err) => {
54
103
  logger.error(`bot error: ${err instanceof Error ? err.message : String(err)}`);
55
104
  });
105
+ // 掉线可观测:willReconnect=false 表示不会再重连(随后 bot.start() 的 Promise 会失败)
106
+ bot.on('disconnected', (event) => {
107
+ const detail = `code=${event.code}${event.reason ? ` reason=${event.reason}` : ''}`;
108
+ if (event.willReconnect) {
109
+ logger.warn(`Bot disconnected (${detail}), reconnecting...`);
110
+ }
111
+ else {
112
+ logger.error(`Bot disconnected permanently (${detail}), giving up`);
113
+ }
114
+ });
56
115
  bot.on('ready', () => {
57
116
  console.log(`[im-qqbot] Bot ready! appId=${config.appId}`);
58
117
  });
118
+ // ── 富媒体过期清理 ──
119
+ if (config.media.enabled) {
120
+ startMediaCleanup(ctx, config.media.ttlHours, logger);
121
+ }
122
+ // ── 视觉工具注册(qqbot_describe_image,复用 dsh llm + attachments) ──
123
+ ensureVisionInputModal(config.vision, logger);
124
+ registerDescribeImageTool(ctx, config.vision, logger);
125
+ // ── 附件发送工具注册(qqbot_send_file) ──
126
+ // 包装 bot:让文件发送也走 msgId 兜底 + 被动回复限额管控。
127
+ // 注意:SDK 文件发送暂不支持 event_id,故 allowEvent=false,跳过 event 候选。
128
+ const mediaSender = {
129
+ sendImage: (target, source) => bot.sendImage(resolveReplyTarget(target, replyLimiter, false), source),
130
+ sendVideo: (target, source) => bot.sendVideo(resolveReplyTarget(target, replyLimiter, false), source),
131
+ sendVoice: (target, source) => bot.sendVoice(resolveReplyTarget(target, replyLimiter, false), source),
132
+ sendFile: (target, source, opts) => bot.sendFile(resolveReplyTarget(target, replyLimiter, false), source, opts),
133
+ };
134
+ registerSendFileTool(ctx, mediaSender, manager, config, logger);
59
135
  // ── 生命周期 ──
60
136
  ctx
61
137
  .effect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/gateway/bootstrap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/gateway/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export { setupMiddlewares } from './middleware-setup.js';
2
- export { bootstrapGateway } from './bootstrap.js';
1
+ export { setupMiddlewares } from './middleware-setup.ts';
2
+ export { bootstrapGateway } from './bootstrap.ts';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,3 @@
1
- export { setupMiddlewares } from './middleware-setup.js';
2
- export { bootstrapGateway } from './bootstrap.js';
1
+ export { setupMiddlewares } from "./middleware-setup.js";
2
+ export { bootstrapGateway } from "./bootstrap.js";
3
3
  //# sourceMappingURL=index.js.map
@@ -5,8 +5,8 @@
5
5
  * 中间件负责过滤与上下文富化;最终消息统一由 bot.on('message') 处理转发。
6
6
  */
7
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';
8
+ import type { ImQQBotConfig } from '../config.ts';
9
+ import type { SessionManager } from '../session/index.ts';
10
+ import type { Logger } from '../types.ts';
11
11
  export declare function setupMiddlewares(bot: QQBot, config: ImQQBotConfig, manager: SessionManager, logger: Logger): void;
12
12
  //# sourceMappingURL=middleware-setup.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"middleware-setup.d.ts","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAe3D,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;AAM1C,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,KAAK,EACV,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAuFN"}
@@ -1,7 +1,8 @@
1
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';
2
+ import { buildCommandList } from "../commands/index.js";
3
+ import { attachmentProcessor } from "../middleware/attachment.js";
4
+ import { questionAnswer } from "../middleware/question-answer.js";
5
+ import { getHistoryStore, historyGroupKey } from "../features/history-store.js";
5
6
  export function setupMiddlewares(bot, config, manager, logger) {
6
7
  // 1. 错误兜底(最外层洋葱皮)
7
8
  bot.use(errorHandler());
@@ -48,18 +49,19 @@ export function setupMiddlewares(bot, config, manager, logger) {
48
49
  bot.use(rateLimiter());
49
50
  // 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
50
51
  const slash = slashCommand({
51
- autoHelp: true,
52
+ // 关闭 autoHelp:help 命令由 qqbot 注册为 /bot-help,
53
+ // 避免 SDK 再自动注册一个无前缀的 /help 造成重复
54
+ autoHelp: false,
52
55
  commands: buildCommandList({ manager, config }),
53
56
  });
54
57
  bot.use(slash.middleware);
58
+ // 8.5. 待答问题截获(在 concurrencyGuard 之前,答案消息不排队直接作答)
59
+ bot.use(questionAnswer(manager));
55
60
  // 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
56
61
  bot.use(concurrencyGuard({
57
62
  strategy: 'merge',
58
63
  maxQueue: config.maxQueue,
59
64
  maxProcessingMs: config.processingTimeoutMs,
60
- urgentPredicate: (mCtx) => {
61
- return (mCtx.message.content ?? '').trim() === '/bot-stop';
62
- },
63
65
  }));
64
66
  // 10. C2C 输入状态指示
65
67
  bot.use(typingIndicator());
@@ -1 +1 @@
1
- {"version":3,"file":"middleware-setup.js","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"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"}
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,13 +2,14 @@
2
2
  * dsh-im-qqbot — QQ Bot IM channel plugin for deepseek-harness
3
3
  *
4
4
  * Cordis 插件入口。将 QQ 消息平台作为 dsh 的前端协议驱动。
5
- * 网关组装(中间件编排 + 事件 + 出站 + 生命周期)见 src/gateway/。
5
+ * 网关组装(中间件编排 + 事件 + 出站 + 生命周期)见 src/gateway/;
6
+ * 凭据绑定编排(扫码状态机 + 端内绑定页路由)见 src/setup/。
6
7
  */
7
8
  import type { Context } from '@deepseek-ai/cordis';
8
- import { type ImQQBotConfig } from './config.js';
9
+ import { type ImQQBotConfig } from './config.ts';
9
10
  export declare const name = "im-qqbot";
10
11
  export declare const inject: string[];
11
12
  export declare const Config: import("@deepseek-ai/schemastery").default<ImQQBotConfig>;
12
- export type { ImQQBotConfig } from './config.js';
13
+ export type { ImQQBotConfig } from './config.ts';
13
14
  export declare function apply(ctx: Context, config: ImQQBotConfig): Promise<void>;
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -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;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,CAuC9E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;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,CA0D9E"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { ConfigSchema } from './config.js';
2
- import { bootstrapGateway } from './gateway/index.js';
3
- import { getProfileDir, resolveEnv } from './shared/index.js';
4
- import { runQrSetup, persistCredentialsToProfile } from './setup.js';
1
+ import { ConfigSchema } from "./config.js";
2
+ import { bootstrapGateway } from "./gateway/index.js";
3
+ import { getProfileDir, resolveEnv } from "./shared/index.js";
4
+ import { ensureCredentials, persistCredentialsToProfile, registerBoundCredentialRoutes } from "./setup/index.js";
5
5
  // ── Cordis 插件元数据 ──
6
6
  export const name = 'im-qqbot';
7
7
  export const inject = ['agents'];
@@ -10,32 +10,51 @@ export const Config = ConfigSchema;
10
10
  export async function apply(ctx, config) {
11
11
  const agents = ctx.agents;
12
12
  const logger = ctx.logger ?? console;
13
- console.log('[im-qqbot] apply() called');
14
13
  let appId = resolveEnv(config.appId, 'QQBOT_APPID');
15
14
  let appSecret = resolveEnv(config.appSecret, 'QQBOT_SECRET');
16
15
  // ── 凭据缺失时唤起扫码绑定 ──
17
16
  if (!appId || !appSecret) {
18
- logger.info('凭据未配置,尝试扫码绑定...');
19
- const credentials = await runQrSetup();
20
- if (!credentials) {
21
- logger.error('无法获取 QQ Bot 凭据,插件未启动');
22
- return;
23
- }
24
- // 写入环境变量(供热更新后的下次 apply 或本次直接启动读取)
25
- process.env.QQBOT_APPID = credentials.appId;
26
- process.env.QQBOT_SECRET = credentials.appSecret;
27
- appId = credentials.appId;
28
- appSecret = credentials.appSecret;
29
- // 持久化到 profile:成功则等待热更新重载,失败则用 env 凭据直接启动
30
- const persisted = persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
31
- if (persisted) {
32
- // 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件。
33
- // 直接返回,避免与热更新产生竞态。
34
- logger.info('配置已保存,等待热更新重新加载...');
35
- return;
36
- }
37
- logger.warn('凭据未能持久化,本次进程将使用环境变量凭据启动(重启后需重新绑定)');
17
+ logger.info('凭据未配置,启动扫码绑定...');
18
+ // 关键:不 await 扫码结果。boot 会等 Loader 树稳定(loader.await()),
19
+ // 无限期等待用户扫码会阻塞 Host ready——Desktop 界面无法进入、
20
+ // Web 的 URL 延迟输出。绑定编排(路由/呈现/生命周期)在
21
+ // ensureCredentials 的同步阶段即完成,凭据就绪后由异步链接管。
22
+ void ensureCredentials(ctx, { logger, config: { ...config } })
23
+ .then((credentials) => {
24
+ if (!credentials) {
25
+ // null 包括:扫码取消、手动提交(已另行持久化)、流程失败
26
+ logger.info('扫码绑定流程结束(未获取凭据或已手动配置)');
27
+ return;
28
+ }
29
+ // 持久化到 profile:成功则等待热更新重载,失败则用凭据直接启动。
30
+ // 注意:不写入 process.env——resolveEnv 的 env 回退属于用户显式
31
+ // export 的配置语义,运行时写入会与解绑清理不对齐(解绑后 HMR
32
+ // 重载的新 fiber 仍从 env 读到旧凭据,状态停留 bound)
33
+ const persisted = persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
34
+ if (persisted) {
35
+ // 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件。
36
+ logger.info('配置已保存,等待热更新重新加载...');
37
+ return;
38
+ }
39
+ logger.warn('凭据未能持久化,本次进程将使用环境变量凭据启动(重启后需重新绑定)');
40
+ // 本 fiber 已激活(ctx 有效),直接以凭据启动网关
41
+ void bootstrapGateway(ctx, agents, {
42
+ ...config,
43
+ appId: credentials.appId,
44
+ appSecret: credentials.appSecret,
45
+ }, logger).catch((err) => {
46
+ logger.error(`网关启动失败: ${err instanceof Error ? err.message : String(err)}`);
47
+ });
48
+ })
49
+ .catch((err) => {
50
+ // 全兜:unhandled rejection 会被宿主 fail-loud 判致命
51
+ logger.error(`扫码绑定流程异常: ${err instanceof Error ? err.message : String(err)}`);
52
+ });
53
+ // apply 立即完成,插件 fiber 进入 ACTIVE,Host ready 不再被阻塞
54
+ return;
38
55
  }
56
+ // 已绑定:注册常驻路由(状态回显 + 手动编辑/更换凭据 + 配置编辑)
57
+ registerBoundCredentialRoutes(ctx, logger, appId, { ...config });
39
58
  const resolvedConfig = { ...config, appId, appSecret };
40
59
  await bootstrapGateway(ctx, agents, resolvedConfig, logger);
41
60
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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,mCAAmC;QACnC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC;QACjD,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC;QAC1B,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;QAElC,0CAA0C;QAC1C,MAAM,SAAS,GAAG,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;QACjG,IAAI,SAAS,EAAE,CAAC;YACd,6CAA6C;YAC7C,mBAAmB;YACnB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACnD,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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,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,iBAAiB,EAAE,2BAA2B,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAGjH,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,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;QAE/B,sDAAsD;QACtD,2CAA2C;QAC3C,mCAAmC;QACnC,0CAA0C;QAC1C,KAAK,iBAAiB,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC;aAC3D,IAAI,CAAC,CAAC,WAAW,EAAQ,EAAE;YAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,iCAAiC;gBACjC,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;gBACrC,OAAO;YACT,CAAC;YAED,sCAAsC;YACtC,gDAAgD;YAChD,sCAAsC;YACtC,sCAAsC;YACtC,MAAM,SAAS,GAAG,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;YACjG,IAAI,SAAS,EAAE,CAAC;gBACd,6CAA6C;gBAC7C,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBAClC,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;YACjD,gCAAgC;YAChC,KAAK,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE;gBACjC,GAAG,MAAM;gBACT,KAAK,EAAE,WAAW,CAAC,KAAK;gBACxB,SAAS,EAAE,WAAW,CAAC,SAAS;aACjC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBAChC,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9E,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACtB,4CAA4C;YAC5C,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;QAEL,iDAAiD;QACjD,OAAO;IACT,CAAC;IAED,sCAAsC;IACtC,6BAA6B,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAEjE,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,19 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { Logger } from '../types.ts';
3
+ /** 富媒体下载根目录(attachment.ts 复用) */
4
+ export declare const MEDIA_ROOT: string;
5
+ /**
6
+ * 清理过期的富媒体文件(按文件 mtime 判断,异步并发执行,不阻塞事件循环)
7
+ *
8
+ * @param ttlHours 保留时长(小时),<=0 表示永不过期(跳过清理)
9
+ * @returns 删除的文件数
10
+ */
11
+ export declare function cleanupExpiredMedia(ttlHours: number, logger?: Logger): Promise<number>;
12
+ /**
13
+ * 启动富媒体过期清理:启动时清理一次 + 定期清理(每 1 小时)
14
+ *
15
+ * 用 ctx.effect 挂载定时器,插件卸载时自动 clearInterval。
16
+ * ttlHours <= 0 时不启动(永不过期)。
17
+ */
18
+ export declare function startMediaCleanup(ctx: Context, ttlHours: number, logger: Logger): void;
19
+ //# sourceMappingURL=media-cleaner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-cleaner.d.ts","sourceRoot":"","sources":["../../src/media/media-cleaner.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,iCAAiC;AACjC,eAAO,MAAM,UAAU,QAA4C,CAAC;AAKpE;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB5F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA4BtF"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 富媒体过期清理 — 避免下载文件持续膨胀
3
+ *
4
+ * 下载目录 ~/.dsh-qqbot/media,文件写入后不再变更,故文件 mtime 即「下载时间」。
5
+ * 清理时按文件 mtime 判断是否超过保留时长。
6
+ */
7
+ import { existsSync } from 'node:fs';
8
+ import { readdir, rm, stat } from 'node:fs/promises';
9
+ import { join, resolve } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ /** 富媒体下载根目录(attachment.ts 复用) */
12
+ export const MEDIA_ROOT = resolve(homedir(), '.dsh-qqbot', 'media');
13
+ /** 定期清理间隔(毫秒),固定 1 小时 */
14
+ const CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
15
+ /**
16
+ * 清理过期的富媒体文件(按文件 mtime 判断,异步并发执行,不阻塞事件循环)
17
+ *
18
+ * @param ttlHours 保留时长(小时),<=0 表示永不过期(跳过清理)
19
+ * @returns 删除的文件数
20
+ */
21
+ export async function cleanupExpiredMedia(ttlHours, logger) {
22
+ if (ttlHours <= 0)
23
+ return 0;
24
+ if (!existsSync(MEDIA_ROOT))
25
+ return 0;
26
+ const now = Date.now();
27
+ const ttlMs = ttlHours * 60 * 60 * 1000;
28
+ const entries = await readdir(MEDIA_ROOT, { withFileTypes: true });
29
+ const files = entries.filter(entry => entry.isFile()).map(entry => entry.name);
30
+ const results = await Promise.all(files.map(async (name) => {
31
+ const filePath = join(MEDIA_ROOT, name);
32
+ try {
33
+ const info = await stat(filePath);
34
+ if (now - info.mtimeMs <= ttlMs)
35
+ return false;
36
+ await rm(filePath, { force: true });
37
+ logger?.debug(`im-qqbot: cleaned expired media: ${name}`);
38
+ return true;
39
+ }
40
+ catch (err) {
41
+ logger?.warn(`im-qqbot: media cleanup failed for ${name}: ${err instanceof Error ? err.message : String(err)}`);
42
+ return false;
43
+ }
44
+ }));
45
+ return results.filter(Boolean).length;
46
+ }
47
+ /**
48
+ * 启动富媒体过期清理:启动时清理一次 + 定期清理(每 1 小时)
49
+ *
50
+ * 用 ctx.effect 挂载定时器,插件卸载时自动 clearInterval。
51
+ * ttlHours <= 0 时不启动(永不过期)。
52
+ */
53
+ export function startMediaCleanup(ctx, ttlHours, logger) {
54
+ if (ttlHours <= 0)
55
+ return;
56
+ logger.info(`im-qqbot: media cleanup started (ttl=${ttlHours}h, interval=1h, root=${MEDIA_ROOT})`);
57
+ // 启动时立即清理一次(异步,不阻塞启动流程)
58
+ void cleanupExpiredMedia(ttlHours, logger)
59
+ .then((cleaned) => {
60
+ if (cleaned > 0)
61
+ logger.info(`im-qqbot: cleaned ${cleaned} expired media files`);
62
+ })
63
+ .catch((err) => {
64
+ logger.warn(`im-qqbot: media cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
65
+ });
66
+ ctx
67
+ .effect(() => {
68
+ const timer = setInterval(() => {
69
+ void cleanupExpiredMedia(ttlHours, logger)
70
+ .then((n) => {
71
+ if (n > 0)
72
+ logger.info(`im-qqbot: cleaned ${n} expired media files`);
73
+ })
74
+ .catch((err) => {
75
+ logger.warn(`im-qqbot: media cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
76
+ });
77
+ }, CLEANUP_INTERVAL_MS);
78
+ return async () => { clearInterval(timer); };
79
+ }, 'im-qqbot.media-cleanup');
80
+ }
81
+ //# sourceMappingURL=media-cleaner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-cleaner.js","sourceRoot":"","sources":["../../src/media/media-cleaner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ImQQBotConfig } from '../config.ts';
3
+ import type { SessionManager } from '../session/index.ts';
4
+ import type { Logger, ReplyTarget } from '../types.ts';
5
+ /** 工具名(qqbot 前缀避免与其他通道的同名工具冲突) */
6
+ export declare const SEND_FILE_TOOL_NAME = "qqbot_send_file";
7
+ /** 发送器最小接口(QQBot 实例满足) */
8
+ interface MediaSendResult {
9
+ upload: {
10
+ file_uuid: string;
11
+ };
12
+ message?: {
13
+ id?: string;
14
+ };
15
+ }
16
+ export interface MediaSenderLike {
17
+ sendImage(target: ReplyTarget, source: {
18
+ localPath?: string;
19
+ }): Promise<MediaSendResult>;
20
+ sendVideo(target: ReplyTarget, source: {
21
+ localPath?: string;
22
+ }): Promise<MediaSendResult>;
23
+ sendVoice(target: ReplyTarget, source: {
24
+ localPath?: string;
25
+ }): Promise<MediaSendResult>;
26
+ sendFile(target: ReplyTarget, source: {
27
+ localPath?: string;
28
+ }, opts?: {
29
+ fileName?: string;
30
+ }): Promise<MediaSendResult>;
31
+ }
32
+ /** 注册 qqbot_send_file 工具(tools 服务缺失时优雅降级,不阻断插件启动) */
33
+ export declare function registerSendFileTool(ctx: Context, bot: MediaSenderLike, manager: SessionManager, config: ImQQBotConfig, logger: Logger): void;
34
+ export {};
35
+ //# sourceMappingURL=send-file-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-file-tool.d.ts","sourceRoot":"","sources":["../../src/media/send-file-tool.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAY,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGvD,kCAAkC;AAClC,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAQrD,0BAA0B;AAC1B,UAAU,eAAe;IACvB,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACvH;AAwCD,qDAAqD;AACrD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,IAAI,CA4GN"}