From e12bba63b9807af5e67f3e0945445000ef457fe3 Mon Sep 17 00:00:00 2001 From: Meik Date: Sun, 9 Nov 2025 21:25:50 +0100 Subject: [PATCH] feat: auto-disable store after notification and improve ntfy payloads --- services/notificationService.js | 16 ++++++++----- services/pickupScheduler.js | 42 +++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/services/notificationService.js b/services/notificationService.js index bcbdb8c..17e7be6 100644 --- a/services/notificationService.js +++ b/services/notificationService.js @@ -33,7 +33,8 @@ async function sendNtfyNotification(adminNtfy, userNtfy, payload) { const url = `${server}/${topic}`; const headers = { Title: payload.title, - Priority: payload.priority || 'default' + Priority: payload.priority || 'default', + 'Content-Type': 'text/plain; charset=utf-8' }; if (adminNtfy.username && adminNtfy.password) { const credentials = Buffer.from(`${adminNtfy.username}:${adminNtfy.password}`).toString('base64'); @@ -51,8 +52,8 @@ async function sendTelegramNotification(adminTelegram, userTelegram, payload) { endpoint, { chat_id: userTelegram.chatId, - text: `${payload.title}\n${payload.message}`, - disable_web_page_preview: true + text: payload.message, + disable_web_page_preview: false }, { timeout: 15000 } ); @@ -71,22 +72,25 @@ async function notifyChannels(profileId, template) { } } -async function sendSlotNotification({ profileId, storeName, pickupDate, onlyNotify, booked }) { +async function sendSlotNotification({ profileId, storeName, pickupDate, onlyNotify, booked, storeId }) { const dateLabel = formatDateLabel(pickupDate); const title = onlyNotify ? `Slot verfügbar bei ${storeName}` : booked ? `Slot gebucht bei ${storeName}` : `Slot gefunden bei ${storeName}`; - const message = onlyNotify + const baseMessage = onlyNotify ? `Es wurde ein freier Slot am ${dateLabel} entdeckt.` : booked ? `Der Slot am ${dateLabel} wurde erfolgreich gebucht.` : `Es wurde ein Slot am ${dateLabel} gefunden.`; + const storeLink = storeId ? `https://foodsharing.de/store/${storeId}` : null; + const fullMessage = storeLink ? `${title}\n${baseMessage}\n${storeLink}` : `${title}\n${baseMessage}`; await notifyChannels(profileId, { title, - message, + message: fullMessage, + link: storeLink, priority: booked ? 'high' : 'default' }); } diff --git a/services/pickupScheduler.js b/services/pickupScheduler.js index e344939..df0e5fa 100644 --- a/services/pickupScheduler.js +++ b/services/pickupScheduler.js @@ -3,6 +3,7 @@ const foodsharingClient = require('./foodsharingClient'); const sessionStore = require('./sessionStore'); const { DEFAULT_SETTINGS } = require('./adminConfig'); const notificationService = require('./notificationService'); +const { readConfig, writeConfig } = require('./configStore'); const weekdayMap = { Montag: 'Monday', @@ -55,6 +56,34 @@ function resolveSettings(settings) { }; } +function deactivateEntryInMemory(entry) { + if (entry) { + entry.active = false; + } +} + +function persistEntryDeactivation(profileId, entryId) { + if (!profileId || !entryId) { + return; + } + try { + const config = readConfig(profileId); + let changed = false; + const updated = config.map((item) => { + if (String(item?.id) === String(entryId) && item?.active !== false) { + changed = true; + return { ...item, active: false }; + } + return item; + }); + if (changed) { + writeConfig(profileId, updated); + } + } catch (error) { + console.error(`[CONFIG] Konnte Eintrag ${entryId} für Profil ${profileId} nicht deaktivieren:`, error.message); + } +} + async function ensureSession(session) { const profileId = session.profile?.id; if (!profileId) { @@ -166,8 +195,11 @@ async function processBooking(session, entry, pickup) { storeName, pickupDate: pickup.date, onlyNotify: true, - booked: false + booked: false, + storeId: entry.id }); + deactivateEntryInMemory(entry); + persistEntryDeactivation(session.profile.id, entry.id); return; } @@ -185,14 +217,20 @@ async function processBooking(session, entry, pickup) { storeName, pickupDate: pickup.date, onlyNotify: false, - booked: true + booked: true, + storeId: entry.id }); + deactivateEntryInMemory(entry); + persistEntryDeactivation(session.profile.id, entry.id); } catch (error) { console.error(`[ERROR] Buchung für ${storeName} am ${readableDate} fehlgeschlagen:`, error.message); } } async function checkEntry(sessionId, entry, settings) { + if (entry?.active === false) { + return; + } const session = sessionStore.get(sessionId); if (!session) { return;