Skip to content

feat: custom bot status rotation#304

Merged
BillChirico merged 18 commits intomainfrom
mohsin/rotating-bot-status
Mar 19, 2026
Merged

feat: custom bot status rotation#304
BillChirico merged 18 commits intomainfrom
mohsin/rotating-bot-status

Conversation

@MohsinCoding
Copy link
Copy Markdown
Contributor

Summary

  • add configurable rotating bot presence support in �otStatus with backward compatibility for legacy activity config
  • support template variables for dynamic status text: {guildCount}, {memberCount}, {commandCount}, {uptime}, {version} (plus existing {botName})
  • wire bot status lifecycle into startup/shutdown and hot-reload on new �otStatus.rotation.* config paths
  • add �otStatus schema validation updates and default config.json rotation configuration
  • add dashboard config support for bot presence/rotation toggles and interval controls
  • expand test coverage for bot status interpolation, rotation scheduler behavior, and config listener/schema updates

Verification

  • root workspace vitest suite passes locally after dependency install
  • web workspace currently has unrelated pre-existing test failures not introduced by this change

Closes #228

Copilot AI review requested due to automatic review settings March 14, 2026 20:41
@github-project-automation github-project-automation Bot moved this to Backlog in Volvox.Bot Mar 14, 2026
@railway-app
Copy link
Copy Markdown

railway-app Bot commented Mar 14, 2026

This PR was not deployed automatically as @MohsinCoding does not have access to the Railway project.

In order to get automatic PR deploys, please add @MohsinCoding to your workspace on Railway.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 14, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added bot presence and activity rotation management with configurable status messages, template variables (server count, member count, uptime, etc.), and automatic rotation intervals.
    • Added dashboard configuration UI for bot presence settings and rotation controls.
  • Bug Fixes

    • Updated documentation link to correct domain.
  • Improvements

    • Enhanced configuration validation system with improved schema support and better error handling.

Walkthrough

This PR implements a custom bot status rotation feature with template variable support. It introduces a new botStatus configuration section with rotation settings, extends the botStatus module to handle message rotation on a configurable interval, updates validation and config listeners, and adds web dashboard UI components for managing the feature.

Changes

Cohort / File(s) Summary
Bot Status Configuration & Validation
config.json, src/api/utils/configValidation.js, tests/api/utils/configValidation.test.js, tests/api/utils/configAllowlist.test.js, tests/api/utils/validateConfigPatch.test.js
Adds botStatus top-level config with enabled, status, and nested rotation settings. Extends CONFIG_SCHEMA with validation for rotation messages (required text, enum type), enforces minLength and pattern constraints, and refactors array/object validation logic. Updates config allowlist and test assertions.
Core Bot Status Module
src/modules/botStatus.js, tests/modules/botStatus.test.js
Refactored to support new rotation config schema while maintaining legacy compatibility. Added helpers: formatUptime(), getRotationMessages(), resolveRotationIntervalMs(), isRotationEnabled(). Extended interpolateActivity() with template variables ({commandCount}, {uptime}, {version}). Rotation now uses configured messages with Discord-safe 20-second minimum interval. Enhanced startBotStatus() to clear existing intervals and apply rotation only when enabled.
Integration & Startup/Shutdown
src/index.js, src/config-listeners.js, tests/config-listeners.test.js, tests/index.test.js
Wires startBotStatus(client) after client login and stopBotStatus() during graceful shutdown. Expands config listener keys to monitor botStatus.rotation.* subkeys. Adds mocking infrastructure for startup/shutdown tests.
Global Config Writes
src/api/routes/guilds.js, tests/api/routes/guilds.test.js
Enforces botStatus updates as global config writes (not guild-scoped). Returns 403 when non-owner attempts OAuth-based global write. Updates config persistence to use undefined guild ID for global scope.
Web Dashboard Types
web/src/types/config.ts, web/src/components/dashboard/config-workspace/types.ts
Adds exported interfaces BotStatusConfig, BotStatusRotationConfig, BotStatusRotationMessage. Extends BotConfig with optional botStatus field. Updates ConfigFeatureId union to include 'bot-status'.
Web Dashboard Categories & UI
web/src/components/dashboard/config-workspace/config-categories.ts, web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
Adds 'bot-status' to community-tools feature IDs and section keys. Creates new CONFIG_SEARCH_ITEMS entry. Implements settings card with presence status select, rotation toggle, and interval input. Includes legacy rotateIntervalMs fallback conversion to minutes.
Landing Page Components
web/src/app/page.tsx, web/src/components/landing/FeatureGrid.tsx, web/src/components/landing/Footer.tsx, web/src/components/landing/Hero.tsx, web/src/components/landing/Pricing.tsx, web/src/components/landing/Stats.tsx
Reformats JSX and updates docs URL from docs.volvox.dev to docs.volvox.bot. Multi-line JSX attribute expansion and object literal refactoring; no functional changes to component logic or UI behavior.
Code Organization & Formatting
src/modules/events.js, src/modules/events/interactionCreate.js, src/modules/handlers/ticketHandler.js, src/modules/performanceMonitor.js, tests/api/routes/warnings.test.js, tests/commands/voice.test.js, tests/utils/discordCache.test.js, tests/modules/events-extra.test.js, src/modules/events/messageCreate.js, biome.json
Minor import reordering, multi-line formatting of JSX/objects, and export statement reorganization. Biome schema updated from 2.4.5 to 2.4.7. No functional logic changes.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: custom bot status rotation' clearly describes the main feature being added and aligns with the primary changeset objective.
Description check ✅ Passed The description comprehensively covers the key changes including configurable rotation, template variables, lifecycle integration, schema validation, dashboard support, and test expansion.
Linked Issues check ✅ Passed All core objectives from issue #228 are met: rotation support with multiple messages, template variable support for guildCount/memberCount/commandCount/uptime/version, Discord ActivityType support, graceful single-message fallback, scheduler with rate-limit consideration, hot-reload, dashboard controls, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed Changes remain focused on bot status rotation feature. Minor formatting changes (imports, whitespace, JSX layout, test wrappers) and schema version bump in biome.json are incidental improvements, not out-of-scope additions.
Docstring Coverage ✅ Passed Docstring coverage is 82.93% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mohsin/rotating-bot-status
  • 🛠️ Publish Changes: Commit on current branch
  • 🛠️ Publish Changes: Create PR
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable rotating Discord bot presence (“botStatus”) with template interpolation, new config schema paths, startup/shutdown lifecycle wiring, and dashboard controls, with expanded test coverage to validate behavior.

Changes:

  • Implement rotating presence messages with new botStatus.rotation.* config shape (plus legacy compatibility) and template variables ({guildCount}, {memberCount}, {commandCount}, {uptime}, {version}, {botName}).
  • Wire bot status lifecycle into bot startup/shutdown and hot-reload via config listeners.
  • Add dashboard UI + web types for bot presence/rotation controls and extend API/schema + tests for new config paths.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/src/types/config.ts Adds botStatus and rotation message types to the web config model.
web/src/components/dashboard/config-workspace/types.ts Adds bot-status feature id for dashboard workspace typing.
web/src/components/dashboard/config-workspace/config-categories.ts Exposes “Bot Presence” in categories, labels, and search.
web/src/components/dashboard/config-editor.tsx Adds UI controls for bot presence status + rotation toggle/interval.
tests/modules/botStatus.test.js Expands tests for interpolation variables and rotation config behavior.
tests/config-listeners.test.js Updates expectations for new botStatus rotation listener keys/count.
tests/api/utils/validateConfigPatch.test.js Allows botStatus as a writable top-level config section in test allowlist.
tests/api/utils/configValidation.test.js Adds schema coverage assertions + botStatus validation tests.
tests/api/utils/configAllowlist.test.js Ensures botStatus is included in readable/writable allowlists.
src/modules/botStatus.js Implements new rotation config parsing, interpolation variables, scheduler behavior.
src/index.js Starts/stops bot status module in startup and graceful shutdown.
src/config-listeners.js Adds hot-reload listeners for botStatus.rotation.* config paths.
src/api/utils/configValidation.js Adds botStatus schema definition for API validation.
config.json Adds default botStatus.rotation configuration to seed defaults.

Comment thread src/modules/botStatus.js
Comment thread src/api/utils/configValidation.js
Comment thread web/src/components/dashboard/config-editor.tsx Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 14, 2026

Greptile Summary

This PR adds configurable rotating bot presence to Volvox Bot, introducing a new botStatus.rotation config shape alongside full backward compatibility for the legacy activities/activityType/rotateIntervalMs format. Template variables ({guildCount}, {memberCount}, {commandCount}, {uptime}, {version}) are supported in rotation messages. The feature is wired into the startup/shutdown lifecycle, hot-reloads on config changes, and is surfaced in the dashboard's Community Tools section. The config validation schema is extended to cover the new nested rotation.messages structure, and test coverage is meaningfully expanded.

Key observations:

  • readFileSync in timer callback (src/modules/botStatus.js): getPackageVersion() blocks the event loop on first invocation from inside applyPresence, which fires on every rotation tick. Moving the read to module-load time would be safer.
  • min attribute vs. parseNumberInput floor mismatch (CommunitySettingsSection.tsx): <Input min={0.5}> signals to users that 0.5 minutes is valid, but parseNumberInput(event.target.value, 1) silently clamps any value below 1 to 1 minute — these two limits should be aligned.
  • The botStatus global-write guard on the guild config PATCH endpoint correctly restricts OAuth callers to bot owners. API-secret auth is treated as an operator credential (consistent with the rest of the middleware), so the bypass is intentional.
  • The Streaming activity type is correctly excluded from ACTIVITY_TYPE_MAP (and documented), though the schema validator still lists it as an allowed enum value — any Streaming submission will silently fall back to Playing.

Confidence Score: 3/5

  • PR is safe to merge with two minor issues to address before production: a blocking readFileSync in the rotation timer and a UI min-attribute mismatch in the dashboard interval input.
  • Core logic is well-structured with good test coverage and backward compatibility. The two flagged issues are low-severity (no data loss or security risk), but the synchronous file read on the hot path and the silent clamping mismatch in the UI are worth fixing before shipping.
  • src/modules/botStatus.js (readFileSync on interval path) and web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx (min attribute vs parseNumberInput floor)

Important Files Changed

Filename Overview
src/modules/botStatus.js Core feature: new rotation config format, template variables ({commandCount}, {uptime}, {version}), and backward-compatible legacy path. Blocking readFileSync in the interval callback is a minor performance concern on first tick.
src/api/routes/guilds.js Adds bot-owner gate for global botStatus writes via OAuth auth. API-secret auth is intentionally treated as operator-level and bypasses the gate, which aligns with the rest of the middleware's design.
src/api/utils/configValidation.js Adds full botStatus schema including nested rotation.messages object array; refactors array-item validation to be recursive and schema-driven. Looks correct.
src/index.js Wires startBotStatus/stopBotStatus into the startup/shutdown lifecycle. The call to startBotStatus immediately after client.login() means the initial applyPresence is a no-op until READY fires (already flagged in a prior thread).
web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx Adds Bot Presence card to the dashboard. The min={0.5} HTML attribute disagrees with the parseNumberInput(…, 1) floor, causing silent clamping of values in the 0.5–1 range.
web/src/types/config.ts Adds BotStatusConfig, BotStatusRotationConfig, BotStatusRotationMessage interfaces; all fields are optional to match partial-config usage. Looks correct and matches real data shapes.
tests/modules/botStatus.test.js Expands coverage for interpolation variables, getRotationMessages, resolveRotationIntervalMs, new rotation config shape, and warn fallbacks. Comprehensive happy-path and error-path cases.

