@tencent-weixin/openclaw-weixin 2.3.1 → 2.4.2

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 (102) hide show
  1. package/dist/index.js +16 -0
  2. package/dist/index.js.map +1 -0
  3. package/dist/src/api/api.js +374 -0
  4. package/dist/src/api/api.js.map +1 -0
  5. package/dist/src/api/config-cache.js +64 -0
  6. package/dist/src/api/config-cache.js.map +1 -0
  7. package/dist/src/api/session-guard.js +49 -0
  8. package/dist/src/api/session-guard.js.map +1 -0
  9. package/dist/src/api/types.js +35 -0
  10. package/dist/src/api/types.js.map +1 -0
  11. package/dist/src/auth/accounts.js +326 -0
  12. package/dist/src/auth/accounts.js.map +1 -0
  13. package/dist/src/auth/login-qr.js +332 -0
  14. package/dist/src/auth/login-qr.js.map +1 -0
  15. package/dist/src/auth/pairing.js +104 -0
  16. package/dist/src/auth/pairing.js.map +1 -0
  17. package/dist/src/cdn/aes-ecb.js +19 -0
  18. package/dist/src/cdn/aes-ecb.js.map +1 -0
  19. package/dist/src/cdn/cdn-upload.js +73 -0
  20. package/dist/src/cdn/cdn-upload.js.map +1 -0
  21. package/dist/src/cdn/cdn-url.js +14 -0
  22. package/dist/src/cdn/cdn-url.js.map +1 -0
  23. package/dist/src/cdn/pic-decrypt.js +89 -0
  24. package/dist/src/cdn/pic-decrypt.js.map +1 -0
  25. package/dist/src/cdn/upload.js +106 -0
  26. package/dist/src/cdn/upload.js.map +1 -0
  27. package/dist/src/channel.js +460 -0
  28. package/dist/src/channel.js.map +1 -0
  29. package/dist/src/compat.js +67 -0
  30. package/dist/src/compat.js.map +1 -0
  31. package/dist/src/config/config-schema.js +19 -0
  32. package/dist/src/config/config-schema.js.map +1 -0
  33. package/dist/src/media/media-download.js +95 -0
  34. package/dist/src/media/media-download.js.map +1 -0
  35. package/dist/src/media/mime.js +73 -0
  36. package/dist/src/media/mime.js.map +1 -0
  37. package/dist/src/media/silk-transcode.js +64 -0
  38. package/dist/src/media/silk-transcode.js.map +1 -0
  39. package/dist/src/media/voice-outbound.js +177 -0
  40. package/dist/src/media/voice-outbound.js.map +1 -0
  41. package/dist/src/messaging/abort-fence.js +70 -0
  42. package/dist/src/messaging/abort-fence.js.map +1 -0
  43. package/dist/src/messaging/buttons.js +117 -0
  44. package/dist/src/messaging/buttons.js.map +1 -0
  45. package/dist/src/messaging/debug-mode.js +63 -0
  46. package/dist/src/messaging/debug-mode.js.map +1 -0
  47. package/dist/src/messaging/error-notice.js +24 -0
  48. package/dist/src/messaging/error-notice.js.map +1 -0
  49. package/dist/src/messaging/inbound.js +201 -0
  50. package/dist/src/messaging/inbound.js.map +1 -0
  51. package/dist/src/messaging/lane-key.js +66 -0
  52. package/dist/src/messaging/lane-key.js.map +1 -0
  53. package/dist/src/messaging/markdown-filter.js +368 -0
  54. package/dist/src/messaging/markdown-filter.js.map +1 -0
  55. package/dist/src/messaging/merged-record.js +149 -0
  56. package/dist/src/messaging/merged-record.js.map +1 -0
  57. package/dist/src/messaging/model-buttons.js +182 -0
  58. package/dist/src/messaging/model-buttons.js.map +1 -0
  59. package/dist/src/messaging/model-callback-handler.js +133 -0
  60. package/dist/src/messaging/model-callback-handler.js.map +1 -0
  61. package/dist/src/messaging/outbound-hooks.js +56 -0
  62. package/dist/src/messaging/outbound-hooks.js.map +1 -0
  63. package/dist/src/messaging/process-message.js +381 -0
  64. package/dist/src/messaging/process-message.js.map +1 -0
  65. package/dist/src/messaging/send-media.js +54 -0
  66. package/dist/src/messaging/send-media.js.map +1 -0
  67. package/dist/src/messaging/send.js +182 -0
  68. package/dist/src/messaging/send.js.map +1 -0
  69. package/dist/src/messaging/slash-commands.js +70 -0
  70. package/dist/src/messaging/slash-commands.js.map +1 -0
  71. package/dist/src/monitor/lane-scheduler.js +46 -0
  72. package/dist/src/monitor/lane-scheduler.js.map +1 -0
  73. package/dist/src/monitor/monitor.js +143 -0
  74. package/dist/src/monitor/monitor.js.map +1 -0
  75. package/dist/src/runtime.js +54 -0
  76. package/dist/src/runtime.js.map +1 -0
  77. package/dist/src/storage/state-dir.js +9 -0
  78. package/dist/src/storage/state-dir.js.map +1 -0
  79. package/dist/src/storage/sync-buf.js +64 -0
  80. package/dist/src/storage/sync-buf.js.map +1 -0
  81. package/dist/src/streaming/stream-pipeline.js +431 -0
  82. package/dist/src/streaming/stream-pipeline.js.map +1 -0
  83. package/dist/src/streaming/stream-session.js +260 -0
  84. package/dist/src/streaming/stream-session.js.map +1 -0
  85. package/dist/src/streaming/stream.js +239 -0
  86. package/dist/src/streaming/stream.js.map +1 -0
  87. package/dist/src/util/logger.js +120 -0
  88. package/dist/src/util/logger.js.map +1 -0
  89. package/dist/src/util/markdown-fences.js +54 -0
  90. package/dist/src/util/markdown-fences.js.map +1 -0
  91. package/dist/src/util/random.js +16 -0
  92. package/dist/src/util/random.js.map +1 -0
  93. package/dist/src/util/redact.js +54 -0
  94. package/dist/src/util/redact.js.map +1 -0
  95. package/index.ts +0 -5
  96. package/openclaw.plugin.json +11 -1
  97. package/package.json +9 -2
  98. package/src/api/api.ts +2 -3
  99. package/src/auth/accounts.ts +0 -1
  100. package/src/channel.ts +13 -1
  101. package/src/monitor/monitor.ts +11 -10
  102. package/src/runtime.ts +0 -70
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Per-bot debug mode toggle, persisted to disk so it survives gateway restarts.
3
+ *
4
+ * State file: `<stateDir>/openclaw-weixin/debug-mode.json`
5
+ * Format: `{ "accounts": { "<accountId>": true, ... } }`
6
+ *
7
+ * When enabled, processOneMessage appends a timing summary after each
8
+ * AI reply is delivered to the user.
9
+ */
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { resolveStateDir } from "../storage/state-dir.js";
13
+ import { logger } from "../util/logger.js";
14
+ function resolveDebugModePath() {
15
+ return path.join(resolveStateDir(), "openclaw-weixin", "debug-mode.json");
16
+ }
17
+ function loadState() {
18
+ try {
19
+ const raw = fs.readFileSync(resolveDebugModePath(), "utf-8");
20
+ const parsed = JSON.parse(raw);
21
+ if (parsed && typeof parsed.accounts === "object")
22
+ return parsed;
23
+ }
24
+ catch {
25
+ // missing or corrupt — start fresh
26
+ }
27
+ return { accounts: {} };
28
+ }
29
+ function saveState(state) {
30
+ const filePath = resolveDebugModePath();
31
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
32
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
33
+ }
34
+ /** Toggle debug mode for a bot account. Returns the new state. */
35
+ export function toggleDebugMode(accountId) {
36
+ const state = loadState();
37
+ const next = !state.accounts[accountId];
38
+ state.accounts[accountId] = next;
39
+ try {
40
+ saveState(state);
41
+ }
42
+ catch (err) {
43
+ logger.error(`debug-mode: failed to persist state: ${String(err)}`);
44
+ }
45
+ return next;
46
+ }
47
+ /** Check whether debug mode is active for a bot account. */
48
+ export function isDebugMode(accountId) {
49
+ return loadState().accounts[accountId] === true;
50
+ }
51
+ /**
52
+ * Reset internal state — only for tests.
53
+ * @internal
54
+ */
55
+ export function _resetForTest() {
56
+ try {
57
+ fs.unlinkSync(resolveDebugModePath());
58
+ }
59
+ catch {
60
+ // ignore if not present
61
+ }
62
+ }
63
+ //# sourceMappingURL=debug-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-mode.js","sourceRoot":"","sources":["../../../src/messaging/debug-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAM3C,SAAS,oBAAoB;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,EAAE,OAAO,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QACjD,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAqB;IACtC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACxC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,SAAS,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { logger } from "../util/logger.js";
2
+ import { sendMessageWeixin } from "./send.js";
3
+ /**
4
+ * Send a plain-text error notice back to the user.
5
+ * Fire-and-forget: errors are logged but never thrown, so callers stay unaffected.
6
+ * No-op when contextToken is absent (we have no conversation reference to reply into).
7
+ */
8
+ export async function sendWeixinErrorNotice(params) {
9
+ if (!params.contextToken) {
10
+ logger.warn(`sendWeixinErrorNotice: no contextToken for to=${params.to}, sending without context`);
11
+ }
12
+ try {
13
+ await sendMessageWeixin({ to: params.to, text: params.message, opts: {
14
+ baseUrl: params.baseUrl,
15
+ token: params.token,
16
+ contextToken: params.contextToken,
17
+ } });
18
+ logger.debug(`sendWeixinErrorNotice: sent to=${params.to}`);
19
+ }
20
+ catch (err) {
21
+ params.errLog(`[weixin] sendWeixinErrorNotice failed to=${params.to}: ${String(err)}`);
22
+ }
23
+ }
24
+ //# sourceMappingURL=error-notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-notice.js","sourceRoot":"","sources":["../../../src/messaging/error-notice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAO3C;IACC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,iDAAiD,MAAM,CAAC,EAAE,2BAA2B,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,CAAC;QACH,MAAM,iBAAiB,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE;gBACnE,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,YAAY,EAAE,MAAM,CAAC,YAAY;aAClC,EAAC,CAAC,CAAC;QACJ,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,MAAM,CAAC,4CAA4C,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC"}
@@ -0,0 +1,201 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { logger } from "../util/logger.js";
4
+ import { generateId } from "../util/random.js";
5
+ import { MessageItemType } from "../api/types.js";
6
+ import { resolveStateDir } from "../storage/state-dir.js";
7
+ // ---------------------------------------------------------------------------
8
+ // Context token store (in-process cache + disk persistence)
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * contextToken is issued per-message by the Weixin getupdates API and must
12
+ * be echoed verbatim in every outbound send. The in-memory map is the primary
13
+ * lookup; a disk-backed file per account ensures tokens survive gateway restarts.
14
+ */
15
+ const contextTokenStore = new Map();
16
+ function contextTokenKey(accountId, userId) {
17
+ return `${accountId}:${userId}`;
18
+ }
19
+ // ---------------------------------------------------------------------------
20
+ // Disk persistence helpers
21
+ // ---------------------------------------------------------------------------
22
+ function resolveContextTokenFilePath(accountId) {
23
+ return path.join(resolveStateDir(), "openclaw-weixin", "accounts", `${accountId}.context-tokens.json`);
24
+ }
25
+ /** Persist all context tokens for a given account to disk. */
26
+ function persistContextTokens(accountId) {
27
+ const prefix = `${accountId}:`;
28
+ const tokens = {};
29
+ for (const [k, v] of contextTokenStore) {
30
+ if (k.startsWith(prefix)) {
31
+ tokens[k.slice(prefix.length)] = v;
32
+ }
33
+ }
34
+ const filePath = resolveContextTokenFilePath(accountId);
35
+ try {
36
+ const dir = path.dirname(filePath);
37
+ fs.mkdirSync(dir, { recursive: true });
38
+ fs.writeFileSync(filePath, JSON.stringify(tokens, null, 0), "utf-8");
39
+ }
40
+ catch (err) {
41
+ logger.warn(`persistContextTokens: failed to write ${filePath}: ${String(err)}`);
42
+ }
43
+ }
44
+ /**
45
+ * Restore persisted context tokens for an account into the in-memory map.
46
+ * Called once during gateway startAccount to survive restarts.
47
+ */
48
+ export function restoreContextTokens(accountId) {
49
+ const filePath = resolveContextTokenFilePath(accountId);
50
+ try {
51
+ if (!fs.existsSync(filePath))
52
+ return;
53
+ const raw = fs.readFileSync(filePath, "utf-8");
54
+ const tokens = JSON.parse(raw);
55
+ let count = 0;
56
+ for (const [userId, token] of Object.entries(tokens)) {
57
+ if (typeof token === "string" && token) {
58
+ contextTokenStore.set(contextTokenKey(accountId, userId), token);
59
+ count++;
60
+ }
61
+ }
62
+ logger.info(`restoreContextTokens: restored ${count} tokens for account=${accountId}`);
63
+ }
64
+ catch (err) {
65
+ logger.warn(`restoreContextTokens: failed to read ${filePath}: ${String(err)}`);
66
+ }
67
+ }
68
+ /** Remove all context tokens for a given account (memory + disk). */
69
+ export function clearContextTokensForAccount(accountId) {
70
+ const prefix = `${accountId}:`;
71
+ for (const k of [...contextTokenStore.keys()]) {
72
+ if (k.startsWith(prefix)) {
73
+ contextTokenStore.delete(k);
74
+ }
75
+ }
76
+ const filePath = resolveContextTokenFilePath(accountId);
77
+ try {
78
+ if (fs.existsSync(filePath))
79
+ fs.unlinkSync(filePath);
80
+ }
81
+ catch (err) {
82
+ logger.warn(`clearContextTokensForAccount: failed to remove ${filePath}: ${String(err)}`);
83
+ }
84
+ logger.info(`clearContextTokensForAccount: cleared tokens for account=${accountId}`);
85
+ }
86
+ /** Store a context token for a given account+user pair (memory + disk). */
87
+ export function setContextToken(accountId, userId, token) {
88
+ const k = contextTokenKey(accountId, userId);
89
+ logger.debug(`setContextToken: key=${k}`);
90
+ contextTokenStore.set(k, token);
91
+ persistContextTokens(accountId);
92
+ }
93
+ /** Retrieve the cached context token for a given account+user pair. */
94
+ export function getContextToken(accountId, userId) {
95
+ const k = contextTokenKey(accountId, userId);
96
+ const val = contextTokenStore.get(k);
97
+ logger.debug(`getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`);
98
+ return val;
99
+ }
100
+ /**
101
+ * Find all accountIds that have an active contextToken for the given userId.
102
+ * Used to infer the sending bot account from the recipient address when
103
+ * accountId is not explicitly provided (e.g. cron delivery).
104
+ *
105
+ * Returns all matching accountIds (not just the first) so the caller can
106
+ * detect ambiguity when multiple accounts have sessions with the same user.
107
+ */
108
+ export function findAccountIdsByContextToken(accountIds, userId) {
109
+ return accountIds.filter((id) => contextTokenStore.has(contextTokenKey(id, userId)));
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Message ID generation
113
+ // ---------------------------------------------------------------------------
114
+ function generateMessageSid() {
115
+ return generateId("openclaw-weixin");
116
+ }
117
+ /** Returns true if the message item is a media type (image, video, file, or voice). */
118
+ export function isMediaItem(item) {
119
+ return (item.type === MessageItemType.IMAGE ||
120
+ item.type === MessageItemType.VIDEO ||
121
+ item.type === MessageItemType.FILE ||
122
+ item.type === MessageItemType.VOICE);
123
+ }
124
+ function bodyFromItemList(itemList) {
125
+ if (!itemList?.length)
126
+ return "";
127
+ for (const item of itemList) {
128
+ if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
129
+ const text = String(item.text_item.text);
130
+ const ref = item.ref_msg;
131
+ if (!ref)
132
+ return text;
133
+ // Quoted media is passed as MediaPath; only include the current text as body.
134
+ if (ref.message_item && isMediaItem(ref.message_item))
135
+ return text;
136
+ // Build quoted context from both title and message_item content.
137
+ const parts = [];
138
+ if (ref.title)
139
+ parts.push(ref.title);
140
+ if (ref.message_item) {
141
+ const refBody = bodyFromItemList([ref.message_item]);
142
+ if (refBody)
143
+ parts.push(refBody);
144
+ }
145
+ if (!parts.length)
146
+ return text;
147
+ return `[引用: ${parts.join(" | ")}]\n${text}`;
148
+ }
149
+ // 语音转文字:如果语音消息有 text 字段,直接使用文字内容
150
+ if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
151
+ return item.voice_item.text;
152
+ }
153
+ }
154
+ return "";
155
+ }
156
+ /**
157
+ * Convert a WeixinMessage from getUpdates to the inbound MsgContext for the core pipeline.
158
+ * Media: only pass MediaPath (local file, after CDN download + decrypt).
159
+ * We never pass MediaUrl — the upstream CDN URL is encrypted/auth-only.
160
+ * Priority when multiple media types present: image > video > file > voice.
161
+ */
162
+ export function weixinMessageToMsgContext(msg, accountId, opts) {
163
+ const from_user_id = msg.from_user_id ?? "";
164
+ const ctx = {
165
+ Body: bodyFromItemList(msg.item_list),
166
+ From: from_user_id,
167
+ To: from_user_id,
168
+ AccountId: accountId,
169
+ OriginatingChannel: "openclaw-weixin",
170
+ OriginatingTo: from_user_id,
171
+ MessageSid: generateMessageSid(),
172
+ Timestamp: msg.create_time_ms,
173
+ Provider: "openclaw-weixin",
174
+ ChatType: "direct",
175
+ };
176
+ if (msg.context_token) {
177
+ ctx.context_token = msg.context_token;
178
+ }
179
+ if (opts?.decryptedPicPath) {
180
+ ctx.MediaPath = opts.decryptedPicPath;
181
+ ctx.MediaType = "image/*";
182
+ }
183
+ else if (opts?.decryptedVideoPath) {
184
+ ctx.MediaPath = opts.decryptedVideoPath;
185
+ ctx.MediaType = "video/mp4";
186
+ }
187
+ else if (opts?.decryptedFilePath) {
188
+ ctx.MediaPath = opts.decryptedFilePath;
189
+ ctx.MediaType = opts.fileMediaType ?? "application/octet-stream";
190
+ }
191
+ else if (opts?.decryptedVoicePath) {
192
+ ctx.MediaPath = opts.decryptedVoicePath;
193
+ ctx.MediaType = opts.voiceMediaType ?? "audio/wav";
194
+ }
195
+ return ctx;
196
+ }
197
+ /** Extract the context_token from an inbound WeixinMsgContext. */
198
+ export function getContextTokenFromMsgContext(ctx) {
199
+ return ctx.context_token;
200
+ }
201
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/messaging/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { isAbortRequestText } from "openclaw/plugin-sdk/reply-runtime";
2
+ import { MessageItemType } from "../api/types.js";
3
+ /**
4
+ * Lane key for Weixin message scheduling.
5
+ *
6
+ * Mirrors the Telegram channel's `getTelegramSequentialKey` (see
7
+ * extensions/telegram/src/sequential-key.ts in the openclaw main repo).
8
+ *
9
+ * Design:
10
+ * - Different users get different lane keys -> processed concurrently.
11
+ * - Same user, regular messages -> share `wx:<account>:<user>` lane,
12
+ * processed strictly in arrival order so AI replies, typing state and
13
+ * inbound session writes do not interleave.
14
+ * - Abort requests (e.g. "stop", "中止") are routed to a separate
15
+ * `wx:<account>:<user>:control` lane so they can run immediately even when
16
+ * the main lane is occupied by a long-running tool turn. The actual
17
+ * "stopping" is implemented via the abort-fence mechanism (see
18
+ * `abort-fence.ts`).
19
+ */
20
+ function extractFirstTextBody(itemList) {
21
+ if (!itemList?.length)
22
+ return "";
23
+ for (const item of itemList) {
24
+ if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
25
+ return String(item.text_item.text);
26
+ }
27
+ }
28
+ return "";
29
+ }
30
+ /** Extract text body suitable for abort detection from an inbound message. */
31
+ export function extractTextBodyForLane(msg) {
32
+ return extractFirstTextBody(msg.item_list);
33
+ }
34
+ /** Returns true when the message text is an abort intent (stop / 中止 / ...). */
35
+ export function isWeixinAbortMessage(msg) {
36
+ const text = extractTextBodyForLane(msg);
37
+ if (!text)
38
+ return false;
39
+ return isAbortRequestText(text);
40
+ }
41
+ /**
42
+ * Compute the lane key used by the per-key scheduler. Abort requests get the
43
+ * `:control` suffix to bypass the main lane.
44
+ *
45
+ * Note: the fallback `unknown` user happens for malformed payloads. Keep them
46
+ * on a shared lane so they cannot fan out and exhaust resources.
47
+ */
48
+ export function getWeixinLaneKey(ctx) {
49
+ const userId = ctx.msg.from_user_id?.trim() || "unknown";
50
+ const baseKey = `wx:${ctx.accountId}:${userId}`;
51
+ if (isWeixinAbortMessage(ctx.msg)) {
52
+ return `${baseKey}:control`;
53
+ }
54
+ return baseKey;
55
+ }
56
+ /**
57
+ * Per-user fence key shared by all dispatches that should be aborted as a
58
+ * group when the user sends an abort. This is intentionally the *base* key
59
+ * (without `:control`) so that an abort dispatched on the control lane bumps
60
+ * the same generation that the long-running main-lane dispatch is checking.
61
+ */
62
+ export function getWeixinFenceKey(ctx) {
63
+ const userId = ctx.msg.from_user_id?.trim() || "unknown";
64
+ return `wx:${ctx.accountId}:${userId}`;
65
+ }
66
+ //# sourceMappingURL=lane-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lane-key.js","sourceRoot":"","sources":["../../../src/messaging/lane-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD;;;;;;;;;;;;;;;;GAgBG;AAEH,SAAS,oBAAoB,CAAC,QAAwB;IACpD,IAAI,CAAC,QAAQ,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;YACvE,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,sBAAsB,CAAC,GAAkB;IACvD,OAAO,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,GAAkB;IACrD,MAAM,IAAI,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAyB;IACxD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IACzD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;IAChD,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,GAAG,OAAO,UAAU,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAyB;IACzD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IACzD,OAAO,MAAM,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;AACzC,CAAC"}