const { Client, LocalAuth } = require('whatsapp-web.js'); const qrcode = require('qrcode-terminal'); const qrcodeLib = require('qrcode'); const cron = require('node-cron'); const http = require('http'); const fs = require('fs'); const path = require('path'); const CONTACTS_PATH = path.join(__dirname, '..', 'config', 'contacts.json'); const SETTINGS_PATH = path.join(__dirname, '..', 'config', 'settings.json'); const DATA_PATH = path.join(__dirname, '..', 'data'); let currentQR = null; let isReady = false; function loadSettings() { return JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8')); } function loadContacts() { return JSON.parse(fs.readFileSync(CONTACTS_PATH, 'utf8')); } // Gibt den Kontakt zurück der im angegebenen Monat dran ist (wiederholt sich jedes Jahr). function getContactForMonth(year, month) { const settings = loadSettings(); const contacts = loadContacts(); const schedule = settings.schedule; if (!Array.isArray(schedule) || schedule.length !== 12) { console.log('schedule in settings.json muss ein Array mit 12 Einträgen sein.'); return null; } const contactId = schedule[month - 1]; if (!contactId) { console.log(`Monat ${month}/${year}: kein Kontakt (null) — nichts zu tun.`); return null; } const contact = contacts.find(c => c.id === contactId); if (!contact) { console.log(`Kontakt-ID "${contactId}" nicht in contacts.json gefunden.`); return null; } return contact; } async function sendReminders(overrideYear = null, overrideMonth = null) { const settings = loadSettings(); const now = new Date(); const year = overrideYear ?? now.getFullYear(); const month = overrideMonth ?? (now.getMonth() + 1); const monthName = new Date(year, month - 1, 1).toLocaleString('de-DE', { month: 'long' }); const contact = getContactForMonth(year, month); if (!contact) { return { skipped: true, reason: `Kein Kontakt für ${monthName} ${year}` }; } const vorname = contact.name.split(' ')[0]; const message = settings.message .replace(/{vorname}/g, vorname) .replace(/{name}/g, contact.name) .replace(/{amount}/g, settings.amount ?? '') .replace(/{currency}/g, settings.currency ?? '') .replace(/{month}/g, monthName) .replace(/{year}/g, year); const phone = contact.phone.replace(/\D/g, ''); const chatId = `${phone}@c.us`; console.log(`[${monthName} ${year}] Sende an: ${contact.name} (${contact.phone})`); try { await client.sendMessage(chatId, message); console.log(`[OK] Nachricht gesendet.`); return { sent: true, contact: contact.name }; } catch (err) { console.error(`[FEHLER] ${err.message}`); return { sent: false, error: err.message }; } } // Web-Interface const server = http.createServer(async (req, res) => { const url = new URL(req.url, 'http://localhost'); if (url.pathname === '/qr') { if (!currentQR) { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); return res.end(htmlPage('Status', isReady ? '
✓ Verbunden und bereit
' : 'Kein QR-Code verfügbar — Client startet noch, bitte kurz warten...
' )); } const qrDataUrl = await qrcodeLib.toDataURL(currentQR); res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); return res.end(htmlPage('QR-Code scannen', `Öffne WhatsApp → Verknüpfte Geräte → Gerät verknüpfen
Seite neu laden falls der Code abgelaufen ist.
`)); } if (url.pathname === '/send-now') { if (!isReady) { res.writeHead(503, { 'Content-Type': 'text/html; charset=utf-8' }); return res.end(htmlPage('Nicht bereit', 'Client ist noch nicht verbunden. Erst QR-Code scannen.
')); } // Optionale Query-Parameter: ?year=2026&month=5 const year = url.searchParams.has('year') ? parseInt(url.searchParams.get('year')) : null; const month = url.searchParams.has('month') ? parseInt(url.searchParams.get('month')) : null; const result = await sendReminders(year, month); res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); if (result.skipped) { return res.end(htmlPage('Übersprungen', `⏭ ${result.reason}
`)); } return res.end(htmlPage(result.sent ? 'Gesendet' : 'Fehler', ` ${result.sent ? `✓ Nachricht an ${result.contact} gesendet
` : `✗ Fehler: ${result.error}
`} `)); } if (url.pathname === '/status') { res.writeHead(200, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ ready: isReady, qr_pending: !!currentQR })); } // Startseite mit Monatsübersicht let scheduleHtml = ''; try { const settings = loadSettings(); const contacts = loadContacts(); const now = new Date(); const thisYear = now.getFullYear(); const thisMonth = now.getMonth() + 1; const monthNames = ['', 'Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember']; const plan = settings.schedule ?? []; const rows = plan.map((id, i) => { const m = i + 1; const contact = id ? contacts.find(c => c.id === id) : null; const isCurrent = m === thisMonth; return `Fehler beim Laden des Plans: ${e.message}
`; } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(htmlPage('WhatsApp Reminder', `Status: ${isReady ? '✓ Verbunden' : currentQR ? 'QR-Code scannen' : 'Startet...'}
▶ Erinnerung für diesen Monat jetzt senden
${scheduleHtml} `)); }); function htmlPage(title, body) { return `