Invite-only sign-up, present mode, series, handouts, templates, dictation, progress, sharing
Build and Push Docker Image / build-and-push (push) Successful in 6m47s
Build and Push Docker Image / build-and-push (push) Successful in 6m47s
- Admin: sign-up Open / Invite only / Closed with single-use invite links - Present mode for scripts (auto-scroll) and outlines (step through, pace vs planned minutes) - Series: group studies on the dashboard, filter by series, calendar file for a study's sessions - Lesson handout: printable page per session (passage, outline, questions, writing lines) - Save a chunk's notes as your own study template (synced) - Voice dictation for notes and scripts (browser speech recognition) - Reading progress: map of the 66 books and a calendar of days read, synced and merged across devices - Export highlights and bookmarks as Markdown or Word, grouped by named colour - Verse of the day push to the ntfy topic (needs IQBIBLE_API_KEY) - Sharing: co-teacher editing via single-use invite links; read-only links can expire and take a passcode Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
88bc59bac0
commit
54cb1c217d
73 files changed
+2739
-134
No files matched your search
@@ -0,0 +1,33 @@
|
||||
// Today's verse of the day for the push notification: IQ Bible picks it (the same verse for everyone each UTC day),
|
||||
// in the KJV, which is free to quote. Needs IQBIBLE_API_KEY on the server. One lookup a day serves every user.
|
||||
import { bookNameFor } from './bibleApi.js';
|
||||
|
||||
let cached = null; // { date, verse }
|
||||
|
||||
export const votdAvailable = () => Boolean(process.env.IQBIBLE_API_KEY);
|
||||
|
||||
// IQ Bible's answer -> { reference, text }, or null when it isn't one we can use
|
||||
export function parseVotd(body) {
|
||||
const d = body?.data;
|
||||
const book = bookNameFor(d?.book_usfm);
|
||||
if (!book || !d?.chapter_number || !d?.verse_number || typeof d.text !== 'string' || !d.text.trim()) return null;
|
||||
return { reference: `${book} ${d.chapter_number}:${d.verse_number}`, text: d.text.replace(/^[¶§*\s]+/, '').replace(/\s+/g, ' ').trim() };
|
||||
}
|
||||
|
||||
export async function fetchVerseOfDay(now = new Date(), fetcher = fetch) {
|
||||
const date = now.toISOString().slice(0, 10);
|
||||
if (cached?.date === date) return cached.verse;
|
||||
if (!votdAvailable()) return null;
|
||||
const base = (process.env.IQBIBLE_API_BASE || 'https://api.iqbible.com/api/v2').replace(/\/+$/, '');
|
||||
try {
|
||||
const res = await fetcher(`${base}/bibles/eng_kjv/verse-of-the-day`, { headers: { 'X-IQBible-API-Key': process.env.IQBIBLE_API_KEY }, signal: AbortSignal.timeout(10_000) });
|
||||
if (!res.ok) return null;
|
||||
const verse = parseVotd(await res.json());
|
||||
if (verse) cached = { date, verse };
|
||||
return verse;
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
export const clearVotdCache = () => { cached = null; };
|
||||
|
||||
export const votdMessage = ({ reference, text }) => ({ title: `Verse of the day: ${reference}`, body: `${text}\n\n${reference} (KJV)`, tags: 'sunrise' });
|
||||
Reference in new issue
Block a user