Building Custom Commands
Relevant source files
The following files were used as context for generating this wiki page:
The Poixpixel Discord Bot uses a modular architecture for implementing slash commands. Commands are built using the discord.js library and are automatically discovered by the bot's initialization logic. This system allows developers to create specialized commands categorized into functional groups such as fun, moderation, util, and testing.
The command building process involves defining command data (using SlashCommandBuilder), implementing an execution handler, and ensuring the command adheres to the project's standardized Command or SubcommandCommand types. Commands are registered to specific guilds rather than globally, facilitating faster development iterations.
Sources: CONTRIBUTING.md:213-217, src/util/deployCommand.ts:74-78
Command Architecture and Discovery
Commands are stored within the src/commands/ directory, organized into subdirectories by category. The bot employs an auto-discovery mechanism that recursively scans these directories for valid TypeScript files during the deployment phase.
Command Registration Flow
The deployment process follows a specific sequence: it identifies all .js files (compiled from TypeScript) in the target directory, imports them as modules, validates their structure, and then updates the Discord API via a REST call.
The registration logic ensures that existing guild commands are undeployed before the new command list is registered to prevent orphaned command definitions.
Sources: src/util/deployCommand.ts:24-38, src/util/deployCommand.ts:48-73
Service Dependencies
Most commands interact with secondary services like PostgreSQL or Redis. The bot ensures these services are available before commands are fully operational.
| Service | Purpose | File Reference |
|---|---|---|
| Drizzle ORM | Database interactions for leveling and achievements | src/db/db.ts |
| ioredis | High-performance caching for transient data | src/db/redis.ts |
| Winston | Structured logging of command execution and errors | src/util/logger.ts |
Sources: CONTRIBUTING.md:213-217, CONTRIBUTING.md:231-255
Command Implementation Standard
Every command must export a default object that satisfies the Command type. This object requires two primary properties: data for defining the slash command interface and execute for handling the interaction logic.
Structure of a Standard Command
import { SlashCommandBuilder } from 'discord.js';
import type { Command } from '@/types/CommandTypes.js';
export default {
data: new SlashCommandBuilder()
.setName('example')
.setDescription('Example command description'),
async execute(interaction) {
// Implementation logic here
},
} satisfies Command;
Sources: CONTRIBUTING.md:213-228
Handling Subcommands
For complex feature sets like "Counting" or "Achievement Management," the bot utilizes subcommands. This groups related functionality under a single primary command name.
Execution Lifecycle and Validation
Command execution includes several mandatory steps to ensure reliability, including permission checks, ephemeral state handling, and error catching.
Interaction Validation
Developers are encouraged to use centralized helper functions like validateInteraction to check if the guild, channel, or message context is still valid before processing.
Permission Management
Permissions are handled either via the SlashCommandBuilder's setDefaultMemberPermissions method or through manual checks inside the execute function using interaction.memberPermissions.
| Command | Permission Required | Context |
|---|---|---|
/config | Administrator | View bot secrets/redacted config |
/counting setcount | Administrator | Modify current count |
/counting ban | BanMembers | Restrict user from counting |
/manage-achievements | ManageGuild | Create/Delete achievements |
Sources: src/commands/util/config.ts:16, src/commands/fun/counting.ts:145, src/commands/util/manage-achievement.ts:18
User Interface Components
Commands often utilize Discord's interactive components such as EmbedBuilder, StringSelectMenuBuilder, and ButtonBuilder to provide a rich user experience, particularly for pagination or help menus.
Help and Navigation Systems
The help command demonstrates advanced usage of select menus and collectors. It categories commands into maps and allows users to browse details by category.
- Collector Pattern: Use
createMessageComponentCollectorto handle subsequent interactions within a defined timeframe (e.g., 60 seconds). - Cleanup: Always call
safeRemoveComponentson collector expiration to avoid "dead" buttons in the UI.
Sources: src/commands/util/help.ts:52-124, src/commands/fun/leaderboard.ts:101-140
Error Handling and Logging
All commands must be wrapped in try-catch blocks. Errors should be logged using the structured logger and the user should receive a safe, non-technical error response via safelyRespond.
try {
// Database or API logic
} catch (error) {
logger.error('[CommandName] Description of failure', error);
await safelyRespond(interaction, 'An error occurred while processing your request.');
}
Sources: CONTRIBUTING.md:247-255, src/discord-bot.ts:36-54
Building custom commands in the Poixpixel bot requires adhering to the structured directory layout, using TypeScript for type safety, and implementing standardized error handling. By leveraging the auto-deployment system, developers can focus on business logic while the bot manages the complexity of Discord API registration and service connectivity.