From 5cafe4b0cf718ea29404f23dcc7719beaa18b628 Mon Sep 17 00:00:00 2001 From: Moeed Ahmed Date: Mon, 22 Jun 2026 20:17:07 +0100 Subject: [PATCH] fix(telegram): keep bot reply answers anchored to current message (#90475) Co-authored-by: Moeed Ahmed --- .../telegram/src/bot-message-dispatch.test.ts | 33 +++++++++++++++++++ .../telegram/src/bot-message-dispatch.ts | 9 +++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/extensions/telegram/src/bot-message-dispatch.test.ts b/extensions/telegram/src/bot-message-dispatch.test.ts index 6099c6114f32..b89acada6363 100644 --- a/extensions/telegram/src/bot-message-dispatch.test.ts +++ b/extensions/telegram/src/bot-message-dispatch.test.ts @@ -1284,6 +1284,39 @@ describe("dispatchTelegramMessage draft streaming", () => { expectRecordFields((delivery.replies as Array)[0], { replyToId: "9001" }); }); + it("keeps bot-reply answers anchored to the current user message", async () => { + dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => { + await dispatcherOptions.deliver({ text: "Hello", replyToId: "1001" }, { kind: "final" }); + return { queuedFinal: true }; + }); + deliverReplies.mockResolvedValue({ delivered: true }); + + await dispatchWithContext({ + context: createContext({ + msg: { + message_id: 1001, + reply_to_message: { + message_id: 9001, + from: { is_bot: true }, + }, + } as unknown as TelegramMessageContext["msg"], + ctxPayload: { + MessageSid: "1001", + ReplyToId: "9001", + ReplyToBody: "quoted bot reply", + ReplyToQuoteText: " quoted bot reply\n", + ReplyToIsQuote: true, + } as unknown as TelegramMessageContext["ctxPayload"], + }), + }); + + const delivery = expectDeliverRepliesParams({ + replyQuoteMessageId: 9001, + replyQuoteText: " quoted bot reply\n", + }); + expectRecordFields((delivery.replies as Array)[0], { replyToId: "1001" }); + }); + it("keeps answer draft stream for current message replies with native quote candidates", async () => { const draftStream = createDraftStream(); createTelegramDraftStream.mockReturnValue(draftStream); diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts index 37bafd4c8e8b..35dfaa9ffe8e 100644 --- a/extensions/telegram/src/bot-message-dispatch.ts +++ b/extensions/telegram/src/bot-message-dispatch.ts @@ -947,6 +947,7 @@ export const dispatchTelegramMessage = async ({ replyQuoteText && !ctxPayload.ReplyToIsExternal ? resolveTelegramReplyId(ctxPayload.ReplyToId) : undefined; + const replyQuoteTargetsBotMessage = msg.reply_to_message?.from?.is_bot === true; const replyQuoteByMessageId: TelegramNativeQuoteCandidateByMessageId = {}; if (replyToMode !== "off") { if (replyQuoteText && replyQuoteMessageId != null) { @@ -992,7 +993,9 @@ export const dispatchTelegramMessage = async ({ !isRoomEvent && streamReasoningDraft && !streamReasoningInProgressDraft; const draftReplyToMessageId = replyToMode !== "off" && typeof msg.message_id === "number" - ? (replyQuoteMessageId ?? msg.message_id) + ? replyQuoteTargetsBotMessage + ? msg.message_id + : (replyQuoteMessageId ?? msg.message_id) : undefined; const draftMinInitialChars = streamMode === "progress" ? 0 : DRAFT_MIN_INITIAL_CHARS; const progressSeed = `${route.accountId}:${chatId}:${threadSpec.id ?? ""}`; @@ -1448,7 +1451,9 @@ export const dispatchTelegramMessage = async ({ }); const implicitQuoteReplyTargetId = - replyQuoteMessageId != null ? String(replyQuoteMessageId) : undefined; + !replyQuoteTargetsBotMessage && replyQuoteMessageId != null + ? String(replyQuoteMessageId) + : undefined; const currentMessageIdForQuoteReply = implicitQuoteReplyTargetId && ctxPayload.MessageSid ? ctxPayload.MessageSid : undefined; const replyQuotePosition =