Version history, notes on highlights and bookmarks, "you've studied this", prayer journal
Build and Push Docker Image / build-and-push (push) Successful in 5m13s
Build and Push Docker Image / build-and-push (push) Successful in 5m13s
- Earlier versions of a study: the server keeps compressed copies (about one per ten minutes of editing, the latest 30); Export shows what differs and puts one back (the replaced copy is kept) - Notes on highlights and bookmarks: synced, merged without loss, in exports and backups - Reader verse panel lists your studies and chunks that cover the verse, and opens them - Prayer journal: requests, who for, linked verses, days prayed, answers; synced, in backups and the account export; daily prayer reminder through ntfy - TODO.md lists the ideas not built yet Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54cb1c217d
commit
207bfcb919
45 files changed
+1669
-75
No files matched your search
+33
-1
@@ -4,7 +4,8 @@
|
||||
// No API keys or push-subscription plumbing needed.
|
||||
import { lookup } from 'dns/promises';
|
||||
import { isIP } from 'net';
|
||||
import { getUsersWithRemindersEnabled, setLastReminderDate, getReadingProgress, getUsersWithVotdEnabled, setLastVotdDate } from './db.js';
|
||||
import { getUsersWithRemindersEnabled, setLastReminderDate, getReadingProgress, getUsersWithVotdEnabled, setLastVotdDate, getUsersWithPrayerReminder, setLastPrayerDate, getPrayers } from './db.js';
|
||||
import { openPrayers } from './prayers.js';
|
||||
import { fetchVerseOfDay, votdMessage } from './votd.js';
|
||||
|
||||
// A reminder still goes out if the server was briefly down (or restarting) at the
|
||||
@@ -142,10 +143,41 @@ export async function checkVotd(now = new Date()) {
|
||||
}
|
||||
}
|
||||
|
||||
// The message for the daily prayer reminder: how many requests are open, and the first few.
|
||||
export function prayerMessage(open) {
|
||||
const lines = open.slice(0, 3).map((p) => `• ${p.forWhom ? `${p.forWhom}: ` : ''}${p.text.replace(/\s+/g, ' ').slice(0, 80)}${p.text.length > 80 ? '…' : ''}`);
|
||||
const more = open.length - lines.length;
|
||||
return {
|
||||
title: open.length === 1 ? 'Time to pray: 1 request' : `Time to pray: ${open.length} requests`,
|
||||
body: `${lines.join('\n')}${more > 0 ? `\n…and ${more} more` : ''}`,
|
||||
tags: 'pray',
|
||||
};
|
||||
}
|
||||
|
||||
// Sends each person who asked for it a nudge to pray, with what they are praying about. Nothing is sent when
|
||||
// the journal has no open requests.
|
||||
export async function checkPrayerReminders(now = new Date()) {
|
||||
for (const user of getUsersWithPrayerReminder().filter((u) => isDue({ reminderTime: u.prayerTime, reminderTz: u.reminderTz, lastReminderDate: u.lastPrayerDate }, now))) {
|
||||
const open = openPrayers(getPrayers(user.id));
|
||||
if (open.length === 0) continue;
|
||||
const key = `prayer:${user.id}`;
|
||||
if (now.getTime() - (_lastAttempt.get(key) ?? 0) < RETRY_AFTER_MS) continue;
|
||||
_lastAttempt.set(key, now.getTime());
|
||||
try {
|
||||
await sendNtfy(user, prayerMessage(open));
|
||||
_lastAttempt.delete(key);
|
||||
setLastPrayerDate(user.id, zonedNow(now, user.reminderTz).date);
|
||||
} catch (err) {
|
||||
console.error(`Prayer 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));
|
||||
checkVotd().catch((err) => console.error('Verse of the day scheduler error:', err));
|
||||
checkPrayerReminders().catch((err) => console.error('Prayer reminder scheduler error:', err));
|
||||
}, 60 * 1000);
|
||||
}
|
||||
Reference in new issue
Block a user