// Lightweight Telegram sender // Sends messages via Telegram Bot API: https://api.telegram.org/bot/sendMessage // Requires env: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID (single or comma/semicolon separated) import dotenv from 'dotenv' dotenv.config() let _fetch = null async function getFetch() { if (typeof globalThis.fetch === 'function') return globalThis.fetch if (_fetch) return _fetch try { const mod = await import('node-fetch') _fetch = mod.default return _fetch } catch (e) { throw new Error('fetch is not available and node-fetch is not installed. Please install node-fetch or use Node 18+.') } } function ensureArray(v) { if (!v) return [] if (Array.isArray(v)) return v if (typeof v === 'string') return v.split(/[;,]/).map(s => s.trim()).filter(Boolean) return [String(v)] } export async function sendTelegramMessage({ text, parseMode = 'HTML', disableWebPagePreview = true, chatIds = null, replyMarkup = null }) { const token = process.env.TELEGRAM_BOT_TOKEN || '' const rawChatIds = chatIds || process.env.TELEGRAM_CHAT_ID || '' const ids = ensureArray(rawChatIds) if (!token || ids.length === 0) { throw new Error('Telegram configuration missing: TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID are required') } const fetchImpl = await getFetch() const base = `https://api.telegram.org/bot${token}` const payloadBase = { text: String(text || ''), parse_mode: parseMode || undefined, disable_web_page_preview: Boolean(disableWebPagePreview), } if (replyMarkup) payloadBase.reply_markup = replyMarkup const results = [] for (const chat_id of ids) { const res = await fetchImpl(`${base}/sendMessage`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...payloadBase, chat_id }), }) let data try { data = await res.json() } catch (e) { throw new Error(`Telegram response parse error: HTTP ${res.status}`) } if (!res.ok || !data?.ok) { const errMsg = data?.description || JSON.stringify(data) throw new Error(`Telegram send failed: ${errMsg}`) } results.push(data) } return results } async function apiCall(method, body) { const token = process.env.TELEGRAM_BOT_TOKEN || '' if (!token) throw new Error('TELEGRAM_BOT_TOKEN missing') const fetchImpl = await getFetch() const base = `https://api.telegram.org/bot${token}` const res = await fetchImpl(`${base}/${method}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }) let data try { data = await res.json() } catch (e) { throw new Error(`Telegram ${method} parse error: HTTP ${res.status}`) } if (!res.ok || !data?.ok) { throw new Error(`Telegram ${method} failed: ${data?.description || JSON.stringify(data)}`) } return data } export async function answerCallbackQuery({ callbackQueryId, text = '', showAlert = false }) { return apiCall('answerCallbackQuery', { callback_query_id: callbackQueryId, text, show_alert: showAlert }) } export async function editMessageText({ chatId, messageId, text, parseMode = 'HTML', replyMarkup = null, disableWebPagePreview = true }) { const body = { chat_id: chatId, message_id: messageId, text, parse_mode: parseMode, disable_web_page_preview: disableWebPagePreview } if (replyMarkup) body.reply_markup = replyMarkup return apiCall('editMessageText', body) } export async function editMessageReplyMarkup({ chatId, messageId, replyMarkup }) { return apiCall('editMessageReplyMarkup', { chat_id: chatId, message_id: messageId, reply_markup: replyMarkup }) } export async function getUpdates({ offset = 0, timeout = 30 }) { return apiCall('getUpdates', { offset, timeout, allowed_updates: ['message', 'callback_query'] }) }