@tencent-weixin/openclaw-weixin 2.1.10 → 2.4.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 (79) hide show
  1. package/README.md +39 -0
  2. package/README.zh_CN.md +36 -0
  3. package/dist/index.js +20 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/src/api/api.js +375 -0
  6. package/dist/src/api/api.js.map +1 -0
  7. package/dist/src/api/config-cache.js +64 -0
  8. package/dist/src/api/config-cache.js.map +1 -0
  9. package/dist/src/api/session-guard.js +49 -0
  10. package/dist/src/api/session-guard.js.map +1 -0
  11. package/dist/src/api/types.js +35 -0
  12. package/dist/src/api/types.js.map +1 -0
  13. package/dist/src/auth/accounts.js +326 -0
  14. package/dist/src/auth/accounts.js.map +1 -0
  15. package/dist/src/auth/login-qr.js +332 -0
  16. package/dist/src/auth/login-qr.js.map +1 -0
  17. package/dist/src/auth/pairing.js +104 -0
  18. package/dist/src/auth/pairing.js.map +1 -0
  19. package/dist/src/cdn/aes-ecb.js +19 -0
  20. package/dist/src/cdn/aes-ecb.js.map +1 -0
  21. package/dist/src/cdn/cdn-upload.js +73 -0
  22. package/dist/src/cdn/cdn-upload.js.map +1 -0
  23. package/dist/src/cdn/cdn-url.js +14 -0
  24. package/dist/src/cdn/cdn-url.js.map +1 -0
  25. package/dist/src/cdn/pic-decrypt.js +89 -0
  26. package/dist/src/cdn/pic-decrypt.js.map +1 -0
  27. package/dist/src/cdn/upload.js +106 -0
  28. package/dist/src/cdn/upload.js.map +1 -0
  29. package/dist/src/channel.js +449 -0
  30. package/dist/src/channel.js.map +1 -0
  31. package/dist/src/compat.js +67 -0
  32. package/dist/src/compat.js.map +1 -0
  33. package/dist/src/config/config-schema.js +19 -0
  34. package/dist/src/config/config-schema.js.map +1 -0
  35. package/dist/src/media/media-download.js +95 -0
  36. package/dist/src/media/media-download.js.map +1 -0
  37. package/dist/src/media/mime.js +73 -0
  38. package/dist/src/media/mime.js.map +1 -0
  39. package/dist/src/media/silk-transcode.js +64 -0
  40. package/dist/src/media/silk-transcode.js.map +1 -0
  41. package/dist/src/messaging/debug-mode.js +63 -0
  42. package/dist/src/messaging/debug-mode.js.map +1 -0
  43. package/dist/src/messaging/error-notice.js +24 -0
  44. package/dist/src/messaging/error-notice.js.map +1 -0
  45. package/dist/src/messaging/inbound.js +201 -0
  46. package/dist/src/messaging/inbound.js.map +1 -0
  47. package/dist/src/messaging/markdown-filter.js +368 -0
  48. package/dist/src/messaging/markdown-filter.js.map +1 -0
  49. package/dist/src/messaging/outbound-hooks.js +56 -0
  50. package/dist/src/messaging/outbound-hooks.js.map +1 -0
  51. package/dist/src/messaging/process-message.js +381 -0
  52. package/dist/src/messaging/process-message.js.map +1 -0
  53. package/dist/src/messaging/send-media.js +54 -0
  54. package/dist/src/messaging/send-media.js.map +1 -0
  55. package/dist/src/messaging/send.js +182 -0
  56. package/dist/src/messaging/send.js.map +1 -0
  57. package/dist/src/messaging/slash-commands.js +70 -0
  58. package/dist/src/messaging/slash-commands.js.map +1 -0
  59. package/dist/src/monitor/monitor.js +150 -0
  60. package/dist/src/monitor/monitor.js.map +1 -0
  61. package/dist/src/runtime.js +54 -0
  62. package/dist/src/runtime.js.map +1 -0
  63. package/dist/src/storage/state-dir.js +9 -0
  64. package/dist/src/storage/state-dir.js.map +1 -0
  65. package/dist/src/storage/sync-buf.js +64 -0
  66. package/dist/src/storage/sync-buf.js.map +1 -0
  67. package/dist/src/util/logger.js +120 -0
  68. package/dist/src/util/logger.js.map +1 -0
  69. package/dist/src/util/random.js +16 -0
  70. package/dist/src/util/random.js.map +1 -0
  71. package/dist/src/util/redact.js +54 -0
  72. package/dist/src/util/redact.js.map +1 -0
  73. package/openclaw.plugin.json +11 -1
  74. package/package.json +9 -2
  75. package/src/api/api.ts +112 -10
  76. package/src/api/types.ts +13 -0
  77. package/src/auth/accounts.ts +12 -0
  78. package/src/auth/login-qr.ts +162 -38
  79. package/src/channel.ts +7 -23
