@wecom/wecom-openclaw-plugin 2026.3.31-beta.0 → 2026.4.1-beta.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.
- package/README.md +4 -251
- package/dist/cjs/index.js +45 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/src/channel.js +339 -0
- package/dist/cjs/src/channel.js.map +1 -0
- package/dist/cjs/src/const.js +137 -0
- package/dist/cjs/src/const.js.map +1 -0
- package/dist/cjs/src/dm-policy.js +89 -0
- package/dist/cjs/src/dm-policy.js.map +1 -0
- package/dist/cjs/src/group-policy.js +133 -0
- package/dist/cjs/src/group-policy.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/biz-error.js +70 -0
- package/dist/cjs/src/mcp/interceptors/biz-error.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/index.js +73 -0
- package/dist/cjs/src/mcp/interceptors/index.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/msg-media.js +159 -0
- package/dist/cjs/src/mcp/interceptors/msg-media.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js +157 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js +93 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js.map +1 -0
- package/dist/cjs/src/mcp/schema.js +104 -0
- package/dist/cjs/src/mcp/schema.js.map +1 -0
- package/dist/cjs/src/mcp/tool.js +208 -0
- package/dist/cjs/src/mcp/tool.js.map +1 -0
- package/dist/cjs/src/mcp/transport.js +417 -0
- package/dist/cjs/src/mcp/transport.js.map +1 -0
- package/dist/cjs/src/media-handler.js +129 -0
- package/dist/cjs/src/media-handler.js.map +1 -0
- package/dist/cjs/src/media-uploader.js +284 -0
- package/dist/cjs/src/media-uploader.js.map +1 -0
- package/dist/cjs/src/message-parser.js +134 -0
- package/dist/cjs/src/message-parser.js.map +1 -0
- package/dist/cjs/src/message-sender.js +89 -0
- package/dist/cjs/src/message-sender.js.map +1 -0
- package/dist/cjs/src/monitor.js +991 -0
- package/dist/cjs/src/monitor.js.map +1 -0
- package/dist/cjs/src/onboarding.js +182 -0
- package/dist/cjs/src/onboarding.js.map +1 -0
- package/dist/cjs/src/openclaw-compat.js +400 -0
- package/dist/cjs/src/openclaw-compat.js.map +1 -0
- package/dist/cjs/src/reqid-store.js +88 -0
- package/dist/cjs/src/reqid-store.js.map +1 -0
- package/dist/cjs/src/runtime.js +16 -0
- package/dist/cjs/src/runtime.js.map +1 -0
- package/dist/cjs/src/state-dir-resolve.js +35 -0
- package/dist/cjs/src/state-dir-resolve.js.map +1 -0
- package/dist/cjs/src/state-manager.js +158 -0
- package/dist/cjs/src/state-manager.js.map +1 -0
- package/dist/cjs/src/template-card-parser.js +641 -0
- package/dist/cjs/src/template-card-parser.js.map +1 -0
- package/dist/cjs/src/timeout.js +42 -0
- package/dist/cjs/src/timeout.js.map +1 -0
- package/dist/cjs/src/utils.js +60 -0
- package/dist/cjs/src/utils.js.map +1 -0
- package/dist/cjs/src/version.js +26 -0
- package/dist/cjs/src/version.js.map +1 -0
- package/dist/esm/index.js +41 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/src/channel.js +337 -0
- package/dist/esm/src/channel.js.map +1 -0
- package/dist/{src → esm/src}/const.js +30 -77
- package/dist/esm/src/const.js.map +1 -0
- package/dist/{src → esm/src}/dm-policy.js +9 -6
- package/dist/esm/src/dm-policy.js.map +1 -0
- package/dist/{src → esm/src}/group-policy.js +7 -3
- package/dist/esm/src/group-policy.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/biz-error.js +6 -2
- package/dist/esm/src/mcp/interceptors/biz-error.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/index.js +10 -6
- package/dist/esm/src/mcp/interceptors/index.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/msg-media.js +10 -6
- package/dist/esm/src/mcp/interceptors/msg-media.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/smartpage-create.js +6 -2
- package/dist/esm/src/mcp/interceptors/smartpage-create.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/smartpage-export.js +6 -2
- package/dist/esm/src/mcp/interceptors/smartpage-export.js.map +1 -0
- package/dist/{src → esm/src}/mcp/schema.js +4 -1
- package/dist/esm/src/mcp/schema.js.map +1 -0
- package/dist/{src → esm/src}/mcp/tool.js +8 -4
- package/dist/esm/src/mcp/tool.js.map +1 -0
- package/dist/{src → esm/src}/mcp/transport.js +16 -15
- package/dist/esm/src/mcp/transport.js.map +1 -0
- package/dist/{src → esm/src}/media-handler.js +10 -6
- package/dist/esm/src/media-handler.js.map +1 -0
- package/dist/{src → esm/src}/media-uploader.js +10 -64
- package/dist/esm/src/media-uploader.js.map +1 -0
- package/dist/{src → esm/src}/message-parser.js +4 -1
- package/dist/esm/src/message-parser.js.map +1 -0
- package/dist/{src → esm/src}/message-sender.js +11 -46
- package/dist/esm/src/message-sender.js.map +1 -0
- package/dist/{src → esm/src}/monitor.js +282 -151
- package/dist/esm/src/monitor.js.map +1 -0
- package/dist/{src → esm/src}/onboarding.js +26 -22
- package/dist/esm/src/onboarding.js.map +1 -0
- package/dist/{src → esm/src}/openclaw-compat.js +27 -143
- package/dist/esm/src/openclaw-compat.js.map +1 -0
- package/dist/{src → esm/src}/reqid-store.js +7 -4
- package/dist/esm/src/reqid-store.js.map +1 -0
- package/dist/{src → esm/src}/runtime.js +5 -2
- package/dist/esm/src/runtime.js.map +1 -0
- package/dist/esm/src/state-dir-resolve.js +13 -0
- package/dist/esm/src/state-dir-resolve.js.map +1 -0
- package/dist/{src → esm/src}/state-manager.js +16 -84
- package/dist/esm/src/state-manager.js.map +1 -0
- package/dist/{src → esm/src}/template-card-parser.js +8 -17
- package/dist/esm/src/template-card-parser.js.map +1 -0
- package/dist/{src → esm/src}/timeout.js +5 -2
- package/dist/esm/src/timeout.js.map +1 -0
- package/dist/{src → esm/src}/utils.js +10 -24
- package/dist/esm/src/utils.js.map +1 -0
- package/dist/esm/src/version.js +23 -0
- package/dist/esm/src/version.js.map +1 -0
- package/dist/esm/types/index.d.ts +9 -0
- package/dist/{src → esm/types/src}/const.d.ts +0 -46
- package/dist/{src → esm/types/src}/interface.d.ts +2 -0
- package/dist/{src → esm/types/src}/message-sender.d.ts +0 -18
- package/dist/{src → esm/types/src}/openclaw-compat.d.ts +0 -49
- package/dist/esm/types/src/state-dir-resolve.d.ts +2 -0
- package/dist/{src → esm/types/src}/utils.d.ts +0 -39
- package/dist/esm/types/src/version.d.ts +2 -0
- package/dist/index.d.ts +4 -2
- package/package.json +7 -15
- package/dist/index.js +0 -113
- package/dist/src/accounts.d.ts +0 -57
- package/dist/src/accounts.js +0 -222
- package/dist/src/agent/api-client.d.ts +0 -94
- package/dist/src/agent/api-client.js +0 -292
- package/dist/src/agent/handler.d.ts +0 -63
- package/dist/src/agent/handler.js +0 -532
- package/dist/src/agent/index.d.ts +0 -5
- package/dist/src/agent/index.js +0 -5
- package/dist/src/agent/webhook.d.ts +0 -23
- package/dist/src/agent/webhook.js +0 -226
- package/dist/src/agent/xml.d.ts +0 -21
- package/dist/src/agent/xml.js +0 -41
- package/dist/src/channel.js +0 -564
- package/dist/src/chat-queue.d.ts +0 -31
- package/dist/src/chat-queue.js +0 -45
- package/dist/src/http.d.ts +0 -26
- package/dist/src/http.js +0 -93
- package/dist/src/interface.js +0 -4
- package/dist/src/mcp/index.js +0 -6
- package/dist/src/mcp/interceptors/types.js +0 -7
- package/dist/src/shared/command-auth.d.ts +0 -23
- package/dist/src/shared/command-auth.js +0 -73
- package/dist/src/shared/xml-parser.d.ts +0 -46
- package/dist/src/shared/xml-parser.js +0 -210
- package/dist/src/target.d.ts +0 -35
- package/dist/src/target.js +0 -67
- package/dist/src/template-card-manager.d.ts +0 -55
- package/dist/src/template-card-manager.js +0 -205
- package/dist/src/types/account.d.ts +0 -29
- package/dist/src/types/account.js +0 -4
- package/dist/src/types/config.d.ts +0 -80
- package/dist/src/types/config.js +0 -7
- package/dist/src/types/constants.d.ts +0 -42
- package/dist/src/types/constants.js +0 -42
- package/dist/src/types/index.d.ts +0 -7
- package/dist/src/types/index.js +0 -5
- package/dist/src/types/message.d.ts +0 -238
- package/dist/src/types/message.js +0 -5
- package/dist/src/version.d.ts +0 -2
- package/dist/src/version.js +0 -24
- package/dist/src/webhook/command-auth.d.ts +0 -47
- package/dist/src/webhook/command-auth.js +0 -96
- package/dist/src/webhook/gateway.d.ts +0 -36
- package/dist/src/webhook/gateway.js +0 -227
- package/dist/src/webhook/handler.d.ts +0 -18
- package/dist/src/webhook/handler.js +0 -420
- package/dist/src/webhook/helpers.d.ts +0 -156
- package/dist/src/webhook/helpers.js +0 -833
- package/dist/src/webhook/http.d.ts +0 -26
- package/dist/src/webhook/http.js +0 -94
- package/dist/src/webhook/index.d.ts +0 -11
- package/dist/src/webhook/index.js +0 -14
- package/dist/src/webhook/media.d.ts +0 -29
- package/dist/src/webhook/media.js +0 -88
- package/dist/src/webhook/monitor.d.ts +0 -59
- package/dist/src/webhook/monitor.js +0 -1147
- package/dist/src/webhook/state.d.ts +0 -220
- package/dist/src/webhook/state.js +0 -491
- package/dist/src/webhook/target.d.ts +0 -41
- package/dist/src/webhook/target.js +0 -132
- package/dist/src/webhook/types.d.ts +0 -347
- package/dist/src/webhook/types.js +0 -33
- /package/dist/{src → esm/types/src}/channel.d.ts +0 -0
- /package/dist/{src → esm/types/src}/dm-policy.d.ts +0 -0
- /package/dist/{src → esm/types/src}/group-policy.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/index.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/biz-error.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/index.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/msg-media.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/smartpage-create.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/smartpage-export.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/types.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/schema.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/tool.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/transport.d.ts +0 -0
- /package/dist/{src → esm/types/src}/media-handler.d.ts +0 -0
- /package/dist/{src → esm/types/src}/media-uploader.d.ts +0 -0
- /package/dist/{src → esm/types/src}/message-parser.d.ts +0 -0
- /package/dist/{src → esm/types/src}/monitor.d.ts +0 -0
- /package/dist/{src → esm/types/src}/onboarding.d.ts +0 -0
- /package/dist/{src → esm/types/src}/reqid-store.d.ts +0 -0
- /package/dist/{src → esm/types/src}/runtime.d.ts +0 -0
- /package/dist/{src → esm/types/src}/state-manager.d.ts +0 -0
- /package/dist/{src → esm/types/src}/template-card-parser.d.ts +0 -0
- /package/dist/{src → esm/types/src}/timeout.d.ts +0 -0
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { WSClient, WSAuthFailureError, WSReconnectExhaustedError, generateReqId } from '@wecom/aibot-node-sdk';
|
|
4
|
+
import { getWeComRuntime } from './runtime.js';
|
|
5
|
+
import { getDefaultMediaLocalRoots } from './openclaw-compat.js';
|
|
6
|
+
import { SCENE_WECOM_OPENCLAW, WS_MAX_AUTH_FAILURE_ATTEMPTS, WS_MAX_RECONNECT_ATTEMPTS, WS_HEARTBEAT_INTERVAL_MS, EVENT_ENTER_CHECK_UPDATE, CMD_ENTER_EVENT_REPLY, CHANNEL_ID, MEDIA_IMAGE_PLACEHOLDER, MEDIA_DOCUMENT_PLACEHOLDER, THINKING_MESSAGE } from './const.js';
|
|
7
|
+
export { WeComCommand } from './const.js';
|
|
8
|
+
import { parseMessageContent } from './message-parser.js';
|
|
9
|
+
import { sendWeComReply, StreamExpiredError } from './message-sender.js';
|
|
10
|
+
import { downloadAndSaveImages, downloadAndSaveFiles } from './media-handler.js';
|
|
11
|
+
import { uploadAndSendMedia } from './media-uploader.js';
|
|
12
|
+
import { maskTemplateCardBlocks, extractTemplateCards } from './template-card-parser.js';
|
|
13
|
+
import { checkGroupPolicy } from './group-policy.js';
|
|
14
|
+
import { checkDmPolicy } from './dm-policy.js';
|
|
15
|
+
import { startMessageStateCleanup, setWeComWebSocket, warmupReqIdStore, stopMessageStateCleanup, cleanupAccount, setReqIdForChat, setMessageState, deleteMessageState } from './state-manager.js';
|
|
16
|
+
export { getWeComWebSocket } from './state-manager.js';
|
|
17
|
+
import { PLUGIN_VERSION } from './version.js';
|
|
18
|
+
|
|
1
19
|
/**
|
|
2
20
|
* 企业微信 WebSocket 监控器主模块
|
|
3
21
|
*
|
|
@@ -15,22 +33,6 @@
|
|
|
15
33
|
* - state-manager.ts : 全局状态管理(带 TTL 清理)
|
|
16
34
|
* - timeout.ts : 超时工具
|
|
17
35
|
*/
|
|
18
|
-
import * as os from "os";
|
|
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";
|
|
23
|
-
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";
|
|
24
|
-
import { parseMessageContent } from "./message-parser.js";
|
|
25
|
-
import { sendWeComReply, sendWeComReplyNonBlocking, StreamExpiredError } from "./message-sender.js";
|
|
26
|
-
import { downloadAndSaveImages, downloadAndSaveFiles } from "./media-handler.js";
|
|
27
|
-
import { uploadAndSendMedia } from "./media-uploader.js";
|
|
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";
|
|
32
|
-
import { setWeComWebSocket, setMessageState, deleteMessageState, setReqIdForChat, warmupReqIdStore, startMessageStateCleanup, stopMessageStateCleanup, cleanupAccount, } from "./state-manager.js";
|
|
33
|
-
import { PLUGIN_VERSION } from "./version.js";
|
|
34
36
|
/**
|
|
35
37
|
* 去除文本中的 `<think>...</think>` 标签(支持跨行),返回剩余可见文本。
|
|
36
38
|
* 用于判断大模型回复中是否包含实际用户可见内容(而非仅有 thinking 推理过程)。
|
|
@@ -39,9 +41,148 @@ function stripThinkTags(text) {
|
|
|
39
41
|
return text;
|
|
40
42
|
// return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
41
43
|
}
|
|
44
|
+
const sentTemplateCardByTaskId = new Map();
|
|
45
|
+
const TEMPLATE_CARD_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
46
|
+
const TEMPLATE_CARD_CACHE_MAX_SIZE = 300;
|
|
47
|
+
function getTemplateCardCacheKey(accountId, taskId) {
|
|
48
|
+
return `${accountId}:${taskId}`;
|
|
49
|
+
}
|
|
50
|
+
function pruneTemplateCardCache() {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
for (const [key, entry] of sentTemplateCardByTaskId) {
|
|
53
|
+
if (now - entry.createdAt >= TEMPLATE_CARD_CACHE_TTL_MS) {
|
|
54
|
+
sentTemplateCardByTaskId.delete(key);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (sentTemplateCardByTaskId.size <= TEMPLATE_CARD_CACHE_MAX_SIZE) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const sortedEntries = [...sentTemplateCardByTaskId.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
61
|
+
const removeCount = sentTemplateCardByTaskId.size - TEMPLATE_CARD_CACHE_MAX_SIZE;
|
|
62
|
+
for (const [key] of sortedEntries.slice(0, removeCount)) {
|
|
63
|
+
sentTemplateCardByTaskId.delete(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function cloneTemplateCard(card) {
|
|
67
|
+
return JSON.parse(JSON.stringify(card));
|
|
68
|
+
}
|
|
69
|
+
function saveTemplateCardToCache(params) {
|
|
70
|
+
const { accountId, templateCard, runtime } = params;
|
|
71
|
+
const taskId = templateCard.task_id;
|
|
72
|
+
if (!taskId) {
|
|
73
|
+
runtime.log?.("[wecom][template-card] Skip cache: template card has no task_id");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
sentTemplateCardByTaskId.set(getTemplateCardCacheKey(accountId, taskId), {
|
|
77
|
+
templateCard: cloneTemplateCard(templateCard),
|
|
78
|
+
createdAt: Date.now(),
|
|
79
|
+
});
|
|
80
|
+
pruneTemplateCardCache();
|
|
81
|
+
}
|
|
82
|
+
function getTemplateCardFromCache(accountId, taskId) {
|
|
83
|
+
pruneTemplateCardCache();
|
|
84
|
+
const cached = sentTemplateCardByTaskId.get(getTemplateCardCacheKey(accountId, taskId));
|
|
85
|
+
if (!cached) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return cloneTemplateCard(cached.templateCard);
|
|
89
|
+
}
|
|
90
|
+
function buildSelectedOptionMap(templateCardEvent) {
|
|
91
|
+
const selectedMap = new Map();
|
|
92
|
+
const selectedItems = templateCardEvent?.selected_items?.selected_item ?? [];
|
|
93
|
+
for (const item of selectedItems) {
|
|
94
|
+
const questionKey = item.question_key?.trim();
|
|
95
|
+
if (!questionKey) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const optionIds = item.option_ids?.option_id?.filter(Boolean) ?? [];
|
|
99
|
+
selectedMap.set(questionKey, optionIds);
|
|
100
|
+
}
|
|
101
|
+
return selectedMap;
|
|
102
|
+
}
|
|
103
|
+
function applySelectedStateToTemplateCard(params) {
|
|
104
|
+
const { templateCard, selectedMap, templateCardEvent } = params;
|
|
105
|
+
const nextCard = cloneTemplateCard(templateCard);
|
|
106
|
+
if (templateCardEvent?.task_id) {
|
|
107
|
+
nextCard.task_id = templateCardEvent.task_id;
|
|
108
|
+
}
|
|
109
|
+
if (templateCardEvent?.card_type) {
|
|
110
|
+
nextCard.card_type = templateCardEvent.card_type;
|
|
111
|
+
}
|
|
112
|
+
// 交互完成后将提交按钮文案更新为已提交,提升用户感知
|
|
113
|
+
if (nextCard.submit_button?.text) {
|
|
114
|
+
nextCard.submit_button.text = "已提交";
|
|
115
|
+
}
|
|
116
|
+
if (nextCard.checkbox?.question_key) {
|
|
117
|
+
const selectedIds = selectedMap.get(nextCard.checkbox.question_key) ?? [];
|
|
118
|
+
nextCard.checkbox.disable = true;
|
|
119
|
+
if (Array.isArray(nextCard.checkbox.option_list)) {
|
|
120
|
+
nextCard.checkbox.option_list = nextCard.checkbox.option_list.map((option) => ({
|
|
121
|
+
...option,
|
|
122
|
+
is_checked: selectedIds.includes(option.id),
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(nextCard.select_list)) {
|
|
127
|
+
nextCard.select_list = nextCard.select_list.map((selection) => {
|
|
128
|
+
const selectedIds = selectedMap.get(selection.question_key) ?? [];
|
|
129
|
+
return {
|
|
130
|
+
...selection,
|
|
131
|
+
disable: true,
|
|
132
|
+
selected_id: selectedIds[0] ?? selection.selected_id,
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (nextCard.button_selection?.question_key) {
|
|
137
|
+
const selectedIds = selectedMap.get(nextCard.button_selection.question_key) ?? [];
|
|
138
|
+
nextCard.button_selection.disable = true;
|
|
139
|
+
if (selectedIds[0]) {
|
|
140
|
+
nextCard.button_selection.selected_id = selectedIds[0];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return nextCard;
|
|
144
|
+
}
|
|
145
|
+
async function updateTemplateCardOnEvent(params) {
|
|
146
|
+
const { frame, accountId, runtime, wsClient } = params;
|
|
147
|
+
const body = frame.body;
|
|
148
|
+
const templateCardEvent = body.event?.template_card_event;
|
|
149
|
+
const taskId = templateCardEvent?.task_id;
|
|
150
|
+
if (!taskId) {
|
|
151
|
+
runtime.log?.(`[${accountId}] [template-card-update] Skip update: missing task_id in callback`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const cachedCard = getTemplateCardFromCache(accountId, taskId);
|
|
155
|
+
if (!cachedCard) {
|
|
156
|
+
runtime.log?.(`[${accountId}] [template-card-update] Skip update: task_id=${taskId} not found in cache`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const selectedMap = buildSelectedOptionMap(templateCardEvent);
|
|
160
|
+
const updatedCard = applySelectedStateToTemplateCard({
|
|
161
|
+
templateCard: cachedCard,
|
|
162
|
+
selectedMap,
|
|
163
|
+
templateCardEvent,
|
|
164
|
+
});
|
|
165
|
+
await wsClient.updateTemplateCard(frame, updatedCard, [body.from.userid]);
|
|
166
|
+
runtime.log?.(`[${accountId}] [template-card-update] Updated card by task_id=${taskId}`);
|
|
167
|
+
// 将更新后的卡片写回缓存,后续多次点击时状态保持一致
|
|
168
|
+
saveTemplateCardToCache({
|
|
169
|
+
accountId,
|
|
170
|
+
templateCard: updatedCard,
|
|
171
|
+
runtime,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
42
174
|
// ============================================================================
|
|
43
175
|
// 媒体本地路径白名单扩展
|
|
44
176
|
// ============================================================================
|
|
177
|
+
/**
|
|
178
|
+
* 解析 openclaw 状态目录(与 plugin-sdk 内部逻辑保持一致)
|
|
179
|
+
*/
|
|
180
|
+
function resolveStateDir() {
|
|
181
|
+
const stateOverride = process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim();
|
|
182
|
+
if (stateOverride)
|
|
183
|
+
return stateOverride;
|
|
184
|
+
return path.join(os.homedir(), ".openclaw");
|
|
185
|
+
}
|
|
45
186
|
/**
|
|
46
187
|
* 在 getDefaultMediaLocalRoots() 基础上,将 stateDir 本身也加入白名单,
|
|
47
188
|
* 并合并用户在 WeComConfig 中配置的自定义 mediaLocalRoots。
|
|
@@ -95,9 +236,6 @@ function buildMediaErrorSummary(mediaUrl, result) {
|
|
|
95
236
|
}
|
|
96
237
|
return `⚠️ 文件发送失败:无法处理文件 ${mediaUrl},请稍后再试。`;
|
|
97
238
|
}
|
|
98
|
-
export { WeComCommand } from "./const.js";
|
|
99
|
-
export { getWeComWebSocket, setReqIdForChat, getReqIdForChatAsync, getReqIdForChat, deleteReqIdForChat, warmupReqIdStore, flushReqIdStore, } from "./state-manager.js";
|
|
100
|
-
export { sendWeComReply } from "./message-sender.js";
|
|
101
239
|
// ============================================================================
|
|
102
240
|
// 消息上下文构建
|
|
103
241
|
// ============================================================================
|
|
@@ -129,12 +267,8 @@ function buildMessageContext(frame, account, config, text, mediaList, quoteConte
|
|
|
129
267
|
const mediaTypes = mediaList.length > 0
|
|
130
268
|
? mediaList.map((m) => m.contentType).filter(Boolean)
|
|
131
269
|
: undefined;
|
|
132
|
-
// 使用 route.agentId 解析 storePath(多 agent 场景下 session 路径隔离)
|
|
133
|
-
const storePath = core.channel.session.resolveStorePath(config.session?.store, {
|
|
134
|
-
agentId: route.agentId,
|
|
135
|
-
});
|
|
136
270
|
// 构建标准消息上下文
|
|
137
|
-
|
|
271
|
+
return core.channel.reply.finalizeInboundContext({
|
|
138
272
|
Body: messageBody,
|
|
139
273
|
RawBody: messageBody,
|
|
140
274
|
CommandBody: messageBody,
|
|
@@ -143,7 +277,7 @@ function buildMessageContext(frame, account, config, text, mediaList, quoteConte
|
|
|
143
277
|
To: `${CHANNEL_ID}:${chatId}`,
|
|
144
278
|
SenderId: body.from.userid,
|
|
145
279
|
SessionKey: route.sessionKey,
|
|
146
|
-
AccountId:
|
|
280
|
+
AccountId: account.accountId,
|
|
147
281
|
ChatType: chatType,
|
|
148
282
|
ConversationLabel: fromLabel,
|
|
149
283
|
Timestamp: Date.now(),
|
|
@@ -162,7 +296,6 @@ function buildMessageContext(frame, account, config, text, mediaList, quoteConte
|
|
|
162
296
|
MediaUrls: mediaPaths,
|
|
163
297
|
ReplyToBody: quoteContent,
|
|
164
298
|
});
|
|
165
|
-
return { ctxPayload, route, storePath, chatId, chatType };
|
|
166
299
|
}
|
|
167
300
|
/**
|
|
168
301
|
* 发送"思考中"消息
|
|
@@ -234,38 +367,68 @@ async function sendMediaBatch(ctx, mediaUrls) {
|
|
|
234
367
|
* 替换掉 thinking 动画,否则 thinking 会一直残留。
|
|
235
368
|
*
|
|
236
369
|
* 关闭策略(按优先级):
|
|
370
|
+
* 0. [新增] 有模板卡片代码块 → 提取卡片并主动发送,用剩余文本关闭流
|
|
237
371
|
* 1. 有可见文本 → 用完整文本关闭
|
|
238
|
-
* 2.
|
|
239
|
-
* 3.
|
|
240
|
-
* 4.
|
|
372
|
+
* 2. 有媒体成功发送(通过 deliver 回调) → 用友好提示"文件已发送"
|
|
373
|
+
* 3. 媒体发送失败 → 直接用错误摘要替换 thinking
|
|
374
|
+
* 4. 其他 → 用通用"处理完成"提示
|
|
375
|
+
* (agent 可能已通过内置 message 工具直接发送了文件,
|
|
376
|
+
* 该路径走 outbound.sendMedia 完全绕过 deliver 回调,
|
|
377
|
+
* 所以 state 中无记录,但文件已实际送达)
|
|
241
378
|
*
|
|
242
379
|
* 降级策略:
|
|
243
380
|
* - 当 streamExpired=true(errcode 846608)时,流式通道已不可用(>6分钟),
|
|
244
381
|
* 改用 wsClient.sendMessage 主动发送完整文本。
|
|
245
|
-
*
|
|
246
|
-
* 注意:模板卡片的检测和发送已在 finishThinkingStream 之前由
|
|
247
|
-
* processTemplateCardsIfNeeded 完成,此处只关心最后的消息发送。
|
|
248
382
|
*/
|
|
249
383
|
async function finishThinkingStream(ctx) {
|
|
250
384
|
const { wsClient, frame, state, runtime } = ctx;
|
|
251
385
|
const body = frame.body;
|
|
252
386
|
const chatId = body.chatid || body.from.userid;
|
|
253
387
|
const visibleText = stripThinkTags(state.accumulatedText);
|
|
388
|
+
// ── 模板卡片检测与发送 ──────────────────────────────────────────────
|
|
389
|
+
// 在确定 finishText 之前,先检查累积文本中是否包含模板卡片 JSON 代码块。
|
|
390
|
+
// 若检测到合法卡片,通过 sendMessage 主动发送后,用剩余文本关闭流。
|
|
391
|
+
if (visibleText) {
|
|
392
|
+
runtime.log?.(`[wecom][template-card] finishThinkingStream: visibleText exists, length=${visibleText.length}, running extractTemplateCards...`);
|
|
393
|
+
const logFn = (...args) => {
|
|
394
|
+
runtime.log?.(...args);
|
|
395
|
+
};
|
|
396
|
+
const { cards, remainingText } = extractTemplateCards(state.accumulatedText, logFn);
|
|
397
|
+
runtime.log?.(`[wecom][template-card] finishThinkingStream: extractTemplateCards result — cards=${cards.length}, remainingTextLength=${remainingText.length}`);
|
|
398
|
+
if (cards.length > 0) {
|
|
399
|
+
runtime.log?.(`[wecom][template-card] finishThinkingStream: ${cards.length} card(s) detected, card_types=[${cards.map(c => c.cardType).join(", ")}]`);
|
|
400
|
+
await sendTemplateCards(ctx, cards);
|
|
401
|
+
// 用剩余文本关闭流(可能为空)
|
|
402
|
+
const trimmedRemaining = stripThinkTags(remainingText);
|
|
403
|
+
const finishText = trimmedRemaining
|
|
404
|
+
? remainingText
|
|
405
|
+
: (state.hasTemplateCard ? "📋 卡片消息已发送。" : "");
|
|
406
|
+
runtime.log?.(`[wecom][template-card] finishThinkingStream: closing stream with finishText="${finishText.slice(0, 100)}...", hasTemplateCard=${state.hasTemplateCard}`);
|
|
407
|
+
await sendWeComReply({ wsClient, frame, text: finishText, runtime, finish: true, streamId: state.streamId });
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
runtime.log?.(`[wecom][template-card] finishThinkingStream: no visibleText, skipping template card extraction`);
|
|
413
|
+
}
|
|
414
|
+
// ── 模板卡片检测结束 ────────────────────────────────────────────────
|
|
254
415
|
let finishText = state.accumulatedText;
|
|
255
416
|
if (visibleText) {
|
|
417
|
+
// 有可见文本:用完整文本关闭流(覆盖 thinking 为真实内容)
|
|
256
418
|
finishText = state.accumulatedText;
|
|
257
419
|
}
|
|
258
|
-
else if (state.hasTemplateCard) {
|
|
259
|
-
finishText = "📋 卡片消息已发送。";
|
|
260
|
-
}
|
|
261
420
|
else if (state.hasMedia) {
|
|
262
421
|
if (state.hasMediaFailed && state.mediaErrorSummary) {
|
|
422
|
+
// 媒体成功发送:用友好提示告知用户
|
|
263
423
|
finishText = finishText ? `${finishText}\n\n${state.mediaErrorSummary}` : state.mediaErrorSummary;
|
|
264
424
|
}
|
|
265
425
|
else if (!finishText) {
|
|
266
426
|
finishText = "📎 文件已发送,请查收。";
|
|
267
427
|
}
|
|
268
428
|
}
|
|
429
|
+
// if (!finishText) {
|
|
430
|
+
// finishText = "✅ 处理完成。";
|
|
431
|
+
// }
|
|
269
432
|
if (finishText) {
|
|
270
433
|
// 尝试流式发送;若已知过期或发送时发现过期,统一降级为主动发送
|
|
271
434
|
let expired = state.streamExpired;
|
|
@@ -291,11 +454,46 @@ async function finishThinkingStream(ctx) {
|
|
|
291
454
|
}
|
|
292
455
|
}
|
|
293
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* 逐个发送已提取的模板卡片(通过 wsClient.sendMessage 主动推送)
|
|
459
|
+
*
|
|
460
|
+
* 发送失败不阻塞流程,仅记录错误日志。
|
|
461
|
+
*/
|
|
462
|
+
async function sendTemplateCards(ctx, cards) {
|
|
463
|
+
const { wsClient, frame, state, runtime, account } = ctx;
|
|
464
|
+
const body = frame.body;
|
|
465
|
+
const chatId = body.chatid || body.from.userid;
|
|
466
|
+
for (const card of cards) {
|
|
467
|
+
try {
|
|
468
|
+
runtime.log?.(`[wecom][template-card] Sending card_type=${card.cardType} to chatId=${chatId}`);
|
|
469
|
+
const rawTemplateCard = card.cardJson;
|
|
470
|
+
if (typeof rawTemplateCard.card_type !== "string") {
|
|
471
|
+
runtime.error?.("[wecom][template-card] Skip sending invalid card: missing card_type");
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const templateCard = rawTemplateCard;
|
|
475
|
+
await wsClient.sendMessage(chatId, {
|
|
476
|
+
msgtype: "template_card",
|
|
477
|
+
template_card: templateCard,
|
|
478
|
+
});
|
|
479
|
+
state.hasTemplateCard = true;
|
|
480
|
+
saveTemplateCardToCache({
|
|
481
|
+
accountId: account.accountId,
|
|
482
|
+
templateCard,
|
|
483
|
+
runtime,
|
|
484
|
+
});
|
|
485
|
+
runtime.log?.(`[wecom][template-card] Card sent successfully: card_type=${card.cardType}`);
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
runtime.error?.(`[wecom][template-card] Failed to send card: card_type=${card.cardType}, error=${JSON.stringify(err)}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
294
492
|
/**
|
|
295
493
|
* 路由消息到核心处理流程并处理回复
|
|
296
494
|
*/
|
|
297
495
|
async function routeAndDispatchMessage(params) {
|
|
298
|
-
const { ctxPayload,
|
|
496
|
+
const { ctxPayload, config, account, wsClient, frame, state, runtime, onCleanup } = params;
|
|
299
497
|
const core = getWeComRuntime();
|
|
300
498
|
const ctx = { wsClient, frame, state, account, runtime };
|
|
301
499
|
// 防止 onCleanup 被多次调用(onError 回调与 catch 块可能重复触发)
|
|
@@ -308,23 +506,6 @@ async function routeAndDispatchMessage(params) {
|
|
|
308
506
|
};
|
|
309
507
|
let isShowThink = !(account.sendThinkingMessage ?? true);
|
|
310
508
|
try {
|
|
311
|
-
// 记录 inbound session 元数据(session 追踪)
|
|
312
|
-
await core.channel.session.recordInboundSession({
|
|
313
|
-
storePath,
|
|
314
|
-
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
|
315
|
-
ctx: ctxPayload,
|
|
316
|
-
updateLastRoute: chatType !== "group"
|
|
317
|
-
? {
|
|
318
|
-
sessionKey: route.mainSessionKey,
|
|
319
|
-
channel: CHANNEL_ID,
|
|
320
|
-
to: `${CHANNEL_ID}:${chatId}`,
|
|
321
|
-
accountId: route.accountId,
|
|
322
|
-
}
|
|
323
|
-
: undefined,
|
|
324
|
-
onRecordError: (err) => {
|
|
325
|
-
runtime.error?.(`[wecom] failed updating session meta: ${String(err)}`);
|
|
326
|
-
},
|
|
327
|
-
});
|
|
328
509
|
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
329
510
|
ctx: ctxPayload,
|
|
330
511
|
cfg: config,
|
|
@@ -355,10 +536,11 @@ async function routeAndDispatchMessage(params) {
|
|
|
355
536
|
}
|
|
356
537
|
},
|
|
357
538
|
deliver: async (payload, info) => {
|
|
539
|
+
state.deliverCalled = true;
|
|
358
540
|
// runtime.log?.(`[openclaw -> plugin] kind=${info.kind}, text=${payload.text ?? ''}, mediaUrl=${payload.mediaUrl ?? ''}, mediaUrls=${JSON.stringify(payload.mediaUrls ?? [])}`);
|
|
359
|
-
//
|
|
541
|
+
// 累积文本
|
|
360
542
|
if (payload.text) {
|
|
361
|
-
state.accumulatedText
|
|
543
|
+
state.accumulatedText += (payload.text || '');
|
|
362
544
|
}
|
|
363
545
|
// 发送媒体(统一走主动发送)
|
|
364
546
|
const mediaUrls = payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
|
|
@@ -378,62 +560,42 @@ async function routeAndDispatchMessage(params) {
|
|
|
378
560
|
runtime.error?.(`[wecom] sendMediaBatch threw: ${errMsg}`);
|
|
379
561
|
}
|
|
380
562
|
}
|
|
381
|
-
//
|
|
382
|
-
// deliver 只负责文本累积和媒体发送
|
|
383
|
-
},
|
|
384
|
-
onError: (err, info) => {
|
|
385
|
-
runtime.error?.(`[wecom] ${info.kind} reply failed: ${String(err)}`);
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
// onPartialReply:token 级别的流式 UI 刷新
|
|
389
|
-
// 使用 SDK 的 replyStreamNonBlocking:上一条同 reqId 的消息未收到 ack 时跳过本次发送,
|
|
390
|
-
// 避免流式中间帧排队积压导致延迟。丢帧在流式场景下可接受(text 是累积文本,最终由 finishThinkingStream 兜底)。
|
|
391
|
-
replyOptions: {
|
|
392
|
-
onPartialReply: (data) => {
|
|
393
|
-
// 累积文本
|
|
394
|
-
if (data.text) {
|
|
395
|
-
state.accumulatedText = (data.text || '');
|
|
396
|
-
}
|
|
397
|
-
if (!state.streamId || !data.text || state.streamExpired)
|
|
398
|
-
return;
|
|
563
|
+
// 中间帧:有可见文本时流式更新(流式过期后跳过,等 deliver 完成后主动发送)
|
|
399
564
|
// 使用 maskTemplateCardBlocks 遮罩正在构建中的模板卡片代码块,
|
|
400
565
|
// 避免 JSON 源码在流式输出过程中暴露给终端用户
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
streamId: state.streamId,
|
|
409
|
-
}).catch((err) => {
|
|
410
|
-
if (err instanceof StreamExpiredError) {
|
|
411
|
-
state.streamExpired = true;
|
|
412
|
-
runtime.log?.(`[wecom] Stream expired during partial reply, will fallback to proactive send`);
|
|
566
|
+
if (info.kind !== "final" && state.accumulatedText && !state.streamExpired) {
|
|
567
|
+
try {
|
|
568
|
+
const displayText = maskTemplateCardBlocks(state.accumulatedText, (...args) => runtime.log?.(...args));
|
|
569
|
+
if (displayText !== state.accumulatedText) {
|
|
570
|
+
runtime.log?.(`[wecom][template-card] Mid-frame masked: original=${state.accumulatedText.length}chars, masked=${displayText.length}chars`);
|
|
571
|
+
}
|
|
572
|
+
await sendWeComReply({ wsClient, frame, text: displayText, runtime, finish: false, streamId: state.streamId });
|
|
413
573
|
}
|
|
414
|
-
|
|
415
|
-
|
|
574
|
+
catch (err) {
|
|
575
|
+
if (err instanceof StreamExpiredError) {
|
|
576
|
+
state.streamExpired = true;
|
|
577
|
+
runtime.log?.(`[wecom] Stream expired during intermediate reply, will fallback to proactive send`);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
throw err;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
onError: (err, info) => {
|
|
586
|
+
runtime.error?.(`[wecom] ${info.kind} reply failed: ${String(err)}`);
|
|
416
587
|
},
|
|
417
588
|
},
|
|
418
589
|
});
|
|
419
|
-
// 模板卡片检测与发送(在关闭 thinking 流之前独立处理)
|
|
420
|
-
const cardResult = await processTemplateCardsIfNeeded({ wsClient, frame, state, account, runtime });
|
|
421
|
-
if (cardResult) {
|
|
422
|
-
// 卡片已发送,用剩余文本替换累积文本
|
|
423
|
-
state.accumulatedText = cardResult.remainingText;
|
|
424
|
-
}
|
|
425
590
|
// 关闭 thinking 流
|
|
426
591
|
await finishThinkingStream(ctx);
|
|
427
592
|
safeCleanup();
|
|
428
593
|
}
|
|
429
594
|
catch (err) {
|
|
430
595
|
runtime.error?.(`[wecom][plugin] Failed to process message: ${String(err)}`);
|
|
431
|
-
// 即使 dispatch
|
|
596
|
+
// 即使 dispatch 抛异常,也需要关闭 thinking 流,
|
|
597
|
+
// 避免 deliver 已成功发送媒体但后续出错时 thinking 消息残留或被错误文案覆盖
|
|
432
598
|
try {
|
|
433
|
-
const cardResult = await processTemplateCardsIfNeeded({ wsClient, frame, state, account, runtime });
|
|
434
|
-
if (cardResult) {
|
|
435
|
-
state.accumulatedText = cardResult.remainingText;
|
|
436
|
-
}
|
|
437
599
|
await finishThinkingStream(ctx);
|
|
438
600
|
}
|
|
439
601
|
catch (finishErr) {
|
|
@@ -443,12 +605,20 @@ async function routeAndDispatchMessage(params) {
|
|
|
443
605
|
}
|
|
444
606
|
}
|
|
445
607
|
/**
|
|
446
|
-
*
|
|
608
|
+
* 处理企业微信消息(主函数)
|
|
447
609
|
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
610
|
+
* 处理流程:
|
|
611
|
+
* 1. 解析消息内容(文本、图片、引用)
|
|
612
|
+
* 2. 群组策略检查(仅群聊)
|
|
613
|
+
* 3. DM Policy 访问控制检查(仅私聊)
|
|
614
|
+
* 4. 下载并保存图片
|
|
615
|
+
* 5. 初始化消息状态
|
|
616
|
+
* 6. 发送"思考中"消息
|
|
617
|
+
* 7. 路由消息到核心处理流程
|
|
618
|
+
*
|
|
619
|
+
* 整体带超时保护,防止单条消息处理阻塞过久
|
|
450
620
|
*/
|
|
451
|
-
async function
|
|
621
|
+
async function processWeComMessage(params) {
|
|
452
622
|
const { frame, account, config, runtime, wsClient } = params;
|
|
453
623
|
const body = frame.body;
|
|
454
624
|
const chatId = body.chatid || body.from.userid;
|
|
@@ -470,7 +640,7 @@ async function prepareWeComMessage(params) {
|
|
|
470
640
|
// 如果既没有文本也没有图片也没有文件也没有引用内容,则跳过
|
|
471
641
|
if (!text && imageUrls.length === 0 && fileUrls.length === 0) {
|
|
472
642
|
runtime.log?.("[wecom][plugin] Skipping empty message (no text, image, file or quote)");
|
|
473
|
-
return
|
|
643
|
+
return;
|
|
474
644
|
}
|
|
475
645
|
// Step 2: 群组策略检查(仅群聊)
|
|
476
646
|
if (chatType === "group") {
|
|
@@ -482,7 +652,7 @@ async function prepareWeComMessage(params) {
|
|
|
482
652
|
runtime,
|
|
483
653
|
});
|
|
484
654
|
if (!groupPolicyResult.allowed) {
|
|
485
|
-
return
|
|
655
|
+
return;
|
|
486
656
|
}
|
|
487
657
|
}
|
|
488
658
|
// Step 3: DM Policy 访问控制检查(仅私聊)
|
|
@@ -495,7 +665,7 @@ async function prepareWeComMessage(params) {
|
|
|
495
665
|
runtime,
|
|
496
666
|
});
|
|
497
667
|
if (!dmPolicyResult.allowed) {
|
|
498
|
-
return
|
|
668
|
+
return;
|
|
499
669
|
}
|
|
500
670
|
// Step 4: 下载并保存图片和文件
|
|
501
671
|
const [imageMediaList, fileMediaList] = await Promise.all([
|
|
@@ -517,28 +687,6 @@ async function prepareWeComMessage(params) {
|
|
|
517
687
|
}),
|
|
518
688
|
]);
|
|
519
689
|
const mediaList = [...imageMediaList, ...fileMediaList];
|
|
520
|
-
return {
|
|
521
|
-
frame,
|
|
522
|
-
account,
|
|
523
|
-
config,
|
|
524
|
-
runtime,
|
|
525
|
-
wsClient,
|
|
526
|
-
text,
|
|
527
|
-
mediaList,
|
|
528
|
-
quoteContent,
|
|
529
|
-
messageId,
|
|
530
|
-
chatId,
|
|
531
|
-
reqId,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* 处理企业微信消息(Step 5-7)
|
|
536
|
-
*
|
|
537
|
-
* 接收解析后的消息数据,执行初始化状态、发送 thinking、路由到核心。
|
|
538
|
-
* 同一会话中的消息通过串行队列保证按序执行。
|
|
539
|
-
*/
|
|
540
|
-
async function processWeComMessageNow(entry) {
|
|
541
|
-
const { frame, account, config, runtime, wsClient, text, mediaList, quoteContent, messageId, chatId, reqId } = entry;
|
|
542
690
|
// Step 5: 初始化消息状态
|
|
543
691
|
setReqIdForChat(chatId, reqId, account.accountId);
|
|
544
692
|
const streamId = generateReqId("stream");
|
|
@@ -553,15 +701,11 @@ async function processWeComMessageNow(entry) {
|
|
|
553
701
|
// await sendThinkingReply({ wsClient, frame, streamId, runtime });
|
|
554
702
|
// }
|
|
555
703
|
// Step 7: 构建上下文并路由到核心处理流程(带整体超时保护)
|
|
556
|
-
const
|
|
704
|
+
const ctxPayload = buildMessageContext(frame, account, config, text, mediaList, quoteContent);
|
|
557
705
|
// runtime.log?.(`[plugin -> openclaw] body=${text}, mediaPaths=${JSON.stringify(mediaList.map(m => m.path))}${quoteContent ? `, quote=${quoteContent}` : ''}`);
|
|
558
706
|
try {
|
|
559
707
|
await routeAndDispatchMessage({
|
|
560
708
|
ctxPayload,
|
|
561
|
-
route,
|
|
562
|
-
storePath,
|
|
563
|
-
chatId: resolvedChatId,
|
|
564
|
-
chatType,
|
|
565
709
|
config,
|
|
566
710
|
account,
|
|
567
711
|
wsClient,
|
|
@@ -605,7 +749,7 @@ function createSdkLogger(runtime, accountId) {
|
|
|
605
749
|
* 监听企业微信 WebSocket 连接
|
|
606
750
|
* 使用 aibot-node-sdk 简化连接管理
|
|
607
751
|
*/
|
|
608
|
-
|
|
752
|
+
async function monitorWeComProvider(options) {
|
|
609
753
|
const { account, config, runtime, abortSignal, setStatus } = options;
|
|
610
754
|
runtime.log?.(`[${account.accountId}] [${PLUGIN_VERSION}] Initializing WSClient with SDK...`);
|
|
611
755
|
runtime.error?.(`[${account.accountId}] [diag] monitor boot marker: build=20260325-event-debug-1`);
|
|
@@ -750,26 +894,13 @@ export async function monitorWeComProvider(options) {
|
|
|
750
894
|
// 监听普通消息
|
|
751
895
|
wsClient.on("message", async (frame) => {
|
|
752
896
|
try {
|
|
753
|
-
|
|
897
|
+
await processWeComMessage({
|
|
754
898
|
frame,
|
|
755
899
|
account,
|
|
756
900
|
config,
|
|
757
901
|
runtime,
|
|
758
902
|
wsClient,
|
|
759
903
|
});
|
|
760
|
-
if (!entry)
|
|
761
|
-
return;
|
|
762
|
-
// 排队逻辑暂时关闭,直接处理消息
|
|
763
|
-
// const { status } = enqueueWeComChatTask({
|
|
764
|
-
// accountId: entry.account.accountId,
|
|
765
|
-
// chatId: entry.chatId,
|
|
766
|
-
// task: () => processWeComMessageNow(entry),
|
|
767
|
-
// });
|
|
768
|
-
//
|
|
769
|
-
// if (status === "queued") {
|
|
770
|
-
// runtime.log?.(`[wecom] Chat task queued for chat=${entry.chatId} (previous task still running)`);
|
|
771
|
-
// }
|
|
772
|
-
await processWeComMessageNow(entry);
|
|
773
904
|
}
|
|
774
905
|
catch (err) {
|
|
775
906
|
runtime.error?.(`[${account.accountId}] Failed to process message: ${String(err)}`);
|
|
@@ -799,16 +930,13 @@ export async function monitorWeComProvider(options) {
|
|
|
799
930
|
catch (updateErr) {
|
|
800
931
|
runtime.error?.(`[${account.accountId}] [template-card-update] Failed to update template card: ${String(updateErr)}`);
|
|
801
932
|
}
|
|
802
|
-
|
|
933
|
+
await processWeComMessage({
|
|
803
934
|
frame,
|
|
804
935
|
account,
|
|
805
936
|
config,
|
|
806
937
|
runtime,
|
|
807
938
|
wsClient,
|
|
808
939
|
});
|
|
809
|
-
if (entry) {
|
|
810
|
-
await processWeComMessageNow(entry);
|
|
811
|
-
}
|
|
812
940
|
}
|
|
813
941
|
catch (err) {
|
|
814
942
|
runtime.error?.(`[${account.accountId}] Failed to process template_card_event: ${String(err)}`);
|
|
@@ -830,3 +958,6 @@ export async function monitorWeComProvider(options) {
|
|
|
830
958
|
});
|
|
831
959
|
});
|
|
832
960
|
}
|
|
961
|
+
|
|
962
|
+
export { monitorWeComProvider, sendWeComReply, setReqIdForChat, warmupReqIdStore };
|
|
963
|
+
//# sourceMappingURL=monitor.js.map
|