Skip to content

Configuration

BotRS provides flexible configuration options to customize your bot's behavior, authentication, and runtime settings. This guide covers all available configuration methods and best practices.

Authentication Configuration

Token Setup

The most important configuration is your bot's authentication credentials:

rust
use botrs::Token;

// Basic token creation
let token = Token::new("your_app_id", "your_secret");

// Validate token before use
if let Err(e) = token.validate() {
    eprintln!("Invalid token: {}", e);
    std::process::exit(1);
}

Environment Variables

The recommended approach for managing credentials is using environment variables:

bash
# Required credentials
export QQ_BOT_APP_ID="your_app_id_here"
export QQ_BOT_SECRET="your_secret_here"

# Optional settings
export QQ_BOT_SANDBOX="false"
export RUST_LOG="botrs=info,my_bot=debug"

Load them in your application:

rust
use std::env;

let app_id = env::var("QQ_BOT_APP_ID")
    .expect("QQ_BOT_APP_ID environment variable not set");
let secret = env::var("QQ_BOT_SECRET")
    .expect("QQ_BOT_SECRET environment variable not set");

let token = Token::new(app_id, secret);

Intent Configuration

Intents control which events your bot receives. Configure them based on your bot's functionality:

Basic Intents

rust
use botrs::Intents;

// Minimal setup - only guild messages
let intents = Intents::default()
    .with_public_guild_messages();

// Common setup - messages and guild events
let intents = Intents::default()
    .with_public_guild_messages()
    .with_guilds();

// Full feature bot
let intents = Intents::default()
    .with_public_guild_messages()
    .with_direct_message()
    .with_guilds()
    .with_guild_members()
    .with_guild_messages()
    .with_guild_message_reactions();

Privileged Intents

Some intents require special permissions:

rust
// These may require approval from QQ
let privileged_intents = Intents::default()
    .with_guild_members()      // Member information
    .with_guild_presences()    // Presence updates
    .with_message_content();   // Full message content access

Client Configuration

Basic Client Setup

rust
use botrs::{Client, Intents, Token};

let token = Token::new("app_id", "secret");
let intents = Intents::default().with_public_guild_messages();

// Create client with sandbox mode
let client = Client::new(token, intents, handler, true)?;  // true = sandbox

// Create client for production
let client = Client::new(token, intents, handler, false)?; // false = production

Advanced Configuration

rust
use botrs::{Client, ClientConfig, HttpConfig};

let http_config = HttpConfig::new()
    .timeout(std::time::Duration::from_secs(60))
    .user_agent("MyBot/1.0")
    .max_retries(3);

let client_config = ClientConfig::new()
    .http_config(http_config)
    .reconnect_attempts(5)
    .heartbeat_interval(std::time::Duration::from_secs(30));

// Note: This is conceptual - actual implementation may vary

Environment Configuration

Development Environment

Create a .env file for development:

bash
# .env file
QQ_BOT_APP_ID=your_development_app_id
QQ_BOT_SECRET=your_development_secret
QQ_BOT_SANDBOX=true
RUST_LOG=botrs=debug,my_bot=trace
BOT_PREFIX=!
WELCOME_CHANNEL_ID=channel_123456

Load using the dotenvy crate:

rust
use dotenvy::dotenv;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load .env file if it exists
    dotenv().ok();

    let token = Token::new(
        std::env::var("QQ_BOT_APP_ID")?,
        std::env::var("QQ_BOT_SECRET")?,
    );

    // Use sandbox mode from environment
    let use_sandbox = std::env::var("QQ_BOT_SANDBOX")
        .unwrap_or_else(|_| "false".to_string())
        .parse::<bool>()
        .unwrap_or(false);

    // Rest of your bot setup...
    Ok(())
}

Production Environment

For production, use secure methods to inject environment variables:

bash
# Docker
docker run -e QQ_BOT_APP_ID=xxx -e QQ_BOT_SECRET=yyy my-bot

# Kubernetes
apiVersion: v1
kind: Secret
metadata:
  name: bot-secrets
data:
  app-id: <base64-encoded-app-id>
  secret: <base64-encoded-secret>

Configuration Files

TOML Configuration

Create a structured configuration system:

toml
# config.toml
[bot]
app_id = "your_app_id"
secret = "your_secret"
sandbox = false
command_prefix = "!"

[features]
auto_reconnect = true
max_reconnect_attempts = 5
heartbeat_interval = 30

[channels]
welcome_channel = "channel_123456"
log_channel = "channel_789012"
admin_channel = "channel_345678"

[commands]
enabled = ["ping", "help", "info"]
admin_only = ["reload", "shutdown"]

[logging]
level = "info"
file_output = true
console_output = true

Load and use the configuration:

rust
use serde::{Deserialize, Serialize};
use std::fs;

