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
The interaction context from a slash command or component interaction.
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:
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
Check permissions early
Use hasRole() at the start of command handlers to fail fast.
Provide clear error messages
Tell users why they can’t use a command and what role they need.
Use ephemeral responses for errors
Set ephemeral: true for permission denied messages.
Log important events
Use console.log to track command usage and errors.