diff --git a/Cargo.lock b/Cargo.lock index b7b374a..4628984 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -747,29 +747,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "homecloud" -version = "0.1.0" -dependencies = [ - "anyhow", - "axum", - "color-eyre", - "config", - "discord", - "redact", - "reqwest", - "reqwest-middleware", - "reqwest-retry", - "reqwest-tracing", - "serde", - "serde_json", - "serde_with", - "tokio", - "tracing", - "tracing-subscriber", - "urlencoding", -] - [[package]] name = "http" version = "1.1.0" @@ -2915,3 +2892,25 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "yourcloud" +version = "0.1.0" +dependencies = [ + "axum", + "color-eyre", + "config", + "discord", + "redact", + "reqwest", + "reqwest-middleware", + "reqwest-retry", + "reqwest-tracing", + "serde", + "serde_json", + "serde_with", + "tokio", + "tracing", + "tracing-subscriber", + "urlencoding", +] diff --git a/Cargo.toml b/Cargo.toml index 5c952c0..dcf0bf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,9 @@ [package] -name = "homecloud" +name = "yourcloud" version = "0.1.0" edition = "2021" [dependencies] -anyhow = "1.0.82" axum = "0.7.5" color-eyre = "0.6.3" config = "0.14.0" diff --git a/flake.lock b/flake.lock index 66240dd..12727d1 100644 --- a/flake.lock +++ b/flake.lock @@ -16,28 +16,9 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "git-hooks": { "inputs": { "flake-compat": "flake-compat", - "flake-utils": "flake-utils", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -45,11 +26,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1712579741, - "narHash": "sha256-igpsH+pa6yFwYOdah3cFciCk8gw+ytniG9quf5f/q84=", + "lastModified": 1722857853, + "narHash": "sha256-3Zx53oz/MSIyevuWO/SumxABkrIvojnB7g9cimxkhiE=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "70f504012f0a132ac33e56988e1028d88a48855c", + "rev": "06939f6b7ec4d4f465bf3132a05367cccbbf64da", "type": "github" }, "original": { @@ -81,11 +62,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1712608508, - "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=", + "lastModified": 1722630782, + "narHash": "sha256-hMyG9/WlUi0Ho9VkRrrez7SeNlDzLxalm9FwY7n/Noo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6", + "rev": "d04953086551086b44b6f3c6b7eeb26294f207da", "type": "github" }, "original": { @@ -97,16 +78,16 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1710695816, - "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } @@ -116,21 +97,6 @@ "git-hooks": "git-hooks", "nixpkgs": "nixpkgs" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/src/chatbot.rs b/src/chatbot.rs index c000fdf..293d47b 100644 --- a/src/chatbot.rs +++ b/src/chatbot.rs @@ -3,7 +3,7 @@ // TODO: handle messages // TODO: data persistence? (sqlite? sled?) -use std::{any::Any, future}; +use std::{any::Any, fmt, future}; use discord::model::{ChannelId, Event, Message, ReadyEvent}; use tokio::sync::Mutex; @@ -13,30 +13,30 @@ const COMMAND_PREFIX: &str = "!"; struct Discord { discord: discord::Discord, connection: Option>>, + me: Option, } impl Discord { pub fn try_new(conf: Arc) -> Result { - let discord = discord::Discord::from_bot_token( - conf.discord - .as_ref() - .unwrap() - .bot_token - .clone() - .expose_secret(), - )?; - Ok(Self { - discord, - connection: None, - }) + if let Some(bot_token) = conf.discord.as_ref().map(|d| &d.bot_token) { + let discord = discord::Discord::from_bot_token(bot_token.expose_secret())?; + Ok(Self { + discord, + connection: None, + me: None, + }) + } else { + return Err(eyre!("discord configuration was empty")); + } } pub async fn connect(&mut self) -> Result { if self.connection.is_some() { - return Err(anyhow::anyhow!("already connected")); + return Err(eyre!("already connected")); } let (connection, ready_ev) = self.discord.connect()?; self.connection = Some(Arc::new(Mutex::new(connection))); + self.me = self.discord.get_current_user().ok(); Ok(ready_ev) } @@ -195,6 +195,20 @@ impl Discord { } pub fn handle_message(&self, msg: Message) -> Result<()> { + if let Some(me) = &self.me { + if msg.author.id == me.id { + trace!("Ignoring message from self"); + } + return Ok(()); + } + if msg.author.bot { + info!( + "Ignoring bot message in channel {} with content: {}", + msg.channel_id, msg.content + ); + return Ok(()); + } + info!( "Recieved Discord message in channel {} with content: {}", msg.channel_id, msg.content @@ -221,6 +235,13 @@ impl Discord { s if s.starts_with("ping") => { let _ = self.send_message(msg.channel_id, "`pong`")?; } + s if s.starts_with("dm-me") => { + let dm = self.discord.create_dm(msg.author.id)?; + self.send_message( + dm.id, + format!("DM'ing you as requested via `{}dm-me", COMMAND_PREFIX).as_str(), + )?; + } _ => {} } Ok(()) diff --git a/src/config.rs b/src/config.rs index 17d2737..f2b66c3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -36,17 +36,10 @@ pub struct ConfigLoadResult { const CONFIG_FILE_PATH: &str = "./conf.toml"; const ENCRYPTED_CONFIG_FILE_PATH: &str = "./conf.toml.sops"; -const TMP_CONFIG_FILE_NAME: &str = "homecloud-conf.toml"; +const TMP_CONFIG_FILE_NAME: &str = "yourcloud-conf.toml"; impl Config { pub fn load() -> Result { - let c = Self::builder()?.build()?; - Ok(c.try_deserialize()?) - } - - #[instrument] - pub fn builder() -> Result> { - info!("builder"); let mut p: PathBuf = CONFIG_FILE_PATH.into(); let mut tmp_file_to_cleanup: Option = None; if !p.exists() { @@ -62,10 +55,10 @@ impl Config { info!("Detected encrypted config file '{}' - attempting to use `sops` to decrypt it to '{}' ", ENCRYPTED_CONFIG_FILE_PATH, tmp_file.display()); let sops_result = Command::new("sops") .args([ - OsStr::new("--decrypt"), - &p.into_os_string(), OsStr::new("--output"), &tmp_file.clone().into_os_string(), + OsStr::new("--decrypt"), + OsStr::new(ENCRYPTED_CONFIG_FILE_PATH), ]) .output(); p = tmp_file.clone(); @@ -78,18 +71,24 @@ impl Config { ); } } - // TODO: log whether or not we were able to load conf.toml? - let result = Ok(Self::default_builder() - .map_err(Error::from)? - .add_source(config::File::from(p).required(false)) - .add_source(config::Environment::with_prefix("homecloud").separator("__"))); + + let c = Self::builder(p)?.build()?; + + let result = c.try_deserialize(); // cleanup if let Some(path) = tmp_file_to_cleanup { let _ = std::fs::remove_file(path); } - result + Ok(result?) + } + + pub fn builder(file: PathBuf) -> Result> { + Ok(Self::default_builder() + .map_err(Report::from)? + .add_source(config::File::from(file).required(false)) + .add_source(config::Environment::with_prefix("yourcloud").separator("__"))) } pub fn default_builder() -> Result> { diff --git a/src/main.rs b/src/main.rs index 4074db9..6d6dc34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use crate::prelude::*; #[tokio::main] async fn main() -> Result<()> { + observe::setup_logging(); let conf: Arc = match Config::load() { Err(err) => { error!("Error loading configuration: {err}"); @@ -19,7 +20,6 @@ async fn main() -> Result<()> { } Ok(conf) => Arc::new(conf), }; - observe::setup_logging(conf.clone()); debug!("Configuration: {conf:?}"); let mut set = tokio::task::JoinSet::new(); diff --git a/src/observe.rs b/src/observe.rs index 2c67d00..94c6f3f 100644 --- a/src/observe.rs +++ b/src/observe.rs @@ -1,14 +1,12 @@ use tracing::level_filters::LevelFilter; use tracing_subscriber::EnvFilter; -use crate::prelude::*; - -pub fn setup_logging(_conf: Arc) { +pub fn setup_logging() { color_eyre::install().expect("Failed to install color_eyre"); let filter = EnvFilter::builder() .with_default_directive(LevelFilter::TRACE.into()) - .parse_lossy("info,homecloud=trace"); + .parse_lossy("info,yourcloud=trace"); tracing_subscriber::fmt().with_env_filter(filter).init(); } diff --git a/src/prelude.rs b/src/prelude.rs index 20b7685..d01aa94 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -2,8 +2,9 @@ // pub use color_eyre; pub use crate::config::Config; -pub use anyhow::Error; -pub use anyhow::Result; +pub use color_eyre::eyre::eyre; +pub use color_eyre::Report; +pub use color_eyre::Result; pub use redact::Secret; pub use serde::{Deserialize, Serialize}; pub use std::sync::Arc; diff --git a/src/webserver.rs b/src/webserver.rs index ddd2cdc..61caee2 100644 --- a/src/webserver.rs +++ b/src/webserver.rs @@ -9,7 +9,7 @@ use reqwest::StatusCode; use crate::{minecraft_server_status::MinecraftServerStatus, prelude::*}; -struct WebserverError(anyhow::Error); +struct WebserverError(Report); type WebserverResult = std::result::Result; pub async fn start(_conf: Arc) -> Result<()> { @@ -74,7 +74,7 @@ impl IntoResponse for WebserverError { // `Result<_, AppError>`. That way you don't need to do that manually. impl From for WebserverError where - E: Into, + E: Into, { fn from(err: E) -> Self { Self(err.into())