@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.
Files changed (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/README_EN.md +273 -0
  4. package/client/qqbot-settings.js +1463 -0
  5. package/cordis.patch.yml +36 -0
  6. package/dist/api/group-admin.d.ts +112 -0
  7. package/dist/api/group-admin.d.ts.map +1 -0
  8. package/dist/api/group-admin.js +179 -0
  9. package/dist/api/group-admin.js.map +1 -0
  10. package/dist/channel-tools.d.ts +14 -0
  11. package/dist/channel-tools.d.ts.map +1 -0
  12. package/dist/channel-tools.js +880 -0
  13. package/dist/channel-tools.js.map +1 -0
  14. package/dist/commands/help.d.ts +11 -0
  15. package/dist/commands/help.d.ts.map +1 -0
  16. package/dist/commands/help.js +21 -0
  17. package/dist/commands/help.js.map +1 -0
  18. package/dist/commands/index.d.ts +13 -0
  19. package/dist/commands/index.d.ts.map +1 -0
  20. package/dist/commands/index.js +29 -0
  21. package/dist/commands/index.js.map +1 -0
  22. package/dist/commands/misc.d.ts +14 -0
  23. package/dist/commands/misc.d.ts.map +1 -0
  24. package/dist/commands/misc.js +39 -0
  25. package/dist/commands/misc.js.map +1 -0
  26. package/dist/commands/model.d.ts +7 -0
  27. package/dist/commands/model.d.ts.map +1 -0
  28. package/dist/commands/model.js +58 -0
  29. package/dist/commands/model.js.map +1 -0
  30. package/dist/commands/session.d.ts +10 -0
  31. package/dist/commands/session.d.ts.map +1 -0
  32. package/dist/commands/session.js +26 -0
  33. package/dist/commands/session.js.map +1 -0
  34. package/dist/commands/status.d.ts +8 -0
  35. package/dist/commands/status.d.ts.map +1 -0
  36. package/dist/commands/status.js +24 -0
  37. package/dist/commands/status.js.map +1 -0
  38. package/dist/commands/types.d.ts +12 -0
  39. package/dist/commands/types.d.ts.map +1 -0
  40. package/dist/commands/types.js +2 -0
  41. package/dist/commands/types.js.map +1 -0
  42. package/dist/config.d.ts +205 -0
  43. package/dist/config.d.ts.map +1 -0
  44. package/dist/config.js +203 -0
  45. package/dist/config.js.map +1 -0
  46. package/dist/features/chat-ledger.d.ts +53 -0
  47. package/dist/features/chat-ledger.d.ts.map +1 -0
  48. package/dist/features/chat-ledger.js +185 -0
  49. package/dist/features/chat-ledger.js.map +1 -0
  50. package/dist/features/face-tags.d.ts +17 -0
  51. package/dist/features/face-tags.d.ts.map +1 -0
  52. package/dist/features/face-tags.js +79 -0
  53. package/dist/features/face-tags.js.map +1 -0
  54. package/dist/features/group-join-request.d.ts +65 -0
  55. package/dist/features/group-join-request.d.ts.map +1 -0
  56. package/dist/features/group-join-request.js +190 -0
  57. package/dist/features/group-join-request.js.map +1 -0
  58. package/dist/features/history-store.d.ts +8 -0
  59. package/dist/features/history-store.d.ts.map +1 -0
  60. package/dist/features/history-store.js +24 -0
  61. package/dist/features/history-store.js.map +1 -0
  62. package/dist/features/qq-approval.d.ts +78 -0
  63. package/dist/features/qq-approval.d.ts.map +1 -0
  64. package/dist/features/qq-approval.js +165 -0
  65. package/dist/features/qq-approval.js.map +1 -0
  66. package/dist/features/schedule-store.d.ts +62 -0
  67. package/dist/features/schedule-store.d.ts.map +1 -0
  68. package/dist/features/schedule-store.js +253 -0
  69. package/dist/features/schedule-store.js.map +1 -0
  70. package/dist/features/scheduler.d.ts +9 -0
  71. package/dist/features/scheduler.d.ts.map +1 -0
  72. package/dist/features/scheduler.js +146 -0
  73. package/dist/features/scheduler.js.map +1 -0
  74. package/dist/features/sticker-gate.d.ts +90 -0
  75. package/dist/features/sticker-gate.d.ts.map +1 -0
  76. package/dist/features/sticker-gate.js +391 -0
  77. package/dist/features/sticker-gate.js.map +1 -0
  78. package/dist/features/sticker-store.d.ts +158 -0
  79. package/dist/features/sticker-store.d.ts.map +1 -0
  80. package/dist/features/sticker-store.js +676 -0
  81. package/dist/features/sticker-store.js.map +1 -0
  82. package/dist/features/sticker-tagger.d.ts +10 -0
  83. package/dist/features/sticker-tagger.d.ts.map +1 -0
  84. package/dist/features/sticker-tagger.js +139 -0
  85. package/dist/features/sticker-tagger.js.map +1 -0
  86. package/dist/gateway/bootstrap.d.ts +12 -0
  87. package/dist/gateway/bootstrap.d.ts.map +1 -0
  88. package/dist/gateway/bootstrap.js +346 -0
  89. package/dist/gateway/bootstrap.js.map +1 -0
  90. package/dist/gateway/debounce.d.ts +40 -0
  91. package/dist/gateway/debounce.d.ts.map +1 -0
  92. package/dist/gateway/debounce.js +262 -0
  93. package/dist/gateway/debounce.js.map +1 -0
  94. package/dist/gateway/index.d.ts +3 -0
  95. package/dist/gateway/index.d.ts.map +1 -0
  96. package/dist/gateway/index.js +3 -0
  97. package/dist/gateway/index.js.map +1 -0
  98. package/dist/gateway/middleware-setup.d.ts +12 -0
  99. package/dist/gateway/middleware-setup.d.ts.map +1 -0
  100. package/dist/gateway/middleware-setup.js +156 -0
  101. package/dist/gateway/middleware-setup.js.map +1 -0
  102. package/dist/index.d.ts +14 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +187 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/middleware/attachment.d.ts +11 -0
  107. package/dist/middleware/attachment.d.ts.map +1 -0
  108. package/dist/middleware/attachment.js +17 -0
  109. package/dist/middleware/attachment.js.map +1 -0
  110. package/dist/middleware/media-history.d.ts +30 -0
  111. package/dist/middleware/media-history.d.ts.map +1 -0
  112. package/dist/middleware/media-history.js +54 -0
  113. package/dist/middleware/media-history.js.map +1 -0
  114. package/dist/middleware/sticker-capture.d.ts +17 -0
  115. package/dist/middleware/sticker-capture.d.ts.map +1 -0
  116. package/dist/middleware/sticker-capture.js +97 -0
  117. package/dist/middleware/sticker-capture.js.map +1 -0
  118. package/dist/model/index.d.ts +10 -0
  119. package/dist/model/index.d.ts.map +1 -0
  120. package/dist/model/index.js +9 -0
  121. package/dist/model/index.js.map +1 -0
  122. package/dist/model/model-resolver.d.ts +88 -0
  123. package/dist/model/model-resolver.d.ts.map +1 -0
  124. package/dist/model/model-resolver.js +151 -0
  125. package/dist/model/model-resolver.js.map +1 -0
  126. package/dist/model/prefs-store.d.ts +24 -0
  127. package/dist/model/prefs-store.d.ts.map +1 -0
  128. package/dist/model/prefs-store.js +126 -0
  129. package/dist/model/prefs-store.js.map +1 -0
  130. package/dist/model/settings-reader.d.ts +19 -0
  131. package/dist/model/settings-reader.d.ts.map +1 -0
  132. package/dist/model/settings-reader.js +76 -0
  133. package/dist/model/settings-reader.js.map +1 -0
  134. package/dist/model/types.d.ts +15 -0
  135. package/dist/model/types.d.ts.map +1 -0
  136. package/dist/model/types.js +5 -0
  137. package/dist/model/types.js.map +1 -0
  138. package/dist/session/idle-evictor.d.ts +18 -0
  139. package/dist/session/idle-evictor.d.ts.map +1 -0
  140. package/dist/session/idle-evictor.js +31 -0
  141. package/dist/session/idle-evictor.js.map +1 -0
  142. package/dist/session/index.d.ts +9 -0
  143. package/dist/session/index.d.ts.map +1 -0
  144. package/dist/session/index.js +8 -0
  145. package/dist/session/index.js.map +1 -0
  146. package/dist/session/session-manager.d.ts +97 -0
  147. package/dist/session/session-manager.d.ts.map +1 -0
  148. package/dist/session/session-manager.js +624 -0
  149. package/dist/session/session-manager.js.map +1 -0
  150. package/dist/session/types.d.ts +130 -0
  151. package/dist/session/types.d.ts.map +1 -0
  152. package/dist/session/types.js +2 -0
  153. package/dist/session/types.js.map +1 -0
  154. package/dist/session/workspace-attach.d.ts +22 -0
  155. package/dist/session/workspace-attach.d.ts.map +1 -0
  156. package/dist/session/workspace-attach.js +21 -0
  157. package/dist/session/workspace-attach.js.map +1 -0
  158. package/dist/setup.d.ts +25 -0
  159. package/dist/setup.d.ts.map +1 -0
  160. package/dist/setup.js +198 -0
  161. package/dist/setup.js.map +1 -0
  162. package/dist/shared/index.d.ts +7 -0
  163. package/dist/shared/index.d.ts.map +1 -0
  164. package/dist/shared/index.js +7 -0
  165. package/dist/shared/index.js.map +1 -0
  166. package/dist/shared/scope.d.ts +14 -0
  167. package/dist/shared/scope.d.ts.map +1 -0
  168. package/dist/shared/scope.js +12 -0
  169. package/dist/shared/scope.js.map +1 -0
  170. package/dist/shared/send-helper.d.ts +11 -0
  171. package/dist/shared/send-helper.d.ts.map +1 -0
  172. package/dist/shared/send-helper.js +13 -0
  173. package/dist/shared/send-helper.js.map +1 -0
  174. package/dist/shared/utils.d.ts +28 -0
  175. package/dist/shared/utils.d.ts.map +1 -0
  176. package/dist/shared/utils.js +143 -0
  177. package/dist/shared/utils.js.map +1 -0
  178. package/dist/transport/attachment.d.ts +18 -0
  179. package/dist/transport/attachment.d.ts.map +1 -0
  180. package/dist/transport/attachment.js +106 -0
  181. package/dist/transport/attachment.js.map +1 -0
  182. package/dist/transport/chunker.d.ts +14 -0
  183. package/dist/transport/chunker.d.ts.map +1 -0
  184. package/dist/transport/chunker.js +78 -0
  185. package/dist/transport/chunker.js.map +1 -0
  186. package/dist/transport/events.d.ts +80 -0
  187. package/dist/transport/events.d.ts.map +1 -0
  188. package/dist/transport/events.js +62 -0
  189. package/dist/transport/events.js.map +1 -0
  190. package/dist/transport/inbound.d.ts +8 -0
  191. package/dist/transport/inbound.d.ts.map +1 -0
  192. package/dist/transport/inbound.js +292 -0
  193. package/dist/transport/inbound.js.map +1 -0
  194. package/dist/transport/index.d.ts +10 -0
  195. package/dist/transport/index.d.ts.map +1 -0
  196. package/dist/transport/index.js +10 -0
  197. package/dist/transport/index.js.map +1 -0
  198. package/dist/transport/inject-rules.d.ts +24 -0
  199. package/dist/transport/inject-rules.d.ts.map +1 -0
  200. package/dist/transport/inject-rules.js +72 -0
  201. package/dist/transport/inject-rules.js.map +1 -0
  202. package/dist/transport/outbound-buffer.d.ts +56 -0
  203. package/dist/transport/outbound-buffer.d.ts.map +1 -0
  204. package/dist/transport/outbound-buffer.js +131 -0
  205. package/dist/transport/outbound-buffer.js.map +1 -0
  206. package/dist/transport/outbound.d.ts +30 -0
  207. package/dist/transport/outbound.d.ts.map +1 -0
  208. package/dist/transport/outbound.js +139 -0
  209. package/dist/transport/outbound.js.map +1 -0
  210. package/dist/transport/rich-media.d.ts +41 -0
  211. package/dist/transport/rich-media.d.ts.map +1 -0
  212. package/dist/transport/rich-media.js +104 -0
  213. package/dist/transport/rich-media.js.map +1 -0
  214. package/dist/transport/streaming-writer.d.ts +47 -0
  215. package/dist/transport/streaming-writer.d.ts.map +1 -0
  216. package/dist/transport/streaming-writer.js +110 -0
  217. package/dist/transport/streaming-writer.js.map +1 -0
  218. package/dist/transport/tool-presenter.d.ts +38 -0
  219. package/dist/transport/tool-presenter.d.ts.map +1 -0
  220. package/dist/transport/tool-presenter.js +121 -0
  221. package/dist/transport/tool-presenter.js.map +1 -0
  222. package/dist/types.d.ts +45 -0
  223. package/dist/types.d.ts.map +1 -0
  224. package/dist/types.js +5 -0
  225. package/dist/types.js.map +1 -0
  226. package/package.json +106 -0
  227. package/settings-host.js +927 -0
@@ -0,0 +1,78 @@
1
+ /**
2
+ * qq-approval.ts — QQ 远程审批: 把 dsh 的权限申请变成 QQ 消息, 由任务发起者在 QQ 里放行/拒绝
3
+ *
4
+ * 思路来源(必须注明):
5
+ * - wang-22-code/dsh-qqbot-bridge 的 src/approval.ts(QqApprovalController 设计)
6
+ * https://github.com/wang-22-code/dsh-qqbot-bridge(README「QQ 权限审批」)
7
+ * - 宿主机制为 dsh 标准事件 ctx.on('approval/request', (request, next) => …),
8
+ * 官方 dsh-acp、dsh-web 审批弹窗同款接线(请求带 request.agent / request.callId,
9
+ * 处理器返回 'allowed-once' | 'rejected' | 'cancelled', 或调用 next() 走宿主默认)。
10
+ *
11
+ * 本实现按本项目结构重写(非拷贝):
12
+ * - 发送走本项目 sender.sendMarkdown(自带 markdown→text→wakeup 降级链与撤回记录);
13
+ * - 会话定位用本项目 SessionManager.findByAgent(带 agent.id 兜底);
14
+ * - 入站拦截挂在本项目 gateway 的 bot.on('message') 最先(消费后不再进 agent)。
15
+ *
16
+ * 流程: 宿主 approval/request → 向"发起者所在会话"推 QQ 审批提示(一次性 CODE)
17
+ * → 入站 /approve|/deny CODE(或 允许/拒绝 CODE)被拦截
18
+ * → 校验"发起者本人 + 同会话" → 结算 allow-once / rejected。
19
+ * 边界: CODE 一次性; approvalTimeoutMs 超时自动拒绝; agent 取消/宿主退出自动取消;
20
+ * 只授权当前这一次操作; 群聊里其他成员看到 CODE 也无法批准(senderId 校验)。
21
+ */
22
+ import type { ReplyTarget } from '@tencent-connect/qqbot-nodejs';
23
+ import type { QQBotSender } from '../transport/outbound-buffer.js';
24
+ import type { SessionManager } from '../session/index.js';
25
+ import type { Logger } from '../types.js';
26
+ export type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
27
+ /** dsh 宿主 approval/request 事件的请求形状(与本项目实际用到的字段) */
28
+ export interface ApprovalRequestLike {
29
+ agent: unknown;
30
+ toolName?: string;
31
+ reason?: string;
32
+ callId?: string;
33
+ signal?: AbortSignal;
34
+ }
35
+ /** 入站消息的最小只读形状(与 transport/inbound.ts 的 msg 同形) */
36
+ interface ApprovableMessage {
37
+ kind?: string;
38
+ senderId?: string;
39
+ content?: string;
40
+ groupOpenid?: string;
41
+ messageId?: string;
42
+ [key: string]: unknown;
43
+ }
44
+ export interface ParsedApprovalCommand {
45
+ outcome: 'allowed-once' | 'rejected';
46
+ code: string;
47
+ }
48
+ /** 只解析显式审批命令; 普通聊天消息原样放行给 agent */
49
+ export declare function parseApprovalCommand(content: string): ParsedApprovalCommand | null;
50
+ /** QQ 载体的一次性审批 answerer(dsh approval/request → QQ 提示 → /approve CODE) */
51
+ export declare class QqApprovalController {
52
+ private readonly manager;
53
+ private readonly sender;
54
+ private readonly logger;
55
+ /** 超时(ms)提供器: 每次 request 现读, 支持 Web 设置热改生效 */
56
+ private readonly timeoutMsProvider;
57
+ private readonly pending;
58
+ constructor(manager: SessionManager, sender: QQBotSender, logger: Logger,
59
+ /** 超时(ms)提供器: 每次 request 现读, 支持 Web 设置热改生效 */
60
+ timeoutMsProvider: () => number);
61
+ /** 宿主 approval/request 处理器: 定位发起者会话并发 QQ 审批提示 */
62
+ request(req: ApprovalRequestLike, next: () => Promise<ApprovalOutcome>): Promise<ApprovalOutcome>;
63
+ /**
64
+ * 入站拦截(挂在 bot.on('message') 最前): 命中 /approve|/deny CODE 则结算并消费消息。
65
+ * @returns true = 消息已被审批逻辑消费(调用方不要再派发给 agent)
66
+ */
67
+ handleInbound(msg: ApprovableMessage, replyTarget: ReplyTarget): Promise<boolean>;
68
+ /** 结算: 清定时器/中止监听/pending 项并 resolve */
69
+ private settle;
70
+ dispose(): void;
71
+ }
72
+ type ApprovalDispatch = (req: ApprovalRequestLike, next: () => Promise<ApprovalOutcome>) => Promise<ApprovalOutcome>;
73
+ /** bootstrap 注册实际处理器(内含开关闸门 + ownership 由调用方处理); 传 undefined 可卸载 */
74
+ export declare function setApprovalDispatch(fn: ApprovalDispatch | undefined): void;
75
+ /** 生成挂在插件 apply ctx 的审批监听(ACP 模式): 非本 bot agent / 未启用 → next() 快速放行 */
76
+ export declare function makeApprovalListener(): (req: unknown, next: () => Promise<string>) => Promise<string>;
77
+ export {};
78
+ //# sourceMappingURL=qq-approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qq-approval.d.ts","sourceRoot":"","sources":["../../src/features/qq-approval.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAExF,mDAAmD;AACnD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,mDAAmD;AACnD,UAAU,iBAAiB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAWD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,cAAc,GAAG,UAAU,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAUlF;AAED,yEAAyE;AACzE,qBAAa,oBAAoB;IAI7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAPpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;gBAG3C,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM;IAC/B,8CAA8C;IAC7B,iBAAiB,EAAE,MAAM,MAAM;IAGlD,iDAAiD;IAC3C,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IA6CvG;;;OAGG;IACG,aAAa,CAAC,GAAG,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAmDvF,uCAAuC;IACvC,OAAO,CAAC,MAAM;IAWd,OAAO,IAAI,IAAI;CAGhB;AAeD,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAIrH,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAE1E;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAMrG"}
@@ -0,0 +1,165 @@
1
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
2
+ /** 只解析显式审批命令; 普通聊天消息原样放行给 agent */
3
+ export function parseApprovalCommand(content) {
4
+ const normalized = content.trim();
5
+ const match = normalized.match(/^\/(approve|allow|deny|reject)\s+([A-Z0-9]{6})$/i)
6
+ ?? normalized.match(/^(允许|同意|拒绝|不同意)\s+([A-Z0-9]{6})$/i);
7
+ if (!match)
8
+ return null;
9
+ const action = match[1].toLowerCase();
10
+ return {
11
+ outcome: ['approve', 'allow', '允许', '同意'].includes(action) ? 'allowed-once' : 'rejected',
12
+ code: match[2].toUpperCase(),
13
+ };
14
+ }
15
+ /** QQ 载体的一次性审批 answerer(dsh approval/request → QQ 提示 → /approve CODE) */
16
+ export class QqApprovalController {
17
+ manager;
18
+ sender;
19
+ logger;
20
+ timeoutMsProvider;
21
+ pending = new Map();
22
+ constructor(manager, sender, logger,
23
+ /** 超时(ms)提供器: 每次 request 现读, 支持 Web 设置热改生效 */
24
+ timeoutMsProvider) {
25
+ this.manager = manager;
26
+ this.sender = sender;
27
+ this.logger = logger;
28
+ this.timeoutMsProvider = timeoutMsProvider;
29
+ }
30
+ /** 宿主 approval/request 处理器: 定位发起者会话并发 QQ 审批提示 */
31
+ async request(req, next) {
32
+ const record = this.manager.findByAgent(req.agent);
33
+ if (!record)
34
+ return next();
35
+ if (req.signal?.aborted)
36
+ return 'cancelled';
37
+ const timeoutMs = Math.max(1000, this.timeoutMsProvider() || 120000);
38
+ // 一次性 6 位大写码(与 parseApprovalCommand 正则一致)
39
+ let code = '';
40
+ do {
41
+ code = Array.from({ length: 6 }, () => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'[Math.floor(Math.random() * 36)]).join('');
42
+ } while (this.pending.has(code));
43
+ const outcome = new Promise((resolve) => {
44
+ const timer = setTimeout(() => this.settle(code, 'rejected'), timeoutMs);
45
+ const pending = { record, toolName: String(req.toolName ?? req.callId ?? ''), resolve, timer, signal: req.signal };
46
+ if (req.signal) {
47
+ pending.onAbort = () => this.settle(code, 'cancelled');
48
+ req.signal.addEventListener('abort', pending.onAbort, { once: true });
49
+ }
50
+ this.pending.set(code, pending);
51
+ });
52
+ const reason = String(req.reason ?? '').replace(/[\r\n]+/g, ' ').slice(0, 200);
53
+ const tool = String(req.toolName ?? req.callId ?? '未知工具');
54
+ const seconds = Math.ceil(timeoutMs / 1000);
55
+ const prompt = [
56
+ '⚠️ **DSH 权限申请**',
57
+ `工具:${tool}`,
58
+ reason ? `原因:${reason}` : '',
59
+ '',
60
+ `允许本次操作:\`/approve ${code}\``,
61
+ `拒绝本次操作:\`/deny ${code}\``,
62
+ `仅本次有效,${seconds} 秒后自动拒绝。`,
63
+ ].filter(Boolean).join('\n');
64
+ try {
65
+ await this.sender.sendMarkdown(record.replyTarget, prompt);
66
+ this.logger.info(`QQ approval requested: code=${code} tool=${tool}`);
67
+ }
68
+ catch (err) {
69
+ this.logger.error(`QQ approval prompt failed: ${err instanceof Error ? err.message : String(err)}`);
70
+ this.settle(code, 'unavailable');
71
+ }
72
+ return outcome;
73
+ }
74
+ /**
75
+ * 入站拦截(挂在 bot.on('message') 最前): 命中 /approve|/deny CODE 则结算并消费消息。
76
+ * @returns true = 消息已被审批逻辑消费(调用方不要再派发给 agent)
77
+ */
78
+ async handleInbound(msg, replyTarget) {
79
+ const command = parseApprovalCommand(msg.content ?? '');
80
+ if (!command)
81
+ return false;
82
+ const pending = this.pending.get(command.code);
83
+ if (!pending) {
84
+ try {
85
+ await this.sender.sendMarkdown(replyTarget, '该权限申请不存在或已过期。');
86
+ }
87
+ catch { /* ignore */ }
88
+ return true;
89
+ }
90
+ // 双校验: 必须是"任务发起者本人"且"同一会话"(群聊里其他成员看到 CODE 也不能批)
91
+ const rec = pending.record;
92
+ const sameSender = msg.senderId === rec.senderId;
93
+ const samePeer = rec.scope === 'c2c'
94
+ ? msg.kind === 'c2c' && msg.senderId === rec.peerId
95
+ : msg.kind === 'group' && msg.groupOpenid === rec.peerId;
96
+ if (!sameSender || !samePeer) {
97
+ try {
98
+ await this.sender.sendMarkdown(replyTarget, '你无权处理这项权限申请。');
99
+ }
100
+ catch { /* ignore */ }
101
+ return true;
102
+ }
103
+ this.settle(command.code, command.outcome);
104
+ // 留痕(不打扰, 正经注入): 用 agent.inject 注入 plugin/notice 消息 —— 不唤醒、排队下个 step
105
+ // 组包(claim 顺序 next-step 先于 next-turn → 排在用户消息前同批进请求); UI 呈现为 context 行,
106
+ // 不再冒充 kind:'user' 发言。宿主无 inject 能力时静默跳过(审计靠 QQ 端 ✅/❌ 回复)。
107
+ const toolName = pending.toolName || '';
108
+ try {
109
+ const agent = pending.record.agent;
110
+ if (agent && typeof agent.inject === 'function') {
111
+ const allowed = command.outcome === 'allowed-once';
112
+ agent.inject(createUserMessage({
113
+ content: [{
114
+ type: 'text',
115
+ text: `主人已在 QQ ${allowed ? '批准' : '拒绝'}一次工具权限申请${toolName ? `(工具: ${toolName})` : ''}。`,
116
+ }],
117
+ source: {
118
+ kind: 'plugin', plugin: 'qqbot-approval', form: 'notice',
119
+ summary: `主人${allowed ? '批准' : '拒绝'}了工具${toolName || ''}的权限申请`,
120
+ },
121
+ }));
122
+ }
123
+ }
124
+ catch { /* 注入失败不影响裁决 */ }
125
+ try {
126
+ await this.sender.sendMarkdown(replyTarget, command.outcome === 'allowed-once' ? '✅ 已允许本次操作。' : '❌ 已拒绝本次操作。');
127
+ }
128
+ catch { /* ignore */ }
129
+ return true;
130
+ }
131
+ /** 结算: 清定时器/中止监听/pending 项并 resolve */
132
+ settle(code, outcome) {
133
+ const pending = this.pending.get(code);
134
+ if (!pending)
135
+ return;
136
+ this.pending.delete(code);
137
+ clearTimeout(pending.timer);
138
+ if (pending.signal && pending.onAbort) {
139
+ try {
140
+ pending.signal.removeEventListener('abort', pending.onAbort);
141
+ }
142
+ catch { /* ignore */ }
143
+ }
144
+ pending.resolve(outcome);
145
+ }
146
+ dispose() {
147
+ for (const code of [...this.pending.keys()])
148
+ this.settle(code, 'cancelled');
149
+ }
150
+ }
151
+ let dispatch;
152
+ /** bootstrap 注册实际处理器(内含开关闸门 + ownership 由调用方处理); 传 undefined 可卸载 */
153
+ export function setApprovalDispatch(fn) {
154
+ dispatch = fn;
155
+ }
156
+ /** 生成挂在插件 apply ctx 的审批监听(ACP 模式): 非本 bot agent / 未启用 → next() 快速放行 */
157
+ export function makeApprovalListener() {
158
+ return ((req, next) => {
159
+ console.log('[qq-approval] ctx listener fired');
160
+ if (!dispatch)
161
+ return next();
162
+ return dispatch(req, next);
163
+ });
164
+ }
165
+ //# sourceMappingURL=qq-approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qq-approval.js","sourceRoot":"","sources":["../../src/features/qq-approval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { Logger } from '../types.js';
2
+ export type TimerKind = 'once' | 'daily';
3
+ export interface TimerJob {
4
+ id: string;
5
+ kind: TimerKind;
6
+ /** 目标会话(scope/peerId 与 qqbot sessionKey 对应) */
7
+ scope: 'group' | 'c2c';
8
+ peerId: string;
9
+ /** 每天触发时刻 HH:MM(daily 必填; once 且用 atTime 时也填) */
10
+ atTime?: string;
11
+ /** 一次性: 绝对触发时间(ms); seconds 相对或 atTime 换算后写入 */
12
+ dueAt?: number;
13
+ /** 到点注入会话的提示内容 */
14
+ prompt: string;
15
+ createdAt: number;
16
+ /** daily 上次触发日期(YYYY-MM-DD), 防同一天重复 */
17
+ lastRunDate?: string;
18
+ enabled: boolean;
19
+ }
20
+ export interface TimerCreateInput {
21
+ kind?: TimerKind;
22
+ scope: 'group' | 'c2c';
23
+ peerId: string;
24
+ /** 相对秒(once; 至少与 atTime 一个) */
25
+ seconds?: number;
26
+ /** 几点几分 HH:MM(24h; daily 必填; once 可选=今天该点, 已过则明天) */
27
+ atTime?: string;
28
+ prompt: string;
29
+ }
30
+ export declare class ScheduleStore {
31
+ private jobs;
32
+ private saveTimer;
33
+ private dirty;
34
+ readonly dataDir: string;
35
+ private logger?;
36
+ constructor(dataDir: string, logger?: Logger);
37
+ private file;
38
+ private load;
39
+ private save;
40
+ flush(): void;
41
+ /** 新建任务; 返回 {ok, id?, error?}。kind 缺省: atTime 且无 seconds→daily, 否则 once */
42
+ add(input: TimerCreateInput): {
43
+ ok: boolean;
44
+ id?: string;
45
+ error?: string;
46
+ };
47
+ get(id: string): TimerJob | undefined;
48
+ /** 下次触发时间戳(用于展示; once=dueAt, daily=今天该点(已过则明天); 禁用/null) */
49
+ nextFireAt(job: TimerJob, now?: number): number | null;
50
+ list(): TimerJob[];
51
+ remove(id: string): boolean;
52
+ /** 当前应触发的任务(由 ticker 调用) */
53
+ due(now?: number): TimerJob[];
54
+ /** 触发后记账: once 删除; daily 记今天 */
55
+ markDone(id: string): void;
56
+ setEnabled(id: string, enabled: boolean): boolean;
57
+ }
58
+ /** 配置定时任务实例(启动早期每账号按自己 dataDir 预初始化; 同目录幂等) */
59
+ export declare function configureScheduleStore(dataDir: string, logger?: Logger): ScheduleStore;
60
+ /** 获取实例。带 dataDir → 按目录取(不在则容错新建); 无参 → primary(主账号, 兼容旧调用)。 */
61
+ export declare function getScheduleStore(dataDir?: string, logger?: Logger): ScheduleStore;
62
+ //# sourceMappingURL=schedule-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-store.d.ts","sourceRoot":"","sources":["../../src/features/schedule-store.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,+CAA+C;IAC/C,KAAK,EAAE,OAAO,GAAG,KAAK,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,KAAK,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD,qBAAa,aAAa;IACxB,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,KAAK,CAAS;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,MAAM,CAAC,CAAS;gBAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAO5C,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,IAAI;IAkBZ,KAAK,IAAI,IAAI;IAOb,2EAA2E;IAC3E,GAAG,CAAC,KAAK,EAAE,gBAAgB,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAsC1E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIrC,4DAA4D;IAC5D,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,SAAa,GAAG,MAAM,GAAG,IAAI;IAY1D,IAAI,IAAI,QAAQ,EAAE;IAIlB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQ3B,4BAA4B;IAC5B,GAAG,CAAC,GAAG,SAAa,GAAG,QAAQ,EAAE;IAejC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAa1B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO;CAOlD;AAMD,+CAA+C;AAC/C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAStF;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAoBjF"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * schedule-store.ts — QQ 会话自我定时任务(一次性/每日)存储与调度
3
+ *
4
+ * AI(QQ 会话内)可给自己安排定时提醒/任务: 到点后把 prompt 注入原会话(followup),
5
+ * 她会像收到新消息一样处理并回复到 QQ(带群聊上下文)。
6
+ * 数据落盘 {dataDir}/timers.json, 重启不丢; bootstrap 启动一个 30s ticker 驱动。
7
+ * 类型: once(一次性: seconds 相对秒 或 atTime 今天某时) / daily(每天 HH:MM)。
8
+ *
9
+ * ⚠️ 本地手改功能(fork 新增)。
10
+ */
11
+ import { mkdirSync, readFileSync, writeFileSync, existsSync, renameSync } from 'node:fs';
12
+ import { join, resolve } from 'node:path';
13
+ const FILE_NAME = 'timers.json';
14
+ /** 最小相对秒(与 ticker 粒度对齐) */
15
+ const MIN_SECONDS = 30;
16
+ /** 单会话(scope:peer)最多启用的任务数 */
17
+ const MAX_PER_PEER = 20;
18
+ /** 全局任务总数上限 */
19
+ const MAX_GLOBAL = 200;
20
+ /** 解析 HH:MM → 当天该时刻 ms; 已过则明天 */
21
+ function dueAtFromHHMM(hhmm, base = Date.now()) {
22
+ const m = /^(\d{1,2}):(\d{2})$/.exec((hhmm ?? '').trim());
23
+ if (!m)
24
+ return NaN;
25
+ const h = Math.min(23, Number(m[1]));
26
+ const min = Math.min(59, Number(m[2]));
27
+ const d = new Date(base);
28
+ d.setHours(h, min, 0, 0);
29
+ if (d.getTime() <= base)
30
+ d.setDate(d.getDate() + 1); // 已过 → 明天
31
+ return d.getTime();
32
+ }
33
+ export class ScheduleStore {
34
+ jobs = [];
35
+ saveTimer = null;
36
+ dirty = false;
37
+ dataDir;
38
+ logger;
39
+ constructor(dataDir, logger) {
40
+ this.dataDir = dataDir;
41
+ this.logger = logger;
42
+ mkdirSync(dataDir, { recursive: true });
43
+ this.load();
44
+ }
45
+ file() {
46
+ return join(this.dataDir, FILE_NAME);
47
+ }
48
+ load() {
49
+ try {
50
+ const p = this.file();
51
+ if (existsSync(p)) {
52
+ const raw = JSON.parse(readFileSync(p, 'utf8'));
53
+ this.jobs = Array.isArray(raw.jobs) ? raw.jobs : [];
54
+ }
55
+ }
56
+ catch {
57
+ this.jobs = [];
58
+ }
59
+ }
60
+ save() {
61
+ this.dirty = true;
62
+ if (this.saveTimer)
63
+ return;
64
+ this.saveTimer = setTimeout(() => {
65
+ this.saveTimer = null;
66
+ if (!this.dirty)
67
+ return;
68
+ this.dirty = false;
69
+ try {
70
+ const p = this.file();
71
+ const tmp = `${p}.tmp`;
72
+ writeFileSync(tmp, JSON.stringify({ version: 1, jobs: this.jobs }, null, 2), 'utf8');
73
+ renameSync(tmp, p);
74
+ }
75
+ catch (err) {
76
+ this.logger?.warn?.(`[schedule] save failed: ${err instanceof Error ? err.message : String(err)}`);
77
+ }
78
+ }, 300);
79
+ }
80
+ flush() {
81
+ if (this.saveTimer) {
82
+ clearTimeout(this.saveTimer);
83
+ this.saveTimer = null;
84
+ }
85
+ if (!this.dirty)
86
+ return;
87
+ this.dirty = false;
88
+ try {
89
+ writeFileSync(this.file(), JSON.stringify({ version: 1, jobs: this.jobs }, null, 2), 'utf8');
90
+ }
91
+ catch { /* ignore */ }
92
+ }
93
+ /** 新建任务; 返回 {ok, id?, error?}。kind 缺省: atTime 且无 seconds→daily, 否则 once */
94
+ add(input) {
95
+ const kind = input.kind ?? (input.atTime && !input.seconds ? 'daily' : 'once');
96
+ const now = Date.now();
97
+ const seconds = Number(input.seconds);
98
+ const hasSec = Number.isFinite(seconds) && seconds > 0;
99
+ const hasAt = /^(\d{1,2}):(\d{2})$/.test((input.atTime ?? '').trim());
100
+ const key = `${input.scope}:${input.peerId}`;
101
+ if (kind === 'once' && hasSec && seconds < MIN_SECONDS)
102
+ return { ok: false, error: `seconds 不能小于 ${MIN_SECONDS} 秒` };
103
+ if (!hasSec && !hasAt)
104
+ return { ok: false, error: 'seconds 与 atTime 至少要给一个' };
105
+ if (kind === 'daily' && !hasAt)
106
+ return { ok: false, error: '每日任务需提供 atTime(几点几分 HH:MM)' };
107
+ if (hasAt && !/^(\d{1,2}):(\d{2})$/.test((input.atTime ?? '').trim()))
108
+ return { ok: false, error: 'atTime 格式应为 HH:MM(如 08:30)' };
109
+ if (kind !== 'once' && kind !== 'daily')
110
+ return { ok: false, error: 'kind 只能为 once 或 daily' };
111
+ // 配额
112
+ const perPeer = this.jobs.filter(j => `${j.scope}:${j.peerId}` === key && j.enabled).length;
113
+ if (perPeer >= MAX_PER_PEER)
114
+ return { ok: false, error: `该会话定时任务已达上限(${MAX_PER_PEER}条)` };
115
+ if (this.jobs.filter(j => j.enabled).length >= MAX_GLOBAL)
116
+ return { ok: false, error: `定时任务总数已达上限(${MAX_GLOBAL}条)` };
117
+ // 去重: 同一会话同一条 daily(同 atTime)不重复建
118
+ if (kind === 'daily') {
119
+ const dup = this.jobs.find(j => j.kind === 'daily' && `${j.scope}:${j.peerId}` === key && j.atTime === input.atTime.trim() && j.enabled);
120
+ if (dup)
121
+ return { ok: false, error: `已存在相同每日任务(每天 ${dup.atTime}), 请删除后再建` };
122
+ }
123
+ const job = {
124
+ id: 't' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
125
+ kind,
126
+ scope: input.scope,
127
+ peerId: input.peerId,
128
+ prompt: (input.prompt ?? '').slice(0, 800),
129
+ createdAt: now,
130
+ enabled: true,
131
+ ...(hasAt ? { atTime: input.atTime.trim() } : {}),
132
+ ...(kind === 'once' ? { dueAt: hasSec && hasAt ? Math.min(now + seconds * 1000, dueAtFromHHMM(input.atTime.trim(), now)) : hasSec ? now + seconds * 1000 : dueAtFromHHMM(input.atTime.trim(), now) } : {}),
133
+ };
134
+ if (!job.prompt)
135
+ return { ok: false, error: 'prompt 不能为空' };
136
+ this.jobs.push(job);
137
+ this.save();
138
+ return { ok: true, id: job.id };
139
+ }
140
+ get(id) {
141
+ return this.jobs.find(j => j.id === id);
142
+ }
143
+ /** 下次触发时间戳(用于展示; once=dueAt, daily=今天该点(已过则明天); 禁用/null) */
144
+ nextFireAt(job, now = Date.now()) {
145
+ if (!job.enabled)
146
+ return null;
147
+ if (job.kind === 'once')
148
+ return job.dueAt ?? null;
149
+ const mm = /^(\d{1,2}):(\d{2})$/.exec(job.atTime ?? '');
150
+ if (!mm)
151
+ return null;
152
+ const d = new Date(now);
153
+ d.setHours(Math.min(23, Number(mm[1])), Math.min(59, Number(mm[2])), 0, 0);
154
+ const today = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
155
+ if (job.lastRunDate === today || d.getTime() <= now)
156
+ d.setDate(d.getDate() + 1);
157
+ return d.getTime();
158
+ }
159
+ list() {
160
+ return this.jobs.slice().sort((a, b) => (a.createdAt - b.createdAt));
161
+ }
162
+ remove(id) {
163
+ const i = this.jobs.findIndex(j => j.id === id);
164
+ if (i < 0)
165
+ return false;
166
+ this.jobs.splice(i, 1);
167
+ this.save();
168
+ return true;
169
+ }
170
+ /** 当前应触发的任务(由 ticker 调用) */
171
+ due(now = Date.now()) {
172
+ const today = new Date(now);
173
+ const ymd = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`;
174
+ const curMin = today.getHours() * 60 + today.getMinutes();
175
+ return this.jobs.filter(j => {
176
+ if (!j.enabled)
177
+ return false;
178
+ if (j.kind === 'once')
179
+ return j.dueAt !== undefined && j.dueAt <= now;
180
+ // daily: 到 HH:MM 且今天还没跑过
181
+ const mm = /^(\d{1,2}):(\d{2})$/.exec(j.atTime ?? '');
182
+ if (!mm)
183
+ return false;
184
+ const target = Math.min(23, Number(mm[1])) * 60 + Math.min(59, Number(mm[2]));
185
+ return curMin >= target && j.lastRunDate !== ymd;
186
+ });
187
+ }
188
+ /** 触发后记账: once 删除; daily 记今天 */
189
+ markDone(id) {
190
+ const j = this.jobs.find(x => x.id === id);
191
+ if (!j)
192
+ return;
193
+ if (j.kind === 'once') {
194
+ const i = this.jobs.indexOf(j);
195
+ if (i >= 0)
196
+ this.jobs.splice(i, 1);
197
+ }
198
+ else {
199
+ const d = new Date();
200
+ j.lastRunDate = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
201
+ }
202
+ this.save();
203
+ }
204
+ setEnabled(id, enabled) {
205
+ const j = this.jobs.find(x => x.id === id);
206
+ if (!j)
207
+ return false;
208
+ j.enabled = enabled;
209
+ this.save();
210
+ return true;
211
+ }
212
+ }
213
+ // ── 多例注册表(多账号支持, 2026-09-03): 每账号实例一个 dataDir({cwd}/.qqbot) → 各自定时任务。
214
+ const _stores = new Map();
215
+ let _primaryDir;
216
+ /** 配置定时任务实例(启动早期每账号按自己 dataDir 预初始化; 同目录幂等) */
217
+ export function configureScheduleStore(dataDir, logger) {
218
+ const key = resolve(dataDir);
219
+ let s = _stores.get(key);
220
+ if (!s) {
221
+ s = new ScheduleStore(dataDir, logger);
222
+ _stores.set(key, s);
223
+ }
224
+ if (!_primaryDir)
225
+ _primaryDir = key;
226
+ return s;
227
+ }
228
+ /** 获取实例。带 dataDir → 按目录取(不在则容错新建); 无参 → primary(主账号, 兼容旧调用)。 */
229
+ export function getScheduleStore(dataDir, logger) {
230
+ if (dataDir) {
231
+ const key = resolve(dataDir);
232
+ let s = _stores.get(key);
233
+ if (!s) {
234
+ s = new ScheduleStore(dataDir, logger);
235
+ _stores.set(key, s);
236
+ }
237
+ return s;
238
+ }
239
+ if (_primaryDir) {
240
+ const s = _stores.get(_primaryDir);
241
+ if (s)
242
+ return s;
243
+ }
244
+ if (_stores.size === 1)
245
+ return _stores.values().next().value;
246
+ const dir = join(process.cwd(), '.qqbot');
247
+ const s = new ScheduleStore(dir, logger);
248
+ _stores.set(resolve(dir), s);
249
+ if (!_primaryDir)
250
+ _primaryDir = resolve(dir);
251
+ return s;
252
+ }
253
+ //# sourceMappingURL=schedule-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-store.js","sourceRoot":"","sources":["../../src/features/schedule-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ImQQBotConfig } from '../config.js';
2
+ import type { Logger } from '../types.js';
3
+ import type { SessionManager } from '../session/index.js';
4
+ /**
5
+ * 启动定时调度器。返回 stop():停止轮询并落盘。
6
+ * config 为 live 对象引用(schedule.targets 现读, Web 设置热更新即时生效)。
7
+ */
8
+ export declare function startScheduler(manager: SessionManager, config: ImQQBotConfig, logger: Logger): () => void;
9
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/features/scheduler.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAwB,MAAM,cAAc,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAqB1D;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,IAAI,CA8GZ"}