From 65bcdf2f261a2440529cdd25264ed51616d628b4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 24 Aug 2026 00:05:07 -0700 Subject: [PATCH] fix(discord): report actual delivered message kinds (#128575) --- extensions/discord/src/send.outbound.ts | 42 +++++++++---------- .../send.sends-basic-channel-messages.test.ts | 11 ++++- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/extensions/discord/src/send.outbound.ts b/extensions/discord/src/send.outbound.ts index 749f8886124e..d4375635d7ac 100644 --- a/extensions/discord/src/send.outbound.ts +++ b/extensions/discord/src/send.outbound.ts @@ -208,6 +208,17 @@ export async function sendMessageDiscord( // Forum/Media channels reject POST /messages; auto-create a thread post instead. const channel = await resolveDiscordChannel(rest, channelId); + const deliveredResults: DiscordSendResult[] = []; + let deliveryThreadId: string | undefined; + const reportResult: DiscordSendProgress = async (progressResult, kind, replyToId) => { + const deliveredResult = toDiscordSendResult(progressResult, deliveryThreadId ?? channelId, { + kind, + threadId: deliveryThreadId, + reply: createReusableDiscordReplyReference(replyToId), + }); + deliveredResults.push(deliveredResult); + await opts.onDeliveryResult?.(deliveredResult); + }; if (isForumLikeChannel(channel)) { if (((channel.flags ?? 0) & DISCORD_FORUM_REQUIRE_TAG_FLAG) !== 0) { @@ -275,6 +286,7 @@ export async function sendMessageDiscord( } const threadId = threadRes.id; + deliveryThreadId = threadId; const messageId = threadRes.message?.id ?? threadId; const resultChannelId = threadRes.message?.channel_id ?? threadId; const remainingChunks = chunks.slice(1); @@ -286,13 +298,8 @@ export async function sendMessageDiscord( channelId, { kind: "text", threadId }, ); - const deliveredResults: DiscordSendResult[] = [starterResult]; + deliveredResults.push(starterResult); await opts.onDeliveryResult?.(starterResult); - const reportThreadResult: DiscordSendProgress = async (result, kind) => { - const deliveredResult = toDiscordSendResult(result, threadId, { kind, threadId }); - deliveredResults.push(deliveredResult); - await opts.onDeliveryResult?.(deliveredResult); - }; try { if (opts.mediaUrl) { @@ -314,7 +321,7 @@ export async function sendMessageDiscord( suppressEmbeds, allowedMentions: opts.allowedMentions, maxChars: textLimit, - onResult: reportThreadResult, + onResult: reportResult, onPlatformSendDispatch: opts.onPlatformSendDispatch, }); await sendDiscordThreadTextChunks({ @@ -328,7 +335,7 @@ export async function sendMessageDiscord( silent: opts.silent, suppressEmbeds, allowedMentions: opts.allowedMentions, - onResult: reportThreadResult, + onResult: reportResult, onPlatformSendDispatch: opts.onPlatformSendDispatch, }); } else { @@ -343,7 +350,7 @@ export async function sendMessageDiscord( silent: opts.silent, suppressEmbeds, allowedMentions: opts.allowedMentions, - onResult: reportThreadResult, + onResult: reportResult, onPlatformSendDispatch: opts.onPlatformSendDispatch, }); } @@ -369,14 +376,6 @@ export async function sendMessageDiscord( } let result: DiscordChannelMessageResult; - const reportResult: DiscordSendProgress = async (progressResult, kind, replyToId) => { - await opts.onDeliveryResult?.( - toDiscordSendResult(progressResult, channelId, { - kind, - reply: createReusableDiscordReplyReference(replyToId), - }), - ); - }; try { if (opts.mediaUrl) { result = await sendDiscordMedia({ @@ -436,10 +435,11 @@ export async function sendMessageDiscord( accountId: accountInfo.accountId, direction: "outbound", }); - return toDiscordSendResult(result, channelId, { - kind: opts.mediaUrl ? "media" : opts.components || opts.embeds ? "card" : "text", - reply: opts.reply, - }); + return { + messageId: result.id || "unknown", + channelId: result.channel_id ?? channelId, + receipt: createDiscordSendReceiptFromResults({ results: deliveredResults }), + }; } export async function sendStickerDiscord( diff --git a/extensions/discord/src/send.sends-basic-channel-messages.test.ts b/extensions/discord/src/send.sends-basic-channel-messages.test.ts index a25b2d4bcd39..f36f221b91d4 100644 --- a/extensions/discord/src/send.sends-basic-channel-messages.test.ts +++ b/extensions/discord/src/send.sends-basic-channel-messages.test.ts @@ -392,7 +392,7 @@ describe("sendMessageDiscord", () => { ]; const onDeliveryResult = vi.fn(); - await sendMessageDiscord("channel:789", "a".repeat(2_500), { + const result = await sendMessageDiscord("channel:789", "a".repeat(2_500), { rest, token: "t", cfg: DISCORD_TEST_CFG, @@ -415,6 +415,7 @@ describe("sendMessageDiscord", () => { "card", "text", ]); + expect(result.receipt.parts.map(({ kind }) => kind)).toEqual(["card", "text"]); }); it("delivers embed-only and native Components V2 messages over real HTTP", async () => { @@ -1008,6 +1009,7 @@ describe("sendMessageDiscord", () => { }); expect(res.messageId).toBe("fallback-msg"); + expectSingleReceiptPart(res.receipt, { platformMessageId: "fallback-msg", kind: "text" }); expect(postMock).toHaveBeenCalledTimes(2); expectBodyFileName(requireRestBody(postMock, 0), "photo.jpg"); const fallbackBody = requireRestBody(postMock, 1); @@ -1199,17 +1201,20 @@ describe("sendMessageDiscord", () => { name: "preserves reply reference across all text chunks by default", params: { text: "a".repeat(2001) }, expectsSecondReply: true, + expectedKinds: ["text", "text"], }, { name: "limits reply reference to the first text chunk when requested", params: { text: "a".repeat(2001), replyScope: "first" as const }, expectsSecondReply: false, checksReceipt: true, + expectedKinds: ["text", "text"], }, { name: "preserves reply reference for follow-up text chunks after media caption split by default", params: { text: "a".repeat(2500), mediaUrl: "file:///tmp/photo.jpg" }, expectsSecondReply: true, + expectedKinds: ["media", "text"], }, { name: "limits media caption reply reference to the first physical message when requested", @@ -1219,9 +1224,11 @@ describe("sendMessageDiscord", () => { replyScope: "first" as const, }, expectsSecondReply: false, + expectedKinds: ["media", "text"], }, - ])("$name", async ({ params, expectsSecondReply, checksReceipt }) => { + ])("$name", async ({ params, expectsSecondReply, checksReceipt, expectedKinds }) => { const { firstBody, secondBody, result } = await sendChunkedReplyAndCollectBodies(params); + expect(result.receipt.parts.map(({ kind }) => kind)).toEqual(expectedKinds); expectReplyReference(firstBody, "orig-123"); if (expectsSecondReply) { expectReplyReference(secondBody, "orig-123");