Issue #2.
Search/request now work without Seerr — when seerr.url/api_key are left
as placeholders the bot falls back to direct Sonarr/Radarr lookup + add
(mirrors how Lidarr music already works). Numbered selection keeps
working across both sources via a _source discriminator stamped onto
each result. !media requests / !media trending now print a friendly
hint when Seerr is absent.
base-config.yaml no longer ships any homelab-specific URLs, MXIDs, or
Emby UIDs — admin_users defaults to [], user_map to {}, and every
service URL uses a docker-hostname placeholder. New per-service config
keys (quality_profile_id, root_folder_path, monitor, search_on_add,
language_profile_id, minimum_availability) let operators pin Sonarr/
Radarr defaults the same way Lidarr already could; null = auto-pick
the first profile/folder.
README rewritten as a self-contained setup guide: requirements, build,
upload, instance config (Required vs Optional with the Seerr fallback
called out), webhook setup, fork notes.
- LidarrClient.health() override: hits /api/v1/health. Base ArrClient
uses /api/v3/health which 404s on Lidarr.
- !media health output now joins service blocks with a blank line for
readability.
Daily-digest fixes for issue #1:
- Drop the Completed section unless a finished release isn't already
represented in Recently Added (slug-substring match on titles).
- Skip the Queued section when both Sonarr and Radarr queues are empty.
- Split the message into numbered chunks (~3000 chars) so Sunday digests
with the emby-cleaner recap don't get truncated by Matrix clients.
The v0.4 schema migration silently failed on first deploy — Maubot's
UpgradeTable hook didn't create the subscriptions / digest_state tables
in the shared Postgres instance. When the digest fired at 8 AM, the
SELECT against digest_state raised, the loop's only except was
CancelledError, and the task crashed silently. No digest, no logs.
Two fixes:
- _ensure_db_schema() runs CREATE TABLE IF NOT EXISTS on start() so
the bot self-heals if migrations don't run for any reason
- digest loop's inner work is now wrapped — any exception sleeps an
hour and retries instead of killing the whole schedule
- LidarrClient (api/v1) with lookup, profile/folder discovery, and
add_artist
- !media music <query> — MusicBrainz lookup via Lidarr, numbered list
cached per (room, sender)
- !media music add <query|N> — POST /api/v1/artist with profile/folder
defaults (auto-picks first if not set in config). Carries through
images/genres/links from the lookup result and kicks off a search
for missing albums by default.
- New config block 'lidarr' with optional quality_profile_id /
metadata_profile_id / root_folder_path overrides.
The cleaner runs Sunday 4:20 AM and pings ntfy. The Sunday digest now
pulls the last 12h of messages from the emby-cleaner topic and appends
them to the daily summary. Configurable via ntfy_url +
emby_cleaner_topic in base-config.yaml.
Also drop the zoneinfo dependency in favour of a manual EST/EDT offset
calc (the maubot container ships without tzdata).
- Admins can react 👍/✅ or 👎/❌ on a !media request to approve/decline
- Posters auto-attach to request confirmations and !media random picks
(TMDB for Seerr items, Emby /Items/{id}/Images/Primary for library items)
- New @web.post(/seerr-webhook) handler — Seerr → Matrix room directly,
replaces the Telegram bridge path
- New config: posters_enabled, admin_users, notifications_room,
seerr_webhook_secret
Maubot plugin: Matrix companion for the homelab media stack.
Services wrapped:
- Seerr: search, request, requests, trending
- Emby: nowplaying, recent, watched
- Sonarr/Radarr: queue, upcoming, missing
- NZBGet/qBittorrent: activity
Each Matrix sender is mapped to per-service user IDs via plugin config;
unmapped senders are rejected. Replies are MXID-prefixed for shared rooms.