Skip to main content

streamocracy/
config.rs

1//! Configuration management for Streamocracy
2//!
3//! Loads configuration from environment variables.
4//! A `.env` file can be used for local development.
5
6use anyhow::Context;
7use serenity::all::GuildId;
8
9/// Bot configuration loaded from environment variables.
10#[derive(Debug, Clone)]
11pub struct Config {
12    /// Discord bot token (required)
13    pub discord_token: String,
14    /// Optional guild ID for instant command registration
15    pub guild_id: Option<u64>,
16    /// Log level filter (default: info)
17    pub log_level: String,
18    /// Default votekick duration in seconds (default: 60)
19    pub default_votekick_duration: u64,
20    /// Minimum votekick duration in seconds (default: 10)
21    pub min_votekick_duration: u64,
22    /// Maximum votekick duration in seconds (default: 300)
23    pub max_votekick_duration: u64,
24}
25
26impl Config {
27    /// Load configuration from environment variables.
28    pub fn from_env() -> anyhow::Result<Self> {
29        let discord_token = std::env::var("DISCORD_TOKEN")
30            .context("DISCORD_TOKEN environment variable is required")?;
31
32        let guild_id = std::env::var("GUILD_ID").ok().and_then(|v| v.parse().ok());
33
34        let log_level = std::env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string());
35
36        let default_votekick_duration = std::env::var("DEFAULT_VOTEKICK_DURATION")
37            .ok()
38            .and_then(|v| v.parse().ok())
39            .unwrap_or(60);
40
41        let min_votekick_duration = std::env::var("MIN_VOTEKICK_DURATION")
42            .ok()
43            .and_then(|v| v.parse().ok())
44            .unwrap_or(10);
45
46        let max_votekick_duration = std::env::var("MAX_VOTEKICK_DURATION")
47            .ok()
48            .and_then(|v| v.parse().ok())
49            .unwrap_or(300);
50
51        Ok(Self {
52            discord_token,
53            guild_id,
54            log_level,
55            default_votekick_duration,
56            min_votekick_duration,
57            max_votekick_duration,
58        })
59    }
60
61    /// Get the guild ID as an Option<serenity::all::GuildId>
62    pub fn guild_id(&self) -> Option<GuildId> {
63        self.guild_id.map(GuildId::new)
64    }
65}
66
67/// Load config and set up logging
68pub fn init() -> anyhow::Result<Config> {
69    let config = Config::from_env()?;
70    let subscriber = tracing_subscriber::fmt()
71        .with_env_filter(
72            tracing_subscriber::EnvFilter::try_from_default_env()
73                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(&config.log_level)),
74        )
75        .finish();
76
77    tracing::subscriber::set_global_default(subscriber)
78        .context("Failed to set global default subscriber")?;
79
80    Ok(config)
81}