// ============================================ // Telegram Bot API Helpers // ============================================ export class TelegramError extends Error { constructor( message: string, public readonly code?: number, public readonly description?: string ) { super(message); this.name = 'TelegramError'; } } export interface InlineKeyboardButton { text: string; url?: string; callback_data?: string; web_app?: { url: string }; } async function callTelegramAPI( token: string, method: string, body: Record ): Promise { const response = await fetch( `https://api.telegram.org/bot${token}/${method}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), } ); if (!response.ok) { let description = ''; try { const errorData = await response.json() as { description?: string }; description = errorData.description || ''; } catch { // JSON parse failure ignored } throw new TelegramError( `Telegram API ${method} failed: ${response.status}`, response.status, description ); } return response; } function wrapTelegramCall(method: string, fn: () => Promise): Promise { return fn().then(() => undefined).catch((error: unknown) => { if (error instanceof TelegramError) throw error; throw new TelegramError( `Network error in ${method}`, undefined, error instanceof Error ? error.message : String(error) ); }); } export async function sendMessage( token: string, chatId: number, text: string, options?: { parse_mode?: 'HTML' | 'Markdown' | 'MarkdownV2'; reply_to_message_id?: number; disable_notification?: boolean; } ): Promise { return wrapTelegramCall('sendMessage', () => callTelegramAPI(token, 'sendMessage', { chat_id: chatId, text, parse_mode: options?.parse_mode || 'HTML', reply_to_message_id: options?.reply_to_message_id, disable_notification: options?.disable_notification, }) ); } export async function sendMessageWithKeyboard( token: string, chatId: number, text: string, keyboard: InlineKeyboardButton[][], options?: { parse_mode?: 'HTML' | 'Markdown' | 'MarkdownV2'; } ): Promise { return wrapTelegramCall('sendMessageWithKeyboard', () => callTelegramAPI(token, 'sendMessage', { chat_id: chatId, text, parse_mode: options?.parse_mode || 'HTML', reply_markup: { inline_keyboard: keyboard }, }) ); } export async function sendChatAction( token: string, chatId: number, action: 'typing' | 'upload_photo' | 'upload_document' = 'typing' ): Promise { return wrapTelegramCall('sendChatAction', () => callTelegramAPI(token, 'sendChatAction', { chat_id: chatId, action, }) ); } export async function answerCallbackQuery( token: string, callbackQueryId: string, options?: { text?: string; show_alert?: boolean; } ): Promise { return wrapTelegramCall('answerCallbackQuery', () => callTelegramAPI(token, 'answerCallbackQuery', { callback_query_id: callbackQueryId, text: options?.text, show_alert: options?.show_alert, }) ); } export async function editMessageText( token: string, chatId: number, messageId: number, text: string, options?: { parse_mode?: 'HTML' | 'Markdown' | 'MarkdownV2'; reply_markup?: { inline_keyboard: InlineKeyboardButton[][] }; } ): Promise { return wrapTelegramCall('editMessageText', () => callTelegramAPI(token, 'editMessageText', { chat_id: chatId, message_id: messageId, text, parse_mode: options?.parse_mode || 'HTML', reply_markup: options?.reply_markup, }) ); } export async function sendPhoto( token: string, chatId: number, photo: ArrayBuffer, options?: { caption?: string; parse_mode?: 'HTML' | 'Markdown' | 'MarkdownV2'; reply_to_message_id?: number; } ): Promise { const formData = new FormData(); formData.append('chat_id', String(chatId)); formData.append('photo', new Blob([photo], { type: 'image/png' }), 'diagram.png'); if (options?.caption) { formData.append('caption', options.caption); formData.append('parse_mode', options.parse_mode || 'HTML'); } if (options?.reply_to_message_id) { formData.append('reply_to_message_id', String(options.reply_to_message_id)); } try { const response = await fetch( `https://api.telegram.org/bot${token}/sendPhoto`, { method: 'POST', body: formData } ); if (!response.ok) { let description = ''; try { const errorData = await response.json() as { description?: string }; description = errorData.description || ''; } catch { // JSON parse failure ignored } throw new TelegramError( `Telegram API sendPhoto failed: ${response.status}`, response.status, description ); } } catch (error) { if (error instanceof TelegramError) throw error; throw new TelegramError( 'Network error in sendPhoto', undefined, error instanceof Error ? error.message : String(error) ); } } export async function setWebhook( token: string, webhookUrl: string, secretToken: string ): Promise<{ ok: boolean; description?: string }> { const response = await fetch( `https://api.telegram.org/bot${token}/setWebhook`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: webhookUrl, secret_token: secretToken, allowed_updates: ['message', 'callback_query'], drop_pending_updates: true, }), } ); return response.json() as Promise<{ ok: boolean; description?: string }>; } export async function getWebhookInfo(token: string): Promise { const response = await fetch( `https://api.telegram.org/bot${token}/getWebhookInfo` ); return response.json(); }