feat: add ntfy and telegram notification workflows

This commit is contained in:
2025-11-09 20:08:14 +01:00
parent d4a28c9897
commit 539759ed60
6 changed files with 820 additions and 6 deletions

View File

@@ -0,0 +1,128 @@
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 topicPrefix = adminNtfy.topicPrefix ? `${adminNtfy.topicPrefix.replace(/\/+$/, '')}/` : '';
const topic = `${topicPrefix}${userNtfy.topic.replace(/^\//, '')}`;
const url = `${server}/${topic}`;
const headers = {
Title: payload.title,
Priority: payload.priority || 'default'
};
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.title}\n${payload.message}`,
disable_web_page_preview: true
},
{ 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 }) {
const dateLabel = formatDateLabel(pickupDate);
const title = onlyNotify
? `Slot verfügbar bei ${storeName}`
: booked
? `Slot gebucht bei ${storeName}`
: `Slot gefunden bei ${storeName}`;
const message = onlyNotify
? `Es wurde ein freier Slot am ${dateLabel} entdeckt.`
: booked
? `Der Slot am ${dateLabel} wurde erfolgreich gebucht.`
: `Es wurde ein Slot am ${dateLabel} gefunden.`;
await notifyChannels(profileId, {
title,
message,
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
};