Flora supports two types of commands: prefix commands (triggered by messages) and slash commands (registered with Discord).
Prefix Commands
Prefix commands are triggered when a user sends a message starting with your bot’s prefix.
Basic Prefix Command
const ping = prefix({
name: 'ping',
description: 'Respond with pong',
run: async (ctx) => {
await ctx.reply('pong!')
}
})
createBot({
prefix: '!',
commands: [ping]
})
Users trigger this with: !ping
Command Arguments
Access arguments via ctx.args:
const greet = prefix({
name: 'greet',
description: 'Greet someone',
run: async (ctx) => {
const name = ctx.args[0] || 'world'
await ctx.reply(`Hello, ${name}!`)
}
})
Users can call: !greet Alice → "Hello, Alice!"
Context Properties
The full message event object from Discord.
Array of space-separated arguments after the command name.
Send a reply. Accepts a string or options object.
Edit the original message (if possible).
Slash Commands
Slash commands are registered with Discord and appear in the command picker.
Basic Slash Command
const hello = slash({
name: 'hello',
description: 'Say hello',
run: async (ctx) => {
await ctx.reply({ content: 'Hello!', ephemeral: true })
}
})
createBot({
slashCommands: [hello]
})
Options
Define typed options users can provide:
const echo = slash({
name: 'echo',
description: 'Echo your input',
options: [
{
name: 'text',
description: 'Text to echo',
type: 'string',
required: true
},
{
name: 'loud',
description: 'Make it loud',
type: 'boolean',
required: false
}
],
run: async (ctx) => {
const text = ctx.options.text as string
const loud = ctx.options.loud as boolean
const output = loud ? text.toUpperCase() : text
await ctx.reply({ content: output, ephemeral: true })
}
})
Option Types
String
Integer
Number
Boolean
{ name: 'message', type: 'string', required: true }
{ name: 'count', type: 'integer', required: false }
{ name: 'amount', type: 'number', required: false }
{ name: 'enabled', type: 'boolean', required: false }
Subcommands
Group related functionality under a single command:
const settings = slash({
name: 'settings',
description: 'Manage settings',
subcommands: [
{
name: 'get',
description: 'Get a setting',
options: [
{ name: 'key', description: 'Setting key', type: 'string', required: true }
],
run: async (ctx) => {
const key = ctx.options.key as string
await ctx.reply(`Setting ${key}: ...`)
}
},
{
name: 'set',
description: 'Set a setting',
options: [
{ name: 'key', description: 'Setting key', type: 'string', required: true },
{ name: 'value', description: 'Setting value', type: 'string', required: true }
],
run: async (ctx) => {
const { key, value } = ctx.options as { key: string; value: string }
await ctx.reply(`Set ${key} = ${value}`)
}
}
]
})
createBot({ slashCommands: [settings] })
Users invoke with: /settings get key:prefix or /settings set key:prefix value:!
Reply Options
Array of component rows (buttons, select menus).
For slash commands: make the response visible only to the user.
Control which mentions are allowed in the message.
Message flags (e.g., MessageFlags.IS_COMPONENTS_V2).
Type Definitions
export type Command = {
name: string
description?: string
run: (ctx: MessageContext & { args: string[] }) => Promise<void> | void
}
export function prefix(command: Command): Command
Examples
Counter with KV Storage
const counter = slash({
name: 'counter',
description: 'A simple counter using KV storage',
subcommands: [
{
name: 'get',
description: 'Get current count',
run: async (ctx) => {
const store = kv.store('counters')
const count = await store.get('main')
await ctx.reply(`Current count: ${count || 0}`)
}
},
{
name: 'increment',
description: 'Increment the counter',
run: async (ctx) => {
const store = kv.store('counters')
const current = parseInt(await store.get('main') || '0', 10)
const newCount = current + 1
await store.set('main', String(newCount))
await ctx.reply(`Count is now: ${newCount}`)
}
},
{
name: 'reset',
description: 'Reset the counter',
run: async (ctx) => {
const store = kv.store('counters')
await store.set('main', '0')
await ctx.reply('Counter reset to 0')
}
}
]
})
createBot({ slashCommands: [counter] })
See the KV Storage guide for more on persistent state.
Best Practices
Use descriptive names
Command names should be clear and concise. Avoid special characters.
Validate input
Always validate user input and provide helpful error messages.
Handle errors gracefully
Wrap async operations in try/catch and reply with error messages.
Use ephemeral for private responses
Set ephemeral: true for responses that should only be visible to the user.