From a3844dde9ef779b5ae9af2ced4d4a44784b808fb Mon Sep 17 00:00:00 2001 From: jude Date: Sun, 6 Feb 2022 15:47:59 +0000 Subject: [PATCH] moved postman into separate crate --- Cargo.lock | 17 +++++++++++++ Cargo.toml | 3 +++ postman/Cargo.toml | 32 +++++++++++++++++++++++++ postman/src/lib.rs | 33 +++++++++++++++++++++++++ {src => postman/src}/sender.rs | 44 +++++++++++++++++++++------------- src/commands/reminder_cmds.rs | 2 +- src/component_models/mod.rs | 3 ++- src/consts.rs | 5 ---- src/main.rs | 28 ++++------------------ web/Cargo.toml | 8 +++++++ web/src/lib.rs | 8 +++++++ 11 files changed, 136 insertions(+), 47 deletions(-) create mode 100644 postman/Cargo.toml create mode 100644 postman/src/lib.rs rename {src => postman/src}/sender.rs (92%) create mode 100644 web/Cargo.toml create mode 100644 web/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index bfcc206..a69bd2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1073,6 +1073,22 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "postman" +version = "0.1.0" +dependencies = [ + "chrono", + "chrono-tz", + "env_logger", + "lazy_static", + "log", + "num-integer", + "regex", + "serenity", + "sqlx", + "tokio", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1228,6 +1244,7 @@ dependencies = [ "levenshtein", "log", "num-integer", + "postman", "rand 0.7.3", "regex", "regex_command_attr", diff --git a/Cargo.toml b/Cargo.toml index beb2e09..6db8316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,9 @@ base64 = "0.13.0" [dependencies.regex_command_attr] path = "command_attributes" +[dependencies.postman] +path = "postman" + [dependencies.serenity] git = "https://github.com/serenity-rs/serenity" branch = "next" diff --git a/postman/Cargo.toml b/postman/Cargo.toml new file mode 100644 index 0000000..5297ae6 --- /dev/null +++ b/postman/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "postman" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["process", "full"] } +regex = "1.4" +log = "0.4" +env_logger = "0.8" +chrono = "0.4" +chrono-tz = { version = "0.5", features = ["serde"] } +lazy_static = "1.4" +num-integer = "0.1" +sqlx = { version = "0.5.10", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]} + +[dependencies.serenity] +git = "https://github.com/serenity-rs/serenity" +branch = "next" +default-features = false +features = [ + "builder", + "client", + "cache", + "gateway", + "http", + "model", + "utils", + "rustls_backend", + "collector", + "unstable_discord_api" +] diff --git a/postman/src/lib.rs b/postman/src/lib.rs new file mode 100644 index 0000000..ed1b852 --- /dev/null +++ b/postman/src/lib.rs @@ -0,0 +1,33 @@ +mod sender; + +use log::info; +use serenity::client::Context; +use sqlx::{Executor, MySql}; +use std::env; +use tokio::time::sleep_until; +use tokio::time::{Duration, Instant}; + +type Database = MySql; + +pub async fn initialize(ctx: Context, pool: impl Executor<'_, Database = Database> + Copy) { + let REMIND_INTERVAL = env::var("REMIND_INTERVAL") + .map(|inner| inner.parse::().ok()) + .ok() + .flatten() + .unwrap_or(10); + + loop { + let sleep_to = Instant::now() + Duration::from_secs(REMIND_INTERVAL); + let reminders = sender::Reminder::fetch_reminders(pool).await; + + if reminders.len() > 0 { + info!("Preparing to send {} reminders.", reminders.len()); + + for reminder in reminders { + reminder.send(pool, ctx.clone()).await; + } + } + + sleep_until(sleep_to).await; + } +} diff --git a/src/sender.rs b/postman/src/sender.rs similarity index 92% rename from src/sender.rs rename to postman/src/sender.rs index d2792e3..140a14d 100644 --- a/src/sender.rs +++ b/postman/src/sender.rs @@ -1,5 +1,7 @@ +use crate::Database; use chrono::Duration; use chrono_tz::Tz; +use lazy_static::lazy_static; use log::{error, info, warn}; use num_integer::Integer; use regex::{Captures, Regex}; @@ -15,7 +17,7 @@ use serenity::{ }; use sqlx::{ types::chrono::{NaiveDateTime, Utc}, - MySqlPool, + Executor, }; lazy_static! { @@ -114,7 +116,10 @@ struct EmbedField { } impl Embed { - pub async fn from_id(pool: &MySqlPool, id: u32) -> Option { + pub async fn from_id( + pool: impl Executor<'_, Database = Database> + Copy, + id: u32, + ) -> Option { let mut inner = sqlx::query_as_unchecked!( EmbedInner, " @@ -135,7 +140,7 @@ WHERE ", id ) - .fetch_one(&pool.clone()) + .fetch_one(pool) .await .unwrap(); @@ -265,7 +270,7 @@ pub struct Reminder { } impl Reminder { - pub async fn fetch_reminders(pool: &MySqlPool) -> Vec { + pub async fn fetch_reminders(pool: impl Executor<'_, Database = Database> + Copy) -> Vec { sqlx::query_as_unchecked!( Reminder, " @@ -317,7 +322,7 @@ WHERE .collect::>() } - async fn reset_webhook(&self, pool: &MySqlPool) { + async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) { let _ = sqlx::query!( " UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ? @@ -328,14 +333,15 @@ UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ? .await; } - async fn refresh(&self, pool: &MySqlPool) { + async fn refresh(&self, pool: impl Executor<'_, Database = Database> + Copy) { if self.interval_seconds.is_some() || self.interval_months.is_some() { let now = Utc::now().naive_local(); let mut updated_reminder_time = self.utc_time; if let Some(interval) = self.interval_months { let row = sqlx::query!( - "SELECT DATE_ADD(?, INTERVAL ? MONTH) AS new_time", + // use the second date_add to force return value to datetime + "SELECT DATE_ADD(DATE_ADD(?, INTERVAL ? MONTH), INTERVAL 0 SECOND) AS new_time", updated_reminder_time, interval ) @@ -373,7 +379,7 @@ UPDATE reminders SET `utc_time` = ? WHERE `id` = ? } } - async fn force_delete(&self, pool: &MySqlPool) { + async fn force_delete(&self, pool: impl Executor<'_, Database = Database> + Copy) { sqlx::query!( " DELETE FROM reminders WHERE `id` = ? @@ -389,7 +395,11 @@ DELETE FROM reminders WHERE `id` = ? let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await; } - pub async fn send(&self, pool: MySqlPool, cache_http: impl CacheHttp) { + pub async fn send( + &self, + pool: impl Executor<'_, Database = Database> + Copy, + cache_http: impl CacheHttp, + ) { async fn send_to_channel( cache_http: impl CacheHttp, reminder: &Reminder, @@ -521,10 +531,10 @@ UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ? ", self.channel_id ) - .execute(&pool.clone()) + .execute(pool) .await; - let embed = Embed::from_id(&pool.clone(), self.id).await.map(|e| e.into()); + let embed = Embed::from_id(pool, self.id).await.map(|e| e.into()); let result = if let (Some(webhook_id), Some(webhook_token)) = (self.webhook_id, &self.webhook_token) @@ -537,7 +547,7 @@ UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ? } else { warn!("Webhook vanished: {:?}", webhook_res); - self.reset_webhook(&pool.clone()).await; + self.reset_webhook(pool).await; send_to_channel(cache_http, &self, embed).await } } else { @@ -550,20 +560,20 @@ UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ? if let Error::Http(error) = e { if error.status_code() == Some(StatusCode::from_u16(404).unwrap()) { error!("Seeing channel is deleted. Removing reminder"); - self.force_delete(&pool).await; + self.force_delete(pool).await; } else { - self.refresh(&pool).await; + self.refresh(pool).await; } } else { - self.refresh(&pool).await; + self.refresh(pool).await; } } else { - self.refresh(&pool).await; + self.refresh(pool).await; } } else { info!("Reminder {} is paused", self.id); - self.refresh(&pool).await; + self.refresh(pool).await; } } } diff --git a/src/commands/reminder_cmds.rs b/src/commands/reminder_cmds.rs index c1731dd..82b6455 100644 --- a/src/commands/reminder_cmds.rs +++ b/src/commands/reminder_cmds.rs @@ -323,7 +323,7 @@ async fn look(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { .iter() .map(|reminder| reminder.display(&flags, &timezone)) .fold(0, |t, r| t + r.len()) - .div_ceil(EMBED_DESCRIPTION_MAX_LENGTH); + .div_ceil(&EMBED_DESCRIPTION_MAX_LENGTH); let pager = LookPager::new(flags, timezone); diff --git a/src/component_models/mod.rs b/src/component_models/mod.rs index bf16b2b..18e0c2e 100644 --- a/src/component_models/mod.rs +++ b/src/component_models/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod pager; use std::io::Cursor; use chrono_tz::Tz; +use num_integer::Integer; use rmp_serde::Serializer; use serde::{Deserialize, Serialize}; use serenity::{ @@ -78,7 +79,7 @@ impl ComponentDataModel { .iter() .map(|reminder| reminder.display(&flags, &pager.timezone)) .fold(0, |t, r| t + r.len()) - .div_ceil(EMBED_DESCRIPTION_MAX_LENGTH); + .div_ceil(&EMBED_DESCRIPTION_MAX_LENGTH); let channel_name = if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) { diff --git a/src/consts.rs b/src/consts.rs index 63e9511..e1dc822 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -14,11 +14,6 @@ use regex::Regex; use serenity::model::prelude::AttachmentType; lazy_static! { - pub static ref REMIND_INTERVAL: u64 = env::var("REMIND_INTERVAL") - .map(|inner| inner.parse::().ok()) - .ok() - .flatten() - .unwrap_or(10); pub static ref DEFAULT_AVATAR: AttachmentType<'static> = ( include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), diff --git a/src/main.rs b/src/main.rs index 5813643..6c8fcf6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![feature(int_roundings)] #[macro_use] extern crate lazy_static; @@ -9,7 +8,6 @@ mod framework; mod hooks; mod interval_parser; mod models; -mod sender; mod time_parser; use std::{ @@ -24,6 +22,7 @@ use std::{ use chrono_tz::Tz; use dotenv::dotenv; use log::info; +use postman::initialize; use serenity::{ async_trait, client::Client, @@ -39,15 +38,12 @@ use serenity::{ utils::shard_id, }; use sqlx::mysql::MySqlPool; -use tokio::{ - sync::RwLock, - time::{Duration, Instant}, -}; +use tokio::sync::RwLock; use crate::{ commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds}, component_models::ComponentDataModel, - consts::{CNC_GUILD, REMIND_INTERVAL, SUBSCRIPTION_ROLES, THEME_COLOR}, + consts::{CNC_GUILD, SUBSCRIPTION_ROLES, THEME_COLOR}, framework::RegexFramework, models::command_macro::CommandMacro, }; @@ -88,24 +84,10 @@ impl EventHandler for Handler { if !self.is_loop_running.load(Ordering::Relaxed) { let ctx = ctx_base.clone(); + let pool = ctx.data.read().await.get::().cloned().unwrap(); tokio::spawn(async move { - let pool = ctx.data.read().await.get::().cloned().unwrap(); - - loop { - let sleep_until = Instant::now() + Duration::from_secs(*REMIND_INTERVAL); - let reminders = sender::Reminder::fetch_reminders(&pool).await; - - if reminders.len() > 0 { - info!("Preparing to send {} reminders.", reminders.len()); - - for reminder in reminders { - reminder.send(pool.clone(), ctx.clone()).await; - } - } - - tokio::time::sleep_until(sleep_until).await; - } + initialize(ctx, &pool).await; }); self.is_loop_running.swap(true, Ordering::Relaxed); diff --git a/web/Cargo.toml b/web/Cargo.toml new file mode 100644 index 0000000..b018c1e --- /dev/null +++ b/web/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "web" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/web/src/lib.rs b/web/src/lib.rs new file mode 100644 index 0000000..1b4a90c --- /dev/null +++ b/web/src/lib.rs @@ -0,0 +1,8 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +}