Skip to main content

Bot Modules

Overview​

The Bot Modules system provides configurable chat bot modules for automated moderation and messaging. Each module can be independently enabled/disabled per account, with module-specific configuration and exemption rules. The system currently supports four modules: link protection, spam protection, word filter, and timed messages. Exempt roles (moderators, VIPs, subscribers) can be configured per module to bypass enforcement.

Architecture​

Backend​

  • GraphQL (apps/api/src/graphql/bot_modules.rs) -- Queries for listing all module configs and fetching individual modules by name. Mutations for upserting and deleting module configs.
  • Database (apps/api/src/db/bot_modules.rs) -- PostgreSQL operations for the bot_module_configs table. Supports list, get, upsert, and delete operations.
  • Validation -- Module names are validated against a whitelist: link_protection, spam_protection, word_filter, timed_messages.

Frontend​

  • Bot modules settings page with a card per module.
  • Toggle switch for enable/disable.
  • Module-specific configuration form.
  • Exempt roles selector (checkboxes for mods, VIPs, subs).

API​

GraphQL Queries​

QueryPermissionDescription
botModulesbot-modules:readList all bot module configs for the account
botModule(name)bot-modules:readGet a single bot module config by name. Validates name against whitelist.

GraphQL Mutations​

MutationPermissionDescription
updateBotModule(name, enabled, config?, exemptRoles?)bot-modules:editUpsert a bot module config. Name is validated against the whitelist. Config and exempt_roles default to {} if not provided.
deleteBotModule(name)bot-modules:editDelete a bot module config. Name is validated against the whitelist.

REST Endpoints​

All paths live under /v1.

MethodPathPermissionDescription
GET/v1/bot-modulesbot-modules:readList all module configs for the account
PUT/v1/bot-modules/{module_name}bot-modules:editUpsert a module config (enabled, config, exempt_roles). Module name is validated against the whitelist.
DELETE/v1/bot-modules/{module_name}bot-modules:editDelete a module config

Valid Module Names​

ModuleDescription
link_protectionDetects and moderates messages containing links
spam_protectionDetects and moderates spam patterns (repeated messages, excessive caps, etc.)
word_filterFilters messages containing blocked words/phrases
timed_messagesSends automatic messages at configured intervals

Permissions​

PermissionDescription
bot-modules:readView bot module configurations
bot-modules:editCreate, update, and delete bot module configurations

Database​

TableDatabaseDescription
bot_module_configsPostgreSQLid, account_id, module_name, enabled (bool), config (JSONB), exempt_roles (JSONB), created_at, updated_at

Config JSONB Structure (per module)​

The config field contains module-specific settings. Examples:

link_protection:

{
"action": "delete",
"warning_message": "Links are not allowed",
"whitelist_domains": ["youtube.com", "twitch.tv"],
"timeout_duration": 60
}

Whitelist and blacklist entries match a domain itself and its subdomains, on a dot boundary: youtube.com covers youtube.com and m.youtube.com, but not evilyoutube.com or youtube.com.evil.net. Entries are compared case-insensitively, and a trailing dot is ignored.

spam_protection:

{
"max_repeated_messages": 3,
"max_caps_percentage": 80,
"min_message_length_for_caps": 10,
"action": "timeout",
"timeout_duration": 30
}

word_filter:

{
"blocked_words": ["badword1", "badword2"],
"blocked_patterns": ["regex_pattern"],
"action": "delete",
"warning_message": "Your message was removed"
}

timed_messages:

{
"messages": [
{ "text": "Follow us on Twitter!", "interval_minutes": 15 },
{ "text": "Join our Discord!", "interval_minutes": 30 }
],
"min_chat_lines_between": 5
}

Exempt Roles JSONB Structure​

{
"moderators": true,
"vips": true,
"subscribers": false
}

Data Flow​

  1. Account owner/admin enables a bot module and configures it via the dashboard.
  2. Configuration is saved via updateBotModule mutation (upsert pattern).
  3. When a chat message is received, the bot checks each enabled module:
    • Is the user exempt (mod/VIP/sub based on exempt_roles)?
    • Does the message violate the module's rules (based on config)?
  4. If a violation is detected, the configured action (delete, timeout, warn) is executed.
  5. Timed messages module sends messages at configured intervals when chat activity meets the minimum threshold.

Feature gating​

The whole Bot Modules surface is gated on the umbrella feature:bot_modules flag: every query and mutation (botModules, botModule, updateBotModule, deleteBotModule on GraphQL; GET/PUT/DELETE /v1/bot-modules on REST) enforces it on both protocols - GraphQL via FeatureGuard::new("feature:bot_modules") chained before the permission guard, REST via require_feature(..., "feature:bot_modules", ...) after the permission check. With feature:bot_modules disabled the API rejects the call directly (Feature 'feature:bot_modules' is not available, HTTP 403), so listing configs is gated too - the webapp route gate is UX only, not the boundary.

In addition, each built-in module has its own finer feature flag, keyed bot_module:{module_name} - bot_module:link_protection, bot_module:spam_protection, bot_module:word_filter, bot_module:timed_messages. Writing or deleting a module config checks that per-module flag on both protocols after the umbrella feature:bot_modules check, so a specific module disabled for the account's plan cannot be configured even when the Bot Modules area itself is enabled. Which plans enable which module flag is a plan_features mapping, editable by admins without a deploy.

Key Files​

PathDescription
apps/api/src/graphql/bot_modules.rsGraphQL queries and mutations
apps/api/src/routes/bot_modules.rsREST endpoints
apps/api/src/db/bot_modules.rsDatabase operations

Extension Bot Modules​

In addition to the four built-in modules above, accounts can install extension bot modules from the Lumio Extension Store. Extension bot modules are community-developed moderation and automation modules that run inside the same V8 isolate sandbox used by all extensions, with the same security guarantees.

How It Works​

Extension bot modules use the bot_module category in lumio.config.json. When installed, the extension registers one or more triggers (commands, keywords, patterns, events, timers, or a moderation hook) that the bots and the Bot Module Worker run against incoming chat messages, stream events and the clock.

Chat message arrives at the platform bot (Twitch, Kick, YouTube, Discord)
→ Bot matches it against the account's extension triggers (command, keyword, regex)
→ Command: min_role + cooldown check, then the Bot Module Worker runs the handler
and the bot posts the reply
→ Keyword / pattern: published to the Bot Module Worker, which runs the handler
and sends the reply back through the bot
→ Moderation (Twitch, Kick, YouTube): the module's moderate handler decides
allow / delete / timeout / ban / warn within 500 ms
Stream event (follow, sub, raid, ...) → the Bot Module Worker runs the event handlers
Timer interval elapses → the Bot Module Worker runs the timer handler
→ Handlers run in the V8 isolate sandbox
→ Any actions a handler returns run after its reply, through the same
permission checks as widget actions

Installing, uninstalling or reconfiguring a bot module - or an admin suspending it - updates the bots' trigger set and the Bot Module Worker's install registry right away; no restart is needed. The same holds for the default system modules installed with a new account. Technical detail: Bot Modules (developer guide).

Installation and Configuration​

Users install extension bot modules from the Extension Store like any other extension. Once installed, the module appears in Dashboard > Bot Modules alongside built-in modules. Each extension bot module supports:

  • Enable/disable toggle per account
  • Configuration via config_schema fields defined by the extension developer (rendered as a form in the dashboard)
  • Trigger overrides - account owners can customize command prefixes, keyword lists, and regex patterns without modifying the extension code
  • Exempt roles - the same moderator/VIP/subscriber exemption system used by built-in modules

Configuration Validation​

Config values set by users (through the dashboard settings panel or API) are validated against the extension's declared schema. Invalid values (wrong type, missing required fields, unknown settings) are rejected with clear error messages. This ensures extensions always receive correctly typed configuration.

Kill Switch​

Account owners can immediately stop all extension bot modules of their account with the kill switch. The kill switch:

  1. Stops extension triggers for the account instantly (no restart required) - commands, keywords, patterns, moderation, event handlers, timers and any pending actions
  2. Is stored per account, so a restarted bot starts with the switch still set, and stays in force when the bot reloads the account's triggers (an install change, a channel the bot joins later, a bot failover)
  3. Preserves every install's configuration, so turning it off resumes the modules as they were
  4. Also refuses every handler server function (query() / mutation() / action()) of the account's extensions with 403 EXTENSIONS_KILLED, since those run on the same worker; declarative server functions (queryRows, insertRow, …) keep working

It is set with POST /v1/bot-modules/kill ({ "account_id", "killed" }) or the GraphQL mutation setBotModuleKillSwitch(accountId, killed). Both require bot-modules:edit, only act on the caller's own account and write the audit event account:extension_kill_switch_set. To stop a single module, disable its install instead.

Disabled installs and installs of an extension that Lumio has suspended never reach the bots: the trigger feed leaves them out, and the Bot Module Worker drops their handlers on its next sync.

Trigger Types​

Triggers are declared, not registered in code: the extension's lumio.config.json carries a triggers object whose keys are the trigger types below. Each entry names the handler the Bot Module Worker dispatches to.

triggers keyDescriptionLimits
commandsMatches a chat command (e.g. !rank). Each entry carries name, description, cooldown_global, cooldown_user, min_role, and optional discord_options.max 20
keywordsMatches any of a list of keywords in message textmax 50, min 3 chars each
patternsMatches a regex pattern against message textmax 10, max 200 chars each
eventsFires on a stream event (e.g. twitch:follower); the module's event() handler runs-
timersFires on a recurring interval - name, interval (seconds), handler. The older form timer: true runs one timer named timer at the install's intervalSeconds setting (default 300 s, clamped to 60 s - 24 h)min interval 60 s; shorter entries are ignored

