@kyan-du/agent-wechat-openclaw 0.14.4 → 0.14.5
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 +35 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6610,9 +6610,12 @@ function safeInboundFilename(raw) {
|
|
|
6610
6610
|
const cleaned = [...base].filter((char) => char >= " " && char !== "\x7F" && char !== "/" && char !== "\\").slice(0, MAX_FILENAME_CHARS).join("").replace(/^[.\s]+|[.\s]+$/g, "");
|
|
6611
6611
|
return cleaned || void 0;
|
|
6612
6612
|
}
|
|
6613
|
-
var SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set(["pdf"]);
|
|
6613
|
+
var SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set(["pdf", "docx"]);
|
|
6614
6614
|
function documentMimeFromMagic(buf) {
|
|
6615
6615
|
if (buf.length >= 5 && buf.toString("ascii", 0, 5) === "%PDF-") return "application/pdf";
|
|
6616
|
+
if (buf.length >= 4 && buf[0] === 80 && buf[1] === 75 && (buf[2] === 3 || buf[2] === 5 || buf[2] === 7) && buf[3] === 4 && buf.includes(Buffer.from("word/document.xml", "ascii"))) {
|
|
6617
|
+
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
6618
|
+
}
|
|
6616
6619
|
return void 0;
|
|
6617
6620
|
}
|
|
6618
6621
|
function binaryMimeFromMagic(buf) {
|
|
@@ -6868,7 +6871,11 @@ async function validateInboundMedia(result) {
|
|
|
6868
6871
|
if (buffer.length > MAX_FILE_BYTES) return { ok: false, code: "MEDIA_FILE_TOO_LARGE" };
|
|
6869
6872
|
if (!safeInboundFilename(result.filename)) return { ok: false, code: "MEDIA_FILENAME_INVALID" };
|
|
6870
6873
|
if (!SUPPORTED_FILE_FORMATS.has(format)) return { ok: false, code: "MEDIA_FILE_TYPE_MISMATCH" };
|
|
6871
|
-
|
|
6874
|
+
const detected = documentMimeFromMagic(buffer);
|
|
6875
|
+
if (format === "pdf" && detected !== "application/pdf") {
|
|
6876
|
+
return { ok: false, code: "MEDIA_FILE_TYPE_MISMATCH" };
|
|
6877
|
+
}
|
|
6878
|
+
if (format === "docx" && detected !== "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
|
|
6872
6879
|
return { ok: false, code: "MEDIA_FILE_TYPE_MISMATCH" };
|
|
6873
6880
|
}
|
|
6874
6881
|
}
|
|
@@ -6938,9 +6945,10 @@ var RETRYABLE_MEDIA_ERRORS = /* @__PURE__ */ new Set([
|
|
|
6938
6945
|
]);
|
|
6939
6946
|
var DEFAULT_MEDIA_POLL_ATTEMPTS = 6;
|
|
6940
6947
|
var DEFAULT_MEDIA_POLL_INTERVAL_MS = 500;
|
|
6948
|
+
var CHAT_HISTORY_MEDIA_POLL_INTERVAL_MS = 1e3;
|
|
6941
6949
|
var IMAGE_MATERIALIZATION_OPEN_CHAT_TIMEOUT_MS = 400;
|
|
6942
6950
|
var FILE_MATERIALIZATION_CLICK_TIMEOUT_MS = 8e3;
|
|
6943
|
-
var CHAT_HISTORY_MATERIALIZATION_CLICK_TIMEOUT_MS =
|
|
6951
|
+
var CHAT_HISTORY_MATERIALIZATION_CLICK_TIMEOUT_MS = 25e3;
|
|
6944
6952
|
function shouldTriggerMediaMaterialization(result) {
|
|
6945
6953
|
return result.type === "image" || result.type === "file" || result.type === "pending";
|
|
6946
6954
|
}
|
|
@@ -7005,7 +7013,7 @@ function triggerChatHistoryMaterialize(client, chatId, result, attempt, options)
|
|
|
7005
7013
|
}
|
|
7006
7014
|
function createChatHistoryMaterializationTrigger(client, chatId, options) {
|
|
7007
7015
|
return async (result, attempt) => {
|
|
7008
|
-
if (!shouldTriggerChatHistoryMaterialization(result)) return;
|
|
7016
|
+
if (!shouldTriggerChatHistoryMaterialization(result) || !options?.title?.trim()) return;
|
|
7009
7017
|
triggerChatHistoryMaterialize(client, chatId, result, attempt, options);
|
|
7010
7018
|
};
|
|
7011
7019
|
}
|
|
@@ -7021,6 +7029,11 @@ function chatHistoryTitleFromMessage(msg) {
|
|
|
7021
7029
|
function createMediaMaterializationTrigger(client, chatId, options) {
|
|
7022
7030
|
const timeoutMs = options?.timeoutMs ?? IMAGE_MATERIALIZATION_OPEN_CHAT_TIMEOUT_MS;
|
|
7023
7031
|
return async (result, attempt) => {
|
|
7032
|
+
if (result.errorCode === "CHAT_HISTORY_NOT_MATERIALIZED") {
|
|
7033
|
+
if (!options?.title?.trim()) return;
|
|
7034
|
+
triggerChatHistoryMaterialize(client, chatId, result, attempt, options);
|
|
7035
|
+
return;
|
|
7036
|
+
}
|
|
7024
7037
|
if (!shouldTriggerMediaMaterialization(result)) return;
|
|
7025
7038
|
if (isFileMaterialization(result)) {
|
|
7026
7039
|
triggerFileBubbleClick(client, chatId, result, attempt, options);
|
|
@@ -7072,6 +7085,15 @@ function isRetryable(result) {
|
|
|
7072
7085
|
if (result.errorCode !== void 0) return RETRYABLE_MEDIA_ERRORS.has(result.errorCode);
|
|
7073
7086
|
return result.type === "pending";
|
|
7074
7087
|
}
|
|
7088
|
+
function chatHistoryMediaPollWindow(opts) {
|
|
7089
|
+
const intervalMs = Math.max(1, opts?.intervalMs ?? CHAT_HISTORY_MEDIA_POLL_INTERVAL_MS);
|
|
7090
|
+
const timeoutMs = opts?.timeoutMs ?? CHAT_HISTORY_MATERIALIZATION_CLICK_TIMEOUT_MS;
|
|
7091
|
+
const maxAttempts = Math.max(
|
|
7092
|
+
DEFAULT_MEDIA_POLL_ATTEMPTS,
|
|
7093
|
+
Math.ceil(timeoutMs / intervalMs) + 1
|
|
7094
|
+
);
|
|
7095
|
+
return { maxAttempts, intervalMs };
|
|
7096
|
+
}
|
|
7075
7097
|
async function pollMedia(client, chatId, localId, log, maxAttempts = DEFAULT_MEDIA_POLL_ATTEMPTS, intervalMs = DEFAULT_MEDIA_POLL_INTERVAL_MS, onRetryTrigger) {
|
|
7076
7098
|
let lastResult;
|
|
7077
7099
|
let triggerUsed = false;
|
|
@@ -7353,6 +7375,9 @@ function loadFile(path) {
|
|
|
7353
7375
|
function inboundEventId(accountId, chatId, message) {
|
|
7354
7376
|
return createHash("sha256").update(`${accountId}\0${chatId}\0${cursorMessageKey(message)}`).digest("hex");
|
|
7355
7377
|
}
|
|
7378
|
+
function nestedMediaEventId(parentEventId, item) {
|
|
7379
|
+
return createHash("sha256").update(JSON.stringify(["nested-media", parentEventId, item.type ?? "", item.filename ?? ""])).digest("hex");
|
|
7380
|
+
}
|
|
7356
7381
|
var InboundEventLedger = class {
|
|
7357
7382
|
path;
|
|
7358
7383
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -8092,13 +8117,14 @@ async function prepareMessage(client, mediaPipeline, msg, chatId, chat, liveAcco
|
|
|
8092
8117
|
if (mayHaveMedia) {
|
|
8093
8118
|
log?.info?.(`[wechat:${liveAccount.accountId}] Checking media for msg ${msg.localId} (type ${baseType})`);
|
|
8094
8119
|
try {
|
|
8120
|
+
const chatHistoryPoll = isChatHistory ? chatHistoryMediaPollWindow() : void 0;
|
|
8095
8121
|
const result = await pollMedia(
|
|
8096
8122
|
client,
|
|
8097
8123
|
chatId,
|
|
8098
8124
|
msg.localId,
|
|
8099
8125
|
log,
|
|
8100
|
-
|
|
8101
|
-
|
|
8126
|
+
chatHistoryPoll?.maxAttempts,
|
|
8127
|
+
chatHistoryPoll?.intervalMs,
|
|
8102
8128
|
mediaMaterializationTriggerForMessage({
|
|
8103
8129
|
client,
|
|
8104
8130
|
chatId,
|
|
@@ -8112,19 +8138,16 @@ async function prepareMessage(client, mediaPipeline, msg, chatId, chat, liveAcco
|
|
|
8112
8138
|
})
|
|
8113
8139
|
);
|
|
8114
8140
|
const nestedItems = nestedChatHistoryMedia(result);
|
|
8115
|
-
if (
|
|
8141
|
+
if (nestedItems.length > 0) {
|
|
8116
8142
|
const savedPaths = [];
|
|
8117
8143
|
const savedMimes = [];
|
|
8118
8144
|
for (const [index, item] of nestedItems.entries()) {
|
|
8119
8145
|
const saved = await mediaPipeline.process(
|
|
8120
8146
|
item,
|
|
8121
8147
|
{
|
|
8122
|
-
eventId: inboundEventId(liveAccount.accountId, chatId,
|
|
8123
|
-
...msg,
|
|
8124
|
-
localId: msg.localId * 1e3 + index
|
|
8125
|
-
}),
|
|
8148
|
+
eventId: nestedMediaEventId(inboundEventId(liveAccount.accountId, chatId, msg), item),
|
|
8126
8149
|
chatId,
|
|
8127
|
-
localId: msg.localId
|
|
8150
|
+
localId: msg.localId
|
|
8128
8151
|
},
|
|
8129
8152
|
async (buffer, mime, filename) => core.channel.media.saveMediaBuffer(buffer, mime, "inbound", void 0, filename),
|
|
8130
8153
|
async (buffer, mime, filename) => core.channel.media.saveMediaBuffer(buffer, mime, "inbound", void 0, filename),
|