#[derive(Debug, Deserialize, Serialize)]
struct Config {
    bot: BotConfig,
    features: FeatureConfig,
    channels: ChannelConfig,
    commands: CommandConfig,
    logging: LoggingConfig,
}

#[derive(Debug, Deserialize, Serialize)]
struct BotConfig {
    app_id: String,
    secret: String,
    sandbox: bool,
    command_prefix: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct FeatureConfig {
    auto_reconnect: bool,
    max_reconnect_attempts: u32,
    heartbeat_interval: u64,
}

#[derive(Debug, Deserialize, Serialize)]
struct ChannelConfig {
    welcome_channel: Option<String>,
    log_channel: Option<String>,
    admin_channel: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
struct CommandConfig {
    enabled: Vec<String>,
    admin_only: Vec<String>,
}

#[derive(Debug, Deserialize, Serialize)]
struct LoggingConfig {
    level: String,
    file_output: bool,
    console_output: bool,
}

fn load_config() -> Result<Config, Box<dyn std::error::Error>> {
    let config_content = fs::read_to_string("config.toml")?;
    let mut config: Config = toml::from_str(&config_content)?;

    // Override with environment variables
    if let Ok(app_id) = std::env::var("QQ_BOT_APP_ID") {
        config.bot.app_id = app_id;
    }
    if let Ok(secret) = std::env::var("QQ_BOT_SECRET") {
        config.bot.secret = secret;
    }

    Ok(config)
}

JSON Configuration

Alternatively, use JSON for configuration:

json
{
  "bot": {
    "app_id": "your_app_id",
    "secret": "your_secret",
    "sandbox": false,
    "command_prefix": "!"
  },
  "intents": {
    "public_guild_messages": true,
    "direct_message": true,
    "guilds": true,
    "guild_members": false
  },
  "features": {
    "auto_reconnect": true,
    "max_reconnect_attempts": 5
  }
}

Logging Configuration

Basic Logging Setup

rust
use tracing_subscriber::{fmt, EnvFilter};

// Simple console logging
tracing_subscriber::fmt()
    .with_env_filter("botrs=info,my_bot=debug")
    .init();

// More detailed configuration
tracing_subscriber::fmt()
    .with_env_filter(EnvFilter::from_default_env())
    .with_target(false)
    .with_thread_ids(true)
    .with_level(true)
    .with_file(true)
    .with_line_number(true)
    .init();

File Logging

rust
use tracing_appender::{non_blocking, rolling};
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};

let file_appender = rolling::daily("logs", "bot.log");
let (non_blocking, _guard) = non_blocking(file_appender);

tracing_subscriber::registry()
    .with(fmt::layer().with_writer(std::io::stdout))
    .with(fmt::layer().with_writer(non_blocking).with_ansi(false))
    .with(EnvFilter::from_default_env())
    .init();

Network Configuration

HTTP Client Settings

rust
use std::time::Duration;

// Custom timeout and user agent
let http_client = reqwest::Client::builder()
    .timeout(Duration::from_secs(30))
    .user_agent("MyBot/1.0.0")
    .build()?;

// Use with BotApi if supported
// Note: Actual implementation may vary

Proxy Configuration

rust
// For environments requiring proxy
let proxy = reqwest::Proxy::http("http://proxy.example.com:8080")?;
let client = reqwest::Client::builder()
    .proxy(proxy)
    .build()?;

Runtime Configuration

Graceful Shutdown

rust
use tokio::signal;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = create_client().await?;

    // Handle shutdown signals
    tokio::select! {
        result = client.start() => {
            if let Err(e) = result {
                eprintln!("Client error: {}", e);
            }
        }
        _ = signal::ctrl_c() => {
            println!("Received shutdown signal");
            client.stop().await?;
        }
    }

    Ok(())
}

Resource Limits

rust
// Set stack size for threads
std::thread::Builder::new()
    .stack_size(8 * 1024 * 1024)  // 8MB stack
    .spawn(|| {
        // Heavy computation
    })?;

// Memory limits (conceptual)
tokio::runtime::Builder::new_multi_thread()
    .worker_threads(4)
    .max_blocking_threads(8)
    .enable_all()
    .build()?
    .block_on(async {
        // Your bot logic
    });

Configuration Validation

Input Validation

rust
impl Config {
    fn validate(&self) -> Result<(), ConfigError> {
        if self.bot.app_id.is_empty() {
            return Err(ConfigError::MissingAppId);
        }

        if self.bot.secret.is_empty() {
            return Err(ConfigError::MissingSecret);
        }

        if self.features.max_reconnect_attempts > 10 {
            return Err(ConfigError::InvalidReconnectAttempts);
        }

        Ok(())
    }
}

