Skip to main content
The Flora SDK provides utility functions to simplify common bot development tasks.

Role Checking

Check if a user has a specific role:
import { hasRole } from '@flora/sdk'

const admin = slash({
  name: 'admin',
  description: 'Admin-only command',
  run: async (ctx) => {
    if (!hasRole(ctx, '123456789012345678')) {
      await ctx.reply({ content: 'You need the Admin role!', ephemeral: true })
      return
    }
    
    await ctx.reply('Admin action performed')
  }
})

Signature

function hasRole(ctx: InteractionContext, roleId: string): boolean
ctx
InteractionContext
The interaction context from a slash command or component interaction.
roleId
string
The Discord role ID (snowflake) to check for.
Returns false if the member data is unavailable or the role is not present.

Subcommand Helpers

Get Subcommand Name

Extract the subcommand name from an interaction:
import { getSubcommand } from '@flora/sdk'

on('interactionCreate', async (ctx) => {
  const sub = getSubcommand(ctx)
  console.log('Subcommand:', sub)  // 'get', 'set', etc.
})

Get Subcommand Group

Extract the subcommand group name:
import { getSubcommandGroup } from '@flora/sdk'

on('interactionCreate', async (ctx) => {
  const group = getSubcommandGroup(ctx)
  if (group) {
    console.log('Group:', group)
  }
})

Signatures

function getSubcommand(ctx: InteractionContext): string | undefined
function getSubcommandGroup(ctx: InteractionContext): string | undefined

Type Definitions

The complete type definitions:
import type { InteractionContext } from './types'

export function hasRole(ctx: InteractionContext, roleId: string): boolean {
  return ctx.msg.member?.roles?.includes(roleId) ?? false
}

export function getSubcommand(ctx: InteractionContext): string | undefined {
  const rawData = ctx.msg.data as any
  if (!rawData?.options || !Array.isArray(rawData.options)) return undefined
  return rawData.options[0]?.name
}

export function getSubcommandGroup(ctx: InteractionContext): string | undefined {
  const rawData = ctx.msg.data as any
  if (!rawData?.options || !Array.isArray(rawData.options)) return undefined
  
  const firstOption = rawData.options[0]
  if (!firstOption) return undefined
  
  const type = firstOption.type
  if (type === 2) {  // Subcommand group type
    return firstOption.name
  }
  
  return undefined
}

Examples

Role-Based Access Control

const ADMIN_ROLE = '123456789012345678'
const MOD_ROLE = '234567890123456789'

const modAction = slash({
  name: 'mod',
  description: 'Moderator command',
  run: async (ctx) => {
    if (!hasRole(ctx, ADMIN_ROLE) && !hasRole(ctx, MOD_ROLE)) {
      await ctx.reply({ 
        content: 'You need Moderator or Admin role!', 
        ephemeral: true 
      })
      return
    }
    
    // Perform mod action
    await ctx.reply('Moderation action completed')
  }
})

Dynamic Subcommand Routing

const settings = slash({
  name: 'settings',
  description: 'Manage settings',
  subcommands: [
    {
      name: 'get',
      description: 'Get a setting',
      options: [{ name: 'key', type: 'string', required: true }],
      run: async (ctx) => {
        const key = ctx.options.key as string
        const store = kv.store('settings')
        const value = await store.get(key)
        await ctx.reply(`${key}: ${value || 'not set'}`)
      }
    },
    {
      name: 'set',
      description: 'Set a setting',
      options: [
        { name: 'key', type: 'string', required: true },
        { name: 'value', type: 'string', required: true }
      ],
      run: async (ctx) => {
        const { key, value } = ctx.options as { key: string; value: string }
        const store = kv.store('settings')
        await store.set(key, value)
        await ctx.reply(`Set ${key} = ${value}`)
      }
    }
  ]
})

// You can also use getSubcommand in interactionCreate
on('interactionCreate', async (ctx) => {
  if (ctx.msg.commandName === 'settings') {
    const sub = getSubcommand(ctx)
    console.log(`User called /settings ${sub}`)
  }
})

Permission Guard

Create a reusable permission check:
const REQUIRED_ROLES = ['123456789', '234567890', '345678901']

function requireAnyRole(ctx: InteractionContext, roleIds: string[]): boolean {
  return roleIds.some(id => hasRole(ctx, id))
}

const restricted = slash({
  name: 'restricted',
  description: 'Restricted command',
  run: async (ctx) => {
    if (!requireAnyRole(ctx, REQUIRED_ROLES)) {
      await ctx.reply({ 
        content: 'You do not have permission!', 
        ephemeral: true 
      })
      return
    }
    
    await ctx.reply('Access granted')
  }
})

Built-in Constants

The SDK exports commonly used constants:

Button Styles

import { ButtonStyles } from '@flora/sdk'

ButtonStyles.Primary    // 1 - Blurple
ButtonStyles.Secondary  // 2 - Gray
ButtonStyles.Success    // 3 - Green
ButtonStyles.Danger     // 4 - Red
ButtonStyles.Link       // 5 - Link button

Input Text Styles

import { InputTextStyles } from '@flora/sdk'

InputTextStyles.Short      // 1 - Single line
InputTextStyles.Paragraph  // 2 - Multi-line

Component Types

import { ComponentType } from '@flora/sdk'

ComponentType.ActionRow        // 1
ComponentType.Button           // 2
ComponentType.StringSelect     // 3
ComponentType.InputText        // 4
ComponentType.UserSelect       // 5
ComponentType.RoleSelect       // 6
ComponentType.MentionableSelect // 7
ComponentType.ChannelSelect    // 8
// ... V2 components

Secrets Management

Access environment secrets in your scripts:
const apiKey = secrets.get('API_KEY')

if (!apiKey) {
  console.log('API_KEY not configured')
} else {
  // Use the API key
  await fetch('https://api.example.com', {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  })
}

Signature

interface Secrets {
  get(name: string): string | undefined
}

declare const secrets: Secrets
Secrets are configured per deployment in the Flora dashboard. They are not stored in your script.

Logging

Use console.log for debugging and monitoring:
console.log('Bot started')
console.log('User ID:', ctx.msg.author?.id)
console.log('Command:', ctx.msg.commandName)
Console output is captured by the runtime and available in logs. Only console.log is supported.

Best Practices

1

Check permissions early

Use hasRole() at the start of command handlers to fail fast.
2

Provide clear error messages

Tell users why they can’t use a command and what role they need.
3

Use ephemeral responses for errors

Set ephemeral: true for permission denied messages.
4

Log important events

Use console.log to track command usage and errors.

Build docs developers (and LLMs) love