# Wiki Jokes 5 jokes every day, generated by an LLM from Wikipedia's *Today's Featured Article*. ## How it works - A scheduled job (06:00 Europe/Copenhagen, plus immediately on cold start) fetches the day's featured article from the Wikipedia REST feed and asks any OpenAI-compatible endpoint for exactly 5 jokes as JSON. - Jokes are stored in SQLite (`/data/jokes.db`) and served instantly — no LLM call at request time. - On failure: retries every 15 min, up to 3 attempts per day. If still failing, the page keeps showing the latest batch with a "stale" note. ## Run ```bash cp .env.example .env # set OPENAI_BASE_URL / OPENAI_MODEL / OPENAI_API_KEY docker compose up -d --build # open http://localhost:8080 ``` ## Configuration (env vars) | Var | Default | Purpose | |---|---|---| | `OPENAI_BASE_URL` | `https://api.openai.com/v1` | OpenAI-compatible endpoint (e.g. llamaswap) | | `OPENAI_MODEL` | `gpt-4o-mini` | Model name | | `OPENAI_API_KEY` | *(empty)* | Bearer token (optional for llamaswap) | | `GENERATE_HOUR` / `GENERATE_MINUTE` | `6` / `0` | Daily generation time | | `MAX_ATTEMPTS` | `3` | Retry budget per burst | | `RETRY_MINUTES` | `15` | Retry interval | | `SLOW_RETRY_MINUTES` | `60` | Cooldown after budget exhausted, then retries resume | | `WIKIPEDIA_LANG` | `en` | Wikipedia language for the feed | | `DB_PATH` | `/data/jokes.db` | SQLite location (volume-mounted) | ## Endpoints - `GET /` — today's 5 jokes + link to the featured article - `GET /trmnl.json` — JSON feed for TRMNL e-ink displays (see `trmnl-plugin/`) - `GET /health` — JSON: whether today's jokes exist / retries exhausted ## TRMNL e-ink feed `GET /trmnl.json` returns the day's jokes in a shape ready for a TRMNL Private Plugin with the **polling** strategy: ```json { "refresh_interval": 900, "day": "2026-10-05", "stale": false, "article_title": "White-headed fruit dove", "article_url": "https://en.wikipedia.org/...", "jokes": [{"text": "..."}] } ``` Optional access token: set `TRMNL_TOKEN` in `.env`; requests must then carry `?token=` or the header `trmnl-token: ` (put the token directly in TRMNL's polling URL or polling headers). Without the env var the endpoint is open — the jokes themselves are public anyway. Setup on my.trmnl.com: 1. Plugins → New private plugin. Strategy **Polling**, URL `https://wiki-jokes.obahan.xyz/trmnl.json` (+ token if enabled). 2. Markup (Full layout): paste `trmnl-plugin/full.liquid`. 3. Assign the plugin to your device's playlist. The template was verified to render against the live payload with python-liquid (5 items, day, stale flag).