discord-bridge: Discord presence into Matrix ghosts
Lint / JS (eslint) (pull_request) Successful in 10s
Lint / Shell (shellcheck) (pull_request) Successful in 11s
Lint / No Hookshot webhook IDs in README (pull_request) Successful in 6s
Lint / No secrets in webhook configs (pull_request) Successful in 6s
Lint / Landing page is rendered (matrix (pull_request) Successful in 6s
Lint / Python (ruff) (pull_request) Successful in 7s
Lint / Secret scan (gitleaks) (pull_request) Successful in 13s
Lint / Shell (shellcheck) (push) Successful in 26s
Lint / JS (eslint) (push) Successful in 18s
Lint / No Hookshot webhook IDs in README (push) Successful in 21s
Lint / No secrets in webhook configs (push) Successful in 12s
Lint / Python deps (pip-audit) (pull_request) Successful in 1m54s
Lint / Landing page is rendered (matrix (push) Successful in 17s
Lint / Python (ruff) (push) Successful in 16s
Lint / Secret scan (gitleaks) (push) Successful in 31s
Lint / Python deps (pip-audit) (push) Successful in 1m37s

mautrix-discord doesn't bridge presence. lotus_presence.py watches the Discord
server (a second gateway session on the bridge's own bot, Guilds + Presence
intents only) and sets presence and a status message on each @discord_* ghost:
online / idle / DND / offline -> online / unavailable / msc3026 busy / offline;
custom status and the first activity become the status message.

Its own appservice registration is non-exclusive on the ghost namespace, so it
can update ghosts but not create them. Synapse drops an unrefreshed user to
offline after about a minute, so live states are re-sent every 20 s; if the
service dies, ghosts go offline rather than stale. The installer generates the
credentials on the server and doesn't restart Synapse. Statuses and activities
are never logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-11-04 19:36:38 -05:00
co-authored by Claude Opus 5.5
parent ed1300f146
commit f8f0ca4549
6 changed files with 560 additions and 0 deletions
+2
View File
@@ -436,6 +436,8 @@ Skip `guilds bridge`: it sets the guild to create a Matrix room for every channe
old room). The relay webhook stays with the channel. Keep `@discordbot` at the default power level so it
can't overwrite the room's name and icon with the Discord channel's.
**Presence (`discord-bridge/lotus_presence.py`, `lotus-discord-presence.service`):** the bridge doesn't carry Discord status, so this small service does. It reuses the bridge's bot (token read from the bridge database at startup; a second gateway session with only the Guilds and Presence intents; **Presence Intent** must stay on in the developer portal) and has its own appservice registration, `/etc/matrix-synapse/discord-presence-registration.yaml`, non-exclusive on `@discord_*`, so it can update ghosts but not create them. Online / idle / DND / offline map to online / unavailable / `org.matrix.msc3026.busy` / offline; custom status and the first activity become the status message. Only people who already have a ghost show up. Synapse drops an unrefreshed user to offline after about a minute, so live states are re-sent every 20 s; if the service dies, ghosts go offline rather than stale. Install once with `discord-bridge/install-presence.sh`, then restart Synapse. Statuses and activities are never logged.
**Bot profile:** `@discordbot` shows as "Discord Bridge" with the Discord logo
(`discord-bridge/bot-avatar.png`). To reapply it: `pct exec 151 -- bash /opt/matrix-config/discord-bridge/set-bot-profile.sh`.