@wecom/wecom-openclaw-plugin 2026.3.30 → 2026.3.31-beta.0
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 +251 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +113 -0
- package/dist/src/accounts.d.ts +57 -0
- package/dist/src/accounts.js +222 -0
- package/dist/src/agent/api-client.d.ts +94 -0
- package/dist/src/agent/api-client.js +292 -0
- package/dist/src/agent/handler.d.ts +63 -0
- package/dist/src/agent/handler.js +532 -0
- package/dist/src/agent/index.d.ts +5 -0
- package/dist/src/agent/index.js +5 -0
- package/dist/src/agent/webhook.d.ts +23 -0
- package/dist/src/agent/webhook.js +226 -0
- package/dist/src/agent/xml.d.ts +21 -0
- package/dist/src/agent/xml.js +41 -0
- package/dist/src/channel.js +564 -0
- package/dist/src/chat-queue.d.ts +31 -0
- package/dist/src/chat-queue.js +45 -0
- package/dist/src/const.d.ts +46 -0
- package/dist/src/const.js +157 -0
- package/dist/src/dm-policy.js +84 -0
- package/dist/src/group-policy.js +126 -0
- package/dist/src/http.d.ts +26 -0
- package/dist/src/http.js +93 -0
- package/dist/src/interface.d.ts +0 -2
- package/dist/src/interface.js +4 -0
- package/dist/src/mcp/index.js +6 -0
- package/dist/src/mcp/interceptors/biz-error.js +64 -0
- package/dist/src/mcp/interceptors/index.js +66 -0
- package/dist/src/mcp/interceptors/msg-media.js +133 -0
- package/dist/src/mcp/interceptors/smartpage-create.js +132 -0
- package/dist/src/mcp/interceptors/smartpage-export.js +87 -0
- package/dist/src/mcp/interceptors/types.js +7 -0
- package/dist/src/mcp/schema.js +99 -0
- package/dist/src/mcp/tool.js +202 -0
- package/dist/src/mcp/transport.js +410 -0
- package/dist/src/media-handler.js +122 -0
- package/dist/src/media-uploader.js +333 -0
- package/dist/src/message-parser.js +129 -0
- package/dist/src/message-sender.d.ts +18 -0
- package/dist/src/message-sender.js +120 -0
- package/dist/src/monitor.js +832 -0
- package/dist/src/onboarding.js +175 -0
- package/dist/src/openclaw-compat.d.ts +49 -0
- package/dist/src/openclaw-compat.js +487 -0
- package/dist/src/reqid-store.js +83 -0
- package/dist/src/runtime.js +10 -0
- package/dist/src/shared/command-auth.d.ts +23 -0
- package/dist/src/shared/command-auth.js +73 -0
- package/dist/src/shared/xml-parser.d.ts +46 -0
- package/dist/src/shared/xml-parser.js +210 -0
- package/dist/src/state-manager.js +216 -0
- package/dist/src/target.d.ts +35 -0
- package/dist/src/target.js +67 -0
- package/dist/src/template-card-manager.d.ts +55 -0
- package/dist/src/template-card-manager.js +205 -0
- package/dist/src/template-card-parser.js +647 -0
- package/dist/src/timeout.js +36 -0
- package/dist/src/types/account.d.ts +29 -0
- package/dist/src/types/account.js +4 -0
- package/dist/src/types/config.d.ts +80 -0
- package/dist/src/types/config.js +7 -0
- package/dist/src/types/constants.d.ts +42 -0
- package/dist/src/types/constants.js +42 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/message.d.ts +238 -0
- package/dist/src/types/message.js +5 -0
- package/dist/src/utils.d.ts +39 -0
- package/dist/src/utils.js +70 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +24 -0
- package/dist/src/webhook/command-auth.d.ts +47 -0
- package/dist/src/webhook/command-auth.js +96 -0
- package/dist/src/webhook/gateway.d.ts +36 -0
- package/dist/src/webhook/gateway.js +227 -0
- package/dist/src/webhook/handler.d.ts +18 -0
- package/dist/src/webhook/handler.js +420 -0
- package/dist/src/webhook/helpers.d.ts +156 -0
- package/dist/src/webhook/helpers.js +833 -0
- package/dist/src/webhook/http.d.ts +26 -0
- package/dist/src/webhook/http.js +94 -0
- package/dist/src/webhook/index.d.ts +11 -0
- package/dist/src/webhook/index.js +14 -0
- package/dist/src/webhook/media.d.ts +29 -0
- package/dist/src/webhook/media.js +88 -0
- package/dist/src/webhook/monitor.d.ts +59 -0
- package/dist/src/webhook/monitor.js +1147 -0
- package/dist/src/webhook/state.d.ts +220 -0
- package/dist/src/webhook/state.js +491 -0
- package/dist/src/webhook/target.d.ts +41 -0
- package/dist/src/webhook/target.js +132 -0
- package/dist/src/webhook/types.d.ts +347 -0
- package/dist/src/webhook/types.js +33 -0
- package/package.json +15 -7
- package/dist/index.cjs.js +0 -4953
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.esm.js +0 -4926
- package/dist/index.esm.js.map +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模板卡片管理器
|
|
3
|
+
*
|
|
4
|
+
* 负责:
|
|
5
|
+
* - 模板卡片缓存管理(内存级,带 TTL 和大小限制)
|
|
6
|
+
* - 卡片交互事件处理(更新卡片 UI 状态)
|
|
7
|
+
* - 模板卡片发送(通过 wsClient.sendMessage 主动推送)
|
|
8
|
+
* - 从 LLM 回复中检测并处理模板卡片
|
|
9
|
+
*/
|
|
10
|
+
import { extractTemplateCards } from "./template-card-parser.js";
|
|
11
|
+
import { TEMPLATE_CARD_CACHE_TTL_MS, TEMPLATE_CARD_CACHE_MAX_SIZE, } from "./const.js";
|
|
12
|
+
const sentTemplateCardByTaskId = new Map();
|
|
13
|
+
function getTemplateCardCacheKey(accountId, taskId) {
|
|
14
|
+
return `${accountId}:${taskId}`;
|
|
15
|
+
}
|
|
16
|
+
function pruneTemplateCardCache() {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
for (const [key, entry] of sentTemplateCardByTaskId) {
|
|
19
|
+
if (now - entry.createdAt >= TEMPLATE_CARD_CACHE_TTL_MS) {
|
|
20
|
+
sentTemplateCardByTaskId.delete(key);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (sentTemplateCardByTaskId.size <= TEMPLATE_CARD_CACHE_MAX_SIZE) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const sortedEntries = [...sentTemplateCardByTaskId.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
27
|
+
const removeCount = sentTemplateCardByTaskId.size - TEMPLATE_CARD_CACHE_MAX_SIZE;
|
|
28
|
+
for (const [key] of sortedEntries.slice(0, removeCount)) {
|
|
29
|
+
sentTemplateCardByTaskId.delete(key);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function cloneTemplateCard(card) {
|
|
33
|
+
return JSON.parse(JSON.stringify(card));
|
|
34
|
+
}
|
|
35
|
+
export function saveTemplateCardToCache(params) {
|
|
36
|
+
const { accountId, templateCard, runtime } = params;
|
|
37
|
+
const taskId = templateCard.task_id;
|
|
38
|
+
if (!taskId) {
|
|
39
|
+
runtime.log?.("[wecom][template-card] Skip cache: template card has no task_id");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
sentTemplateCardByTaskId.set(getTemplateCardCacheKey(accountId, taskId), {
|
|
43
|
+
templateCard: cloneTemplateCard(templateCard),
|
|
44
|
+
createdAt: Date.now(),
|
|
45
|
+
});
|
|
46
|
+
pruneTemplateCardCache();
|
|
47
|
+
}
|
|
48
|
+
export function getTemplateCardFromCache(accountId, taskId) {
|
|
49
|
+
pruneTemplateCardCache();
|
|
50
|
+
const cached = sentTemplateCardByTaskId.get(getTemplateCardCacheKey(accountId, taskId));
|
|
51
|
+
if (!cached) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return cloneTemplateCard(cached.templateCard);
|
|
55
|
+
}
|
|
56
|
+
function buildSelectedOptionMap(templateCardEvent) {
|
|
57
|
+
const selectedMap = new Map();
|
|
58
|
+
const selectedItems = templateCardEvent?.selected_items?.selected_item ?? [];
|
|
59
|
+
for (const item of selectedItems) {
|
|
60
|
+
const questionKey = item.question_key?.trim();
|
|
61
|
+
if (!questionKey) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const optionIds = item.option_ids?.option_id?.filter(Boolean) ?? [];
|
|
65
|
+
selectedMap.set(questionKey, optionIds);
|
|
66
|
+
}
|
|
67
|
+
return selectedMap;
|
|
68
|
+
}
|
|
69
|
+
function applySelectedStateToTemplateCard(params) {
|
|
70
|
+
const { templateCard, selectedMap, templateCardEvent } = params;
|
|
71
|
+
const nextCard = cloneTemplateCard(templateCard);
|
|
72
|
+
if (templateCardEvent?.task_id) {
|
|
73
|
+
nextCard.task_id = templateCardEvent.task_id;
|
|
74
|
+
}
|
|
75
|
+
if (templateCardEvent?.card_type) {
|
|
76
|
+
nextCard.card_type = templateCardEvent.card_type;
|
|
77
|
+
}
|
|
78
|
+
if (nextCard.submit_button?.text) {
|
|
79
|
+
nextCard.submit_button.text = "已提交";
|
|
80
|
+
}
|
|
81
|
+
if (nextCard.checkbox?.question_key) {
|
|
82
|
+
const selectedIds = selectedMap.get(nextCard.checkbox.question_key) ?? [];
|
|
83
|
+
nextCard.checkbox.disable = true;
|
|
84
|
+
if (Array.isArray(nextCard.checkbox.option_list)) {
|
|
85
|
+
nextCard.checkbox.option_list = nextCard.checkbox.option_list.map((option) => ({
|
|
86
|
+
...option,
|
|
87
|
+
is_checked: selectedIds.includes(option.id),
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(nextCard.select_list)) {
|
|
92
|
+
nextCard.select_list = nextCard.select_list.map((selection) => {
|
|
93
|
+
const selectedIds = selectedMap.get(selection.question_key) ?? [];
|
|
94
|
+
return {
|
|
95
|
+
...selection,
|
|
96
|
+
disable: true,
|
|
97
|
+
selected_id: selectedIds[0] ?? selection.selected_id,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (nextCard.button_selection?.question_key) {
|
|
102
|
+
const selectedIds = selectedMap.get(nextCard.button_selection.question_key) ?? [];
|
|
103
|
+
nextCard.button_selection.disable = true;
|
|
104
|
+
if (selectedIds[0]) {
|
|
105
|
+
nextCard.button_selection.selected_id = selectedIds[0];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return nextCard;
|
|
109
|
+
}
|
|
110
|
+
export async function updateTemplateCardOnEvent(params) {
|
|
111
|
+
const { frame, accountId, runtime, wsClient } = params;
|
|
112
|
+
const body = frame.body;
|
|
113
|
+
const templateCardEvent = body.event?.template_card_event;
|
|
114
|
+
const taskId = templateCardEvent?.task_id;
|
|
115
|
+
if (!taskId) {
|
|
116
|
+
runtime.log?.(`[${accountId}] [template-card-update] Skip update: missing task_id in callback`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const cachedCard = getTemplateCardFromCache(accountId, taskId);
|
|
120
|
+
if (!cachedCard) {
|
|
121
|
+
runtime.log?.(`[${accountId}] [template-card-update] Skip update: task_id=${taskId} not found in cache (cache is in-memory only, may have been cleared after restart)`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const selectedMap = buildSelectedOptionMap(templateCardEvent);
|
|
125
|
+
const updatedCard = applySelectedStateToTemplateCard({
|
|
126
|
+
templateCard: cachedCard,
|
|
127
|
+
selectedMap,
|
|
128
|
+
templateCardEvent,
|
|
129
|
+
});
|
|
130
|
+
await wsClient.updateTemplateCard(frame, updatedCard, [body.from.userid]);
|
|
131
|
+
runtime.log?.(`[${accountId}] [template-card-update] Updated card by task_id=${taskId}`);
|
|
132
|
+
saveTemplateCardToCache({
|
|
133
|
+
accountId,
|
|
134
|
+
templateCard: updatedCard,
|
|
135
|
+
runtime,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// 模板卡片发送
|
|
140
|
+
// ============================================================================
|
|
141
|
+
/**
|
|
142
|
+
* 逐个发送已提取的模板卡片(通过 wsClient.sendMessage 主动推送)
|
|
143
|
+
*
|
|
144
|
+
* 发送失败不阻塞流程,仅记录错误日志。
|
|
145
|
+
*/
|
|
146
|
+
export async function sendTemplateCards(params) {
|
|
147
|
+
const { wsClient, frame, state, runtime, account, cards } = params;
|
|
148
|
+
const body = frame.body;
|
|
149
|
+
const chatId = body.chatid || body.from.userid;
|
|
150
|
+
for (const card of cards) {
|
|
151
|
+
try {
|
|
152
|
+
runtime.log?.(`[wecom][template-card] Sending card_type=${card.cardType} to chatId=${chatId}`);
|
|
153
|
+
const rawTemplateCard = card.cardJson;
|
|
154
|
+
if (typeof rawTemplateCard.card_type !== "string") {
|
|
155
|
+
runtime.error?.("[wecom][template-card] Skip sending invalid card: missing card_type");
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const templateCard = rawTemplateCard;
|
|
159
|
+
await wsClient.sendMessage(chatId, {
|
|
160
|
+
msgtype: "template_card",
|
|
161
|
+
template_card: templateCard,
|
|
162
|
+
});
|
|
163
|
+
state.hasTemplateCard = true;
|
|
164
|
+
saveTemplateCardToCache({
|
|
165
|
+
accountId: account.accountId,
|
|
166
|
+
templateCard,
|
|
167
|
+
runtime,
|
|
168
|
+
});
|
|
169
|
+
runtime.log?.(`[wecom][template-card] Card sent successfully: card_type=${card.cardType}`);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
runtime.error?.(`[wecom][template-card] Failed to send card: card_type=${card.cardType}, error=${JSON.stringify(err)}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// 模板卡片检测与处理(从 finishThinkingStream 中分离)
|
|
178
|
+
// ============================================================================
|
|
179
|
+
/**
|
|
180
|
+
* 从累积文本中检测并发送模板卡片。
|
|
181
|
+
*
|
|
182
|
+
* 在 finishThinkingStream 之前调用,将卡片处理和流关闭解耦。
|
|
183
|
+
*
|
|
184
|
+
* @returns 移除卡片代码块后的剩余文本(如果没有卡片则返回 null,表示无需修改)
|
|
185
|
+
*/
|
|
186
|
+
export async function processTemplateCardsIfNeeded(params) {
|
|
187
|
+
const { state, runtime } = params;
|
|
188
|
+
const visibleText = state.accumulatedText?.trim();
|
|
189
|
+
if (!visibleText) {
|
|
190
|
+
runtime.log?.(`[wecom][template-card] processTemplateCardsIfNeeded: no visibleText, skipping`);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
runtime.log?.(`[wecom][template-card] processTemplateCardsIfNeeded: visibleText exists, length=${visibleText.length}, running extractTemplateCards...`);
|
|
194
|
+
const logFn = (...args) => {
|
|
195
|
+
runtime.log?.(...args);
|
|
196
|
+
};
|
|
197
|
+
const { cards, remainingText } = extractTemplateCards(state.accumulatedText, logFn);
|
|
198
|
+
runtime.log?.(`[wecom][template-card] processTemplateCardsIfNeeded: extractTemplateCards result — cards=${cards.length}, remainingTextLength=${remainingText.length}`);
|
|
199
|
+
if (cards.length === 0) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
runtime.log?.(`[wecom][template-card] processTemplateCardsIfNeeded: ${cards.length} card(s) detected, card_types=[${cards.map(c => c.cardType).join(", ")}]`);
|
|
203
|
+
await sendTemplateCards({ ...params, cards });
|
|
204
|
+
return { remainingText, cardsDetected: true };
|
|
205
|
+
}
|