Files
study-bible-project/server/reminders.js
T
nmemmertandClaude Sonnet 5 1dd6aef6a5
Build and Push Docker Image / build-and-push (push) Failing after 21s
Add streaks, today view, custom plan editing/chapter ranges, and ntfy reminders
Reading plans now track a cross-plan activity log for streaks, surface a
combined "today's reading" view on both Home and the Reading Plans page,
and let custom plans be edited after creation (including narrowing a book
to a specific chapter range). The reader auto-marks a plan's day read after
a dwell period, active plans that fall behind their pace get a "catch me
up" banner, and a new Settings section wires up daily reading reminders
via ntfy.sh, checked once a minute by a small server-side scheduler.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 13:16:27 -04:00

53 lines
2.0 KiB
JavaScript

// 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);
}