fix(telegram): include forward_from_chat metadata in forwarded message context (#8133)

Extract missing metadata from forwarded Telegram messages:

- Add fromChatType to TelegramForwardedContext, capturing the original
  chat type (channel/supergroup/group) from forward_from_chat.type
  and forward_origin.chat/sender_chat.type
- Add fromMessageId to capture the original message ID from channel forwards
- Read author_signature from forward_origin objects (modern API),
  preferring it over the deprecated forward_signature field
- Pass ForwardedFromChatType and ForwardedFromMessageId through to
  the inbound context payload
- Add test coverage for forward_origin channel/chat types, including
  author_signature extraction and fromChatType propagation
This commit is contained in:
Glucksberg
2026-02-03 23:00:57 +00:00
committed by Ayaan Zaidi
parent da6de49815
commit 57566c5e4d
4 changed files with 97 additions and 0 deletions

View File

@@ -56,6 +56,8 @@ export type MsgContext = {
ForwardedFromUsername?: string;
ForwardedFromTitle?: string;
ForwardedFromSignature?: string;
ForwardedFromChatType?: string;
ForwardedFromMessageId?: number;
ForwardedDate?: number;
ThreadStarterBody?: string;
ThreadLabel?: string;

View File

@@ -598,6 +598,8 @@ export const buildTelegramMessageContext = async ({
ForwardedFromUsername: forwardOrigin?.fromUsername,
ForwardedFromTitle: forwardOrigin?.fromTitle,
ForwardedFromSignature: forwardOrigin?.fromSignature,
ForwardedFromChatType: forwardOrigin?.fromChatType,
ForwardedFromMessageId: forwardOrigin?.fromMessageId,
ForwardedDate: forwardOrigin?.date ? forwardOrigin.date * 1000 : undefined,
Timestamp: msg.date ? msg.date * 1000 : undefined,
WasMentioned: isGroup ? effectiveWasMentioned : undefined,

View File

@@ -100,6 +100,90 @@ describe("normalizeForwardedContext", () => {
expect(ctx?.fromTitle).toBe("Hidden Name");
expect(ctx?.date).toBe(456);
});
it("handles forward_origin channel with author_signature and message_id", () => {
const ctx = normalizeForwardedContext({
forward_origin: {
type: "channel",
chat: {
title: "Tech News",
username: "technews",
id: -1001234,
type: "channel",
},
date: 500,
author_signature: "Editor",
message_id: 42,
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(ctx).not.toBeNull();
expect(ctx?.from).toBe("Tech News (Editor)");
expect(ctx?.fromType).toBe("channel");
expect(ctx?.fromId).toBe("-1001234");
expect(ctx?.fromUsername).toBe("technews");
expect(ctx?.fromTitle).toBe("Tech News");
expect(ctx?.fromSignature).toBe("Editor");
expect(ctx?.fromChatType).toBe("channel");
expect(ctx?.fromMessageId).toBe(42);
expect(ctx?.date).toBe(500);
});
it("handles forward_origin chat with sender_chat and author_signature", () => {
const ctx = normalizeForwardedContext({
forward_origin: {
type: "chat",
sender_chat: {
title: "Discussion Group",
id: -1005678,
type: "supergroup",
},
date: 600,
author_signature: "Admin",
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(ctx).not.toBeNull();
expect(ctx?.from).toBe("Discussion Group (Admin)");
expect(ctx?.fromType).toBe("chat");
expect(ctx?.fromId).toBe("-1005678");
expect(ctx?.fromTitle).toBe("Discussion Group");
expect(ctx?.fromSignature).toBe("Admin");
expect(ctx?.fromChatType).toBe("supergroup");
expect(ctx?.date).toBe(600);
});
it("uses author_signature from forward_origin", () => {
const ctx = normalizeForwardedContext({
forward_origin: {
type: "channel",
chat: { title: "My Channel", id: -100999, type: "channel" },
date: 700,
author_signature: "New Sig",
message_id: 1,
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(ctx).not.toBeNull();
expect(ctx?.fromSignature).toBe("New Sig");
expect(ctx?.from).toBe("My Channel (New Sig)");
});
it("handles forward_origin channel without author_signature", () => {
const ctx = normalizeForwardedContext({
forward_origin: {
type: "channel",
chat: { title: "News", id: -100111, type: "channel" },
date: 900,
message_id: 1,
},
// oxlint-disable-next-line typescript/no-explicit-any
} as any);
expect(ctx).not.toBeNull();
expect(ctx?.from).toBe("News");
expect(ctx?.fromSignature).toBeUndefined();
expect(ctx?.fromChatType).toBe("channel");
});
});
describe("expandTextLinks", () => {

View File

@@ -261,6 +261,10 @@ export type TelegramForwardedContext = {
fromUsername?: string;
fromTitle?: string;
fromSignature?: string;
/** Original chat type from forward_from_chat (e.g. "channel", "supergroup", "group"). */
fromChatType?: string;
/** Original message ID in the source chat (channel forwards). */
fromMessageId?: number;
};
function normalizeForwardedUserLabel(user: User) {
@@ -323,6 +327,7 @@ function buildForwardedContextFromChat(params: {
date?: number;
type: string;
signature?: string;
messageId?: number;
}): TelegramForwardedContext | null {
const fallbackKind = params.type === "channel" ? "channel" : "chat";
const { display, title, username, id } = normalizeForwardedChatLabel(params.chat, fallbackKind);
@@ -331,6 +336,7 @@ function buildForwardedContextFromChat(params: {
}
const signature = params.signature?.trim() || undefined;
const from = signature ? `${display} (${signature})` : display;
const chatType = params.chat.type?.trim() || undefined;
return {
from,
date: params.date,
@@ -339,6 +345,8 @@ function buildForwardedContextFromChat(params: {
fromUsername: username,
fromTitle: title,
fromSignature: signature,
fromChatType: chatType,
fromMessageId: params.messageId,
};
}
@@ -369,6 +377,7 @@ function resolveForwardOrigin(origin: MessageOrigin): TelegramForwardedContext |
date: origin.date,
type: "channel",
signature: origin.author_signature,
messageId: origin.message_id,
});
default:
// Exhaustiveness guard: if Grammy adds a new MessageOrigin variant,