aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock81
-rw-r--r--Cargo.toml7
-rw-r--r--config.debug.toml5
-rw-r--r--config.toml17
-rw-r--r--src/config.rs80
-rw-r--r--src/hooks/mod.rs2
-rw-r--r--src/hooks/wolfram_alpha.rs38
-rw-r--r--src/lib.rs35
-rw-r--r--src/main.rs4
10 files changed, 213 insertions, 58 deletions
diff --git a/.gitignore b/.gitignore
index 05e354e..9b6afdc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
/target
/.cache/
-config.json
/deploy/vendor
+/config.debug.toml
diff --git a/Cargo.lock b/Cargo.lock
index 914a422..70e4c42 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -67,6 +67,15 @@ dependencies = [
]
[[package]]
+name = "atomic"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -130,6 +139,7 @@ dependencies = [
"async-trait",
"base64",
"catinator_macros",
+ "figment",
"futures",
"irc",
"irc-proto",
@@ -318,6 +328,20 @@ dependencies = [
]
[[package]]
+name = "figment"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df"
+dependencies = [
+ "atomic",
+ "pear",
+ "serde",
+ "toml",
+ "uncased",
+ "version_check",
+]
+
+[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -597,6 +621,12 @@ dependencies = [
]
[[package]]
+name = "inlinable_string"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77"
+
+[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -855,6 +885,29 @@ dependencies = [
]
[[package]]
+name = "pear"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702"
+dependencies = [
+ "inlinable_string",
+ "pear_codegen",
+ "yansi",
+]
+
+[[package]]
+name = "pear_codegen"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0"
+dependencies = [
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -920,6 +973,19 @@ dependencies = [
]
[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+ "yansi",
+]
+
+[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1480,6 +1546,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
+name = "uncased"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
name = "unicode-bidi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1680,3 +1755,9 @@ checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
"winapi",
]
+
+[[package]]
+name = "yansi"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
diff --git a/Cargo.toml b/Cargo.toml
index 6d2f5e3..e0f9d2a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,14 +12,15 @@ irc-proto = "0.15"
sasl = "0.5"
base64 = "0.13"
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
+figment = { version = "0.10", features = ["env", "toml"] }
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
toml = "0.5"
anyhow = "1"
futures = "0.3"
-tokio = { version = "1.5.0", features = ["full", "rt-multi-thread"] }
+tokio = { version = "1", features = ["full", "rt-multi-thread"] }
tracing = "0.1"
tracing-subscriber = "0.2"
diff --git a/config.debug.toml b/config.debug.toml
new file mode 100644
index 0000000..9e7a0e7
--- /dev/null
+++ b/config.debug.toml
@@ -0,0 +1,5 @@
+[debug]
+[debug.user]
+nickname = "kittynator"
+[debug.server]
+channels = ["#audron-test"]
diff --git a/config.toml b/config.toml
index 4ce6caf..fba4301 100644
--- a/config.toml
+++ b/config.toml
@@ -1,16 +1,19 @@
-[user]
-nickname = "\\__{^-_-^}"
+[default]
+[default.user]
username = "catinator"
-password = ""
realname = "moaw"
-[server]
+[default.server]
hostname = "irc.snoonet.org"
port = 6697
tls = true
sasl = true
-channels = ["#redoxmasterrace", "#linuxmasterrace", "#gnulag"]
-[settings]
+[default.settings]
prefix = ':'
-wa_api_key = ""
+
+[release]
+[release.user]
+nickname = "\\__{^-_-^}"
+[release.server]
+channels = ["#redoxmasterrace", "#linuxmasterrace", "#gnulag"]
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 {
diff --git a/src/lib.rs b/src/lib.rs
index 82bdad5..37e9d89 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
),
];
}