openclaw-weixin 2.4.6

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 (80) hide show
  1. package/CHANGELOG.md +175 -0
  2. package/CHANGELOG.zh_CN.md +171 -0
  3. package/LICENSE +28 -0
  4. package/README.md +371 -0
  5. package/README.zh_CN.md +358 -0
  6. package/dist/index.js +16 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/src/api/api.js +513 -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 +318 -0
  17. package/dist/src/auth/accounts.js.map +1 -0
  18. package/dist/src/auth/login-qr.js +337 -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 +72 -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 +498 -0
  33. package/dist/src/channel.js.map +1 -0
  34. package/dist/src/compat.js +70 -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 +93 -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 +29 -0
  49. package/dist/src/messaging/error-notice.js.map +1 -0
  50. package/dist/src/messaging/inbound.js +204 -0
  51. package/dist/src/messaging/inbound.js.map +1 -0
  52. package/dist/src/messaging/markdown-filter.js +367 -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 +429 -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 +218 -0
  63. package/dist/src/messaging/send.js.map +1 -0
  64. package/dist/src/messaging/slash-commands.js +68 -0
  65. package/dist/src/messaging/slash-commands.js.map +1 -0
  66. package/dist/src/monitor/monitor.js +190 -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 +52 -0
  77. package/dist/src/util/redact.js.map +1 -0
  78. package/index.ts +19 -0
  79. package/openclaw.plugin.json +20 -0
  80. package/package.json +109 -0
