Skip to content

Latest commit

 

History

History

README.md

lehr.salon

Cloudflare Worker that aggregates public lectures, readings, and discussions across Frankfurt — Polytechnische Gesellschaft, Haus am Dom, Jüdische Gemeinde, FGZ StreitClub, Literaturhaus, Goethe-Uni Bürgeruniversität, Institut für Sozialforschung, Evangelische Akademie, Sigmund-Freud-Institut, Denkbar, Romanfabrik, DIG Frankfurt, OPEN BOOKS, plus talk-shaped events from museum, theatre, and academic venues that the hub tags talk:*.

Live: frankfurt.lehr.salon

Architecture

GitHub Action (.github/workflows/scrape.yml — lehrhaus job)
  ↓ daily cron 06:30 UTC
  ↓ runs `bun apps/lehrhaus/scripts/scrape.ts`
  ↓ writes apps/lehrhaus/src/scrape-data.ts (typed module)
  ↓ commits + pushes if content actually changed
Cloudflare git integration
  ↓ redeploys the worker with the new bundled data
Worker
  ↓ imports SCRAPE_DATA, in-memory filters serve every read path

Three formats — Vortrag (monologic lecture), Lesung (literary reading / book launch), Diskussion (panel / debate). The format comes from the hub's talk:vortrag / talk:lesung / talk:diskussion label (classified upstream in @museumsufer/classify, talk.ts).

The default home view is a rolling next-7-days list grouped per date; /tag/YYYY-MM-DD is the single-day permalink.

Stack

  • Cloudflare Workers (TypeScript)
  • Hono v4 + JSX SSR
  • htmx for date / range partial swaps
  • Bundled src/scrape-data.ts (no D1 reads for content)
  • D1 (lehrhaus-db) for Web Push subscriptions only

Visual identity

"The editor's annotated quarto." Foxed paper (#F2E9D5) + iron-gall ink (#1C1812) + cinnabar rubric (#A33222) + marginalia blue + book-binding umber. Cormorant Garamond serif + DM Mono. Pilcrows ¶ as per-entry anchors, manicule ☞ on action buttons, asterism ⁂ as the empty-state mark. Wordmark mirrors konzert.haus (lehr ink + rubric dot + italic-rubric salon).

Dev

bun install                                # from repo root
bun run -F @museumsufer/lehrhaus dev
bun run -F @museumsufer/lehrhaus scrape    # regenerate src/scrape-data.ts

curl http://localhost:8787/
curl http://localhost:8787/api/day
curl 'http://localhost:8787/api/events?format=Lesung'
curl http://localhost:8787/feed.ics

Adding a new source

Lehrhaus reads its events from the central event hub at @museumsufer/event-hub. The actual scraping happens in packages/scrapers/src/venues/; scripts/scrape.ts here just filters EVENTS to entries with a talk:* label and maps them onto LehrhausEvent.

  1. Add a canonical scraper under packages/scrapers/src/venues/<slug>.ts that emits a talk:vortrag / talk:diskussion / talk:lesung label for the talk-shaped events. Register it in packages/scrapers/src/index.ts.
  2. Add a LehrhausSource entry in src/source-config.ts whose slug matches the hub source_slug. Lehrhaus picks it up automatically on the next bun scrape run.
  3. Optionally curate a display name in packages/event-hub/src/venue-names.ts.

Talks from museum / theatre venues

Talks held at museums, theatres, and other venues surface here under their own hub source_slug (e.g. senckenberg-naturmuseum, schauspiel-frankfurt) — whatever scraper emitted the talk:* label. scripts/scrape.ts keeps every hub event with a talk:* label inside the Frankfurt bbox, regardless of which scraper produced it. Sources not curated in src/source-config.ts are auto-synthesized into the bundle's source list (display name via the hub's venue-names.ts), so /quelle/<slug> works for them too.