moved postman into separate crate

This commit is contained in:
jude 2022-02-06 15:47:59 +00:00
parent d62c8c95c2
commit a3844dde9e
11 changed files with 136 additions and 47 deletions

17
Cargo.lock generated
View File

@ -1073,6 +1073,22 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 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]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.16" version = "0.2.16"
@ -1228,6 +1244,7 @@ dependencies = [
"levenshtein", "levenshtein",
"log", "log",
"num-integer", "num-integer",
"postman",
"rand 0.7.3", "rand 0.7.3",
"regex", "regex",
"regex_command_attr", "regex_command_attr",

View File

@ -28,6 +28,9 @@ base64 = "0.13.0"
[dependencies.regex_command_attr] [dependencies.regex_command_attr]
path = "command_attributes" path = "command_attributes"
[dependencies.postman]
path = "postman"
[dependencies.serenity] [dependencies.serenity]
git = "https://github.com/serenity-rs/serenity" git = "https://github.com/serenity-rs/serenity"
branch = "next" branch = "next"

32
postman/Cargo.toml Normal file
View File

@ -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"
]

33
postman/src/lib.rs Normal file
View File

@ -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::<u64>().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;
}
}

View File

@ -1,5 +1,7 @@
use crate::Database;
use chrono::Duration; use chrono::Duration;
use chrono_tz::Tz; use chrono_tz::Tz;
use lazy_static::lazy_static;
use log::{error, info, warn}; use log::{error, info, warn};
use num_integer::Integer; use num_integer::Integer;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
@ -15,7 +17,7 @@ use serenity::{
}; };
use sqlx::{ use sqlx::{
types::chrono::{NaiveDateTime, Utc}, types::chrono::{NaiveDateTime, Utc},
MySqlPool, Executor,
}; };
lazy_static! { lazy_static! {
@ -114,7 +116,10 @@ struct EmbedField {
} }
impl Embed { impl Embed {
pub async fn from_id(pool: &MySqlPool, id: u32) -> Option<Self> { pub async fn from_id(
pool: impl Executor<'_, Database = Database> + Copy,
id: u32,
) -> Option<Self> {
let mut inner = sqlx::query_as_unchecked!( let mut inner = sqlx::query_as_unchecked!(
EmbedInner, EmbedInner,
" "
@ -135,7 +140,7 @@ WHERE
", ",
id id
) )
.fetch_one(&pool.clone()) .fetch_one(pool)
.await .await
.unwrap(); .unwrap();
@ -265,7 +270,7 @@ pub struct Reminder {
} }
impl Reminder { impl Reminder {
pub async fn fetch_reminders(pool: &MySqlPool) -> Vec<Self> { pub async fn fetch_reminders(pool: impl Executor<'_, Database = Database> + Copy) -> Vec<Self> {
sqlx::query_as_unchecked!( sqlx::query_as_unchecked!(
Reminder, Reminder,
" "
@ -317,7 +322,7 @@ WHERE
.collect::<Vec<Self>>() .collect::<Vec<Self>>()
} }
async fn reset_webhook(&self, pool: &MySqlPool) { async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
let _ = sqlx::query!( let _ = sqlx::query!(
" "
UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ? 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; .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() { if self.interval_seconds.is_some() || self.interval_months.is_some() {
let now = Utc::now().naive_local(); let now = Utc::now().naive_local();
let mut updated_reminder_time = self.utc_time; let mut updated_reminder_time = self.utc_time;
if let Some(interval) = self.interval_months { if let Some(interval) = self.interval_months {
let row = sqlx::query!( 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, updated_reminder_time,
interval 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!( sqlx::query!(
" "
DELETE FROM reminders WHERE `id` = ? 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; 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( async fn send_to_channel(
cache_http: impl CacheHttp, cache_http: impl CacheHttp,
reminder: &Reminder, reminder: &Reminder,
@ -521,10 +531,10 @@ UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?
", ",
self.channel_id self.channel_id
) )
.execute(&pool.clone()) .execute(pool)
.await; .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)) = let result = if let (Some(webhook_id), Some(webhook_token)) =
(self.webhook_id, &self.webhook_token) (self.webhook_id, &self.webhook_token)
@ -537,7 +547,7 @@ UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?
} else { } else {
warn!("Webhook vanished: {:?}", webhook_res); warn!("Webhook vanished: {:?}", webhook_res);
self.reset_webhook(&pool.clone()).await; self.reset_webhook(pool).await;
send_to_channel(cache_http, &self, embed).await send_to_channel(cache_http, &self, embed).await
} }
} else { } else {
@ -550,20 +560,20 @@ UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?
if let Error::Http(error) = e { if let Error::Http(error) = e {
if error.status_code() == Some(StatusCode::from_u16(404).unwrap()) { if error.status_code() == Some(StatusCode::from_u16(404).unwrap()) {
error!("Seeing channel is deleted. Removing reminder"); error!("Seeing channel is deleted. Removing reminder");
self.force_delete(&pool).await; self.force_delete(pool).await;
} else { } else {
self.refresh(&pool).await; self.refresh(pool).await;
} }
} else { } else {
self.refresh(&pool).await; self.refresh(pool).await;
} }
} else { } else {
self.refresh(&pool).await; self.refresh(pool).await;
} }
} else { } else {
info!("Reminder {} is paused", self.id); info!("Reminder {} is paused", self.id);
self.refresh(&pool).await; self.refresh(pool).await;
} }
} }
} }

View File

@ -323,7 +323,7 @@ async fn look(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) {
.iter() .iter()
.map(|reminder| reminder.display(&flags, &timezone)) .map(|reminder| reminder.display(&flags, &timezone))
.fold(0, |t, r| t + r.len()) .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); let pager = LookPager::new(flags, timezone);

View File

@ -3,6 +3,7 @@ pub(crate) mod pager;
use std::io::Cursor; use std::io::Cursor;
use chrono_tz::Tz; use chrono_tz::Tz;
use num_integer::Integer;
use rmp_serde::Serializer; use rmp_serde::Serializer;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::{
@ -78,7 +79,7 @@ impl ComponentDataModel {
.iter() .iter()
.map(|reminder| reminder.display(&flags, &pager.timezone)) .map(|reminder| reminder.display(&flags, &pager.timezone))
.fold(0, |t, r| t + r.len()) .fold(0, |t, r| t + r.len())
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH); .div_ceil(&EMBED_DESCRIPTION_MAX_LENGTH);
let channel_name = let channel_name =
if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) { if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {

View File

@ -14,11 +14,6 @@ use regex::Regex;
use serenity::model::prelude::AttachmentType; use serenity::model::prelude::AttachmentType;
lazy_static! { lazy_static! {
pub static ref REMIND_INTERVAL: u64 = env::var("REMIND_INTERVAL")
.map(|inner| inner.parse::<u64>().ok())
.ok()
.flatten()
.unwrap_or(10);
pub static ref DEFAULT_AVATAR: AttachmentType<'static> = ( pub static ref DEFAULT_AVATAR: AttachmentType<'static> = (
include_bytes!(concat!( include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),

View File

@ -1,4 +1,3 @@
#![feature(int_roundings)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -9,7 +8,6 @@ mod framework;
mod hooks; mod hooks;
mod interval_parser; mod interval_parser;
mod models; mod models;
mod sender;
mod time_parser; mod time_parser;
use std::{ use std::{
@ -24,6 +22,7 @@ use std::{
use chrono_tz::Tz; use chrono_tz::Tz;
use dotenv::dotenv; use dotenv::dotenv;
use log::info; use log::info;
use postman::initialize;
use serenity::{ use serenity::{
async_trait, async_trait,
client::Client, client::Client,
@ -39,15 +38,12 @@ use serenity::{
utils::shard_id, utils::shard_id,
}; };
use sqlx::mysql::MySqlPool; use sqlx::mysql::MySqlPool;
use tokio::{ use tokio::sync::RwLock;
sync::RwLock,
time::{Duration, Instant},
};
use crate::{ use crate::{
commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds}, commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds},
component_models::ComponentDataModel, component_models::ComponentDataModel,
consts::{CNC_GUILD, REMIND_INTERVAL, SUBSCRIPTION_ROLES, THEME_COLOR}, consts::{CNC_GUILD, SUBSCRIPTION_ROLES, THEME_COLOR},
framework::RegexFramework, framework::RegexFramework,
models::command_macro::CommandMacro, models::command_macro::CommandMacro,
}; };
@ -88,24 +84,10 @@ impl EventHandler for Handler {
if !self.is_loop_running.load(Ordering::Relaxed) { if !self.is_loop_running.load(Ordering::Relaxed) {
let ctx = ctx_base.clone(); let ctx = ctx_base.clone();
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
tokio::spawn(async move { tokio::spawn(async move {
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap(); initialize(ctx, &pool).await;
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;
}
}); });
self.is_loop_running.swap(true, Ordering::Relaxed); self.is_loop_running.swap(true, Ordering::Relaxed);

8
web/Cargo.toml Normal file
View File

@ -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]

8
web/src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}