Files
Pickup-Config/services/notificationService.js

135 lines
4.4 KiB
JavaScript

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;
const url = `${server}/${topic}`;
const headers = {
Title: payload.title,
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
};