Sequence Diagram

sequenceDiagram
    participant Client as Discord Client
    participant Index as index.js startup
    participant BS as botStatus.js
    participant Discord as Discord API
    participant ConfigListener as config-listeners.js
    participant DashAPI as guilds PATCH /config

    Index->>Client: await client.login(token)
    Index->>BS: startBotStatus(client)
    BS->>BS: getConfig().botStatus
    BS->>BS: getRotationMessages(cfg)
    BS->>BS: applyPresence(client) — no-op if client.user null
    BS->>BS: setInterval(rotate, intervalMs) [if rotation enabled]

    Note over Client,Discord: READY event fires (async)
    BS-->>Discord: setPresence() on first interval tick

    loop Every intervalMs
        BS->>BS: rotate() → currentActivityIndex += 1
        BS->>BS: applyPresence(client)
        BS->>BS: interpolateActivity(text, client)
        BS->>Discord: client.user.setPresence({status, activities})
    end

    DashAPI->>DashAPI: validateConfigPatch (botStatus → global scope)
    DashAPI->>DashAPI: isGlobalBotStatusWrite check
    DashAPI->>DashAPI: setConfigValue(path, value, undefined)
    ConfigListener->>BS: onConfigChange → reloadBotStatus()
    BS->>BS: stopBotStatus() → clearInterval
    BS->>BS: startBotStatus(client) with new config
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/modules/botStatus.js
Line: 101-109

Comment:
**Synchronous `readFileSync` in timer callback**

`getPackageVersion()` is called from `interpolateActivity`, which is called from `applyPresence`, which fires on every `setInterval` tick. On the very first tick, `readFileSync` blocks the event loop while reading `package.json`. Even though the result is cached, blocking I/O inside a recurring timer is a pattern to avoid.

Since the version never changes at runtime, consider reading it eagerly at module-load time:

```js
// At module top-level (runs once at import time, before any timer fires)
let _packageVersion;
try {
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
  _packageVersion = typeof pkg?.version === 'string' ? pkg.version : 'unknown';
} catch {
  _packageVersion = 'unknown';
}
```

This trades a tiny startup cost for guaranteed non-blocking behaviour during rotation ticks.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
Line: 211-214

Comment:
**`min` attribute contradicts `parseNumberInput` floor**

The `<Input min={0.5} ...>` tells users that 0.5 minutes is the lowest valid value, but `parseNumberInput(event.target.value, 1)` (where the second argument is the `min` floor — see `config-editor-utils.ts`) silently clamps anything below 1 minute up to 1. A user who types `0.5` will see the value snap to `1` with no feedback, even though the HTML hint said `0.5` was fine.

Either lower the parse floor to match the HTML minimum:
```suggestion
                const num = parseNumberInput(event.target.value, 0.5);
```
or raise the HTML `min` to match the code:
```suggestion
                min={1}
```
The backend already enforces a hard floor of `MIN_PRESENCE_INTERVAL_MS` (20 s ≈ 0.33 min), so `0.5` is safe to allow through.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix: biome auto-fix ..."

Comment thread src/modules/botStatus.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/api/utils/configValidation.js`:
- Around line 196-201: The rotation.messages[] JSON schema currently only
requires the presence of a text key; tighten it by making the message item
schema validate shapes: in the schema for rotation.messages.items (the object
validated today), change required/items to explicitly define properties: set
properties.text: { type: 'string', minLength: 1 }, optionally add
properties.type: { type: 'string', enum: ['text','markdown','html'] } (or your
allowed types), set additionalProperties: false, and keep required: ['text'];
this ensures non-string text or unexpected keys are rejected by the validator.

In `@src/index.js`:
- Around line 487-488: The call to startBotStatus(client) runs before the bot is
logged in so applyPresence(client) returns early and the initial presence is
delayed; move the startBotStatus(client) invocation to after the
client.login(token) (or explicitly call applyPresence(client) immediately after
client.login) so the initial presence is applied right away; update any code in
src/index.js where startBotStatus or applyPresence is invoked to run post-login
(referencing startBotStatus, applyPresence, and client.login(token)).

In `@src/modules/botStatus.js`:
- Around line 235-242: resolveRotationIntervalMs currently returns whatever the
config supplies (cfg.rotation.intervalMinutes or cfg.rotateIntervalMs) without
enforcing the Discord-safe minimum; update it to clamp the computed interval to
a minimum of 4000 ms (20s/5 updates) before returning. In practice compute the
interval from cfg.rotation.intervalMinutes
(Math.round(cfg.rotation.intervalMinutes * 60000)) or take cfg.rotateIntervalMs,
then apply Math.max(computedMs, 4000) and return that clamped value so presence
updates cannot exceed Discord rate limits; keep using the existing function name
resolveRotationIntervalMs and the same config keys (cfg.rotation.intervalMinutes
and cfg.rotateIntervalMs).

In `@tests/modules/botStatus.test.js`:
- Around line 117-121: The test for interpolateActivity is too weak; update the
spec so it asserts the placeholder is actually removed and that the string
begins with the 'v' prefix. In the 'replaces {version} from package.json' test
(using makeClient() and interpolateActivity('v{version}', client)), add an
assertion that result does not contain the literal '{version}' (e.g.,
expect(result).not.toContain('{version}')) and keep/strengthen the existing
check that it starts with 'v' and has content after it (e.g.,
expect(result).toMatch(/^v.+/)).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6490c900-2402-42cb-8c4d-8990bf351ace

📥 Commits

Reviewing files that changed from the base of the PR and between 72ee8f0 and 965a5c6.

📒 Files selected for processing (14)
  • config.json
  • src/api/utils/configValidation.js
  • src/config-listeners.js
  • src/index.js
  • src/modules/botStatus.js
  • tests/api/utils/configAllowlist.test.js
  • tests/api/utils/configValidation.test.js
  • tests/api/utils/validateConfigPatch.test.js
  • tests/config-listeners.test.js
  • tests/modules/botStatus.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/types/config.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (10)
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • tests/api/utils/configAllowlist.test.js
  • src/index.js
  • tests/api/utils/validateConfigPatch.test.js
  • tests/modules/botStatus.test.js
  • src/config-listeners.js
  • tests/config-listeners.test.js
  • tests/api/utils/configValidation.test.js
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • tests/api/utils/configAllowlist.test.js
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/types/config.ts
  • src/index.js
  • tests/api/utils/validateConfigPatch.test.js
  • tests/modules/botStatus.test.js
  • src/config-listeners.js
  • tests/config-listeners.test.js
  • tests/api/utils/configValidation.test.js
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • src/api/utils/configValidation.js
  • web/src/components/dashboard/config-editor.tsx
  • src/modules/botStatus.js
tests/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.test.js: Write bot tests using Vitest 4 with the node environment, matching the src/ structure in the tests/ directory
Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Files:

  • tests/api/utils/configAllowlist.test.js
  • tests/api/utils/validateConfigPatch.test.js
  • tests/modules/botStatus.test.js
  • tests/config-listeners.test.js
  • tests/api/utils/configValidation.test.js
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • tests/api/utils/configAllowlist.test.js
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/types/config.ts
  • src/index.js
  • tests/api/utils/validateConfigPatch.test.js
  • tests/modules/botStatus.test.js
  • src/config-listeners.js
  • tests/config-listeners.test.js
  • tests/api/utils/configValidation.test.js
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • src/api/utils/configValidation.js
  • web/src/components/dashboard/config-editor.tsx
  • src/modules/botStatus.js
**/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use double quotes in JSON files (standard JSON format); this exception applies only to JSON files, not JavaScript/TypeScript

Files:

  • config.json
config.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

All community features in config.json should be gated behind a config.<feature>.enabled flag; moderation commands are always available regardless of config settings

Files:

  • config.json
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/types/config.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
src/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.js: Never use console.* methods; use the Winston logger instead via import logger from '../logger.js' (adjust path as needed), then call logger.info(), logger.warn(), logger.error(), or logger.debug()
Always use safeReply(), safeSend(), or safeEditReply() instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Files:

  • src/index.js
  • src/config-listeners.js
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}

📄 CodeRabbit inference engine (AGENTS.md)

Use safeReply(), safeSend(), or safeEditReply() for Discord messages — never send unsafe messages directly

Files:

  • src/index.js
  • src/modules/botStatus.js
src/modules/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create feature modules in src/modules/ and add corresponding config sections to config.json

Files:

  • src/modules/botStatus.js
🧠 Learnings (5)
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to src/api/utils/configAllowlist.js : When adding a new config section, add it to `SAFE_CONFIG_KEYS` to enable saves via the API/dashboard, and to `READABLE_CONFIG_KEYS` for API read access

Applied to files:

  • tests/api/utils/configAllowlist.test.js
  • web/src/components/dashboard/config-workspace/types.ts
  • tests/api/utils/validateConfigPatch.test.js
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Add config sections to `SAFE_CONFIG_KEYS` in `src/api/utils/configAllowlist.js` when adding new writable config options

Applied to files:

  • tests/api/utils/configAllowlist.test.js
  • tests/api/utils/validateConfigPatch.test.js
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to config.json : All community features in `config.json` should be gated behind a `config.<feature>.enabled` flag; moderation commands are always available regardless of config settings

Applied to files:

  • config.json
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to src/**/{startup,command-register,reload}*.{js,ts} : Remove process.env.GUILD_ID runtime reads from bot startup and reload command registration

Applied to files:

  • src/index.js
🪛 GitHub Check: SonarCloud Code Analysis
tests/api/utils/configValidation.test.js

[warning] 141-141: Unnecessary escape character: ".

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbO693q8aLTr6G9-&open=AZzuFbO693q8aLTr6G9-&pullRequest=304


[warning] 141-141: Unnecessary escape character: ".

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbO693q8aLTr6G9_&open=AZzuFbO693q8aLTr6G9_&pullRequest=304

web/src/components/dashboard/config-editor.tsx

[warning] 961-961: The empty object is useless.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbLy93q8aLTr6G98&open=AZzuFbLy93q8aLTr6G98&pullRequest=304


[warning] 962-962: The empty object is useless.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbLy93q8aLTr6G99&open=AZzuFbLy93q8aLTr6G99&pullRequest=304

src/modules/botStatus.js

[warning] 125-125: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-B&open=AZzuFbPN93q8aLTr6G-B&pullRequest=304


[warning] 148-148: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-E&open=AZzuFbPN93q8aLTr6G-E&pullRequest=304


[warning] 124-124: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-A&open=AZzuFbPN93q8aLTr6G-A&pullRequest=304


[warning] 127-127: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-D&open=AZzuFbPN93q8aLTr6G-D&pullRequest=304


[warning] 126-126: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-C&open=AZzuFbPN93q8aLTr6G-C&pullRequest=304

🔇 Additional comments (13)
config.json (1)

153-174: botStatus default config is well-structured and feature-gated.

The added section has clear enable flags and sane starter rotation defaults.

src/modules/botStatus.js (1)

195-217: Backward-compatible message resolution (new + legacy) is implemented cleanly.

The fallback flow from rotation.messages to legacy activities is solid and resilient.

src/index.js (1)

296-296: Good shutdown lifecycle wiring.

Stopping bot status during graceful shutdown is the right cleanup point.

tests/api/utils/configAllowlist.test.js (1)

23-23: Allowlist coverage extension is correct.

Good addition of botStatus assertions for both writable and readable config surfaces.

Also applies to: 39-39

src/config-listeners.js (1)

116-119: Rotation-path listener coverage is complete.

Adding dedicated listeners for the new botStatus.rotation.* keys correctly wires hot-reload behavior.

web/src/components/dashboard/config-workspace/types.ts (1)

29-30: ConfigFeatureId update is aligned with dashboard feature mapping.

The new 'bot-status' union member cleanly supports the added workspace feature.

tests/api/utils/validateConfigPatch.test.js (1)

15-15: Test fixture allowlist sync looks good.

Including botStatus in the safe key set matches the new writable config surface.

tests/config-listeners.test.js (1)

95-103: Rotation listener coverage update looks correct.

These assertions properly extend the registration checks for the new botStatus.rotation.* paths and keep the total listener count expectation in sync.

web/src/components/dashboard/config-workspace/config-categories.ts (1)

38-39: Bot Presence wiring across category metadata is coherent.

sectionKeys, featureIds, FEATURE_LABELS, and CONFIG_SEARCH_ITEMS are all aligned for the new bot-status feature, so navigation and search indexing should resolve correctly.

Also applies to: 70-71, 353-361

tests/api/utils/configValidation.test.js (1)

107-115: Good schema-test expansion for botStatus.

You added both top-level schema presence checks and targeted validation cases (valid paths + invalid enum/shape), which strengthens regression protection for the new config section.

Also applies to: 120-143

tests/modules/botStatus.test.js (1)

109-115: Rotation test coverage additions are solid.

The new tests cover placeholder interpolation, interval resolution, message-source selection, and runtime rotation behavior for the new rotation config shape.

Also applies to: 212-232, 405-425

web/src/components/dashboard/config-editor.tsx (1)

119-120: Bot Presence editor integration is consistent with the existing workspace architecture.

The new section key recognition, nested draft updaters, and SettingsFeatureCard implementation fit cleanly into the current category-driven editor flow.

Based on learnings: "Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)".

Also applies to: 944-968, 2218-2275

web/src/types/config.ts (1)

249-264: Type-layer updates for botStatus are well connected.

The new interfaces plus BotConfig/ConfigSection extensions provide the expected type plumbing for the feature across editor and workspace code.

Also applies to: 368-369, 394-395

Comment thread src/api/utils/configValidation.js
Comment thread src/index.js Outdated
Comment thread src/modules/botStatus.js
Comment thread tests/modules/botStatus.test.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/modules/botStatus.js`:
- Around line 146-151: In resolveActivityType, simplify the negated undefined
check by using a clearer membership or truthy test: replace the ternary
condition that uses ACTIVITY_TYPE_MAP[typeStr] !== undefined with either the
in-operator (typeStr in ACTIVITY_TYPE_MAP) or a nullish fallback
(ACTIVITY_TYPE_MAP[typeStr] ?? ActivityType.Playing) so the function returns
ACTIVITY_TYPE_MAP[typeStr] when present and ActivityType.Playing otherwise;
update the function resolveActivityType accordingly to reference
ACTIVITY_TYPE_MAP and ActivityType.Playing.
- Around line 121-127: The chained global regex replacements in the function
that builds the status text should use String.prototype.replaceAll for clearer
intent and to avoid regex overhead; update the code in the function (the block
returning text.replace(...).replace(...)) to call
text.replaceAll('{memberCount}', String(memberCount)).replaceAll('{guildCount}',
String(guildCount)).replaceAll('{botName}',
botName).replaceAll('{commandCount}',
String(commandCount)).replaceAll('{uptime}', uptime).replaceAll('{version}',
version) ensuring each placeholder is passed as a plain string to replaceAll.

