// Daemon configuration. // // Lives at ~/.consciousness/daemon.toml. Loaded on startup, updated at // runtime when modules change state (join channel, etc.). use crate::home; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; fn config_path() -> PathBuf { home().join(".consciousness/daemon.toml") } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Config { #[serde(default)] pub irc: IrcConfig, #[serde(default)] pub telegram: TelegramConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct IrcConfig { pub enabled: bool, pub server: String, pub port: u16, pub tls: bool, pub nick: String, pub user: String, pub realname: String, pub channels: Vec, } impl Default for IrcConfig { fn default() -> Self { Self { enabled: true, server: "irc.libera.chat".into(), port: 6697, tls: true, nick: "ProofOfConcept".into(), user: "poc".into(), realname: "ProofOfConcept".into(), channels: vec!["#bcachefs".into(), "#bcachefs-ai".into()], } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TelegramConfig { pub enabled: bool, pub token: String, pub chat_id: i64, } impl Default for TelegramConfig { fn default() -> Self { // Load token and chat_id from legacy files if they exist let token = std::fs::read_to_string(home().join(".consciousness/telegram/token")) .map(|s| s.trim().to_string()) .unwrap_or_default(); let chat_id = std::fs::read_to_string(home().join(".consciousness/telegram/chat_id")) .ok() .and_then(|s| s.trim().parse().ok()) .unwrap_or(0); Self { enabled: !token.is_empty() && chat_id != 0, token, chat_id, } } } impl Config { pub fn load() -> Self { let path = config_path(); match fs::read_to_string(&path) { Ok(data) => toml::from_str(&data).unwrap_or_else(|e| { tracing::warn!("bad config {}: {e}, using defaults", path.display()); Self::default() }), Err(_) => { let config = Self::default(); config.save(); config } } } pub fn save(&self) { let path = config_path(); if let Ok(data) = toml::to_string_pretty(self) { let _ = fs::write(path, data); } } }