2ba9c9f7f4
Adds a one-click "Fetch from BSB" button in the lesson passage text field that calls the BSB API and overwrites the field with verse-numbered text. Also fixes reference parsing to tolerate leading book abbreviations like t:1-2 or col:1-2 by stripping letters before the chapter:verse pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
2.8 KiB
JavaScript
78 lines
2.8 KiB
JavaScript
const passageCache = new Map()
|
|
|
|
const BOLLS_BOOK_MAP = {
|
|
genesis: 1, exodus: 2, leviticus: 3, numbers: 4, deuteronomy: 5,
|
|
joshua: 6, judges: 7, ruth: 8, '1samuel': 9, '2samuel': 10,
|
|
'1kings': 11, '2kings': 12, '1chronicles': 13, '2chronicles': 14,
|
|
ezra: 15, nehemiah: 16, esther: 17, job: 18, psalms: 19, proverbs: 20,
|
|
ecclesiastes: 21, songofsolomon: 22, isaiah: 23, jeremiah: 24,
|
|
lamentations: 25, ezekiel: 26, daniel: 27, hosea: 28, joel: 29,
|
|
amos: 30, obadiah: 31, jonah: 32, micah: 33, nahum: 34, habakkuk: 35,
|
|
zephaniah: 36, haggai: 37, zechariah: 38, malachi: 39,
|
|
matthew: 40, mark: 41, luke: 42, john: 43, acts: 44,
|
|
romans: 45, '1corinthians': 46, '2corinthians': 47, galatians: 48,
|
|
ephesians: 49, philippians: 50, colossians: 51, '1thessalonians': 52,
|
|
'2thessalonians': 53, '1timothy': 54, '2timothy': 55, titus: 56,
|
|
philemon: 57, hebrews: 58, james: 59, '1peter': 60, '2peter': 61,
|
|
'1john': 62, '2john': 63, '3john': 64, jude: 65, revelation: 66,
|
|
}
|
|
|
|
function parseReference(reference) {
|
|
const stripped = reference.replace(/^[a-z]+/i, '')
|
|
const match = stripped.match(/^(\d+):(\d+)(?:-(\d+))?$/)
|
|
if (!match) return null
|
|
return {
|
|
chapter: parseInt(match[1], 10),
|
|
verseStart: parseInt(match[2], 10),
|
|
verseEnd: match[3] ? parseInt(match[3], 10) : parseInt(match[2], 10),
|
|
}
|
|
}
|
|
|
|
export function register(app) {
|
|
app.get('/api/bible-passage', async (req, res) => {
|
|
const book = String(req.query.book ?? '').trim().toLowerCase().replace(/[\s-]/g, '')
|
|
const reference = String(req.query.reference ?? '').trim()
|
|
|
|
const bookNum = BOLLS_BOOK_MAP[book]
|
|
if (!bookNum) {
|
|
res.status(400).json({ message: 'Unknown book.' })
|
|
return
|
|
}
|
|
|
|
const parsed = parseReference(reference)
|
|
if (!parsed) {
|
|
res.status(400).json({ message: 'Invalid reference format. Expected format: chapter:verseStart-verseEnd' })
|
|
return
|
|
}
|
|
|
|
const cacheKey = `BSB:${bookNum}:${parsed.chapter}:${parsed.verseStart}:${parsed.verseEnd}`
|
|
if (passageCache.has(cacheKey)) {
|
|
res.json({ text: passageCache.get(cacheKey) })
|
|
return
|
|
}
|
|
|
|
try {
|
|
const verseNums = []
|
|
for (let v = parsed.verseStart; v <= parsed.verseEnd; v++) verseNums.push(v)
|
|
|
|
const results = await Promise.all(
|
|
verseNums.map(v =>
|
|
fetch(`https://bolls.life/get-verse/BSB/${bookNum}/${parsed.chapter}/${v}/`, { signal: AbortSignal.timeout(8000) })
|
|
.then(r => r.ok ? r.json() : Promise.reject(new Error(`${r.status}`)))
|
|
)
|
|
)
|
|
|
|
if (results.length === 0) {
|
|
res.status(404).json({ message: 'Passage not found.' })
|
|
return
|
|
}
|
|
|
|
const text = results.map(v => `v. ${v.verse} ${String(v.text).trim()}`).join('\n\n')
|
|
passageCache.set(cacheKey, text)
|
|
res.json({ text })
|
|
} catch {
|
|
res.status(502).json({ message: 'Unable to fetch passage right now.' })
|
|
}
|
|
})
|
|
}
|