In `@web/src/components/landing/Pricing.tsx`:
- Around line 81-83: The long className expression on the toggle label should be
refactored to respect the 100-char width and improve readability: replace the
negation (!isAnnual) with positive logic (isAnnual ? 'text-muted-foreground' :
'text-foreground') and either compute a small variable (e.g., const
toggleLabelClass = isAnnual ? 'text-muted-foreground' : 'text-foreground') and
use className={`text-sm font-medium transition-colors ${toggleLabelClass}`} in
the span, or break the template literal across multiple concatenated lines;
update the span's className usage (and the analogous one at the other
occurrence) accordingly.

In `@web/src/components/landing/Stats.tsx`:
- Around line 217-225: The fallback object assigned to s (the expression "const
s = stats ?? { ... }") is missing a trailing comma after the last property;
update the object literal used in the stats nullish-coalescing fallback (the
properties servers, members, commandsServed, activeConversations, uptime,
messagesProcessed, cachedAt) to include a trailing comma after cachedAt so it
conforms to the project's multi-line object comma style.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 39606bb3-6e0c-4e66-9864-ee5a5fe8c756

📥 Commits

Reviewing files that changed from the base of the PR and between 965a5c6 and e987db7.

📒 Files selected for processing (13)
  • src/index.js
  • src/modules/botStatus.js
  • src/modules/events/messageCreate.js
  • tests/api/routes/warnings.test.js
  • tests/api/utils/configValidation.test.js
  • tests/commands/voice.test.js
  • tests/utils/discordCache.test.js
  • web/src/app/page.tsx
  • web/src/components/landing/FeatureGrid.tsx
  • web/src/components/landing/Footer.tsx
  • web/src/components/landing/Hero.tsx
  • web/src/components/landing/Pricing.tsx
  • web/src/components/landing/Stats.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • tests/commands/voice.test.js
  • tests/api/utils/configValidation.test.js
  • tests/api/routes/warnings.test.js
  • src/modules/events/messageCreate.js
  • tests/utils/discordCache.test.js
  • src/index.js
  • src/modules/botStatus.js
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • tests/commands/voice.test.js
  • web/src/components/landing/FeatureGrid.tsx
  • web/src/components/landing/Footer.tsx
  • tests/api/utils/configValidation.test.js
  • web/src/components/landing/Stats.tsx
  • tests/api/routes/warnings.test.js
  • src/modules/events/messageCreate.js
  • web/src/components/landing/Pricing.tsx
  • tests/utils/discordCache.test.js
  • web/src/app/page.tsx
  • web/src/components/landing/Hero.tsx
  • src/index.js
  • src/modules/botStatus.js
tests/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.test.js: Write bot tests using Vitest 4 with the node environment, matching the src/ structure in the tests/ directory
Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Files:

  • tests/commands/voice.test.js
  • tests/api/utils/configValidation.test.js
  • tests/api/routes/warnings.test.js
  • tests/utils/discordCache.test.js
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • tests/commands/voice.test.js
  • web/src/components/landing/FeatureGrid.tsx
  • web/src/components/landing/Footer.tsx
  • tests/api/utils/configValidation.test.js
  • web/src/components/landing/Stats.tsx
  • tests/api/routes/warnings.test.js
  • src/modules/events/messageCreate.js
  • web/src/components/landing/Pricing.tsx
  • tests/utils/discordCache.test.js
  • web/src/app/page.tsx
  • web/src/components/landing/Hero.tsx
  • src/index.js
  • src/modules/botStatus.js
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/landing/FeatureGrid.tsx
  • web/src/components/landing/Footer.tsx
  • web/src/components/landing/Stats.tsx
  • web/src/components/landing/Pricing.tsx
  • web/src/app/page.tsx
  • web/src/components/landing/Hero.tsx
src/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.js: Never use console.* methods; use the Winston logger instead via import logger from '../logger.js' (adjust path as needed), then call logger.info(), logger.warn(), logger.error(), or logger.debug()
Always use safeReply(), safeSend(), or safeEditReply() instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Files:

  • src/modules/events/messageCreate.js
  • src/index.js
  • src/modules/botStatus.js
src/modules/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create feature modules in src/modules/ and add corresponding config sections to config.json

Files:

  • src/modules/events/messageCreate.js
  • src/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}

📄 CodeRabbit inference engine (AGENTS.md)

Use safeReply(), safeSend(), or safeEditReply() for Discord messages — never send unsafe messages directly

Files:

  • src/modules/events/messageCreate.js
  • src/index.js
  • src/modules/botStatus.js
web/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

web/src/app/**/*.{ts,tsx}: Export metadata using createPageMetadata() from web/src/lib/page-titles.ts in SSR entry points for dashboard pages
Use DashboardTitleSync component and getDashboardDocumentTitle() for client-side navigation title updates in the dashboard

Files:

  • web/src/app/page.tsx
🧠 Learnings (8)
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/src/components/landing/FeatureGrid.tsx
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to src/**/*.js : Always use `safeReply()`, `safeSend()`, or `safeEditReply()` instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Applied to files:

  • src/modules/events/messageCreate.js
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to {src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js} : Use `safeReply()`, `safeSend()`, or `safeEditReply()` for Discord messages — never send unsafe messages directly

Applied to files:

  • src/modules/events/messageCreate.js
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to src/utils/cache.js : Use `src/utils/cache.js` for generic caching with Redis primary + in-memory fallback; use `src/utils/discordCache.js` for Discord API response caching (channels, roles, members); use `src/utils/reputationCache.js` for leaderboard and user reputation data; all caches auto-invalidate when config changes

Applied to files:

  • tests/utils/discordCache.test.js
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/src/app/page.tsx
  • web/src/components/landing/Hero.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard

Applied to files:

  • web/src/app/page.tsx
  • web/src/components/landing/Hero.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/src/components/landing/Hero.tsx
🪛 GitHub Actions: CI
web/src/components/landing/FeatureGrid.tsx

[error] 1-1: Command failed with exit code 1: pnpm --prefix web test (vitest run).

web/src/components/landing/Footer.tsx

[error] 1-1: Command failed with exit code 1: pnpm --prefix web test (vitest run).

web/src/components/landing/Stats.tsx

[error] 1-1: Command failed with exit code 1: pnpm --prefix web test (vitest run).

web/src/components/landing/Pricing.tsx

[error] 1-1: Command failed with exit code 1: pnpm --prefix web test (vitest run).

web/src/app/page.tsx

[error] 1-1: Command failed with exit code 1: pnpm --prefix web test (vitest run).

web/src/components/landing/Hero.tsx

[error] 1-1: Command failed with exit code 1: pnpm --prefix web test (vitest run).

🪛 GitHub Check: SonarCloud Code Analysis
web/src/components/landing/Stats.tsx

[warning] 113-121: Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuGMJvtB6A8wBpV-K4&open=AZzuGMJvtB6A8wBpV-K4&pullRequest=304

web/src/components/landing/Pricing.tsx

[warning] 82-82: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuGMJZtB6A8wBpV-K3&open=AZzuGMJZtB6A8wBpV-K3&pullRequest=304

src/modules/botStatus.js

[warning] 148-148: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-E&open=AZzuFbPN93q8aLTr6G-E&pullRequest=304


[warning] 126-126: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-C&open=AZzuFbPN93q8aLTr6G-C&pullRequest=304


[warning] 125-125: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-B&open=AZzuFbPN93q8aLTr6G-B&pullRequest=304


[warning] 127-127: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-D&open=AZzuFbPN93q8aLTr6G-D&pullRequest=304


[warning] 124-124: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-A&open=AZzuFbPN93q8aLTr6G-A&pullRequest=304

🔇 Additional comments (24)
tests/commands/voice.test.js (1)

