Persist notification email batches with leased idempotent delivery

This commit is contained in:
2026-09-06 13:03:47 +03:00
parent cbfdd072d1
commit d956de3014
7 changed files with 122 additions and 17 deletions
+20 -13
View File
@@ -9,8 +9,8 @@ import {deadlineInfo} from '../src/services/tenderCalendar.js';
* NOTIFY_MAX=25 → e-postada listelenecek en fazla ihale
*/
import { list, markSent, markBaseline, available } from './notify-store.mjs';
import { emailConfigured, sendEmail, tenderDigestHtml } from './email.mjs';
import { list, markBaseline, reserveDelivery, acceptDelivery, failDelivery, available } from './notify-store.mjs';
import { emailConfigured, emailFrom, sendEmail, tenderDigestHtml } from './email.mjs';
import { telegramConfigured, sendTelegram, tgKacir } from './telegram.mjs';
import { getData } from './user-store.mjs';
@@ -52,7 +52,7 @@ export function bildirilebilir(t,now=new Date()) {
*/
let processing=false;
export async function bildirimleriIsle(tenderGetir, { zorla = false } = {}, dependencies={}) {
const {readSubscriptions=list,saveSent=markSent,saveBaseline=markBaseline,configured=emailConfigured,
const {readSubscriptions=list,saveBaseline=markBaseline,reserve=reserveDelivery,accepted=acceptDelivery,failed=failDelivery,configured=emailConfigured,
deliverEmail=sendEmail,telegramReady=telegramConfigured,deliverTelegram=sendTelegram,now=()=>new Date()}=dependencies;
if(processing)return {atlandi:'önceki tur sürüyor'};
processing=true;
@@ -80,12 +80,12 @@ export async function bildirimleriIsle(tenderGetir, { zorla = false } = {}, depe
// İlk kurulumda geçmişi spam'lemeyelim: sadece işaretle, gönderme.
// zorla=true ise (yönetici "şimdi gönder" dediğinde) bu adım atlanır.
if (!a.initialized && !zorla) {
if (!a.initialized && !a.deliveryPending && !zorla) {
saveBaseline(a.id, eslesen.map((t) => t.id));
continue;
}
const gonderilecek = (zorla && !yeniler.length ? eslesen : yeniler).slice(0,maxAdet);
if (!gonderilecek.length) continue;
if (!gonderilecek.length && !a.deliveryPending) continue;
const kriterMetni = [
a.sehir && `İl: ${a.sehir}`,
@@ -102,22 +102,29 @@ export async function bildirimleriIsle(tenderGetir, { zorla = false } = {}, depe
Bu bildirimi uygulamadaki Mobil PWA → E-posta Bildirimi bölümünden kapatabilirsiniz.
</p>`;
await deliverEmail({
to: a.email,
const delivery=reserve(a.id,{
from:emailFrom(),to: a.email,
subject: `İhaleist — ${gonderilecek.length} ihale (${kriterMetni})`,
html,
});
html,digest:gonderilecek,criteria:kriterMetni,
},gonderilecek.map(t=>t.id),{force:zorla});
if(!delivery)continue;
if(delivery.blocked){hata++;continue;}
try {
const result=await deliverEmail({...delivery.payload,idempotencyKey:'notify/'+delivery.id});
accepted(delivery,result?.id);
}catch(error){failed(delivery,error);throw error;}
// Telegram bağlıysa aynı özet oraya da (kısa biçim)
try {
const tg = telegramReady() ? getData(a.email, 'telegram') : null;
if (tg?.chatId) {
const satirlar = gonderilecek.slice(0, 10).map((t) => `• ${tgKacir(t.baslik)}\n ${tgKacir(t.kurum || '')} · ${tgKacir(t.tarih || '')} · ${tgKacir(t.ikn || '')}`);
await deliverTelegram(tg.chatId, `<b>İhaleist — ${gonderilecek.length} yeni ihale</b> (${tgKacir(kriterMetni)})\n\n${satirlar.join('\n')}${gonderilecek.length > 10 ? `\n… +${gonderilecek.length - 10} ihale` : ''}`);
const deliveredTenders=delivery.payload.digest || [];
const deliveredCriteria=delivery.payload.criteria || '';
const satirlar = deliveredTenders.slice(0, 10).map((t) => `• ${tgKacir(t.baslik)}\n ${tgKacir(t.kurum || '')} · ${tgKacir(t.tarih || '')} · ${tgKacir(t.ikn || '')}`);
await deliverTelegram(tg.chatId, `<b>İhaleist — ${deliveredTenders.length} yeni ihale</b> (${tgKacir(deliveredCriteria)})\n\n${satirlar.join('\n')}${deliveredTenders.length > 10 ? `\n… +${deliveredTenders.length - 10} ihale` : ''}`);
}
} catch (e) { console.warn(`[notifier] telegram ${a.email}: ${e.message}`); }
saveSent(a.id, gonderilecek.map((t) => t.id));
gonderilen++;
console.log(`[notifier] ${a.email} → ${gonderilecek.length} ihale gönderildi (${kriterMetni})`);
console.log(`[notifier] ${a.email} → ${delivery.tenderIds.length} ihale gönderildi (${kriterMetni})`);
} catch (e) {
hata++;
console.warn(`[notifier] abonelik ${a.id} gönderilemedi: ${e.message}`);