Files
ihaleist/server/telegram.mjs
T
hikmettanandClaude Fable 5.1 df89eaac33 feat: takip izleme (zeyilname + sonuc bildirimi), hazirlik gorevi, belge sure uyarisi, Telegram, OCR, yasakli sorgu
28 depo taramasindan alinan alti ozellik. Kod kopyalanmadi (depolar
lisanssiz), fikirler uygulandi.

1) Zeyilname / duzeltme ilani takibi (server/zeyilname.mjs): takipteki
   IKN'ler sunucuya yazilir; 3 saatte bir EKAP detayindan ilan sayisi ve
   ihale tarihi karsilastirilir, degisince "ne degisti" bildirimi (uygulama
   ici + e-posta + Telegram). Test: anlik kaydi eskitilince tur "yeni ilan
   yayimlandi; ihale tarihi 01.01.2026 → 08.10.2026" bildirimi uretti.
2) Sonuc bildirimi: takipteki ihale sonuclaninca "X firmasi Y'ye aldi, %Z
   kirim". Yalniz takiptekiler; kismi ihalede oran yazilmaz.
3) Ihaleden goreve (MOBIT-DURT): detayda "Hazirlik Gorevi Ac" → teslim
   tarihi ihale saati, pano "Teklif Hazirlaniyor", derin analizden gelen
   belge kontrol listesi gorevde; Kanban kartinda geri sayim rozeti.
4) Sirket belgesi sure takibi: Belge Kasasi'nda son kullanma tarihi girisi,
   dolmus / 30 gun icinde dolacak uyarisi; girise gunde bir bildirim.
   PATCH /api/me/documents/meta.
5) Telegram (server/telegram.mjs): TELEGRAM_BOT_TOKEN sunucuda; kullanici
   6 haneli kodu bota yazar, "Dogrula" → getUpdates ile chat id baglanir.
   Arayuzde adim adim kurulum rehberi (BotFather → env → Redeploy → kod).
   Ihale ozeti, zeyilname ve sonuc bildirimleri Telegram'a da gider.
6) OCR (tesseract.js, tur): PDF metin katmani bossa (taranmis) ilk 8 sayfa
   OCR. Olcum: sentetik cetvelde guven 87, ~0,3 sn/sayfa; IKN ve kalemler
   dogru okundu. Kalem havuzu ve derin analiz artik taranmis PDF'ten de
   besleniyor.
7) Rakip Radari'nda KIK yasakli sorgusu baglantisi (firma adi panoya
   kopyalanir; EKAP sorguyu API'den vermiyor).

Sinir deger hesaplayici KURULMADI: Kamu Ihale Genel Tebligi'ne ulasilamadi
(mevzuat.gov.tr / ihale.gov.tr bu makineden erisilemiyor); formul kaynaksiz
yazilmadi.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 22:58:13 +03:00

65 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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/<ad>) — 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) {
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), parse_mode: 'HTML', 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) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));