agep-cargo/backend/telegram.js
2025-10-23 16:35:54 +02:00

104 lines
3.7 KiB
JavaScript

// Lightweight Telegram sender
// Sends messages via Telegram Bot API: https://api.telegram.org/bot<token>/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'] })
}