diff --git a/src/channels/plugins/normalize/telegram.test.ts b/src/channels/plugins/normalize/telegram.test.ts index dee61e395a0..23e90288f0b 100644 --- a/src/channels/plugins/normalize/telegram.test.ts +++ b/src/channels/plugins/normalize/telegram.test.ts @@ -6,6 +6,15 @@ describe("normalizeTelegramMessagingTarget", () => { expect(normalizeTelegramMessagingTarget("https://t.me/MyChannel")).toBe("telegram:@mychannel"); }); + it("keeps unprefixed topic targets valid", () => { + expect(normalizeTelegramMessagingTarget("@MyChannel:topic:9")).toBe( + "telegram:@mychannel:topic:9", + ); + expect(normalizeTelegramMessagingTarget("-1001234567890:topic:456")).toBe( + "telegram:-1001234567890:topic:456", + ); + }); + it("keeps legacy prefixed topic targets valid", () => { expect(normalizeTelegramMessagingTarget("telegram:group:-1001234567890:topic:456")).toBe( "telegram:group:-1001234567890:topic:456", @@ -17,6 +26,11 @@ describe("normalizeTelegramMessagingTarget", () => { }); describe("looksLikeTelegramTargetId", () => { + it("recognizes unprefixed topic targets", () => { + expect(looksLikeTelegramTargetId("@mychannel:topic:9")).toBe(true); + expect(looksLikeTelegramTargetId("-1001234567890:topic:456")).toBe(true); + }); + it("recognizes legacy prefixed topic targets", () => { expect(looksLikeTelegramTargetId("telegram:group:-1001234567890:topic:456")).toBe(true); expect(looksLikeTelegramTargetId("tg:group:-1001234567890:topic:456")).toBe(true); diff --git a/src/channels/plugins/normalize/telegram.ts b/src/channels/plugins/normalize/telegram.ts index e091d9fea46..a21ad160d03 100644 --- a/src/channels/plugins/normalize/telegram.ts +++ b/src/channels/plugins/normalize/telegram.ts @@ -1,32 +1,44 @@ -import { normalizeTelegramLookupTarget } from "../../../telegram/targets.js"; +import { normalizeTelegramLookupTarget, parseTelegramTarget } from "../../../telegram/targets.js"; -export function normalizeTelegramMessagingTarget(raw: string): string | undefined { +const TELEGRAM_PREFIX_RE = /^(telegram|tg):/i; + +function normalizeTelegramTargetBody(raw: string): string | undefined { const trimmed = raw.trim(); if (!trimmed) { return undefined; } - const normalized = normalizeTelegramLookupTarget(trimmed); - if (!normalized) { - // Keep legacy prefixed targets (including :topic: suffixes) valid. - if (/^(telegram|tg):/i.test(trimmed)) { - const stripped = trimmed.replace(/^(telegram|tg):/i, "").trim(); - if (stripped) { - return `telegram:${stripped}`.toLowerCase(); - } - } + const prefixStripped = trimmed.replace(TELEGRAM_PREFIX_RE, "").trim(); + if (!prefixStripped) { return undefined; } - return `telegram:${normalized}`.toLowerCase(); + + const parsed = parseTelegramTarget(trimmed); + const normalizedChatId = normalizeTelegramLookupTarget(parsed.chatId); + if (!normalizedChatId) { + return undefined; + } + + const keepLegacyGroupPrefix = /^group:/i.test(prefixStripped); + const hasTopicSuffix = /:topic:\d+$/i.test(prefixStripped); + const chatSegment = keepLegacyGroupPrefix ? `group:${normalizedChatId}` : normalizedChatId; + if (parsed.messageThreadId == null) { + return chatSegment; + } + const threadSuffix = hasTopicSuffix + ? `:topic:${parsed.messageThreadId}` + : `:${parsed.messageThreadId}`; + return `${chatSegment}${threadSuffix}`; +} + +export function normalizeTelegramMessagingTarget(raw: string): string | undefined { + const normalizedBody = normalizeTelegramTargetBody(raw); + if (!normalizedBody) { + return undefined; + } + return `telegram:${normalizedBody}`.toLowerCase(); } export function looksLikeTelegramTargetId(raw: string): boolean { - const trimmed = raw.trim(); - if (!trimmed) { - return false; - } - if (normalizeTelegramLookupTarget(trimmed)) { - return true; - } - return /^(telegram|tg):/i.test(trimmed); + return normalizeTelegramTargetBody(raw) !== undefined; }