# 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 `. 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.