266-268: Looks good — readable assertion formatting with unchanged behavior.

This multiline toContain assertion keeps the same expected CSV content while improving readability.

web/src/components/landing/Footer.tsx (1)

26-26: LGTM — Formatting-only changes.

These are purely cosmetic JSX formatting adjustments (inline text wrapping, multiline anchor tag attributes) with no behavioral impact. The code adheres to the coding guidelines.

Also applies to: 72-92, 104-105

tests/api/routes/warnings.test.js (1)

70-72: LGTM: Formatting improvement.

The multi-line format improves readability without changing the test behavior. The formatting follows all coding guidelines: single quotes, proper semicolon, and 2-space indentation.

web/src/app/page.tsx (2)

96-101: Formatting-only SVG path updates look good.

These edits only reflow attributes and preserve icon path data and rendering behavior.

Also applies to: 106-111


144-147: Anchor tag reformatting is safe and preserves behavior.

Docs/GitHub link targets and security attributes remain intact; this is a clean non-functional change.

Also applies to: 150-155

web/src/components/landing/Pricing.tsx (1)

159-161: CTA anchor formatting change looks good

This is a formatting-only change; behavior and security attributes (target="_blank" + rel="noopener noreferrer") remain correct.

Also applies to: 163-165

web/src/components/landing/Stats.tsx (2)

113-121: Make StatCard props read-only to prevent accidental mutation

Please type the component props as Readonly<StatCardProps> (or readonly fields in the interface) to align with immutable prop conventions and the static analysis warning.

