@netcat-ai/openclaw-weixin 2.4.7

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 (116) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/CHANGELOG.zh_CN.md +156 -0
  3. package/LICENSE +27 -0
  4. package/README.md +355 -0
  5. package/README.zh_CN.md +351 -0
  6. package/dist/index.js +16 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/src/api/api.js +484 -0
  9. package/dist/src/api/api.js.map +1 -0
  10. package/dist/src/api/config-cache.js +64 -0
  11. package/dist/src/api/config-cache.js.map +1 -0
  12. package/dist/src/api/session-guard.js +49 -0
  13. package/dist/src/api/session-guard.js.map +1 -0
  14. package/dist/src/api/types.js +37 -0
  15. package/dist/src/api/types.js.map +1 -0
  16. package/dist/src/auth/accounts.js +328 -0
  17. package/dist/src/auth/accounts.js.map +1 -0
  18. package/dist/src/auth/login-qr.js +331 -0
  19. package/dist/src/auth/login-qr.js.map +1 -0
  20. package/dist/src/auth/pairing.js +104 -0
  21. package/dist/src/auth/pairing.js.map +1 -0
  22. package/dist/src/cdn/aes-ecb.js +19 -0
  23. package/dist/src/cdn/aes-ecb.js.map +1 -0
  24. package/dist/src/cdn/cdn-upload.js +74 -0
  25. package/dist/src/cdn/cdn-upload.js.map +1 -0
  26. package/dist/src/cdn/cdn-url.js +14 -0
  27. package/dist/src/cdn/cdn-url.js.map +1 -0
  28. package/dist/src/cdn/pic-decrypt.js +89 -0
  29. package/dist/src/cdn/pic-decrypt.js.map +1 -0
  30. package/dist/src/cdn/upload.js +115 -0
  31. package/dist/src/cdn/upload.js.map +1 -0
  32. package/dist/src/channel.js +478 -0
  33. package/dist/src/channel.js.map +1 -0
  34. package/dist/src/compat.js +67 -0
  35. package/dist/src/compat.js.map +1 -0
  36. package/dist/src/config/config-schema.js +20 -0
  37. package/dist/src/config/config-schema.js.map +1 -0
  38. package/dist/src/config/reply-progress.js +5 -0
  39. package/dist/src/config/reply-progress.js.map +1 -0
  40. package/dist/src/media/media-download.js +95 -0
  41. package/dist/src/media/media-download.js.map +1 -0
  42. package/dist/src/media/mime.js +73 -0
  43. package/dist/src/media/mime.js.map +1 -0
  44. package/dist/src/media/silk-transcode.js +64 -0
  45. package/dist/src/media/silk-transcode.js.map +1 -0
  46. package/dist/src/messaging/debug-mode.js +63 -0
  47. package/dist/src/messaging/debug-mode.js.map +1 -0
  48. package/dist/src/messaging/error-notice.js +25 -0
  49. package/dist/src/messaging/error-notice.js.map +1 -0
  50. package/dist/src/messaging/inbound.js +201 -0
  51. package/dist/src/messaging/inbound.js.map +1 -0
  52. package/dist/src/messaging/markdown-filter.js +368 -0
  53. package/dist/src/messaging/markdown-filter.js.map +1 -0
  54. package/dist/src/messaging/outbound-hooks.js +58 -0
  55. package/dist/src/messaging/outbound-hooks.js.map +1 -0
  56. package/dist/src/messaging/process-message.js +406 -0
  57. package/dist/src/messaging/process-message.js.map +1 -0
  58. package/dist/src/messaging/reply-progress-sender.js +93 -0
  59. package/dist/src/messaging/reply-progress-sender.js.map +1 -0
  60. package/dist/src/messaging/send-media.js +54 -0
  61. package/dist/src/messaging/send-media.js.map +1 -0
  62. package/dist/src/messaging/send.js +220 -0
  63. package/dist/src/messaging/send.js.map +1 -0
  64. package/dist/src/messaging/slash-commands.js +70 -0
  65. package/dist/src/messaging/slash-commands.js.map +1 -0
  66. package/dist/src/monitor/monitor.js +146 -0
  67. package/dist/src/monitor/monitor.js.map +1 -0
  68. package/dist/src/storage/state-dir.js +9 -0
  69. package/dist/src/storage/state-dir.js.map +1 -0
  70. package/dist/src/storage/sync-buf.js +64 -0
  71. package/dist/src/storage/sync-buf.js.map +1 -0
  72. package/dist/src/util/logger.js +120 -0
  73. package/dist/src/util/logger.js.map +1 -0
  74. package/dist/src/util/random.js +16 -0
  75. package/dist/src/util/random.js.map +1 -0
  76. package/dist/src/util/redact.js +54 -0
  77. package/dist/src/util/redact.js.map +1 -0
  78. package/index.ts +19 -0
  79. package/openclaw.plugin.json +22 -0
  80. package/package.json +75 -0
  81. package/src/api/api.ts +586 -0
  82. package/src/api/config-cache.ts +79 -0
  83. package/src/api/session-guard.ts +58 -0
  84. package/src/api/types.ts +278 -0
  85. package/src/auth/accounts.ts +394 -0
  86. package/src/auth/login-qr.ts +458 -0
  87. package/src/auth/pairing.ts +120 -0
  88. package/src/cdn/aes-ecb.ts +21 -0
  89. package/src/cdn/cdn-upload.ts +93 -0
  90. package/src/cdn/cdn-url.ts +20 -0
  91. package/src/cdn/pic-decrypt.ts +101 -0
  92. package/src/cdn/upload.ts +168 -0
  93. package/src/channel.ts +548 -0
  94. package/src/compat.ts +77 -0
  95. package/src/config/config-schema.ts +23 -0
  96. package/src/config/reply-progress.ts +10 -0
  97. package/src/media/media-download.ts +149 -0
  98. package/src/media/mime.ts +76 -0
  99. package/src/media/silk-transcode.ts +74 -0
  100. package/src/messaging/debug-mode.ts +69 -0
  101. package/src/messaging/error-notice.ts +32 -0
  102. package/src/messaging/inbound.ts +262 -0
  103. package/src/messaging/markdown-filter.ts +361 -0
  104. package/src/messaging/outbound-hooks.ts +88 -0
  105. package/src/messaging/process-message.ts +525 -0
  106. package/src/messaging/reply-progress-sender.ts +122 -0
  107. package/src/messaging/send-media.ts +72 -0
  108. package/src/messaging/send.ts +294 -0
  109. package/src/messaging/slash-commands.ts +110 -0
  110. package/src/monitor/monitor.ts +224 -0
  111. package/src/storage/state-dir.ts +11 -0
  112. package/src/storage/sync-buf.ts +81 -0
  113. package/src/util/logger.ts +145 -0
  114. package/src/util/random.ts +17 -0
  115. package/src/util/redact.ts +54 -0
  116. package/src/vendor.d.ts +25 -0
