From 2fd4a4b1c0ac0a0e4fe637ff3c4c8a97154c88d8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 8 Jun 2026 01:05:46 +0000 Subject: [PATCH] docs: refresh engineering runbooks Co-authored-by: zVapor_ --- README.md | 2 +- docs/architecture.md | 4 +++- docs/commands.md | 4 +++- docs/configuration.md | 22 ++++++++++++++-------- docs/contributing.md | 5 ++++- docs/database.md | 2 ++ docs/engineering-guide.md | 24 +++++++++++++----------- docs/features.md | 17 ++++++++++++----- docs/getting-started.md | 6 +++--- 9 files changed, 55 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 986e693..d7ad02f 100644 --- a/README.md +++ b/README.md @@ -41,5 +41,5 @@ This project is currently a WIP! This is a Discord bot application, not an importable npm module. It starts from `src/index.js`, logs in with the token selected by `src/config.js`, connects to MongoDB through Prisma when enabled, registers commands/components/events, and exposes an unauthenticated health endpoint on `0.0.0.0:8080` that returns a plain-text online message plus Discord invite link. -Developer and operator notes live in [`docs/engineering-guide.md`](docs/engineering-guide.md). Start there for setup constraints, command deployment, permission gates, economy behavior, and troubleshooting. +Start with the [`docs/README.md`](docs/README.md) index for setup, configuration, commands, features, database, architecture, and contribution guides. Developer and operator notes live in [`docs/engineering-guide.md`](docs/engineering-guide.md) for setup constraints, command deployment, permission gates, economy behavior, and troubleshooting. diff --git a/docs/architecture.md b/docs/architecture.md index 8db3f43..c50655f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -130,10 +130,11 @@ Files in `src/contextmenus/` export `{ data, run }` where `data` includes `type` ## Command Deployment -Two deployment paths exist: +Three deployment paths exist: 1. **Slash commands** — `src/events/ready/registerCommands.js` diffs local commands against Discord API and creates/edits/deletes as needed on `DEV_GUILD_ID` 2. **Developer commands** — `src/handlers/deploy.js` bulk-overwrites guild commands on `config.handler.guildId` using the developer command array +3. **Context menus** — `src/events/ready/registerContextMenus.js` creates missing menus and deletes menus marked `deleted` on `DEV_GUILD_ID`; it does not edit existing menu definitions ## Database Layer @@ -176,6 +177,7 @@ The schema files maintain backward-compatible import paths so existing `require( | `getButtons.js` / `getSelects.js` / `getModals.js` | Load component modules | | `commandComparing.js` | Normalize command data for diff comparison | | `normalizeIdAllowlist.js` | Ensure ID lists are arrays | +| `rankCardPresenceStatus.js` | Normalize Discord member presence into canvacord-supported rank-card statuses | | `buttonPagination.js` | Alternative pagination helper | | `join-to-create/generateEmbed.js` | JTC status embed builder | | `join-to-create/generateRow.js` | JTC dashboard button row | diff --git a/docs/commands.md b/docs/commands.md index af2c56a..7373ba0 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -29,7 +29,7 @@ Slash commands are registered per-guild on `DEV_GUILD_ID` at startup. |---------|-------------|---------| | `/afk set [message]` | Set your AFK status | `message` (optional): AFK reason | | `/afk remove` | Remove your AFK status | — | -| `/rank info [user]` | View XP rank card | `user` (optional): defaults to self | +| `/rank info ` | View XP rank card | `user` (required): must be a member of the guild | | `/rank reset ` | Reset a user's XP and level | `user` (required) | | `/rank set ` | Set a user's level | `user` (required), `level` (required) | | `/test` | Simple test command | — | @@ -73,6 +73,8 @@ The setup wizard provides a select menu to configure: - **Welcome System** — channel, message template, rules channel, member/bot auto-roles - **Ticket System** — category, panel channel, support role +The setup command also displays Join-to-Create as a future feature, but it is not currently available in the select menu. + --- ## Context Menu Commands diff --git a/docs/configuration.md b/docs/configuration.md index fcff25a..9023f4b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -10,10 +10,10 @@ Copy `.env.example` to `.env` and fill in the values. | Variable | Description | Used When | |----------|-------------|-----------| -| `PRODUCTION` | Set to `true` for production, `false` for development | Always — controls which token/ID/URI set is used | +| `PRODUCTION` | Set to `true` for production, `false` for development | Always — controls selected token/client ID/guild ID, with MongoDB URI caveat below | | `DEV_TOKEN` | Discord bot token for development | `PRODUCTION=false` | | `DEV_CLIENT_ID` | Discord application ID for development | `PRODUCTION=false` | -| `DEV_GUILD_ID` | Guild ID for slash command registration | Always | +| `DEV_GUILD_ID` | Guild ID for ready-time slash and context-menu registration | Always | | `DEV_MONGODB_URI` | MongoDB connection string for development | `PRODUCTION=false` | ### Production Variables @@ -34,19 +34,25 @@ Copy `.env.example` to `.env` and fill in the values. ### Production Toggle Behavior -The `PRODUCTION` flag controls which set of credentials the bot uses: +The `PRODUCTION` flag is read in `src/example.config.js`, but not every value is selected the same way: ``` -PRODUCTION=false → DEV_TOKEN, DEV_CLIENT_ID, DEV_MONGODB_URI -PRODUCTION=true → CLIENT_TOKEN, CLIENT_ID, MONGODB_URI +PRODUCTION=false → DEV_TOKEN, DEV_CLIENT_ID, DEV_GUILD_ID +PRODUCTION=true → CLIENT_TOKEN, CLIENT_ID, GUILD_ID ``` -The guild ID for command registration: +MongoDB URI selection uses JavaScript truthiness instead of comparing to `"true"`: + ``` -PRODUCTION=false → DEV_GUILD_ID -PRODUCTION=true → GUILD_ID +PRODUCTION unset or empty → DEV_MONGODB_URI +PRODUCTION=false → MONGODB_URI because the string "false" is truthy +PRODUCTION=true → MONGODB_URI ``` +Verify the generated `src/config.js` before starting the bot. For local development with `PRODUCTION=false`, either keep `MONGODB_URI` pointed at a safe development database or adjust `src/config.js` after copying the template. + +Ready-time slash and context-menu registration always uses `DEV_GUILD_ID` in `src/events/ready/registerCommands.js` and `src/events/ready/registerContextMenus.js`. `GUILD_ID` feeds `config.handler.guildId`, which is used by developer-command deployment and support-guild features such as stats and welcome handling. + --- ## Runtime Configuration (`src/config.js`) diff --git a/docs/contributing.md b/docs/contributing.md index 50a2444..966fb33 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -203,8 +203,11 @@ test("my feature works correctly", () => { | `dev-command-gate.test.js` | Developer command `options.developers` flag detection | | `developer-gate.test.js` | Developer ID allowlist validation | | `prefix-developer-gate.test.js` | Prefix command developer restriction | +| `events-handler-shape.test.js` | Event loader registers `{ event, run }` modules on their declared Discord event names | +| `interaction-cooldown.test.js` | Slash cooldown bookkeeping records one command per first use and expiry timers no-op safely | | `economy-amount-all.test.js` | Case-insensitive `all` keyword for deposit/withdraw | -| `economy-account-delete.test.js` | Account deletion uses correct deleteMany filter | +| `economy-account-delete.test.js` | Historical regression for document-shaped `deleteMany()` deletion failures; current Prisma code should delete through the model delegate | +| `rank-card-presence-status.test.js` | Rank-card presence normalization maps null, missing, and unsupported statuses to canvacord-safe values | | `rob-syntax.test.js` | `/rob` source file is valid JavaScript | | `rob-module-loads.test.js` | `/rob` file parses without errors | | `rob-cooldown-race.test.js` | Cooldown lock prevents concurrent rob races | diff --git a/docs/database.md b/docs/database.md index eeffb0c..8e3cbf4 100644 --- a/docs/database.md +++ b/docs/database.md @@ -215,6 +215,8 @@ Join-to-Create voice channel configuration — one per guild. **Used by:** `voiceStateUpdate` event handler +**Operational note:** `src/events/Guild/jointocreate.js` reads `data.UserLimit` and passes it to Discord as the temporary voice channel `userLimit`, but `UserLimit` is not defined in `prisma/schema.prisma`. With the current schema, operators should expect the created channel to use Discord's default user limit unless the schema and setup flow are extended. + ## Schema Files The schema files in `src/schemas/` are thin re-exports of Prisma model delegates: diff --git a/docs/engineering-guide.md b/docs/engineering-guide.md index 7bef14a..f4b3b5f 100644 --- a/docs/engineering-guide.md +++ b/docs/engineering-guide.md @@ -134,15 +134,17 @@ Command execution contracts: - The Guild slash-command handler in `src/events/Guild/interactionCreate.js` supports `command.options.cooldown` as a millisecond duration. The cooldown store is an in-memory `Map` keyed by Discord user ID, with command names as values, so it is per-process and clears on restart. - Slash cooldowns are per user and per command name. A user can be cooling down for one slash command while using another command, and another user is not blocked by the first user's cooldown. - The slash cooldown is recorded before `command.run(client, interaction)` executes. Expiry uses `setTimeout`; if another timer has already removed the user entry, the expiry handler no-ops instead of throwing. -- The active validation path in `src/events/validations/chatInputCommandValidator.js` calls chat-input commands directly and does not apply the Guild handler cooldown map. Verify the event loader caveat below before depending on `options.cooldown` in production. +- The active validation path in `src/events/validations/chatInputCommandValidator.js` calls chat-input commands directly and does not apply the Guild handler cooldown map. The Guild `interactionCreate` handler is registered separately and also listens for slash commands, but it skips interactions that have already been replied to or deferred. - Prefix commands are executed through `src/events/Guild/messageCreate.js` with `await command.run(client, message, args)`, so async command failures are caught by that handler's `try/catch` and logged through `log(error, "err")`. - Prefix command metadata can include `data.permissions` and `data.developers`; `data.cooldown` is present on the prefix eval command but is not enforced by `messageCreate.js`. -Event loader caveat: +Event loader contracts: -- `src/handlers/events.js` registers each direct folder under `src/events` as an event name, except `validations`, which is remapped to `interactionCreate`. -- Files under `src/events/ready` and `src/events/validations` export callable functions and match that loader. -- Files under `src/events/Guild` export `{ event, run }` objects and the folder name would register as `Guild`. That shape does not match the current loader's callable function contract or Discord event names such as `messageCreate`, so verify runtime registration before relying on those handlers for prefix commands or component routing. +- `src/handlers/events.js` scans each direct folder under `src/events`. +- The `validations` folder is a special case: every file is called in sequence from one `interactionCreate` listener, which forms the primary interaction validation and execution pipeline. +- Function exports in other folders are grouped under the folder name as the Discord event name. Files under `src/events/ready` use this path. +- Object exports with `{ event, run }` are registered on `eventModule.event` and call `eventModule.run(client, ...args)`. Files under `src/events/Guild` use this path for `messageCreate`, `guildMemberAdd`, `voiceStateUpdate`, and backup `interactionCreate` handling. +- `interactionCreate` currently has multiple listeners: the Guild backup routers and the validations chain. Registration follows the filesystem order returned by `getAllFiles`, so handlers that can overlap should keep their own `interaction.replied` / `interaction.deferred` guards. ## Command Deployment @@ -161,19 +163,19 @@ Troubleshooting command registration: ## GitHub Release Automation -`.github/workflows/release.yml` publishes GitHub Releases from `main` when `package.json` changes the top-level `version` field. +`.github/workflows/release.yml` publishes GitHub Releases from `main` when the derived `v` tag does not already exist. Release workflow behavior: 1. A push to `main` starts the `Release` workflow. 2. The workflow reads `package.json` with Node and derives the release tag as `v`, for example `v1.2.1`. -3. It checks only the latest pushed commit range, `HEAD~1..HEAD`, for a `package.json` line containing `"version"`. -4. If the version changed, it fetches tags and skips the release when the derived tag already exists. -5. If the tag is new, it sets up Node.js `22`, installs dependencies with `npm ci || npm install`, runs `npm test`, generates a changelog from commits since the most recent version-sorted tag, and creates a non-draft, non-prerelease GitHub Release with `softprops/action-gh-release`. +3. It checks whether that tag is already present in git history with `git rev-parse "$TAG"`. +4. If the tag exists, the workflow skips all install, test, changelog, and release steps. +5. If the tag is missing, it sets up Node.js `22`, installs dependencies with `npm ci || npm install`, runs `npm test`, generates a changelog from commits since the most recent version-sorted tag, and creates a non-draft, non-prerelease GitHub Release with `softprops/action-gh-release`. Release operator notes: -- Bump `package.json` in the commit that lands on `main` when you want a release. The workflow does not create or commit version bumps. +- Bump `package.json` before merging when you want a new release tag. The workflow does not create or commit version bumps, and it does not inspect whether the version changed in the latest commit range. - The workflow creates a Git tag through the GitHub Release action; do not pre-create the same `v` tag unless you intend the workflow to skip release creation. - The workflow publishes a GitHub Release only. It does not publish an npm package, build Docker images, deploy the bot, or update Discord commands. - `contents: write` permission is required so the workflow token can create the release and tag. @@ -239,4 +241,4 @@ When changing economy code, prefer adding or updating focused `node:test` regres - Prisma/MongoDB connection failures are logged and rethrown from `src/handlers/prisma.js`; `ExtendedClient.start()` attaches a `.catch()` and does not block Discord login while the connection attempt runs. Commands that query MongoDB still depend on a valid runtime URI, network, generated Prisma client, and database credentials. - Top.gg autoposting only starts when `TOPGG_TOKEN` is present, but the functions module is required during client startup. - The health endpoint is not authenticated. Do not expose port `8080` publicly unless that is intentional for the hosting environment. -- Prefix command support depends on the `messageCreate` handler in `src/events/Guild/messageCreate.js`; because of the event loader caveat above, verify runtime registration before documenting prefix commands as available to server members. +- Prefix command support depends on both `config.handler.commands.prefix` and the `messageCreate` handler in `src/events/Guild/messageCreate.js`, which is registered from the module's `{ event: "messageCreate", run }` export. diff --git a/docs/features.md b/docs/features.md index 1ab197a..08b174a 100644 --- a/docs/features.md +++ b/docs/features.md @@ -41,17 +41,19 @@ A configurable support ticket system with HTML transcripts. 2. Select **Ticket** from the setup menu 3. Configure: - **Category** — the channel category where tickets are created - - **Channel** — the channel where the ticket panel is posted + - **Channel** — stored as the ticket panel channel ID - **Role** — the support role that gets access to tickets +The setup flow stores these values in MongoDB. It does not currently post a ticket panel for you; operators still need to provide a message with a select menu using the `ticket` custom ID before members can open tickets through `ticket-menu.js`. + ### How It Works -1. Members select a ticket type from the panel select menu +1. Members select a ticket type from a `ticket` select menu 2. A modal appears asking for a reason/description 3. A private channel is created named `ticket-` 4. The channel is visible only to the member, support role, and admins 5. When resolved, click the **Close Ticket** button -6. An HTML transcript is generated and DM'd to the ticket creator +6. An HTML transcript is generated and DM'd to the member who clicked **Close Ticket** 7. The channel is deleted after 10 seconds --- @@ -125,7 +127,7 @@ Temporary voice channels that are created when a user joins a hub channel. ### How It Works -1. An admin configures a hub voice channel via the setup system +1. A `JTCSetup` record exists for the guild with a hub voice channel 2. When a user joins the hub channel, a temporary voice channel is created 3. The channel is named after the user (e.g., `🔊 | Username`) 4. The user gets Manage Channels permission on their channel @@ -137,6 +139,8 @@ Temporary voice channels that are created when a user joins a hub channel. JTC setup data is stored in the `jtcsetups` collection with the hub channel ID, category, and active temporary channels. +The runtime `voiceStateUpdate` handler exists, but `/setup` does not expose Join-to-Create today. The setup command shows JTC as a future feature and its select-menu option is commented out, so current operators need a pre-existing database record or a future setup flow before the handler can run. + --- ## Rank / XP System @@ -145,7 +149,7 @@ Per-guild leveling system with visual rank cards. ### Commands -- **`/rank info [user]`** — View a rank card showing current level and XP +- **`/rank info `** — View a rank card showing current level and XP for a guild member - **`/rank reset `** — Reset a user's XP and level to defaults - **`/rank set `** — Manually set a user's level @@ -156,6 +160,9 @@ Rank cards are generated using the `canvacord` library and display: - Current level - XP progress - Username +- Presence status dot (`online`, `idle`, `dnd`, `offline`, or `streaming`) + +If the bot cannot see a member presence, or Discord reports an unsupported status such as `invisible`, the rank card uses `offline` so `canvacord` does not throw. Enable Presence Intent in the Discord Developer Portal if you expect live status dots. ### Data Storage diff --git a/docs/getting-started.md b/docs/getting-started.md index 4a72571..e4e0d04 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -22,9 +22,7 @@ cd Doubt-Discord-Bot npm install ``` -This installs all runtime dependencies and the Prisma CLI (dev dependency). The Prisma client is auto-generated during install via the `postinstall` hook. - -If you need to regenerate the Prisma client manually: +This installs all runtime dependencies and the Prisma CLI (dev dependency). This repo does not currently define a `postinstall` hook, so generate the Prisma client after installing dependencies and any time `prisma/schema.prisma` changes: ```bash npx prisma generate @@ -106,6 +104,8 @@ Set `DEV_MONGODB_URI=mongodb://localhost:27017/doubt-dev` in your `.env`. npm run dev ``` +`npm run dev` invokes `nodemon`, but `nodemon` is not listed in `package.json`. Install it globally or add it to your local development environment if the command is not available. + ### Production mode: ```bash