mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:00:44 +00:00
fix(bluebubbles): move attachment retry before rawBody guard, fix stale log
Move the attachment retry block (2s BB API refetch for empty attachments) before the !rawBody early-return guard. Previously, image-only messages with text='' and attachments=[] would be dropped by the !rawBody check before the retry could fire, making fix #4 dead code for its primary use-case. Now the retry runs first and recomputes the placeholder from resolved attachments so rawBody becomes non-empty when media is found. Also fix stale log message that still said 'without reaction' after the filter was expanded to pass through attachment updates.
This commit is contained in:
@@ -696,7 +696,51 @@ async function processMessageAfterDedupe(
|
|||||||
|
|
||||||
const text = message.text.trim();
|
const text = message.text.trim();
|
||||||
let attachments = message.attachments ?? [];
|
let attachments = message.attachments ?? [];
|
||||||
let placeholder = buildMessagePlaceholder(message);
|
const baseUrl = normalizeSecretInputString(account.config.serverUrl);
|
||||||
|
const password = normalizeSecretInputString(account.config.password);
|
||||||
|
|
||||||
|
// BlueBubbles may fire the webhook before attachment indexing is complete,
|
||||||
|
// so the initial `attachments` array can be empty for messages that actually
|
||||||
|
// have media. When the message text is empty (image-only) or this is an
|
||||||
|
// `updated-message` event, wait briefly and re-fetch from the BB API as a
|
||||||
|
// fallback for cases where BB doesn't send a follow-up webhook. (#65430, #67437)
|
||||||
|
// This must run before the !rawBody guard below, otherwise image-only messages
|
||||||
|
// with empty attachments are dropped before the retry can fire.
|
||||||
|
const retryMessageId = message.messageId?.trim();
|
||||||
|
const shouldRetryAttachments =
|
||||||
|
attachments.length === 0 &&
|
||||||
|
retryMessageId &&
|
||||||
|
baseUrl &&
|
||||||
|
password &&
|
||||||
|
(text.length === 0 || message.eventType === "updated-message");
|
||||||
|
if (shouldRetryAttachments) {
|
||||||
|
try {
|
||||||
|
await new Promise<void>((resolve) => setTimeout(resolve, 2_000));
|
||||||
|
const fetched = await fetchBlueBubblesMessageAttachments(retryMessageId, {
|
||||||
|
baseUrl,
|
||||||
|
password,
|
||||||
|
timeoutMs: 10_000,
|
||||||
|
allowPrivateNetwork: isPrivateNetworkOptInEnabled(account.config),
|
||||||
|
});
|
||||||
|
if (fetched.length > 0) {
|
||||||
|
logVerbose(
|
||||||
|
core,
|
||||||
|
runtime,
|
||||||
|
`attachment retry found ${fetched.length} attachment(s) for msgId=${message.messageId}`,
|
||||||
|
);
|
||||||
|
attachments = fetched;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logVerbose(
|
||||||
|
core,
|
||||||
|
runtime,
|
||||||
|
`attachment retry failed for msgId=${message.messageId}: ${String(err)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recompute placeholder from resolved attachments (may have been updated by retry).
|
||||||
|
const placeholder = buildMessagePlaceholder({ ...message, attachments });
|
||||||
// Check if text is a tapback pattern (e.g., 'Loved "hello"') and transform to emoji format
|
// Check if text is a tapback pattern (e.g., 'Loved "hello"') and transform to emoji format
|
||||||
// For tapbacks, we'll append [[reply_to:N]] at the end; for regular messages, prepend it
|
// For tapbacks, we'll append [[reply_to:N]] at the end; for regular messages, prepend it
|
||||||
const tapbackContext = resolveTapbackContext(message);
|
const tapbackContext = resolveTapbackContext(message);
|
||||||
@@ -1074,9 +1118,6 @@ async function processMessageAfterDedupe(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = normalizeSecretInputString(account.config.serverUrl);
|
|
||||||
const password = normalizeSecretInputString(account.config.password);
|
|
||||||
|
|
||||||
if (isGroup && !message.participants?.length && baseUrl && password) {
|
if (isGroup && !message.participants?.length && baseUrl && password) {
|
||||||
try {
|
try {
|
||||||
const fetchedParticipants = await fetchBlueBubblesParticipantsForInboundMessage({
|
const fetchedParticipants = await fetchBlueBubblesParticipantsForInboundMessage({
|
||||||
@@ -1120,53 +1161,14 @@ async function processMessageAfterDedupe(
|
|||||||
? account.config.mediaMaxMb * 1024 * 1024
|
? account.config.mediaMaxMb * 1024 * 1024
|
||||||
: 8 * 1024 * 1024;
|
: 8 * 1024 * 1024;
|
||||||
|
|
||||||
// BlueBubbles may fire the webhook before attachment indexing is complete,
|
|
||||||
// so the initial `attachments` array can be empty for messages that actually
|
|
||||||
// have media. When the message text is empty (image-only) or this is an
|
|
||||||
// `updated-message` event, wait briefly and re-fetch from the BB API as a
|
|
||||||
// fallback for cases where BB doesn't send a follow-up webhook. (#65430, #67437)
|
|
||||||
let resolvedAttachments = attachments;
|
|
||||||
const retryMessageId = message.messageId?.trim();
|
|
||||||
const shouldRetryAttachments =
|
|
||||||
resolvedAttachments.length === 0 &&
|
|
||||||
retryMessageId &&
|
|
||||||
baseUrl &&
|
|
||||||
password &&
|
|
||||||
(text.length === 0 || message.eventType === "updated-message");
|
|
||||||
if (shouldRetryAttachments) {
|
|
||||||
try {
|
|
||||||
await new Promise<void>((resolve) => setTimeout(resolve, 2_000));
|
|
||||||
const fetched = await fetchBlueBubblesMessageAttachments(retryMessageId, {
|
|
||||||
baseUrl,
|
|
||||||
password,
|
|
||||||
timeoutMs: 10_000,
|
|
||||||
allowPrivateNetwork: isPrivateNetworkOptInEnabled(account.config),
|
|
||||||
});
|
|
||||||
if (fetched.length > 0) {
|
|
||||||
logVerbose(
|
|
||||||
core,
|
|
||||||
runtime,
|
|
||||||
`attachment retry found ${fetched.length} attachment(s) for msgId=${message.messageId}`,
|
|
||||||
);
|
|
||||||
resolvedAttachments = fetched;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logVerbose(
|
|
||||||
core,
|
|
||||||
runtime,
|
|
||||||
`attachment retry failed for msgId=${message.messageId}: ${String(err)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mediaUrls: string[] = [];
|
let mediaUrls: string[] = [];
|
||||||
let mediaPaths: string[] = [];
|
let mediaPaths: string[] = [];
|
||||||
let mediaTypes: string[] = [];
|
let mediaTypes: string[] = [];
|
||||||
if (resolvedAttachments.length > 0) {
|
if (attachments.length > 0) {
|
||||||
if (!baseUrl || !password) {
|
if (!baseUrl || !password) {
|
||||||
logVerbose(core, runtime, "attachment download skipped (missing serverUrl/password)");
|
logVerbose(core, runtime, "attachment download skipped (missing serverUrl/password)");
|
||||||
} else {
|
} else {
|
||||||
for (const attachment of resolvedAttachments) {
|
for (const attachment of attachments) {
|
||||||
if (!attachment.guid) {
|
if (!attachment.guid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user