MWBot is a Telegram bot for Alertmanager alert handling and media/Plex access controls. It uses a menu-first Telegram flow, with /start opening the inline button menu for all supported actions.
- An owner-only Alerts section where the live Alertmanager alert list is the menu: pick an alert to file it as an incident, resolve it, or silence it.
- Per-alert silences that match only that alert's label set, alongside a blanket maintenance window for the whole homelab.
- Alerts already filed are marked
→ #Non the list, so a known fault is visible before you tap anything. - Manage ISP ASN access through Cloudflare WAF.
- Detect a user's Cloudflare-observed ASN through a short-lived Worker session.
- Authenticate users from Seerr Telegram notification settings.
- Interactive redownload workflow from the Media menu for Seerr issue, movie, or series URLs that blocklists bad Sonarr/Radarr releases.
- Owner-only incident creation in the private
homelab-opsrepository, filed from a firing Alertmanager alert.
To deploy MWBot using Docker Compose, use the following configuration:
version: '3'
services:
mwbot:
image: ghcr.io/<your-ghcr-user>/mwbot:main
container_name: mwbot
environment:
- TOKEN=${TELEGRAM_TOKEN} # Set your Telegram bot token
- CHAT_ID=${TELEGRAM_CHATID} # Default Telegram chat ID for notifications
- ALERTMANAGER_URL=${ALERTMANAGER_URL} # Optional Alertmanager API base URL
- ALERTMANAGER_MW_MATCHERS=${ALERTMANAGER_MW_MATCHERS} # Optional silence matcher JSON
- ALERTMANAGER_OPEN_MW_DURATION=${ALERTMANAGER_OPEN_MW_DURATION} # Optional maintenance safety expiry; defaults to 12h
- ALERTMANAGER_ALERT_SILENCE_DURATIONS=${ALERTMANAGER_ALERT_SILENCE_DURATIONS} # Optional per-alert silence choices; defaults to 1d,3d,7d
- GITHUB_INCIDENT_REPO=${GITHUB_INCIDENT_REPO} # Private incident repository
- GITHUB_INCIDENT_TOKEN=${GITHUB_INCIDENT_TOKEN} # Fine-grained token with Issues write access
- WAF_TOKEN=${WAF_TOKEN} # Cloudflare WAF API token
- WAF_ZONE=${WAF_ZONE} # Cloudflare WAF zone ID
- WAF_RULESET=${WAF_RULESET} # Cloudflare WAF ruleset ID
- WAF_RULEID=${WAF_RULEID} # Cloudflare WAF rule ID
- CDN_URL=${CDN_URL} # CDN URL for firewall rules
- MW_BOT_ASN_DEFAULT=${MW_BOT_ASN_DEFAULT} # Default ASN for MWBot
- ACCESS_CHECK_API_URL=${ACCESS_CHECK_API_URL} # Optional Cloudflare network-check Worker URL
- ACCESS_CHECK_API_TOKEN=${ACCESS_CHECK_API_TOKEN} # Shared Worker API token
- TZ=${TIMEZONE} # Server timezone
- SEERR_BASE_URL=${SEERR_BASE_URL} # Seerr base URL
- SEERR_API_KEY=${SEERR_API_KEY} # Seerr API key
- SONARR_BASE_URL=${SONARR_BASE_URL} # Sonarr base URL
- SONARR_API_KEY=${SONARR_API_KEY} # Sonarr API key
- RADARR_BASE_URL=${RADARR_BASE_URL} # Radarr base URL
- RADARR_API_KEY=${RADARR_API_KEY} # Radarr API key
- SONARR4K_BASE_URL=${SONARR4K_BASE_URL} # Optional Sonarr 4K base URL
- SONARR4K_API_KEY=${SONARR4K_API_KEY} # Optional Sonarr 4K API key
- RADARR4K_BASE_URL=${RADARR4K_BASE_URL} # Optional Radarr 4K base URL
- RADARR4K_API_KEY=${RADARR4K_API_KEY} # Optional Radarr 4K API keyTOKEN: Your Telegram bot token.CHAT_ID: The default Telegram chat ID to receive notifications.ALERTMANAGER_URL: Optional Alertmanager API base URL used by the Alerts section.ALERTMANAGER_MW_MATCHERS: Optional JSON array of matchers for the blanket maintenance window. Defaults to all alerts. Per-alert silences do not use this; they match the selected alert's own label set.ALERTMANAGER_OPEN_MW_DURATION: Maintenance window safety expiry. Defaults to12h.ALERTMANAGER_ALERT_SILENCE_DURATIONS: Comma-separated durations offered when silencing one alert, in button order. Defaults to1d,3d,7d. Durations usem,hord.GITHUB_INCIDENT_REPO: Private GitHub repository used for incidents. Defaults tofreender/homelab-ops.GITHUB_INCIDENT_TOKEN: Fine-grained token limited to that repository with Issues read/write access.WAF_TOKEN: The API token for Cloudflare WAF.WAF_ZONE: The zone ID for Cloudflare WAF.WAF_RULESET: The ruleset ID for Cloudflare WAF.WAF_RULEID: The rule ID for Cloudflare WAF.CDN_URL: The CDN URL used in firewall rules.MW_BOT_ASN_DEFAULT: The default ASN for MWBot.ACCESS_CHECK_API_URL: Optional base URL for the Cloudflare network-check Worker.ACCESS_CHECK_API_TOKEN: Shared secret for MWBot-to-Worker API calls. Automatic detection is enabled only when both Worker values are configured.TZ: The server's timezone.SEERR_BASE_URL: Base URL for Seerr.SEERR_API_KEY: API key for Seerr issue lookups and Telegram access sync.SONARR_BASE_URL: Base URL for Sonarr.SONARR_API_KEY: API key for Sonarr queue/history access.RADARR_BASE_URL: Base URL for Radarr.RADARR_API_KEY: API key for Radarr queue/history access.SONARR4K_BASE_URL: Optional Sonarr 4K base URL. Defaults toSONARR_BASE_URL.SONARR4K_API_KEY: Optional Sonarr 4K API key. Defaults toSONARR_API_KEY.RADARR4K_BASE_URL: Optional Radarr 4K base URL. Defaults toRADARR_BASE_URL.RADARR4K_API_KEY: Optional Radarr 4K API key. Defaults toRADARR_API_KEY.
-
Open the Bot: Use
/startto open the main menu. -
Alerts: Owner-only. Opens the live Alertmanager alert list, summarised above it and annotated with the open incident already filed for each alert. Pick an alert for its action sheet:
- File Incident — see Incidents below. Replaced by Open Incident #N when one already exists.
- Resolve — offered only for one-shot event alerts (by default
source=pve). A metric alert clears itself once the condition ends, and would be re-sent by vmalert within one evaluation interval. Confirmation is required, because a resolved alert cannot be brought back. - Silence / Unsilence — mutes just that alert by matching its exact label set, after asking for how long (
1d/3d/7dby default). Unsilence lifts only silences MWBot created for that alert, never the blanket maintenance window. A silenced alert stays in the list marked🔇 6d, so a long silence is visible rather than forgotten.
The list footer starts or ends the blanket maintenance window; only the one that applies to the current state is shown.
-
Plex Access: Open the Plex Access section and tap the network-check link. MWBot applies the detected ISP ASN automatically, then updates the same menu with an explicit success or failure result.
-
Redownload Control: Open the Media section and follow the prompts. The bot confirms the target, then blocklists the matching release in Sonarr or Radarr so it is not downloaded again.
-
Incidents: Owner-only, and filed from a firing alert only, reachable from an alert's action sheet. The incident carries the alert's labels, annotations, and firing time. There is no free-text path: this keeps the issue body machine-generated, which is what the triage agent reasons over. The issue is created with the
incidentlabel, and MWBot posts a/occomment that triggers read-only OpenCode triage in that repository. Filing the same alert twice is refused: the alert fingerprint in the issue body is both the dedup key and what draws→ #Non the alert list.
- Send
/startin Telegram. - Open the Media section and choose the redownload action.
- The bot asks for a Seerr URL such as
https://seerr.example.com/issues/29,https://seerr.example.com/movie/1220564, orhttps://seerr.example.com/tv/1408. - If you send a movie or series URL, the bot looks up the most recent matching Seerr issue automatically, then resolves the target media plus whether it belongs to the standard or 4K arr instance.
- The bot shows a confirmation message with the selected backend:
Radarr,Radarr4k,Sonarr, orSonarr4k. - After you confirm in Telegram, the bot tries to stop future grabs in this order:
- remove a matching queued release with
blocklist=trueandskipRedownload=true - if nothing is queued, mark the best matching history item as failed
- remove a matching queued release with
- For history fallback, the bot prefers
grabbedrecords beforedownloadFolderImportedrecords so the blocklist entry is created against the actual grabbed release.
- MWBot must be able to resolve and reach
seerr,sonarr,sonarr4k,radarr, andradarr4kover Docker networking. - In the current homelab deployment,
mwbotis attached to bothnet.internalandnet_overlayso it can talk to the arr containers on tower. - If you only run one Sonarr or Radarr instance, the optional
SONARR4K_*andRADARR4K_*values can be omitted and will fall back to the standard endpoints. - Network detection uses the Worker in
worker/; seeworker/README.md. Both Worker environment variables are required for Plex access grants. - The temporary WAF rule permits the ASN reported directly by Cloudflare, using the same classification as the WAF
ip.geoip.asnumfield.
- Blanket maintenance-window state is persisted to
/config/alertmanager_mw_state.json, and every maintenance silence has a safety expiry. - Per-alert silences are deliberately stateless: they are identified in Alertmanager by their comment, so Unsilence reads the alert's own
silencedBylist rather than a local file that a restart could lose. - The per-alert silence floor is one day. A sub-day silence does not survive a nightly re-alert, so it defers the same interruption to tomorrow rather than stopping it. The ceiling is one week, because nothing re-notifies when a silence lapses.
- A silenced alert still appears in the list, marked with the time left on its silence. Silencing removes an alert from your attention; nothing else puts it back.
Contributions are welcome! Please fork the repository and submit a pull request with your changes.
This project is licensed under the MIT License. See the LICENSE file for details.