// Daily "don't forget to read" reminders, sent via ntfy.sh (https://ntfy.sh) — // a free pub/sub push service: POST a message to a topic URL, anyone // subscribed to that topic (via the ntfy app or a browser) gets notified. // No API keys or push-subscription plumbing needed. import { getUsersWithRemindersEnabled, setLastReminderDate, getReadingProgress } from './db.js'; function todayDateString(d = new Date()) { return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; } function currentHHMM(d = new Date()) { return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; } async function sendNtfyReminder(user) { const base = (user.ntfyUrl || 'https://ntfy.sh').replace(/\/+$/, ''); const url = `${base}/${encodeURIComponent(user.ntfyTopic)}`; const res = await fetch(url, { method: 'POST', headers: { Title: 'Reading Plan Reminder', Tags: 'open_book' }, body: "Don't forget today's reading!", }); if (!res.ok) throw new Error(`ntfy responded ${res.status}`); } async function checkReminders() { const now = new Date(); const hhmm = currentHHMM(now); const today = todayDateString(now); const due = getUsersWithRemindersEnabled().filter( (u) => u.reminderTime === hhmm && u.lastReminderDate !== today, ); for (const user of due) { try { const progress = getReadingProgress(user.id); const hasActivePlan = Array.isArray(progress?.progress) && progress.progress.some((p) => !p.completedAt); if (!hasActivePlan) continue; // nothing to nudge them about await sendNtfyReminder(user); setLastReminderDate(user.id, today); } catch (err) { console.error(`Reading reminder failed for user ${user.id}:`, err.message); } } } /** Checks once a minute whether any user's reminder time has arrived. */ export function startReminderScheduler() { setInterval(() => { checkReminders().catch((err) => console.error('Reminder scheduler error:', err)); }, 60 * 1000); }