




















@@ -60,6 +60,7 @@ import {
6060resolveBlueBubblesMessageId,
6161resolveReplyContextFromCache,
6262} from "./monitor-reply-cache.js";
63+import { fetchBlueBubblesReplyContext } from "./monitor-reply-fetch.js";
6364import {
6465hasBlueBubblesSelfChatCopy,
6566rememberBlueBubblesSelfChatCopy,
@@ -1235,11 +1236,17 @@ async function processMessageAfterDedupe(
12351236mediaTypes.push(saved.contentType);
12361237}
12371238} catch (err) {
1238-logVerbose(
1239-core,
1240-runtime,
1241-`attachment download failed guid=${sanitizeForLog(attachment.guid)} err=${sanitizeForLog(err)}`,
1239+// Promote to runtime.error so silently-dropped inbound images are
1240+// visible at default log level, while keeping verbose detail for
1241+// debug sessions. Sanitize both fields — BB attachment GUIDs are
1242+// user-influenced and the error chain can carry the password
1243+// (see sanitizeForLog above).
1244+const safeGuid = sanitizeForLog(attachment.guid, 80);
1245+const safeErr = sanitizeForLog(err);
1246+runtime.error?.(
1247+`[bluebubbles] attachment download failed guid=${safeGuid} err=${safeErr}`,
12421248);
1249+logVerbose(core, runtime, `attachment download failed guid=${safeGuid} err=${safeErr}`);
12431250}
12441251}
12451252}
@@ -1280,6 +1287,49 @@ async function processMessageAfterDedupe(
12801287}
12811288}
128212891290+// Opt-in fallback: if the in-memory cache missed and the BB credentials are
1291+// available, ask the BlueBubbles HTTP API for the original message. Useful
1292+// when multiple OpenClaw instances share one BB account, after a restart,
1293+// or when the cache TTL has evicted the message. Best-effort, never throws.
1294+if (
1295+replyToId &&
1296+(!replyToBody || !replyToSender) &&
1297+baseUrl &&
1298+password &&
1299+account.config.replyContextApiFallback === true
1300+) {
1301+const fetched = await fetchBlueBubblesReplyContext({
1302+accountId: account.accountId,
1303+ replyToId,
1304+ baseUrl,
1305+ password,
1306+accountConfig: account.config,
1307+chatGuid: message.chatGuid,
1308+chatIdentifier: message.chatIdentifier,
1309+chatId: message.chatId,
1310+});
1311+if (fetched) {
1312+if (!replyToBody && fetched.body) {
1313+replyToBody = fetched.body;
1314+}
1315+if (!replyToSender && fetched.sender) {
1316+replyToSender = fetched.sender;
1317+}
1318+if (core.logging.shouldLogVerbose()) {
1319+// Run the body preview through sanitizeForLog so the redaction regex
1320+// (?password=, ?token=, Authorization: …) catches credential-shaped
1321+// strings that may appear in user message bodies, matching the
1322+// hygiene of adjacent verbose log lines in this file.
1323+const preview = sanitizeForLog((fetched.body ?? "").replace(/\s+/g, " "), 120);
1324+logVerbose(
1325+core,
1326+runtime,
1327+`reply-context API fallback replyToId=${sanitizeForLog(replyToId)} sender=${sanitizeForLog(fetched.sender ?? "")} body="${preview}"`,
1328+);
1329+}
1330+}
1331+}
1332+12831333// If no cached short ID, try to get one from the UUID directly
12841334if (replyToId && !replyToShortId) {
12851335replyToShortId = getShortIdForUuid(replyToId);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。