Files
MineGeo/docs/discord-integration.md
pavel444-byte c9fc9496eb
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Has been cancelled
Improve worker error logging and update Dokploy deployment docs
2026-10-16 09:37:57 +05:00

138 lines
8.0 KiB
Markdown

# Discord announcements
MineGeo uses your Discord bot to send messages through Discord's REST API.
It does not need a continuously running bot process, Gateway,
Message Content intent, slash commands, an interactions URL, or a Discord webhook.
The bot can appear offline while announcements work.
New maps are announced when a Moderator or Admin approves their first public
version. Drafts, imported ZIP copies, player contributions, subsequent map updates,
and re-publication do not create new-map announcements. Existing maps from before
this integration are not backfilled.
In an open multiplayer lobby, the host can select **Publish party to Discord**.
Hosting requires a signed-in account and always uses its stored Display Name.
Signed-in players join with account names; guests can join with nicknames.
If the host leaves, another signed-in player takes over; the party closes if
only guests remain.
All published parties go to the single server-wide `DISCORD_PARTIES_CHANNEL_ID`.
Players cannot choose a destination. Discord readers can open the invite and join
without a MineGeo account while the lobby is open and has space. Messages show
the map, host, difficulty, player count, and link. Publishing is optional: ordinary
private invite links still work. Parties on private or imported maps cannot be
announced publicly. A room gets one announcement, including repeated clicks or
a transfer of lobby ownership. The counts are a snapshot when posted.
## Create and invite your bot
1. Open the [Discord Developer Portal](https://discord.com/developers/applications)
and select **New Application**. Name it **MineGeo**, or your preferred bot name.
2. Open **Bot**. Create the bot if prompted, then use **Reset Token** to obtain
its bot token. Keep it private; save it directly in Dokploy as described below.
3. Under **Installation**, enable **Guild Install**. For the guild's install
settings, choose the **bot** scope and these permissions: **View Channels**,
**Send Messages**, **Embed Links**. You do not need Administrator permission.
Use the installation link to add the bot to your Discord server. Alternatively,
**OAuth2 → URL Generator** with the `bot` scope generates an invite link.
4. Create or choose a text channel for map announcements and another for parties.
You can use the same channel for both. Ensure the bot has the three permissions
above in each channel, including any category or channel permission overrides.
Ensure community members can view the party channel.
5. In Discord's **User Settings → Advanced**, enable **Developer Mode**. Right-click
each channel and choose **Copy Channel ID**. On mobile, long-press the channel
or open its channel menu, then choose **Copy Channel ID**. Copy the numeric
channel IDs; an invite link or server ID is not a channel ID.
Discord setup references: [application setup](https://docs.discord.com/developers/quick-start/getting-started),
[bot permissions](https://docs.discord.com/developers/topics/permissions).
## Add the Dokploy environment variables
In Dokploy, open the MineGeo application's **Environment** settings and add:
| Name | Value | Sensitive? |
| --- | --- | --- |
| `DISCORD_BOT_TOKEN` | Bot token from the Developer Portal, without `Bot ` in front | Yes |
| `DISCORD_MAPS_CHANNEL_ID` | Numeric channel ID for new approved maps | No |
| `DISCORD_PARTIES_CHANNEL_ID` | Numeric channel ID where **all published parties** appear | No |
| `MINEGEO_URL` | `https://minegeo.site` (already configured) | No |
Use **Sensitive** for the token, and never prefix these names with `NEXT_PUBLIC_`.
Leave a channel variable blank to disable that announcement type. A Discord
invite such as `MINEGEO_DISCORD_URL` controls the footer button only; it cannot
send announcements.
After saving, redeploy the MineGeo application so the server uses the new
environment values.
For local use, put the same variables in ignored `.env.local` (ordinary `.env`
also works) and restart the server. Use a publicly reachable HTTPS `MINEGEO_URL`
for links that Discord members can open. Do not paste bot tokens into chat,
source control, Linear issues, or public map metadata.
## Check it
Sign in as **PerfectPython500**, open **Moderation → Discord integration**, and
select **Check Discord setup**. This reads the bot and channels without sending
a test message. It confirms the bot token, channel visibility, and channel type;
the bot must also have **Send Messages** and **Embed Links**.
Approve a genuinely new map to test a map announcement. Create a multiplayer room
on a published map, choose **Publish party to Discord**, and open the resulting
link from Discord. The lobby shows queued, sent, expired, or failed delivery.
## Saved delivery and retries
Announcements are stored in a private Supabase queue. Delivery runs after map
approval or party publication, leaving gameplay and approval independent of
Discord availability. Active workers hold leases, repeated clicks share one event,
and Discord receives a stable nonce with `enforce_nonce`. Rate limits and temporary
errors retry with a delay, up to eight attempts. Invalid tokens and missing channel
permissions require fixing the settings and selecting **Retry saved announcements**
as an Admin. Delivery resumes while the host polls their lobby or an Admin opens
the integration panel; other announcements also wake pending jobs.
Started, closed, full, removed, private, or stale parties are skipped. Pending
party announcements expire one hour after sharing; lobbies themselves last up to
24 hours. A map removed before delivery is skipped. Messages already posted are
kept when the match starts; the linked lobby shows its live state. When a party
closes (including its last player leaving), its posted announcement turns red,
says **Party closed**, and removes the join prompt. Five minutes after Discord
acknowledges that notice, the bot deletes the message. Vercel Workflow stores the
timer and resumes deletion even if every player closes the page. Retries preserve
the original deadline and tolerate messages already removed by a moderator.
Previously posted announcements for closed or deleted rooms receive the same
cleanup when the queue next runs. The original message's channel is used even
if `DISCORD_PARTIES_CHANNEL_ID` has since changed. Cleanup failures appear in the
Admin integration panel and can be retried after fixing bot access.
Discord only deduplicates nonces for a few minutes. A long outage after an
unconfirmed send can therefore result in a duplicate; inspect the channel before
manually retrying uncertain deliveries.
For optional scheduled retries, configure a private `CRON_SECRET` and call
`GET /api/integrations/discord/dispatch` with `Authorization: Bearer <CRON_SECRET>`.
This endpoint claims at most five due announcements. A Vercel Cron entry can use
that path; scheduling limits depend on your plan. No schedule is installed by
default, and the secret must never be placed in a URL.
## Database and verification
Apply `20261009133431_discord_announcements.sql` and
`20261009153708_discord_party_cleanup.sql` after existing migrations.
The queue and functions are service-role-only, use RLS and invoker permissions,
and verify the host from the room's server-verified access hash. No Discord
credentials are stored in Supabase or sent to a browser.
Vercel Workflow uses the project's managed backend automatically. Keep **Settings
→ Environment Variables → Enable access to System Environment Variables** enabled
and redeploy after changing it. No additional secret or scheduled cron is needed
for the five-minute timer. The bot edits and deletes its own announcements, so
**Manage Messages** is not required.
Run `npm test`, `npm run typecheck -- --incremental false`, `npm run build`, and
`node scripts/test-discord-sql.mjs`. `node scripts/test-discord-http.mjs` verifies
the live role gates, destination restrictions, optional configuration, and
host-only room action with temporary accounts and a room, which it removes.
Discord transport tests use a mocked API; real delivery requires your bot token
and channel permissions.