Creating AI Commands
CommandKit allows you to create commands that can be executed by AI models through natural language. This enables users to interact with your bot using conversational language instead of specific command syntax.
Basic AI Command Structure
To make a command AI-compatible, you need to:
- Add an
ai
function to handle AI execution - Add an
aiConfig
object to define the AI parameters
import { CommandData, MessageCommandContext } from 'commandkit';
import { AiConfig, AiContext } from '@commandkit/ai';
import { z } from 'zod';
export const command: CommandData = {
name: 'greet',
description: 'Greet a user',
};
export const aiConfig = {
description: 'Greet a specific user by their name or mention',
parameters: z.object({
username: z.string().describe('The name or mention of the user to greet'),
}),
} satisfies AiConfig;
// Regular command handler
export async function messageCommand(ctx: MessageCommandContext) {
const username = ctx.args.join(' ') || 'stranger';
await ctx.message.reply(`Hello, ${username}!`);
}
// AI command handler
export async function ai(ctx: AiContext) {
const { username } = ctx.ai.params;
await ctx.message.reply(`Hello, ${username}! 👋`);
}
AI Configuration (aiConfig
)
The aiConfig
object defines how the AI model should understand and execute your command:
Parameters
The parameters
field uses Zod schemas to define the structure and validation for AI-generated parameters:
export const aiConfig = {
description: 'Create a poll with multiple options',
parameters: z.object({
question: z.string().describe('The poll question'),
options: z.array(z.string()).describe('Array of poll options'),
duration: z.number().optional().describe('Poll duration in minutes'),
}),
} satisfies AiConfig;
Description
The description
field helps the AI model understand when and how to use your command:
export const aiConfig = {
description: 'Ban a user from the server with an optional reason',
parameters: z.object({
userId: z.string().describe('The ID of the user to ban'),
reason: z.string().optional().describe('The reason for the ban'),
}),
} satisfies AiConfig;
AI Context (AiContext
)
The AiContext
provides access to AI-specific data and functionality:
export async function ai(ctx: AiContext) {
// Access AI-generated parameters
const { message, duration } = ctx.ai.params;
// Access the original message
const originalMessage = ctx.message;
// Access the client and CommandKit instance
const client = ctx.client;
const commandkit = ctx.commandkit;
// Use the store for temporary data
ctx.store.set('startTime', Date.now());
}
Available Properties
params
: The AI-generated parameters based on your schemamessage
: The original Discord message that triggered the AIclient
: The Discord client instancecommandkit
: The CommandKit instancestore
: A Map for storing temporary data during execution
Complex Parameter Schemas
You can create sophisticated parameter schemas for complex commands:
export const aiConfig = {
description: 'Schedule a server event with date, time, and details',
parameters: z.object({
title: z.string().describe('Event title'),
description: z.string().optional().describe('Event description'),
date: z.string().describe('Event date in YYYY-MM-DD format'),
time: z.string().describe('Event time in HH:MM format'),
location: z.string().optional().describe('Event location or channel'),
maxParticipants: z
.number()
.optional()
.describe('Maximum number of participants'),
}),
} satisfies AiConfig;
export async function ai(ctx: AiContext) {
const { title, description, date, time, location, maxParticipants } =
ctx.ai.params;
// Create the event with the AI-generated parameters
await ctx.message.reply({
embeds: [
{
title: `📅 ${title}`,
description: description || 'No description provided',
fields: [
{ name: 'Date', value: date, inline: true },
{ name: 'Time', value: time, inline: true },
{ name: 'Location', value: location || 'TBD', inline: true },
{
name: 'Max Participants',
value: maxParticipants?.toString() || 'Unlimited',
inline: true,
},
],
color: 0x00ff00,
},
],
});
}
Error Handling
Handle errors gracefully in your AI commands:
export async function ai(ctx: AiContext) {
try {
const { userId, reason } = ctx.ai.params;
const user = await ctx.client.users.fetch(userId);
if (!user) {
throw new Error('User not found');
}
// Perform the action
await performBan(user, reason);
} catch (error) {
await ctx.message.reply({
content: `❌ Error: ${error.message}`,
allowedMentions: { parse: [] },
});
}
}
Best Practices
- Clear Descriptions: Write clear, specific descriptions for your commands and parameters
- Validation: Use Zod schemas to validate and constrain AI-generated parameters
- Error Handling: Always handle potential errors in AI command execution
- User Feedback: Provide clear feedback about the action taken
- Permission Checks: Verify user permissions before executing sensitive actions
export async function ai(ctx: AiContext) {
// Check permissions
if (!ctx.message.member?.permissions.has('ManageMessages')) {
await ctx.message.reply(
'❌ You need Manage Messages permission to use this command.',
);
return;
}
// Validate parameters
const { count } = ctx.ai.params;
if (count > 100) {
await ctx.message.reply('❌ Cannot delete more than 100 messages at once.');
return;
}
// Execute the action
// ... rest of the implementation
}
Interactive Components
AI commands can also work with interactive components like buttons and select menus:
import { Button, ActionRow } from 'commandkit';
import { ButtonStyle } from 'discord.js';
export async function ai(ctx: AiContext) {
const { message } = ctx.ai.params;
const confirmButton = (
<ActionRow>
<Button
onClick={async (interaction) => {
await interaction.reply('Action confirmed!');
}}
style={ButtonStyle.Success}
>
Confirm
</Button>
<Button
onClick={async (interaction) => {
await interaction.reply('Action cancelled.');
}}
style={ButtonStyle.Danger}
>
Cancel
</Button>
</ActionRow>
);
await ctx.message.reply({
content: message,
components: [confirmButton],
});
}