@@ -0,0 +1,58 @@
1
+ import { logger } from "../util/logger.js";
2
+
3
+ const SESSION_PAUSE_DURATION_MS = 60 * 60 * 1000;
4
+
5
+ /** Error code returned by the server when the bot token is stale / expired. */
6
+ export const STALE_TOKEN_ERRCODE = -14;
7
+
8
+ const pauseUntilMap = new Map<string, number>();
9
+
10
+ /** Pause all inbound/outbound API calls for `accountId` for one hour. */
11
+ export function pauseSession(accountId: string): void {
12
+ const until = Date.now() + SESSION_PAUSE_DURATION_MS;
13
+ pauseUntilMap.set(accountId, until);
14
+ logger.info(
15
+ `session-guard: paused accountId=${accountId} until=${new Date(until).toISOString()} (${SESSION_PAUSE_DURATION_MS / 1000}s)`,
16
+ );
17
+ }
18
+
19
+ /** Returns `true` when the bot is still within its one-hour cooldown window. */
20
+ export function isSessionPaused(accountId: string): boolean {
21
+ const until = pauseUntilMap.get(accountId);
22
+ if (until === undefined) return false;
23
+ if (Date.now() >= until) {
24
+ pauseUntilMap.delete(accountId);
25
+ return false;
26
+ }
27
+ return true;
28
+ }
29
+
30
+ /** Milliseconds remaining until the pause expires (0 when not paused). */
31
+ export function getRemainingPauseMs(accountId: string): number {
32
+ const until = pauseUntilMap.get(accountId);
33
+ if (until === undefined) return 0;
34
+ const remaining = until - Date.now();
35
+ if (remaining <= 0) {
36
+ pauseUntilMap.delete(accountId);
37
+ return 0;
38
+ }
39
+ return remaining;
40
+ }
41
+
42
+ /** Throw if the session is currently paused. Call before any API request. */
43
+ export function assertSessionActive(accountId: string): void {
44
+ if (isSessionPaused(accountId)) {
45
+ const remainingMin = Math.ceil(getRemainingPauseMs(accountId) / 60_000);
46
+ throw new Error(
47
+ `session paused for accountId=${accountId}, ${remainingMin} min remaining (errcode ${STALE_TOKEN_ERRCODE})`,
48
+ );
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Reset internal state — only for tests.
54
+ * @internal
55
+ */
56
+ export function _resetForTest(): void {
57
+ pauseUntilMap.clear();
58
+ }
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Weixin protocol types (mirrors proto: GetUpdatesReq/Resp, WeixinMessage, SendMessageReq).
3
+ * API uses JSON over HTTP; bytes fields are base64 strings in JSON.
4
+ */
5
+
6
+ /** Common request metadata attached to every CGI request. */
7
+ export interface BaseInfo {
8
+ channel_version?: string;
9
+ /**
10
+ * Self-declared identity of the upstream bot/app, analogous to HTTP
11
+ * `User-Agent`. Filled from `channels.openclaw-weixin.botAgent` in
12
+ * openclaw.json; defaults to `"OpenClaw"` when unset.
13
+ *
14
+ * Format: UA-style `Name/Version` tokens, optionally followed by
15
+ * `(comment)`, multiple tokens space-separated. ASCII only, total
16
+ * length <= 256 bytes after sanitization.
17
+ *
18
+ * For observability only (logging, monitoring aggregation); not used
19
+ * for authentication or routing.
20
+ */
21
+ bot_agent?: string;
22
+ }
23
+
24
+ /** proto: UploadMediaType */
25
+ export const UploadMediaType = {
26
+ IMAGE: 1,
27
+ VIDEO: 2,
28
+ FILE: 3,
29
+ VOICE: 4,
30
+ } as const;
31
+
32
+ export interface GetUploadUrlReq {
33
+ filekey?: string;
34
+ /** proto field 2: media_type, see UploadMediaType */
35
+ media_type?: number;
36
+ to_user_id?: string;
37
+ /** 原文件明文大小 */
38
+ rawsize?: number;
39
+ /** 原文件明文 MD5 */
40
+ rawfilemd5?: string;
41
+ /** 原文件密文大小(AES-128-ECB 加密后) */
42
+ filesize?: number;
43
+ /** 缩略图明文大小(IMAGE/VIDEO 时必填) */
44
+ thumb_rawsize?: number;
45
+ /** 缩略图明文 MD5(IMAGE/VIDEO 时必填) */
46
+ thumb_rawfilemd5?: string;
47
+ /** 缩略图密文大小(IMAGE/VIDEO 时必填) */
48
+ thumb_filesize?: number;
49
+ /** 不需要缩略图上传 URL,默认 false */
50
+ no_need_thumb?: boolean;
51
+ /** 加密 key */
52
+ aeskey?: string;
53
+ }
54
+
55
+ export interface GetUploadUrlResp {
56
+ /** 原图上传加密参数 */
57
+ upload_param?: string;
58
+ /** 缩略图上传加密参数,无缩略图时为空 */
59
+ thumb_upload_param?: string;
60
+ /** 完整上传 URL(服务端直接返回,无需客户端拼接) */
61
+ upload_full_url?: string;
62
+ }
63
+
64
+ export const MessageType = {
65
+ NONE: 0,
66
+ USER: 1,
67
+ BOT: 2,
68
+ } as const;
69
+
70
+ export const MessageItemType = {
71
+ NONE: 0,
72
+ TEXT: 1,
73
+ IMAGE: 2,
74
+ VOICE: 3,
75
+ FILE: 4,
76
+ VIDEO: 5,
77
+ TOOL_CALL_START: 11,
78
+ TOOL_CALL_RESULT: 12,
79
+ } as const;
80
+
81
+ export const MessageState = {
82
+ NEW: 0,
83
+ GENERATING: 1,
84
+ FINISH: 2,
85
+ } as const;
86
+
87
+ export interface TextItem {
88
+ text?: string;
89
+ }
90
+
91
+ /** CDN media reference; aes_key is base64-encoded bytes in JSON. */
92
+ export interface CDNMedia {
93
+ encrypt_query_param?: string;
94
+ aes_key?: string;
95
+ /** 加密类型: 0=只加密fileid, 1=打包缩略图/中图等信息 */
96
+ encrypt_type?: number;
97
+ /** 完整下载 URL(服务端直接返回,无需客户端拼接) */
98
+ full_url?: string;
99
+ }
100
+
101
+ export interface ImageItem {
102
+ /** 原图 CDN 引用 */
103
+ media?: CDNMedia;
104
+ /** 缩略图 CDN 引用 */
105
+ thumb_media?: CDNMedia;
106
+ /** Raw AES-128 key as hex string (16 bytes); preferred over media.aes_key for inbound decryption. */
107
+ aeskey?: string;
108
+ url?: string;
109
+ mid_size?: number;
110
+ thumb_size?: number;
111
+ thumb_height?: number;
112
+ thumb_width?: number;
113
+ hd_size?: number;
114
+ }
115
+
116
+ export interface VoiceItem {
117
+ media?: CDNMedia;
118
+ /** 语音编码类型:1=pcm 2=adpcm 3=feature 4=speex 5=amr 6=silk 7=mp3 8=ogg-speex */
119
+ encode_type?: number;
120
+ bits_per_sample?: number;
121
+ /** 采样率 (Hz) */
122
+ sample_rate?: number;
123
+ /** 语音长度 (毫秒) */
124
+ playtime?: number;
125
+ /** 语音转文字内容 */
126
+ text?: string;
127
+ }
128
+
129
+ export interface FileItem {
130
+ media?: CDNMedia;
131
+ file_name?: string;
132
+ md5?: string;
133
+ len?: string;
134
+ }
135
+
136
+ export interface VideoItem {
137
+ media?: CDNMedia;
138
+ video_size?: number;
139
+ play_length?: number;
140
+ video_md5?: string;
141
+ thumb_media?: CDNMedia;
142
+ thumb_size?: number;
143
+ thumb_height?: number;
144
+ thumb_width?: number;
145
+ }
146
+
147
+ export interface RefMessage {
148
+ message_item?: MessageItem;
149
+ title?: string; // 摘要
150
+ }
151
+
152
+ export interface ToolCallStartItem {
153
+ tool_name?: string;
154
+ tool_call_id?: string;
155
+ }
156
+
157
+ export interface ToolCallResultItem {
158
+ tool_name?: string;
159
+ tool_call_id?: string;
160
+ status?: string;
161
+ }
162
+
163
+ export interface MessageItem {
164
+ type?: number;
165
+ create_time_ms?: number;
166
+ update_time_ms?: number;
167
+ is_completed?: boolean;
168
+ msg_id?: string;
169
+ ref_msg?: RefMessage;
170
+ text_item?: TextItem;
171
+ image_item?: ImageItem;
172
+ voice_item?: VoiceItem;
173
+ file_item?: FileItem;
174
+ video_item?: VideoItem;
175
+ tool_call_start_item?: ToolCallStartItem;
176
+ tool_call_result_item?: ToolCallResultItem;
177
+ }
178
+
179
+ /** Unified message (proto: WeixinMessage). Replaces the old split Message + MessageContent + FullMessage. */
180
+ export interface WeixinMessage {
181
+ seq?: number;
182
+ message_id?: number;
183
+ from_user_id?: string;
184
+ to_user_id?: string;
185
+ client_id?: string;
186
+ create_time_ms?: number;
187
+ update_time_ms?: number;
188
+ delete_time_ms?: number;
189
+ session_id?: string;
190
+ group_id?: string;
191
+ message_type?: number;
192
+ message_state?: number;
193
+ item_list?: MessageItem[];
194
+ context_token?: string;
195
+ run_id?: string;
196
+ }
197
+
198
+ /** GetUpdates request: bytes fields are base64 strings in JSON. */
199
+ export interface GetUpdatesReq {
200
+ /** @deprecated compat only, will be removed */
201
+ sync_buf?: string;
202
+ /** Full context buf cached locally; send "" when none (first request or after reset). */
203
+ get_updates_buf?: string;
204
+ }
205
+
206
+ /** GetUpdates response: bytes fields are base64 strings in JSON. */
207
+ export interface GetUpdatesResp {
208
+ ret?: number;
209
+ /** Error code returned by the server (e.g. -14 = session timeout). Present when request fails. */
210
+ errcode?: number;
211
+ errmsg?: string;
212
+ msgs?: WeixinMessage[];
213
+ /** @deprecated compat only */
214
+ sync_buf?: string;
215
+ /** Full context buf to cache locally and send on next request. */
216
+ get_updates_buf?: string;
217
+ /** Server-suggested timeout (ms) for the next getUpdates long-poll. */
218
+ longpolling_timeout_ms?: number;
219
+ }
220
+
221
+ /** SendMessage request: wraps a single WeixinMessage. */
222
+ export interface SendMessageReq {
223
+ msg?: WeixinMessage;
224
+ }
225
+
226
+ export interface SendMessageResp {
227
+ ret?: number;
228
+ errmsg?: string;
229
+ }
230
+
231
+ /** Typing status: 1 = typing (default), 2 = cancel typing. */
232
+ export const TypingStatus = {
233
+ TYPING: 1,
234
+ CANCEL: 2,
235
+ } as const;
236
+
237
+ /** SendTyping request: send a typing indicator to a user. */
238
+ export interface SendTypingReq {
239
+ ilink_user_id?: string;
240
+ typing_ticket?: string;
241
+ /** 1=typing (default), 2=cancel typing */
242
+ status?: number;
243
+ }
244
+
245
+ export interface SendTypingResp {
246
+ ret?: number;
247
+ errmsg?: string;
248
+ }
249
+
250
+ /** GetConfig response: bot config including typing_ticket. */
251
+ export interface GetConfigResp {
252
+ ret?: number;
253
+ errmsg?: string;
254
+ /** Base64-encoded typing ticket for sendTyping. */
255
+ typing_ticket?: string;
256
+ }
257
+
258
+ /** proto: NotifyStopReq — notify server when the channel client is stopping. */
259
+ export interface NotifyStopReq {
260
+ base_info?: BaseInfo;
261
+ }
262
+
263
+ /** proto: NotifyStopResp */
264
+ export interface NotifyStopResp {
265
+ ret?: number;
266
+ errmsg?: string;
267
+ }
268
+
269
+ /** proto: NotifyStartReq — notify server when the channel client is starting. */
270
+ export interface NotifyStartReq {
271
+ base_info?: BaseInfo;
272
+ }
273
+
274
+ /** proto: NotifyStartResp */
275
+ export interface NotifyStartResp {
276
+ ret?: number;
277
+ errmsg?: string;
278
+ }