diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.rs | 80 | ||||
| -rw-r--r-- | src/hooks/mod.rs | 2 | ||||
| -rw-r--r-- | src/hooks/wolfram_alpha.rs | 38 | ||||
| -rw-r--r-- | src/lib.rs | 35 | ||||
| -rw-r--r-- | src/main.rs | 4 |
5 files changed, 112 insertions, 47 deletions
diff --git a/src/config.rs b/src/config.rs index 776d60c..1a88b95 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,12 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +use figment::{ + providers::{Format, Toml}, + value::{Dict, Map}, + Error, Figment, Metadata, Profile, Provider, +}; + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] pub struct Config { pub user: User, pub server: Server, @@ -13,7 +19,7 @@ impl From<Config> for irc::client::prelude::Config { nickname: Some(input.user.nickname), username: Some(input.user.username), realname: Some(input.user.realname), - nick_password: Some(input.user.password), + nick_password: input.user.password, server: Some(input.server.hostname), port: Some(input.server.port), use_tls: Some(input.server.tls), @@ -23,25 +29,83 @@ impl From<Config> for irc::client::prelude::Config { } } -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] pub struct User { pub nickname: String, pub username: String, - pub password: String, + #[serde(default)] + pub password: Option<String>, pub realname: String, } -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] pub struct Server { pub hostname: String, + #[serde(default = "default_port")] pub port: u16, + #[serde(default = "default_tls")] pub tls: bool, + #[serde(default)] pub sasl: bool, + #[serde(default)] pub channels: Vec<String>, } -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +const fn default_port() -> u16 { + 6697 +} + +const fn default_tls() -> bool { + true +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] pub struct Settings { + #[serde(default = "default_prefix")] pub prefix: char, - pub wa_api_key: String, + // pub wa_api_key: String, +} + +const fn default_prefix() -> char { + ':' +} + +impl Config { + // Allow the configuration to be extracted from any `Provider`. + pub fn from<T: Provider>(provider: T) -> Result<Config, Error> { + Figment::from(provider).extract() + } + + // Provide a default provider, a `Figment`. + pub fn figment() -> Figment { + use figment::providers::Env; + + let figment = Figment::new(); + + #[cfg(debug_assertions)] + const PROFILE: &str = "debug"; + #[cfg(not(debug_assertions))] + const PROFILE: &str = "release"; + + figment + .merge(Toml::file("config.toml").nested()) + .merge(Toml::file("config.debug.toml").nested()) + .merge(Env::prefixed("CATINATOR_").split('_')) + .select(PROFILE) + } +} + +// Make `Config` a provider itself for composability. +impl Provider for Config { + fn metadata(&self) -> Metadata { + Metadata::named("Library Config") + } + + fn data(&self) -> Result<Map<Profile, Dict>, Error> { + figment::providers::Serialized::defaults(self).data() + } + + fn profile(&self) -> Option<Profile> { + Some(Profile::Default) + } } diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs index cdf3787..86d5697 100644 --- a/src/hooks/mod.rs +++ b/src/hooks/mod.rs @@ -36,7 +36,7 @@ pub fn sasl(bot: &crate::Bot, msg: Message) -> Result<()> { if text == "+" { let creds = Credentials::default() .with_username(bot.config.clone().user.username) - .with_password(bot.config.clone().user.password); + .with_password(bot.config.clone().user.password.unwrap()); let mut mechanism = Plain::from_credentials(creds)?; diff --git a/src/hooks/wolfram_alpha.rs b/src/hooks/wolfram_alpha.rs index 3faddbf..0fceb0b 100644 --- a/src/hooks/wolfram_alpha.rs +++ b/src/hooks/wolfram_alpha.rs @@ -3,12 +3,39 @@ use crate::util::{ web::{quote_plus, IsgdUrlShortener, UrlShortener}, }; use anyhow::{bail, Context, Error, Result}; +use figment::providers::Env; use futures::join; use irc::client::prelude::*; use macros::privmsg; use reqwest::{get, Url}; use serde::{Deserialize, Serialize}; +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct WolframAlpha { + wa_api_key: String, +} + +impl WolframAlpha { + pub fn new(bot: &crate::Bot) -> Result<WolframAlpha> { + bot.figment + .clone() + .merge(Env::prefixed("CATINATOR_")) + .extract() + .context("failed to extract wolfram alpha config") + } + + pub async fn wa(&self, bot: &crate::Bot, msg: Message) -> Result<()> { + privmsg!(msg, { + let content = get_input_query(text)?; + bot.send_privmsg( + msg.response_target() + .context("failed to get response target")?, + &wa_query(&content, Some(&self.wa_api_key), None).await?, + )?; + }) + } +} + #[derive(Serialize, Deserialize, Debug)] struct WaResponse { queryresult: QueryResult, @@ -147,17 +174,6 @@ fn get_input_query(text: &str) -> Result<String, Error> { Ok(content.to_string()) } -pub async fn wa(bot: &crate::Bot, msg: Message) -> Result<()> { - privmsg!(msg, { - let content = get_input_query(text)?; - bot.send_privmsg( - msg.response_target() - .context("failed to get response target")?, - &wa_query(&content, Some(&bot.config.settings.wa_api_key), None).await?, - )?; - }) -} - #[cfg(test)] mod tests { @@ -2,12 +2,10 @@ #[cfg(all(test, feature = "bench"))] extern crate test; -use anyhow::Result; +use anyhow::{Context, Result}; use irc::client::prelude::*; -use tracing::info; - pub mod config; pub mod hooks; pub mod util; @@ -23,44 +21,29 @@ macro_rules! reply { pub struct Bot { pub config: config::Config, + pub figment: figment::Figment, pub irc_client: irc::client::Client, } -fn get_env_var(var_name: &str) -> Option<String> { - match std::env::var(var_name) { - Ok(var) => { - info!("using {} from env", var_name); - Some(var) - } - Err(_) => None, - } -} - impl Bot { - pub async fn new(config_path: &str) -> Result<Bot> { - use std::fs; + pub async fn new() -> Result<Bot> { + let figment = config::Config::figment(); + let config: config::Config = figment.extract().context("failed to extract config")?; + + let irc_client = Client::from_config(config.clone().into()).await?; - let config_str = fs::read_to_string(config_path)?; - let mut config: config::Config = toml::from_str(&config_str)?; let bot = Bot { irc_client, config, figment }; - if let Some(v) = get_env_var("CATINATOR_PASSWORD") { - config.user.password = v - }; if bot.config.server.sasl && bot.config.user.password.is_some() { tracing::info!("initializing sasl"); bot.sasl_init().await.unwrap() } - if let Some(v) = get_env_var("CATINATOR_WA_API_KEY") { - config.settings.wa_api_key = v - }; - - let irc_client = Client::from_config(config.clone().into()).await?; Ok(bot) } - Ok(Bot { irc_client, config }) + pub fn figment(&self) -> &figment::Figment { + &self.figment } pub async fn sasl_init(&self) -> Result<()> { diff --git a/src/main.rs b/src/main.rs index 100af91..2428dff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,8 @@ async fn main() { let mut bot = Bot::new().await.unwrap(); let mut sed = catinator::hooks::sed::Sed::new(); + let wolfram_alpha = catinator::hooks::wolfram_alpha::WolframAlpha::new(&bot) + .expect("failed to initialize WolframAlpha command"); catinator![ hook( @@ -57,7 +59,7 @@ async fn main() { async command( "wa", "Returns Wolfram Alpha results for a query", - catinator::hooks::wolfram_alpha::wa + wolfram_alpha.wa ), ]; } |
