Liturgy Bot — Solution Design & Architecture
Version 8.0 · April 2026 · Status: Production
Liturgy Bot is a fully automated weekly pipeline that generates a Children's Liturgy of the Word document for a Catholic parish in the Diocese of Westminster. Every Saturday at 06:00 UTC, a Railway cron service fetches two source PDFs from diocesan websites, generates a formatted .docx document using the Anthropic API (Claude Opus 4.6 for reflections, Claude Sonnet 4 for supporting content), and emails it via the Brevo REST API to two catechists.
| Platform | Railway (cron service, Hobby plan) |
| Trigger | Railway cron — 0 6 * * 6 (Saturday 06:00 UTC) |
| Runtime | Docker container (Python 3.12-slim, ~25MB compressed, 42–48s execution) |
| AI Models | Claude Opus 4.6 (reflection) + Claude Sonnet 4 (introduction, gospel summary) |
| Brevo REST API, from bot@liturgybot.com | |
| Codebase | ~6,200 lines · 46 files · 41 commits · 247 tests passing |
| First Automated Run | Saturday 12 April 2026 |
| Est. Annual Cost | ~£14 (Railway ~£8 + Anthropic API ~£6 + Brevo £0) |
Westminster Diocese is the authoritative source for all readings and liturgical texts. The following hierarchy applies whenever sources conflict:
| Rank | Source | Authority |
|---|---|---|
| 1 | Westminster Diocese PDF | Authoritative for readings, prayers, Gospel reference, creed, bidding prayers. Always overrides other sources. |
| 2 | Adorers of the Blood of Christ PDF | Children's lectionary text and Gospel Acclamation verse. Westminster overrides on any conflict. Adorers' reflection is excluded. |
| 3 | Calendar YAML | Season rules, Gloria/Alleluia flags, song, file naming only. Gospel references are indicative and may be overridden at runtime. |
After parsing both PDFs, the pipeline compares the Westminster Gospel reference against the YAML entry. If they differ, it uses Westminster's version, logs a warning, and sends a conflict notification email.
The pipeline runs as a Railway cron service with seven sequential stages, making three separate Anthropic API calls. Each stage is a separate Python module. The orchestrator (main.py) executes them in order with structured logging and error handling.
| Stage | Module | Purpose | Output |
|---|---|---|---|
| 1 | calendar.py | Resolve next Sunday, season rules | Sunday dataclass |
| 2 | fetcher.py | Scrape landing pages, download PDFs | 2 PDF files |
| 3 | parser.py | Extract text, split into sections | Parsed sections |
| 4 | main.py | Conflict detection (YAML vs Westminster) | Validated gospel ref |
| 5 | generator.py | 3 API calls: intro + reflection + summary | GeneratedContent |
| 6 | assembler.py | Build formatted Word document | .docx file |
| 7 | emailer.py | Send email with attachment | Email delivered |
Seven-stage sequential pipeline with three Anthropic API calls
The pipeline makes three separate Anthropic API calls, using two different models chosen for their distinct strengths:
| Call | Model | Max Tokens | Purpose |
|---|---|---|---|
| Introduction | Claude Sonnet 4 | 512 | 2–3 sentence introduction linking to previous weeks |
| Reflection | Claude Opus 4.6 | 4,096 | Conversation-guide reflection: questions woven through the Gospel story |
| Gospel Summary | Claude Sonnet 4 | 128 | One-sentence summary for the email body |
Every generated document follows a fixed 14-row single-column table format:
| Row | Section | Content Source |
|---|---|---|
| 1 | Prayer for Holy Spirit guidance | Static text (italic) |
| 2 | Dismissal | Static |
| 3 | Sign of the Cross | Static |
| 4 | Light the Candles | Static |
| 5 | Introduction | AI-generated (Sonnet 4) |
| 6 | Saying Sorry | Westminster PDF |
| 7 | Gloria / Seasonal Substitute | Calendar engine |
| 8 | Opening Prayer | Westminster PDF |
| 9 | First Reading | Heading only |
| 10 | Gospel Acclamation | Adorers (verse) + Calendar (frame) + Westminster (ref) |
| 11 | Reflection | AI-generated (Opus 4.6) |
| 12 | Creed | Westminster PDF |
| 13 | Song | Calendar engine |
| 14 | Bidding Prayers | Westminster PDF + standard closing |
| Module | Purpose | Lines | Tests |
|---|---|---|---|
calendar.py | Liturgical calendar engine, Year A YAML data, Sunday dataclass | 235 | 40 |
fetcher.py | PDF fetch via landing page scrape, CL availability check | 296 | 32 |
parser.py | PDF text extraction (pymupdf), regex section parsing | 349 | 45 |
generator.py | Anthropic API client — 3 calls (intro, reflection, summary) | 360 | 26 |
assembler.py | DOCX template assembler — 14-row table | 338 | 22 |
emailer.py | Brevo REST API sender + notification emails | ~280 | 50 |
main.py | Pipeline orchestrator with CLI, conflict detection | 194 | 13 |
Railway cron service with Brevo REST API email delivery
The bot was originally deployed on Google Cloud Platform (Cloud Run Job + Cloud Scheduler + Secret Manager + Gmail API). This was abandoned due to Google's impractical OAuth verification process for small internal tools. GCP resources have been deleted.
| Component | Before (GCP) | After (Railway + Brevo) |
|---|---|---|
| Runtime | Cloud Run Job | Railway cron service |
| Scheduler | Cloud Scheduler | Railway cron |
| Gmail API (OAuth) | Brevo REST API (HTTPS) | |
| From address | Personal Gmail | bot@liturgybot.com |
| Deploy trigger | Manual script | Auto-deploy on git push |
| Verification | GCP OAuth (abandoned) | None needed |
| Annual cost | ~£15 | ~£14 |
| Package | Version | Purpose |
|---|---|---|
anthropic | ≥0.42.0 | Claude API client (Opus 4.6 + Sonnet 4) |
beautifulsoup4 | ≥4.12.0 | HTML scraping |
pymupdf | ≥1.25.0 | PDF text extraction |
python-docx | ≥1.1.0 | Word document generation |
pyyaml | ≥6.0 | YAML calendar parsing |
requests | ≥2.32.0 | HTTP requests + Brevo REST API |
247 tests, all passing.
| Module | Tests | Key Coverage |
|---|---|---|
test_calendar.py | 40 | Calendar engine, season rules, lookups, edge cases |
test_fetcher.py | 32 | PDF fetch, scraping, date matching, error handling |
test_parser.py | 45 | Westminster & Adorers parsing, gospel ref regex |
test_generator.py | 26 | Three-call architecture, model selection, response parsing |
test_assembler.py | 22 | All 14 rows, season-dependent content, file naming |
test_emailer.py | 50 | Brevo REST API calls, all four email types, error handling |
test_main.py | 13 | Pipeline orchestration, conflict detection, CLI |
| Integration (skipped) | 12 | End-to-end with real PDFs (require network access) |
| Component | Estimated Annual Cost |
|---|---|
| Railway (shared Hobby plan, proportional usage) | ~£8 |
| Anthropic API: Opus 4.6 reflection | ~£5 |
| Anthropic API: Sonnet 4 intro + summary | ~£1 |
| Brevo REST API (free tier) | £0 |
| GitHub Pages (website hosting) | £0 |
| Total | ~£14/year |