/** * Telegram Notification Service * * Sends alerts to admin via Telegram Bot when: * - New TRC20 purchase is confirmed * - Purchase is marked as distributed * * Configuration (via environment variables or admin settings table): * TELEGRAM_BOT_TOKEN — Bot token from @BotFather (e.g. 123456:ABC-DEF...) * TELEGRAM_CHAT_ID — Chat ID to send messages to (personal or group) * * How to set up: * 1. Open Telegram, search @BotFather, send /newbot * 2. Follow prompts to get your Bot Token * 3. Start a chat with your bot (or add it to a group) * 4. Get Chat ID: https://api.telegram.org/bot/getUpdates * 5. Set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID in environment variables */ const TELEGRAM_API = "https://api.telegram.org"; export interface TelegramConfig { botToken: string; chatId: string; } /** * Get Telegram config from environment variables or DB settings. * Returns null if not configured. */ export async function getTelegramConfig(): Promise { const botToken = process.env.TELEGRAM_BOT_TOKEN; const chatId = process.env.TELEGRAM_CHAT_ID; if (!botToken || !chatId) { return null; } return { botToken, chatId }; } /** * Send a message via Telegram Bot API. * Uses HTML parse mode for formatting. */ export async function sendTelegramMessage( config: TelegramConfig, message: string ): Promise { try { const url = `${TELEGRAM_API}/bot${config.botToken}/sendMessage`; const resp = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ chat_id: config.chatId, text: message, parse_mode: "HTML", disable_web_page_preview: true, }), signal: AbortSignal.timeout(10_000), }); if (!resp.ok) { const err = await resp.text(); console.warn(`[Telegram] Send failed (${resp.status}): ${err}`); return false; } console.log("[Telegram] Message sent successfully"); return true; } catch (e) { console.warn("[Telegram] Send error:", e); return false; } } /** * Notify admin about a new TRC20 purchase. */ export async function notifyNewTRC20Purchase(purchase: { txHash: string; fromAddress: string; usdtAmount: number; xicAmount: number; evmAddress: string | null; }): Promise { const config = await getTelegramConfig(); if (!config) { console.log("[Telegram] Not configured — skipping notification (set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID)"); return; } const evmLine = purchase.evmAddress ? `\nšŸ”‘ EVM Address: ${purchase.evmAddress}` : "\nāš ļø EVM Address: Not provided (manual distribution required)"; const txUrl = `https://tronscan.org/#/transaction/${purchase.txHash}`; const message = [ "🟢 New XIC Presale Purchase!", "", `šŸ’° Amount: $${purchase.usdtAmount.toFixed(2)} USDT`, `šŸŖ™ XIC Tokens: ${purchase.xicAmount.toLocaleString()} XIC`, `šŸ“ From (TRON): ${purchase.fromAddress}`, evmLine, `šŸ”— TX: ${purchase.txHash.slice(0, 16)}...`, "", `ā° ${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })} (CST)`, ].join("\n"); await sendTelegramMessage(config, message); } /** * Notify admin when a purchase is marked as distributed. */ export async function notifyDistributed(purchase: { txHash: string; evmAddress: string; xicAmount: number; distributeTxHash?: string | null; }): Promise { const config = await getTelegramConfig(); if (!config) return; const distTxLine = purchase.distributeTxHash ? `\nšŸ”— Dist TX: ${purchase.distributeTxHash}` : ""; const message = [ "āœ… XIC Tokens Distributed!", "", `šŸŖ™ XIC Tokens: ${purchase.xicAmount.toLocaleString()} XIC`, `šŸ“¬ To EVM: ${purchase.evmAddress}`, distTxLine, `šŸ”— Original TX: ${purchase.txHash.slice(0, 16)}...`, "", `ā° ${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })} (CST)`, ].join("\n"); await sendTelegramMessage(config, message); } /** * Test the Telegram connection with a test message. * Returns { success, error } for admin UI feedback. */ export async function testTelegramConnection( botToken: string, chatId: string ): Promise<{ success: boolean; error?: string }> { try { const config: TelegramConfig = { botToken, chatId }; const message = [ "šŸ”” NAC Presale — Telegram Notification Test", "", "āœ… Connection successful! You will receive purchase alerts here.", "", `ā° ${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })} (CST)`, ].join("\n"); const ok = await sendTelegramMessage(config, message); if (!ok) return { success: false, error: "Failed to send message. Check Bot Token and Chat ID." }; return { success: true }; } catch (e) { return { success: false, error: e instanceof Error ? e.message : String(e) }; } }