Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Instance Config (config.toml)

config.toml describes a bot instance’s identity, command surface, and feature configuration. Every field on this page comes from the InstanceConfig, Features, AutoRoleConfig, JoinRoleConfig, WelcomeConfig, MinecraftConfig, DonatorSyncConfig, and ChargebackConfig structs in src/instance_config.rs. If you find a field in the source that isn’t documented here, please open an issue.

The file is read once at startup from CONFIG_DIR/config.toml (where CONFIG_DIR defaults to the working directory and is /config inside the Docker image). Parse failures panic with a path and the underlying TOML error — you’ll see them immediately when the container starts.

Top-level fields

FieldTypeRequiredDefaultDescription
bot_namestringyesDisplay name shown in help output and logs
command_prefixstringyesPrefix for text-based commands
command_rootstringno"m"Parent command name; "" for flat commands
personality_filestringno"personality.txt"Path to the personality file, relative to config.toml
timezonestringno(UTC)IANA timezone for the AI system prompt’s date/time line

bot_name

The human-readable name of this instance. It is used in help output, in welcome messages where templates reference it, and in startup log lines so you can tell which instance you’re looking at when you tail several at once.

command_prefix

The prefix the bot listens for on text commands. The repo ships with "!" in the example, but you can use anything that won’t collide with normal chat. discord-bot-rs uses prefix commands only — there are no slash commands — so this setting controls how users invoke every command.

command_root

The name of the parent command that wraps every subcommand. Default "m" gives users the historical form <prefix>m <subcommand> (e.g. !m play). Three modes:

ValueEffect
"m" (default)!m play, !m skip, … (current behaviour)
"bot" etc.!bot play, !bot skip, …
"" (empty)!play, !skip, … (flat — no parent)

The renamed-parent mode is useful when you run multiple bot instances in the same Discord guild — give each one a distinct command_root so users can address them unambiguously even when they share command_prefix. The flat mode is useful for single-bot servers where the prefix alone is enough discriminator.

Validation: must be a single token. Whitespace is rejected at startup with a clear error pointing back at this field.

The bot pre-renders the full command-invocation string (<prefix><root> for the rename case, <prefix> for the flat case) and uses it everywhere the help output prints example commands, so the help embed and the bot’s Discord activity status (Playing <prefix>help or @ me) both stay consistent across all three modes.

Note: the rest of these docs (and the README) use !m play / !m skip / etc. as their command examples, since ! is the default command_prefix and m is the default command_root. If you’ve customized either field, mentally substitute your values when reading them — !m play<your-prefix><your-root> play. The bot itself always shows the right form at runtime, so this only affects reading the docs.

personality_file

The filename (relative to the same directory as config.toml) where the AI system prompt lives. Defaults to personality.txt. The file must exist and must not be empty when AI chat is active — the loader panics if either condition fails. See Personality Files for how to write one.

timezone

Optional IANA timezone name (for example "America/Toronto", "Europe/London", "Asia/Tokyo") used to build the “current date / time” line in the AI system prompt. When this field is set, the bot formats the line as the local date, local clock time, IANA zone, and numeric UTC offset — for example:

Today is Friday, April 17, 2026. Current local time: 10:52 PM (America/Toronto, UTC-04:00).

When the field is absent, the bot falls back to UTC and explicitly labels the line as UTC so the AI model doesn’t guess at a timezone. The bare-UTC fallback is safe for non-chat workloads but is typically wrong for conversation: at 10:52 PM local in most of the Americas, UTC has already rolled over to the next day, and without a timezone label the model will happily report “today” as tomorrow’s date (and sometimes invent a plausible-sounding city to justify it).

Accepted values are any zone name chrono-tz can parse. Prefer full IANA zone names like "America/New_York", "America/Los_Angeles", or "Europe/Paris" over bare abbreviations like "EST", "PST", or "CET" — the abbreviations parse as fixed offsets with no daylight saving, so a config that says "EST" will report the wrong time for half the year. A value chrono-tz can’t parse makes the bot panic at startup with the offending string, so misconfiguration fails loudly rather than silently defaulting.

[features] section

The features table holds the master feature flags. Every flag defaults to false, so an empty [features] section (or no section at all) means a stripped-down bot that only does AI chat, music, games, and the always-on commands.

FieldTypeDefaultGates
minecraftboolfalseThe Minecraft module (verify, donator_sync, chargeback)
auto_roleboolfalsePeriodic auto-role promotion based on member age and activity
join_roleboolfalseAssigning a role to every new member on join
welcomeboolfalseAI-generated welcome messages on member join

Setting a flag to false disables the entire feature area; the corresponding sub-section ([auto_role], [minecraft], etc.) is not required and will be ignored if present. Setting a flag to true activates the loader for the matching sub-section. If the sub-section is missing the bot logs a warning at startup and disables the feature anyway, rather than panicking — see Validation below.

