/** * Telegram bildirim kanalı — Bot API, bağımlılık yok. * * KURULUM (kullanıcı/yönetici): BotFather'da bot oluşturulur, tokenı * TELEGRAM_BOT_TOKEN env'ine girilir. Her kullanıcı uygulamadan aldığı 6 haneli * kodu bota yazar; sunucu getUpdates ile kodu görüp chat id'yi kullanıcıya * bağlar. Token bizde tutulmaz, sohbet kimliği kullanıcı verisinde durur. */ const API = () => `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}`; export function telegramConfigured() { return Boolean(process.env.TELEGRAM_BOT_TOKEN); } let botOnbellek = { ad: null, zaman: 0 }; /** Bot kullanıcı adı (t.me/) — arayüzde bağlantı vermek için. */ export async function botAdi() { if (!telegramConfigured()) return null; if (botOnbellek.ad && Date.now() - botOnbellek.zaman < 3600e3) return botOnbellek.ad; try { const r = await fetch(`${API()}/getMe`, { signal: AbortSignal.timeout(10000) }); const d = await r.json(); if (d.ok) { botOnbellek = { ad: d.result.username, zaman: Date.now() }; return d.result.username; } } catch { /* aşağıda null */ } return null; } export async function sendTelegram(chatId, text, { parseMode = 'HTML' } = {}) { if (!telegramConfigured() || !chatId) return false; const r = await fetch(`${API()}/sendMessage`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id: chatId, text: String(text).slice(0, 4000), ...(parseMode === null ? {} : { parse_mode:parseMode }), disable_web_page_preview: true }), signal: AbortSignal.timeout(15000), }); const d = await r.json().catch(() => ({})); if (!d.ok) throw new Error(d.description || `Telegram HTTP ${r.status}`); return true; } /** * Kullanıcının bota yazdığı 6 haneli kodu son güncellemelerde arar. * Bulursa { chatId, ad } döner. Long-polling yok; kullanıcı "doğrula" dediğinde * tek getUpdates çağrısı yeterli (Telegram son 24 saati tutar). */ export async function kodIleBagla(kod) { if (!telegramConfigured()) throw new Error('Sunucuda TELEGRAM_BOT_TOKEN tanımlı değil'); const r = await fetch(`${API()}/getUpdates?limit=100`, { signal: AbortSignal.timeout(15000) }); const d = await r.json().catch(() => ({})); if (!d.ok) throw new Error(d.description || 'getUpdates başarısız'); const aranan = String(kod).trim(); for (const u of [...(d.result || [])].reverse()) { const m = u.message || u.edited_message; if (!m?.text) continue; if (m.text.replace(/\D/g, '').includes(aranan)) { return { chatId: String(m.chat.id), ad: [m.chat.first_name, m.chat.last_name].filter(Boolean).join(' ') || m.chat.username || null }; } } return null; } /** HTML kaçışı — ihale adları '<' '&' içerebiliyor. */ export const tgKacir = (s) => String(s ?? '').replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c]));