@tencent-weixin/openclaw-weixin 2.3.1 → 2.4.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/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api/api.js +375 -0
- package/dist/src/api/api.js.map +1 -0
- package/dist/src/api/config-cache.js +64 -0
- package/dist/src/api/config-cache.js.map +1 -0
- package/dist/src/api/session-guard.js +49 -0
- package/dist/src/api/session-guard.js.map +1 -0
- package/dist/src/api/types.js +35 -0
- package/dist/src/api/types.js.map +1 -0
- package/dist/src/auth/accounts.js +326 -0
- package/dist/src/auth/accounts.js.map +1 -0
- package/dist/src/auth/login-qr.js +332 -0
- package/dist/src/auth/login-qr.js.map +1 -0
- package/dist/src/auth/pairing.js +104 -0
- package/dist/src/auth/pairing.js.map +1 -0
- package/dist/src/cdn/aes-ecb.js +19 -0
- package/dist/src/cdn/aes-ecb.js.map +1 -0
- package/dist/src/cdn/cdn-upload.js +73 -0
- package/dist/src/cdn/cdn-upload.js.map +1 -0
- package/dist/src/cdn/cdn-url.js +14 -0
- package/dist/src/cdn/cdn-url.js.map +1 -0
- package/dist/src/cdn/pic-decrypt.js +89 -0
- package/dist/src/cdn/pic-decrypt.js.map +1 -0
- package/dist/src/cdn/upload.js +106 -0
- package/dist/src/cdn/upload.js.map +1 -0
- package/dist/src/channel.js +449 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/compat.js +67 -0
- package/dist/src/compat.js.map +1 -0
- package/dist/src/config/config-schema.js +19 -0
- package/dist/src/config/config-schema.js.map +1 -0
- package/dist/src/media/media-download.js +95 -0
- package/dist/src/media/media-download.js.map +1 -0
- package/dist/src/media/mime.js +73 -0
- package/dist/src/media/mime.js.map +1 -0
- package/dist/src/media/silk-transcode.js +64 -0
- package/dist/src/media/silk-transcode.js.map +1 -0
- package/dist/src/messaging/debug-mode.js +63 -0
- package/dist/src/messaging/debug-mode.js.map +1 -0
- package/dist/src/messaging/error-notice.js +24 -0
- package/dist/src/messaging/error-notice.js.map +1 -0
- package/dist/src/messaging/inbound.js +201 -0
- package/dist/src/messaging/inbound.js.map +1 -0
- package/dist/src/messaging/markdown-filter.js +368 -0
- package/dist/src/messaging/markdown-filter.js.map +1 -0
- package/dist/src/messaging/outbound-hooks.js +56 -0
- package/dist/src/messaging/outbound-hooks.js.map +1 -0
- package/dist/src/messaging/process-message.js +381 -0
- package/dist/src/messaging/process-message.js.map +1 -0
- package/dist/src/messaging/send-media.js +54 -0
- package/dist/src/messaging/send-media.js.map +1 -0
- package/dist/src/messaging/send.js +182 -0
- package/dist/src/messaging/send.js.map +1 -0
- package/dist/src/messaging/slash-commands.js +70 -0
- package/dist/src/messaging/slash-commands.js.map +1 -0
- package/dist/src/monitor/monitor.js +150 -0
- package/dist/src/monitor/monitor.js.map +1 -0
- package/dist/src/runtime.js +54 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/storage/state-dir.js +9 -0
- package/dist/src/storage/state-dir.js.map +1 -0
- package/dist/src/storage/sync-buf.js +64 -0
- package/dist/src/storage/sync-buf.js.map +1 -0
- package/dist/src/util/logger.js +120 -0
- package/dist/src/util/logger.js.map +1 -0
- package/dist/src/util/random.js +16 -0
- package/dist/src/util/random.js.map +1 -0
- package/dist/src/util/redact.js +54 -0
- package/dist/src/util/redact.js.map +1 -0
- package/openclaw.plugin.json +11 -1
- package/package.json +9 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { logger } from "../util/logger.js";
|
|
2
|
+
import { getMimeFromFilename } from "./mime.js";
|
|
3
|
+
import { downloadAndDecryptBuffer, downloadPlainCdnBuffer, } from "../cdn/pic-decrypt.js";
|
|
4
|
+
import { silkToWav } from "./silk-transcode.js";
|
|
5
|
+
import { MessageItemType } from "../api/types.js";
|
|
6
|
+
const WEIXIN_MEDIA_MAX_BYTES = 100 * 1024 * 1024;
|
|
7
|
+
/**
|
|
8
|
+
* Download and decrypt media from a single MessageItem.
|
|
9
|
+
* Returns the populated WeixinInboundMediaOpts fields; empty object on unsupported type or failure.
|
|
10
|
+
*/
|
|
11
|
+
export async function downloadMediaFromItem(item, deps) {
|
|
12
|
+
const { cdnBaseUrl, saveMedia, log, errLog, label } = deps;
|
|
13
|
+
const result = {};
|
|
14
|
+
if (item.type === MessageItemType.IMAGE) {
|
|
15
|
+
const img = item.image_item;
|
|
16
|
+
if (!img?.media?.encrypt_query_param && !img?.media?.full_url)
|
|
17
|
+
return result;
|
|
18
|
+
const aesKeyBase64 = img.aeskey
|
|
19
|
+
? Buffer.from(img.aeskey, "hex").toString("base64")
|
|
20
|
+
: img.media.aes_key;
|
|
21
|
+
logger.debug(`${label} image: encrypt_query_param=${(img.media.encrypt_query_param ?? "").slice(0, 40)}... hasAesKey=${Boolean(aesKeyBase64)} aeskeySource=${img.aeskey ? "image_item.aeskey" : "media.aes_key"} full_url=${Boolean(img.media.full_url)}`);
|
|
22
|
+
try {
|
|
23
|
+
const buf = aesKeyBase64
|
|
24
|
+
? await downloadAndDecryptBuffer(img.media.encrypt_query_param ?? "", aesKeyBase64, cdnBaseUrl, `${label} image`, img.media.full_url)
|
|
25
|
+
: await downloadPlainCdnBuffer(img.media.encrypt_query_param ?? "", cdnBaseUrl, `${label} image-plain`, img.media.full_url);
|
|
26
|
+
const saved = await saveMedia(buf, undefined, "inbound", WEIXIN_MEDIA_MAX_BYTES);
|
|
27
|
+
result.decryptedPicPath = saved.path;
|
|
28
|
+
logger.debug(`${label} image saved: ${saved.path}`);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
logger.error(`${label} image download/decrypt failed: ${String(err)}`);
|
|
32
|
+
errLog(`weixin ${label} image download/decrypt failed: ${String(err)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (item.type === MessageItemType.VOICE) {
|
|
36
|
+
const voice = item.voice_item;
|
|
37
|
+
if ((!voice?.media?.encrypt_query_param && !voice?.media?.full_url) || !voice?.media?.aes_key)
|
|
38
|
+
return result;
|
|
39
|
+
try {
|
|
40
|
+
const silkBuf = await downloadAndDecryptBuffer(voice.media.encrypt_query_param ?? "", voice.media.aes_key, cdnBaseUrl, `${label} voice`, voice.media.full_url);
|
|
41
|
+
logger.debug(`${label} voice: decrypted ${silkBuf.length} bytes, attempting silk transcode`);
|
|
42
|
+
const wavBuf = await silkToWav(silkBuf);
|
|
43
|
+
if (wavBuf) {
|
|
44
|
+
const saved = await saveMedia(wavBuf, "audio/wav", "inbound", WEIXIN_MEDIA_MAX_BYTES);
|
|
45
|
+
result.decryptedVoicePath = saved.path;
|
|
46
|
+
result.voiceMediaType = "audio/wav";
|
|
47
|
+
logger.debug(`${label} voice: saved WAV to ${saved.path}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const saved = await saveMedia(silkBuf, "audio/silk", "inbound", WEIXIN_MEDIA_MAX_BYTES);
|
|
51
|
+
result.decryptedVoicePath = saved.path;
|
|
52
|
+
result.voiceMediaType = "audio/silk";
|
|
53
|
+
logger.debug(`${label} voice: silk transcode unavailable, saved raw SILK to ${saved.path}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
logger.error(`${label} voice download/transcode failed: ${String(err)}`);
|
|
58
|
+
errLog(`weixin ${label} voice download/transcode failed: ${String(err)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (item.type === MessageItemType.FILE) {
|
|
62
|
+
const fileItem = item.file_item;
|
|
63
|
+
if ((!fileItem?.media?.encrypt_query_param && !fileItem?.media?.full_url) || !fileItem?.media?.aes_key)
|
|
64
|
+
return result;
|
|
65
|
+
try {
|
|
66
|
+
const buf = await downloadAndDecryptBuffer(fileItem.media.encrypt_query_param ?? "", fileItem.media.aes_key, cdnBaseUrl, `${label} file`, fileItem.media.full_url);
|
|
67
|
+
const mime = getMimeFromFilename(fileItem.file_name ?? "file.bin");
|
|
68
|
+
const saved = await saveMedia(buf, mime, "inbound", WEIXIN_MEDIA_MAX_BYTES, fileItem.file_name ?? undefined);
|
|
69
|
+
result.decryptedFilePath = saved.path;
|
|
70
|
+
result.fileMediaType = mime;
|
|
71
|
+
logger.debug(`${label} file: saved to ${saved.path} mime=${mime}`);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.error(`${label} file download failed: ${String(err)}`);
|
|
75
|
+
errLog(`weixin ${label} file download failed: ${String(err)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (item.type === MessageItemType.VIDEO) {
|
|
79
|
+
const videoItem = item.video_item;
|
|
80
|
+
if ((!videoItem?.media?.encrypt_query_param && !videoItem?.media?.full_url) || !videoItem?.media?.aes_key)
|
|
81
|
+
return result;
|
|
82
|
+
try {
|
|
83
|
+
const buf = await downloadAndDecryptBuffer(videoItem.media.encrypt_query_param ?? "", videoItem.media.aes_key, cdnBaseUrl, `${label} video`, videoItem.media.full_url);
|
|
84
|
+
const saved = await saveMedia(buf, "video/mp4", "inbound", WEIXIN_MEDIA_MAX_BYTES);
|
|
85
|
+
result.decryptedVideoPath = saved.path;
|
|
86
|
+
logger.debug(`${label} video: saved to ${saved.path}`);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
logger.error(`${label} video download failed: ${String(err)}`);
|
|
90
|
+
errLog(`weixin ${label} video download failed: ${String(err)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=media-download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-download.js","sourceRoot":"","sources":["../../../src/media/media-download.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
const EXTENSION_TO_MIME = {
|
|
3
|
+
".pdf": "application/pdf",
|
|
4
|
+
".doc": "application/msword",
|
|
5
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
6
|
+
".xls": "application/vnd.ms-excel",
|
|
7
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
8
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
9
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
10
|
+
".txt": "text/plain",
|
|
11
|
+
".csv": "text/csv",
|
|
12
|
+
".zip": "application/zip",
|
|
13
|
+
".tar": "application/x-tar",
|
|
14
|
+
".gz": "application/gzip",
|
|
15
|
+
".mp3": "audio/mpeg",
|
|
16
|
+
".ogg": "audio/ogg",
|
|
17
|
+
".wav": "audio/wav",
|
|
18
|
+
".mp4": "video/mp4",
|
|
19
|
+
".mov": "video/quicktime",
|
|
20
|
+
".webm": "video/webm",
|
|
21
|
+
".mkv": "video/x-matroska",
|
|
22
|
+
".avi": "video/x-msvideo",
|
|
23
|
+
".png": "image/png",
|
|
24
|
+
".jpg": "image/jpeg",
|
|
25
|
+
".jpeg": "image/jpeg",
|
|
26
|
+
".gif": "image/gif",
|
|
27
|
+
".webp": "image/webp",
|
|
28
|
+
".bmp": "image/bmp",
|
|
29
|
+
};
|
|
30
|
+
const MIME_TO_EXTENSION = {
|
|
31
|
+
"image/jpeg": ".jpg",
|
|
32
|
+
"image/jpg": ".jpg",
|
|
33
|
+
"image/png": ".png",
|
|
34
|
+
"image/gif": ".gif",
|
|
35
|
+
"image/webp": ".webp",
|
|
36
|
+
"image/bmp": ".bmp",
|
|
37
|
+
"video/mp4": ".mp4",
|
|
38
|
+
"video/quicktime": ".mov",
|
|
39
|
+
"video/webm": ".webm",
|
|
40
|
+
"video/x-matroska": ".mkv",
|
|
41
|
+
"video/x-msvideo": ".avi",
|
|
42
|
+
"audio/mpeg": ".mp3",
|
|
43
|
+
"audio/ogg": ".ogg",
|
|
44
|
+
"audio/wav": ".wav",
|
|
45
|
+
"application/pdf": ".pdf",
|
|
46
|
+
"application/zip": ".zip",
|
|
47
|
+
"application/x-tar": ".tar",
|
|
48
|
+
"application/gzip": ".gz",
|
|
49
|
+
"text/plain": ".txt",
|
|
50
|
+
"text/csv": ".csv",
|
|
51
|
+
};
|
|
52
|
+
/** Get MIME type from filename extension. Returns "application/octet-stream" for unknown extensions. */
|
|
53
|
+
export function getMimeFromFilename(filename) {
|
|
54
|
+
const ext = path.extname(filename).toLowerCase();
|
|
55
|
+
return EXTENSION_TO_MIME[ext] ?? "application/octet-stream";
|
|
56
|
+
}
|
|
57
|
+
/** Get file extension from MIME type. Returns ".bin" for unknown types. */
|
|
58
|
+
export function getExtensionFromMime(mimeType) {
|
|
59
|
+
const ct = mimeType.split(";")[0].trim().toLowerCase();
|
|
60
|
+
return MIME_TO_EXTENSION[ct] ?? ".bin";
|
|
61
|
+
}
|
|
62
|
+
/** Get file extension from Content-Type header or URL path. Returns ".bin" for unknown. */
|
|
63
|
+
export function getExtensionFromContentTypeOrUrl(contentType, url) {
|
|
64
|
+
if (contentType) {
|
|
65
|
+
const ext = getExtensionFromMime(contentType);
|
|
66
|
+
if (ext !== ".bin")
|
|
67
|
+
return ext;
|
|
68
|
+
}
|
|
69
|
+
const ext = path.extname(new URL(url).pathname).toLowerCase();
|
|
70
|
+
const knownExts = new Set(Object.keys(EXTENSION_TO_MIME));
|
|
71
|
+
return knownExts.has(ext) ? ext : ".bin";
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=mime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mime.js","sourceRoot":"","sources":["../../../src/media/mime.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,iBAAiB,GAA2B;IAChD,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,oBAAoB;IAC5B,OAAO,EAAE,yEAAyE;IAClF,MAAM,EAAE,0BAA0B;IAClC,OAAO,EAAE,mEAAmE;IAC5E,MAAM,EAAE,+BAA+B;IACvC,OAAO,EAAE,2EAA2E;IACpF,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,kBAAkB;IACzB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,iBAAiB;IACzB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,kBAAkB;IAC1B,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF,MAAM,iBAAiB,GAA2B;IAChD,YAAY,EAAE,MAAM;IACpB,WAAW,EAAE,MAAM;IACnB,WAAW,EAAE,MAAM;IACnB,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,OAAO;IACrB,WAAW,EAAE,MAAM;IACnB,WAAW,EAAE,MAAM;IACnB,iBAAiB,EAAE,MAAM;IACzB,YAAY,EAAE,OAAO;IACrB,kBAAkB,EAAE,MAAM;IAC1B,iBAAiB,EAAE,MAAM;IACzB,YAAY,EAAE,MAAM;IACpB,WAAW,EAAE,MAAM;IACnB,WAAW,EAAE,MAAM;IACnB,iBAAiB,EAAE,MAAM;IACzB,iBAAiB,EAAE,MAAM;IACzB,mBAAmB,EAAE,MAAM;IAC3B,kBAAkB,EAAE,KAAK;IACzB,YAAY,EAAE,MAAM;IACpB,UAAU,EAAE,MAAM;CACnB,CAAC;AAEF,wGAAwG;AACxG,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,OAAO,iBAAiB,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AAC9D,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvD,OAAO,iBAAiB,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC;AACzC,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,gCAAgC,CAAC,WAA0B,EAAE,GAAW;IACtF,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,GAAG,CAAC;IACjC,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC1D,OAAO,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { logger } from "../util/logger.js";
|
|
2
|
+
/** Default sample rate for Weixin voice messages. */
|
|
3
|
+
const SILK_SAMPLE_RATE = 24_000;
|
|
4
|
+
/**
|
|
5
|
+
* Wrap raw pcm_s16le bytes in a WAV container.
|
|
6
|
+
* Mono channel, 16-bit signed little-endian.
|
|
7
|
+
*/
|
|
8
|
+
function pcmBytesToWav(pcm, sampleRate) {
|
|
9
|
+
const pcmBytes = pcm.byteLength;
|
|
10
|
+
const totalSize = 44 + pcmBytes;
|
|
11
|
+
const buf = Buffer.allocUnsafe(totalSize);
|
|
12
|
+
let offset = 0;
|
|
13
|
+
buf.write("RIFF", offset);
|
|
14
|
+
offset += 4;
|
|
15
|
+
buf.writeUInt32LE(totalSize - 8, offset);
|
|
16
|
+
offset += 4;
|
|
17
|
+
buf.write("WAVE", offset);
|
|
18
|
+
offset += 4;
|
|
19
|
+
buf.write("fmt ", offset);
|
|
20
|
+
offset += 4;
|
|
21
|
+
buf.writeUInt32LE(16, offset);
|
|
22
|
+
offset += 4; // fmt chunk size
|
|
23
|
+
buf.writeUInt16LE(1, offset);
|
|
24
|
+
offset += 2; // PCM format
|
|
25
|
+
buf.writeUInt16LE(1, offset);
|
|
26
|
+
offset += 2; // mono
|
|
27
|
+
buf.writeUInt32LE(sampleRate, offset);
|
|
28
|
+
offset += 4;
|
|
29
|
+
buf.writeUInt32LE(sampleRate * 2, offset);
|
|
30
|
+
offset += 4; // byte rate (mono 16-bit)
|
|
31
|
+
buf.writeUInt16LE(2, offset);
|
|
32
|
+
offset += 2; // block align
|
|
33
|
+
buf.writeUInt16LE(16, offset);
|
|
34
|
+
offset += 2; // bits per sample
|
|
35
|
+
buf.write("data", offset);
|
|
36
|
+
offset += 4;
|
|
37
|
+
buf.writeUInt32LE(pcmBytes, offset);
|
|
38
|
+
offset += 4;
|
|
39
|
+
Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength).copy(buf, offset);
|
|
40
|
+
return buf;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Try to transcode a SILK audio buffer to WAV using silk-wasm.
|
|
44
|
+
* silk-wasm's decode() returns { data: Uint8Array (pcm_s16le), duration: number }.
|
|
45
|
+
*
|
|
46
|
+
* Returns a WAV Buffer on success, or null if silk-wasm is unavailable or decoding fails.
|
|
47
|
+
* Callers should fall back to passing the raw SILK file when null is returned.
|
|
48
|
+
*/
|
|
49
|
+
export async function silkToWav(silkBuf) {
|
|
50
|
+
try {
|
|
51
|
+
const { decode } = await import("silk-wasm");
|
|
52
|
+
logger.debug(`silkToWav: decoding ${silkBuf.length} bytes of SILK`);
|
|
53
|
+
const result = await decode(silkBuf, SILK_SAMPLE_RATE);
|
|
54
|
+
logger.debug(`silkToWav: decoded duration=${result.duration}ms pcmBytes=${result.data.byteLength}`);
|
|
55
|
+
const wav = pcmBytesToWav(result.data, SILK_SAMPLE_RATE);
|
|
56
|
+
logger.debug(`silkToWav: WAV size=${wav.length}`);
|
|
57
|
+
return wav;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
logger.warn(`silkToWav: transcode failed, will use raw silk err=${String(err)}`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=silk-transcode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"silk-transcode.js","sourceRoot":"","sources":["../../../src/media/silk-transcode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-bot debug mode toggle, persisted to disk so it survives gateway restarts.
|
|
3
|
+
*
|
|
4
|
+
* State file: `<stateDir>/openclaw-weixin/debug-mode.json`
|
|
5
|
+
* Format: `{ "accounts": { "<accountId>": true, ... } }`
|
|
6
|
+
*
|
|
7
|
+
* When enabled, processOneMessage appends a timing summary after each
|
|
8
|
+
* AI reply is delivered to the user.
|
|
9
|
+
*/
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { resolveStateDir } from "../storage/state-dir.js";
|
|
13
|
+
import { logger } from "../util/logger.js";
|
|
14
|
+
function resolveDebugModePath() {
|
|
15
|
+
return path.join(resolveStateDir(), "openclaw-weixin", "debug-mode.json");
|
|
16
|
+
}
|
|
17
|
+
function loadState() {
|
|
18
|
+
try {
|
|
19
|
+
const raw = fs.readFileSync(resolveDebugModePath(), "utf-8");
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
if (parsed && typeof parsed.accounts === "object")
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// missing or corrupt — start fresh
|
|
26
|
+
}
|
|
27
|
+
return { accounts: {} };
|
|
28
|
+
}
|
|
29
|
+
function saveState(state) {
|
|
30
|
+
const filePath = resolveDebugModePath();
|
|
31
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
32
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
33
|
+
}
|
|
34
|
+
/** Toggle debug mode for a bot account. Returns the new state. */
|
|
35
|
+
export function toggleDebugMode(accountId) {
|
|
36
|
+
const state = loadState();
|
|
37
|
+
const next = !state.accounts[accountId];
|
|
38
|
+
state.accounts[accountId] = next;
|
|
39
|
+
try {
|
|
40
|
+
saveState(state);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
logger.error(`debug-mode: failed to persist state: ${String(err)}`);
|
|
44
|
+
}
|
|
45
|
+
return next;
|
|
46
|
+
}
|
|
47
|
+
/** Check whether debug mode is active for a bot account. */
|
|
48
|
+
export function isDebugMode(accountId) {
|
|
49
|
+
return loadState().accounts[accountId] === true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Reset internal state — only for tests.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export function _resetForTest() {
|
|
56
|
+
try {
|
|
57
|
+
fs.unlinkSync(resolveDebugModePath());
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// ignore if not present
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=debug-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-mode.js","sourceRoot":"","sources":["../../../src/messaging/debug-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAM3C,SAAS,oBAAoB;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,EAAE,OAAO,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QACjD,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAqB;IACtC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACxC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,SAAS,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { logger } from "../util/logger.js";
|
|
2
|
+
import { sendMessageWeixin } from "./send.js";
|
|
3
|
+
/**
|
|
4
|
+
* Send a plain-text error notice back to the user.
|
|
5
|
+
* Fire-and-forget: errors are logged but never thrown, so callers stay unaffected.
|
|
6
|
+
* No-op when contextToken is absent (we have no conversation reference to reply into).
|
|
7
|
+
*/
|
|
8
|
+
export async function sendWeixinErrorNotice(params) {
|
|
9
|
+
if (!params.contextToken) {
|
|
10
|
+
logger.warn(`sendWeixinErrorNotice: no contextToken for to=${params.to}, sending without context`);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
await sendMessageWeixin({ to: params.to, text: params.message, opts: {
|
|
14
|
+
baseUrl: params.baseUrl,
|
|
15
|
+
token: params.token,
|
|
16
|
+
contextToken: params.contextToken,
|
|
17
|
+
} });
|
|
18
|
+
logger.debug(`sendWeixinErrorNotice: sent to=${params.to}`);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
params.errLog(`[weixin] sendWeixinErrorNotice failed to=${params.to}: ${String(err)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=error-notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-notice.js","sourceRoot":"","sources":["../../../src/messaging/error-notice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAO3C;IACC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,iDAAiD,MAAM,CAAC,EAAE,2BAA2B,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,CAAC;QACH,MAAM,iBAAiB,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE;gBACnE,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,YAAY,EAAE,MAAM,CAAC,YAAY;aAClC,EAAC,CAAC,CAAC;QACJ,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,MAAM,CAAC,4CAA4C,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { logger } from "../util/logger.js";
|
|
4
|
+
import { generateId } from "../util/random.js";
|
|
5
|
+
import { MessageItemType } from "../api/types.js";
|
|
6
|
+
import { resolveStateDir } from "../storage/state-dir.js";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Context token store (in-process cache + disk persistence)
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* contextToken is issued per-message by the Weixin getupdates API and must
|
|
12
|
+
* be echoed verbatim in every outbound send. The in-memory map is the primary
|
|
13
|
+
* lookup; a disk-backed file per account ensures tokens survive gateway restarts.
|
|
14
|
+
*/
|
|
15
|
+
const contextTokenStore = new Map();
|
|
16
|
+
function contextTokenKey(accountId, userId) {
|
|
17
|
+
return `${accountId}:${userId}`;
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Disk persistence helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function resolveContextTokenFilePath(accountId) {
|
|
23
|
+
return path.join(resolveStateDir(), "openclaw-weixin", "accounts", `${accountId}.context-tokens.json`);
|
|
24
|
+
}
|
|
25
|
+
/** Persist all context tokens for a given account to disk. */
|
|
26
|
+
function persistContextTokens(accountId) {
|
|
27
|
+
const prefix = `${accountId}:`;
|
|
28
|
+
const tokens = {};
|
|
29
|
+
for (const [k, v] of contextTokenStore) {
|
|
30
|
+
if (k.startsWith(prefix)) {
|
|
31
|
+
tokens[k.slice(prefix.length)] = v;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const filePath = resolveContextTokenFilePath(accountId);
|
|
35
|
+
try {
|
|
36
|
+
const dir = path.dirname(filePath);
|
|
37
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
38
|
+
fs.writeFileSync(filePath, JSON.stringify(tokens, null, 0), "utf-8");
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
logger.warn(`persistContextTokens: failed to write ${filePath}: ${String(err)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Restore persisted context tokens for an account into the in-memory map.
|
|
46
|
+
* Called once during gateway startAccount to survive restarts.
|
|
47
|
+
*/
|
|
48
|
+
export function restoreContextTokens(accountId) {
|
|
49
|
+
const filePath = resolveContextTokenFilePath(accountId);
|
|
50
|
+
try {
|
|
51
|
+
if (!fs.existsSync(filePath))
|
|
52
|
+
return;
|
|
53
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
54
|
+
const tokens = JSON.parse(raw);
|
|
55
|
+
let count = 0;
|
|
56
|
+
for (const [userId, token] of Object.entries(tokens)) {
|
|
57
|
+
if (typeof token === "string" && token) {
|
|
58
|
+
contextTokenStore.set(contextTokenKey(accountId, userId), token);
|
|
59
|
+
count++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
logger.info(`restoreContextTokens: restored ${count} tokens for account=${accountId}`);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
logger.warn(`restoreContextTokens: failed to read ${filePath}: ${String(err)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Remove all context tokens for a given account (memory + disk). */
|
|
69
|
+
export function clearContextTokensForAccount(accountId) {
|
|
70
|
+
const prefix = `${accountId}:`;
|
|
71
|
+
for (const k of [...contextTokenStore.keys()]) {
|
|
72
|
+
if (k.startsWith(prefix)) {
|
|
73
|
+
contextTokenStore.delete(k);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const filePath = resolveContextTokenFilePath(accountId);
|
|
77
|
+
try {
|
|
78
|
+
if (fs.existsSync(filePath))
|
|
79
|
+
fs.unlinkSync(filePath);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.warn(`clearContextTokensForAccount: failed to remove ${filePath}: ${String(err)}`);
|
|
83
|
+
}
|
|
84
|
+
logger.info(`clearContextTokensForAccount: cleared tokens for account=${accountId}`);
|
|
85
|
+
}
|
|
86
|
+
/** Store a context token for a given account+user pair (memory + disk). */
|
|
87
|
+
export function setContextToken(accountId, userId, token) {
|
|
88
|
+
const k = contextTokenKey(accountId, userId);
|
|
89
|
+
logger.debug(`setContextToken: key=${k}`);
|
|
90
|
+
contextTokenStore.set(k, token);
|
|
91
|
+
persistContextTokens(accountId);
|
|
92
|
+
}
|
|
93
|
+
/** Retrieve the cached context token for a given account+user pair. */
|
|
94
|
+
export function getContextToken(accountId, userId) {
|
|
95
|
+
const k = contextTokenKey(accountId, userId);
|
|
96
|
+
const val = contextTokenStore.get(k);
|
|
97
|
+
logger.debug(`getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`);
|
|
98
|
+
return val;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Find all accountIds that have an active contextToken for the given userId.
|
|
102
|
+
* Used to infer the sending bot account from the recipient address when
|
|
103
|
+
* accountId is not explicitly provided (e.g. cron delivery).
|
|
104
|
+
*
|
|
105
|
+
* Returns all matching accountIds (not just the first) so the caller can
|
|
106
|
+
* detect ambiguity when multiple accounts have sessions with the same user.
|
|
107
|
+
*/
|
|
108
|
+
export function findAccountIdsByContextToken(accountIds, userId) {
|
|
109
|
+
return accountIds.filter((id) => contextTokenStore.has(contextTokenKey(id, userId)));
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Message ID generation
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
function generateMessageSid() {
|
|
115
|
+
return generateId("openclaw-weixin");
|
|
116
|
+
}
|
|
117
|
+
/** Returns true if the message item is a media type (image, video, file, or voice). */
|
|
118
|
+
export function isMediaItem(item) {
|
|
119
|
+
return (item.type === MessageItemType.IMAGE ||
|
|
120
|
+
item.type === MessageItemType.VIDEO ||
|
|
121
|
+
item.type === MessageItemType.FILE ||
|
|
122
|
+
item.type === MessageItemType.VOICE);
|
|
123
|
+
}
|
|
124
|
+
function bodyFromItemList(itemList) {
|
|
125
|
+
if (!itemList?.length)
|
|
126
|
+
return "";
|
|
127
|
+
for (const item of itemList) {
|
|
128
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
|
|
129
|
+
const text = String(item.text_item.text);
|
|
130
|
+
const ref = item.ref_msg;
|
|
131
|
+
if (!ref)
|
|
132
|
+
return text;
|
|
133
|
+
// Quoted media is passed as MediaPath; only include the current text as body.
|
|
134
|
+
if (ref.message_item && isMediaItem(ref.message_item))
|
|
135
|
+
return text;
|
|
136
|
+
// Build quoted context from both title and message_item content.
|
|
137
|
+
const parts = [];
|
|
138
|
+
if (ref.title)
|
|
139
|
+
parts.push(ref.title);
|
|
140
|
+
if (ref.message_item) {
|
|
141
|
+
const refBody = bodyFromItemList([ref.message_item]);
|
|
142
|
+
if (refBody)
|
|
143
|
+
parts.push(refBody);
|
|
144
|
+
}
|
|
145
|
+
if (!parts.length)
|
|
146
|
+
return text;
|
|
147
|
+
return `[引用: ${parts.join(" | ")}]\n${text}`;
|
|
148
|
+
}
|
|
149
|
+
// 语音转文字:如果语音消息有 text 字段,直接使用文字内容
|
|
150
|
+
if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
151
|
+
return item.voice_item.text;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Convert a WeixinMessage from getUpdates to the inbound MsgContext for the core pipeline.
|
|
158
|
+
* Media: only pass MediaPath (local file, after CDN download + decrypt).
|
|
159
|
+
* We never pass MediaUrl — the upstream CDN URL is encrypted/auth-only.
|
|
160
|
+
* Priority when multiple media types present: image > video > file > voice.
|
|
161
|
+
*/
|
|
162
|
+
export function weixinMessageToMsgContext(msg, accountId, opts) {
|
|
163
|
+
const from_user_id = msg.from_user_id ?? "";
|
|
164
|
+
const ctx = {
|
|
165
|
+
Body: bodyFromItemList(msg.item_list),
|
|
166
|
+
From: from_user_id,
|
|
167
|
+
To: from_user_id,
|
|
168
|
+
AccountId: accountId,
|
|
169
|
+
OriginatingChannel: "openclaw-weixin",
|
|
170
|
+
OriginatingTo: from_user_id,
|
|
171
|
+
MessageSid: generateMessageSid(),
|
|
172
|
+
Timestamp: msg.create_time_ms,
|
|
173
|
+
Provider: "openclaw-weixin",
|
|
174
|
+
ChatType: "direct",
|
|
175
|
+
};
|
|
176
|
+
if (msg.context_token) {
|
|
177
|
+
ctx.context_token = msg.context_token;
|
|
178
|
+
}
|
|
179
|
+
if (opts?.decryptedPicPath) {
|
|
180
|
+
ctx.MediaPath = opts.decryptedPicPath;
|
|
181
|
+
ctx.MediaType = "image/*";
|
|
182
|
+
}
|
|
183
|
+
else if (opts?.decryptedVideoPath) {
|
|
184
|
+
ctx.MediaPath = opts.decryptedVideoPath;
|
|
185
|
+
ctx.MediaType = "video/mp4";
|
|
186
|
+
}
|
|
187
|
+
else if (opts?.decryptedFilePath) {
|
|
188
|
+
ctx.MediaPath = opts.decryptedFilePath;
|
|
189
|
+
ctx.MediaType = opts.fileMediaType ?? "application/octet-stream";
|
|
190
|
+
}
|
|
191
|
+
else if (opts?.decryptedVoicePath) {
|
|
192
|
+
ctx.MediaPath = opts.decryptedVoicePath;
|
|
193
|
+
ctx.MediaType = opts.voiceMediaType ?? "audio/wav";
|
|
194
|
+
}
|
|
195
|
+
return ctx;
|
|
196
|
+
}
|
|
197
|
+
/** Extract the context_token from an inbound WeixinMsgContext. */
|
|
198
|
+
export function getContextTokenFromMsgContext(ctx) {
|
|
199
|
+
return ctx.context_token;
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=inbound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/messaging/inbound.ts"],"names":[],"mappings":"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"}
|