Wire Matrix messages to the *arr clients. Dispatcher parses "<prefix><cmd> <query>", routes movie/tv/music to Radarr/Sonarr/Lidarr, adds the top hit, and replies in a thread. Unconfigured services reply with a clear message instead of failing.
2.8 KiB
2.8 KiB
Bellhop Go Rewrite — Session Plan
Rewriting Bellhop from a Python FastAPI web portal into a Go-based Matrix command bot, modeled after Pete. Users issue commands like !movie dune in allowlisted Matrix rooms; the bot performs the top-hit search against Radarr/Sonarr/Lidarr and adds the result.
Design decisions (locked in)
- Command UX: top-hit auto-add (no numbered picker, no reaction selector).
- Authz: any member of an allowlisted room ID can issue commands. No per-user list.
- Old Python code: deleted, clean-slate Go rewrite.
- Module layout: mirrors Pete (
main.go+internal/<pkg>/). - Config: YAML with
${ENV_VAR}expansion (Pete-style). - Matrix client: mautrix-go with cryptohelper for E2EE support.
Session breakdown
-
Session 1 — Foundation
- Delete Python (
app/,requirements.txt,Dockerfile,.env.example) - Reset
.gitignore/.dockerignorefor Go go.mod(mautrix + yaml)internal/config/config.go— YAML loader: matrix creds,allowed_rooms,services.{radarr,sonarr,lidarr}
- Delete Python (
-
Session 2 — Matrix client (
internal/matrix/)- Adapt Pete's
client.go: login + device persistence + cryptohelper - Sync loop, auto-join on invite
- Message handler scoped to
allowed_rooms(not channel-name map like Pete) - Threaded reply helper for command responses
- Drop Pete-specific bits: image upload, reaction handler, story formatting
- Adapt Pete's
-
*Session 3 — arr clients (
internal/arr/)- One file per service or a single generic client (TBD in session)
Search(term) → []ResultandAdd(result) → error- Radarr:
/api/v3/movie/lookup+ POST/api/v3/movie - Sonarr:
/api/v3/series/lookup+ POST/api/v3/series - Lidarr:
/api/v1/artist/lookup+ POST/api/v1/artist - All adds: monitored=true, search-on-add=true, quality profile + root folder from config
- Unit tests with httptest
-
Session 4 — Command dispatch + main (
internal/bot/,main.go)- Parse
<prefix><cmd> <query>(default prefix!) - Commands:
movie,tv,music,help - On match: search → take
[0]→ add → reply with title/year (or error) - "Service not configured" reply if the corresponding *arr block is absent
main.go: load config, init matrix, wire handler, SIGINT/SIGTERM shutdown
- Parse
-
Session 5 — Ship polish
- Multi-stage Dockerfile (
golang:1.25-alpine→alpine:3.21, build with-tags goolm) config.example.yaml- Rewrite
README.md: command UX, install, config reference, Docker go build ./...+go vet ./...clean
- Multi-stage Dockerfile (
Out of scope (for now)
- Reaction-based result picker
- Per-user allowlist or power-level gating
- Persistent request audit log (the Matrix room itself is the audit trail since requests are in-channel)
- Removal/cancellation commands
- Search-only mode (browse without adding)