1. Write the command
One file,src/commands/<category>/<name>.js. The category folder becomes a page in /help, so put it in an
existing one unless you genuinely want a new page.
2. Get the guild right
If the command touches a Minecraft bot, it acts on one Hypixel guild. UseresolveTarget(interaction) —
it handles every “no guild / unknown guild / disabled / offline” case in one place, with the right tone. Never
index bridge.mcBots with a raw option value; autocomplete values are attacker-controlled.
Use guildPhrase(guild) / inGuild(guild) in reply text so a single-guild install keeps its original
wording.
3. Get the permissions right
Do not invent a third path, and do not add a dispatch route that skips the single-server guard in
handleCommands.js. See Admin roles.
4. Talk to Hypixel through the helpers
- Reading something back:
queryGuild(record, '/guild list'). Never hand-roll amessagelistener — Hypixel gives no request/response correlation, andqueryGuildis what stops two concurrent commands eating each other’s output. - Saying something:
sendChat(record, text). Callingbot.chatdirectly for relayed traffic gets accounts muted. - Building a chat line from user input:
buildGuildChatCommand(name, body). If you build one by hand, validate the username withisValidMinecraftName()and run free text throughsanitizeForChat()— an unflattened multi-line message sends its second line at command position.
5. Reply in the house style
deferReply() first for anything touching a Minecraft bot, then editReply(). ✅ / ⚠️ / ❌ prefixes and >
blockquotes. Truncate explicitly against Discord’s limits — 4096 for an embed description, 2000 for message
content.
6. The after-every-change checklist
This is the part that is easy to skip and shouldn’t be. All four, in the same commit as the code:- CHANGELOG.md — an entry under
## Unreleased, written for the people running the bot. Format in Releasing. - DISCORD_CHANGELOG.md — one line, second person, only if a guild member would notice.
- README.md — a row in the command table, and anything else it mentions that you changed.
- The public docs — docs/FEATURES.md is the canonical in-repo description; the Mintlify page restates it for its reader. A new command always touches Commands, which is meant to be exhaustive. New config file? Config files. New permission? Permissions.
7. Do not run the bot to check
Starting it connects to live Discord and Hypixel. Test the pure parts directly instead —chatRouting,
guilds, crossBridge, queryGuild against a bare EventEmitter, chatQueue, areCommandsDifferent.
If a live check is genuinely the only way, say so and ask.
Registration
Nothing to do.001registerCommands.js diffs the local commands against Discord’s on startup and
creates, edits or deletes as needed. Two things to know:
- Global propagation takes up to an hour, so a new command will not appear immediately.
choicesandautocompleteare mutually exclusive in the Discord API. Never set both.
deleted: true on a command unregisters it.