From d5c22a39591846784994062c48c89f858ee743dc Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Tue, 23 Jun 2026 23:27:23 +0700 Subject: [PATCH] fix(discord-gateway): handle forwarded message content via messageSnapshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two-layer fix for forwarded messages showing as empty/clean: Layer 1 (messageMetadata.ts): getReferencedMessageContent() now falls back to message.messageSnapshots Collection when channel.messages.cache lookup fails. Discord stores forward content in message_snapshots API field, not in message.content. Layer 2 (moderationBuilders.ts): buildReferenceXml() now parses msg. metadata JSON to extract reference.content when DB getMessageById() fails (cross-server forwards not in local DB). Previously: forward messages captured with empty parentContent → LLM saw no reference text → '99% confidence, pesan kosong'. Now: forward content flows through capture → metadata → analysis. --- .../ai-moderation/moderationBuilders.ts | 18 ++++++++++++- .../message-capture/messageMetadata.ts | 26 ++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/services/discord-gateway/src/modules/ai-moderation/moderationBuilders.ts b/services/discord-gateway/src/modules/ai-moderation/moderationBuilders.ts index 1fea644..6fc1ab6 100644 --- a/services/discord-gateway/src/modules/ai-moderation/moderationBuilders.ts +++ b/services/discord-gateway/src/modules/ai-moderation/moderationBuilders.ts @@ -48,6 +48,7 @@ export async function buildReferenceXml(msg: MessageRecord): Promise { let parentContent = ""; if (msg.reference_message_id) { + // 1. Try DB first — works for messages captured in the same server try { const parent = await getMessageById(msg.reference_message_id); if (parent) { @@ -55,7 +56,22 @@ export async function buildReferenceXml(msg: MessageRecord): Promise { parentContent = parentText.slice(0, 500); } } catch { - // Parent fetch failed — still inject reference with available info + // Parent fetch failed — fall through to metadata + } + + // 2. Fall back to metadata — works for forwards from other servers/channels + // where the original message was never captured in this DB. + // The capture phase stores the snapshot content in metadata.reference.content. + if (!parentContent && msg.metadata) { + try { + const meta = JSON.parse(msg.metadata); + const refContent = meta?.reference?.content; + if (refContent && typeof refContent === "string") { + parentContent = refContent.slice(0, 500); + } + } catch { + // Metadata parse failed — no fallback available + } } } diff --git a/services/discord-gateway/src/modules/message-capture/messageMetadata.ts b/services/discord-gateway/src/modules/message-capture/messageMetadata.ts index fa81912..796a43d 100644 --- a/services/discord-gateway/src/modules/message-capture/messageMetadata.ts +++ b/services/discord-gateway/src/modules/message-capture/messageMetadata.ts @@ -211,17 +211,20 @@ export function getEmbedMetadata( } /** - * Try to get referenced message content from the channel cache. + * Try to get referenced message content from the channel cache or message snapshots. * For replies, Discord sends `referenced_message` in the API, which * discord.js-selfbot-v13 caches in the channel's message manager. - * Returns null if the message isn't cached (e.g. forwards without - * referenced_message payload). + * For forwards, Discord sends `message_snapshots` which discord.js-selfbot-v13 + * stores in `message.messageSnapshots` as a Collection of partial Message objects. + * Returns null if the message can't be resolved from either source. */ function getReferencedMessageContent( message: Message, ): { content: string; username: string; userId: string } | null { const ref = message.reference; if (!ref?.messageId) return null; + + // 1. Channel cache — works for same-channel replies/forwards try { const cached = (message.channel as any)?.messages?.cache?.get( ref.messageId, @@ -236,6 +239,23 @@ function getReferencedMessageContent( } catch { // Cache may not be available or message not in it } + + // 2. messageSnapshots — works for cross-channel/cross-server forwards + // Discord API sends message_snapshots for FORWARD type messages, + // and discord.js-selfbot-v13 stores them in message.messageSnapshots. + try { + const snapshot = message.messageSnapshots?.get(ref.messageId); + if (snapshot?.content) { + return { + content: snapshot.content, + username: (snapshot as any).author?.username ?? "Unknown", + userId: (snapshot as any).author?.id ?? "", + }; + } + } catch { + // Snapshots may not be available + } + return null; }