const axios = require('axios'); const adminConfig = require('./adminConfig'); const { readNotificationSettings } = require('./userSettingsStore'); function formatDateLabel(dateInput) { try { const date = new Date(dateInput); if (Number.isNaN(date.getTime())) { return 'unbekanntes Datum'; } return date.toLocaleString('de-DE', { weekday: 'short', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (_error) { return 'unbekanntes Datum'; } } async function sendNtfyNotification(adminNtfy, userNtfy, payload) { if (!adminNtfy?.enabled || !userNtfy?.enabled || !userNtfy.topic) { return; } const server = (userNtfy.serverUrl || adminNtfy.serverUrl || 'https://ntfy.sh').replace(/\/+$/, ''); const sanitizedPrefix = (adminNtfy.topicPrefix || '').replace(/^-+|-+$/g, ''); const sanitizedTopic = (userNtfy.topic || '').replace(/^-+|-+$/g, ''); const separator = sanitizedPrefix && sanitizedTopic ? '-' : ''; const topic = `${sanitizedPrefix}${separator}${sanitizedTopic}` || sanitizedPrefix || sanitizedTopic; let url = `${server}/${topic}`; if (payload.title) { const delimiter = url.includes('?') ? '&' : '?'; url = `${url}${delimiter}title=${encodeURIComponent(payload.title)}`; } const headers = { 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'); headers.Authorization = `Basic ${credentials}`; } await axios.post(url, payload.message, { headers, timeout: 15000 }); } async function sendTelegramNotification(adminTelegram, userTelegram, payload) { if (!adminTelegram?.enabled || !adminTelegram.botToken || !userTelegram?.enabled || !userTelegram.chatId) { return; } const endpoint = `https://api.telegram.org/bot${adminTelegram.botToken}/sendMessage`; await axios.post( endpoint, { chat_id: userTelegram.chatId, text: payload.message, disable_web_page_preview: false }, { timeout: 15000 } ); } async function notifyChannels(profileId, template) { const adminSettings = adminConfig.readSettings(); const userSettings = readNotificationSettings(profileId); try { await Promise.allSettled([ sendNtfyNotification(adminSettings.notifications?.ntfy, userSettings.notifications?.ntfy, template), sendTelegramNotification(adminSettings.notifications?.telegram, userSettings.notifications?.telegram, template) ]); } catch (error) { console.error('[NOTIFICATIONS] Versand fehlgeschlagen:', error.message); } } 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 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: fullMessage, link: storeLink, priority: booked ? 'high' : 'default' }); } async function sendTestNotification(profileId, channel) { const title = 'Pickup Benachrichtigung (Test)'; const message = 'Das ist eine Testnachricht. Bei Fragen wende dich bitte an den Admin.'; const adminSettings = adminConfig.readSettings(); const userSettings = readNotificationSettings(profileId); const tasks = []; if (!channel || channel === 'ntfy') { tasks.push( sendNtfyNotification(adminSettings.notifications?.ntfy, userSettings.notifications?.ntfy, { title, message, priority: 'default' }) ); } if (!channel || channel === 'telegram') { tasks.push( sendTelegramNotification(adminSettings.notifications?.telegram, userSettings.notifications?.telegram, { title, message, priority: 'default' }) ); } if (tasks.length === 0) { throw new Error('Kein unterstützter Kanal oder Kanal deaktiviert.'); } await Promise.all(tasks); } module.exports = { sendSlotNotification, sendTestNotification };