Conversation
|
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. |
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThis PR implements a custom bot status rotation feature with template variable support. It introduces a new Changes
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
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. |
|
| 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
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 ..."
There was a problem hiding this comment.
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
📒 Files selected for processing (14)
config.jsonsrc/api/utils/configValidation.jssrc/config-listeners.jssrc/index.jssrc/modules/botStatus.jstests/api/utils/configAllowlist.test.jstests/api/utils/configValidation.test.jstests/api/utils/validateConfigPatch.test.jstests/config-listeners.test.jstests/modules/botStatus.test.jsweb/src/components/dashboard/config-editor.tsxweb/src/components/dashboard/config-workspace/config-categories.tsweb/src/components/dashboard/config-workspace/types.tsweb/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, neverrequire()/module.exports
Files:
tests/api/utils/configAllowlist.test.jssrc/index.jstests/api/utils/validateConfigPatch.test.jstests/modules/botStatus.test.jssrc/config-listeners.jstests/config-listeners.test.jstests/api/utils/configValidation.test.jssrc/api/utils/configValidation.jssrc/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.jsweb/src/components/dashboard/config-workspace/types.tsweb/src/types/config.tssrc/index.jstests/api/utils/validateConfigPatch.test.jstests/modules/botStatus.test.jssrc/config-listeners.jstests/config-listeners.test.jstests/api/utils/configValidation.test.jsweb/src/components/dashboard/config-workspace/config-categories.tssrc/api/utils/configValidation.jsweb/src/components/dashboard/config-editor.tsxsrc/modules/botStatus.js
tests/**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
tests/**/*.test.js: Write bot tests using Vitest 4 with thenodeenvironment, matching thesrc/structure in thetests/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.jstests/api/utils/validateConfigPatch.test.jstests/modules/botStatus.test.jstests/config-listeners.test.jstests/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 fromsrc/logger.js— never useconsole.*methods
Files:
tests/api/utils/configAllowlist.test.jsweb/src/components/dashboard/config-workspace/types.tsweb/src/types/config.tssrc/index.jstests/api/utils/validateConfigPatch.test.jstests/modules/botStatus.test.jssrc/config-listeners.jstests/config-listeners.test.jstests/api/utils/configValidation.test.jsweb/src/components/dashboard/config-workspace/config-categories.tssrc/api/utils/configValidation.jsweb/src/components/dashboard/config-editor.tsxsrc/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.jsonshould be gated behind aconfig.<feature>.enabledflag; 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.tsweb/src/types/config.tsweb/src/components/dashboard/config-workspace/config-categories.tsweb/src/components/dashboard/config-editor.tsx
src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.js: Never useconsole.*methods; use the Winston logger instead viaimport logger from '../logger.js'(adjust path as needed), then calllogger.info(),logger.warn(),logger.error(), orlogger.debug()
Always usesafeReply(),safeSend(), orsafeEditReply()instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully
Files:
src/index.jssrc/config-listeners.jssrc/api/utils/configValidation.jssrc/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}
📄 CodeRabbit inference engine (AGENTS.md)
Use
safeReply(),safeSend(), orsafeEditReply()for Discord messages — never send unsafe messages directly
Files:
src/index.jssrc/modules/botStatus.js
src/modules/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Create feature modules in
src/modules/and add corresponding config sections toconfig.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.jsweb/src/components/dashboard/config-workspace/types.tstests/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.jstests/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.jsonweb/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.tsweb/src/components/dashboard/config-workspace/config-categories.tsweb/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: ".
[warning] 141-141: Unnecessary escape character: ".
web/src/components/dashboard/config-editor.tsx
[warning] 961-961: The empty object is useless.
[warning] 962-962: The empty object is useless.
src/modules/botStatus.js
[warning] 125-125: Prefer String#replaceAll() over String#replace().
[warning] 148-148: Unexpected negated condition.
[warning] 124-124: Prefer String#replaceAll() over String#replace().
[warning] 127-127: Prefer String#replaceAll() over String#replace().
[warning] 126-126: Prefer String#replaceAll() over String#replace().
🔇 Additional comments (13)
config.json (1)
153-174:botStatusdefault 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.messagesto legacyactivitiesis 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
botStatusassertions 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:ConfigFeatureIdupdate 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
botStatusin 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, andCONFIG_SEARCH_ITEMSare all aligned for the newbot-statusfeature, 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 forbotStatus.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
rotationconfig 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
SettingsFeatureCardimplementation 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 forbotStatusare well connected.The new interfaces plus
BotConfig/ConfigSectionextensions provide the expected type plumbing for the feature across editor and workspace code.Also applies to: 368-369, 394-395
There was a problem hiding this comment.
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
📒 Files selected for processing (13)
src/index.jssrc/modules/botStatus.jssrc/modules/events/messageCreate.jstests/api/routes/warnings.test.jstests/api/utils/configValidation.test.jstests/commands/voice.test.jstests/utils/discordCache.test.jsweb/src/app/page.tsxweb/src/components/landing/FeatureGrid.tsxweb/src/components/landing/Footer.tsxweb/src/components/landing/Hero.tsxweb/src/components/landing/Pricing.tsxweb/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, neverrequire()/module.exports
Files:
tests/commands/voice.test.jstests/api/utils/configValidation.test.jstests/api/routes/warnings.test.jssrc/modules/events/messageCreate.jstests/utils/discordCache.test.jssrc/index.jssrc/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.jsweb/src/components/landing/FeatureGrid.tsxweb/src/components/landing/Footer.tsxtests/api/utils/configValidation.test.jsweb/src/components/landing/Stats.tsxtests/api/routes/warnings.test.jssrc/modules/events/messageCreate.jsweb/src/components/landing/Pricing.tsxtests/utils/discordCache.test.jsweb/src/app/page.tsxweb/src/components/landing/Hero.tsxsrc/index.jssrc/modules/botStatus.js
tests/**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
tests/**/*.test.js: Write bot tests using Vitest 4 with thenodeenvironment, matching thesrc/structure in thetests/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.jstests/api/utils/configValidation.test.jstests/api/routes/warnings.test.jstests/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 fromsrc/logger.js— never useconsole.*methods
Files:
tests/commands/voice.test.jsweb/src/components/landing/FeatureGrid.tsxweb/src/components/landing/Footer.tsxtests/api/utils/configValidation.test.jsweb/src/components/landing/Stats.tsxtests/api/routes/warnings.test.jssrc/modules/events/messageCreate.jsweb/src/components/landing/Pricing.tsxtests/utils/discordCache.test.jsweb/src/app/page.tsxweb/src/components/landing/Hero.tsxsrc/index.jssrc/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.tsxweb/src/components/landing/Footer.tsxweb/src/components/landing/Stats.tsxweb/src/components/landing/Pricing.tsxweb/src/app/page.tsxweb/src/components/landing/Hero.tsx
src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.js: Never useconsole.*methods; use the Winston logger instead viaimport logger from '../logger.js'(adjust path as needed), then calllogger.info(),logger.warn(),logger.error(), orlogger.debug()
Always usesafeReply(),safeSend(), orsafeEditReply()instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully
Files:
src/modules/events/messageCreate.jssrc/index.jssrc/modules/botStatus.js
src/modules/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Create feature modules in
src/modules/and add corresponding config sections toconfig.json
Files:
src/modules/events/messageCreate.jssrc/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}
📄 CodeRabbit inference engine (AGENTS.md)
Use
safeReply(),safeSend(), orsafeEditReply()for Discord messages — never send unsafe messages directly
Files:
src/modules/events/messageCreate.jssrc/index.jssrc/modules/botStatus.js
web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
web/src/app/**/*.{ts,tsx}: ExportmetadatausingcreatePageMetadata()fromweb/src/lib/page-titles.tsin SSR entry points for dashboard pages
UseDashboardTitleSynccomponent andgetDashboardDocumentTitle()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.tsxweb/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.tsxweb/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.
web/src/components/landing/Pricing.tsx
[warning] 82-82: Unexpected negated condition.
src/modules/botStatus.js
[warning] 148-148: Unexpected negated condition.
[warning] 126-126: Prefer String#replaceAll() over String#replace().
[warning] 125-125: Prefer String#replaceAll() over String#replace().
[warning] 127-127: Prefer String#replaceAll() over String#replace().
[warning] 124-124: Prefer String#replaceAll() over String#replace().
🔇 Additional comments (24)
tests/commands/voice.test.js (1)
266-268: Looks good — readable assertion formatting with unchanged behavior.This multiline
toContainassertion 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 goodThis 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: MakeStatCardprops read-only to prevent accidental mutationPlease 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 goodThese 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
safeReplyfor 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
botStatusandauditLogare present.
120-143: LGTM!Solid test coverage for the new
botStatusschema validation:
- Valid rotation settings acceptance
- Invalid status value rejection
- Missing required
textfield rejectionConsider 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
asChildpattern 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: MovestartBotStatus(client)to afterclient.login(token)to ensure initial presence applies immediately.Currently
startBotStatus()is called beforeclient.login(token)(line 499), butapplyPresence()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.
There was a problem hiding this comment.
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
botStatusrotation + 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.jsonwith 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.
There was a problem hiding this comment.
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
Streamingactivity type cannot be properly configured without a stream URL.Discord requires a
urlfield forActivityType.Streamingto display the streaming status. The current implementation supportsStreamingin the activity type map but the message model only carries{ type, text }andbuildActivityPayload()never includes aurlfield. Configs selectingStreamingwill not display correctly on Discord. Either add URL support to the message schema and payload builder, or removeStreamingfrom 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 | 🟠 MajorClamp configured intervals before starting rotation.
Issue
#228explicitly calls out ~20s presence spacing, butresolveRotationIntervalMs()returns any positive value unchanged. The newrotation.intervalMinutespath can therefore schedule 60ms/6s updates, which defeats that guardrail and pushes the unsafe value all the way intosetInterval().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
📒 Files selected for processing (4)
src/api/utils/configValidation.jssrc/modules/botStatus.jstests/api/utils/configValidation.test.jstests/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, neverrequire()/module.exports
Files:
src/api/utils/configValidation.jstests/api/utils/configValidation.test.jstests/modules/botStatus.test.jssrc/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.jstests/api/utils/configValidation.test.jstests/modules/botStatus.test.jssrc/modules/botStatus.js
src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.js: Never useconsole.*methods; use the Winston logger instead viaimport logger from '../logger.js'(adjust path as needed), then calllogger.info(),logger.warn(),logger.error(), orlogger.debug()
Always usesafeReply(),safeSend(), orsafeEditReply()instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully
Files:
src/api/utils/configValidation.jssrc/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 fromsrc/logger.js— never useconsole.*methods
Files:
src/api/utils/configValidation.jstests/api/utils/configValidation.test.jstests/modules/botStatus.test.jssrc/modules/botStatus.js
tests/**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
tests/**/*.test.js: Write bot tests using Vitest 4 with thenodeenvironment, matching thesrc/structure in thetests/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.jstests/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 toconfig.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(), orsafeEditReply()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.
[warning] 158-158: Prefer String#replaceAll() over String#replace().
[warning] 160-160: Prefer String#replaceAll() over String#replace().
[warning] 228-228: Extract this nested ternary operation into an independent statement.
There was a problem hiding this comment.
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.rotationconfig shape (with legacy compatibility), interpolation vars, and rotation scheduler lifecycle. - Route
botStatusconfig writes to global scope, extend config schema/allowlists, and update defaults inconfig.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
writeScopeisglobal, the webhook payload still sendsguildId: req.params.idand doesn’t include the scope, which can mislead downstream consumers/audit logs into thinking this was a guild-scoped config change. Consider includingscopein the webhook payload and/or settingguildIdtonull/globalwhen the write is global.
fireAndForgetWebhook('DASHBOARD_WEBHOOK_URL', {
event: 'config.updated',
guildId: req.params.id,
section: topLevelKey,
updatedKeys: [path],
|
Can you also set the config in the database? |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 | 🔵 TrivialUpdate 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 | 🔵 TrivialConsider using
String#replaceAll()for clarity.Static analysis recommends
replaceAll()overreplace()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
📒 Files selected for processing (18)
biome.jsonsrc/api/routes/guilds.jssrc/index.jssrc/modules/botStatus.jssrc/modules/events.jssrc/modules/events/interactionCreate.jssrc/modules/handlers/ticketHandler.jstests/api/routes/guilds.test.jstests/api/utils/configValidation.test.jstests/index.test.jstests/modules/botStatus.test.jstests/modules/events-extra.test.jsweb/src/app/page.tsxweb/src/components/dashboard/config-editor-utils.tsweb/src/components/dashboard/config-sections/CommunitySettingsSection.tsxweb/src/components/dashboard/config-workspace/types.tsweb/src/components/dashboard/types.tsweb/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, neverrequire()/module.exports
Files:
src/modules/events/interactionCreate.jssrc/modules/events.jssrc/modules/handlers/ticketHandler.jstests/modules/events-extra.test.jssrc/index.jstests/index.test.jstests/api/routes/guilds.test.jssrc/api/routes/guilds.jstests/api/utils/configValidation.test.jstests/modules/botStatus.test.jssrc/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.jsweb/src/components/dashboard/types.tssrc/modules/events.jsweb/src/components/dashboard/config-workspace/types.tsweb/src/app/page.tsxsrc/modules/handlers/ticketHandler.jsweb/src/components/dashboard/config-editor-utils.tstests/modules/events-extra.test.jssrc/index.jstests/index.test.jstests/api/routes/guilds.test.jsweb/src/components/landing/Footer.tsxsrc/api/routes/guilds.jsweb/src/components/dashboard/config-sections/CommunitySettingsSection.tsxtests/api/utils/configValidation.test.jstests/modules/botStatus.test.jssrc/modules/botStatus.js
src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.js: Never useconsole.*methods; use the Winston logger instead viaimport logger from '../logger.js'(adjust path as needed), then calllogger.info(),logger.warn(),logger.error(), orlogger.debug()
Always usesafeReply(),safeSend(), orsafeEditReply()instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully
Files:
src/modules/events/interactionCreate.jssrc/modules/events.jssrc/modules/handlers/ticketHandler.jssrc/index.jssrc/api/routes/guilds.jssrc/modules/botStatus.js
src/modules/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Create feature modules in
src/modules/and add corresponding config sections toconfig.json
Files:
src/modules/events/interactionCreate.jssrc/modules/events.jssrc/modules/handlers/ticketHandler.jssrc/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 fromsrc/logger.js— never useconsole.*methods
Files:
src/modules/events/interactionCreate.jsweb/src/components/dashboard/types.tssrc/modules/events.jsweb/src/components/dashboard/config-workspace/types.tsweb/src/app/page.tsxsrc/modules/handlers/ticketHandler.jsweb/src/components/dashboard/config-editor-utils.tstests/modules/events-extra.test.jssrc/index.jstests/index.test.jstests/api/routes/guilds.test.jsweb/src/components/landing/Footer.tsxsrc/api/routes/guilds.jsweb/src/components/dashboard/config-sections/CommunitySettingsSection.tsxtests/api/utils/configValidation.test.jstests/modules/botStatus.test.jssrc/modules/botStatus.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}
📄 CodeRabbit inference engine (AGENTS.md)
Use
safeReply(),safeSend(), orsafeEditReply()for Discord messages — never send unsafe messages directly
Files:
src/modules/events/interactionCreate.jssrc/modules/events.jssrc/modules/handlers/ticketHandler.jssrc/index.jssrc/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.tsweb/src/components/dashboard/config-workspace/types.tsweb/src/app/page.tsxweb/src/components/dashboard/config-editor-utils.tsweb/src/components/landing/Footer.tsxweb/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
web/src/app/**/*.{ts,tsx}: ExportmetadatausingcreatePageMetadata()fromweb/src/lib/page-titles.tsin SSR entry points for dashboard pages
UseDashboardTitleSynccomponent andgetDashboardDocumentTitle()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 thenodeenvironment, matching thesrc/structure in thetests/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.jstests/index.test.jstests/api/routes/guilds.test.jstests/api/utils/configValidation.test.jstests/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 insrc/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.tsweb/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.tsweb/src/components/dashboard/config-editor-utils.tssrc/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.jstests/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.jssrc/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.jsweb/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.
src/modules/botStatus.js
[failure] 214-214: Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.
[warning] 229-229: Extract this nested ternary operation into an independent statement.
[warning] 161-161: Prefer String#replaceAll() over String#replace().
[warning] 159-159: Prefer String#replaceAll() over String#replace().
[warning] 160-160: Prefer String#replaceAll() over String#replace().
[warning] 158-158: Prefer String#replaceAll() over String#replace().
🔇 Additional comments (33)
biome.json (1)
2-2: Schema version bump looks good.The
$schemaupdate 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
ModalBuilderinstantiation 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 intoCONFIG_CATEGORIES(under'community-tools') andFEATURE_LABELSwith 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
registerWelcomeOnboardingHandlersis 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
botStatusmock is properly structured with the expectedstartBotStatusandstopBotStatusfunctions, following the established pattern used by other module mocks in this file.
213-216: LGTM!Mock registration for
botStatus.jsfollows the established pattern.
383-391: LGTM!This test correctly verifies the startup sequence by:
- Asserting
startBotStatusis called with the client instance- Using
invocationCallOrderto verifyclient.logincompletes beforestartBotStatusis invokedThis 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
botStatusconfig writes are routed to global scope:
- The route passes
undefinedtosetConfigValuewhentopLevelKey === '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'forbotStatusupdates insrc/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
botStatusconfig writes to the global scope (viasetConfigValue(..., undefined)) while maintaining guild-level authorization checks. The effective config read on line 747 correctly usesgetConfig()for global scope, and the logging now includes thescopefield 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 afterclient.login(token)completes, ensuringclient.useris 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-statusfeature 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
messagesarray 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.messagesschema 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.messagesformat consumptionintervalMinutesto 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
resolveRotationIntervalMsfunction now correctly enforcesMIN_PRESENCE_INTERVAL_MS(20,000ms) viaMath.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
formatUptimefunction handles edge cases (non-finite, negative values) and produces a compact human-readable format.
262-301: LGTM! Robust message normalization with appropriate fallbacks.The
getRotationMessagesfunction:
- Prioritizes new
rotation.messagesformat- Falls back to legacy
activitiesarray- 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
applyPresencefunction correctly:
- Resolves status and messages from config
- Handles
ActivityType.Customspecially viabuildActivityPayload- Interpolates template variables before setting presence
- Catches and logs errors gracefully
419-449: LGTM! Rotation lifecycle correctly manages intervals.The
startBotStatusfunction:
- 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
f8df612 to
439f752
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/modules/botStatus.js (1)
40-46:⚠️ Potential issue | 🟠 Major
Streamingstill isn't renderable with the current payload shape.The allowed type list exposes
Streaming, butbuildActivityPayload()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
📒 Files selected for processing (7)
src/api/routes/guilds.jssrc/api/utils/configValidation.jssrc/index.jssrc/modules/botStatus.jsweb/src/app/page.tsxweb/src/components/landing/Pricing.tsxweb/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.tsweb/src/app/page.tsxsrc/index.jsweb/src/components/landing/Pricing.tsxsrc/api/utils/configValidation.jssrc/modules/botStatus.jssrc/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.tsweb/src/app/page.tsxweb/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 fromsrc/logger.js— never useconsole.*methods
Files:
web/src/types/config.tsweb/src/app/page.tsxsrc/index.jsweb/src/components/landing/Pricing.tsxsrc/api/utils/configValidation.jssrc/modules/botStatus.jssrc/api/routes/guilds.js
web/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
web/src/app/**/*.{ts,tsx}: ExportmetadatausingcreatePageMetadata()fromweb/src/lib/page-titles.tsin SSR entry points for dashboard pages
UseDashboardTitleSynccomponent andgetDashboardDocumentTitle()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, neverrequire()/module.exports
Files:
src/index.jssrc/api/utils/configValidation.jssrc/modules/botStatus.jssrc/api/routes/guilds.js
src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.js: Never useconsole.*methods; use the Winston logger instead viaimport logger from '../logger.js'(adjust path as needed), then calllogger.info(),logger.warn(),logger.error(), orlogger.debug()
Always usesafeReply(),safeSend(), orsafeEditReply()instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully
Files:
src/index.jssrc/api/utils/configValidation.jssrc/modules/botStatus.jssrc/api/routes/guilds.js
{src/commands/**/*.{js,ts},src/modules/**/*.{js,ts},src/index.js}
📄 CodeRabbit inference engine (AGENTS.md)
Use
safeReply(),safeSend(), orsafeEditReply()for Discord messages — never send unsafe messages directly
Files:
src/index.jssrc/modules/botStatus.js
src/modules/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Create feature modules in
src/modules/and add corresponding config sections toconfig.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 insrc/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.tsxweb/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.jssrc/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 \.
🔇 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 ofrotation.messages[]validation.The recursive array/object validation plus the non-whitespace
textrule 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 withclient.useravailability and prevents the rotation timer from outliving the process.Also applies to: 384-385
src/modules/botStatus.js (1)
419-423: The/statuscommand does not change bot presence and no manual override scenario exists.The
/statuscommand (src/commands/status.js) displays bot health metrics only—it does not callclient.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.
…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
baef635 to
b466e4e
Compare
|
🧹 Preview Environment Cleaned UpThe Railway preview environment for this PR has been removed. Environment: |


Summary
Verification
Closes #228