From 19252f6e90c84140794edf57120603e215743f83 Mon Sep 17 00:00:00 2001 From: Xyon Date: Thu, 24 Aug 2023 22:39:38 +0100 Subject: [PATCH] Bump the major, add the frog tips --- Cargo.toml | 3 +- src/commands/frog.rs | 23 +++++++++ src/commands/mod.rs | 2 + src/lib.rs | 3 ++ src/models/fueltank.rs | 115 +++++++++++++++++++++++++++++++++++++++++ src/models/mod.rs | 1 + 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/commands/frog.rs create mode 100644 src/models/fueltank.rs diff --git a/Cargo.toml b/Cargo.toml index 7880461..de7f0d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manifold" -version = "1.0.0" +version = "2.0.0" authors = ["Lucy Bladen "] edition = "2021" @@ -18,6 +18,7 @@ config = { version = "0.13.1", features = [ "yaml" ] } d20 = "0.1.0" diesel = { version = "2.1.0", features = ["sqlite", "r2d2", "chrono"] } diesel_migrations = "2.1.0" +dirs = "5.0.1" env_logger = "0.10.0" log = "0.4.14" num = "0.4.1" diff --git a/src/commands/frog.rs b/src/commands/frog.rs new file mode 100644 index 0000000..8c0c7e9 --- /dev/null +++ b/src/commands/frog.rs @@ -0,0 +1,23 @@ +use crate::models::fueltank::Tip; +use crate::error::{ManifoldError, ManifoldResult}; +use crate::{ManifoldContext, ManifoldData}; + +#[poise::command(slash_command, prefix_command, aliases("ft"))] +async fn frogtip(ctx: ManifoldContext<'_>) -> ManifoldResult<()> { + + debug!("Processing tip"); + + let tank = ctx.data().frogtip_fueltank.lock().await; + + let tip: &Tip = &tank.next_or_fill().await?; + + debug!("Tip: {:?}", tip); + + ctx.send(|f| f.content(format!("{}", tip)).reply(true)).await?; + + Ok(()) +} + +pub fn commands() -> [poise::Command; 1] { + [frogtip()] +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9a28727..b843b2d 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,4 +1,5 @@ mod admin; +mod frog; mod core; mod weather; @@ -7,6 +8,7 @@ use crate::ManifoldCommand; pub fn collect_commands(injected: Vec) -> Vec { core::commands().into_iter() .chain(admin::commands()) + .chain(frog::commands()) .chain(weather::commands()) .chain(injected) .collect() diff --git a/src/lib.rs b/src/lib.rs index ca68fa7..7c6e472 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ use crate::config::ManifoldConfig; use crate::error::{ManifoldError, ManifoldResult}; use crate::events::Handler; use crate::models::user::{ManifoldUserInfo, UserInfo}; +use crate::models::fueltank::FuelTank; use crate::responses::Responses; pub mod autocomplete_helpers; @@ -66,6 +67,7 @@ pub struct ManifoldDataInner { responses: Responses, user_info: Mutex, version_string: String, + frogtip_fueltank: Mutex, } pub type ManifoldContext<'a> = poise::Context<'a, ManifoldData, ManifoldError>; @@ -128,6 +130,7 @@ pub async fn prepare_client(arguments: ArgMatches, intents: GatewayIntents, inje responses, user_info: Mutex::new(user_info), version_string: format!("{caller} (Manifold framework version {mfold_ver} built at {mfold_time} from revision {mfold_rev})", caller=caller_version_string, mfold_ver=built_info::PKG_VERSION, mfold_time=built_info::BUILT_TIME_UTC, mfold_rev=git_info), + frogtip_fueltank: Mutex::new(FuelTank::with_cache()) }))) }) }); diff --git a/src/models/fueltank.rs b/src/models/fueltank.rs new file mode 100644 index 0000000..242a80b --- /dev/null +++ b/src/models/fueltank.rs @@ -0,0 +1,115 @@ +use std::fmt::{Display, Formatter}; +use std::fs; +use std::path::PathBuf; +use dirs::home_dir; +use std::io::{SeekFrom, Seek}; +use reqwest::Client; +use reqwest::header::CONTENT_TYPE; + +use crate::error::{ManifoldError, ManifoldResult}; + +const CACHE_VERSION: &'static str = "1"; + +pub trait Pump { + fn pump(self) -> Result; +} + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub struct FuelTank { + mode: TankMode, +} + +#[derive(Copy, Clone, Debug)] +enum TankMode { + Cache, +} + +impl FuelTank { + pub fn with_cache() -> Self { + Self { + mode: TankMode::Cache, + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Tip { + pub number: i32, + pub tip: String, +} + +impl Display for Tip { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}: {}", self.number, self.tip) + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Croak { + pub tips: Vec, +} + +pub async fn croak() -> ManifoldResult> { + + debug!("Getting new tips"); + let result: Croak; + + let client = Client::new(); + result = client.get("https://frog.tips/api/1/tips/") + .header(CONTENT_TYPE, "application/json") + .send().await.unwrap().json().await.unwrap(); + + debug!("Result: {:?}", &result); + + Ok(result.tips) +} + +impl FuelTank { + pub async fn next_or_fill(self) -> Result { + debug!("Filling tank!"); + let mut cache = { + let mut path = PathBuf::new(); + path.push(home_dir().ok_or(ManifoldError::from("No home dir"))?); + path.push(".local"); + path.push(format!("coffeebot-frog-cache-{}.json", CACHE_VERSION)); + debug!("Cache path: {:?}", &path); + fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(path) + .map_err(|e| { + error!("Error creating cache file: {}", e); + ManifoldError::from("CACHE ERROR") + })? + }; + + debug!("Cache: {:?}", &cache); + + let json: Result, serde_json::Error> = serde_json::from_reader(&mut cache); + debug!("Json: {:?}", &json); + let mut tips = match json { + Ok(contents) => { + match contents.len() { + 0 => croak().await?, + _ => contents, + } + } + _ => croak().await?, + }; + + debug!("Got tips: {:?}", &tips); + let tip = tips.pop().ok_or(ManifoldError::from("FROG HAS NO TIPS FOR YOU. CROAK ERROR 12"))?; + + debug!("Got single tip: {:?}", &tip); + cache + .set_len(0) + .and_then(|_| cache.seek(SeekFrom::Start(0)))?; + + debug!("Writing cache"); + serde_json::to_writer(cache, &tips)?; + + Ok(tip) + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index fbfeedb..b1eabeb 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,2 +1,3 @@ +pub mod fueltank; pub mod user; pub mod weather; \ No newline at end of file