# Archive Bot An AIO Discord bot foundation for The Mithral Archive. The first module is status monitoring: it checks configured URLs and keeps one live status message updated in the Discord `status` channel. The message uses a summary embed plus one small embed per service, so each service gets its own green or red Discord color bar. It does not need Discord gateway intents or slash commands for the status module. It only needs a bot token, the `status` channel ID, and permission to send/edit its own messages. The bot also includes a small web dashboard for editing monitored services and forcing immediate Discord refreshes. ## Discord Bot Setup Create a Discord application and bot in the Discord Developer Portal, then invite it with: ```text https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=84992&scope=bot ``` Required channel permissions: - View Channel - Send Messages - Embed Links - Read Message History The current `status` channel ID is: ```text 1504278732070981683 ``` ## Local Setup ```sh cp .env.example .env cp services.example.json services.json ``` Edit `.env` and set: - `DISCORD_BOT_TOKEN` - `DASHBOARD_USERNAME` - `DASHBOARD_PASSWORD_HASH` Generate the dashboard password hash: ```sh python3 status_bot.py --hash-password ``` Paste the output into `DASHBOARD_PASSWORD_HASH`. The dashboard does not store a reusable browser token; it uses an HttpOnly session cookie and CSRF token after login. Dashboard auth includes: - PBKDF2-SHA256 password hashing - HttpOnly `SameSite=Strict` session cookie - CSRF token required for write actions - basic failed-login throttling Edit `services.json` with the URLs you want displayed. Keep private/internal URLs out of Git if they should not be shared. Preview the Discord embed payload: ```sh ARCHIVE_STATUS_CONFIG=services.json python3 status_bot.py --preview ``` Open `preview.html` in a browser to see an approximate Discord-style render. Paste the JSON from `--preview` into the textarea and render it to test changes before sending anything to Discord. Run it: ```sh python3 status_bot.py ``` Local runs automatically read `.env` from the current directory. For dashboard testing without touching Discord, set: ```env DISCORD_DRY_RUN=true ARCHIVE_STATUS_STATE=state/status-message.json ``` Open the dashboard: ```text http://127.0.0.1:8787 ``` Sign in with `DASHBOARD_USERNAME` and the password you used when generating `DASHBOARD_PASSWORD_HASH`. ## Docker Setup ```sh cp .env.deploy.example .env cp services.example.json services.json docker compose up -d --build ``` The bot stores the Discord message ID in `state/status-message.json`. Keep that file mounted so the bot edits the same message after restarts. The deploy compose joins your existing reverse-proxy network: ```yaml networks: mediaserver_default: external: true ``` If that network does not already exist on the deploy host, create it once: ```sh docker network create mediaserver_default ``` The dashboard is exposed inside Docker on port `8787` for your reverse proxy. It is not published directly to the host by default. Use this target from your proxy: ```text http://archive-status-bot:8787 ``` For HTTPS behind a reverse proxy, set: ```env DASHBOARD_COOKIE_SECURE=true ``` Leave it `false` only for direct localhost HTTP testing. For direct local Docker testing without a proxy: ```sh docker compose -f compose.yaml -f compose.local.yaml up -d --build ``` Then open: ```text http://127.0.0.1:8787 ``` ## Dashboard The dashboard currently supports: - viewing monitored services - adding/removing service rows - editing check URL, display URL, expected statuses, timeout, and keyword - saving `services.json` - forcing an immediate check and Discord message update The sidebar leaves room for future modules like polls, webhooks, automations, and service integrations without changing the bot shape later. ## Service Config Each service supports: - `name`: label shown in Discord - `url`: URL checked by the bot - `displayUrl`: URL linked in the embed - `method`: optional, defaults to `GET` - `timeoutSeconds`: optional, defaults to `10` - `expectedStatuses`: optional list such as `["200-399"]` or `[200, 204]` - `keyword`: optional text that must appear in the response body Example: ```json { "name": "Jellyfin", "url": "https://jellyfin.mithraic.cloud/health", "displayUrl": "https://jellyfin.mithraic.cloud", "expectedStatuses": ["200-399"] } ```