#[derive(Debug, thiserror::Error)]
enum ConfigError {
    #[error("App ID is required")]
    MissingAppId,
    #[error("Secret is required")]
    MissingSecret,
    #[error("Max reconnect attempts must be <= 10")]
    InvalidReconnectAttempts,
}

Environment Detection

rust
fn detect_environment() -> Environment {
    if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
        Environment::Kubernetes
    } else if std::env::var("DYNO").is_ok() {
        Environment::Heroku
    } else if std::env::var("AWS_LAMBDA_FUNCTION_NAME").is_ok() {
        Environment::Lambda
    } else {
        Environment::Local
    }
}

enum Environment {
    Local,
    Kubernetes,
    Heroku,
    Lambda,
}

Best Practices

Security

  1. Never hardcode credentials in source code
  2. Use environment variables for sensitive data
  3. Rotate credentials regularly
  4. Use least privilege intents
  5. Validate all input from configuration files

Performance

  1. Configure appropriate timeouts for your use case
  2. Use connection pooling for HTTP clients
  3. Set reasonable retry limits to avoid infinite loops
  4. Monitor resource usage in production

Maintainability

  1. Use structured configuration files
  2. Document all configuration options
  3. Provide sensible defaults
  4. Support environment-specific overrides
  5. Validate configuration at startup

Example: Complete Configuration Setup

rust
use botrs::{Client, EventHandler, Intents, Token};
use serde::{Deserialize, Serialize};
use std::fs;
use tracing::{info, warn};

#[derive(Debug, Deserialize, Serialize, Clone)]
struct AppConfig {
    bot: BotSettings,
    intents: IntentSettings,
    logging: LoggingSettings,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
struct BotSettings {
    app_id: String,
    secret: String,
    sandbox: bool,
    command_prefix: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
struct IntentSettings {
    public_guild_messages: bool,
    direct_message: bool,
    guilds: bool,
    guild_members: bool,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
struct LoggingSettings {
    level: String,
    file_output: bool,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            bot: BotSettings {
                app_id: String::new(),
                secret: String::new(),
                sandbox: true,
                command_prefix: "!".to_string(),
            },
            intents: IntentSettings {
                public_guild_messages: true,
                direct_message: false,
                guilds: true,
                guild_members: false,
            },
            logging: LoggingSettings {
                level: "info".to_string(),
                file_output: false,
            },
        }
    }
}

fn load_config() -> Result<AppConfig, Box<dyn std::error::Error>> {
    let mut config = if std::path::Path::new("config.toml").exists() {
        let content = fs::read_to_string("config.toml")?;
        toml::from_str(&content)?
    } else {
        AppConfig::default()
    };

    // Override with environment variables
    if let Ok(app_id) = std::env::var("QQ_BOT_APP_ID") {
        config.bot.app_id = app_id;
    }
    if let Ok(secret) = std::env::var("QQ_BOT_SECRET") {
        config.bot.secret = secret;
    }

    // Validate
    if config.bot.app_id.is_empty() {
        return Err("App ID is required".into());
    }
    if config.bot.secret.is_empty() {
        return Err("Secret is required".into());
    }

    Ok(config)
}

fn setup_logging(config: &LoggingSettings) -> Result<(), Box<dyn std::error::Error>> {
    let filter = format!("botrs={},my_bot={}", config.level, config.level);

    if config.file_output {
        // Setup file logging
        let file_appender = tracing_appender::rolling::daily("logs", "bot.log");
        let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);

        tracing_subscriber::fmt()
            .with_env_filter(filter)
            .with_writer(non_blocking)
            .init();
    } else {
        tracing_subscriber::fmt()
            .with_env_filter(filter)
            .init();
    }

    Ok(())
}

fn build_intents(config: &IntentSettings) -> Intents {
    let mut intents = Intents::default();

    if config.public_guild_messages {
        intents = intents.with_public_guild_messages();
    }
    if config.direct_message {
        intents = intents.with_direct_message();
    }
    if config.guilds {
        intents = intents.with_guilds();
    }
    if config.guild_members {
        intents = intents.with_guild_members();
    }

    intents
}

struct MyBot {
    config: AppConfig,
}

#[async_trait::async_trait]
impl EventHandler for MyBot {
    async fn ready(&self, _ctx: botrs::Context, ready: botrs::Ready) {
        info!("Bot ready with config: sandbox={}", self.config.bot.sandbox);
    }

    // Other event handlers...
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = load_config()?;
    setup_logging(&config.logging)?;

    info!("Starting bot with configuration");

    let token = Token::new(&config.bot.app_id, &config.bot.secret);
    let intents = build_intents(&config.intents);
    let handler = MyBot { config: config.clone() };

    let mut client = Client::new(token, intents, handler, config.bot.sandbox)?;
    client.start().await?;

    Ok(())
}

This comprehensive configuration setup provides flexibility, security, and maintainability for your BotRS applications.

Released under the MIT License.