Bot Commands
Overview
Cross-platform custom command system. Users create commands in the dashboard, assign them to platforms, and bots execute them with template variable support, cooldowns, and automation triggers.
Command Types
| Type | Description |
|---|---|
| Text | Fixed text response |
| Template | Text with variables: {user}, {uptime}, {song}, {count}, {random:a,b,c} |
| Automation | Triggers an automation flow instead of sending text |
Platform Assignment
Each command has a platforms JSON array. When creating, user selects which platforms the command is active on via checkboxes. Discord commands support both slash commands and prefix commands (configurable per command).
Permission Levels
Hierarchical — higher includes lower:
broadcaster > moderator > vip > subscriber > follower > everyone
Each bot maps platform-specific user data to this hierarchy.
Cooldowns
Redis-based, per-command:
- Global:
lumio:cmd_cd:{account_id}:{command_name}:global(TTL = cooldown seconds) - Per-user:
lumio:cmd_cd:{account_id}:{command_name}:user:{user_id}(TTL = cooldown seconds)
Template Variables
Extensible via VariableResolver trait in crates/lo-bot-commands/:
| Variable | Feature Gate | Description |
|---|---|---|
{user} | — | Display name of invoker |
{channel} | — | Channel/server name |
{platform} | — | Platform name |
{uptime} | — | Stream uptime (from Redis cache) |
{song} | widget:spotify_now_playing | Currently playing Spotify track |
{count} | — | Command use count |
{random:a,b,c} | — | Random selection from list |
Adding new variables: implement VariableResolver trait + register in VariableRegistry.
Global Commands & Overrides
- Global commands:
account_id IS NULL, created by admins, inherited by all accounts - Account commands:
account_idset, owned by specific account - Overrides: Accounts can override global commands (customize response, platforms, cooldowns) or disable them
- Shadowing: Account commands with same name as globals take priority
- Security: Overrides cannot make
min_roleless restrictive than global
Database
bot_commands— command definitions (global + account-scoped)bot_command_overrides— per-account overrides of global commandsbot_command_stats— usage statistics (Redis live counter, periodic DB flush)
Shared Crate: lo-bot-commands
Used by all 5 bots:
context.rs—CommandExecutionContext,UserRole,ApiCallback,CommandApiProviderregistry.rs—LoadedCommand,AccountCommands,execute_commandpermission.rs— Role hierarchy + checkingcooldown.rs— Redis cooldown check/set/incrementtemplate.rs—{variable}parser with right-to-left replacementvariables.rs—VariableResolvertrait,VariableRegistry, 7 built-in resolvers
Bot Integration
All 5 bots (Twitch, YouTube, Kick, Trovo, Discord):
- Load commands per account via
botCommandsForAccount(accountId, platform)system query - Store in-memory
CommandStore(HashMap<AccountId, AccountCommands>) - On chat message: check dynamic commands → permission → cooldown → execute
command_syncRedis action triggers reload- Discord: also registers slash commands per guild (bulk overwrite, 200/day rate limit)
API
| Type | Endpoint | Permission |
|---|---|---|
| GraphQL Query | botCommands: [BotCommand!]! | bot-commands:read |
| GraphQL Query | botCommand(id: ID!): BotCommand | bot-commands:read |
| GraphQL Mutation | createBotCommand(input: CreateBotCommandInput!) | bot-commands:create |
| GraphQL Mutation | updateBotCommand(id: ID!, input: UpdateBotCommandInput!) | bot-commands:edit |
| GraphQL Mutation | deleteBotCommand(id: ID!) | bot-commands:delete |
| GraphQL Mutation | overrideGlobalCommand(commandId: ID!, input: OverrideInput!) | bot-commands:edit |
| GraphQL Mutation | removeGlobalCommandOverride(commandId: ID!) | bot-commands:delete |
| System Query | botCommandsForAccount(accountId: ID!, platform: String!) | System key only |
REST Endpoints
All paths live under /v1 and mirror the GraphQL mutations.
| Method | Path | Permission | Description |
|---|---|---|---|
GET | /v1/bot-commands | bot-commands:read | List account commands (+ inherited globals) |
POST | /v1/bot-commands | bot-commands:create | Create an account command |
PUT | /v1/bot-commands/{id} | bot-commands:edit | Update a command |
DELETE | /v1/bot-commands/{id} | bot-commands:delete | Delete a command |
POST | /v1/bot-commands/{id}/override | bot-commands:edit | Override a global command for the account |
DELETE | /v1/bot-commands/{id}/override | bot-commands:delete | Remove the account's override of a global command |
Plan Limits
The plans.max_commands column caps how many commands an account may define. It
is read straight from the account's canonical plans row on every create — both
POST /v1/bot-commands and the createBotCommand mutation enforce the same cap
with the same PLAN_LIMIT_REACHED error (ZAF-1092). 0 means unlimited; if the
plan row cannot be resolved the cap fails open (unlimited). There is no
per-account override for this limit.
| Tier | Max Commands |
|---|---|
| Free | 25 |
| Pro | 200 |
Feature Gating
The whole Bot Commands surface is gated on the feature:bot_commands flag, in
addition to the bot-commands:* permissions. Every query and mutation
(botCommands, botCommand, createBotCommand, updateBotCommand,
deleteBotCommand, overrideGlobalCommand, removeGlobalCommandOverride on
GraphQL; GET/POST/PUT/DELETE /v1/bot-commands and the /override
endpoints on REST) enforces it on both protocols — GraphQL via
FeatureGuard::new("feature:bot_commands") chained before the permission guard,
REST via require_feature(..., "feature:bot_commands", ...). With the flag
disabled the API rejects the call directly (Feature 'feature:bot_commands' is not available, HTTP 403); the webapp route gate is UX only. The
system-key-only botCommandsForAccount query (used by bot workers) is
intentionally not feature-gated.
RBAC Permissions
Account: bot-commands:read/create/edit/delete
Admin: bot-commands:read/create/edit/delete (via require_admin_permission)
Key Files
| Path | Description |
|---|---|
crates/lo-bot-commands/ | Shared command execution crate (context, registry, permission, cooldown, template, variables) |
apps/api/src/db/bot_commands.rs | Database operations |
apps/api/src/graphql/bot_commands.rs | GraphQL queries and mutations |
apps/api/src/routes/bot_commands_crud.rs | REST endpoints |
See Also
- Bot Control — Bot pause/resume/reconnect dashboard
- Bot Connections — Custom bot identity OAuth flow
- Bot Modules — Chat moderation modules
- Automations — Automation flows triggered by commands