Discord notifications
HTTP routes and behavior for discord notifications.
GET /api/admin/discord-notifications # public configuration, has_webhook, recent, error?; never returns the URLPUT /api/admin/discord-notifications # enabled, webhook_url?, events?, enable_mentions?, role_id?, role_events?, thread_id?, username?, avatar_url?, embed_poster?DELETE /api/admin/discord-notifications # remove the webhook and disable deliveryPOST /api/admin/discord-notifications/test # draft options and webhook_url?; tests without saving, enabling, or mentioning anyoneGET /api/auth/discord-notifications # own enabled, discord_ids, events, allowed_events, server_enabled, server_mentions, blocked_reason?PUT /api/auth/discord-notifications # own enabled, discord_ids (up to 10 numeric IDs), eventsPOST /api/auth/discord-notifications/test # mention only the caller's saved IDs; requires server and personal opt-inAdmin routes require credentials:manage; personal routes use the authenticated account and never accept a target user ID. Both tests are limited to three requests per minute per IP. The encrypted server configuration contains events and role_events maps using these keys:
| Events | Audience for personal mentions |
|---|---|
request_pending, request_auto_approved, request_failed |
Administrators |
request_approved, request_denied, request_available |
The requester and applicable shared-book subscribers |
issue_created |
Administrators, after the existing report observation and alert hold-down |
issue_comment, issue_resolved, issue_reopened |
Administrators and the reporter |
Only user-submitted reports enter Discord. Report replies and resolutions use fixed summaries and a report link; thread bodies, diagnostics, and agent transcripts are excluded. A close without a fix is not a resolved event. The acting account is excluded from personal mentions. Failed requests mean dispatch exhausted its retries or a book import failed, not an ordinary approval wait, missing match, or transient retry.
The server master, selected event, mention master, personal opt-in/category, and current role/library/content authorization all apply at delivery. Role mentions require the mention master and their separately selected event categories. Discord payloads always use explicit allowed_mentions with an empty parse list, never automatic @everyone parsing. Large audiences are divided into independently receipted deliveries. Pasted IDs are user-supplied, not linked Discord accounts. This integration sends channel/thread posts and mentions, not DMs, and operates independently of phone push.
New settings preserve existing submission choices: request_pending is selected; the deprecated include_auto_approved field remains an alias for request_auto_approved. Other categories and mentions start off. Older clients preserve options they omit. The entire configuration is AES-GCM encrypted; URLs must be Discord HTTPS incoming webhook URLs. Arbitrary webhook hosts, query parameters in credentials, and redirects are rejected. An optional numeric thread_id targets an existing thread in that channel. Name/avatar overrides and optional movie/TV posters customize embeds; Discord refuses display names containing “Discord” or “Clyde”, so those are rejected on save. HTTP uses httpx.External(), wait=true, and a ten-second timeout. Webhook tokens never appear in API responses or logs.
Embeds share the selected title, media type, requester username, library name, and event with Discord and channel readers. External Address supplies links to media detail, approvals, or the report thread as web app routes (<address>/#/approvals); it is never inferred from request headers. Movie/TV artwork uses public TMDB images, never private arr URLs.
Availability uses the saved request’s exact library and scope: a movie file, each requested book format, a verified complete album, or newly imported selected TV episodes. Corrected TV numbering and pilot selections use the canonical request resolver. Arr webhooks wake observation, and a 30-second background sweep also checks provider state with the existing short caches (movie digests up to 120 seconds). A TV request is read in full when its observation key changes: delivery acceptance, the saved target, the local TV match configuration, or the selected seasons’ file count, total, and size on disk in the shared series digest (120 seconds, cleared by arr webhooks). An unchanged key skips full reads for at most five minutes. Every process restart also forces a full read; observation bypasses the brief TV episode cache so a new digest cannot be paired with an older snapshot. An unchanged sweep writes nothing. The selected seasons’ mapping and series identity must still match what was saved; a season TMDB adds later does not invalidate them. Events for the same title and library collect in a fixed 60-second window. Durable unit receipts prevent upgrade/restart duplicates. Enabling availability or changing its destination establishes a baseline for existing requests; existing files and files first observed while that baseline is being established are not announced. Unreadable libraries and TV matches that need attention remain unknown and show a settings diagnostic. TV selections that can never be verified (coarse selections saved before season tracking, or ones naming Specials) are skipped without a diagnostic and removed from queued deliveries. Administrator-paused TV matches also stay quiet, but their queued notifications remain pending and retry without consuming HTTP attempts within the existing 24-hour delivery window. A TV repair’s first delivered read only establishes its baseline, so episodes already in the library are not announced again. Later imports are compared with the successful baseline. Completed movie, album, and book receipts no longer need polling.
The SQLite outbox records distinct lifecycle events, retains legacy delivery history, and commits availability receipts with their queued events. Confirmed rate-limit rejections respect a destination-wide cooldown, with at most five HTTP attempts within 24 hours. Failed authorization/library preflights wait without consuming HTTP attempts or blocking unrelated messages. Disconnects, timeouts, server errors, interrupted sends, or successful responses without a message ID are unconfirmed, never automatically reposted because Discord may already have saved them. Other rejections fail visibly. Disabling delivery, changing the destination/thread, or disabling a category cancels applicable unsent events. Re-enabling does not replay historical or muted content. Queue errors never roll back accepted requests.