Overview
Flora provides helper functions for common tasks like checking roles and extracting subcommand information.
hasRole()
Check if a user has a specific role in the current guild.
Signature
function hasRole(ctx: InteractionContext, roleId: string): boolean
Parameters
ctx
InteractionContext
required
The interaction context from a slash command
The Discord role ID to check for
Return Value
Returns true if the user has the role, false otherwise.
Example
slash({
name: 'admin',
description: 'Admin-only command',
run: async (ctx) => {
const adminRoleId = '123456789'
if (!hasRole(ctx, adminRoleId)) {
await ctx.reply({ content: 'You need admin role to use this!', ephemeral: true })
return
}
await ctx.reply('Admin command executed!')
}
})
Multiple Roles
slash({
name: 'moderator',
description: 'Moderator command',
run: async (ctx) => {
const modRoleId = '111111111'
const adminRoleId = '222222222'
if (!hasRole(ctx, modRoleId) && !hasRole(ctx, adminRoleId)) {
await ctx.reply({ content: 'Insufficient permissions', ephemeral: true })
return
}
await ctx.reply('Access granted!')
}
})
getSubcommand()
Extract the name of the subcommand that was invoked.
Signature
function getSubcommand(ctx: InteractionContext): string | undefined
Parameters
ctx
InteractionContext
required
The interaction context from a slash command
Return Value
Returns the subcommand name as a string, or undefined if no subcommand was used.
Example
slash({
name: 'admin',
description: 'Admin commands',
subcommands: [
{
name: 'ban',
description: 'Ban a user',
run: async (ctx) => {
await ctx.reply('User banned')
}
},
{
name: 'kick',
description: 'Kick a user',
run: async (ctx) => {
await ctx.reply('User kicked')
}
}
]
})
// In a global interaction handler
on('interactionCreate', async (ctx) => {
const subcommand = getSubcommand(ctx)
console.log(`Subcommand used: ${subcommand}`) // 'ban' or 'kick'
})
Manual Subcommand Routing
slash({
name: 'config',
description: 'Bot configuration',
options: [
{
name: 'prefix',
description: 'Change prefix',
type: 'subcommand',
options: [{ name: 'new_prefix', type: 'string', required: true }]
},
{
name: 'channel',
description: 'Set log channel',
type: 'subcommand',
options: [{ name: 'channel_id', type: 'string', required: true }]
}
],
run: async (ctx) => {
const sub = getSubcommand(ctx)
if (sub === 'prefix') {
const newPrefix = ctx.options.new_prefix
await ctx.reply(`Prefix changed to ${newPrefix}`)
} else if (sub === 'channel') {
const channelId = ctx.options.channel_id
await ctx.reply(`Log channel set to <#${channelId}>`)
}
}
})
getSubcommandGroup()
Extract the name of the subcommand group that was invoked (for nested subcommands).
Signature
function getSubcommandGroup(ctx: InteractionContext): string | undefined
Parameters
ctx
InteractionContext
required
The interaction context from a slash command
Return Value
Returns the subcommand group name as a string, or undefined if no group was used.
Example
// Command structure: /settings user display-name
// Group: 'user', Subcommand: 'display-name'
slash({
name: 'settings',
description: 'Bot settings',
options: [
{
name: 'user',
description: 'User settings',
type: 'subcommand_group',
options: [
{
name: 'display-name',
type: 'subcommand',
description: 'Set display name',
options: [{ name: 'name', type: 'string', required: true }]
},
{
name: 'theme',
type: 'subcommand',
description: 'Set theme'
}
]
}
],
run: async (ctx) => {
const group = getSubcommandGroup(ctx)
const sub = getSubcommand(ctx)
console.log(`Group: ${group}, Subcommand: ${sub}`)
// Group: user, Subcommand: display-name
if (group === 'user' && sub === 'display-name') {
await ctx.reply(`Name updated to ${ctx.options.name}`)
}
}
})
Common Patterns
Role-Based Permission Check
function requireRole(ctx: InteractionContext, roleId: string, roleName: string): boolean {
if (!hasRole(ctx, roleId)) {
ctx.reply({
content: `❌ You need the ${roleName} role to use this command.`,
ephemeral: true
})
return false
}
return true
}
slash({
name: 'purge',
description: 'Delete messages',
run: async (ctx) => {
if (!requireRole(ctx, 'MOD_ROLE_ID', 'Moderator')) return
// Command logic
await ctx.reply('Messages purged')
}
})
Combining Helpers
slash({
name: 'admin',
description: 'Admin panel',
subcommands: [
{
name: 'reset',
description: 'Reset settings',
run: async (ctx) => {
// Check role
if (!hasRole(ctx, 'ADMIN_ROLE_ID')) {
await ctx.reply({ content: 'Admin only!', ephemeral: true })
return
}
// Execute reset
const sub = getSubcommand(ctx)
console.log(`Admin ${ctx.msg.user.username} used ${sub}`)
await ctx.reply('Settings reset!')
}
}
]
})
Dynamic Subcommand Logging
on('interactionCreate', async (ctx) => {
const group = getSubcommandGroup(ctx)
const sub = getSubcommand(ctx)
if (group && sub) {
console.log(`/${ctx.msg.commandName} ${group} ${sub}`)
} else if (sub) {
console.log(`/${ctx.msg.commandName} ${sub}`)
} else {
console.log(`/${ctx.msg.commandName}`)
}
})
Notes
hasRole() only works with InteractionContext (slash commands), not message commands
- Role IDs are strings, not numbers
getSubcommand() returns the first-level subcommand name
getSubcommandGroup() is for nested command structures (group → subcommand)
- These helpers safely return
undefined if data is missing
- Always check permissions before executing sensitive commands