Conversation
- Add botStatus module with configurable status (online/idle/dnd/invisible)
- Support custom activity text with variable interpolation ({memberCount}, {guildCount}, {botName})
- Rotating activities with configurable interval (default 30s)
- Hot-reload on config change via config-listeners
- Wire into startup/shutdown in index.js
- Add botStatus to config allowlist for API access
- Add default config in config.json
- 41 unit tests covering all exported functions
Closes #40
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR introduces a bot status management system with configurable Discord presence and activity rotation, integrated into the bot's lifecycle. It also restructures the configuration file with modularized top-level sections for various features. Changes
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 a new botStatus module to manage the Discord bot’s presence (status + activity text), including variable interpolation and optional rotation, with hot-reload via config listeners and API/dashboard writability.
Changes:
- Introduces
src/modules/botStatus.jswith interpolation, config resolution, presence application, rotation, and reload support. - Wires the module into startup/shutdown and config hot-reload (
src/index.js,src/config-listeners.js). - Adds unit tests and updates config allowlist + default
config.json(tests/modules/botStatus.test.js,src/api/utils/configAllowlist.js,config.json).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/modules/botStatus.test.js | New Vitest suite covering interpolation/config resolution/presence/rotation/reload behaviors. |
| src/modules/botStatus.js | New bot presence module implementing configurable status/activity + rotation + reload. |
| src/index.js | Starts bot status after login and stops it during graceful shutdown. |
| src/config-listeners.js | Adds config change listeners to trigger reloadBotStatus() on botStatus updates. |
| src/api/utils/configAllowlist.js | Allows API/dashboard writes to botStatus (and also adds quietMode). |
| config.json | Adds default botStatus section (and also adds quietMode) and reformats file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
| Filename | Overview |
|---|---|
| src/modules/botStatus.js | New module for bot presence management with rotation, variable interpolation, and hot-reload support. Clean implementation with comprehensive error handling. |
| src/index.js | Added bot status startup/shutdown integration but accidentally removed stopScheduler() and stopGithubFeed() calls during merge — critical bug. |
| tests/modules/botStatus.test.js | Comprehensive test suite with 41 tests covering all functions, edge cases, error handling, and rotation logic. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
Start[Bot Login] --> Init[startBotStatus called]
Init --> CheckEnabled{botStatus.enabled?}
CheckEnabled -->|No| Skip[Skip - module disabled]
CheckEnabled -->|Yes| LoadConfig[Load config: status, activityType, activities]
LoadConfig --> SetFirst[Apply first activity immediately]
SetFirst --> CountActivities{activities.length > 1?}
CountActivities -->|No| SingleActivity[Single activity - no rotation]
CountActivities -->|Yes| StartInterval[Start rotation interval]
StartInterval --> RotateLoop[Every rotateIntervalMs]
RotateLoop --> Increment[Increment activity index]
Increment --> WrapAround[Wrap around using modulo]
WrapAround --> Interpolate[Interpolate variables]
Interpolate --> SetPresence[client.user.setPresence]
SetPresence --> RotateLoop
ConfigChange[Config Change Detected] --> Reload[reloadBotStatus]
Reload --> StopInterval[Stop existing interval]
StopInterval --> Init
Shutdown[Bot Shutdown] --> StopBot[stopBotStatus]
StopBot --> ClearInterval[Clear rotation interval]
ClearInterval --> End[Bot stopped]
Last reviewed commit: fa596b2
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 `@config.json`:
- Around line 2-303: The README's Configuration section is missing docs for
several config keys; add configuration tables (option name, type, default,
description) for the following sections: memory, logging, help, announce,
snippet, poll, showcase, github, tldr, afk, engagement, challenges, review,
reminders, botStatus, and quietMode; follow the exact format used for the
existing documented sections (ai, triage, welcome, moderation, auditLog,
starboard, reputation, permissions), include each section's nested options
(e.g., memory.maxContextMemories, logging.fileOutput, logging.database.*,
botStatus.activities,
quietMode.allowedRoles/defaultDurationMinutes/maxDurationMinutes, github.feed.*,
engagement.activityBadges, etc.), and ensure examples/defaults match the values
present in the config.json diff.
In `@src/index.js`:
- Around line 478-479: The status rotation is started too early—call
startBotStatus(client) only after the bot is fully ready so applyPresence()
reads populated caches; move the startBotStatus invocation into the ClientReady
event handler (i.e., after client.once('ready' / 'clientReady', ...) or
equivalent) so it runs after guilds are cached, ensuring applyPresence() uses
correct memberCount/guildCount from client.guilds.cache rather than stale zeros.
In `@src/modules/botStatus.js`:
- Around line 97-102: getActivities currently returns an empty array when
cfg.activities exists but all entries are blank, causing applyPresence() to skip
setting presence; update getActivities so that after filtering blank/whitespace
entries it checks the filtered list and returns it only if length > 0, otherwise
return the fallback ['with Discord']; reference the getActivities function and
ensure the "filter" result is used to decide between returning the filtered
array or the default.
In `@tests/modules/botStatus.test.js`:
- Around line 175-193: Add a regression test that ensures getActivities treats
whitespace-only strings as empty: create a test named like "returns default
activity when activities are whitespace-only" that calls getActivities({
activities: [' ', '\t'] }) (or similar whitespace-only values) and assert the
result equals ['with Discord']; locate the existing tests around getActivities
and mirror their structure for consistency.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
config.jsonsrc/api/utils/configAllowlist.jssrc/config-listeners.jssrc/index.jssrc/modules/botStatus.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). (3)
- GitHub Check: Agent
- GitHub Check: Greptile Review
- GitHub Check: Docker Build Validation
🧰 Additional context used
📓 Path-based instructions (5)
**/*.js
📄 CodeRabbit inference engine (AGENTS.md)
**/*.js: Use ESM modules — useimport/export, neverrequire()
Always usenode:protocol for Node.js builtins (e.g.import { readFileSync } from 'node:fs')
Always use semicolons in JavaScript code
Use single quotes for strings (enforced by Biome)
Use 2-space indentation (enforced by Biome)
Files:
src/config-listeners.jstests/modules/botStatus.test.jssrc/api/utils/configAllowlist.jssrc/modules/botStatus.jssrc/index.js
src/**/*.js
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.js: Always use Winston logger —import { info, warn, error } from '../logger.js'. NEVER useconsole.log,console.warn,console.error, or anyconsole.*method in src/ files — replace any existing console calls with Winston equivalents
Pass structured metadata to Winston logs:info('Message processed', { userId, channelId })
Files:
src/config-listeners.jssrc/api/utils/configAllowlist.jssrc/modules/botStatus.jssrc/index.js
tests/**/*.js
📄 CodeRabbit inference engine (AGENTS.md)
All new code must include tests. Test coverage must maintain an 80% threshold on statements, branches, functions, and lines. Run
pnpm testbefore every commit
Files:
tests/modules/botStatus.test.js
src/modules/*.js
📄 CodeRabbit inference engine (AGENTS.md)
Per-request modules (AI, spam, moderation) should call
getConfig(guildId)on every invocation for automatic config changes. Stateful resources should useonConfigChangelisteners for reactive updates
Files:
src/modules/botStatus.js
config.json
📄 CodeRabbit inference engine (AGENTS.md)
When adding a new config section or key, document it in README.md's config reference section
Files:
config.json
🧠 Learnings (2)
📚 Learning: 2026-03-01T06:03:34.399Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to src/modules/*.js : Per-request modules (AI, spam, moderation) should call `getConfig(guildId)` on every invocation for automatic config changes. Stateful resources should use `onConfigChange` listeners for reactive updates
Applied to files:
src/config-listeners.jssrc/modules/botStatus.js
📚 Learning: 2026-03-01T06:03:34.399Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Tempban scheduler runs on a 60s interval. Started during startup in `index.js`, stopped during graceful shutdown. Catches up on missed unbans after restart
Applied to files:
src/index.js
🧬 Code graph analysis (4)
src/config-listeners.js (2)
src/modules/config.js (1)
onConfigChange(353-355)src/modules/botStatus.js (1)
reloadBotStatus(213-220)
tests/modules/botStatus.test.js (1)
src/modules/botStatus.js (14)
guildCount(65-65)interpolateActivity(61-72)resolvePresenceConfig(80-88)resolvePresenceConfig(116-116)status(81-81)activityType(84-85)getActivities(97-103)stopBotStatus(198-205)cfg(112-112)cfg(150-150)cfg(165-165)applyPresence(110-142)startBotStatus(162-193)reloadBotStatus(213-220)
src/modules/botStatus.js (1)
src/logger.js (2)
info(231-233)warn(238-240)
src/index.js (1)
src/modules/botStatus.js (2)
stopBotStatus(198-205)startBotStatus(162-193)
🪛 GitHub Check: Test (Vitest Coverage)
src/modules/botStatus.js
[failure] 34-34: tests/index.test.js > index.js > should handle autocomplete errors gracefully
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should handle autocomplete interactions
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should load state from disk when state file exists
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should not call markUnavailable when checkMem0Health succeeds
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should call markUnavailable when checkMem0Health rejects
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should warn and skip db init when DATABASE_URL is not set
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should initialize startup with database when DATABASE_URL is set
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
[failure] 34-34: tests/index.test.js > index.js > should configure allowedMentions to only parse users (Issue #61)
Error: [vitest] No "ActivityType" export is defined on the "discord.js" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("discord.js"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/modules/botStatus.js:34:12
❯ src/config-listeners.js:12:1
🔇 Additional comments (3)
src/api/utils/configAllowlist.js (1)
30-31: Allowlist expansion is consistent with the new config surface.Adding
botStatusandquietModetoSAFE_CONFIG_KEYSmatches the new top-level config sections.src/config-listeners.js (1)
107-121: Global bot-status hot-reload wiring looks solid.The global-scope guard plus
reloadBotStatus()callback is the right reactive pattern for this stateful module.Based on learnings: "Stateful resources should use
onConfigChangelisteners for reactive updates".src/modules/botStatus.js (1)
28-40: No action required. The review comment's premise is incorrect.
tests/modules/botStatus.test.jsdoes not mockdiscord.js, so it imports the real module fromnode_modules, which exportsActivityTypein v14.25.1. Withglobals: falseinvitest.config.js, mocks are file-scoped; the incomplete mock intests/index.test.jsdoes not affectbotStatus.test.js. The tests pass successfully in CI.Likely an incorrect or invalid review comment.
| stopTempbanScheduler(); | ||
| stopScheduler(); | ||
| stopGithubFeed(); | ||
| stopBotStatus(); |
There was a problem hiding this comment.
stopScheduler() and stopGithubFeed() calls are missing. These were accidentally removed during merge. Add them back:
| stopBotStatus(); | |
| stopBotStatus(); | |
| stopScheduler(); | |
| stopGithubFeed(); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/index.js
Line: 280
Comment:
`stopScheduler()` and `stopGithubFeed()` calls are missing. These were accidentally removed during merge. Add them back:
```suggestion
stopBotStatus();
stopScheduler();
stopGithubFeed();
```
How can I resolve this? If you propose a fix, please make it concise.
Summary
Implements configurable bot status and activity messages.
Features
{memberCount}— total members across all guilds{guildCount}— number of guilds{botName}— bot usernamebotStatusadded to config allowlist for dashboard controlConfig Shape
{ "botStatus": { "enabled": true, "status": "online", "activityType": "Playing", "activities": [ "with {memberCount} members", "in {guildCount} servers", "your assistant | Volvox" ], "rotateIntervalMs": 30000 } }Tests
41 unit tests covering:
Closes #40