[auto_role] section

Required when features.auto_role = true. The auto-role module periodically scans members who currently have from_role and grants them to_role (and removes from_role) once they meet the configured criteria. The first scan runs at bot startup; subsequent scans run on a fixed schedule.

FieldTypeRequiredDefaultDescription
from_rolestringyesSnowflake of the role to remove
to_rolestringyesSnowflake of the role to grant
min_agestringno"3d"Minimum time in the guild before promotion
min_messagesintno20Minimum messages sent in the guild before promotion
require_allboolnofalseIf true, both criteria must hold; if false, either

Duration format for min_age

min_age is a short duration string parsed by parse_duration in src/util/duration.rs. The grammar is <integer><unit> with no spaces, where unit is one of:

SuffixUnitExample
sseconds30s
mminutes15m
hhours2h
ddays3d (the default)
wweeks1w

Combined units (1d12h) are not supported — pick the largest unit that expresses what you want. The maximum allowed duration is 365 days; anything longer is rejected.

How the criteria combine

With require_all = false (the default), a member is promoted as soon as either condition holds: they’ve been in the guild long enough, or they’ve sent enough messages. With require_all = true, both must hold. Newly added IDs and recent message counts are picked up between scans, so the lag between meeting the threshold and getting promoted is bounded by the scan interval.

See Auto-Role for behavioral details.

[join_role] section

Required when features.join_role = true. Assigns a single role to every new member on join. Most often used to mark unverified accounts that haven’t gone through whatever onboarding flow your server has set up.

FieldTypeRequiredDefaultDescription
rolestringyesSnowflake of the role to grant

See Join Features.

[welcome] section

Required when features.welcome = true. Posts an AI-generated welcome message to a designated channel when a new member joins. The bot needs at least one AI provider key (DEEPSEEK_API_KEY or GEMINI_API_KEY); without one, it logs a warning at startup and the feature stays inactive.

FieldTypeRequiredDefaultDescription
channelstringyesSnowflake of the channel to post into
prompt_filestringno"welcome_prompt.txt"File (relative to config.toml) holding the welcome prompt template

The prompt file is loaded by load_welcome_prompt in src/instance_config.rs; if it’s missing or empty the bot logs a warning and welcome stays off. See Join Features.

[minecraft] section

Required when features.minecraft = true. The Minecraft module is itself a bundle of three independently toggleable sub-features. All three depend on the bot being able to talk to a companion plugin on the Minecraft server, which means MC_VERIFY_URL and MC_VERIFY_SECRET must be set in the instance’s .env.

FieldTypeRequiredDefaultDescription
verifyboolnotrueEnable the !m verify linking command
donator_syncboolnofalsePoll the MC server and sync donator-tier roles
chargebackboolnofalseReceive chargeback alerts from the MC store

verify defaults to true so that the most common case (you want player-account linking) needs no extra configuration beyond enabling the module. donator_sync and chargeback default to false and need their own sub-sections (below) before they do anything useful.

See Minecraft Verify, Minecraft Donator Sync, and Minecraft Chargeback.

[minecraft.donator_sync_config] section

Required when minecraft.donator_sync = true. Polls the Minecraft server on an interval and synchronizes Discord roles to match each user’s purchased tier.

FieldTypeRequiredDefaultDescription
supporter_rolestringyesSnowflake of the supporter-tier role
premium_rolestringyesSnowflake of the premium-tier role
check_intervalintno300Seconds between donator-tier checks

The default 300-second interval is conservative enough to avoid spamming the MC server while keeping role state reasonably fresh. See Minecraft Donator Sync.

[minecraft.chargeback_config] section

Required when minecraft.chargeback = true. The bot exposes an HTTP endpoint that receives chargeback notifications from the MC store; on receipt it strips the user’s roles, applies a restricted role, and posts an interactive alert to a staff channel with Ban and Dismiss buttons.

FieldTypeRequiredDefaultDescription
staff_channelstringyesSnowflake of the channel to post alerts in
restricted_rolestringyesSnowflake of the role to apply to offenders
staff_roleslist of stringno[]Snowflakes allowed to press Ban/Dismiss buttons. Empty list means no role can act on alerts (the buttons reply “You don’t have permission to do this.” for everyone), so configure this if you want staff to be able to confirm or dismiss chargebacks.

See Minecraft Chargeback.

Validation

