diff --git a/README.md b/README.md index b6d370d..dcd558e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ Discord bot for the [Fleetyards](https://fleetyards.net) community server. +## Scope + +**Single-tenant.** This bot is built for the Fleetyards Discord only. The Discord application has `bot_public: false`, so it cannot be added to other servers, and the config hardcodes Fleetyards-specific guild/channel/message/role IDs anyway. + +Anything we want to make available to other Star Citizen servers (slash-command ship lookups, hangar lookups, release announcements via webhook, etc.) belongs in a separate, public, multi-tenant bot — not yet started. yards-bot's scope is verification, role management, and other Fleetyards-only mod/admin work. + ## Status v0.1 — scaffold only. See [`docs/exec-plans/v1.md`](docs/exec-plans/v1.md) for the v1 design and feature scope. @@ -36,7 +42,7 @@ pnpm dev The bot needs `View Channel`, `Read Message History`, and `Manage Roles` on the target guild. Its top role must sit **above** any role it grants — Discord silently rejects role changes otherwise. -The `Server Members Intent` (privileged) must be enabled in the [Developer Portal](https://discord.com/developers/applications) under the bot's settings. +No privileged intents are required. v1 uses `Guilds` + `GuildMessageReactions` and fetches members on demand via REST, so the `Server Members Intent` does not need to be enabled. ## Deployment diff --git a/docs/exec-plans/v1.md b/docs/exec-plans/v1.md index 2a2001b..144ebcd 100644 --- a/docs/exec-plans/v1.md +++ b/docs/exec-plans/v1.md @@ -4,9 +4,13 @@ Replace the manual reaction-sweep with a real-time Discord bot. No database, no Rails integration. React-with-emoji grants a role; un-reacting revokes it. A periodic backfill sweep heals anything the gateway missed. +## Scope + +**Single-tenant.** yards-bot only runs against the Fleetyards Discord. The Discord application is private (`bot_public: false`) and the config hardcodes Fleetyards-specific IDs. Anything we'd want to expose to other Star Citizen servers (slash-command lookups, release announcements, etc.) belongs in a **separate, future, public bot** — not in this repo. + ## Out of scope for v1 -Account linking with the Fleetyards Rails app, slash commands, audit-to-channel, captcha, welcome DMs, role sync with fleets/supporters. Tracked separately for v2+. +Account linking with the Fleetyards Rails app, slash commands, audit-to-channel, captcha, welcome DMs, role sync with fleets/supporters. Multi-tenant operation. Tracked separately for v2+ (or, for the multi-tenant pieces, the future public bot). ## Stack @@ -73,8 +77,8 @@ sweep: ## Discord intents & partials -- Intents: `Guilds`, `GuildMembers` *(privileged — toggle in dev portal)*, `GuildMessageReactions` -- Partials: `Message`, `Channel`, `Reaction` — required so the bot receives reaction events on messages older than its uptime +- Intents: `Guilds`, `GuildMessageReactions`. No privileged intents required — members are fetched on demand via REST instead of cached via the `GuildMembers` intent. +- Partials: `Message`, `Channel`, `Reaction`, `User`. The first three let the bot receive reaction events on messages older than its uptime; `User` is required for `MESSAGE_REACTION_REMOVE` on uncached users, because the gateway payload only ships `user_id` (not a member object like the add event does), and without it discord.js silently drops the event. ## Required bot permissions in the guild @@ -130,20 +134,21 @@ sweep: ## Implementation order -1. Repo scaffold + tsconfig + biome + CI — **done**. -2. Config loader with zod. -3. discord.js client + READY logging. -4. Sweep (TS port of the original bash script) with unit tests. -5. `reactionAdd` + `reactionRemove` handlers. -6. Health endpoint. -7. Dockerfile finalization + local docker-compose. -8. Kamal deploy config. -9. AppSignal wiring. -10. README updates with the role-hierarchy gotcha + bot invite URL. +1. Repo scaffold + tsconfig + biome + CI — **done** (PR #0, initial scaffold). +2. Config loader with zod — **done** (PR #1). +3. discord.js client + READY logging — **done** (PR #2). +4. Sweep (TS port of the original bash script) with unit tests, run on startup — **done** (PR #3). +5. `reactionAdd` + `reactionRemove` handlers — **done** (PR #4). +6. Cron-scheduled sweep on top of the on-startup sweep. +7. Health endpoint. +8. Dockerfile finalization + local docker-compose. +9. Kamal deploy config. +10. AppSignal wiring. +11. README polish (role-hierarchy gotcha, bot invite URL). ## Risks - **Role hierarchy misconfiguration** is the most common silent failure for this kind of bot. Sweep should explicitly probe and log on startup. -- **Privileged `GuildMembers` intent** must be toggled in the Developer Portal; if missing, members can't be resolved on reaction events. -- **Single-instance constraint.** Kamal config must not scale horizontally. +- **Partials.User pitfall:** `MESSAGE_REACTION_REMOVE` is silently dropped by discord.js if the user isn't cached and `Partials.User` isn't enabled (the gateway payload only carries `user_id`). We do enable it; don't remove it from `src/client.ts` without testing the remove path live. +- **Single-instance constraint.** Kamal config must not scale horizontally — duplicate gateway connections would produce duplicate role grants. - **Stateless trade-off**: no in-bot audit trail beyond logs. Acceptable in v1; if richer audit is needed later, the cheapest add is a Discord audit channel before reaching for a DB.