Skip to main content

Mirror Discord channels into EVE Online in-game mailing lists

Project description

aa-discord-relay

Mirror selected Discord channels into EVE Online in-game mailing lists and into Telegram chats.

An Alliance Auth module. See docs/ for design and implementation plans.

What it does

A Discord channel is watched by a gateway bot. Every message posted there is recorded and delivered to each destination configured for that channel. The main case is a fleet ping posted by aa-fleetpings through a webhook: it reaches the people who read in-game mail and the people who read Telegram, without anyone reposting it by hand.

One channel can have several destinations, and each pairing carries its own templates, hourly quota, maximum age and footer language.

Requirements

  • Alliance Auth 5.x.
  • A cache backend shared between processes - Redis or Memcached. The resource lock, the send throttle and the bot heartbeat are all cache state written by one process and read by another; on a per-process backend all three silently do nothing.
  • Celery workers and celerybeat.
  • A process supervisor for the gateway bot (discordrelay_bot).
  • A Discord application with the privileged MESSAGE_CONTENT intent enabled. Without it messages arrive empty and every one of them looks like it has nothing to mirror.

Concepts

Four entities, and one mirror needs two screens because of it:

Entity What it is
Channel subscription a Discord channel that is read at all
Route one channel-to-destination pairing: templates, quota, age, language
Destination where messages go: an EVE mail address set, or a Telegram chat
Delivery one attempt to put one message into one destination

A message accepted from Discord produces one delivery per enabled route. Deliveries are independent: a broken Telegram chat does not stop the mailing list, and a resend acts on one delivery, never on the whole fan-out.

Installation

  1. Install the package into the Auth virtualenv and add discordrelay to INSTALLED_APPS.
  2. Run python manage.py migrate.
  3. Configure Alliance Auth's Discord service, including its DISCORD_BOT_TOKEN, and enable the MESSAGE_CONTENT intent for that bot.
  4. Invite the bot and grant View Channel only on the channels that are mirrored. The bot reads everything it can see; the permission is the boundary.
  5. Announce in each mirrored channel that it is mirrored. People who post there are being republished to a wider audience.
  6. Register the sender character: open EVE mail destinations in the admin and use Register sender character. Use a dedicated service character, not a personal one - every mail lands in its Sent Items and replies go to it.
  7. Import the mailing lists the sender is subscribed to: RecipientsImport mailing lists. The sender must be a member of every list it mirrors into.
  8. For Telegram, add a bot with its token under Telegram bots, add the bot to the chat, and create a Telegram destination naming the chat (and a forum topic, if any).
  9. Create a Channel subscription for the Discord channel and add one route per destination inline.
  10. Start the gateway bot under the supervisor and run python manage.py discordrelay_check.

Upgrading

supervisorctl stop discordrelay_bot
python manage.py migrate
supervisorctl restart worker
supervisorctl start discordrelay_bot
python manage.py discordrelay_check

Periodic tasks

Both schedules belong in the Auth installation's local.py:

CELERYBEAT_SCHEDULE["discordrelay_purge_old_history"] = {
    "task": "discordrelay.tasks.purge_old_history",
    "schedule": crontab(minute=0, hour=3),
}

# Not optional. requeue_stalled is the only thing that recovers a row
# claimed by a worker that died, and the only thing that queues a row
# whose on_commit callback never reached the broker: without it those
# rows sit forever, never retried, never reported, and still counted
# against both hourly quotas.
CELERYBEAT_SCHEDULE["discordrelay_requeue_stalled"] = {
    "task": "discordrelay.tasks.requeue_stalled",
    "schedule": crontab(minute="*/5"),
}

The five-minute interval matches ORPHANED_PENDING_AFTER_SECONDS = 120: run it less often and a lost task waits longer than a fleet ping lives.

Diagnostics

python manage.py discordrelay_check
python manage.py discordrelay_check --route-id 3
python manage.py discordrelay_check --route-id 3 --send-test-message

The command walks the chain in order - cache backend, reverse relay, bot heartbeat, channels without routes, then each route's destination - and exits non-zero when anything failed. --send-test-message needs a route id: one real message per route at once trips the remote spam limit and spends the installation-wide ESI error budget.

Delivery states:

State Meaning
pending accepted, waiting for a worker
sending a worker holds it right now
sent the remote side accepted it
failed terminal failure; the reason is on the row
expired older than the route's maximum age, never sent
dry_run rendered and recorded, deliberately not sent

