@ryantest/openclaw-qqbot 0.0.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 (197) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +483 -0
  3. package/README.zh.md +478 -0
  4. package/bin/qqbot-cli.js +243 -0
  5. package/clawdbot.plugin.json +16 -0
  6. package/dist/index.d.ts +17 -0
  7. package/dist/index.js +26 -0
  8. package/dist/src/admin-resolver.d.ts +27 -0
  9. package/dist/src/admin-resolver.js +122 -0
  10. package/dist/src/api.d.ts +156 -0
  11. package/dist/src/api.js +599 -0
  12. package/dist/src/channel.d.ts +11 -0
  13. package/dist/src/channel.js +354 -0
  14. package/dist/src/config.d.ts +25 -0
  15. package/dist/src/config.js +161 -0
  16. package/dist/src/credential-backup.d.ts +31 -0
  17. package/dist/src/credential-backup.js +66 -0
  18. package/dist/src/gateway.d.ts +18 -0
  19. package/dist/src/gateway.js +1265 -0
  20. package/dist/src/image-server.d.ts +68 -0
  21. package/dist/src/image-server.js +462 -0
  22. package/dist/src/inbound-attachments.d.ts +58 -0
  23. package/dist/src/inbound-attachments.js +234 -0
  24. package/dist/src/known-users.d.ts +100 -0
  25. package/dist/src/known-users.js +263 -0
  26. package/dist/src/message-queue.d.ts +50 -0
  27. package/dist/src/message-queue.js +115 -0
  28. package/dist/src/onboarding.d.ts +10 -0
  29. package/dist/src/onboarding.js +203 -0
  30. package/dist/src/outbound-deliver.d.ts +48 -0
  31. package/dist/src/outbound-deliver.js +462 -0
  32. package/dist/src/outbound.d.ts +203 -0
  33. package/dist/src/outbound.js +1102 -0
  34. package/dist/src/proactive.d.ts +170 -0
  35. package/dist/src/proactive.js +399 -0
  36. package/dist/src/ref-index-store.d.ts +70 -0
  37. package/dist/src/ref-index-store.js +273 -0
  38. package/dist/src/reply-dispatcher.d.ts +35 -0
  39. package/dist/src/reply-dispatcher.js +311 -0
  40. package/dist/src/runtime.d.ts +3 -0
  41. package/dist/src/runtime.js +10 -0
  42. package/dist/src/session-store.d.ts +52 -0
  43. package/dist/src/session-store.js +254 -0
  44. package/dist/src/slash-commands.d.ts +71 -0
  45. package/dist/src/slash-commands.js +1179 -0
  46. package/dist/src/startup-greeting.d.ts +30 -0
  47. package/dist/src/startup-greeting.js +78 -0
  48. package/dist/src/stt.d.ts +21 -0
  49. package/dist/src/stt.js +70 -0
  50. package/dist/src/tools/channel.d.ts +16 -0
  51. package/dist/src/tools/channel.js +234 -0
  52. package/dist/src/tools/remind.d.ts +2 -0
  53. package/dist/src/tools/remind.js +247 -0
  54. package/dist/src/types.d.ts +175 -0
  55. package/dist/src/types.js +1 -0
  56. package/dist/src/typing-keepalive.d.ts +27 -0
  57. package/dist/src/typing-keepalive.js +64 -0
  58. package/dist/src/update-checker.d.ts +34 -0
  59. package/dist/src/update-checker.js +166 -0
  60. package/dist/src/user-messages.d.ts +8 -0
  61. package/dist/src/user-messages.js +8 -0
  62. package/dist/src/utils/audio-convert.d.ts +89 -0
  63. package/dist/src/utils/audio-convert.js +704 -0
  64. package/dist/src/utils/file-utils.d.ts +55 -0
  65. package/dist/src/utils/file-utils.js +150 -0
  66. package/dist/src/utils/image-size.d.ts +51 -0
  67. package/dist/src/utils/image-size.js +234 -0
  68. package/dist/src/utils/media-tags.d.ts +14 -0
  69. package/dist/src/utils/media-tags.js +164 -0
  70. package/dist/src/utils/payload.d.ts +112 -0
  71. package/dist/src/utils/payload.js +186 -0
  72. package/dist/src/utils/platform.d.ts +137 -0
  73. package/dist/src/utils/platform.js +390 -0
  74. package/dist/src/utils/text-parsing.d.ts +32 -0
  75. package/dist/src/utils/text-parsing.js +80 -0
  76. package/dist/src/utils/upload-cache.d.ts +34 -0
  77. package/dist/src/utils/upload-cache.js +93 -0
  78. package/index.ts +31 -0
  79. package/moltbot.plugin.json +16 -0
  80. package/node_modules/@eshaz/web-worker/LICENSE +201 -0
  81. package/node_modules/@eshaz/web-worker/README.md +134 -0
  82. package/node_modules/@eshaz/web-worker/browser.js +17 -0
  83. package/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
  84. package/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
  85. package/node_modules/@eshaz/web-worker/index.d.ts +4 -0
  86. package/node_modules/@eshaz/web-worker/node.js +223 -0
  87. package/node_modules/@eshaz/web-worker/package.json +54 -0
  88. package/node_modules/@wasm-audio-decoders/common/index.js +5 -0
  89. package/node_modules/@wasm-audio-decoders/common/package.json +36 -0
  90. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
  91. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
  92. package/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
  93. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
  94. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
  95. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
  96. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
  97. package/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
  98. package/node_modules/mpg123-decoder/README.md +265 -0
  99. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
  100. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
  101. package/node_modules/mpg123-decoder/index.js +8 -0
  102. package/node_modules/mpg123-decoder/package.json +58 -0
  103. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
  104. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
  105. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
  106. package/node_modules/mpg123-decoder/types.d.ts +30 -0
  107. package/node_modules/silk-wasm/LICENSE +21 -0
  108. package/node_modules/silk-wasm/README.md +85 -0
  109. package/node_modules/silk-wasm/lib/index.cjs +16 -0
  110. package/node_modules/silk-wasm/lib/index.d.ts +70 -0
  111. package/node_modules/silk-wasm/lib/index.mjs +16 -0
  112. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  113. package/node_modules/silk-wasm/lib/utils.d.ts +4 -0
  114. package/node_modules/silk-wasm/package.json +39 -0
  115. package/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
  116. package/node_modules/simple-yenc/.prettierignore +1 -0
  117. package/node_modules/simple-yenc/LICENSE +7 -0
  118. package/node_modules/simple-yenc/README.md +163 -0
  119. package/node_modules/simple-yenc/dist/esm.js +1 -0
  120. package/node_modules/simple-yenc/dist/index.js +1 -0
  121. package/node_modules/simple-yenc/package.json +50 -0
  122. package/node_modules/simple-yenc/rollup.config.js +27 -0
  123. package/node_modules/simple-yenc/src/simple-yenc.js +302 -0
  124. package/node_modules/ws/LICENSE +20 -0
  125. package/node_modules/ws/README.md +548 -0
  126. package/node_modules/ws/browser.js +8 -0
  127. package/node_modules/ws/index.js +13 -0
  128. package/node_modules/ws/lib/buffer-util.js +131 -0
  129. package/node_modules/ws/lib/constants.js +19 -0
  130. package/node_modules/ws/lib/event-target.js +292 -0
  131. package/node_modules/ws/lib/extension.js +203 -0
  132. package/node_modules/ws/lib/limiter.js +55 -0
  133. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  134. package/node_modules/ws/lib/receiver.js +706 -0
  135. package/node_modules/ws/lib/sender.js +602 -0
  136. package/node_modules/ws/lib/stream.js +161 -0
  137. package/node_modules/ws/lib/subprotocol.js +62 -0
  138. package/node_modules/ws/lib/validation.js +152 -0
  139. package/node_modules/ws/lib/websocket-server.js +554 -0
  140. package/node_modules/ws/lib/websocket.js +1393 -0
  141. package/node_modules/ws/package.json +69 -0
  142. package/node_modules/ws/wrapper.mjs +8 -0
  143. package/openclaw.plugin.json +16 -0
  144. package/package.json +76 -0
  145. package/scripts/cleanup-legacy-plugins.sh +124 -0
  146. package/scripts/proactive-api-server.ts +369 -0
  147. package/scripts/send-proactive.ts +293 -0
  148. package/scripts/set-markdown.sh +156 -0
  149. package/scripts/test-sendmedia.ts +116 -0
  150. package/scripts/upgrade-via-alt-pkg.sh +307 -0
  151. package/scripts/upgrade-via-npm.ps1 +296 -0
  152. package/scripts/upgrade-via-npm.sh +301 -0
  153. package/scripts/upgrade-via-source.sh +774 -0
  154. package/skills/qqbot-channel/SKILL.md +263 -0
  155. package/skills/qqbot-channel/references/api_references.md +521 -0
  156. package/skills/qqbot-media/SKILL.md +56 -0
  157. package/skills/qqbot-remind/SKILL.md +149 -0
  158. package/src/admin-resolver.ts +140 -0
  159. package/src/api.ts +819 -0
  160. package/src/bot-logs-2026-03-21T11-21-47(2).txt +46 -0
  161. package/src/channel.ts +381 -0
  162. package/src/config.ts +187 -0
  163. package/src/credential-backup.ts +72 -0
  164. package/src/gateway.log +43 -0
  165. package/src/gateway.ts +1404 -0
  166. package/src/image-server.ts +539 -0
  167. package/src/inbound-attachments.ts +304 -0
  168. package/src/known-users.ts +353 -0
  169. package/src/message-queue.ts +169 -0
  170. package/src/onboarding.ts +274 -0
  171. package/src/openclaw-2026-03-21.log +3729 -0
  172. package/src/openclaw-plugin-sdk.d.ts +522 -0
  173. package/src/outbound-deliver.ts +552 -0
  174. package/src/outbound.ts +1266 -0
  175. package/src/proactive.ts +530 -0
  176. package/src/ref-index-store.ts +357 -0
  177. package/src/reply-dispatcher.ts +334 -0
  178. package/src/runtime.ts +14 -0
  179. package/src/session-store.ts +303 -0
  180. package/src/slash-commands.ts +1305 -0
  181. package/src/startup-greeting.ts +98 -0
  182. package/src/stt.ts +86 -0
  183. package/src/tools/channel.ts +281 -0
  184. package/src/tools/remind.ts +296 -0
  185. package/src/types.ts +183 -0
  186. package/src/typing-keepalive.ts +59 -0
  187. package/src/update-checker.ts +179 -0
  188. package/src/user-messages.ts +7 -0
  189. package/src/utils/audio-convert.ts +803 -0
  190. package/src/utils/file-utils.ts +167 -0
  191. package/src/utils/image-size.ts +266 -0
  192. package/src/utils/media-tags.ts +182 -0
  193. package/src/utils/payload.ts +265 -0
  194. package/src/utils/platform.ts +435 -0
  195. package/src/utils/text-parsing.ts +82 -0
  196. package/src/utils/upload-cache.ts +128 -0
  197. package/tsconfig.json +16 -0
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Session 持久化存储
3
+ * 将 WebSocket 连接状态(sessionId、lastSeq)持久化到文件
4
+ * 支持进程重启后通过 Resume 机制快速恢复连接
5
+ */
6
+
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+
10
+ // Session 状态接口
11
+ export interface SessionState {
12
+ /** WebSocket Session ID */
13
+ sessionId: string | null;
14
+ /** 最后收到的消息序号 */
15
+ lastSeq: number | null;
16
+ /** 上次连接成功的时间戳 */
17
+ lastConnectedAt: number;
18
+ /** 上次成功的权限级别索引 */
19
+ intentLevelIndex: number;
20
+ /** 关联的机器人账户 ID */
21
+ accountId: string;
22
+ /** 保存时间 */
23
+ savedAt: number;
24
+ /** 创建此 session 时使用的 appId(用于检测凭据变更) */
25
+ appId?: string;
26
+ }
27
+
28
+ import { getQQBotDataDir } from "./utils/platform.js";
29
+
30
+ // Session 文件目录
31
+ const SESSION_DIR = getQQBotDataDir("sessions");
32
+
33
+ // Session 过期时间(5分钟)- Resume 要求在断开后一定时间内恢复
34
+ const SESSION_EXPIRE_TIME = 5 * 60 * 1000;
35
+
36
+ // 写入节流时间(避免频繁写入)
37
+ const SAVE_THROTTLE_MS = 1000;
38
+
39
+ // 每个账户的节流状态
40
+ const throttleState = new Map<string, {
41
+ pendingState: SessionState | null;
42
+ lastSaveTime: number;
43
+ throttleTimer: ReturnType<typeof setTimeout> | null;
44
+ }>();
45
+
46
+ /**
47
+ * 确保目录存在
48
+ */
49
+ function ensureDir(): void {
50
+ if (!fs.existsSync(SESSION_DIR)) {
51
+ fs.mkdirSync(SESSION_DIR, { recursive: true });
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 获取 Session 文件路径
57
+ */
58
+ function getSessionPath(accountId: string): string {
59
+ // 清理 accountId 中的特殊字符
60
+ const safeId = accountId.replace(/[^a-zA-Z0-9_-]/g, "_");
61
+ return path.join(SESSION_DIR, `session-${safeId}.json`);
62
+ }
63
+
64
+ /**
65
+ * 加载 Session 状态
66
+ * @param accountId 账户 ID
67
+ * @param expectedAppId 当前使用的 appId,如果与保存时的 appId 不匹配则视为失效
68
+ * @returns Session 状态,如果不存在、已过期或 appId 不匹配返回 null
69
+ */
70
+ export function loadSession(accountId: string, expectedAppId?: string): SessionState | null {
71
+ const filePath = getSessionPath(accountId);
72
+
73
+ try {
74
+ if (!fs.existsSync(filePath)) {
75
+ return null;
76
+ }
77
+
78
+ const data = fs.readFileSync(filePath, "utf-8");
79
+ const state = JSON.parse(data) as SessionState;
80
+
81
+ // 检查是否过期
82
+ const now = Date.now();
83
+ if (now - state.savedAt > SESSION_EXPIRE_TIME) {
84
+ console.log(`[session-store] Session expired for ${accountId}, age: ${Math.round((now - state.savedAt) / 1000)}s`);
85
+ try {
86
+ fs.unlinkSync(filePath);
87
+ } catch {
88
+ // 忽略删除错误
89
+ }
90
+ return null;
91
+ }
92
+
93
+ // 检查 appId 是否匹配(凭据变更检测)
94
+ if (expectedAppId && state.appId && state.appId !== expectedAppId) {
95
+ console.log(`[session-store] appId mismatch for ${accountId}: saved=${state.appId}, current=${expectedAppId}. Discarding stale session.`);
96
+ try {
97
+ fs.unlinkSync(filePath);
98
+ } catch {
99
+ // 忽略删除错误
100
+ }
101
+ return null;
102
+ }
103
+
104
+ // 验证必要字段
105
+ if (!state.sessionId || state.lastSeq === null || state.lastSeq === undefined) {
106
+ console.log(`[session-store] Invalid session data for ${accountId}`);
107
+ return null;
108
+ }
109
+
110
+ console.log(`[session-store] Loaded session for ${accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}, appId=${state.appId ?? "unknown"}, age=${Math.round((now - state.savedAt) / 1000)}s`);
111
+ return state;
112
+ } catch (err) {
113
+ console.error(`[session-store] Failed to load session for ${accountId}: ${err}`);
114
+ return null;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * 保存 Session 状态(带节流,避免频繁写入)
120
+ * @param state Session 状态
121
+ */
122
+ export function saveSession(state: SessionState): void {
123
+ const { accountId } = state;
124
+
125
+ // 获取或初始化节流状态
126
+ let throttle = throttleState.get(accountId);
127
+ if (!throttle) {
128
+ throttle = {
129
+ pendingState: null,
130
+ lastSaveTime: 0,
131
+ throttleTimer: null,
132
+ };
133
+ throttleState.set(accountId, throttle);
134
+ }
135
+
136
+ const now = Date.now();
137
+ const timeSinceLastSave = now - throttle.lastSaveTime;
138
+
139
+ // 如果距离上次保存时间足够长,立即保存
140
+ if (timeSinceLastSave >= SAVE_THROTTLE_MS) {
141
+ doSaveSession(state);
142
+ throttle.lastSaveTime = now;
143
+ throttle.pendingState = null;
144
+
145
+ // 清除待定的节流定时器
146
+ if (throttle.throttleTimer) {
147
+ clearTimeout(throttle.throttleTimer);
148
+ throttle.throttleTimer = null;
149
+ }
150
+ } else {
151
+ // 记录待保存的状态
152
+ throttle.pendingState = state;
153
+
154
+ // 如果没有设置定时器,设置一个
155
+ if (!throttle.throttleTimer) {
156
+ const delay = SAVE_THROTTLE_MS - timeSinceLastSave;
157
+ throttle.throttleTimer = setTimeout(() => {
158
+ const t = throttleState.get(accountId);
159
+ if (t && t.pendingState) {
160
+ doSaveSession(t.pendingState);
161
+ t.lastSaveTime = Date.now();
162
+ t.pendingState = null;
163
+ }
164
+ if (t) {
165
+ t.throttleTimer = null;
166
+ }
167
+ }, delay);
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * 实际执行保存操作
174
+ */
175
+ function doSaveSession(state: SessionState): void {
176
+ const filePath = getSessionPath(state.accountId);
177
+
178
+ try {
179
+ ensureDir();
180
+
181
+ // 更新保存时间
182
+ const stateToSave: SessionState = {
183
+ ...state,
184
+ savedAt: Date.now(),
185
+ };
186
+
187
+ fs.writeFileSync(filePath, JSON.stringify(stateToSave, null, 2), "utf-8");
188
+ console.log(`[session-store] Saved session for ${state.accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}`);
189
+ } catch (err) {
190
+ console.error(`[session-store] Failed to save session for ${state.accountId}: ${err}`);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * 清除 Session 状态
196
+ * @param accountId 账户 ID
197
+ */
198
+ export function clearSession(accountId: string): void {
199
+ const filePath = getSessionPath(accountId);
200
+
201
+ // 清除节流状态
202
+ const throttle = throttleState.get(accountId);
203
+ if (throttle) {
204
+ if (throttle.throttleTimer) {
205
+ clearTimeout(throttle.throttleTimer);
206
+ }
207
+ throttleState.delete(accountId);
208
+ }
209
+
210
+ try {
211
+ if (fs.existsSync(filePath)) {
212
+ fs.unlinkSync(filePath);
213
+ console.log(`[session-store] Cleared session for ${accountId}`);
214
+ }
215
+ } catch (err) {
216
+ console.error(`[session-store] Failed to clear session for ${accountId}: ${err}`);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * 更新 lastSeq(轻量级更新)
222
+ * @param accountId 账户 ID
223
+ * @param lastSeq 最新的消息序号
224
+ */
225
+ export function updateLastSeq(accountId: string, lastSeq: number): void {
226
+ const existing = loadSession(accountId);
227
+ if (existing && existing.sessionId) {
228
+ saveSession({
229
+ ...existing,
230
+ lastSeq,
231
+ });
232
+ }
233
+ }
234
+
235
+ /**
236
+ * 获取所有保存的 Session 状态
237
+ */
238
+ export function getAllSessions(): SessionState[] {
239
+ const sessions: SessionState[] = [];
240
+
241
+ try {
242
+ ensureDir();
243
+ const files = fs.readdirSync(SESSION_DIR);
244
+
245
+ for (const file of files) {
246
+ if (file.startsWith("session-") && file.endsWith(".json")) {
247
+ const filePath = path.join(SESSION_DIR, file);
248
+ try {
249
+ const data = fs.readFileSync(filePath, "utf-8");
250
+ const state = JSON.parse(data) as SessionState;
251
+ sessions.push(state);
252
+ } catch {
253
+ // 忽略解析错误
254
+ }
255
+ }
256
+ }
257
+ } catch {
258
+ // 目录不存在等错误
259
+ }
260
+
261
+ return sessions;
262
+ }
263
+
264
+ /**
265
+ * 清理过期的 Session 文件
266
+ */
267
+ export function cleanupExpiredSessions(): number {
268
+ let cleaned = 0;
269
+
270
+ try {
271
+ ensureDir();
272
+ const files = fs.readdirSync(SESSION_DIR);
273
+ const now = Date.now();
274
+
275
+ for (const file of files) {
276
+ if (file.startsWith("session-") && file.endsWith(".json")) {
277
+ const filePath = path.join(SESSION_DIR, file);
278
+ try {
279
+ const data = fs.readFileSync(filePath, "utf-8");
280
+ const state = JSON.parse(data) as SessionState;
281
+
282
+ if (now - state.savedAt > SESSION_EXPIRE_TIME) {
283
+ fs.unlinkSync(filePath);
284
+ cleaned++;
285
+ console.log(`[session-store] Cleaned expired session: ${file}`);
286
+ }
287
+ } catch {
288
+ // 忽略解析错误,但也删除损坏的文件
289
+ try {
290
+ fs.unlinkSync(filePath);
291
+ cleaned++;
292
+ } catch {
293
+ // 忽略
294
+ }
295
+ }
296
+ }
297
+ }
298
+ } catch {
299
+ // 目录不存在等错误
300
+ }
301
+
302
+ return cleaned;
303
+ }