73 lines
2.6 KiB
Markdown
73 lines
2.6 KiB
Markdown
# 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=<token>` or the header `trmnl-token: <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).
|