fix: require thread specs for telegram sends

This commit is contained in:
Ayaan Zaidi 2026-02-02 09:05:36 +05:30 committed by Ayaan Zaidi
parent 19b8416a81
commit 1d7dd5f261
5 changed files with 22 additions and 20 deletions

View file

@ -39,7 +39,7 @@ export async function deliverReplies(params: {
bot: Bot; bot: Bot;
replyToMode: ReplyToMode; replyToMode: ReplyToMode;
textLimit: number; textLimit: number;
thread?: TelegramThreadSpec | number | null; thread?: TelegramThreadSpec | null;
tableMode?: MarkdownTableMode; tableMode?: MarkdownTableMode;
chunkMode?: ChunkMode; chunkMode?: ChunkMode;
/** Callback invoked before sending a voice message to switch typing indicator. */ /** Callback invoked before sending a voice message to switch typing indicator. */
@ -451,7 +451,7 @@ async function sendTelegramVoiceFallbackText(opts: {
replyToId?: number; replyToId?: number;
replyToMode: ReplyToMode; replyToMode: ReplyToMode;
hasReplied: boolean; hasReplied: boolean;
thread?: TelegramThreadSpec | number | null; thread?: TelegramThreadSpec | null;
linkPreview?: boolean; linkPreview?: boolean;
replyMarkup?: ReturnType<typeof buildInlineKeyboard>; replyMarkup?: ReturnType<typeof buildInlineKeyboard>;
replyQuoteText?: string; replyQuoteText?: string;
@ -479,7 +479,7 @@ async function sendTelegramVoiceFallbackText(opts: {
function buildTelegramSendParams(opts?: { function buildTelegramSendParams(opts?: {
replyToMessageId?: number; replyToMessageId?: number;
thread?: TelegramThreadSpec | number | null; thread?: TelegramThreadSpec | null;
replyQuoteText?: string; replyQuoteText?: string;
}): Record<string, unknown> { }): Record<string, unknown> {
const threadParams = buildTelegramThreadParams(opts?.thread); const threadParams = buildTelegramThreadParams(opts?.thread);
@ -509,7 +509,7 @@ async function sendTelegramText(
opts?: { opts?: {
replyToMessageId?: number; replyToMessageId?: number;
replyQuoteText?: string; replyQuoteText?: string;
thread?: TelegramThreadSpec | number | null; thread?: TelegramThreadSpec | null;
textMode?: "markdown" | "html"; textMode?: "markdown" | "html";
plainText?: string; plainText?: string;
linkPreview?: boolean; linkPreview?: boolean;

View file

@ -34,11 +34,13 @@ describe("resolveTelegramForumThreadId", () => {
describe("buildTelegramThreadParams", () => { describe("buildTelegramThreadParams", () => {
it("omits General topic thread id for message sends", () => { it("omits General topic thread id for message sends", () => {
expect(buildTelegramThreadParams(1)).toBeUndefined(); expect(buildTelegramThreadParams({ id: 1, scope: "forum" })).toBeUndefined();
}); });
it("includes non-General topic thread ids", () => { it("includes non-General topic thread ids", () => {
expect(buildTelegramThreadParams(99)).toEqual({ message_thread_id: 99 }); expect(buildTelegramThreadParams({ id: 99, scope: "forum" })).toEqual({
message_thread_id: 99,
});
}); });
it("keeps thread id=1 for dm threads", () => { it("keeps thread id=1 for dm threads", () => {
@ -48,7 +50,9 @@ describe("buildTelegramThreadParams", () => {
}); });
it("normalizes thread ids to integers", () => { it("normalizes thread ids to integers", () => {
expect(buildTelegramThreadParams(42.9)).toEqual({ message_thread_id: 42 }); expect(buildTelegramThreadParams({ id: 42.9, scope: "forum" })).toEqual({
message_thread_id: 42,
});
}); });
}); });

View file

@ -69,18 +69,12 @@ export function resolveTelegramThreadSpec(params: {
* General forum topic (id=1) must be treated like a regular supergroup send: * General forum topic (id=1) must be treated like a regular supergroup send:
* Telegram rejects sendMessage/sendMedia with message_thread_id=1 ("thread not found"). * Telegram rejects sendMessage/sendMedia with message_thread_id=1 ("thread not found").
*/ */
export function buildTelegramThreadParams(thread?: TelegramThreadSpec | number | null) { export function buildTelegramThreadParams(thread?: TelegramThreadSpec | null) {
let spec: TelegramThreadSpec | undefined; if (!thread?.id) {
if (typeof thread === "number") {
spec = { id: thread, scope: "forum" };
} else if (thread && typeof thread === "object") {
spec = thread;
}
if (!spec?.id) {
return undefined; return undefined;
} }
const normalized = Math.trunc(spec.id); const normalized = Math.trunc(thread.id);
if (normalized === TELEGRAM_GENERAL_TOPIC_ID && spec.scope === "forum") { if (normalized === TELEGRAM_GENERAL_TOPIC_ID && thread.scope === "forum") {
return undefined; return undefined;
} }
return { message_thread_id: normalized }; return { message_thread_id: normalized };

View file

@ -15,7 +15,7 @@ export function createTelegramDraftStream(params: {
chatId: number; chatId: number;
draftId: number; draftId: number;
maxChars?: number; maxChars?: number;
thread?: TelegramThreadSpec | number | null; thread?: TelegramThreadSpec | null;
throttleMs?: number; throttleMs?: number;
log?: (message: string) => void; log?: (message: string) => void;
warn?: (message: string) => void; warn?: (message: string) => void;

View file

@ -221,7 +221,9 @@ export async function sendMessageTelegram(
// Only include these if actually provided to keep API calls clean. // Only include these if actually provided to keep API calls clean.
const messageThreadId = const messageThreadId =
opts.messageThreadId != null ? opts.messageThreadId : target.messageThreadId; opts.messageThreadId != null ? opts.messageThreadId : target.messageThreadId;
const threadIdParams = buildTelegramThreadParams(messageThreadId); const threadSpec =
messageThreadId != null ? { id: messageThreadId, scope: "forum" as const } : undefined;
const threadIdParams = buildTelegramThreadParams(threadSpec);
const threadParams: Record<string, unknown> = threadIdParams ? { ...threadIdParams } : {}; const threadParams: Record<string, unknown> = threadIdParams ? { ...threadIdParams } : {};
const quoteText = opts.quoteText?.trim(); const quoteText = opts.quoteText?.trim();
if (opts.replyToMessageId != null) { if (opts.replyToMessageId != null) {
@ -694,7 +696,9 @@ export async function sendStickerTelegram(
const messageThreadId = const messageThreadId =
opts.messageThreadId != null ? opts.messageThreadId : target.messageThreadId; opts.messageThreadId != null ? opts.messageThreadId : target.messageThreadId;
const threadIdParams = buildTelegramThreadParams(messageThreadId); const threadSpec =
messageThreadId != null ? { id: messageThreadId, scope: "forum" as const } : undefined;
const threadIdParams = buildTelegramThreadParams(threadSpec);
const threadParams: Record<string, number> = threadIdParams ? { ...threadIdParams } : {}; const threadParams: Record<string, number> = threadIdParams ? { ...threadIdParams } : {};
if (opts.replyToMessageId != null) { if (opts.replyToMessageId != null) {
threadParams.reply_to_message_id = Math.trunc(opts.replyToMessageId); threadParams.reply_to_message_id = Math.trunc(opts.replyToMessageId);