💡 Suggested refactor
-function StatCard({
+function StatCard({
   icon,
   color,
   value,
   label,
   formatter = formatNumber,
   delay,
   isInView,
-}: StatCardProps) {
+}: Readonly<StatCardProps>) {

[ suggest_recommended_refactor ]
As per coding guidelines, "Maintain a maximum line width of 100 characters" and project lint/static checks should be respected consistently in changed TS/TSX segments.


88-89: Formatting-only updates look good

These changes are behavior-preserving and keep rendering logic intact.

Also applies to: 228-269, 308-311

web/src/components/landing/FeatureGrid.tsx (2)

4-5: LGTM: Type import separation follows TypeScript best practices.

Separating the type import from runtime imports improves code organization and is a common TypeScript convention.


64-65: LGTM: JSX formatting is clean and within line length limits.

The single-line formatting for the title and description elements is appropriate given their concise content, and both lines remain well within the 100-character limit.

src/modules/events/messageCreate.js (1)

21-21: LGTM!

Import reorganization is clean. The file correctly uses ESM syntax, the Winston logger, and safeReply for Discord messaging throughout.

tests/utils/discordCache.test.js (1)

266-270: LGTM!

The assertion refactor from multi-line await to inline expect(await ...) is functionally equivalent and slightly more concise.

tests/api/utils/configValidation.test.js (2)

107-117: LGTM!

Good expansion of the CONFIG_SCHEMA test to verify that all expected top-level sections including the new botStatus and auditLog are present.


120-143: LGTM!

Solid test coverage for the new botStatus schema validation:

  • Valid rotation settings acceptance
  • Invalid status value rejection
  • Missing required text field rejection

Consider adding edge case tests for intervalMinutes (e.g., zero or negative values) in a follow-up if the schema enforces minimum bounds.

web/src/components/landing/Hero.tsx (3)

48-51: LGTM!

Minor JSX formatting improvement for better readability.


132-132: LGTM!

Adding parentheses around the arrow function parameter (prev) is a consistent style improvement.


460-489: LGTM!

The Button/Link composition updates using asChild pattern for the dashboard and GitHub links follow proper Next.js and Radix UI patterns for accessible navigation.

src/modules/botStatus.js (4)

238-252: Clamp configured rotation interval to a Discord-safe minimum.

The resolveRotationIntervalMs() function accepts unclamped values that can trigger Discord presence update throttling. Users can configure very small intervals (e.g., intervalMinutes: 0.1 = 6 seconds) which would exceed Discord's documented rate limit of 5 presence updates per 20 seconds.

Proposed fix
 const DEFAULT_LEGACY_ROTATE_INTERVAL_MS = 30_000;
 const DEFAULT_ROTATE_INTERVAL_MINUTES = 5;
+const MIN_PRESENCE_INTERVAL_MS = 20_000;

 export function resolveRotationIntervalMs(cfg) {
   if (typeof cfg?.rotation?.intervalMinutes === 'number' && cfg.rotation.intervalMinutes > 0) {
-    return Math.round(cfg.rotation.intervalMinutes * 60_000);
+    return Math.max(Math.round(cfg.rotation.intervalMinutes * 60_000), MIN_PRESENCE_INTERVAL_MS);
   }

   if (typeof cfg?.rotateIntervalMs === 'number' && cfg.rotateIntervalMs > 0) {
-    return cfg.rotateIntervalMs;
+    return Math.max(cfg.rotateIntervalMs, MIN_PRESENCE_INTERVAL_MS);
   }

   if (cfg?.rotation) {
     return DEFAULT_ROTATE_INTERVAL_MINUTES * 60_000;
   }

   return DEFAULT_LEGACY_ROTATE_INTERVAL_MS;
 }

73-86: LGTM!

The formatUptime() function handles edge cases well (non-finite, negative values) and produces a clean, compact format.


198-220: LGTM!

Good design with getRotationMessages() providing graceful fallback from new format to legacy format to default, ensuring the bot always has at least one status message.


338-368: LGTM!

The startBotStatus() function correctly clears any existing interval before starting a new one, preventing interval leaks on hot-reload.

src/index.js (3)

486-488: Move startBotStatus(client) to after client.login(token) to ensure initial presence applies immediately.

Currently startBotStatus() is called before client.login(token) (line 499), but applyPresence() returns early when !client?.user. This delays the first visible status update until the next rotation interval tick (5+ minutes by default).

Proposed fix
   // Start triage module (per-channel message classification + response)
   await startTriage(client, config, healthMonitor);

-  // Start configurable bot presence rotation
-  startBotStatus(client);
-
   // Start tempban scheduler for automatic unbans (DB required)
   if (dbPool) {
     startTempbanScheduler(client);
     startWarningExpiryScheduler();
     startScheduler(client);
     startGithubFeed(client);
   }

   // Load commands and login
   await loadCommands();
   await client.login(token);
+
+  // Start configurable bot presence rotation after client is authenticated
+  startBotStatus(client);

46-46: LGTM!

Clean import of the new bot status module functions.


295-295: LGTM!

Proper integration of stopBotStatus() in the graceful shutdown sequence, placed appropriately before API server shutdown.

Comment thread src/modules/botStatus.js Outdated
Comment thread src/modules/botStatus.js
Comment thread web/src/components/landing/Pricing.tsx
Comment thread web/src/components/landing/Stats.tsx
Comment thread config.json
Comment thread src/api/utils/configValidation.js
Comment thread src/modules/botStatus.js Outdated
Comment thread src/modules/botStatus.js Outdated
Comment thread src/modules/botStatus.js Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable rotating Discord bot presence (“bot status”) with template interpolation, plus validation, default config, dashboard controls, and tests across bot + API + web.

Changes:

  • Implement botStatus rotation + interpolation variables (guild/member/command counts, uptime, version) and hook into startup/shutdown + config hot-reload.
  • Extend API config schema + allowlists and update default config.json with rotation configuration.
  • Add dashboard config surface for presence status + rotation enable/interval; update related web config types; expand test coverage.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
web/src/types/config.ts Adds botStatus types (rotation config + message shape) and exposes section key.
web/src/components/landing/Stats.tsx Formatting-only refactor (line breaks/object formatting).
web/src/components/landing/Pricing.tsx Formatting-only refactor (JSX wrapping).
web/src/components/landing/Hero.tsx Formatting-only refactor (JSX wrapping, minor spacing).
web/src/components/landing/Footer.tsx Formatting-only refactor (JSX wrapping/spacing).
web/src/components/landing/FeatureGrid.tsx Import ordering tweak + formatting-only JSX changes.
web/src/components/dashboard/config-workspace/types.ts Adds bot-status feature id.
web/src/components/dashboard/config-workspace/config-categories.ts Surfaces “Bot Presence” in categories/search metadata.
web/src/components/dashboard/config-editor.tsx Adds bot presence UI controls and draft update helpers.
web/src/app/page.tsx Formatting-only refactor (SVG path JSX wrapping).
tests/utils/discordCache.test.js Formatting-only assertion wrapping.
tests/modules/botStatus.test.js Adds tests for new interpolation vars + rotation helpers + new config shape rotation.
tests/config-listeners.test.js Asserts new botStatus rotation listener keys + updates listener count.
tests/commands/voice.test.js Formatting-only assertion wrapping.
tests/api/utils/validateConfigPatch.test.js Extends mocked SAFE_CONFIG_KEYS to include botStatus.
tests/api/utils/configValidation.test.js Adds schema presence + botStatus validation tests.
tests/api/utils/configAllowlist.test.js Adds allowlist assertions for botStatus.
tests/api/routes/warnings.test.js Formatting-only assertion wrapping.
src/modules/events/messageCreate.js Import ordering tweak only.
src/modules/botStatus.js Implements rotation messages normalization, interval resolution, template interpolation, and presence application logic.
src/index.js Starts/stops bot status module during startup/shutdown lifecycle.
src/config-listeners.js Registers hot-reload listeners for new botStatus.rotation.* paths.
src/api/utils/configValidation.js Adds botStatus schema (including rotation subtree).
config.json Adds default botStatus.rotation configuration.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread src/index.js Outdated
Comment thread src/modules/botStatus.js
Comment thread src/api/utils/configValidation.js
Comment thread web/src/components/dashboard/config-editor.tsx Outdated
Comment thread src/index.js Outdated
Copilot AI review requested due to automatic review settings March 19, 2026 03:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/modules/botStatus.js (1)

40-46: ⚠️ Potential issue | 🟠 Major

Streaming activity type cannot be properly configured without a stream URL.

Discord requires a url field for ActivityType.Streaming to display the streaming status. The current implementation supports Streaming in the activity type map but the message model only carries { type, text } and buildActivityPayload() never includes a url field. Configs selecting Streaming will not display correctly on Discord. Either add URL support to the message schema and payload builder, or remove Streaming from the supported activity types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/botStatus.js` around lines 40 - 46, The ACTIVITY_TYPE_MAP
currently includes Streaming but the message model and buildActivityPayload do
not supply a required stream URL; update either to fix it: best option is to
extend the message/activity schema (where messages carry { type, text }) to
include an optional url property, add validation in the message creation/parse
paths to require url when type === 'Streaming', and modify buildActivityPayload
to include the url field when mapping ActivityType.Streaming; alternatively,
remove 'Streaming' from ACTIVITY_TYPE_MAP to prevent selecting it. Make these
changes around the ACTIVITY_TYPE_MAP constant and the buildActivityPayload
function and any message schema/validation logic so Discord streaming activity
is sent with a url or never offered.
♻️ Duplicate comments (1)
src/modules/botStatus.js (1)

52-55: ⚠️ Potential issue | 🟠 Major

Clamp configured intervals before starting rotation.

Issue #228 explicitly calls out ~20s presence spacing, but resolveRotationIntervalMs() returns any positive value unchanged. The new rotation.intervalMinutes path can therefore schedule 60ms/6s updates, which defeats that guardrail and pushes the unsafe value all the way into setInterval().

Suggested fix
 const DEFAULT_LEGACY_ROTATE_INTERVAL_MS = 30_000;
 const DEFAULT_ROTATE_INTERVAL_MINUTES = 5;
+const MIN_ROTATION_INTERVAL_MS = 20_000;
@@
 export function resolveRotationIntervalMs(cfg) {
   if (typeof cfg?.rotation?.intervalMinutes === 'number' && cfg.rotation.intervalMinutes > 0) {
-    return Math.round(cfg.rotation.intervalMinutes * 60_000);
+    return Math.max(
+      Math.round(cfg.rotation.intervalMinutes * 60_000),
+      MIN_ROTATION_INTERVAL_MS,
+    );
   }
 
   if (typeof cfg?.rotateIntervalMs === 'number' && cfg.rotateIntervalMs > 0) {
-    return cfg.rotateIntervalMs;
+    return Math.max(cfg.rotateIntervalMs, MIN_ROTATION_INTERVAL_MS);
   }

Also applies to: 317-324

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/botStatus.js` around lines 52 - 55, resolveRotationIntervalMs
currently returns any positive value allowing very small intervals to reach
setInterval; clamp the computed interval to a safe minimum (use
DEFAULT_LEGACY_ROTATE_INTERVAL_MS = 30_000 or the ~20s guardrail mentioned)
before returning or before passing into the rotation scheduler. Update
resolveRotationIntervalMs (and any call sites that accept
rotation.intervalMinutes) to enforce Math.max(resolvedMs,
DEFAULT_LEGACY_ROTATE_INTERVAL_MS) and ensure the value passed into setInterval
(or the rotation starter function) is the clamped value so extremely small
intervals are not scheduled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/api/utils/configValidation.js`:
- Line 205: The schema currently allows whitespace-only strings for the rotation
message (text: { type: 'string', minLength: 1 }), which diverges from
normalizeMessage() that trims and drops such entries; change the validation to
reject whitespace-only values by adding a non-whitespace pattern to the text
schema (e.g., include pattern: '\\S' or an equivalent regex) so that the schema
for text only accepts strings containing at least one non-whitespace character
and stays consistent with normalizeMessage() and the handling of entry.text.

---

Outside diff comments:
In `@src/modules/botStatus.js`:
- Around line 40-46: The ACTIVITY_TYPE_MAP currently includes Streaming but the
message model and buildActivityPayload do not supply a required stream URL;
update either to fix it: best option is to extend the message/activity schema
(where messages carry { type, text }) to include an optional url property, add
validation in the message creation/parse paths to require url when type ===
'Streaming', and modify buildActivityPayload to include the url field when
mapping ActivityType.Streaming; alternatively, remove 'Streaming' from
ACTIVITY_TYPE_MAP to prevent selecting it. Make these changes around the
ACTIVITY_TYPE_MAP constant and the buildActivityPayload function and any message
schema/validation logic so Discord streaming activity is sent with a url or
never offered.

---

Duplicate comments:
In `@src/modules/botStatus.js`:
- Around line 52-55: resolveRotationIntervalMs currently returns any positive
value allowing very small intervals to reach setInterval; clamp the computed
interval to a safe minimum (use DEFAULT_LEGACY_ROTATE_INTERVAL_MS = 30_000 or
the ~20s guardrail mentioned) before returning or before passing into the
rotation scheduler. Update resolveRotationIntervalMs (and any call sites that
accept rotation.intervalMinutes) to enforce Math.max(resolvedMs,
DEFAULT_LEGACY_ROTATE_INTERVAL_MS) and ensure the value passed into setInterval
(or the rotation starter function) is the clamped value so extremely small
intervals are not scheduled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0d71510c-d674-4528-8111-8471999de63b

📥 Commits

Reviewing files that changed from the base of the PR and between e987db7 and 5b3271d.

📒 Files selected for processing (4)
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (7)
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • src/api/utils/configValidation.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
  • src/modules/botStatus.js
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • src/api/utils/configValidation.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
  • src/modules/botStatus.js
src/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.js: Never use console.* methods; use the Winston logger instead via import logger from '../logger.js' (adjust path as needed), then call logger.info(), logger.warn(), logger.error(), or logger.debug()
Always use safeReply(), safeSend(), or safeEditReply() instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Files:

  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • src/api/utils/configValidation.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
  • src/modules/botStatus.js
tests/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.test.js: Write bot tests using Vitest 4 with the node environment, matching the src/ structure in the tests/ directory
Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Files:

  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
src/modules/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create feature modules in src/modules/ and add corresponding config sections to config.json

Files:

  • src/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}

📄 CodeRabbit inference engine (AGENTS.md)

Use safeReply(), safeSend(), or safeEditReply() for Discord messages — never send unsafe messages directly

Files:

  • src/modules/botStatus.js
🧠 Learnings (1)
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to tests/**/*.test.js : Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Applied to files:

  • tests/api/utils/configValidation.test.js
🪛 GitHub Check: SonarCloud Code Analysis
src/modules/botStatus.js

[failure] 213-213: Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ0EJ5ACh8i__9kIwTIq&open=AZ0EJ5ACh8i__9kIwTIq&pullRequest=304


[warning] 158-158: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-B&open=AZzuFbPN93q8aLTr6G-B&pullRequest=304


[warning] 160-160: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-D&open=AZzuFbPN93q8aLTr6G-D&pullRequest=304


[warning] 228-228: Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ0EJ5ACh8i__9kIwTIr&open=AZ0EJ5ACh8i__9kIwTIr&pullRequest=304

Comment thread src/api/utils/configValidation.js Outdated
Comment thread src/api/routes/guilds.js Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable rotating Discord bot presence (“botStatus”) with template interpolation, wires it into startup/shutdown + config hot-reload, and exposes basic rotation controls in the web dashboard.

Changes:

  • Implement new botStatus.rotation config shape (with legacy compatibility), interpolation vars, and rotation scheduler lifecycle.
  • Route botStatus config writes to global scope, extend config schema/allowlists, and update defaults in config.json.
  • Add dashboard UI toggles + tests for interpolation, rotation timing/clamping, schema validation, and listener registration.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/src/types/config.ts Adds botStatus and rotation message types to the web config model.
web/src/components/landing/Stats.tsx Formatting-only refactor for readability.
web/src/components/landing/Pricing.tsx Formatting-only refactor for readability.
web/src/components/landing/Hero.tsx Formatting-only refactor for readability.
web/src/components/landing/Footer.tsx Formatting-only refactor for readability.
web/src/components/landing/FeatureGrid.tsx Import ordering + minor formatting.
web/src/components/dashboard/config-workspace/types.ts Adds bot-status feature id.
web/src/components/dashboard/config-workspace/config-categories.ts Exposes Bot Presence feature in Community Tools and search items.
web/src/components/dashboard/config-editor.tsx Adds Bot Presence controls (status + rotation enabled + interval).
web/src/app/page.tsx Formatting-only refactor for readability.
tests/utils/discordCache.test.js Formatting-only change to expectation layout.
tests/modules/botStatus.test.js Expands coverage for new interpolation vars, rotation message normalization, and interval clamping.
tests/index.test.js Ensures bot status lifecycle starts after login (mocked) and resets mocks.
tests/config-listeners.test.js Updates listener registration expectations for new botStatus rotation paths.
tests/commands/voice.test.js Formatting-only change to expectation layout.
tests/api/utils/validateConfigPatch.test.js Allows botStatus writes in the patch validator test allowlist.
tests/api/utils/configValidation.test.js Adds tests for nested array/object schema validation and botStatus schema paths.
tests/api/utils/configAllowlist.test.js Verifies botStatus is readable/writable via API allowlists.
tests/api/routes/warnings.test.js Formatting-only change to SQL expectation layout.
tests/api/routes/guilds.test.js Adds coverage that botStatus writes are routed to global config.
src/modules/events/messageCreate.js Import ordering tweak.
src/modules/botStatus.js Implements rotation messages, interpolation vars, interval clamping, and lifecycle helpers.
src/index.js Starts/stops bot status rotation on startup/shutdown.
src/config-listeners.js Hot-reloads bot status on new botStatus.rotation.* paths.
src/api/utils/configValidation.js Adds botStatus schema + improves array/object/string validation recursion.
src/api/routes/guilds.js Routes botStatus config patches to global scope and logs scope.
config.json Adds default botStatus.rotation configuration.
Comments suppressed due to low confidence (1)

src/api/routes/guilds.js:761

  • When writeScope is global, the webhook payload still sends guildId: req.params.id and doesn’t include the scope, which can mislead downstream consumers/audit logs into thinking this was a guild-scoped config change. Consider including scope in the webhook payload and/or setting guildId to null/global when the write is global.
    fireAndForgetWebhook('DASHBOARD_WEBHOOK_URL', {
      event: 'config.updated',
      guildId: req.params.id,
      section: topLevelKey,
      updatedKeys: [path],

Comment thread web/src/types/config.ts
Comment thread src/api/routes/guilds.js
Comment thread web/src/types/config.ts
Comment thread src/modules/botStatus.js
@BillChirico
Copy link
Copy Markdown
Collaborator

Can you also set the config in the database?

Copilot AI review requested due to automatic review settings March 19, 2026 19:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable rotating Discord bot presence (“botStatus”) with template variables, wires it into startup/shutdown + config hot-reload, updates API/dashboard config handling, and expands test coverage to validate the new behavior.

Changes:

  • Implement bot status rotation + template interpolation (guild/member/command counts, uptime, version) with legacy config compatibility.
  • Add schema/allowlist/listener updates so botStatus can be validated, saved via API/dashboard, and hot-reloaded.
  • Extend dashboard config UI/types for presence + rotation controls and add/adjust tests accordingly.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/src/types/config.ts Adds dashboard TS types for botStatus config section.
web/src/components/landing/Stats.tsx Formatting-only changes.
web/src/components/landing/Pricing.tsx Formatting-only changes.
web/src/components/landing/Hero.tsx Formatting-only changes.
web/src/components/landing/Footer.tsx Formatting-only changes.
web/src/components/landing/FeatureGrid.tsx Minor import ordering + formatting changes.
web/src/components/dashboard/types.ts Formatting-only change in validator.
web/src/components/dashboard/config-workspace/types.ts Adds bot-status feature id.
web/src/components/dashboard/config-workspace/config-categories.ts Exposes botStatus in “Community Tools” category + search metadata.
web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx Adds Bot Presence settings card (status + rotation enable + interval).
web/src/components/dashboard/config-editor-utils.ts Adds botStatus to guild-config type guard allowlist.
web/src/app/page.tsx Formatting-only changes.
tests/utils/discordCache.test.js Formatting-only change.
tests/modules/events-extra.test.js Formatting-only changes.
tests/modules/botStatus.test.js Adds tests for new interpolation vars + rotation normalization/interval clamping.
tests/index.test.js Ensures bot status starts after login; adds botStatus module mocks.
tests/config-listeners.test.js Updates listener assertions/count to include new botStatus.rotation keys.
tests/commands/voice.test.js Formatting-only change.
tests/api/utils/validateConfigPatch.test.js Updates SAFE_CONFIG_KEYS test fixture to include botStatus.
tests/api/utils/configValidation.test.js Adds coverage for nested array-of-object validation and botStatus schema validation.
tests/api/utils/configAllowlist.test.js Asserts botStatus is readable/writable via API allowlists.
tests/api/routes/warnings.test.js Formatting-only change.
tests/api/routes/guilds.test.js Tests that botStatus writes are routed to global config scope.
src/modules/handlers/ticketHandler.js Formatting-only change.
src/modules/events/messageCreate.js Formatting-only import ordering change.
src/modules/events/interactionCreate.js Formatting-only import/export formatting change.
src/modules/events.js Minor import ordering change.
src/modules/botStatus.js Core implementation: rotation messages normalization, interval resolution, new template vars, presence application + lifecycle.
src/index.js Starts/stops botStatus in startup/shutdown lifecycle.
src/config-listeners.js Adds hot-reload listeners for botStatus.rotation.* keys.
src/api/utils/configValidation.js Adds botStatus schema + improves array/object validation recursion + string minLength support.
src/api/routes/guilds.js Routes botStatus writes through guild config PATCH endpoint to global scope.
config.json Adds default botStatus.rotation configuration.
biome.json Bumps Biome schema URL version.

Comment thread web/src/types/config.ts
Comment thread src/api/utils/configValidation.js Outdated
Comment thread src/api/routes/guilds.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/components/dashboard/config-editor-utils.ts (1)

52-86: 🧹 Nitpick | 🔵 Trivial

Update the docstring to include botStatus.

The function docstring at lines 55-57 lists the known sections but doesn't include the newly added botStatus. This creates a documentation inconsistency.

📝 Proposed docstring update
 /**
  * Type guard that checks whether a value is a guild configuration object returned by the API.
  *
  * `@returns` `true` if the value is an object containing at least one known top-level section
- *   (`ai`, `welcome`, `spam`, `moderation`, `triage`, `starboard`, `permissions`, `memory`) and each present section is a plain object
+ *   (`ai`, `welcome`, `spam`, `moderation`, `triage`, `starboard`, `permissions`, `memory`, `botStatus`, etc.) and each present section is a plain object
  *   (not an array or null). Returns `false` otherwise.
  */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/config-editor-utils.ts` around lines 52 - 86,
The docstring for isGuildConfig is out of date because it omits the newly added
'botStatus' section; update the comment block above the isGuildConfig function
to include 'botStatus' among the listed known top-level sections (matching the
entries in the knownSections array) and ensure the description still states that
each present section must be a plain object (not array or null).
♻️ Duplicate comments (1)
src/modules/botStatus.js (1)

155-161: 🧹 Nitpick | 🔵 Trivial

Consider using String#replaceAll() for clarity.

Static analysis recommends replaceAll() over replace() with global regex for better readability and to avoid regex overhead. This was also flagged in a previous review.

Suggested refactor
   return text
-    .replace(/\{memberCount\}/g, String(memberCount))
-    .replace(/\{guildCount\}/g, String(guildCount))
-    .replace(/\{botName\}/g, botName)
-    .replace(/\{commandCount\}/g, String(commandCount))
-    .replace(/\{uptime\}/g, uptime)
-    .replace(/\{version\}/g, version);
+    .replaceAll('{memberCount}', String(memberCount))
+    .replaceAll('{guildCount}', String(guildCount))
+    .replaceAll('{botName}', botName)
+    .replaceAll('{commandCount}', String(commandCount))
+    .replaceAll('{uptime}', uptime)
+    .replaceAll('{version}', version);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/botStatus.js` around lines 155 - 161, The chained use of
text.replace with global regexes should be simplified to use
String.prototype.replaceAll for clarity and performance; update the code that
transforms the local variable text (the return expression that currently calls
.replace(/\{memberCount\}/g ... .replace(/\{version\}/g, version)) to call
text.replaceAll("{memberCount}", String(memberCount)) and likewise for
"{guildCount}", "{botName}", "{commandCount}", "{uptime}", and "{version}";
ensure each placeholder string and corresponding replacement (e.g.,
String(commandCount)) are used and that the environment supports replaceAll.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/modules/botStatus.js`:
- Around line 214-254: The function normalizeMessage exceeds cognitive
complexity and contains a nested ternary used when logging invalid entry types;
extract that logic into a small helper (e.g., determineEntryTypeLabel(entry) or
getEntryTypeLabel) that returns 'array', 'null', or typeof entry, and replace
the ternary in the warn call with a call to this helper; keep all existing early
returns and validation branches in normalizeMessage but move the entry-type
string determination and any related formatting into the new helper to reduce
complexity and make the warn invocation in normalizeMessage simpler.

In `@web/src/app/page.tsx`:
- Around line 96-101: The JSX in page.tsx uses double-quoted string literals for
SVG attributes (e.g., the <path> element with attributes strokeLinecap,
strokeLinejoin, and d) — change those attribute values from double quotes to
single quotes to match the repository TSX quote rule; update the same style for
the other affected JSX segments referenced (lines around the second <path> at
106-111 and the SVG group at 144-155) so all string literals in JS/TS/TSX use
single quotes consistently.
- Line 145: The mobile nav anchor in web/src/app/page.tsx currently uses
href="https://docs.volvox.dev" which is inconsistent with the desktop nav anchor
that uses href="https://docs.volvox.bot"; update the mobile nav link (the JSX
anchor element whose href is "https://docs.volvox.dev") to use
"https://docs.volvox.bot" so both desktop and mobile navs point to the same docs
URL.

In `@web/src/components/landing/Footer.tsx`:
- Around line 72-75: The anchor element in Footer.tsx uses double-quoted JSX
strings and a too-long className; update the href and className attributes in
the <a> element(s) (the link block around the docs link and any sibling links)
to use single quotes and reflow long className values to keep lines ≤100
characters (e.g., break the className into multiple concatenated strings or
multiple JSX props/lines) so they follow the repository's single-quote and
max-line-width conventions; ensure identical changes are applied to the other
affected anchor blocks in this component.

---

Outside diff comments:
In `@web/src/components/dashboard/config-editor-utils.ts`:
- Around line 52-86: The docstring for isGuildConfig is out of date because it
omits the newly added 'botStatus' section; update the comment block above the
isGuildConfig function to include 'botStatus' among the listed known top-level
sections (matching the entries in the knownSections array) and ensure the
description still states that each present section must be a plain object (not
array or null).

---

Duplicate comments:
In `@src/modules/botStatus.js`:
- Around line 155-161: The chained use of text.replace with global regexes
should be simplified to use String.prototype.replaceAll for clarity and
performance; update the code that transforms the local variable text (the return
expression that currently calls .replace(/\{memberCount\}/g ...
.replace(/\{version\}/g, version)) to call text.replaceAll("{memberCount}",
String(memberCount)) and likewise for "{guildCount}", "{botName}",
"{commandCount}", "{uptime}", and "{version}"; ensure each placeholder string
and corresponding replacement (e.g., String(commandCount)) are used and that the
environment supports replaceAll.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0b8446b6-460e-42f4-8fdc-e3655fdd1782

📥 Commits

Reviewing files that changed from the base of the PR and between 5b3271d and f8df612.

📒 Files selected for processing (18)
  • biome.json
  • src/api/routes/guilds.js
  • src/index.js
  • src/modules/botStatus.js
  • src/modules/events.js
  • src/modules/events/interactionCreate.js
  • src/modules/handlers/ticketHandler.js
  • tests/api/routes/guilds.test.js
  • tests/api/utils/configValidation.test.js
  • tests/index.test.js
  • tests/modules/botStatus.test.js
  • tests/modules/events-extra.test.js
  • web/src/app/page.tsx
  • web/src/components/dashboard/config-editor-utils.ts
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/types.ts
  • web/src/components/landing/Footer.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (11)
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • src/modules/events/interactionCreate.js
  • src/modules/events.js
  • src/modules/handlers/ticketHandler.js
  • tests/modules/events-extra.test.js
  • src/index.js
  • tests/index.test.js
  • tests/api/routes/guilds.test.js
  • src/api/routes/guilds.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
  • src/modules/botStatus.js
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • src/modules/events/interactionCreate.js
  • web/src/components/dashboard/types.ts
  • src/modules/events.js
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/app/page.tsx
  • src/modules/handlers/ticketHandler.js
  • web/src/components/dashboard/config-editor-utils.ts
  • tests/modules/events-extra.test.js
  • src/index.js
  • tests/index.test.js
  • tests/api/routes/guilds.test.js
  • web/src/components/landing/Footer.tsx
  • src/api/routes/guilds.js
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
  • src/modules/botStatus.js
src/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.js: Never use console.* methods; use the Winston logger instead via import logger from '../logger.js' (adjust path as needed), then call logger.info(), logger.warn(), logger.error(), or logger.debug()
Always use safeReply(), safeSend(), or safeEditReply() instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Files:

  • src/modules/events/interactionCreate.js
  • src/modules/events.js
  • src/modules/handlers/ticketHandler.js
  • src/index.js
  • src/api/routes/guilds.js
  • src/modules/botStatus.js
src/modules/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create feature modules in src/modules/ and add corresponding config sections to config.json

Files:

  • src/modules/events/interactionCreate.js
  • src/modules/events.js
  • src/modules/handlers/ticketHandler.js
  • src/modules/botStatus.js
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • src/modules/events/interactionCreate.js
  • web/src/components/dashboard/types.ts
  • src/modules/events.js
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/app/page.tsx
  • src/modules/handlers/ticketHandler.js
  • web/src/components/dashboard/config-editor-utils.ts
  • tests/modules/events-extra.test.js
  • src/index.js
  • tests/index.test.js
  • tests/api/routes/guilds.test.js
  • web/src/components/landing/Footer.tsx
  • src/api/routes/guilds.js
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
  • src/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}

📄 CodeRabbit inference engine (AGENTS.md)

Use safeReply(), safeSend(), or safeEditReply() for Discord messages — never send unsafe messages directly

Files:

  • src/modules/events/interactionCreate.js
  • src/modules/events.js
  • src/modules/handlers/ticketHandler.js
  • src/index.js
  • src/modules/botStatus.js
**/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use double quotes in JSON files (standard JSON format); this exception applies only to JSON files, not JavaScript/TypeScript

Files:

  • biome.json
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/dashboard/types.ts
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/app/page.tsx
  • web/src/components/dashboard/config-editor-utils.ts
  • web/src/components/landing/Footer.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
web/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

web/src/app/**/*.{ts,tsx}: Export metadata using createPageMetadata() from web/src/lib/page-titles.ts in SSR entry points for dashboard pages
Use DashboardTitleSync component and getDashboardDocumentTitle() for client-side navigation title updates in the dashboard

Files:

  • web/src/app/page.tsx
tests/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.test.js: Write bot tests using Vitest 4 with the node environment, matching the src/ structure in the tests/ directory
Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Files:

  • tests/modules/events-extra.test.js
  • tests/index.test.js
  • tests/api/routes/guilds.test.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/botStatus.test.js
src/api/routes/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create API route handlers in src/api/routes/ and mount them in src/api/server.js; add auth middleware if the endpoint requires authentication

Files:

  • src/api/routes/guilds.js
🧠 Learnings (20)
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.js : Use ESM-only syntax: `import`/`export`, never `require()`/`module.exports`

Applied to files:

  • biome.json
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to src/api/utils/configAllowlist.js : When adding a new config section, add it to `SAFE_CONFIG_KEYS` to enable saves via the API/dashboard, and to `READABLE_CONFIG_KEYS` for API read access

Applied to files:

  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-editor-utils.ts
  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-11T05:32:46.325Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T05:32:46.325Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-12T02:03:52.709Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.709Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to src/api/ws/logStream.js : Implement backward-compatible WebSocket ticket parsing to accept both legacy 3-part and new 4-part tickets for maintaining existing log stream behavior

Applied to files:

  • src/modules/handlers/ticketHandler.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/tests/**/*.test.{ts,tsx} : Write web dashboard tests using Vitest 4 with the `jsdom` environment and React Testing Library, matching the `web/src/` structure

Applied to files:

  • tests/modules/events-extra.test.js
  • tests/index.test.js
📚 Learning: 2026-03-11T17:18:17.626Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T17:18:17.626Z
Learning: Applies to src/**/{startup,command-register,reload}*.{js,ts} : Remove process.env.GUILD_ID runtime reads from bot startup and reload command registration

Applied to files:

  • src/index.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to tests/**/*.test.js : Write bot tests using Vitest 4 with the `node` environment, matching the `src/` structure in the `tests/` directory

Applied to files:

  • tests/index.test.js
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to src/api/routes/*.js : Add adaptDeleteGuildIdParam in API route handlers to enforce guild moderation checks on DELETE requests without losing record id

Applied to files:

  • tests/api/routes/guilds.test.js
  • src/api/routes/guilds.js
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to src/api/ws/**/*.js : Enforce tenant scoping on WebSocket handlers: reject mismatched guildId in filters and require entry.guild_id === ws.guildId for broadcast matching

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:52.709Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.709Z
Learning: Use `getConfig(guildId)` to retrieve merged global and guild-specific config

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-10T23:29:51.063Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:29:51.063Z
Learning: Applies to src/api/routes/*.js : Apply requireGuildModerator authorization check to DELETE endpoints that modify guild-scoped resources

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to web/src/app/api/**/route.ts : Include guildId in signed WebSocket ticket payload when issuing tickets from dashboard endpoints

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to src/commands/**/*.js : Guild owners should be explicitly exempted from role hierarchy restrictions in command handlers to match Discord permission model expectations

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to config.json : All community features in `config.json` should be gated behind a `config.<feature>.enabled` flag; moderation commands are always available regardless of config settings

Applied to files:

  • src/api/routes/guilds.js
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to src/commands/reactionrole.js : Enforce invoker role hierarchy check in /reactionrole add command to prevent non-owner users from configuring roles at or above their highest role

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to tests/**/*.test.js : Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Applied to files:

  • tests/api/utils/configValidation.test.js
🪛 GitHub Actions: CI
web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx

[error] 239-239: Biome lint error (lint/suspicious/noArrayIndexKey): Avoid using the index of an array as key property. Offending key: key={badge-${index}}.

🪛 GitHub Check: SonarCloud Code Analysis
src/modules/events/interactionCreate.js

[warning] 22-22: 'registerWelcomeOnboardingHandlers' is deprecated.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ0EO4HUXJTc6kWAe8RF&open=AZ0EO4HUXJTc6kWAe8RF&pullRequest=304

src/modules/botStatus.js

[failure] 214-214: Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ0EJ5ACh8i__9kIwTIq&open=AZ0EJ5ACh8i__9kIwTIq&pullRequest=304


[warning] 229-229: Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ0EJ5ACh8i__9kIwTIr&open=AZ0EJ5ACh8i__9kIwTIr&pullRequest=304


[warning] 161-161: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-D&open=AZzuFbPN93q8aLTr6G-D&pullRequest=304


[warning] 159-159: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-B&open=AZzuFbPN93q8aLTr6G-B&pullRequest=304


[warning] 160-160: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-C&open=AZzuFbPN93q8aLTr6G-C&pullRequest=304


[warning] 158-158: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuFbPN93q8aLTr6G-A&open=AZzuFbPN93q8aLTr6G-A&pullRequest=304

🔇 Additional comments (33)
biome.json (1)

2-2: Schema version bump looks good.

The $schema update is valid JSON and consistent with the current Biome config structure.

src/modules/handlers/ticketHandler.js (1)

41-41: LGTM! Clean refactoring to chained builder pattern.

The refactored ModalBuilder instantiation using method chaining is idiomatic, concise, and maintains identical behavior. The change follows all coding guidelines.

web/src/app/page.tsx (1)

96-101: Non-functional SVG path refactor looks safe.

These updates only reflow attributes and preserve icon path data; no behavior/UI change expected.

Also applies to: 106-111

web/src/components/landing/Footer.tsx (2)

26-26: Text update is clear and preserves CTA readability.
No issues in this change.


104-105: Copyright tagline wrap is fine and keeps rendered text intact.
No issues in this change.

web/src/components/dashboard/config-workspace/types.ts (1)

28-29: LGTM!

The 'bot-status' feature identifier is correctly added to the union type. The relevant code snippets confirm it's properly wired into CONFIG_CATEGORIES (under 'community-tools') and FEATURE_LABELS with the label 'Bot Presence'.

tests/modules/events-extra.test.js (2)

90-93: LGTM!

The multi-line import formatting improves readability and follows the 2-space indentation with trailing comma conventions.


120-127: LGTM!

The multi-line expect(await ...) pattern is cleaner for complex assertions and maintains consistent formatting throughout the test file.

src/modules/events/interactionCreate.js (2)

20-23: LGTM!

The multi-line import formatting is consistent with other imports in this file. The static analysis deprecation warning for registerWelcomeOnboardingHandlers is acknowledged by the comment on line 25 indicating these are backward-compatible re-exports.


30-33: LGTM!

Multi-line re-export formatting is consistent with the file's style.

web/src/components/dashboard/types.ts (1)

71-75: LGTM!

The multi-line formatting improves readability of the compound type check without changing the validation logic.

src/modules/events.js (1)

11-23: LGTM!

Import specifier reordering with no functional change.

tests/index.test.js (3)

65-68: LGTM!

The botStatus mock is properly structured with the expected startBotStatus and stopBotStatus functions, following the established pattern used by other module mocks in this file.


213-216: LGTM!

Mock registration for botStatus.js follows the established pattern.


383-391: LGTM!

This test correctly verifies the startup sequence by:

  1. Asserting startBotStatus is called with the client instance
  2. Using invocationCallOrder to verify client.login completes before startBotStatus is invoked

This aligns with the PR objective to wire bot status lifecycle into startup.

tests/api/routes/guilds.test.js (1)

470-497: LGTM!

This test correctly validates that botStatus config writes are routed to global scope:

  • The route passes undefined to setConfigValue when topLevelKey === 'botStatus', which this test verifies at line 495
  • The assertion at line 496 confirms at least one getConfig() call was made without arguments (global read)

This aligns with the context snippet showing writeScope = 'global' for botStatus updates in src/api/routes/guilds.js.

src/api/routes/guilds.js (1)

743-756: LGTM! Global scope routing for botStatus is correctly implemented.

The logic appropriately routes botStatus config writes to the global scope (via setConfigValue(..., undefined)) while maintaining guild-level authorization checks. The effective config read on line 747 correctly uses getConfig() for global scope, and the logging now includes the scope field for auditability.

The webhook still references guildId: req.params.id (line 759), which is appropriate for tracking which guild dashboard triggered the change.

src/index.js (3)

46-46: LGTM! Bot status module properly imported.


296-296: LGTM! Shutdown sequence correctly stops bot status rotation.

The stopBotStatus() call is appropriately placed early in the shutdown sequence alongside other background service stops.


499-501: LGTM! Bot status correctly starts after login.

The startBotStatus(client) call is now correctly placed after client.login(token) completes, ensuring client.user is available for presence updates. This addresses the timing issue from the previous review.

web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx (2)

133-220: LGTM! Bot Presence settings card follows established patterns.

The new bot-status feature card correctly:

  • Integrates with the feature visibility system via showFeature('bot-status')
  • Uses appropriate defaults matching backend schema (enabled=true, status='online', rotation.enabled=false, intervalMinutes=5)
  • Properly validates numeric input with parseNumberInput
  • Disables controls during save operations

Note: The rotation messages array configuration is not exposed in this UI. If end-users need to configure custom rotation messages (not just enable/disable rotation), that would require additional UI work.


290-291: LGTM! Formatting-only change.

The multi-line formatting of the disabled condition is a style improvement with no behavioral change.

tests/api/utils/configValidation.test.js (3)

39-63: LGTM! Comprehensive array item validation test.

This test properly exercises the schema validation for nested object arrays, covering:

  • Valid entries
  • Invalid enum values
  • MinLength violations
  • Unknown keys

These assertions align with the botStatus.rotation.messages schema requirements.


132-141: LGTM! Schema keys updated to include botStatus.


145-189: LGTM! Thorough botStatus schema validation coverage.

The test suite comprehensively validates:

  • All valid botStatus paths (enabled, status, rotation.*)
  • Invalid enum values for status and message type
  • Required field validation (text)
  • MinLength constraint on text
  • Unknown key rejection in message objects

This provides confidence that the schema validation correctly enforces the botStatus configuration structure.

tests/modules/botStatus.test.js (3)

109-123: LGTM! New template variable interpolation tests.

Good coverage of the new {commandCount}, {uptime}, and {version} placeholders. The {version} test correctly asserts both that the placeholder is removed and the result starts with 'v', addressing the previous review feedback.


214-304: LGTM! Comprehensive rotation helper tests.

The test suite thoroughly covers:

  • New rotation.messages format consumption
  • intervalMinutes to milliseconds conversion
  • Discord-safe minimum clamping (20,000ms)
  • Warning/fallback behavior for invalid activity types
  • Warning/fallback for invalid message types
  • Warning when all configured messages are unusable

The assertions correctly verify both the returned values and the warning log calls.


394-496: LGTM! Interval tests updated for Discord-safe minimum.

The test intervals correctly reflect the 20,000ms minimum that the implementation now enforces. The new test at lines 476-496 validates the rotation behavior with the new config shape.

src/modules/botStatus.js (5)

319-333: LGTM! Discord-safe minimum clamping implemented.

The resolveRotationIntervalMs function now correctly enforces MIN_PRESENCE_INTERVAL_MS (20,000ms) via Math.max() for both new (rotation.intervalMinutes) and legacy (rotateIntervalMs) config paths. This addresses the previous review feedback about Discord presence update rate limits.


76-89: LGTM! Clean uptime formatting utility.

The formatUptime function handles edge cases (non-finite, negative values) and produces a compact human-readable format.


262-301: LGTM! Robust message normalization with appropriate fallbacks.

The getRotationMessages function:

  • Prioritizes new rotation.messages format
  • Falls back to legacy activities array
  • Logs warnings when entries are unusable
  • Always returns at least one default message

This ensures the bot always has a valid presence to display.


369-399: LGTM! Presence application using normalized messages.

The updated applyPresence function correctly:

  • Resolves status and messages from config
  • Handles ActivityType.Custom specially via buildActivityPayload
  • Interpolates template variables before setting presence
  • Catches and logs errors gracefully

419-449: LGTM! Rotation lifecycle correctly manages intervals.

The startBotStatus function:

  • Clears any existing interval before starting (prevents leaks on reload)
  • Applies presence immediately
  • Only starts rotation when enabled and multiple messages exist
  • Uses Discord-safe interval from resolveRotationIntervalMs

Comment thread src/modules/botStatus.js
Comment thread web/src/app/page.tsx
Comment thread web/src/app/page.tsx Outdated
Comment thread web/src/components/landing/Footer.tsx
@BillChirico BillChirico force-pushed the mohsin/rotating-bot-status branch from f8df612 to 439f752 Compare March 19, 2026 21:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 2 comments.

Comment thread src/modules/botStatus.js Outdated
Comment thread src/api/routes/guilds.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/modules/botStatus.js (1)

40-46: ⚠️ Potential issue | 🟠 Major

Streaming still isn't renderable with the current payload shape.

The allowed type list exposes Streaming, but buildActivityPayload() has no way to attach the stream URL Discord expects, so these entries will fall back to a normal activity instead of a real streaming presence.

In discord.js v14, does ActivityType.Streaming require a `url` in the presence payload, and how is the activity rendered if the URL is omitted?

Also applies to: 370-375

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/botStatus.js` around lines 40 - 46, The activity map exposes
"Streaming" but buildActivityPayload currently never sets the presence.url
field, so Streaming activities render as normal activities; update
buildActivityPayload (the function that builds the presence/activity object) to
detect when activity.type === 'Streaming' (or equals ActivityType.Streaming via
ACTIVITY_TYPE_MAP) and include a validated url property (e.g., from
activity.streamUrl or similar input) on the activity/presence payload; ensure
the url is only added for Streaming and is omitted or null for other types and
validate its format to avoid sending an empty/invalid url.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/api/routes/guilds.js`:
- Around line 742-749: The code currently treats topLevelKey === 'botStatus' as
a global write but still authorizes against the selected guild; before using
writeScope, add an explicit bot-owner authorization check when topLevelKey ===
'botStatus' (e.g., call the existing bot-owner check/utility or verify
req.user.isBotOwner) and reject non-bot-owners with a 403, then proceed to call
setConfigValue(path, value, ...) and getConfig(...) only if the bot-owner check
passes; update the branching around topLevelKey/writeScope so botStatus patches
require bot-owner auth rather than guild admin authorization.

In `@src/modules/botStatus.js`:
- Around line 439-443: When bot status is disabled the code only cancels the
timer but never clears Discord presence, so call the Discord client presence
clear path inside stopBotStatus() (or when startBotStatus() returns early) to
remove the previously set activity; specifically, invoke the client's presence
reset (e.g., client.user.setPresence with an empty activities array and
appropriate status) from the stopBotStatus()/startBotStatus() logic so toggling
enabled:false immediately clears the displayed activity (also apply the same
change to the other disabled-path handling around
startBotStatus()/stopBotStatus()).

In `@web/src/app/page.tsx`:
- Around line 96-111: The JSX SVG attributes in the page.tsx menu SVG hunk use
single quotes (e.g., strokeLinecap, strokeLinejoin, strokeWidth, d, className,
fill, viewBox, stroke, title), which conflicts with Biome's JSX quote
normalization; update all JSX attribute quotes in the SVG elements inside
page.tsx (the open/close menu SVG blocks) to use double quotes consistently so
linting passes and Biome no longer rewrites them.

---

Duplicate comments:
In `@src/modules/botStatus.js`:
- Around line 40-46: The activity map exposes "Streaming" but
buildActivityPayload currently never sets the presence.url field, so Streaming
activities render as normal activities; update buildActivityPayload (the
function that builds the presence/activity object) to detect when activity.type
=== 'Streaming' (or equals ActivityType.Streaming via ACTIVITY_TYPE_MAP) and
include a validated url property (e.g., from activity.streamUrl or similar
input) on the activity/presence payload; ensure the url is only added for
Streaming and is omitted or null for other types and validate its format to
avoid sending an empty/invalid url.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a21f2f98-c71c-48b1-9dfe-7609b4e4a54b

📥 Commits

Reviewing files that changed from the base of the PR and between 439f752 and baef635.

📒 Files selected for processing (7)
  • src/api/routes/guilds.js
  • src/api/utils/configValidation.js
  • src/index.js
  • src/modules/botStatus.js
  • web/src/app/page.tsx
  • web/src/components/landing/Pricing.tsx
  • web/src/types/config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • web/src/types/config.ts
  • web/src/app/page.tsx
  • src/index.js
  • web/src/components/landing/Pricing.tsx
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
  • src/api/routes/guilds.js
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/types/config.ts
  • web/src/app/page.tsx
  • web/src/components/landing/Pricing.tsx
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • web/src/types/config.ts
  • web/src/app/page.tsx
  • src/index.js
  • web/src/components/landing/Pricing.tsx
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
  • src/api/routes/guilds.js
web/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

web/src/app/**/*.{ts,tsx}: Export metadata using createPageMetadata() from web/src/lib/page-titles.ts in SSR entry points for dashboard pages
Use DashboardTitleSync component and getDashboardDocumentTitle() for client-side navigation title updates in the dashboard

Files:

  • web/src/app/page.tsx
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • src/index.js
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
  • src/api/routes/guilds.js
src/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.js: Never use console.* methods; use the Winston logger instead via import logger from '../logger.js' (adjust path as needed), then call logger.info(), logger.warn(), logger.error(), or logger.debug()
Always use safeReply(), safeSend(), or safeEditReply() instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Files:

  • src/index.js
  • src/api/utils/configValidation.js
  • src/modules/botStatus.js
  • src/api/routes/guilds.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}

📄 CodeRabbit inference engine (AGENTS.md)

Use safeReply(), safeSend(), or safeEditReply() for Discord messages — never send unsafe messages directly

Files:

  • src/index.js
  • src/modules/botStatus.js
src/modules/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create feature modules in src/modules/ and add corresponding config sections to config.json

Files:

  • src/modules/botStatus.js
src/api/routes/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create API route handlers in src/api/routes/ and mount them in src/api/server.js; add auth middleware if the endpoint requires authentication

Files:

  • src/api/routes/guilds.js
🧠 Learnings (19)
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-11T05:32:46.325Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T05:32:46.325Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-12T02:03:52.709Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.709Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.{js,ts,tsx} : Use single quotes for strings (except in JSON files); no double quotes

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-12T02:03:52.709Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.709Z
Learning: Applies to **/*.{js,ts,tsx,mjs} : Use single quotes for strings — double quotes only allowed in JSON files

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.json : Use double quotes in JSON files (standard JSON format); this exception applies only to JSON files, not JavaScript/TypeScript

Applied to files:

  • web/src/app/page.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.{js,ts,tsx} : Maintain a maximum line width of 100 characters

Applied to files:

  • web/src/app/page.tsx
  • web/src/components/landing/Pricing.tsx
📚 Learning: 2026-03-11T17:18:17.626Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T17:18:17.626Z
Learning: Applies to src/**/{startup,command-register,reload}*.{js,ts} : Remove process.env.GUILD_ID runtime reads from bot startup and reload command registration

Applied to files:

  • src/index.js
  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.js : Use ESM-only syntax: `import`/`export`, never `require()`/`module.exports`

Applied to files:

  • src/index.js
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to src/api/routes/*.js : Add adaptDeleteGuildIdParam in API route handlers to enforce guild moderation checks on DELETE requests without losing record id

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:52.709Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.709Z
Learning: Use `getConfig(guildId)` to retrieve merged global and guild-specific config

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to src/api/ws/**/*.js : Enforce tenant scoping on WebSocket handlers: reject mismatched guildId in filters and require entry.guild_id === ws.guildId for broadcast matching

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-10T23:29:51.063Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:29:51.063Z
Learning: Applies to src/api/routes/*.js : Apply requireGuildModerator authorization check to DELETE endpoints that modify guild-scoped resources

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to src/commands/**/*.js : Guild owners should be explicitly exempted from role hierarchy restrictions in command handlers to match Discord permission model expectations

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to config.json : All community features in `config.json` should be gated behind a `config.<feature>.enabled` flag; moderation commands are always available regardless of config settings

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to src/commands/reactionrole.js : Enforce invoker role hierarchy check in /reactionrole add command to prevent non-owner users from configuring roles at or above their highest role

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to web/src/app/api/**/route.ts : Include guildId in signed WebSocket ticket payload when issuing tickets from dashboard endpoints

Applied to files:

  • src/api/routes/guilds.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to src/api/utils/configAllowlist.js : When adding a new config section, add it to `SAFE_CONFIG_KEYS` to enable saves via the API/dashboard, and to `READABLE_CONFIG_KEYS` for API read access

Applied to files:

  • src/api/routes/guilds.js
🪛 GitHub Actions: CI
web/src/types/config.ts

[error] 1-1: pnpm run lint failed in volvox-bot-web (Biome check . / mono:lint via turbo). Command failed with exit code 1.

web/src/app/page.tsx

[error] 97-111: Biome format failed: Formatter would have normalized JSX attribute quoting (strokeLinecap/strokeLinejoin and d values switched to double quotes).

web/src/components/landing/Pricing.tsx

[error] 1-1: pnpm run lint failed in volvox-bot-web (Biome check . / mono:lint via turbo). Command failed with exit code 1.

🪛 GitHub Check: SonarCloud Code Analysis
src/api/utils/configValidation.js

[warning] 205-205: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ0IAguQxQIG-OxP98ir&open=AZ0IAguQxQIG-OxP98ir&pullRequest=304

🔇 Additional comments (5)
web/src/components/landing/Pricing.tsx (2)

81-85: Toggle label conditional refactor looks correct and cleaner.

Good update: the className expressions are now easier to read and remain behaviorally equivalent.

As per coding guidelines, "Maintain a maximum line width of 100 characters."

Also applies to: 102-106


163-165: CTA anchor JSX reformat is clear and behavior-preserving.

This keeps link behavior unchanged while improving readability of the conditional branches.

Also applies to: 167-169

src/api/utils/configValidation.js (1)

180-213: Nice tightening of rotation.messages[] validation.

The recursive array/object validation plus the non-whitespace text rule keeps bad entries from being persisted and then silently discarded later by runtime normalization.

Also applies to: 288-320, 329-343

src/index.js (1)

197-197: Good lifecycle sequencing for bot status.

Starting after client.login() and stopping during shutdown lines up with client.user availability and prevents the rotation timer from outliving the process.

Also applies to: 384-385

src/modules/botStatus.js (1)

419-423: The /status command does not change bot presence and no manual override scenario exists.

The /status command (src/commands/status.js) displays bot health metrics only—it does not call client.user.setPresence() or interact with the botStatus module. There are no other commands in the codebase that directly set presence outside of botStatus.js, so the concern about a rotation interval overwriting manual presence changes is not applicable to the current implementation.

			> Likely an incorrect or invalid review comment.

Comment thread src/api/routes/guilds.js
Comment thread src/modules/botStatus.js
Comment thread web/src/app/page.tsx
MohsinCoding and others added 18 commits March 19, 2026 18:05
…ility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Clamp rotation interval to MIN_PRESENCE_INTERVAL_MS (20s)
- Fix cross-format fallback order in resolveRotationIntervalMs
- Use replaceAll() instead of regex .replace(/…/g)
- Extract getEntryTypeLabel() to reduce cognitive complexity
- Add pattern validation for whitespace-only text rejection
- Document global botStatus scope escalation intent
@BillChirico BillChirico force-pushed the mohsin/rotating-bot-status branch from baef635 to b466e4e Compare March 19, 2026 22:13
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

@BillChirico BillChirico merged commit 30a8560 into main Mar 19, 2026
7 of 16 checks passed
@BillChirico BillChirico deleted the mohsin/rotating-bot-status branch March 19, 2026 22:17
@github-project-automation github-project-automation Bot moved this from In Review to Done in Volvox.Bot Mar 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Environment Cleaned Up

The Railway preview environment for this PR has been removed.

Environment: pr-304

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

feat: Custom Bot Status Rotation

3 participants