# maubot-media A [maubot](https://github.com/maubot/maubot) plugin that turns a Matrix room into a control surface for the *arr / Plex-style media stack: - **Search & request** via [Jellyseerr / Overseerr](https://docs.jellyseerr.dev/) **or** directly against Sonarr/Radarr. - **Library + playback** via Emby **or Jellyfin** (`recent`, `nowplaying`, `watched`, `find`, `resume`, `random`). - **Sonarr / Radarr / Lidarr** queue, calendar, missing, music search/add. - **Downloads** — NZBGet + qBittorrent activity, completed, speed, pause/resume. - **Subscriptions** — `!media subscribe `, with a Sonarr webhook pinging the room when new episodes import. - **Daily digest** posted to a notifications room (recently added, completed, queued, plus an optional Sunday recap pulled from an ntfy topic). - **Health check** across all seven backing services in one command. Each Matrix user is mapped to per-service IDs in plugin config; unmapped senders get a refusal. ## Commands ```text !media help # full command list # Search & request !media search # numbered results !media request [--tv|--movie] !media request # pick N from the last search/trending !media requests # (Seerr only) your pending/processing !media trending # (Seerr only) what's trending # Library & playback (Emby) !media nowplaying !media recent [movies|tv] !media watched !media find !media resume !media random [movie|tv] # Sonarr / Radarr / Lidarr !media queue !media upcoming !media missing !media music !media music add !media health # Downloads (NZBGet + qBittorrent) !media activity !media completed !media speed !media pause / unpause # Subscriptions / digest !media subscribe !media unsubscribe !media subscriptions !media digest # fire today's digest now ``` ## Requirements - maubot **0.3.1+** running somewhere reachable from your Matrix server. - A Matrix user for the bot. Provision via `/_synapse/admin/v1/register` (Synapse) or your homeserver's equivalent, then log in once to capture an `access_token` + `device_id` for the maubot client config. - At minimum: Sonarr **and** Radarr (or just one, with the other left as the placeholder URL — the missing one will fail searches for that media type). - Optional: Seerr/Jellyseerr/Overseerr, Emby, NZBGet, qBittorrent, Lidarr. ## Build & install ```bash git clone https://git.3ddbrewery.com/maddox/maubot-media.git cd maubot-media # Bump the plugin id in maubot.yaml if you fork this — it must be unique per # maubot instance. The current id (com.3ddbrewery.media) is reserved for the # upstream deployment. PLUGIN_VERSION=$(grep '^version:' maubot.yaml | awk '{print $2}') zip -rq "com.3ddbrewery.media-v${PLUGIN_VERSION}.mbp" \ maubot.yaml base-config.yaml media_bot/ README.md LICENSE \ -x '*/__pycache__/*' ``` Upload `*.mbp` via the maubot UI's *Plugins → upload*, then create an instance pointing at your bot user. ## Configuration All config lives in the maubot UI's *instance config* tab; the defaults shipped in `base-config.yaml` use placeholder hostnames (`http://sonarr:8989`, etc.) and `CHANGEME` API keys. ### Required ```yaml sonarr: url: http://:8989 api_key: radarr: url: http://:7878 api_key: user_map: "@alice:example.com": seerr_user_id: 1 # only required if Seerr is configured emby_user_id: "abc123def" # only required for Emby commands ``` ### Seerr (optional) If `seerr.url` and `seerr.api_key` are set, search/request route through Seerr (with its approval workflow + 👍/👎 admin reactions). If left empty, the bot falls back to direct Sonarr/Radarr lookup + add — no approval, no per-user request quotas, but no Seerr install needed either. ```yaml seerr: url: http://:5055 api_key: ``` ### Lidarr / Emby (or Jellyfin) / Downloads All optional — the corresponding commands fail gracefully when the service isn't reachable. Commands that need them: Lidarr (`!media music`), Emby/Jellyfin (`!media nowplaying|recent|watched|find|resume|random`), NZBGet/qBt (`!media activity|completed|speed|pause|unpause` and the daily digest's "completed" section). The bot uses the same config block (`emby:`) for both servers — they expose the same API surface. Set `emby.type: jellyfin` so the display labels (`!media health`, `!media nowplaying`, etc.) read correctly: ```yaml emby: type: jellyfin # or "emby" (default) url: http://:8096 # no /emby suffix on Jellyfin api_key: ``` ### Webhooks (optional) Two endpoints are exposed when the plugin's `webapp` is enabled (default): | URL | Purpose | |---|---| | `/_matrix/maubot/plugin//seerr-webhook` | Seerr → Matrix notifications (request created/approved/available/etc) | | `/_matrix/maubot/plugin//sonarr-webhook` | Sonarr Download events → ping subscribers | Configure them in Seerr/Sonarr's *Settings → Notifications/Connect → Webhook* and set `Authorization: Bearer ` headers. The shared secret is `seerr_webhook_secret` / `sonarr_webhook_secret` in the instance config; leave empty to disable auth (not recommended). ### Notifications room + digest ```yaml notifications_room: "!roomid:example.com" # bot must be joined digest_enabled: true digest_hour: 8 # local hour, 24h ``` The Sunday digest can also pull a recap from an ntfy topic (e.g. an external library-cleaner job that posts its summary). Set `ntfy_url` to enable. ## Notes for forkers - The plugin id (`com.3ddbrewery.media`) is unique per maubot deployment — change it in `maubot.yaml` before building if you're running this alongside the upstream instance. - No personal data is stored *in* the plugin: every URL, key, and Matrix-ID lives in the instance config you set in the maubot UI. - The plugin's SQLite tables (`subscriptions`, `digest_state`) are managed via maubot's `UpgradeTable`; nothing manual needed. ## License MIT — see `LICENSE`.