TheOrb/README.md

7.5 KiB
Raw Blame History

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.

It also has a media catalog module. From the dashboard you can upload Movies.csv and/or Shows.csv, choose a Discord channel ID, and publish a formatted catalog embed set.

Discord Bot Setup

Create a Discord application and bot in the Discord Developer Portal, then invite it with:

https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=84992&scope=bot

Required channel permissions for each dashboard-selected channel:

  • View Channel
  • Send Messages
  • Embed Links
  • Attach Files
  • Read Message History

Channel IDs are configured from the dashboard and stored in state/bot-settings.json.

Local Setup

cp .env.example .env
cp services.example.json services.json

Edit .env and set:

  • DISCORD_BOT_TOKEN
  • DISCORD_GATEWAY_ENABLED=true
  • DASHBOARD_USERNAME
  • DASHBOARD_PASSWORD_HASH

Use the raw Discord bot token value. Do not include a Bot prefix.

Generate the dashboard password hash:

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

The bot keeps a Gateway connection open so Discord shows it as online even when no status refresh is happening.

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:

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:

python3 status_bot.py

Local runs automatically read .env from the current directory.

For dashboard testing without touching Discord, set:

DISCORD_DRY_RUN=true
ARCHIVE_STATUS_STATE=state/status-message.json

Open the dashboard:

http://127.0.0.1:8787

Sign in with DASHBOARD_USERNAME and the password you used when generating DASHBOARD_PASSWORD_HASH.

Set the Status and Media channel IDs from the dashboard before publishing Discord updates.

Docker Setup

cp .env.deploy.example .env
cp services.example.json services.json
python3 status_bot.py --hash-password
docker compose up -d --build

Paste the generated password hash into .env before starting the container.

Make sure Docker can read the service config and write runtime state:

chmod 600 .env
chmod 644 services.json
mkdir -p state
chmod 755 state

The container runs as 1001:1001 inside the image. If the mounted services.json or state/ were created by another user, fix ownership once:

sudo chown -R 1001:1001 services.json state

If state/ or services.json were created by a previous container as another user, fix ownership once:

sudo chown -R "$(id -u):$(id -g)" services.json state

The bot stores channel settings in state/bot-settings.json and Discord message IDs in state/status-message.json and state/media-catalog.json. Keep state/ mounted so the bot edits the same messages after restarts.

The deploy compose joins your existing reverse-proxy network:

networks:
  mediaserver_default:
    external: true

If that network does not already exist on the deploy host, create it once:

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:

http://archive-status-bot:8787

For Nginx Proxy Manager, put the dashboard behind an Access List or basic auth on the proxy host and disable the app's own login:

DASHBOARD_AUTH_DISABLED=true
DASHBOARD_COOKIE_SECURE=true

NPMs Access Lists use browser Authorization headers, so the app never needs to receive your username/password directly. Leave DASHBOARD_AUTH_DISABLED=false only for direct localhost testing.

NPMs own docs call out that Access List basic auth and app-side auth both use Authorization, so the app-side login is the one to disable in production.

If DASHBOARD_USERNAME and DASHBOARD_PASSWORD_HASH are omitted, the app now falls back to proxy-only mode automatically.

For direct local Docker testing without a proxy:

docker compose -f compose.yaml -f compose.local.yaml up -d --build

Then open:

http://127.0.0.1:8787

Dashboard

The dashboard currently supports:

  • viewing monitored services
  • selecting the Discord channel used by the status updater
  • 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
  • uploading Movies.csv and Shows.csv
  • editing imported movies and shows before publishing
  • publishing a compact Markdown media catalog to a selected Discord channel

The sidebar leaves room for future modules like polls, webhooks, automations, and service integrations without changing the bot shape later.

Media Catalog

Open the dashboard and switch to Media. Set the target Discord channel ID, choose Movies.csv, Shows.csv, or both, then import the CSVs into the library editor.

The editor supports adding, editing, and deleting movie/show rows before saving or publishing. Publishing sends the currently edited dashboard library, not the raw uploaded files.

Discord publishing uses one message with an attached media-catalog.md file so the channel does not get flooded by a long embed wall.

Channel selections are stored in:

BOT_SETTINGS_STATE=state/bot-settings.json

The parser accepts common column names such as title, name, year, genre, genres, rating, runtime, summary, overview, season, and episode. Show exports that contain one row per episode are grouped by show title where possible.

The bot stores media catalog message IDs in:

MEDIA_CATALOG_STATE=state/media-catalog.json

The editable media library is stored in:

MEDIA_LIBRARY_STATE=state/media-library.json

Republishing deletes the previous media catalog message and posts a fresh compact Markdown attachment.

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:

{
  "name": "Jellyfin",
  "url": "https://jellyfin.mithraic.cloud/health",
  "displayUrl": "https://jellyfin.mithraic.cloud",
  "expectedStatuses": ["200-399"]
}