import { briefWeekKey } from './brief-outbox.mjs'; import { splitBriefText } from './brief-content.mjs'; /** Delivery accounting shared by scheduled and administrator-triggered briefs. */ export function createBriefSender({ store, buildBrief, text, html, telegramConfigured, sendTelegram, emailConfigured, sendEmail, outbox, clock = () => new Date(), warn = console.warn }) { return async function sendBrief(tenderGetir) { const tenders = await tenderGetir(); const r = { kullanici:0, gonderilen:0, bos:0, hata:0, kapali:0, kanalsiz:0, atlanan:0, bekleyen:0, kanallar:{ telegram:{denenen:0,kabul:0,hata:0}, email:{denenen:0,kabul:0,hata:0} } }; for (const user of store.listUsers()) { r.kullanici++; let accepted = false, failed = false; try { // Manual runs must also honor the recipient's opt-out. if (store.getData(user.email, 'brief')?.kapali) { r.kapali++; continue; } const brief = buildBrief(user.email, tenders); if (brief.bos) { r.bos++; continue; } const channels = []; const telegram = store.getData(user.email, 'telegram'); if (telegramConfigured() && telegram?.chatId) { for (const part of splitBriefText(text(brief))) channels.push({ payload:{channel:'telegram',chatId:telegram.chatId,text:part}, name:'telegram', send:() => sendTelegram(telegram.chatId, part, { parseMode:null }), valid:receipt => receipt === true, }); } if (emailConfigured()) channels.push({ payload:{channel:'email',to:user.email,subject:`İhaleist — Haftalık Brief (${brief.hafta})`,html:html(brief),text:text(brief)}, name:'email', send:() => sendEmail({ to:user.email, subject:`İhaleist — Haftalık Brief (${brief.hafta})`, html:html(brief), text:text(brief) }), valid:receipt => typeof receipt?.id === 'string' && Boolean(receipt.id.trim()), }); if (!channels.length) { r.kanalsiz++; continue; } const batch = outbox?.prepare(briefWeekKey(clock()),user.email,channels.map(channel=>channel.payload)); if (batch?.changed) { r.bekleyen++; r.hata++; continue; } const failedChannels = new Set(); for (const [position,channel] of channels.entries()) { if (failedChannels.has(channel.name)) continue; // An earlier channel can await the network while the user disables briefs. if (store.getData(user.email, 'brief')?.kapali) { r.kapali++; break; } const claim = outbox?.claim(batch.id,position,clock().getTime()); if (claim?.state === 'accepted') { r.atlanan++; continue; } if (claim && claim.state !== 'sending') { r.bekleyen++; failedChannels.add(channel.name); if (claim.state === 'uncertain') failed=true; continue; } const counts = r.kanallar[channel.name]; counts.denenen++; try { const receipt = await channel.send(); if (!channel.valid(receipt)) throw new Error('Sağlayıcı gönderimi doğrulamadı.'); if (claim) outbox.accept(claim,receipt === true ? 'telegram-accepted' : receipt.id); counts.kabul++; accepted = true; } catch (error) { if (claim) outbox.hold(claim,error); counts.hata++; failedChannels.add(channel.name); failed = true; warn('[brief]', user.email, channel.name, error.message); } } } catch (error) { failed = true; warn('[brief]', user.email, error.message); } if (accepted) r.gonderilen++; if (failed) r.hata++; } return r; }; }