Skip to main content
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

ctx.msg
EventMessage
The full message event object from Discord.
ctx.args
string[]
Array of space-separated arguments after the command name.
ctx.reply
function
Send a reply. Accepts a string or options object.
ctx.edit
function
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

{ name: 'message', type: 'string', required: true }

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

content
string
The message text.
embeds
RawEmbed[]
Array of embed objects.
components
JsonValue[]
Array of component rows (buttons, select menus).
ephemeral
boolean
For slash commands: make the response visible only to the user.
allowedMentions
object
Control which mentions are allowed in the message.
flags
number
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

1

Use descriptive names

Command names should be clear and concise. Avoid special characters.
2

Validate input

Always validate user input and provide helpful error messages.
3

Handle errors gracefully

Wrap async operations in try/catch and reply with error messages.
4

Use ephemeral for private responses

Set ephemeral: true for responses that should only be visible to the user.

Build docs developers (and LLMs) love