At startup the loader inspects each enabled feature flag and tries to find its sub-section:

  • TOML parse errors panic with the file path and the parser’s error message. Fix the syntax and restart.
  • Missing bot_name or command_prefix is a TOML parse error (they’re required fields with no default), so they fall into the same case.
  • Missing personality file when AI chat is implicitly active panics with the file path. An empty file panics with a different message asking you to fill it in.
  • features.minecraft = true with no [minecraft] section logs a warning and disables the Minecraft module. The bot still starts.
  • features.auto_role = true with no [auto_role] section logs a warning and disables auto-role. The bot still starts.
  • features.join_role = true with no [join_role] section logs a warning and disables join-role. The bot still starts.
  • features.welcome = true with no [welcome] section, no prompt file, or an empty prompt file logs a warning and disables welcomes. The bot still starts.
  • minecraft.donator_sync = true with no [minecraft.donator_sync_config] logs a warning and disables donator sync.
  • minecraft.chargeback = true with no [minecraft.chargeback_config] logs a warning and disables chargeback.

Validation strategy is “loud warnings, soft fail”: misconfiguration of optional features doesn’t crash the bot, but it does show up clearly in the logs. Run with RUST_LOG=info,discord_bot=info (or similar) to see the per-module enable/disable lines on startup.

AI Provider Configuration ([ai.providers] and [ai.routing])

Define custom AI providers and override role routing per-instance. Both sections are optional — when absent, the bot uses a baked-in default registry equivalent to all releases prior to 0.15.0.

See AI Providers for the full schema reference, default registry contents, validation rules, and worked examples (one-model setup, three-provider setup with cascade, overriding a default).

Complete annotated example

The example instance ships with this config.toml (also at instances/example/config.toml in the repo):

# ============================================================================
# discord-bot-rs — Example Instance Configuration
# ============================================================================
#
# This file is the authoritative reference for every config.toml option.
# Copy this whole directory to create a new instance:
#
#     cp -r instances/example instances/mybot
#
# Then edit this file, instances/mybot/.env, and instances/mybot/personality.txt.
# ============================================================================


# ----- Identity -------------------------------------------------------------

# Display name shown in bot output (help text, welcome messages, etc.)
bot_name = "Example Bot"

# Prefix for all user commands. discord-bot-rs uses prefix commands only
# (no slash commands), so this is the single prefix every user sees.
command_prefix = "!"

# Path to the personality file (system prompt for AI chat), relative to this
# config file. Default: "personality.txt"
personality_file = "personality.txt"


# ----- Feature Flags --------------------------------------------------------
#
# Each flag gates a whole feature area. Setting to false disables the feature
# entirely — the corresponding config section below is not required.

[features]
minecraft = false     # Minecraft verification + donator sync + chargeback alerts
auto_role = false     # Automatic role promotion based on activity
join_role = false     # Assign a role to new members on join
welcome = false       # AI-generated welcome message on join


# ----- Auto-Role Promotion --------------------------------------------------
#
# Required when features.auto_role = true.
# Promotes members from `from_role` to `to_role` once they meet activity
# criteria. Runs periodically; the first pass happens at bot startup.

# [auto_role]
# from_role = "ROLE_ID"       # Snowflake of the role to remove
# to_role = "ROLE_ID"         # Snowflake of the role to grant
# min_age = "3d"              # Time in guild before eligible (e.g. "1h", "3d", "1w")
# min_messages = 20           # Messages sent in the guild before eligible
# require_all = false         # true = both criteria required, false = either


# ----- Join Role ------------------------------------------------------------
#
# Required when features.join_role = true.

# [join_role]
# role = "ROLE_ID"            # Snowflake of the role to grant on join


# ----- Welcome Messages -----------------------------------------------------
#
# Required when features.welcome = true.

# [welcome]
# channel = "CHANNEL_ID"
# prompt_file = "welcome_prompt.txt"   # Relative to this config file


# ----- Minecraft Module -----------------------------------------------------
#
# Required when features.minecraft = true.
# Any sub-feature can be independently toggled.
# MC_VERIFY_URL and MC_VERIFY_SECRET in .env are required for all sub-features.

# [minecraft]
# verify = true               # !m verify command (default: true)
# donator_sync = false        # Poll MC server for donator tier role sync
# chargeback = false          # Webhook listener for chargeback alerts


# ----- Donator Sync ---------------------------------------------------------
#
# Required when minecraft.donator_sync = true.

# [minecraft.donator_sync_config]
# supporter_role = "ROLE_ID"
# premium_role = "ROLE_ID"
# check_interval = 300        # Poll interval in seconds (default: 300)


# ----- Chargeback Alerts ----------------------------------------------------
#
# Required when minecraft.chargeback = true.

# [minecraft.chargeback_config]
# staff_channel = "CHANNEL_ID"
# restricted_role = "ROLE_ID"
# staff_roles = ["MOD_ROLE_ID", "ADMIN_ROLE_ID", "OWNER_ROLE_ID"]   # Snowflakes allowed to press Ban/Dismiss; empty = no one

To turn any feature on, flip its flag in [features] and uncomment the matching sub-section, replacing ROLE_ID and CHANNEL_ID placeholders with real Discord snowflakes (right-click → Copy ID with Developer Mode enabled).