136 lines
4.1 KiB
Rust
136 lines
4.1 KiB
Rust
|
|
use serde::Deserialize;
|
||
|
|
use std::path::Path;
|
||
|
|
|
||
|
|
/// Configuration for the Matrix bot, read from `.story_kit/bot.toml`.
|
||
|
|
#[derive(Deserialize, Clone, Debug)]
|
||
|
|
pub struct BotConfig {
|
||
|
|
/// Matrix homeserver URL, e.g. `https://matrix.example.com`
|
||
|
|
pub homeserver: String,
|
||
|
|
/// Bot user ID, e.g. `@storykit:example.com`
|
||
|
|
pub username: String,
|
||
|
|
/// Bot password
|
||
|
|
pub password: String,
|
||
|
|
/// Matrix room ID to join, e.g. `!roomid:example.com`
|
||
|
|
pub room_id: String,
|
||
|
|
/// Set to `true` to enable the bot (default: false)
|
||
|
|
#[serde(default)]
|
||
|
|
pub enabled: bool,
|
||
|
|
/// Previously used to select an Anthropic model. Now ignored — the bot
|
||
|
|
/// uses Claude Code which manages its own model selection. Kept for
|
||
|
|
/// backwards compatibility so existing bot.toml files still parse.
|
||
|
|
#[allow(dead_code)]
|
||
|
|
pub model: Option<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl BotConfig {
|
||
|
|
/// Load bot configuration from `.story_kit/bot.toml`.
|
||
|
|
///
|
||
|
|
/// Returns `None` if the file does not exist, fails to parse, or has
|
||
|
|
/// `enabled = false`.
|
||
|
|
pub fn load(project_root: &Path) -> Option<Self> {
|
||
|
|
let path = project_root.join(".story_kit").join("bot.toml");
|
||
|
|
if !path.exists() {
|
||
|
|
return None;
|
||
|
|
}
|
||
|
|
let content = std::fs::read_to_string(&path)
|
||
|
|
.map_err(|e| eprintln!("[matrix-bot] Failed to read bot.toml: {e}"))
|
||
|
|
.ok()?;
|
||
|
|
let config: BotConfig = toml::from_str(&content)
|
||
|
|
.map_err(|e| eprintln!("[matrix-bot] Invalid bot.toml: {e}"))
|
||
|
|
.ok()?;
|
||
|
|
if !config.enabled {
|
||
|
|
return None;
|
||
|
|
}
|
||
|
|
Some(config)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
use std::fs;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn load_returns_none_when_file_missing() {
|
||
|
|
let tmp = tempfile::tempdir().unwrap();
|
||
|
|
let result = BotConfig::load(tmp.path());
|
||
|
|
assert!(result.is_none());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn load_returns_none_when_disabled() {
|
||
|
|
let tmp = tempfile::tempdir().unwrap();
|
||
|
|
let sk = tmp.path().join(".story_kit");
|
||
|
|
fs::create_dir_all(&sk).unwrap();
|
||
|
|
fs::write(
|
||
|
|
sk.join("bot.toml"),
|
||
|
|
r#"
|
||
|
|
homeserver = "https://matrix.example.com"
|
||
|
|
username = "@bot:example.com"
|
||
|
|
password = "secret"
|
||
|
|
room_id = "!abc:example.com"
|
||
|
|
enabled = false
|
||
|
|
"#,
|
||
|
|
)
|
||
|
|
.unwrap();
|
||
|
|
let result = BotConfig::load(tmp.path());
|
||
|
|
assert!(result.is_none());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn load_returns_config_when_enabled() {
|
||
|
|
let tmp = tempfile::tempdir().unwrap();
|
||
|
|
let sk = tmp.path().join(".story_kit");
|
||
|
|
fs::create_dir_all(&sk).unwrap();
|
||
|
|
fs::write(
|
||
|
|
sk.join("bot.toml"),
|
||
|
|
r#"
|
||
|
|
homeserver = "https://matrix.example.com"
|
||
|
|
username = "@bot:example.com"
|
||
|
|
password = "secret"
|
||
|
|
room_id = "!abc:example.com"
|
||
|
|
enabled = true
|
||
|
|
"#,
|
||
|
|
)
|
||
|
|
.unwrap();
|
||
|
|
let result = BotConfig::load(tmp.path());
|
||
|
|
assert!(result.is_some());
|
||
|
|
let config = result.unwrap();
|
||
|
|
assert_eq!(config.homeserver, "https://matrix.example.com");
|
||
|
|
assert_eq!(config.username, "@bot:example.com");
|
||
|
|
assert_eq!(config.room_id, "!abc:example.com");
|
||
|
|
assert!(config.model.is_none());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn load_returns_none_when_toml_invalid() {
|
||
|
|
let tmp = tempfile::tempdir().unwrap();
|
||
|
|
let sk = tmp.path().join(".story_kit");
|
||
|
|
fs::create_dir_all(&sk).unwrap();
|
||
|
|
fs::write(sk.join("bot.toml"), "not valid toml {{{").unwrap();
|
||
|
|
let result = BotConfig::load(tmp.path());
|
||
|
|
assert!(result.is_none());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn load_respects_optional_model() {
|
||
|
|
let tmp = tempfile::tempdir().unwrap();
|
||
|
|
let sk = tmp.path().join(".story_kit");
|
||
|
|
fs::create_dir_all(&sk).unwrap();
|
||
|
|
fs::write(
|
||
|
|
sk.join("bot.toml"),
|
||
|
|
r#"
|
||
|
|
homeserver = "https://matrix.example.com"
|
||
|
|
username = "@bot:example.com"
|
||
|
|
password = "secret"
|
||
|
|
room_id = "!abc:example.com"
|
||
|
|
enabled = true
|
||
|
|
model = "claude-sonnet-4-6"
|
||
|
|
"#,
|
||
|
|
)
|
||
|
|
.unwrap();
|
||
|
|
let config = BotConfig::load(tmp.path()).unwrap();
|
||
|
|
assert_eq!(config.model.as_deref(), Some("claude-sonnet-4-6"));
|
||
|
|
}
|
||
|
|
}
|