Files
ihaleist/server/brief.mjs
T

88 lines
4.9 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.
// Haftalık brief — Pazartesi 08:00: takipteki ihalelerden bu hafta yapılacaklar,
// önceki yedi takvim günündeki takipli sonuç kayıtları.
// Telegram (bağlıysa) + e-posta; kalıcı mesaj kabul kayıtları brief.sqlite.
// Eski brief-son.json yalnız geçiş haftasında tekrar göndermemek için okunur.
import fs from 'node:fs';
import { createBriefOutbox } from './brief-outbox.mjs';
import { createBriefBuilder } from './brief-content.mjs';
import { createBriefSender } from './brief-delivery.mjs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as store from './user-store.mjs';
import { resultsByIkn } from './history-store.mjs';
import { emailConfigured, sendEmail } from './email.mjs';
import { sendTelegram, telegramConfigured } from './telegram.mjs';
import { yaklasanIhaleler, trTarih } from './feeds-mcp.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DURUM_DOSYASI = path.join(__dirname, 'data', 'brief-son.json');
function sonGonderim() {
try { return JSON.parse(fs.readFileSync(DURUM_DOSYASI, 'utf8')); } catch { return {}; }
}
const paraTR = (v, birim) => (v == null ? '' : `${Number(v).toLocaleString('tr-TR')} ${birim || 'TL'}`);
/** Bir kullanıcı için brief içeriği. */
export const briefOlustur = createBriefBuilder({
trackedKeys:store.getTrackedTenderKeys, upcoming:yaklasanIhaleler, results:resultsByIkn, parseDate:trTarih,
});
const deadlineText = t => t.son_teklif_zamani
? new Date(t.son_teklif_zamani).toLocaleTimeString('tr-TR', { timeZone: 'Europe/Istanbul', hour: '2-digit', minute: '2-digit' }) + ' (Türkiye saati)'
: 'saat doğrulanmalı';
export function briefMetni(b) {
const s = [`📋 Haftalık İhale Brief — ${b.hafta}`];
if (b.yaklasan.length) {
s.push('', `⏰ Bu hafta yapılacak takipli ihaleler (${b.yaklasan.length}):`);
for (const t of b.yaklasan) s.push(`• ${t.kalan_gun === 0 ? 'BUGÜN' : t.kalan_gun + ' gün'} — ${t.baslik} (${t.kurum}, ${t.tarih} · ${deadlineText(t)})`);
}
if (b.sonuclar.length) {
s.push('', `🏁 Önceki 7 takvim günündeki takipli sonuç kayıtları (${b.sonuclar.length}):`);
for (const r of b.sonuclar) s.push(`• ${r.baslik} → ${r.kazanan || 'kazanan açıklanmadı'}${r.bedel != null ? ' · ' + paraTR(r.bedel, r.birim) : ''}${r.teklif != null ? ' · ' + r.teklif + ' teklif' : ''}`);
}
s.push('', 'ihaleist.com');
return s.join('\n');
}
export function briefHtml(b) {
const esc = (x) => String(x ?? '').replace(/[<>&]/g, (c) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;' }[c]));
const bolum = (baslik, satirlar) => satirlar.length
? `<h3 style="margin:18px 0 6px;font-size:15px;color:#0f172a">${baslik}</h3><ul style="margin:0;padding-left:18px;color:#334155;font-size:14px">${satirlar.map((x) => `<li style="margin:4px 0">${x}</li>`).join('')}</ul>` : '';
return `<div style="font-family:Inter,Arial,sans-serif;max-width:640px;margin:auto;padding:20px;color:#0f172a">
<h2 style="margin:0 0 4px;font-size:20px">📋 Haftalık İhale Brief</h2><div style="color:#64748b;font-size:13px">${esc(b.hafta)}</div>
${bolum('⏰ Bu hafta yapılacak takipli ihaleler', b.yaklasan.map((t) => `<b>${t.kalan_gun === 0 ? 'BUGÜN' : t.kalan_gun + ' gün'}</b> — ${esc(t.baslik)} (${esc(t.kurum)}, ${esc(t.tarih)} · ${esc(deadlineText(t))})`))}
${bolum('🏁 Önceki 7 takvim günündeki takipli sonuç kayıtları', b.sonuclar.map((r) => `${esc(r.baslik)} → ${esc(r.kazanan || 'kazanan açıklanmadı')}${r.bedel != null ? ' · ' + esc(paraTR(r.bedel, r.birim)) : ''}`))}
<p style="margin-top:20px;font-size:12px;color:#94a3b8">Bu brief her Pazartesi gönderilir; Bildirimler sekmesinden kapatabilirsiniz.</p></div>`;
}
export const briefOutbox = createBriefOutbox(process.env.IHALEIST_USER_DATA_DIR || path.join(__dirname,'data'));
export const haftalikBriefGonder = createBriefSender({
store, buildBrief:briefOlustur, text:briefMetni, html:briefHtml,
telegramConfigured, sendTelegram, emailConfigured, sendEmail,
outbox:briefOutbox,
});
export function briefScheduleKey(now = new Date()) {
const tr = new Date(now.getTime() + 3 * 3600000);
if (tr.getUTCDay() !== 1 || tr.getUTCHours() < 8) return null;
return `${tr.getUTCFullYear()}-${tr.getUTCMonth()}-${tr.getUTCDate()}`;
}
/** Saatlik kontrol: Türkiye saatiyle Pazartesi ≥08:00. */
export function briefZamanlayici(tenderGetir) {
if (process.env.BRIEF_ENABLED === '0') { console.log('[brief] haftalık brief devre dışı (BRIEF_ENABLED=0)'); return; }
const kontrol = async () => {
const anahtar = briefScheduleKey();
if (!anahtar) return;
const d = sonGonderim();
if (d.son === anahtar) return;
const r = await haftalikBriefGonder(tenderGetir).catch((e) => ({ hata: e.message }));
console.log('[brief] haftalık brief:', JSON.stringify(r));
};
setTimeout(kontrol, 3 * 60 * 1000).unref?.();
setInterval(kontrol, 60 * 60 * 1000).unref?.();
console.log('[brief] haftalık brief zamanlandı (Pazartesi 08:00, saatlik kontrol)');
}