@marshulll/openclaw-wecom 0.1.12 → 0.1.14
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/package.json +1 -1
- package/wecom/src/wecom-app.ts +70 -52
package/package.json
CHANGED
package/wecom/src/wecom-app.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { XMLParser } from "fast-xml-parser";
|
|
3
|
-
import { mkdir, readdir, rm, stat,
|
|
3
|
+
import { mkdir, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
|
|
@@ -171,8 +171,13 @@ async function startAgentForApp(params: {
|
|
|
171
171
|
chatId?: string;
|
|
172
172
|
isGroup: boolean;
|
|
173
173
|
messageText: string;
|
|
174
|
+
media?: {
|
|
175
|
+
type: "image" | "voice" | "video" | "file";
|
|
176
|
+
path: string;
|
|
177
|
+
url?: string;
|
|
178
|
+
} | null;
|
|
174
179
|
}): Promise<void> {
|
|
175
|
-
const { target, fromUser, chatId, isGroup, messageText } = params;
|
|
180
|
+
const { target, fromUser, chatId, isGroup, messageText, media } = params;
|
|
176
181
|
const core = getWecomRuntime();
|
|
177
182
|
const config = target.config;
|
|
178
183
|
const account = target.account;
|
|
@@ -221,6 +226,14 @@ async function startAgentForApp(params: {
|
|
|
221
226
|
OriginatingTo: `wecom:${peerId}`,
|
|
222
227
|
});
|
|
223
228
|
|
|
229
|
+
if (media?.path) {
|
|
230
|
+
ctxPayload.MediaPath = media.path;
|
|
231
|
+
ctxPayload.MediaType = media.type;
|
|
232
|
+
if (media.url) {
|
|
233
|
+
ctxPayload.MediaUrl = media.url;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
224
237
|
await core.channel.session.recordInboundSession({
|
|
225
238
|
storePath,
|
|
226
239
|
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
|
@@ -318,7 +331,7 @@ async function processAppMessage(params: {
|
|
|
318
331
|
if (!fromUser) return;
|
|
319
332
|
|
|
320
333
|
let messageText = "";
|
|
321
|
-
let
|
|
334
|
+
let mediaContext: { type: "image" | "voice" | "video" | "file"; path: string; url?: string } | null = null;
|
|
322
335
|
|
|
323
336
|
if (msgType === "text") {
|
|
324
337
|
messageText = String(msgObj?.Content ?? "");
|
|
@@ -337,17 +350,19 @@ async function processAppMessage(params: {
|
|
|
337
350
|
if (maxBytes && media.buffer.length > maxBytes) {
|
|
338
351
|
messageText = "[语音消息过大,未处理]\n\n请发送更短的语音消息。";
|
|
339
352
|
} else {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
353
|
+
const ext = resolveExtFromContentType(media.contentType, "amr");
|
|
354
|
+
const tempDir = resolveMediaTempDir(target);
|
|
355
|
+
await mkdir(tempDir, { recursive: true });
|
|
356
|
+
await cleanupMediaDir(
|
|
357
|
+
tempDir,
|
|
358
|
+
target.account.config.media?.retentionHours,
|
|
359
|
+
target.account.config.media?.cleanupOnStart,
|
|
360
|
+
);
|
|
361
|
+
const tempVoicePath = join(tempDir, `voice-${Date.now()}-${Math.random().toString(36).slice(2)}.${ext}`);
|
|
362
|
+
await writeFile(tempVoicePath, media.buffer);
|
|
363
|
+
mediaContext = { type: "voice", path: tempVoicePath };
|
|
364
|
+
logVerbose(target, `app voice saved (${media.buffer.length} bytes): ${tempVoicePath}`);
|
|
365
|
+
messageText = `[用户发送了一条语音消息,已保存到: ${tempVoicePath}]\n\n请根据语音内容回复用户。`;
|
|
351
366
|
}
|
|
352
367
|
} catch (err) {
|
|
353
368
|
target.runtime.error?.(`wecom app voice download failed: ${String(err)}`);
|
|
@@ -380,17 +395,19 @@ async function processAppMessage(params: {
|
|
|
380
395
|
if (maxBytes && buffer.length > maxBytes) {
|
|
381
396
|
messageText = "[图片过大,未处理]\n\n请发送更小的图片。";
|
|
382
397
|
} else {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
398
|
+
const ext = resolveExtFromContentType(contentType, "jpg");
|
|
399
|
+
const tempDir = resolveMediaTempDir(target);
|
|
400
|
+
await mkdir(tempDir, { recursive: true });
|
|
401
|
+
await cleanupMediaDir(
|
|
402
|
+
tempDir,
|
|
403
|
+
target.account.config.media?.retentionHours,
|
|
404
|
+
target.account.config.media?.cleanupOnStart,
|
|
405
|
+
);
|
|
406
|
+
const tempImagePath = join(tempDir, `image-${Date.now()}-${Math.random().toString(36).slice(2)}.${ext}`);
|
|
407
|
+
await writeFile(tempImagePath, buffer);
|
|
408
|
+
mediaContext = { type: "image", path: tempImagePath, url: picUrl || undefined };
|
|
409
|
+
logVerbose(target, `app image saved (${buffer.length} bytes): ${tempImagePath}`);
|
|
410
|
+
messageText = `[用户发送了一张图片,已保存到: ${tempImagePath}]\n\n请根据图片内容回复用户。`;
|
|
394
411
|
}
|
|
395
412
|
} else {
|
|
396
413
|
messageText = "[用户发送了一张图片,但下载失败]\n\n请告诉用户图片处理暂时不可用。";
|
|
@@ -417,17 +434,19 @@ async function processAppMessage(params: {
|
|
|
417
434
|
if (maxBytes && media.buffer.length > maxBytes) {
|
|
418
435
|
messageText = "[视频过大,未处理]\n\n请发送更小的视频。";
|
|
419
436
|
} else {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
437
|
+
const ext = resolveExtFromContentType(media.contentType, "mp4");
|
|
438
|
+
const tempDir = resolveMediaTempDir(target);
|
|
439
|
+
await mkdir(tempDir, { recursive: true });
|
|
440
|
+
await cleanupMediaDir(
|
|
441
|
+
tempDir,
|
|
442
|
+
target.account.config.media?.retentionHours,
|
|
443
|
+
target.account.config.media?.cleanupOnStart,
|
|
444
|
+
);
|
|
445
|
+
const tempVideoPath = join(tempDir, `video-${Date.now()}-${Math.random().toString(36).slice(2)}.${ext}`);
|
|
446
|
+
await writeFile(tempVideoPath, media.buffer);
|
|
447
|
+
mediaContext = { type: "video", path: tempVideoPath };
|
|
448
|
+
logVerbose(target, `app video saved (${media.buffer.length} bytes): ${tempVideoPath}`);
|
|
449
|
+
messageText = `[用户发送了一个视频文件,已保存到: ${tempVideoPath}]\n\n请根据视频内容回复用户。`;
|
|
431
450
|
}
|
|
432
451
|
} catch (err) {
|
|
433
452
|
target.runtime.error?.(`wecom app video download failed: ${String(err)}`);
|
|
@@ -446,18 +465,20 @@ async function processAppMessage(params: {
|
|
|
446
465
|
if (maxBytes && media.buffer.length > maxBytes) {
|
|
447
466
|
messageText = "[文件过大,未处理]\n\n请发送更小的文件。";
|
|
448
467
|
} else {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
468
|
+
const ext = fileName.includes(".") ? fileName.split(".").pop() : resolveExtFromContentType(media.contentType, "bin");
|
|
469
|
+
const tempDir = resolveMediaTempDir(target);
|
|
470
|
+
await mkdir(tempDir, { recursive: true });
|
|
471
|
+
await cleanupMediaDir(
|
|
472
|
+
tempDir,
|
|
473
|
+
target.account.config.media?.retentionHours,
|
|
474
|
+
target.account.config.media?.cleanupOnStart,
|
|
475
|
+
);
|
|
476
|
+
const safeName = sanitizeFilename(fileName, `file-${Date.now()}.${ext}`);
|
|
477
|
+
const tempFilePath = join(tempDir, safeName);
|
|
478
|
+
await writeFile(tempFilePath, media.buffer);
|
|
479
|
+
mediaContext = { type: "file", path: tempFilePath };
|
|
480
|
+
logVerbose(target, `app file saved (${media.buffer.length} bytes): ${tempFilePath}`);
|
|
481
|
+
messageText = `[用户发送了一个文件: ${safeName},已保存到: ${tempFilePath}]\n\n请根据文件内容回复用户。`;
|
|
461
482
|
}
|
|
462
483
|
} catch (err) {
|
|
463
484
|
target.runtime.error?.(`wecom app file download failed: ${String(err)}`);
|
|
@@ -490,6 +511,7 @@ async function processAppMessage(params: {
|
|
|
490
511
|
chatId,
|
|
491
512
|
isGroup,
|
|
492
513
|
messageText,
|
|
514
|
+
media: mediaContext,
|
|
493
515
|
});
|
|
494
516
|
} catch (err) {
|
|
495
517
|
target.runtime.error?.(`wecom app agent failed: ${String(err)}`);
|
|
@@ -503,10 +525,6 @@ async function processAppMessage(params: {
|
|
|
503
525
|
} catch {
|
|
504
526
|
// ignore
|
|
505
527
|
}
|
|
506
|
-
} finally {
|
|
507
|
-
if (tempImagePath) {
|
|
508
|
-
unlink(tempImagePath).catch(() => {});
|
|
509
|
-
}
|
|
510
528
|
}
|
|
511
529
|
}
|
|
512
530
|
|