package/README.md CHANGED
@@ -72,6 +72,45 @@ By default, DMs can share one session bucket. For **multiple logged-in WeChat ac
72
72
  openclaw config set session.dmScope per-account-channel-peer
73
73
  ```
74
74
 
75
+ ## Custom BotAgent (optional)
76
+
77
+ Every outbound request to the WeChat backend carries a self-declared `bot_agent`
78
+ identifier — analogous to an HTTP `User-Agent` — used for log attribution and
79
+ monitoring aggregation. The default is `OpenClaw`. Declaring your own app name
80
+ makes it much easier to trace your traffic in backend logs.
81
+
82
+ Add one line to `openclaw.json`:
83
+
84
+ ```json
85
+ {
86
+ "channels": {
87
+ "openclaw-weixin": {
88
+ "botAgent": "MyBot/1.2.0"
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ **Format** (UA-style):
95
+
96
+ - One or more `Name/Version` tokens, space-separated
97
+ - Each token may optionally be followed by ` (comment)`
98
+ - ASCII only; total length ≤ 256 bytes
99
+ - Invalid tokens are silently dropped during sanitization; falls back to
100
+ `OpenClaw` if nothing valid remains
101
+
102
+ Examples that pass through unchanged:
103
+
104
+ - `MyBot/1.2.0`
105
+ - `MyBot/1.2.0 (region=cn;env=prod)`
106
+ - `MyBot/1.2.0 LangChain/0.3.5`
107
+ - `MyBot/1.2.0-rc.1+build.5`
108
+
109
+ **Note**: `bot_agent` is for observability only — it is not used for
110
+ authentication or routing. All registered agents on this plugin instance
111
+ currently share the same `botAgent` declaration; per-agent overrides may be
112
+ added in a future version if needed.
113
+
75
114
  ## Backend API Protocol
76
115
 
77
116
  This plugin communicates with the backend gateway via HTTP JSON API. Developers integrating with their own backend need to implement the following interfaces.
package/README.zh_CN.md CHANGED
@@ -71,6 +71,42 @@ openclaw channels login --channel openclaw-weixin
71
71
  openclaw config set session.dmScope per-account-channel-peer
72
72
  ```
73
73
 
74
+ ## 自定义 BotAgent(可选)
75
+
76
+ 每条出站请求会带一个自我声明的 `bot_agent` 字段——类似 HTTP `User-Agent`——用于
77
+ 后台日志归因和监控聚合。**默认值为 `OpenClaw`**。声明自己的应用名能让你的流量
78
+ 在后台日志中更容易识别。
79
+
80
+ 在 `openclaw.json` 中加一行即可:
81
+
82
+ ```json
83
+ {
84
+ "channels": {
85
+ "openclaw-weixin": {
86
+ "botAgent": "MyBot/1.2.0"
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ **格式规范**(UA 风格):
93
+
94
+ - 一个或多个 `Name/Version` token,空格分隔
95
+ - 每个 token 可选地跟一个 ` (comment)`
96
+ - 仅允许 ASCII 字符;总长 ≤ 256 字节
97
+ - 不合规的 token 在清洗时静默丢弃;如果最终为空,回退到 `OpenClaw`
98
+
99
+ 可直接使用的示例:
100
+
101
+ - `MyBot/1.2.0`
102
+ - `MyBot/1.2.0 (region=cn;env=prod)`
103
+ - `MyBot/1.2.0 LangChain/0.3.5`
104
+ - `MyBot/1.2.0-rc.1+build.5`
105
+
106
+ **注意**:`bot_agent` 仅用于观测,**不参与鉴权或路由**。当前本插件实例下所有
107
+ 已注册的 agent 共享同一个 `botAgent` 声明;如有需要按 agent 单独标识的场景,
108
+ 可在后续版本扩展配置。
109
+
74
110
  ## 后端 API 协议
75
111
 
76
112
  本插件通过 HTTP JSON API 与后端网关通信。二次开发者若需对接自有后端,需实现以下接口。
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
2
+ import { weixinPlugin } from "./src/channel.js";
3
+ import { assertHostCompatibility } from "./src/compat.js";
4
+ import { WeixinConfigSchema } from "./src/config/config-schema.js";
5
+ import { setWeixinRuntime } from "./src/runtime.js";
6
+ export default {
7
+ id: "openclaw-weixin",
8
+ name: "Weixin",
9
+ description: "Weixin channel (getUpdates long-poll + sendMessage)",
10
+ configSchema: buildChannelConfigSchema(WeixinConfigSchema),
11
+ register(api) {
12
+ // Fail-fast: reject incompatible host versions before any side-effects.
13
+ assertHostCompatibility(api.runtime?.version);
14
+ if (api.runtime) {
15
+ setWeixinRuntime(api.runtime);
16
+ }
17
+ api.registerChannel({ plugin: weixinPlugin });
18
+ },
19
+ };
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AAErF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,eAAe;IACb,EAAE,EAAE,iBAAiB;IACrB,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,qDAAqD;IAClE,YAAY,EAAE,wBAAwB,CAAC,kBAAkB,CAAC;IAC1D,QAAQ,CAAC,GAAsB;QAC7B,wEAAwE;QACxE,uBAAuB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAE9C,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QAED,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IAChD,CAAC;CACF,CAAC"}
@@ -0,0 +1,375 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { loadConfigBotAgent, loadConfigRouteTag } from "../auth/accounts.js";
6
+ import { logger } from "../util/logger.js";
7
+ import { redactBody, redactUrl } from "../util/redact.js";
8
+ function readPackageJson() {
9
+ try {
10
+ const dir = path.dirname(fileURLToPath(import.meta.url));
11
+ const pkgPath = path.resolve(dir, "..", "..", "package.json");
12
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
13
+ }
14
+ catch {
15
+ return {};
16
+ }
17
+ }
18
+ const pkg = readPackageJson();
19
+ const CHANNEL_VERSION = pkg.version ?? "unknown";
20
+ /** iLink-App-Id: 直接读取 package.json 顶层 ilink_appid 字段。 */
21
+ const ILINK_APP_ID = pkg.ilink_appid ?? "";
22
+ /**
23
+ * iLink-App-ClientVersion: uint32 encoded as 0x00MMNNPP
24
+ * High 8 bits fixed to 0; remaining bits: major<<16 | minor<<8 | patch.
25
+ * e.g. "1.0.11" -> 0x0001000B = 65547
26
+ */
27
+ function buildClientVersion(version) {
28
+ const parts = version.split(".").map((p) => parseInt(p, 10));
29
+ const major = parts[0] ?? 0;
30
+ const minor = parts[1] ?? 0;
31
+ const patch = parts[2] ?? 0;
32
+ return ((major & 0xff) << 16) | ((minor & 0xff) << 8) | (patch & 0xff);
33
+ }
34
+ const ILINK_APP_CLIENT_VERSION = buildClientVersion(pkg.version ?? "0.0.0");
35
+ /**
36
+ * Default `bot_agent` value used when the upstream app does not declare one.
37
+ * Mirrors the role of HTTP `User-Agent`'s implicit "no UA" fallback.
38
+ */
39
+ const DEFAULT_BOT_AGENT = "OpenClaw";
40
+ /** Maximum length (bytes) of the sanitized `bot_agent` string. */
41
+ const BOT_AGENT_MAX_LEN = 256;
42
+ /**
43
+ * Sanitize a user-supplied `botAgent` config value into a wire-safe string.
44
+ *
45
+ * Grammar (UA-style):
46
+ * bot_agent = product *( SP product )
47
+ * product = name "/" version [ SP "(" comment ")" ]
48
+ * name = 1*32( ALPHA / DIGIT / "_" / "." / "-" )
49
+ * version = 1*32( ALPHA / DIGIT / "_" / "." / "+" / "-" )
50
+ * comment = 1*64( printable ASCII minus "(" ")" )
51
+ *
52
+ * Tokens that fail to parse are dropped silently (no partial tokens kept).
53
+ * Returns `DEFAULT_BOT_AGENT` when the input is empty / all tokens dropped /
54
+ * the result exceeds the length cap after truncation.
55
+ */
56
+ export function sanitizeBotAgent(raw) {
57
+ if (!raw || typeof raw !== "string")
58
+ return DEFAULT_BOT_AGENT;
59
+ const trimmed = raw.trim();
60
+ if (!trimmed)
61
+ return DEFAULT_BOT_AGENT;
62
+ const productRe = /^[A-Za-z0-9_.\-]{1,32}\/[A-Za-z0-9_.+\-]{1,32}$/;
63
+ const commentCharRe = /^[\x20-\x27\x2A-\x7E]{1,64}$/;
64
+ // Tokenize on whitespace, but keep `(comment)` glued to the preceding product.
65
+ // Strategy: split by spaces, then re-attach any token that starts with "(".
66
+ const rawTokens = trimmed.split(/\s+/);
67
+ const tokens = [];
68
+ for (let i = 0; i < rawTokens.length; i += 1) {
69
+ const tok = rawTokens[i];
70
+ if (tok.startsWith("(") && !tok.endsWith(")")) {
71
+ // Multi-word comment; greedily collect until we find the closing ")".
72
+ let acc = tok;
73
+ while (i + 1 < rawTokens.length && !acc.endsWith(")")) {
74
+ i += 1;
75
+ acc += " " + rawTokens[i];
76
+ }
77
+ tokens.push(acc);
78
+ }
79
+ else {
80
+ tokens.push(tok);
81
+ }
82
+ }
83
+ const accepted = [];
84
+ let pendingProduct = null;
85
+ for (const tok of tokens) {
86
+ if (tok.startsWith("(") && tok.endsWith(")")) {
87
+ const inner = tok.slice(1, -1);
88
+ if (pendingProduct && commentCharRe.test(inner)) {
89
+ accepted.push(`${pendingProduct} (${inner})`);
90
+ pendingProduct = null;
91
+ }
92
+ else {
93
+ if (pendingProduct) {
94
+ accepted.push(pendingProduct);
95
+ pendingProduct = null;
96
+ }
97
+ }
98
+ continue;
99
+ }
100
+ if (pendingProduct) {
101
+ accepted.push(pendingProduct);
102
+ pendingProduct = null;
103
+ }
104
+ if (productRe.test(tok)) {
105
+ pendingProduct = tok;
106
+ }
107
+ }
108
+ if (pendingProduct)
109
+ accepted.push(pendingProduct);
110
+ if (accepted.length === 0)
111
+ return DEFAULT_BOT_AGENT;
112
+ const joined = accepted.join(" ");
113
+ if (Buffer.byteLength(joined, "utf-8") <= BOT_AGENT_MAX_LEN)
114
+ return joined;
115
+ // Truncate by dropping trailing tokens until under the cap.
116
+ const truncated = [];
117
+ let len = 0;
118
+ for (const t of accepted) {
119
+ const add = (truncated.length === 0 ? 0 : 1) + Buffer.byteLength(t, "utf-8");
120
+ if (len + add > BOT_AGENT_MAX_LEN)
121
+ break;
122
+ truncated.push(t);
123
+ len += add;
124
+ }
125
+ return truncated.length > 0 ? truncated.join(" ") : DEFAULT_BOT_AGENT;
126
+ }
127
+ /** Build the `base_info` payload included in every API request. */
128
+ export function buildBaseInfo() {
129
+ return {
130
+ channel_version: CHANNEL_VERSION,
131
+ bot_agent: sanitizeBotAgent(loadConfigBotAgent()),
132
+ };
133
+ }
134
+ /** Default timeout for long-poll getUpdates requests. */
135
+ const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
136
+ /** Default timeout for regular API requests (sendMessage, getUploadUrl). */
137
+ const DEFAULT_API_TIMEOUT_MS = 15_000;
138
+ /** Default timeout for lightweight API requests (getConfig, sendTyping). */
139
+ const DEFAULT_CONFIG_TIMEOUT_MS = 10_000;
140
+ function ensureTrailingSlash(url) {
141
+ return url.endsWith("/") ? url : `${url}/`;
142
+ }
143
+ /** X-WECHAT-UIN header: random uint32 -> decimal string -> base64. */
144
+ function randomWechatUin() {
145
+ const uint32 = crypto.randomBytes(4).readUInt32BE(0);
146
+ return Buffer.from(String(uint32), "utf-8").toString("base64");
147
+ }
148
+ /** Build headers shared by both GET and POST requests. */
149
+ function buildCommonHeaders() {
150
+ const headers = {
151
+ "iLink-App-Id": ILINK_APP_ID,
152
+ "iLink-App-ClientVersion": String(ILINK_APP_CLIENT_VERSION),
153
+ };
154
+ const routeTag = loadConfigRouteTag();
155
+ if (routeTag) {
156
+ headers.SKRouteTag = routeTag;
157
+ }
158
+ return headers;
159
+ }
160
+ function buildHeaders(opts) {
161
+ const headers = {
162
+ "Content-Type": "application/json",
163
+ AuthorizationType: "ilink_bot_token",
164
+ "Content-Length": String(Buffer.byteLength(opts.body, "utf-8")),
165
+ "X-WECHAT-UIN": randomWechatUin(),
166
+ ...buildCommonHeaders(),
167
+ };
168
+ if (opts.token?.trim()) {
169
+ headers.Authorization = `Bearer ${opts.token.trim()}`;
170
+ }
171
+ logger.debug(`requestHeaders: ${JSON.stringify({ ...headers, Authorization: headers.Authorization ? "Bearer ***" : undefined })}`);
172
+ return headers;
173
+ }
174
+ /**
175
+ * GET fetch wrapper: send a GET request to a Weixin API endpoint.
176
+ * When `timeoutMs` is set, the request is aborted after that many milliseconds.
177
+ * Query parameters should already be encoded in `endpoint`.
178
+ * Returns the raw response text on success; throws on HTTP error or (if used) timeout abort.
179
+ */
180
+ export async function apiGetFetch(params) {
181
+ const base = ensureTrailingSlash(params.baseUrl);
182
+ const url = new URL(params.endpoint, base);
183
+ const hdrs = buildCommonHeaders();
184
+ logger.debug(`GET ${redactUrl(url.toString())}`);
185
+ const timeoutMs = params.timeoutMs;
186
+ const controller = timeoutMs != null && timeoutMs > 0 ? new AbortController() : undefined;
187
+ const t = controller != null && timeoutMs != null
188
+ ? setTimeout(() => controller.abort(), timeoutMs)
189
+ : undefined;
190
+ try {
191
+ const res = await fetch(url.toString(), {
192
+ method: "GET",
193
+ headers: hdrs,
194
+ ...(controller ? { signal: controller.signal } : {}),
195
+ });
196
+ if (t !== undefined)
197
+ clearTimeout(t);
198
+ const rawText = await res.text();
199
+ logger.debug(`${params.label} status=${res.status} raw=${redactBody(rawText)}`);
200
+ if (!res.ok) {
201
+ throw new Error(`${params.label} ${res.status}: ${rawText}`);
202
+ }
203
+ return rawText;
204
+ }
205
+ catch (err) {
206
+ if (t !== undefined)
207
+ clearTimeout(t);
208
+ throw err;
209
+ }
210
+ }
211
+ /**
212
+ * Common fetch wrapper: POST JSON to a Weixin API endpoint.
213
+ * When `timeoutMs` is provided, the request is aborted after that many milliseconds.
214
+ * When omitted, no client-side timeout is applied (relies on OS/TCP stack).
215
+ * Returns the raw response text on success; throws on HTTP error or timeout.
216
+ */
217
+ export async function apiPostFetch(params) {
218
+ const base = ensureTrailingSlash(params.baseUrl);
219
+ const url = new URL(params.endpoint, base);
220
+ const hdrs = buildHeaders({ token: params.token, body: params.body });
221
+ logger.debug(`POST ${redactUrl(url.toString())} body=${redactBody(params.body)}`);
222
+ const controller = params.timeoutMs !== undefined ? new AbortController() : undefined;
223
+ const t = controller != null && params.timeoutMs !== undefined
224
+ ? setTimeout(() => controller.abort(), params.timeoutMs)
225
+ : undefined;
226
+ try {
227
+ const res = await fetch(url.toString(), {
228
+ method: "POST",
229
+ headers: hdrs,
230
+ body: params.body,
231
+ ...(controller ? { signal: controller.signal } : {}),
232
+ });
233
+ if (t !== undefined)
234
+ clearTimeout(t);
235
+ const rawText = await res.text();
236
+ logger.debug(`${params.label} status=${res.status} raw=${redactBody(rawText)}`);
237
+ if (!res.ok) {
238
+ throw new Error(`${params.label} ${res.status}: ${rawText}`);
239
+ }
240
+ return rawText;
241
+ }
242
+ catch (err) {
243
+ if (t !== undefined)
244
+ clearTimeout(t);
245
+ throw err;
246
+ }
247
+ }
248
+ /**
249
+ * Long-poll getUpdates. Server should hold the request until new messages or timeout.
250
+ *
251
+ * On client-side timeout (no server response within timeoutMs), returns an empty response
252
+ * with ret=0 so the caller can simply retry. This is normal for long-poll.
253
+ */
254
+ export async function getUpdates(params) {
255
+ const timeout = params.timeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS;
256
+ try {
257
+ const rawText = await apiPostFetch({
258
+ baseUrl: params.baseUrl,
259
+ endpoint: "ilink/bot/getupdates",
260
+ body: JSON.stringify({
261
+ get_updates_buf: params.get_updates_buf ?? "",
262
+ base_info: buildBaseInfo(),
263
+ }),
264
+ token: params.token,
265
+ timeoutMs: timeout,
266
+ label: "getUpdates",
267
+ });
268
+ const resp = JSON.parse(rawText);
269
+ return resp;
270
+ }
271
+ catch (err) {
272
+ // Long-poll timeout is normal; return empty response so caller can retry
273
+ if (err instanceof Error && err.name === "AbortError") {
274
+ logger.debug(`getUpdates: client-side timeout after ${timeout}ms, returning empty response`);
275
+ return { ret: 0, msgs: [], get_updates_buf: params.get_updates_buf };
276
+ }
277
+ throw err;
278
+ }
279
+ }
280
+ /** Get a pre-signed CDN upload URL for a file. */
281
+ export async function getUploadUrl(params) {
282
+ const rawText = await apiPostFetch({
283
+ baseUrl: params.baseUrl,
284
+ endpoint: "ilink/bot/getuploadurl",
285
+ body: JSON.stringify({
286
+ filekey: params.filekey,
287
+ media_type: params.media_type,
288
+ to_user_id: params.to_user_id,
289
+ rawsize: params.rawsize,
290
+ rawfilemd5: params.rawfilemd5,
291
+ filesize: params.filesize,
292
+ thumb_rawsize: params.thumb_rawsize,
293
+ thumb_rawfilemd5: params.thumb_rawfilemd5,
294
+ thumb_filesize: params.thumb_filesize,
295
+ no_need_thumb: params.no_need_thumb,
296
+ aeskey: params.aeskey,
297
+ base_info: buildBaseInfo(),
298
+ }),
299
+ token: params.token,
300
+ timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,
301
+ label: "getUploadUrl",
302
+ });
303
+ const resp = JSON.parse(rawText);
304
+ return resp;
305
+ }
306
+ /** Send a single message downstream. */
307
+ export async function sendMessage(params) {
308
+ await apiPostFetch({
309
+ baseUrl: params.baseUrl,
310
+ endpoint: "ilink/bot/sendmessage",
311
+ body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
312
+ token: params.token,
313
+ timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,
314
+ label: "sendMessage",
315
+ });
316
+ }
317
+ /** Fetch bot config (includes typing_ticket) for a given user. */
318
+ export async function getConfig(params) {
319
+ const rawText = await apiPostFetch({
320
+ baseUrl: params.baseUrl,
321
+ endpoint: "ilink/bot/getconfig",
322
+ body: JSON.stringify({
323
+ ilink_user_id: params.ilinkUserId,
324
+ context_token: params.contextToken,
325
+ base_info: buildBaseInfo(),
326
+ }),
327
+ token: params.token,
328
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
329
+ label: "getConfig",
330
+ });
331
+ const resp = JSON.parse(rawText);
332
+ return resp;
333
+ }
334
+ /** Send a typing indicator to a user. */
335
+ export async function sendTyping(params) {
336
+ await apiPostFetch({
337
+ baseUrl: params.baseUrl,
338
+ endpoint: "ilink/bot/sendtyping",
339
+ body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
340
+ token: params.token,
341
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
342
+ label: "sendTyping",
343
+ });
344
+ }
345
+ /**
346
+ * Notify Weixin that this channel client is stopping (gateway shutdown / channel stop).
347
+ * Uses a standalone timeout (not the gateway abort signal) so the request can finish
348
+ * after OpenClaw has already aborted the long-poll.
349
+ */
350
+ export async function notifyStop(params) {
351
+ const rawText = await apiPostFetch({
352
+ baseUrl: params.baseUrl,
353
+ endpoint: "ilink/bot/msg/notifystop",
354
+ body: JSON.stringify({ base_info: buildBaseInfo() }),
355
+ token: params.token,
356
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
357
+ label: "notifyStop",
358
+ });
359
+ return JSON.parse(rawText);
360
+ }
361
+ /**
362
+ * Notify Weixin that this channel client is starting (gateway startup / channel start).
363
+ */
364
+ export async function notifyStart(params) {
365
+ const rawText = await apiPostFetch({
366
+ baseUrl: params.baseUrl,
367
+ endpoint: "ilink/bot/msg/notifystart",
368
+ body: JSON.stringify({ base_info: buildBaseInfo() }),
369
+ token: params.token,
370
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
371
+ label: "notifyStart",
372
+ });
373
+ return JSON.parse(rawText);
374
+ }
375
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/api/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { getConfig } from "./api.js";
2
+ const CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
3
+ const CONFIG_CACHE_INITIAL_RETRY_MS = 2_000;
4
+ const CONFIG_CACHE_MAX_RETRY_MS = 60 * 60 * 1000;
5
+ /**
6
+ * Per-user getConfig cache with periodic random refresh (within 24h) and
7
+ * exponential-backoff retry (up to 1h) on failure.
8
+ */
9
+ export class WeixinConfigManager {
10
+ apiOpts;
11
+ log;
12
+ cache = new Map();
13
+ constructor(apiOpts, log) {
14
+ this.apiOpts = apiOpts;
15
+ this.log = log;
16
+ }
17
+ async getForUser(userId, contextToken) {
18
+ const now = Date.now();
19
+ const entry = this.cache.get(userId);
20
+ const shouldFetch = !entry || now >= entry.nextFetchAt;
21
+ if (shouldFetch) {
22
+ let fetchOk = false;
23
+ try {
24
+ const resp = await getConfig({
25
+ baseUrl: this.apiOpts.baseUrl,
26
+ token: this.apiOpts.token,
27
+ ilinkUserId: userId,
28
+ contextToken,
29
+ });
30
+ if (resp.ret === 0) {
31
+ this.cache.set(userId, {
32
+ config: { typingTicket: resp.typing_ticket ?? "" },
33
+ everSucceeded: true,
34
+ nextFetchAt: now + Math.random() * CONFIG_CACHE_TTL_MS,
35
+ retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
36
+ });
37
+ this.log(`[weixin] config ${entry?.everSucceeded ? "refreshed" : "cached"} for ${userId}`);
38
+ fetchOk = true;
39
+ }
40
+ }
41
+ catch (err) {
42
+ this.log(`[weixin] getConfig failed for ${userId} (ignored): ${String(err)}`);
43
+ }
44
+ if (!fetchOk) {
45
+ const prevDelay = entry?.retryDelayMs ?? CONFIG_CACHE_INITIAL_RETRY_MS;
46
+ const nextDelay = Math.min(prevDelay * 2, CONFIG_CACHE_MAX_RETRY_MS);
47
+ if (entry) {
48
+ entry.nextFetchAt = now + nextDelay;
49
+ entry.retryDelayMs = nextDelay;
50
+ }
51
+ else {
52
+ this.cache.set(userId, {
53
+ config: { typingTicket: "" },
54
+ everSucceeded: false,
55
+ nextFetchAt: now + CONFIG_CACHE_INITIAL_RETRY_MS,
56
+ retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
57
+ });
58
+ }
59
+ }
60
+ }
61
+ return this.cache.get(userId)?.config ?? { typingTicket: "" };
62
+ }
63
+ }
64
+ //# sourceMappingURL=config-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-cache.js","sourceRoot":"","sources":["../../../src/api/config-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { logger } from "../util/logger.js";
2
+ const SESSION_PAUSE_DURATION_MS = 60 * 60 * 1000;
3
+ /** Error code returned by the server when the bot session has expired. */
4
+ export const SESSION_EXPIRED_ERRCODE = -14;
5
+ const pauseUntilMap = new Map();
6
+ /** Pause all inbound/outbound API calls for `accountId` for one hour. */
7
+ export function pauseSession(accountId) {
8
+ const until = Date.now() + SESSION_PAUSE_DURATION_MS;
9
+ pauseUntilMap.set(accountId, until);
10
+ logger.info(`session-guard: paused accountId=${accountId} until=${new Date(until).toISOString()} (${SESSION_PAUSE_DURATION_MS / 1000}s)`);
11
+ }
12
+ /** Returns `true` when the bot is still within its one-hour cooldown window. */
13
+ export function isSessionPaused(accountId) {
14
+ const until = pauseUntilMap.get(accountId);
15
+ if (until === undefined)
16
+ return false;
17
+ if (Date.now() >= until) {
18
+ pauseUntilMap.delete(accountId);
19
+ return false;
20
+ }
21
+ return true;
22
+ }
23
+ /** Milliseconds remaining until the pause expires (0 when not paused). */
24
+ export function getRemainingPauseMs(accountId) {
25
+ const until = pauseUntilMap.get(accountId);
26
+ if (until === undefined)
27
+ return 0;
28
+ const remaining = until - Date.now();
29
+ if (remaining <= 0) {
30
+ pauseUntilMap.delete(accountId);
31
+ return 0;
32
+ }
33
+ return remaining;
34
+ }
35
+ /** Throw if the session is currently paused. Call before any API request. */
36
+ export function assertSessionActive(accountId) {
37
+ if (isSessionPaused(accountId)) {
38
+ const remainingMin = Math.ceil(getRemainingPauseMs(accountId) / 60_000);
39
+ throw new Error(`session paused for accountId=${accountId}, ${remainingMin} min remaining (errcode ${SESSION_EXPIRED_ERRCODE})`);
40
+ }
41
+ }
42
+ /**
43
+ * Reset internal state — only for tests.
44
+ * @internal
45
+ */
46
+ export function _resetForTest() {
47
+ pauseUntilMap.clear();
48
+ }
49
+ //# sourceMappingURL=session-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-guard.js","sourceRoot":"","sources":["../../../src/api/session-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEjD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,EAAE,CAAC;AAE3C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEhD,yEAAyE;AACzE,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,yBAAyB,CAAC;IACrD,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,CACT,mCAAmC,SAAS,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,KAAK,yBAAyB,GAAG,IAAI,IAAI,CAC7H,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,EAAE,CAAC;QACxB,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnB,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,IAAI,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,gCAAgC,SAAS,KAAK,YAAY,2BAA2B,uBAAuB,GAAG,CAChH,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,35 @@
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
+ /** proto: UploadMediaType */
6
+ export const UploadMediaType = {
7
+ IMAGE: 1,
8
+ VIDEO: 2,
9
+ FILE: 3,
10
+ VOICE: 4,
11
+ };
12
+ export const MessageType = {
13
+ NONE: 0,
14
+ USER: 1,
15
+ BOT: 2,
16
+ };
17
+ export const MessageItemType = {
18
+ NONE: 0,
19
+ TEXT: 1,
20
+ IMAGE: 2,
21
+ VOICE: 3,
22
+ FILE: 4,
23
+ VIDEO: 5,
24
+ };
25
+ export const MessageState = {
26
+ NEW: 0,
27
+ GENERATING: 1,
28
+ FINISH: 2,
29
+ };
30
+ /** Typing status: 1 = typing (default), 2 = cancel typing. */
31
+ export const TypingStatus = {
32
+ TYPING: 1,
33
+ CANCEL: 2,
34
+ };
35
+ //# sourceMappingURL=types.js.map