fix(telegram): link forwarded messages with comments (#9720)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 5f81061b5f
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
Marcus Castro
2026-02-22 12:53:56 -03:00
committed by GitHub
parent 40a68a8936
commit 337eef55d7
6 changed files with 217 additions and 2 deletions

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import {
buildTelegramThreadParams,
buildTypingThreadParams,
describeReplyTarget,
expandTextLinks,
normalizeForwardedContext,
resolveTelegramForumThreadId,
@@ -199,6 +200,137 @@ describe("normalizeForwardedContext", () => {
});
});
describe("describeReplyTarget", () => {
it("returns null when no reply_to_message", () => {
const result = describeReplyTarget(
// oxlint-disable-next-line typescript/no-explicit-any
{ message_id: 1, date: 1000, chat: { id: 1, type: "private" } } as any,
);
expect(result).toBeNull();
});
it("extracts basic reply info", () => {
const result = describeReplyTarget({
message_id: 2,
date: 1000,
chat: { id: 1, type: "private" },
reply_to_message: {
message_id: 1,
date: 900,
chat: { id: 1, type: "private" },
text: "Original message",
from: { id: 42, first_name: "Alice", is_bot: false },
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(result).not.toBeNull();
expect(result?.body).toBe("Original message");
expect(result?.sender).toBe("Alice");
expect(result?.id).toBe("1");
expect(result?.kind).toBe("reply");
});
it("extracts forwarded context from reply_to_message (issue #9619)", () => {
// When user forwards a message with a comment, the comment message has
// reply_to_message pointing to the forwarded message. We should extract
// the forward_origin from the reply target.
const result = describeReplyTarget({
message_id: 3,
date: 1100,
chat: { id: 1, type: "private" },
text: "Here is my comment about this forwarded content",
reply_to_message: {
message_id: 2,
date: 1000,
chat: { id: 1, type: "private" },
text: "This is the forwarded content",
forward_origin: {
type: "user",
sender_user: {
id: 999,
first_name: "Bob",
last_name: "Smith",
username: "bobsmith",
is_bot: false,
},
date: 500,
},
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(result).not.toBeNull();
expect(result?.body).toBe("This is the forwarded content");
expect(result?.id).toBe("2");
// The reply target's forwarded context should be included
expect(result?.forwardedFrom).toBeDefined();
expect(result?.forwardedFrom?.from).toBe("Bob Smith (@bobsmith)");
expect(result?.forwardedFrom?.fromType).toBe("user");
expect(result?.forwardedFrom?.fromId).toBe("999");
expect(result?.forwardedFrom?.date).toBe(500);
});
it("extracts forwarded context from channel forward in reply_to_message", () => {
const result = describeReplyTarget({
message_id: 4,
date: 1200,
chat: { id: 1, type: "private" },
text: "Interesting article!",
reply_to_message: {
message_id: 3,
date: 1100,
chat: { id: 1, type: "private" },
text: "Channel post content here",
forward_origin: {
type: "channel",
chat: { id: -1001234567, title: "Tech News", username: "technews", type: "channel" },
date: 800,
message_id: 456,
author_signature: "Editor",
},
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(result).not.toBeNull();
expect(result?.forwardedFrom).toBeDefined();
expect(result?.forwardedFrom?.from).toBe("Tech News (Editor)");
expect(result?.forwardedFrom?.fromType).toBe("channel");
expect(result?.forwardedFrom?.fromMessageId).toBe(456);
});
it("extracts forwarded context from external_reply", () => {
const result = describeReplyTarget({
message_id: 5,
date: 1300,
chat: { id: 1, type: "private" },
text: "Comment on forwarded message",
external_reply: {
message_id: 4,
date: 1200,
chat: { id: 1, type: "private" },
text: "Forwarded from elsewhere",
forward_origin: {
type: "user",
sender_user: {
id: 123,
first_name: "Eve",
last_name: "Stone",
username: "eve",
is_bot: false,
},
date: 700,
},
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(result).not.toBeNull();
expect(result?.id).toBe("4");
expect(result?.forwardedFrom?.from).toBe("Eve Stone (@eve)");
expect(result?.forwardedFrom?.fromType).toBe("user");
expect(result?.forwardedFrom?.fromId).toBe("123");
expect(result?.forwardedFrom?.date).toBe(700);
});
});
describe("expandTextLinks", () => {
it("returns text unchanged when no entities are provided", () => {
expect(expandTextLinks("Hello world")).toBe("Hello world");

View File

@@ -321,6 +321,8 @@ export type TelegramReplyTarget = {
sender: string;
body: string;
kind: "reply" | "quote";
/** Forward context if the reply target was itself a forwarded message (issue #9619). */
forwardedFrom?: TelegramForwardedContext;
};
export function describeReplyTarget(msg: Message): TelegramReplyTarget | null {
@@ -359,11 +361,17 @@ export function describeReplyTarget(msg: Message): TelegramReplyTarget | null {
const sender = replyLike ? buildSenderName(replyLike) : undefined;
const senderLabel = sender ?? "unknown sender";
// Extract forward context from the resolved reply target (reply_to_message or external_reply).
const forwardedFrom = replyLike?.forward_origin
? (resolveForwardOrigin(replyLike.forward_origin) ?? undefined)
: undefined;
return {
id: replyLike?.message_id ? String(replyLike.message_id) : undefined,
sender: senderLabel,
body,
kind,
forwardedFrom,
};
}