@wecom/wecom-openclaw-plugin 2026.4.23 → 2026.4.27
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.
- package/dist/src/media-handler.d.ts +18 -0
- package/dist/src/media-handler.js +52 -0
- package/dist/src/message-parser.d.ts +8 -0
- package/dist/src/message-parser.js +14 -0
- package/dist/src/monitor.js +112 -40
- package/package.json +1 -1
|
@@ -7,6 +7,24 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
|
7
7
|
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
|
8
8
|
import type { WSClient } from "@wecom/aibot-node-sdk";
|
|
9
9
|
import type { ResolvedWeComAccount } from "./utils.js";
|
|
10
|
+
/**
|
|
11
|
+
* 附件超过 OpenClaw 配置的 `agents.defaults.mediaMaxMb` 上限时抛出。
|
|
12
|
+
*
|
|
13
|
+
* 本错误由插件层主动判定并抛出,不依赖 OpenClaw 核心层错误消息的字符串匹配,
|
|
14
|
+
* 上层(monitor)可通过 `instanceof MediaOversizeError` 精确识别并向用户提示。
|
|
15
|
+
*/
|
|
16
|
+
export declare class MediaOversizeError extends Error {
|
|
17
|
+
readonly kind: "image" | "file";
|
|
18
|
+
readonly filename?: string;
|
|
19
|
+
readonly sizeBytes: number;
|
|
20
|
+
readonly maxBytes: number;
|
|
21
|
+
constructor(params: {
|
|
22
|
+
kind: "image" | "file";
|
|
23
|
+
filename?: string;
|
|
24
|
+
sizeBytes: number;
|
|
25
|
+
maxBytes: number;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
10
28
|
/**
|
|
11
29
|
* 下载并保存所有图片到本地,每张图片的下载带超时保护
|
|
12
30
|
*/
|
|
@@ -8,6 +8,30 @@ import { getWeComRuntime } from "./runtime.js";
|
|
|
8
8
|
import { IMAGE_DOWNLOAD_TIMEOUT_MS, FILE_DOWNLOAD_TIMEOUT_MS, DEFAULT_MEDIA_MAX_MB } from "./const.js";
|
|
9
9
|
import { withTimeout } from "./timeout.js";
|
|
10
10
|
// ============================================================================
|
|
11
|
+
// 媒体超限错误
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* 附件超过 OpenClaw 配置的 `agents.defaults.mediaMaxMb` 上限时抛出。
|
|
15
|
+
*
|
|
16
|
+
* 本错误由插件层主动判定并抛出,不依赖 OpenClaw 核心层错误消息的字符串匹配,
|
|
17
|
+
* 上层(monitor)可通过 `instanceof MediaOversizeError` 精确识别并向用户提示。
|
|
18
|
+
*/
|
|
19
|
+
export class MediaOversizeError extends Error {
|
|
20
|
+
kind;
|
|
21
|
+
filename;
|
|
22
|
+
sizeBytes;
|
|
23
|
+
maxBytes;
|
|
24
|
+
constructor(params) {
|
|
25
|
+
super(`Media oversize: kind=${params.kind}, size=${params.sizeBytes}, max=${params.maxBytes}` +
|
|
26
|
+
(params.filename ? `, filename=${params.filename}` : ""));
|
|
27
|
+
this.name = "MediaOversizeError";
|
|
28
|
+
this.kind = params.kind;
|
|
29
|
+
this.filename = params.filename;
|
|
30
|
+
this.sizeBytes = params.sizeBytes;
|
|
31
|
+
this.maxBytes = params.maxBytes;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// ============================================================================
|
|
11
35
|
// 图片格式检测辅助函数(基于 file-type 包)
|
|
12
36
|
// ============================================================================
|
|
13
37
|
/**
|
|
@@ -66,11 +90,25 @@ export async function downloadAndSaveImages(params) {
|
|
|
66
90
|
runtime.log?.(`[wecom] WARN: Downloaded data is not a valid image format`);
|
|
67
91
|
}
|
|
68
92
|
}
|
|
93
|
+
// 大小校验由插件层主动进行,超限抛出 MediaOversizeError,由 monitor 统一提示用户。
|
|
94
|
+
// 不再把 maxBytes 传给核心层 saveMediaBuffer,避免重复校验产生无结构化错误。
|
|
95
|
+
if (imageBuffer.length > maxBytes) {
|
|
96
|
+
throw new MediaOversizeError({
|
|
97
|
+
kind: "image",
|
|
98
|
+
filename: originalFilename,
|
|
99
|
+
sizeBytes: imageBuffer.length,
|
|
100
|
+
maxBytes,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
69
103
|
const saved = await core.channel.media.saveMediaBuffer(imageBuffer, imageContentType, "inbound", maxBytes, originalFilename);
|
|
70
104
|
mediaList.push({ path: saved.path, contentType: saved.contentType });
|
|
71
105
|
runtime.log?.(`[wecom][plugin] Image saved: path=${saved.path}, contentType=${saved.contentType}`);
|
|
72
106
|
}
|
|
73
107
|
catch (err) {
|
|
108
|
+
// 媒体超限错误需要上抛给 monitor,用于向用户发送明确的提示文案。
|
|
109
|
+
if (err instanceof MediaOversizeError) {
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
74
112
|
runtime.error?.(`[wecom] Failed to download image: ${String(err)}`);
|
|
75
113
|
}
|
|
76
114
|
}
|
|
@@ -110,11 +148,25 @@ export async function downloadAndSaveFiles(params) {
|
|
|
110
148
|
fileBuffer = fetched.buffer;
|
|
111
149
|
fileContentType = fetched.contentType ?? "application/octet-stream";
|
|
112
150
|
}
|
|
151
|
+
// 大小校验由插件层主动进行,超限抛出 MediaOversizeError,由 monitor 统一提示用户。
|
|
152
|
+
// 不再把 maxBytes 传给核心层 saveMediaBuffer,避免重复校验产生无结构化错误。
|
|
153
|
+
if (fileBuffer.length > maxBytes) {
|
|
154
|
+
throw new MediaOversizeError({
|
|
155
|
+
kind: "file",
|
|
156
|
+
filename: originalFilename,
|
|
157
|
+
sizeBytes: fileBuffer.length,
|
|
158
|
+
maxBytes,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
113
161
|
const saved = await core.channel.media.saveMediaBuffer(fileBuffer, fileContentType, "inbound", maxBytes, originalFilename);
|
|
114
162
|
mediaList.push({ path: saved.path, contentType: saved.contentType });
|
|
115
163
|
runtime.log?.(`[wecom][plugin] File saved: path=${saved.path}, contentType=${saved.contentType}`);
|
|
116
164
|
}
|
|
117
165
|
catch (err) {
|
|
166
|
+
// 媒体超限错误需要上抛给 monitor,用于向用户发送明确的提示文案。
|
|
167
|
+
if (err instanceof MediaOversizeError) {
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
118
170
|
runtime.error?.(`[wecom] Failed to download file: ${String(err)}`);
|
|
119
171
|
}
|
|
120
172
|
}
|
|
@@ -42,6 +42,10 @@ export interface MessageBody {
|
|
|
42
42
|
url?: string;
|
|
43
43
|
aeskey?: string;
|
|
44
44
|
};
|
|
45
|
+
video?: {
|
|
46
|
+
url?: string;
|
|
47
|
+
aeskey?: string;
|
|
48
|
+
};
|
|
45
49
|
quote?: {
|
|
46
50
|
msgtype: string;
|
|
47
51
|
text?: {
|
|
@@ -58,6 +62,10 @@ export interface MessageBody {
|
|
|
58
62
|
url?: string;
|
|
59
63
|
aeskey?: string;
|
|
60
64
|
};
|
|
65
|
+
video?: {
|
|
66
|
+
url?: string;
|
|
67
|
+
aeskey?: string;
|
|
68
|
+
};
|
|
61
69
|
};
|
|
62
70
|
event?: {
|
|
63
71
|
eventtype?: string;
|
|
@@ -175,6 +175,13 @@ export function parseMessageContent(body) {
|
|
|
175
175
|
fileAesKeys.set(body.file.url, body.file.aeskey);
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
+
// 处理视频消息(沿用 file 下载/解密通路,作为文件附件透传)
|
|
179
|
+
if (body.msgtype === "video" && body.video?.url) {
|
|
180
|
+
fileUrls.push(body.video.url);
|
|
181
|
+
if (body.video.aeskey) {
|
|
182
|
+
fileAesKeys.set(body.video.url, body.video.aeskey);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
178
185
|
}
|
|
179
186
|
// 处理引用消息
|
|
180
187
|
if (body.quote) {
|
|
@@ -198,6 +205,13 @@ export function parseMessageContent(body) {
|
|
|
198
205
|
fileAesKeys.set(body.quote.file.url, body.quote.file.aeskey);
|
|
199
206
|
}
|
|
200
207
|
}
|
|
208
|
+
else if (body.quote.msgtype === "video" && body.quote.video?.url) {
|
|
209
|
+
// 引用的视频消息:沿用文件下载通路
|
|
210
|
+
fileUrls.push(body.quote.video.url);
|
|
211
|
+
if (body.quote.video.aeskey) {
|
|
212
|
+
fileAesKeys.set(body.quote.video.url, body.quote.video.aeskey);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
201
215
|
}
|
|
202
216
|
return { textParts, imageUrls, imageAesKeys, fileUrls, fileAesKeys, quoteContent };
|
|
203
217
|
}
|
package/dist/src/monitor.js
CHANGED
|
@@ -17,21 +17,31 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import * as os from "os";
|
|
19
19
|
import * as path from "path";
|
|
20
|
-
import { WSClient, generateReqId, WSAuthFailureError, WSReconnectExhaustedError } from "@wecom/aibot-node-sdk";
|
|
21
|
-
import { getWeComRuntime } from "./runtime.js";
|
|
22
|
-
import { getDefaultMediaLocalRoots, resolveStateDir } from "./openclaw-compat.js";
|
|
20
|
+
import { WSClient, generateReqId, WSAuthFailureError, WSReconnectExhaustedError, } from "@wecom/aibot-node-sdk";
|
|
23
21
|
import { CHANNEL_ID, THINKING_MESSAGE, MEDIA_IMAGE_PLACEHOLDER, MEDIA_DOCUMENT_PLACEHOLDER, WS_HEARTBEAT_INTERVAL_MS, WS_MAX_RECONNECT_ATTEMPTS, WS_MAX_AUTH_FAILURE_ATTEMPTS, EVENT_ENTER_CHECK_UPDATE, CMD_ENTER_EVENT_REPLY, SCENE_WECOM_OPENCLAW, } from "./const.js";
|
|
22
|
+
import { checkDmPolicy } from "./dm-policy.js";
|
|
23
|
+
import { processDynamicRouting } from "./dynamic-routing.js";
|
|
24
|
+
import { checkGroupPolicy } from "./group-policy.js";
|
|
25
|
+
import { downloadAndSaveImages, downloadAndSaveFiles, MediaOversizeError, } from "./media-handler.js";
|
|
26
|
+
import { uploadAndSendMedia } from "./media-uploader.js";
|
|
24
27
|
import { parseMessageContent } from "./message-parser.js";
|
|
25
28
|
import { sendWeComReply, StreamExpiredError } from "./message-sender.js";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { maskTemplateCardBlocks } from "./template-card-parser.js";
|
|
29
|
-
import { updateTemplateCardOnEvent, processTemplateCardsIfNeeded, } from "./template-card-manager.js";
|
|
30
|
-
import { checkGroupPolicy } from "./group-policy.js";
|
|
31
|
-
import { checkDmPolicy } from "./dm-policy.js";
|
|
29
|
+
import { getDefaultMediaLocalRoots, resolveStateDir } from "./openclaw-compat.js";
|
|
30
|
+
import { getWeComRuntime } from "./runtime.js";
|
|
32
31
|
import { setWeComWebSocket, setMessageState, deleteMessageState, setReqIdForChat, setSessionChatInfo, warmupReqIdStore, startMessageStateCleanup, stopMessageStateCleanup, cleanupAccount, } from "./state-manager.js";
|
|
32
|
+
import { updateTemplateCardOnEvent, processTemplateCardsIfNeeded, } from "./template-card-manager.js";
|
|
33
|
+
import { maskTemplateCardBlocks } from "./template-card-parser.js";
|
|
33
34
|
import { PLUGIN_VERSION } from "./version.js";
|
|
34
|
-
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// 附件超限提示文案
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* 构造「附件超过 OpenClaw 大小限制」的中文提示文案。
|
|
40
|
+
*/
|
|
41
|
+
function buildMediaOversizeHintText(err) {
|
|
42
|
+
const maxMb = err.maxBytes / 1024 / 1024;
|
|
43
|
+
return `当前OpenClaw限制文件不超过${maxMb}MB,请修改OpenClaw配置。`;
|
|
44
|
+
}
|
|
35
45
|
// ============================================================================
|
|
36
46
|
// 媒体本地路径白名单扩展
|
|
37
47
|
// ============================================================================
|
|
@@ -135,7 +145,12 @@ function buildMessageContext(frame, account, config, text, mediaList, quoteConte
|
|
|
135
145
|
const fromLabel = chatType === "group" ? `group:${chatId}` : `user:${body.from.userid}`;
|
|
136
146
|
// 当只有媒体没有文本时,使用占位符标识媒体类型
|
|
137
147
|
const hasImages = mediaList.some((m) => m.contentType?.startsWith("image/"));
|
|
138
|
-
const messageBody = text ||
|
|
148
|
+
const messageBody = text ||
|
|
149
|
+
(mediaList.length > 0
|
|
150
|
+
? hasImages
|
|
151
|
+
? MEDIA_IMAGE_PLACEHOLDER
|
|
152
|
+
: MEDIA_DOCUMENT_PLACEHOLDER
|
|
153
|
+
: "");
|
|
139
154
|
// 构建多媒体数组
|
|
140
155
|
const mediaPaths = mediaList.length > 0 ? mediaList.map((m) => m.path) : undefined;
|
|
141
156
|
const mediaTypes = mediaList.length > 0
|
|
@@ -272,7 +287,9 @@ async function finishThinkingStream(ctx) {
|
|
|
272
287
|
}
|
|
273
288
|
else if (state.hasMedia) {
|
|
274
289
|
if (state.hasMediaFailed && state.mediaErrorSummary) {
|
|
275
|
-
finishText = finishText
|
|
290
|
+
finishText = finishText
|
|
291
|
+
? `${finishText}\n\n${state.mediaErrorSummary}`
|
|
292
|
+
: state.mediaErrorSummary;
|
|
276
293
|
}
|
|
277
294
|
else if (!finishText) {
|
|
278
295
|
finishText = "📎 文件已发送,请查收。";
|
|
@@ -283,7 +300,14 @@ async function finishThinkingStream(ctx) {
|
|
|
283
300
|
let expired = state.streamExpired;
|
|
284
301
|
if (!expired) {
|
|
285
302
|
try {
|
|
286
|
-
await sendWeComReply({
|
|
303
|
+
await sendWeComReply({
|
|
304
|
+
wsClient,
|
|
305
|
+
frame,
|
|
306
|
+
text: finishText,
|
|
307
|
+
runtime,
|
|
308
|
+
finish: true,
|
|
309
|
+
streamId: state.streamId,
|
|
310
|
+
});
|
|
287
311
|
}
|
|
288
312
|
catch (err) {
|
|
289
313
|
if (err instanceof StreamExpiredError) {
|
|
@@ -307,7 +331,7 @@ async function finishThinkingStream(ctx) {
|
|
|
307
331
|
* 路由消息到核心处理流程并处理回复
|
|
308
332
|
*/
|
|
309
333
|
async function routeAndDispatchMessage(params) {
|
|
310
|
-
const { ctxPayload, route, storePath, chatId, chatType, config, account, wsClient, frame, state, runtime, onCleanup } = params;
|
|
334
|
+
const { ctxPayload, route, storePath, chatId, chatType, config, account, wsClient, frame, state, runtime, onCleanup, } = params;
|
|
311
335
|
const core = getWeComRuntime();
|
|
312
336
|
const ctx = { wsClient, frame, state, account, runtime };
|
|
313
337
|
// 防止 onCleanup 被多次调用(onError 回调与 catch 块可能重复触发)
|
|
@@ -351,7 +375,13 @@ async function routeAndDispatchMessage(params) {
|
|
|
351
375
|
onReplyStart: async () => {
|
|
352
376
|
if (!isShowThink && state.streamId && !state.accumulatedText) {
|
|
353
377
|
try {
|
|
354
|
-
await sendThinkingReply({
|
|
378
|
+
await sendThinkingReply({
|
|
379
|
+
wsClient,
|
|
380
|
+
frame,
|
|
381
|
+
streamId: state.streamId,
|
|
382
|
+
runtime,
|
|
383
|
+
state,
|
|
384
|
+
});
|
|
355
385
|
}
|
|
356
386
|
catch (e) {
|
|
357
387
|
runtime.error?.(`[wecom] sendThinkingReply threw err: ${String(e)}`);
|
|
@@ -363,10 +393,14 @@ async function routeAndDispatchMessage(params) {
|
|
|
363
393
|
runtime.log?.(`[openclaw -> plugin] kind=${info.kind}, payload=${JSON.stringify(payload)}, info=${JSON.stringify(info)}`);
|
|
364
394
|
// 累积文本
|
|
365
395
|
if (payload.text) {
|
|
366
|
-
state.accumulatedText += `${payload.text ||
|
|
396
|
+
state.accumulatedText += `${payload.text || ""}`;
|
|
367
397
|
}
|
|
368
398
|
// 发送媒体(统一走主动发送)
|
|
369
|
-
const mediaUrls = payload.mediaUrls?.length
|
|
399
|
+
const mediaUrls = payload.mediaUrls?.length
|
|
400
|
+
? payload.mediaUrls
|
|
401
|
+
: payload.mediaUrl
|
|
402
|
+
? [payload.mediaUrl]
|
|
403
|
+
: [];
|
|
370
404
|
if (mediaUrls.length > 0) {
|
|
371
405
|
try {
|
|
372
406
|
await sendMediaBatch(ctx, mediaUrls);
|
|
@@ -392,7 +426,14 @@ async function routeAndDispatchMessage(params) {
|
|
|
392
426
|
// if (displayText !== state.accumulatedText) {
|
|
393
427
|
// runtime.log?.(`[wecom][template-card] Mid-frame masked: original=${state.accumulatedText.length}chars, masked=${displayText.length}chars`);
|
|
394
428
|
// }
|
|
395
|
-
await sendWeComReply({
|
|
429
|
+
await sendWeComReply({
|
|
430
|
+
wsClient,
|
|
431
|
+
frame,
|
|
432
|
+
text: displayText,
|
|
433
|
+
runtime,
|
|
434
|
+
finish: false,
|
|
435
|
+
streamId: state.streamId,
|
|
436
|
+
});
|
|
396
437
|
}
|
|
397
438
|
catch (err) {
|
|
398
439
|
if (err instanceof StreamExpiredError) {
|
|
@@ -411,7 +452,13 @@ async function routeAndDispatchMessage(params) {
|
|
|
411
452
|
},
|
|
412
453
|
});
|
|
413
454
|
// 模板卡片检测与发送(在关闭 thinking 流之前独立处理)
|
|
414
|
-
const cardResult = await processTemplateCardsIfNeeded({
|
|
455
|
+
const cardResult = await processTemplateCardsIfNeeded({
|
|
456
|
+
wsClient,
|
|
457
|
+
frame,
|
|
458
|
+
state,
|
|
459
|
+
account,
|
|
460
|
+
runtime,
|
|
461
|
+
});
|
|
415
462
|
if (cardResult) {
|
|
416
463
|
// 卡片已发送,用剩余文本替换累积文本
|
|
417
464
|
state.accumulatedText = cardResult.remainingText;
|
|
@@ -424,7 +471,13 @@ async function routeAndDispatchMessage(params) {
|
|
|
424
471
|
runtime.error?.(`[wecom][plugin] Failed to process message: ${String(err)}`);
|
|
425
472
|
// 即使 dispatch 抛异常,也需要处理卡片和关闭 thinking 流
|
|
426
473
|
try {
|
|
427
|
-
const cardResult = await processTemplateCardsIfNeeded({
|
|
474
|
+
const cardResult = await processTemplateCardsIfNeeded({
|
|
475
|
+
wsClient,
|
|
476
|
+
frame,
|
|
477
|
+
state,
|
|
478
|
+
account,
|
|
479
|
+
runtime,
|
|
480
|
+
});
|
|
428
481
|
if (cardResult) {
|
|
429
482
|
state.accumulatedText = cardResult.remainingText;
|
|
430
483
|
}
|
|
@@ -492,24 +545,43 @@ async function prepareWeComMessage(params) {
|
|
|
492
545
|
return null;
|
|
493
546
|
}
|
|
494
547
|
// Step 4: 下载并保存图片和文件
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
548
|
+
let imageMediaList;
|
|
549
|
+
let fileMediaList;
|
|
550
|
+
try {
|
|
551
|
+
[imageMediaList, fileMediaList] = await Promise.all([
|
|
552
|
+
downloadAndSaveImages({
|
|
553
|
+
imageUrls,
|
|
554
|
+
imageAesKeys,
|
|
555
|
+
account,
|
|
556
|
+
config,
|
|
557
|
+
runtime,
|
|
558
|
+
wsClient,
|
|
559
|
+
}),
|
|
560
|
+
downloadAndSaveFiles({
|
|
561
|
+
fileUrls,
|
|
562
|
+
fileAesKeys,
|
|
563
|
+
account,
|
|
564
|
+
config,
|
|
565
|
+
runtime,
|
|
566
|
+
wsClient,
|
|
567
|
+
}),
|
|
568
|
+
]);
|
|
569
|
+
}
|
|
570
|
+
catch (err) {
|
|
571
|
+
if (err instanceof MediaOversizeError) {
|
|
572
|
+
// 附件超过 OpenClaw 配置的大小上限:向用户发送明确的中文提示并终止本次消息处理。
|
|
573
|
+
const hintText = buildMediaOversizeHintText(err);
|
|
574
|
+
runtime.error?.(`[wecom] Media oversize: kind=${err.kind}, size=${err.sizeBytes}, max=${err.maxBytes}, filename=${err.filename ?? "(none)"}`);
|
|
575
|
+
try {
|
|
576
|
+
await sendWeComReply({ wsClient, frame, text: hintText, runtime, finish: true });
|
|
577
|
+
}
|
|
578
|
+
catch (replyErr) {
|
|
579
|
+
runtime.error?.(`[wecom] Failed to send oversize hint: ${String(replyErr)}`);
|
|
580
|
+
}
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
throw err;
|
|
584
|
+
}
|
|
513
585
|
const mediaList = [...imageMediaList, ...fileMediaList];
|
|
514
586
|
return {
|
|
515
587
|
frame,
|
|
@@ -532,7 +604,7 @@ async function prepareWeComMessage(params) {
|
|
|
532
604
|
* 同一会话中的消息通过串行队列保证按序执行。
|
|
533
605
|
*/
|
|
534
606
|
async function processWeComMessageNow(entry) {
|
|
535
|
-
const { frame, account, config, runtime, wsClient, text, mediaList, quoteContent, messageId, chatId, reqId } = entry;
|
|
607
|
+
const { frame, account, config, runtime, wsClient, text, mediaList, quoteContent, messageId, chatId, reqId, } = entry;
|
|
536
608
|
// Step 5: 初始化消息状态
|
|
537
609
|
setReqIdForChat(chatId, reqId, account.accountId);
|
|
538
610
|
const streamId = generateReqId("stream");
|
|
@@ -547,7 +619,7 @@ async function processWeComMessageNow(entry) {
|
|
|
547
619
|
// await sendThinkingReply({ wsClient, frame, streamId, runtime });
|
|
548
620
|
// }
|
|
549
621
|
// Step 7: 构建上下文并路由到核心处理流程(带整体超时保护)
|
|
550
|
-
const { ctxPayload, route, storePath, chatId: resolvedChatId, chatType } = buildMessageContext(frame, account, config, text, mediaList, quoteContent, runtime);
|
|
622
|
+
const { ctxPayload, route, storePath, chatId: resolvedChatId, chatType, } = buildMessageContext(frame, account, config, text, mediaList, quoteContent, runtime);
|
|
551
623
|
// 以 sessionKey 为键记录「原始大小写」的 chatId 与 chatType,
|
|
552
624
|
// 供 MCP 工具工厂(index.ts:registerTool)在构造工具闭包时取回,
|
|
553
625
|
// 进而传递给需要原始 chatId 的拦截器(如 doc-auth-error 发送 biz_msg)。
|