min_role accepts everyone, follower, subscriber, vip, moderator, or broadcaster. Command triggers are access-controlled exactly like dynamic commands: before a matched command reaches the Bot Module Worker, the bot checks the sender's role against min_role and takes the cooldown_global / cooldown_user slots (shared gate lo_bot_commands::access; Redis keys lumio:cmd_cd:{account_id}:ext:{install_id}:{name}:…). A sender below min_role, or a command still cooling down, is ignored silently. The worker re-checks min_role before it runs a command handler and refuses a handler whose minimum it does not know. The effective values are the manifest defaults with the install's config.triggers.commands[] overrides on top. The command fields are snake_case as documented (min_role, cooldown_global, cooldown_user); the camelCase spelling of older CLI templates (minRole, …) is still read.

Events​

A module that lists event types in triggers.events has its event() handler run once for every matching stream event of the account (followers, subs, raids, donations, …). The handler receives the same event payload widgets receive. A reply goes to the chat of the platform the event came from (Twitch, Kick or YouTube); Discord events have no chat channel, so a reply to them is dropped.

Viewer input​

Viewers interact with an extension only through chat commands (!vote 2 runs the module's command("vote", …) handler) and channel point rewards on Twitch and Kick (twitch:reward / kick:reward run its event() handlers). There is no viewer page and no viewer identity. A handler can store the input with ctx.db and push it with ctx.realtime.push(topic, payload) to the extension's editor panel and overlay layer, which receive it with useRealtime(topic, cb) over the WebSocket channel extension_install:{install_id}. Event handlers run at most 50 times per install and minute (shared with timers). Details: Bot Modules (developer guide).

Timers​

Each declared timer runs once per interval, no matter how many Bot Module Worker replicas are running. A missed interval (for example during a restart) is skipped, not caught up. Changing a timer's interval, disabling the install or setting the kill switch takes effect right away. A timer's reply goes to the chat of every platform the install runs on, except Discord.

Only while live. An install whose configuration sets onlyWhenLive: true (the Timed Messages module offers this setting) runs its timer only while at least one of its chat platforms is live, and posts only on the platforms that are live.

Moderation​

A module that exports a moderate handler and declares triggers.moderation: true sees every chat message of the account on Twitch, Kick and YouTube and returns a decision:

DecisionWhat happensPermission the module must declare
allownothing-
deletethe message is deletedchat:delete
timeoutthe sender is timed out for the decision's duration (default 600 s)chat:timeout
banthe sender is bannedchat:ban
warnthe decision's message (or its reason) is posted to chatchat:send

Moderation never slows chat down: each check has 500 ms, and a check that times out, fails or arrives while the account already has 16 checks running lets the message through. Messages from the broadcaster and moderators are not moderated. On Discord moderate handlers are not called, because none of the chat actions supports Discord.

Actions​

Besides a chat reply, a handler can return actions - the same actions a widget can run (for example chat:timeout, obs:set_scene, spotify:skip). They run after the reply, at most once each (a failed action is not retried), and pass the same checks as widget actions:

  • the module's manifest must declare the action name as a permission;
  • the account's plan must include the action's feature (chat:* → multichat, obs:* → OBS remote, spotify:* → music);
  • the parameters must be valid.

Each install may run at most 120 actions per minute (operator setting [extensions] bot_module_action_rate_per_minute); actions beyond that are refused until the next minute. Every outcome - success, refusal, failure, including moderation decisions - appears in the install's logs in the dashboard, at most one entry per second; an allow decision writes nothing. Log entries carry the action name and, for chat actions, the platform and target ids - never message text.

Bans, unbans and timeouts a module issues are recorded in the account's audit log as account:extension_moderation with source bot_module. Message deletions by a module appear only in the install log.

Permissions​

Extension bot modules use the same permission system as other extensions: an action a handler returns runs only when the manifest declares it (see Actions). The chat:ban permission requires enhanced review during the extension approval process due to its impact on user experience.

Feature gating​

Extension bot modules are gated by the extension-platform feature flags rather than by a plan constant. Installing and running them requires the account to have the extension platform enabled; see Extensions.

Key Files​

PathDescription
apps/bot-module-worker/Bot Module Worker HTTP service (binary)
crates/lo-bot-module-worker/src/router.rsTrigger matching and dispatch logic
crates/lo-bot-module-worker/src/executor.rsHandler execution and result handling
crates/lo-bot-module-worker/src/types.rsInstallTriggerConfig, CommandTrigger, KeywordTrigger, PatternTrigger
crates/lo-extensions-v8/src/executor.rsV8 isolate execution for extension handlers
apps/api/src/routes/bot_module_extensions.rsTrigger sync, kill switch, and extension log/metric endpoints