@dingxiang-me/openclaw-wechat 1.7.2 → 2.0.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/CHANGELOG.md +160 -0
- package/README.en.md +379 -11
- package/README.md +620 -12
- package/docs/channels/wecom.md +181 -3
- package/openclaw.plugin.json +148 -5
- package/package.json +9 -5
- package/src/core/delivery-router.js +2 -0
- package/src/core/stream-manager.js +13 -2
- package/src/core.js +96 -6
- package/src/wecom/account-config-core.js +2 -0
- package/src/wecom/account-config.js +12 -3
- package/src/wecom/agent-context.js +7 -1
- package/src/wecom/agent-dispatch-executor.js +13 -1
- package/src/wecom/agent-dispatch-fallback.js +23 -0
- package/src/wecom/agent-inbound-dispatch.js +1 -1
- package/src/wecom/agent-inbound-processor.js +33 -2
- package/src/wecom/agent-late-reply-runtime.js +31 -1
- package/src/wecom/agent-runtime-context.js +3 -0
- package/src/wecom/agent-webhook-handler.js +5 -0
- package/src/wecom/api-client-core.js +1 -1
- package/src/wecom/api-client-send-text.js +43 -20
- package/src/wecom/bot-context.js +7 -1
- package/src/wecom/bot-dispatch-fallback.js +34 -3
- package/src/wecom/bot-dispatch-handlers.js +47 -4
- package/src/wecom/bot-inbound-content.js +14 -6
- package/src/wecom/bot-inbound-dispatch-runtime.js +10 -0
- package/src/wecom/bot-inbound-executor-helpers.js +44 -11
- package/src/wecom/bot-inbound-executor.js +40 -0
- package/src/wecom/bot-long-connection-manager.js +983 -0
- package/src/wecom/bot-reply-runtime.js +36 -6
- package/src/wecom/bot-runtime-context.js +3 -0
- package/src/wecom/bot-state-store.js +4 -5
- package/src/wecom/bot-webhook-dispatch.js +7 -0
- package/src/wecom/bot-webhook-handler.js +5 -0
- package/src/wecom/callback-health-diagnostics.js +86 -0
- package/src/wecom/channel-config-schema.js +242 -0
- package/src/wecom/channel-plugin.js +162 -4
- package/src/wecom/channel-status-state.js +150 -0
- package/src/wecom/command-handlers.js +6 -0
- package/src/wecom/command-status-text.js +32 -3
- package/src/wecom/doc-client.js +537 -0
- package/src/wecom/doc-schema.js +380 -0
- package/src/wecom/doc-tool.js +833 -0
- package/src/wecom/outbound-active-stream.js +17 -10
- package/src/wecom/outbound-delivery.js +46 -0
- package/src/wecom/outbound-webhook-sender.js +39 -16
- package/src/wecom/plugin-account-policy-services.js +4 -1
- package/src/wecom/plugin-composition.js +2 -0
- package/src/wecom/plugin-constants.js +1 -1
- package/src/wecom/plugin-delivery-inbound-services.js +4 -0
- package/src/wecom/plugin-processing-deps.js +5 -0
- package/src/wecom/plugin-route-runtime-deps.js +2 -0
- package/src/wecom/plugin-services.js +37 -0
- package/src/wecom/register-runtime.js +20 -1
- package/src/wecom/request-parsers.js +1 -0
- package/src/wecom/route-registration.js +4 -1
- package/src/wecom/session-reset.js +168 -0
- package/src/wecom/target-utils.js +41 -5
- package/src/wecom/text-format.js +22 -5
- package/src/wecom/text-inbound-scheduler.js +1 -1
- package/src/wecom/thinking-parser.js +74 -0
- package/src/wecom/voice-transcription-process.js +145 -11
- package/src/wecom/voice-transcription.js +14 -2
- package/src/wecom/webhook-adapter-normalize.js +29 -0
- package/src/wecom/webhook-adapter.js +294 -59
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
collectWecomBotImageEntries,
|
|
2
3
|
collectWecomBotImageUrls,
|
|
3
4
|
dedupeUrlList,
|
|
4
5
|
normalizeLowerToken,
|
|
@@ -7,7 +8,202 @@ import {
|
|
|
7
8
|
normalizeWecomBotOutboundMediaUrls,
|
|
8
9
|
} from "./webhook-adapter-normalize.js";
|
|
9
10
|
|
|
10
|
-
export { collectWecomBotImageUrls, normalizeWecomBotOutboundMediaUrls };
|
|
11
|
+
export { collectWecomBotImageEntries, collectWecomBotImageUrls, normalizeWecomBotOutboundMediaUrls };
|
|
12
|
+
|
|
13
|
+
function dedupeMediaEntries(entries) {
|
|
14
|
+
const seen = new Map();
|
|
15
|
+
for (const rawEntry of Array.isArray(entries) ? entries : []) {
|
|
16
|
+
if (!rawEntry || typeof rawEntry !== "object") continue;
|
|
17
|
+
const url = normalizeToken(rawEntry.url);
|
|
18
|
+
if (!url) continue;
|
|
19
|
+
const aesKey = normalizeToken(rawEntry.aesKey);
|
|
20
|
+
const existing = seen.get(url);
|
|
21
|
+
if (!existing) {
|
|
22
|
+
seen.set(url, { url, aesKey });
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (!existing.aesKey && aesKey) {
|
|
26
|
+
seen.set(url, { url, aesKey });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return Array.from(seen.values());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function pickNestedValue(source, paths = []) {
|
|
33
|
+
if (!source || typeof source !== "object") return undefined;
|
|
34
|
+
for (const rawPath of Array.isArray(paths) ? paths : []) {
|
|
35
|
+
const segments = Array.isArray(rawPath)
|
|
36
|
+
? rawPath
|
|
37
|
+
: String(rawPath ?? "")
|
|
38
|
+
.split(".")
|
|
39
|
+
.map((part) => String(part ?? "").trim())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
if (segments.length === 0) continue;
|
|
42
|
+
let current = source;
|
|
43
|
+
let found = true;
|
|
44
|
+
for (const segment of segments) {
|
|
45
|
+
if (!current || typeof current !== "object" || !Object.hasOwn(current, segment)) {
|
|
46
|
+
found = false;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
current = current[segment];
|
|
50
|
+
}
|
|
51
|
+
if (found && current != null) {
|
|
52
|
+
return current;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function collectWecomBotMixedItems(payload) {
|
|
59
|
+
const directItems = pickNestedValue(payload, [
|
|
60
|
+
["mixed", "msg_item"],
|
|
61
|
+
["mixed", "msgItem"],
|
|
62
|
+
["mixed", "items"],
|
|
63
|
+
["mixed", "msgItems"],
|
|
64
|
+
["msg_item"],
|
|
65
|
+
["msgItem"],
|
|
66
|
+
["items"],
|
|
67
|
+
["attachments"],
|
|
68
|
+
["attachment"],
|
|
69
|
+
["message", "attachments"],
|
|
70
|
+
["message", "items"],
|
|
71
|
+
["message", "msg_item"],
|
|
72
|
+
["message", "msgItem"],
|
|
73
|
+
]);
|
|
74
|
+
if (Array.isArray(directItems)) {
|
|
75
|
+
return directItems.filter((item) => item && typeof item === "object");
|
|
76
|
+
}
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function inferWecomBotItemType(item) {
|
|
81
|
+
const explicitType = normalizeLowerToken(
|
|
82
|
+
item?.msgtype ||
|
|
83
|
+
item?.msg_type ||
|
|
84
|
+
item?.msgType ||
|
|
85
|
+
item?.type ||
|
|
86
|
+
item?.message_type ||
|
|
87
|
+
item?.messageType ||
|
|
88
|
+
item?.kind,
|
|
89
|
+
);
|
|
90
|
+
if (explicitType) return explicitType;
|
|
91
|
+
if (item?.text || item?.content_type === "text" || item?.contentType === "text") return "text";
|
|
92
|
+
if (item?.image || item?.pic_url || item?.image_url || item?.imageUrl) return "image";
|
|
93
|
+
if (item?.voice || item?.voice_url || item?.voiceUrl || item?.audio || item?.audio_url || item?.audioUrl) return "voice";
|
|
94
|
+
if (
|
|
95
|
+
item?.file ||
|
|
96
|
+
item?.file_url ||
|
|
97
|
+
item?.fileUrl ||
|
|
98
|
+
item?.download_url ||
|
|
99
|
+
item?.downloadUrl ||
|
|
100
|
+
item?.filename ||
|
|
101
|
+
item?.file_name
|
|
102
|
+
) {
|
|
103
|
+
return "file";
|
|
104
|
+
}
|
|
105
|
+
if (item?.link || item?.url) return "link";
|
|
106
|
+
if (item?.location || item?.latitude || item?.longitude) return "location";
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function normalizeWecomBotMediaPayload(payload) {
|
|
111
|
+
if (!payload || typeof payload !== "object") return null;
|
|
112
|
+
const nestedMessage = pickNestedValue(payload, [["message"], ["msg"], ["data"], ["event", "message"]]);
|
|
113
|
+
const candidates = [payload];
|
|
114
|
+
if (nestedMessage && typeof nestedMessage === "object" && nestedMessage !== payload) {
|
|
115
|
+
candidates.unshift(nestedMessage);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const candidate of candidates) {
|
|
119
|
+
const msgType = normalizeLowerToken(
|
|
120
|
+
pickNestedValue(candidate, [
|
|
121
|
+
["msgtype"],
|
|
122
|
+
["msg_type"],
|
|
123
|
+
["msgType"],
|
|
124
|
+
["message_type"],
|
|
125
|
+
["messageType"],
|
|
126
|
+
["type"],
|
|
127
|
+
]),
|
|
128
|
+
);
|
|
129
|
+
if (!msgType) continue;
|
|
130
|
+
return {
|
|
131
|
+
source: candidate,
|
|
132
|
+
msgType,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractWecomBotTextContent(textLike, fallbackContent = "") {
|
|
139
|
+
return normalizeToken(
|
|
140
|
+
textLike?.content ||
|
|
141
|
+
textLike?.text ||
|
|
142
|
+
textLike?.body ||
|
|
143
|
+
textLike?.message ||
|
|
144
|
+
fallbackContent,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function extractWecomBotVoicePayload(voiceLike, fallback = {}) {
|
|
149
|
+
const source = voiceLike && typeof voiceLike === "object" ? voiceLike : fallback;
|
|
150
|
+
return {
|
|
151
|
+
url: normalizeToken(
|
|
152
|
+
source?.url ||
|
|
153
|
+
source?.media_url ||
|
|
154
|
+
source?.mediaUrl ||
|
|
155
|
+
source?.download_url ||
|
|
156
|
+
source?.downloadUrl ||
|
|
157
|
+
source?.file_url ||
|
|
158
|
+
source?.fileUrl ||
|
|
159
|
+
source?.voice_url ||
|
|
160
|
+
source?.voiceUrl ||
|
|
161
|
+
source?.audio_url ||
|
|
162
|
+
source?.audioUrl,
|
|
163
|
+
),
|
|
164
|
+
mediaId: normalizeToken(
|
|
165
|
+
source?.media_id ||
|
|
166
|
+
source?.mediaid ||
|
|
167
|
+
source?.mediaId ||
|
|
168
|
+
source?.id,
|
|
169
|
+
),
|
|
170
|
+
contentType: normalizeToken(
|
|
171
|
+
source?.content_type ||
|
|
172
|
+
source?.contentType ||
|
|
173
|
+
source?.mime_type ||
|
|
174
|
+
source?.mimeType ||
|
|
175
|
+
source?.format,
|
|
176
|
+
),
|
|
177
|
+
text: extractWecomBotTextContent(source, source?.content),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function extractWecomBotFilePayload(fileLike, fallback = {}) {
|
|
182
|
+
const source = fileLike && typeof fileLike === "object" ? fileLike : fallback;
|
|
183
|
+
return {
|
|
184
|
+
url: normalizeToken(
|
|
185
|
+
source?.url ||
|
|
186
|
+
source?.download_url ||
|
|
187
|
+
source?.downloadUrl ||
|
|
188
|
+
source?.media_url ||
|
|
189
|
+
source?.mediaUrl ||
|
|
190
|
+
source?.file_url ||
|
|
191
|
+
source?.fileUrl,
|
|
192
|
+
),
|
|
193
|
+
name: normalizeToken(
|
|
194
|
+
source?.name ||
|
|
195
|
+
source?.filename ||
|
|
196
|
+
source?.file_name ||
|
|
197
|
+
source?.fileName ||
|
|
198
|
+
source?.title,
|
|
199
|
+
),
|
|
200
|
+
aesKey: normalizeToken(
|
|
201
|
+
source?.aeskey ||
|
|
202
|
+
source?.aes_key ||
|
|
203
|
+
source?.aesKey,
|
|
204
|
+
),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
11
207
|
|
|
12
208
|
export function buildWecomBotMixedPayload({ text = "", mediaUrl, mediaUrls } = {}) {
|
|
13
209
|
const normalizedText = normalizeToken(text);
|
|
@@ -46,81 +242,113 @@ export function buildWecomBotMixedPayload({ text = "", mediaUrl, mediaUrls } = {
|
|
|
46
242
|
|
|
47
243
|
export function parseWecomBotInboundMessage(payload) {
|
|
48
244
|
if (!payload || typeof payload !== "object") return null;
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
51
|
-
const
|
|
245
|
+
const normalizedPayload = normalizeWecomBotMediaPayload(payload);
|
|
246
|
+
if (!normalizedPayload) return null;
|
|
247
|
+
const source = normalizedPayload.source;
|
|
248
|
+
const msgType = normalizedPayload.msgType;
|
|
249
|
+
const feedbackId = normalizeToken(
|
|
250
|
+
pickNestedValue(source, [["feedback", "id"], ["stream", "feedback", "id"], ["stream", "feedbackId"], ["feedbackId"]]),
|
|
251
|
+
);
|
|
52
252
|
if (msgType === "stream") {
|
|
53
253
|
return {
|
|
54
254
|
kind: "stream-refresh",
|
|
55
|
-
streamId: normalizeToken(
|
|
255
|
+
streamId: normalizeToken(pickNestedValue(source, [["stream", "id"], ["streamId"]])),
|
|
56
256
|
feedbackId,
|
|
57
257
|
};
|
|
58
258
|
}
|
|
59
259
|
|
|
60
|
-
const msgId =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
260
|
+
const msgId =
|
|
261
|
+
normalizeToken(
|
|
262
|
+
pickNestedValue(source, [["msgid"], ["msg_id"], ["msgId"], ["message_id"], ["messageId"], ["id"]]),
|
|
263
|
+
) || `wecom-bot-${Date.now()}`;
|
|
264
|
+
const fromUser = normalizeToken(
|
|
265
|
+
pickNestedValue(source, [
|
|
266
|
+
["from", "userid"],
|
|
267
|
+
["from", "user_id"],
|
|
268
|
+
["from", "userId"],
|
|
269
|
+
["sender", "userid"],
|
|
270
|
+
["sender", "user_id"],
|
|
271
|
+
["sender", "userId"],
|
|
272
|
+
["sender", "id"],
|
|
273
|
+
["userid"],
|
|
274
|
+
["user_id"],
|
|
275
|
+
["userId"],
|
|
276
|
+
]),
|
|
277
|
+
);
|
|
278
|
+
const chatType =
|
|
279
|
+
normalizeLowerToken(
|
|
280
|
+
pickNestedValue(source, [["chattype"], ["chat_type"], ["chatType"]]) || "single",
|
|
281
|
+
) || "single";
|
|
282
|
+
const chatId = normalizeToken(
|
|
283
|
+
pickNestedValue(source, [["chatid"], ["chat_id"], ["chatId"], ["conversation_id"], ["conversationId"]]),
|
|
284
|
+
);
|
|
285
|
+
const responseUrl = normalizeToken(
|
|
286
|
+
pickNestedValue(source, [["response_url"], ["responseUrl"], ["reply_url"], ["replyUrl"]]),
|
|
287
|
+
);
|
|
288
|
+
const quote = normalizeQuotePayload(pickNestedValue(source, [["quote"], ["quoted_message"], ["quotedMessage"]]));
|
|
66
289
|
let content = "";
|
|
290
|
+
const imageEntries = [];
|
|
67
291
|
const imageUrls = [];
|
|
68
292
|
let fileUrl = "";
|
|
69
293
|
let fileName = "";
|
|
294
|
+
let fileAesKey = "";
|
|
70
295
|
let voiceUrl = "";
|
|
71
296
|
let voiceMediaId = "";
|
|
72
297
|
let voiceContentType = "";
|
|
73
298
|
|
|
74
299
|
if (msgType === "text") {
|
|
75
|
-
content =
|
|
300
|
+
content = extractWecomBotTextContent(
|
|
301
|
+
pickNestedValue(source, [["text"], ["message", "text"]]),
|
|
302
|
+
pickNestedValue(source, [["content"], ["text_content"], ["textContent"]]),
|
|
303
|
+
);
|
|
76
304
|
} else if (msgType === "voice") {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
payload?.voice?.media_url ||
|
|
81
|
-
payload?.voice?.download_url ||
|
|
82
|
-
payload?.voice?.file_url,
|
|
305
|
+
const voicePayload = extractWecomBotVoicePayload(
|
|
306
|
+
pickNestedValue(source, [["voice"], ["audio"], ["message", "voice"], ["message", "audio"]]),
|
|
307
|
+
source,
|
|
83
308
|
);
|
|
84
|
-
|
|
85
|
-
|
|
309
|
+
content = voicePayload.text;
|
|
310
|
+
voiceUrl = voicePayload.url;
|
|
311
|
+
voiceMediaId = voicePayload.mediaId;
|
|
312
|
+
voiceContentType = voicePayload.contentType;
|
|
86
313
|
} else if (msgType === "link") {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
314
|
+
const linkPayload = pickNestedValue(source, [["link"], ["message", "link"]]) || source;
|
|
315
|
+
const title = normalizeToken(linkPayload?.title);
|
|
316
|
+
const description = normalizeToken(linkPayload?.description);
|
|
317
|
+
const url = normalizeToken(linkPayload?.url);
|
|
90
318
|
content = [title ? `[链接] ${title}` : "", description, url].filter(Boolean).join("\n").trim();
|
|
91
319
|
} else if (msgType === "location") {
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
320
|
+
const locationPayload = pickNestedValue(source, [["location"], ["message", "location"]]) || source;
|
|
321
|
+
const latitude = normalizeToken(locationPayload?.latitude);
|
|
322
|
+
const longitude = normalizeToken(locationPayload?.longitude);
|
|
323
|
+
const name = normalizeToken(locationPayload?.name || locationPayload?.label);
|
|
95
324
|
content = name ? `[位置] ${name} (${latitude}, ${longitude})` : `[位置] ${latitude}, ${longitude}`;
|
|
96
325
|
} else if (msgType === "image") {
|
|
97
|
-
|
|
326
|
+
const topLevelImageEntries = collectWecomBotImageEntries(
|
|
327
|
+
pickNestedValue(source, [["image"], ["message", "image"]]) || source,
|
|
328
|
+
);
|
|
329
|
+
imageEntries.push(...topLevelImageEntries);
|
|
330
|
+
imageUrls.push(...topLevelImageEntries.map((entry) => entry.url));
|
|
98
331
|
content = "[图片]";
|
|
99
332
|
} else if (msgType === "mixed") {
|
|
100
|
-
const items =
|
|
333
|
+
const items = collectWecomBotMixedItems(source);
|
|
101
334
|
const parts = [];
|
|
102
335
|
for (const item of items) {
|
|
103
|
-
const itemType =
|
|
336
|
+
const itemType = inferWecomBotItemType(item);
|
|
104
337
|
if (itemType === "text") {
|
|
105
|
-
const text =
|
|
338
|
+
const text = extractWecomBotTextContent(item?.text, item?.content);
|
|
106
339
|
if (text) parts.push(text);
|
|
107
340
|
} else if (itemType === "image") {
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
110
|
-
|
|
341
|
+
const itemImageEntries = collectWecomBotImageEntries(item?.image || item);
|
|
342
|
+
if (itemImageEntries.length > 0) {
|
|
343
|
+
imageEntries.push(...itemImageEntries);
|
|
344
|
+
imageUrls.push(...itemImageEntries.map((entry) => entry.url));
|
|
111
345
|
parts.push("[图片]");
|
|
112
346
|
}
|
|
113
347
|
} else if (itemType === "voice") {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
item?.voice?.file_url,
|
|
119
|
-
);
|
|
120
|
-
const itemVoiceMediaId = normalizeToken(item?.voice?.media_id || item?.voice?.mediaid || item?.voice?.id);
|
|
121
|
-
const itemVoiceContentType = normalizeToken(
|
|
122
|
-
item?.voice?.content_type || item?.voice?.mime_type || item?.voice?.format,
|
|
123
|
-
);
|
|
348
|
+
const voicePayload = extractWecomBotVoicePayload(item?.voice || item?.audio, item);
|
|
349
|
+
const itemVoiceUrl = voicePayload.url;
|
|
350
|
+
const itemVoiceMediaId = voicePayload.mediaId;
|
|
351
|
+
const itemVoiceContentType = voicePayload.contentType;
|
|
124
352
|
if (itemVoiceUrl) {
|
|
125
353
|
voiceUrl = voiceUrl || itemVoiceUrl;
|
|
126
354
|
voiceMediaId = voiceMediaId || itemVoiceMediaId;
|
|
@@ -128,29 +356,27 @@ export function parseWecomBotInboundMessage(payload) {
|
|
|
128
356
|
parts.push("[语音]");
|
|
129
357
|
}
|
|
130
358
|
} else if (itemType === "file") {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
item?.file?.file_url,
|
|
136
|
-
);
|
|
137
|
-
const itemFileName = normalizeToken(item?.file?.name || item?.file?.filename);
|
|
359
|
+
const filePayload = extractWecomBotFilePayload(item?.file, item);
|
|
360
|
+
const itemFileUrl = filePayload.url;
|
|
361
|
+
const itemFileName = filePayload.name;
|
|
362
|
+
const itemFileAesKey = filePayload.aesKey;
|
|
138
363
|
if (itemFileUrl || itemFileName) {
|
|
139
364
|
fileUrl = fileUrl || itemFileUrl;
|
|
140
365
|
fileName = fileName || itemFileName;
|
|
366
|
+
fileAesKey = fileAesKey || itemFileAesKey;
|
|
141
367
|
const displayName = itemFileName || itemFileUrl || "附件";
|
|
142
368
|
parts.push(`[文件] ${displayName}`);
|
|
143
369
|
}
|
|
144
370
|
} else if (itemType === "link") {
|
|
145
|
-
const title = normalizeToken(item?.link?.title);
|
|
146
|
-
const description = normalizeToken(item?.link?.description);
|
|
147
|
-
const url = normalizeToken(item?.link?.url);
|
|
371
|
+
const title = normalizeToken(item?.link?.title || item?.title);
|
|
372
|
+
const description = normalizeToken(item?.link?.description || item?.description);
|
|
373
|
+
const url = normalizeToken(item?.link?.url || item?.url);
|
|
148
374
|
const linkText = [title ? `[链接] ${title}` : "", description, url].filter(Boolean).join("\n").trim();
|
|
149
375
|
if (linkText) parts.push(linkText);
|
|
150
376
|
} else if (itemType === "location") {
|
|
151
|
-
const latitude = normalizeToken(item?.location?.latitude);
|
|
152
|
-
const longitude = normalizeToken(item?.location?.longitude);
|
|
153
|
-
const name = normalizeToken(item?.location?.name || item?.location?.label);
|
|
377
|
+
const latitude = normalizeToken(item?.location?.latitude || item?.latitude);
|
|
378
|
+
const longitude = normalizeToken(item?.location?.longitude || item?.longitude);
|
|
379
|
+
const name = normalizeToken(item?.location?.name || item?.location?.label || item?.name || item?.label);
|
|
154
380
|
const locationText = name ? `[位置] ${name} (${latitude}, ${longitude})` : `[位置] ${latitude}, ${longitude}`;
|
|
155
381
|
if (locationText.trim() !== "[位置] ,") {
|
|
156
382
|
parts.push(locationText);
|
|
@@ -159,14 +385,21 @@ export function parseWecomBotInboundMessage(payload) {
|
|
|
159
385
|
}
|
|
160
386
|
content = parts.join("\n").trim();
|
|
161
387
|
} else if (msgType === "file") {
|
|
162
|
-
|
|
163
|
-
|
|
388
|
+
const filePayload = extractWecomBotFilePayload(
|
|
389
|
+
pickNestedValue(source, [["file"], ["message", "file"], ["attachment"], ["document"]]),
|
|
390
|
+
source,
|
|
391
|
+
);
|
|
392
|
+
fileUrl = filePayload.url;
|
|
393
|
+
fileName = filePayload.name;
|
|
394
|
+
fileAesKey = filePayload.aesKey;
|
|
164
395
|
const displayName = fileName || fileUrl || "附件";
|
|
165
396
|
content = `[文件] ${displayName}`;
|
|
166
397
|
} else if (msgType === "event") {
|
|
167
398
|
return {
|
|
168
399
|
kind: "event",
|
|
169
|
-
eventType: normalizeToken(
|
|
400
|
+
eventType: normalizeToken(
|
|
401
|
+
pickNestedValue(source, [["event", "event_type"], ["event", "eventType"], ["event"], ["event_type"], ["eventType"]]),
|
|
402
|
+
),
|
|
170
403
|
fromUser,
|
|
171
404
|
};
|
|
172
405
|
} else {
|
|
@@ -197,8 +430,10 @@ export function parseWecomBotInboundMessage(payload) {
|
|
|
197
430
|
responseUrl,
|
|
198
431
|
content,
|
|
199
432
|
imageUrls: dedupeUrlList(imageUrls),
|
|
433
|
+
imageEntries: dedupeMediaEntries(imageEntries),
|
|
200
434
|
fileUrl,
|
|
201
435
|
fileName,
|
|
436
|
+
fileAesKey,
|
|
202
437
|
voiceUrl,
|
|
203
438
|
voiceMediaId,
|
|
204
439
|
voiceContentType,
|