use crate::config::GlobalData; use crate::discord::shop::restock_shop; use crate::error::Error; use crate::models::birthday::BirthdayEntry; use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType}; use chrono::{Days, Duration, TimeZone, Timelike, Utc}; use j_db::database::Database; use j_db::model::JdbModel; use serde::{Deserialize, Serialize}; use serenity::all::Mentionable; use serenity::prelude::Context; #[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)] pub enum TaskType { RemoveRole { user_id: u64, role_id: u64 }, CheckBirthdays, HandleReload, RestockShop, } impl TaskType { pub fn exclusive(&self) -> bool { matches!( self, TaskType::CheckBirthdays | TaskType::HandleReload | TaskType::RestockShop ) } } #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Task { id: Option, pub task_type: TaskType, pub time: chrono::DateTime, } impl JdbModel for Task { fn id(&self) -> Option { self.id } fn set_id(&mut self, id: u64) { self.id = Some(id) } fn tree() -> String { "tasks".to_string() } fn check_unique(&self, _other: &Self) -> bool { true } } impl Task { pub fn add_task( db: &Database, task_type: TaskType, time: chrono::DateTime, ) -> Result<(), Error> { if task_type.exclusive() { let old_tasks: Vec = db .filter(|_, task: &Task| task.task_type == task_type)? .collect(); for task in old_tasks { db.remove::(task.id().unwrap())?; } } db.insert::(Task { id: None, task_type, time, })?; println!("Adding {:?} task to run at {}", task_type, time); Ok(()) } pub async fn create_reoccurring_tasks(ctx: &Context) -> Result<(), Error> { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); Task::add_task(&global_data.db, TaskType::CheckBirthdays, Utc::now())?; Task::add_task(&global_data.db, TaskType::RestockShop, Utc::now())?; Task::add_task( &global_data.db, TaskType::HandleReload, Utc::now() + Duration::hours(1), )?; Ok(()) } pub async fn run_tasks(ctx: &Context) -> Result<(), Error> { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); let active_tasks: Vec = global_data .db .filter(|_, task: &Task| task.time < chrono::Utc::now())? .collect(); for task in active_tasks { match task.task_type { TaskType::RemoveRole { user_id, role_id } => { let user = global_data .cfg .guild_id .member(&ctx.http, user_id) .await .unwrap(); println!("Removing role {} from {}", role_id, user.display_name()); user.remove_role(&ctx.http, role_id).await.unwrap(); } TaskType::CheckBirthdays => { println!("Checking Birthdays"); let todays_birthdays = BirthdayEntry::todays_birthdays( &global_data.db, chrono::Utc::now().date_naive(), ) .unwrap(); for birth in todays_birthdays { if let Ok(user) = global_data .cfg .guild_id .member(&ctx.http, birth.discord_id) .await { global_data .cfg .announcement_channel .say(&ctx.http, format!("Happy birthday {}!", user.mention())) .await .unwrap(); let compliment = RandomResponseTemplate::get_random_response( &global_data.db, ResponseType::Compliment, user.display_name(), ) .unwrap() .unwrap(); global_data .cfg .announcement_channel .say(&ctx.http, compliment) .await .unwrap(); } } let next_check = chrono_tz::America::Chicago .from_utc_datetime(&Utc::now().naive_utc()) .with_hour(8) .unwrap() .with_minute(0) .unwrap() .with_second(0) .unwrap() .checked_add_days(Days::new(1)) .unwrap(); Task::add_task( &global_data.db, TaskType::CheckBirthdays, next_check.with_timezone(&Utc), )?; } TaskType::HandleReload => { println!("Reloading config..."); let res = global_data.reload().await; match res { Ok(_) => println!("Finished reloading config!"), Err(err) => println!("Error reloading config: {:?}", err), } Task::add_task( &global_data.db, TaskType::HandleReload, Utc::now() + Duration::hours(1), )?; } TaskType::RestockShop => { println!("Restocking Shop..."); let res = restock_shop(ctx, global_data).await; match res { Ok(_) => println!("Finished restocking shop!"), Err(err) => println!("Error restocking shop: {:?}", err), } Task::add_task( &global_data.db, TaskType::RestockShop, Utc::now() + Duration::hours(1), )?; } } let _ = global_data.db.remove::(task.id().unwrap()).is_ok(); } Ok(()) } }