The gap alarm depends on the cache. How long the bot was away is measured from the heartbeat, which lives in the shared cache. If that entry is evicted - an allkeys-lru Redis under memory pressure, or a cache flush - the outage reads as "never seen" and no gap notification is sent. The catch-up itself is unaffected; only the announcement is.

Channel visibility cannot be checked from here. A bot without View Channel behaves exactly like a bot with no subscription: no error, no message, nothing in the history. If a channel is silent and every check passes, verify the permission in Discord.

Duplicate policy

A worker can die between handing a message to the remote side and recording that it did. What happens next differs by transport, and the difference is not a bug:

  • EVE mail is recovered. The sender's Sent Items are searched for the message before it is sent again, so a crash costs a delay, not a duplicate. This needs the esi-mail.read_mail.v1 grant; without it the check cannot run.
  • Telegram is not recovered. The Bot API cannot be asked what it has already sent, so a crash mid-send leaves a duplicate message in the chat. The admin says so on every Telegram destination, and discordrelay_check reports it as a warning.

Rules and limits

  • The EVE Online Developer License Agreement applies. §2.3 (no automation that plays the game for a user) and §2.5 (respect the ESI error limit) are the two that bound this module.
  • ESI_SSO_CLIENT_ID is shared by the whole Auth installation. The ESI error budget is shared with it: a retry storm here returns 420 on every ESI route for every other module.
  • Two quotas apply to every send: the route's own hourly limit, and the transport resource limit on the sender character or the chat. Per-route quotas add up - five channels at twenty an hour on one character is a hundred, against an in-game limit of roughly five a minute.
  • Use a dedicated service character as the sender.
  • The Telegram Bot API limits a group to roughly twenty messages a minute; the per-bot and per-chat spacings are configured separately.

Development

Run every finite local gate, including the wheel and MariaDB suite:

uv run nox -s all

Preview dependency upgrades without changing uv.lock with uv run nox -s deps_upgrade; apply them with uv run nox -s deps_upgrade -- --apply.

To boot a persistent local demo, export ESI_SSO_CLIENT_ID, ESI_SSO_CLIENT_SECRET, ESI_USER_CONTACT_EMAIL, and Alliance Auth's DISCORD_BOT_TOKEN, then run:

uv run nox -s demo

Register http://localhost:8000/sso/callback/ as the callback. The session sets that URL itself instead of inheriting the production callback, validates the remaining settings, and starts a Docker Compose stack with MariaDB, Redis, the Django server, Celery worker and beat, and the Discord gateway bot. Ctrl+C removes the containers; the MariaDB volume is retained for faster subsequent starts.

Not supported in this version

  • Mail to individual characters: it needs CSPA charge handling.
  • An author allow-list: anyone who can post in a mirrored channel is mirrored.
  • Detection of a Discord bridge reposting into a mirrored channel.
  • Drift detection for the Telegram Bot API, unlike the ESI contract session.
  • A registry of characters enrolled through this module's own SSO flow: the sender choice is narrowed to characters holding a send grant, which is an approximation of consent, not consent.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aa_discord_relay-0.1.3.tar.gz (94.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aa_discord_relay-0.1.3-py3-none-any.whl (122.4 kB view details)

Uploaded Python 3

File details

Details for the file aa_discord_relay-0.1.3.tar.gz.

File metadata

  • Download URL: aa_discord_relay-0.1.3.tar.gz
  • Upload date:
  • Size: 94.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for aa_discord_relay-0.1.3.tar.gz
Algorithm Hash digest
SHA256 236a4143fa3f563dfb84239318b06fe125dd86ed47575505dd7b28696564a3e4
MD5 70a60fcdaa38789e0b1d94ee4a10299b
BLAKE2b-256 69bbb2147808c64148cd3271afeafceb5be8d286c29367b8632e98d20fb32858

See more details on using hashes here.

File details

Details for the file aa_discord_relay-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: aa_discord_relay-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 122.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for aa_discord_relay-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 2dabd77ebf21d41139cae83fcd4708def7f1705009658b54ea6ffbd1313843b6
MD5 bad2a39bff55ed9b13ede22c73b61843
BLAKE2b-256 9c9a08bd53a480faac35a701fba8883c52ac74792b5ff7e8d934287cc75addde

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page