package/README.md ADDED
@@ -0,0 +1,371 @@
1
+ # openclaw-weixin
2
+
3
+ [简体中文](./README.zh_CN.md)
4
+
5
+ Community-maintained distribution of
6
+ [Tencent/openclaw-weixin](https://github.com/Tencent/openclaw-weixin), providing
7
+ OpenClaw's WeChat channel with QR-code login.
8
+
9
+ > The community npm package, plugin id, and channel id are all
10
+ > `openclaw-weixin`. Existing
11
+ > `channels.openclaw-weixin`, `plugins.entries.openclaw-weixin`, and
12
+ > `~/.openclaw/openclaw-weixin/` data continue to use that id.
13
+
14
+ ## Compatibility
15
+
16
+ | Requirement | Minimum |
17
+ |-------------|---------|
18
+ | OpenClaw | `2026.7.1` |
19
+ | Node.js | `>=22.22.3 <23`, `>=24.15.0 <25`, or `>=25.9.0` |
20
+
21
+ The plugin checks the OpenClaw host version at startup and refuses to load on
22
+ older hosts. Node.js 24.15.0 is the recommended runtime.
23
+
24
+ ## Prerequisites
25
+
26
+ [OpenClaw](https://docs.openclaw.ai/install) must be installed and the
27
+ `openclaw` CLI must be available.
28
+
29
+ Check your version: `openclaw --version`
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ openclaw plugins install npm:openclaw-weixin
35
+ openclaw config set plugins.entries.openclaw-weixin.enabled true
36
+ openclaw channels login --channel openclaw-weixin
37
+ openclaw gateway restart
38
+ ```
39
+
40
+ A QR code appears in the terminal. Scan it with your phone and confirm the
41
+ authorization. Credentials are saved locally. Verify the installation after the
42
+ restart:
43
+
44
+ ```bash
45
+ openclaw plugins list
46
+ openclaw channels status --probe
47
+ ```
48
+
49
+ ## Switch from Tencent's package
50
+
51
+ Switch in place from `@tencent-weixin/openclaw-weixin`; **do not uninstall the
52
+ Tencent package first**. Current OpenClaw uninstall behavior removes channel
53
+ configuration owned by the plugin.
54
+
55
+ ```bash
56
+ openclaw plugins install npm:openclaw-weixin --force
57
+ openclaw gateway restart
58
+ openclaw plugins list
59
+ openclaw channels status --probe
60
+ ```
61
+
62
+ The forced install replaces the package that owns the same internal
63
+ `openclaw-weixin` plugin/channel id. The old and community packages must not be
64
+ enabled at the same time. Because the internal id and state paths are unchanged,
65
+ channel configuration and login credentials are normally retained.
66
+
67
+ ## Installation limitations
68
+
69
+ - Use the CLI for this community npm package. OpenClaw's Control UI does not
70
+ install arbitrary npm, git, or local-path plugin sources.
71
+ - In Nix mode (`OPENCLAW_NIX_MODE=1`), plugin install, update, uninstall,
72
+ enable, and disable commands are intentionally disabled. Add the package and
73
+ config to the Nix source, then rebuild instead.
74
+ - OpenClaw installs plugin dependencies with lifecycle scripts disabled. This
75
+ package therefore ships its compiled `dist/index.js` runtime and does not
76
+ build on the user's machine.
77
+
78
+ ## Adding More WeChat Accounts
79
+
80
+ ```bash
81
+ openclaw channels login --channel openclaw-weixin
82
+ ```
83
+
84
+ Each QR code login creates a new account entry, supporting multiple WeChat accounts online simultaneously.
85
+
86
+ ## Multi-Account Context Isolation
87
+
88
+ By default, DMs can share one session bucket. For **multiple logged-in WeChat accounts**, isolate by account + channel + sender:
89
+
90
+ ```bash
91
+ openclaw config set session.dmScope per-account-channel-peer
92
+ ```
93
+
94
+ ## Custom BotAgent (optional)
95
+
96
+ Every outbound request to the WeChat backend carries a self-declared `bot_agent`
97
+ identifier — analogous to an HTTP `User-Agent` — used for log attribution and
98
+ monitoring aggregation. The default is `OpenClaw`. Declaring your own app name
99
+ makes it much easier to trace your traffic in backend logs.
100
+
101
+ Add one line to `openclaw.json`:
102
+
103
+ ```json
104
+ {
105
+ "channels": {
106
+ "openclaw-weixin": {
107
+ "botAgent": "MyBot/1.2.0"
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ **Format** (UA-style):
114
+
115
+ - One or more `Name/Version` tokens, space-separated
116
+ - Each token may optionally be followed by ` (comment)`
117
+ - ASCII only; total length ≤ 256 bytes
118
+ - Invalid tokens are silently dropped during sanitization; falls back to
119
+ `OpenClaw` if nothing valid remains
120
+
121
+ Examples that pass through unchanged:
122
+
123
+ - `MyBot/1.2.0`
124
+ - `MyBot/1.2.0 (region=cn;env=prod)`
125
+ - `MyBot/1.2.0 LangChain/0.3.5`
126
+ - `MyBot/1.2.0-rc.1+build.5`
127
+
128
+ **Note**: `bot_agent` is for observability only — it is not used for
129
+ authentication or routing. All registered agents on this plugin instance
130
+ currently share the same `botAgent` declaration; per-agent overrides may be
131
+ added in a future version if needed.
132
+
133
+ ## Backend API Protocol
134
+
135
+ This plugin communicates with the backend gateway via HTTP JSON API. Developers integrating with their own backend need to implement the following interfaces.
136
+
137
+ All endpoints use `POST` with JSON request and response bodies. Common request headers:
138
+
139
+ | Header | Description |
140
+ |--------|-------------|
141
+ | `Content-Type` | `application/json` |
142
+ | `AuthorizationType` | Fixed value `ilink_bot_token` |
143
+ | `Authorization` | `Bearer <token>` (obtained after login) |
144
+ | `X-WECHAT-UIN` | Base64-encoded random uint32 |
145
+
146
+ ### Endpoint List
147
+
148
+ | Endpoint | Path | Description |
149
+ |----------|------|-------------|
150
+ | getUpdates | `getupdates` | Long-poll for new messages |
151
+ | sendMessage | `sendmessage` | Send a message (text/image/video/file) |
152
+ | getUploadUrl | `getuploadurl` | Get CDN upload pre-signed URL |
153
+ | getConfig | `getconfig` | Get account config (typing ticket, etc.) |
154
+ | sendTyping | `sendtyping` | Send/cancel typing status indicator |
155
+
156
+ ### getUpdates
157
+
158
+ Long-polling endpoint. The server responds when new messages arrive or on timeout.
159
+
160
+ **Request body:**
161
+
162
+ ```json
163
+ {
164
+ "get_updates_buf": ""
165
+ }
166
+ ```
167
+
168
+ | Field | Type | Description |
169
+ |-------|------|-------------|
170
+ | `get_updates_buf` | `string` | Sync cursor from the previous response; empty string for the first request |
171
+
172
+ **Response body:**
173
+
174
+ ```json
175
+ {
176
+ "ret": 0,
177
+ "msgs": [...],
178
+ "get_updates_buf": "<new cursor>",
179
+ "longpolling_timeout_ms": 35000
180
+ }
181
+ ```
182
+
183
+ | Field | Type | Description |
184
+ |-------|------|-------------|
185
+ | `ret` | `number` | Return code, `0` = success |
186
+ | `errcode` | `number?` | Error code (e.g., `-14` = session timeout) |
187
+ | `errmsg` | `string?` | Error description |
188
+ | `msgs` | `WeixinMessage[]` | Message list (structure below) |
189
+ | `get_updates_buf` | `string` | New sync cursor to pass in the next request |
190
+ | `longpolling_timeout_ms` | `number?` | Server-suggested long-poll timeout for the next request (ms) |
191
+
192
+ ### sendMessage
193
+
194
+ Send a message to a user.
195
+
196
+ **Request body:**
197
+
198
+ ```json
199
+ {
200
+ "msg": {
201
+ "to_user_id": "<target user ID>",
202
+ "context_token": "<conversation context token>",
203
+ "item_list": [
204
+ {
205
+ "type": 1,
206
+ "text_item": { "text": "Hello" }
207
+ }
208
+ ]
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### getUploadUrl
214
+
215
+ Get CDN upload pre-signed parameters. Call this endpoint before uploading a file to obtain `upload_param` and `thumb_upload_param`.
216
+
217
+ **Request body:**
218
+
219
+ ```json
220
+ {
221
+ "filekey": "<file identifier>",
222
+ "media_type": 1,
223
+ "to_user_id": "<target user ID>",
224
+ "rawsize": 12345,
225
+ "rawfilemd5": "<plaintext MD5>",
226
+ "filesize": 12352,
227
+ "thumb_rawsize": 1024,
228
+ "thumb_rawfilemd5": "<thumbnail plaintext MD5>",
229
+ "thumb_filesize": 1040
230
+ }
231
+ ```
232
+
233
+ | Field | Type | Description |
234
+ |-------|------|-------------|
235
+ | `media_type` | `number` | `1` = IMAGE, `2` = VIDEO, `3` = FILE |
236
+ | `rawsize` | `number` | Original file plaintext size |
237
+ | `rawfilemd5` | `string` | Original file plaintext MD5 |
238
+ | `filesize` | `number` | Ciphertext size after AES-128-ECB encryption |
239
+ | `thumb_rawsize` | `number?` | Thumbnail plaintext size (required for IMAGE/VIDEO) |
240
+ | `thumb_rawfilemd5` | `string?` | Thumbnail plaintext MD5 (required for IMAGE/VIDEO) |
241
+ | `thumb_filesize` | `number?` | Thumbnail ciphertext size (required for IMAGE/VIDEO) |
242
+
243
+ **Response body:**
244
+
245
+ ```json
246
+ {
247
+ "upload_param": "<original image upload encrypted parameters>",
248
+ "thumb_upload_param": "<thumbnail upload encrypted parameters>"
249
+ }
250
+ ```
251
+
252
+ ### getConfig
253
+
254
+ Get account configuration, including the typing ticket.
255
+
256
+ **Request body:**
257
+
258
+ ```json
259
+ {
260
+ "ilink_user_id": "<user ID>",
261
+ "context_token": "<optional, conversation context token>"
262
+ }
263
+ ```
264
+
265
+ **Response body:**
266
+
267
+ ```json
268
+ {
269
+ "ret": 0,
270
+ "typing_ticket": "<base64-encoded typing ticket>"
271
+ }
272
+ ```
273
+
274
+ ### sendTyping
275
+
276
+ Send or cancel the typing status indicator.
277
+
278
+ **Request body:**
279
+
280
+ ```json
281
+ {
282
+ "ilink_user_id": "<user ID>",
283
+ "typing_ticket": "<obtained from getConfig>",
284
+ "status": 1
285
+ }
286
+ ```
287
+
288
+ | Field | Type | Description |
289
+ |-------|------|-------------|
290
+ | `status` | `number` | `1` = typing, `2` = cancel typing |
291
+
292
+ ### Message Structure
293
+
294
+ #### WeixinMessage
295
+
296
+ | Field | Type | Description |
297
+ |-------|------|-------------|
298
+ | `seq` | `number?` | Message sequence number |
299
+ | `message_id` | `number?` | Unique message ID |
300
+ | `from_user_id` | `string?` | Sender ID |
301
+ | `to_user_id` | `string?` | Receiver ID |
302
+ | `create_time_ms` | `number?` | Creation timestamp (ms) |
303
+ | `session_id` | `string?` | Session ID |
304
+ | `message_type` | `number?` | `1` = USER, `2` = BOT |
305
+ | `message_state` | `number?` | `0` = NEW, `1` = GENERATING, `2` = FINISH |
306
+ | `item_list` | `MessageItem[]?` | Message content list |
307
+ | `context_token` | `string?` | Conversation context token, must be passed back when replying |
308
+
309
+ #### MessageItem
310
+
311
+ | Field | Type | Description |
312
+ |-------|------|-------------|
313
+ | `type` | `number` | `1` TEXT, `2` IMAGE, `3` VOICE, `4` FILE, `5` VIDEO |
314
+ | `text_item` | `{ text: string }?` | Text content |
315
+ | `image_item` | `ImageItem?` | Image (with CDN reference and AES key) |
316
+ | `voice_item` | `VoiceItem?` | Voice (SILK encoded) |
317
+ | `file_item` | `FileItem?` | File attachment |
318
+ | `video_item` | `VideoItem?` | Video |
319
+ | `ref_msg` | `RefMessage?` | Referenced message |
320
+
321
+ #### CDN Media Reference (CDNMedia)
322
+
323
+ All media types (image/voice/file/video) are transferred via CDN using AES-128-ECB encryption:
324
+
325
+ | Field | Type | Description |
326
+ |-------|------|-------------|
327
+ | `encrypt_query_param` | `string?` | Encrypted parameters for CDN download/upload |
328
+ | `aes_key` | `string?` | Base64-encoded AES-128 key |
329
+
330
+ ### CDN Upload Flow
331
+
332
+ 1. Calculate the file's plaintext size, MD5, and ciphertext size after AES-128-ECB encryption
333
+ 2. If a thumbnail is needed (image/video), calculate the thumbnail's plaintext and ciphertext parameters as well
334
+ 3. Call `getUploadUrl` to get `upload_param` (and `thumb_upload_param`)
335
+ 4. Encrypt the file content with AES-128-ECB and PUT upload to the CDN URL
336
+ 5. Encrypt and upload the thumbnail in the same way
337
+ 6. Use the returned `encrypt_query_param` to construct a `CDNMedia` reference, include it in the `MessageItem`, and send
338
+
339
+ > For complete type definitions, see [`src/api/types.ts`](src/api/types.ts). For API call implementations, see [`src/api/api.ts`](src/api/api.ts).
340
+
341
+ ## Uninstall
342
+
343
+ Back up `~/.openclaw/openclaw.json` first if you may want to reinstall: current
344
+ OpenClaw versions remove the plugin entry and owned
345
+ `channels.openclaw-weixin` configuration during uninstall.
346
+
347
+ ```bash
348
+ openclaw plugins uninstall openclaw-weixin
349
+ ```
350
+
351
+ ## Troubleshooting
352
+
353
+ ### "requires OpenClaw >=2026.7.1" error
354
+
355
+ Your OpenClaw version is too old for this plugin version. Check with:
356
+
357
+ ```bash
358
+ openclaw --version
359
+ ```
360
+
361
+ Upgrade OpenClaw before installing this package. The community package does not
362
+ publish a legacy compatibility line.
363
+
364
+ ### Channel shows "OK" but doesn't connect
365
+
366
+ Ensure `plugins.entries.openclaw-weixin.enabled` is `true` in `~/.openclaw/openclaw.json`:
367
+
368
+ ```bash
369
+ openclaw config set plugins.entries.openclaw-weixin.enabled true
370
+ openclaw gateway restart
371
+ ```