From ecadf7a90d991bb8917787edc557e9689d08aed1 Mon Sep 17 00:00:00 2001 From: Max batleforc Date: Tue, 25 Jun 2024 02:53:13 +0200 Subject: [PATCH] feat: Start/Stop/Add keywords are working YIPEEE and update with surrealdb are fixed --- Cargo.lock | 1 + Cargo.toml | 1 + src/botv2/cmd/concour/keyword.rs | 11 +- src/botv2/cmd/concour/main.rs | 9 +- src/botv2/cmd/concour/mod.rs | 2 + src/botv2/cmd/concour/start.rs | 137 ++++++++++++++++++++++ src/botv2/cmd/concour/stop.rs | 108 +++++++++++++++++ src/botv2/domain/concour/add_keyword.rs | 3 +- src/botv2/domain/concour/start_concour.rs | 96 ++++++++++++--- src/botv2/domain/concour/stop_concour.rs | 60 +++++++--- src/db/concour.rs | 80 +++++++++++-- src/db/server_config.rs | 4 + src/db/user_image.rs | 7 +- src/event/schedule_job.rs | 59 ++++++++-- src/main.rs | 1 - 15 files changed, 515 insertions(+), 64 deletions(-) create mode 100644 src/botv2/cmd/concour/start.rs create mode 100644 src/botv2/cmd/concour/stop.rs diff --git a/Cargo.lock b/Cargo.lock index 81135b4..80e1c73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -703,6 +703,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", + "serde_with", "serde_yaml", "serial_test", "surrealdb", diff --git a/Cargo.toml b/Cargo.toml index fd70d16..0ce854b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ tokio-cron-scheduler = { version = "0.10", features = [ "tracing-subscriber", "signal", ] } +serde_with = "3.8.1" [[bin]] diff --git a/src/botv2/cmd/concour/keyword.rs b/src/botv2/cmd/concour/keyword.rs index 9d31a8c..ea89305 100644 --- a/src/botv2/cmd/concour/keyword.rs +++ b/src/botv2/cmd/concour/keyword.rs @@ -75,17 +75,22 @@ pub async fn keyword( .color(colour::Color::RED) } else { let concour = concour.unwrap(); - CreateEmbed::new() + let output = CreateEmbed::new() .title(concour.title) .description(concour.description) .field("Start date", concour.start_date.to_string(), false) .field("Periode", concour.periode.to_string(), false) - .field( + .field("keyword", concour.keywords.len().to_string(), true); + + if concour.role_recompense != 0 { + output.field( "Role récompense", RoleId::new(concour.role_recompense).mention().to_string(), false, ) - .field("keyword", concour.keywords.len().to_string(), true) + } else { + output + } } } Err(err) => match err { diff --git a/src/botv2/cmd/concour/main.rs b/src/botv2/cmd/concour/main.rs index 2559553..da2e186 100644 --- a/src/botv2/cmd/concour/main.rs +++ b/src/botv2/cmd/concour/main.rs @@ -1,5 +1,8 @@ use crate::botv2::{ - cmd::concour::{create::create, keyword::keyword, list::list, period::period, update::update}, + cmd::concour::{ + create::create, keyword::keyword, list::list, period::period, start::start, stop::stop, + update::update, + }, domain::concour::{ check_if_allowed::check_if_concour_allowed, get_channel_concour::get_channel_concour, }, @@ -17,7 +20,9 @@ use tracing::instrument; slash_command, prefix_command, category = "concour", - subcommands("get", "list", "update", "create", "keyword", "period"), + subcommands( + "get", "list", "update", "create", "keyword", "period", "start", "stop" + ), guild_only = true )] pub async fn concour( diff --git a/src/botv2/cmd/concour/mod.rs b/src/botv2/cmd/concour/mod.rs index 2909a5f..c68c9bf 100644 --- a/src/botv2/cmd/concour/mod.rs +++ b/src/botv2/cmd/concour/mod.rs @@ -3,4 +3,6 @@ pub mod keyword; pub mod list; pub mod main; pub mod period; +pub mod start; +pub mod stop; pub mod update; diff --git a/src/botv2/cmd/concour/start.rs b/src/botv2/cmd/concour/start.rs new file mode 100644 index 0000000..fbad844 --- /dev/null +++ b/src/botv2/cmd/concour/start.rs @@ -0,0 +1,137 @@ +use crate::botv2::{ + domain::concour::{ + check_if_allowed::check_if_allowed, + start_concour::{start_concour, start_concour_step2, StartConcourError}, + }, + init::{Context, Error}, +}; +use poise::{ + serenity_prelude::{model::colour, CreateEmbed, CreateEmbedFooter}, + CreateReply, +}; +use tracing::instrument; + +/// Start concour (only for admin) +#[instrument(skip(ctx), level = "info", fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))] +#[poise::command( + slash_command, + prefix_command, + category = "server_config", + guild_only = true +)] +pub async fn start(ctx: Context<'_>) -> Result<(), Error> { + let guild = match ctx.guild_id() { + Some(guild) => guild, + None => return Ok(()), + }; + let entity_name = ctx.data().entity_name.clone(); + let mut cron_schedule = ctx.data().scheduler.clone(); + let footer = CreateEmbedFooter::new(entity_name.clone()); + match check_if_allowed(guild.get(), ctx.author().id.get(), ctx.http()).await { + Ok(ok) => { + if !ok { + let embed = CreateEmbed::new() + .title("You are not an admin") + .color(colour::Color::RED) + .footer(footer); + if let Err(why) = ctx + .send(CreateReply::default().embed(embed).ephemeral(true)) + .await + { + tracing::error!("Error sending message: {:?}", why); + } + return Ok(()); + } + } + Err(_) => { + let embed = CreateEmbed::new() + .title("You are not an admin") + .color(colour::Color::RED) + .footer(footer); + if let Err(why) = ctx + .send(CreateReply::default().embed(embed).ephemeral(true)) + .await + { + tracing::error!("Error sending message: {:?}", why); + } + return Ok(()); + } + }; + + let (concour, success) = + match start_concour(guild.get(), ctx.channel_id().get(), &mut cron_schedule).await { + Ok(concour) => { + if concour.is_none() { + ( + CreateEmbed::new() + .title("No concour created") + .color(colour::Color::RED), + false, + ) + } else { + let concour = concour.unwrap(); + let keyword_index = concour.index_keyword as usize; + let keyword = concour.keywords.get(keyword_index).unwrap(); + let output = CreateEmbed::new() + .title(format!( + "Concour: {} Jour : {}", + concour.title, + concour.index_keyword + 1 + )) + .description(concour.description) + .field("Mot du jours ", keyword.to_string(), false) + .field("Good luck !", "", false) + .field( + "Vous avez jusqu'a demain 17h", + "HARD CODED FOR THE MOMENT", + false, + ) + .color(colour::Color::DARK_GREEN); + (output, true) + } + } + Err(err) => ( + match err { + StartConcourError::AlreadyOnGoing => CreateEmbed::new() + .title("Concour already OnGoing") + .color(colour::Color::RED), + StartConcourError::DoesntExist => CreateEmbed::new() + .title("Concour doesn't exist") + .color(colour::Color::RED), + StartConcourError::KeyWordListEmpty => CreateEmbed::new() + .title("Keyword list empty") + .color(colour::Color::RED), + StartConcourError::FinishedKeyWordList => CreateEmbed::new() + .title("Finished keyword list, add new one") + .color(colour::Color::RED), + _ => CreateEmbed::new() + .title("Error while creating concour") + .field("Please contact your administrator", "", false) + .field("Your concour is possibly not initied correctly", "", false) + .color(colour::Color::RED), + }, + false, + ), + }; + let mut builder = CreateReply::default().ephemeral(!success); + builder = builder.embed(concour.footer(footer)); + match ctx.send(builder).await { + Ok(handler) => { + if success { + let message_id = handler.message().await.unwrap().id.get(); + tracing::info!(msg_id = message_id, "Concour started"); + match start_concour_step2(guild.get(), ctx.channel_id().get(), message_id).await { + Ok(_) => {} + Err(err) => { + tracing::error!("Error starting concour step 2: {:?}", err); + } + } + } + } + Err(why) => { + tracing::error!("Error sending message: {:?}", why); + } + } + + Ok(()) +} diff --git a/src/botv2/cmd/concour/stop.rs b/src/botv2/cmd/concour/stop.rs new file mode 100644 index 0000000..3d89fed --- /dev/null +++ b/src/botv2/cmd/concour/stop.rs @@ -0,0 +1,108 @@ +use crate::botv2::{ + domain::concour::{ + check_if_allowed::check_if_allowed, + stop_concour::{stop_concour, StopConcourError}, + }, + init::{Context, Error}, +}; +use poise::{ + serenity_prelude::{model::colour, CreateEmbed, CreateEmbedFooter}, + CreateReply, +}; +use tracing::instrument; + +/// Stop concour (only for admin) +#[instrument(skip(ctx), level = "info", fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))] +#[poise::command( + slash_command, + prefix_command, + category = "server_config", + guild_only = true +)] +pub async fn stop(ctx: Context<'_>) -> Result<(), Error> { + let guild = match ctx.guild_id() { + Some(guild) => guild, + None => return Ok(()), + }; + let entity_name = ctx.data().entity_name.clone(); + let mut cron_schedule = ctx.data().scheduler.clone(); + let footer = CreateEmbedFooter::new(entity_name.clone()); + match check_if_allowed(guild.get(), ctx.author().id.get(), ctx.http()).await { + Ok(ok) => { + if !ok { + let embed = CreateEmbed::new() + .title("You are not an admin") + .color(colour::Color::RED) + .footer(footer); + if let Err(why) = ctx + .send(CreateReply::default().embed(embed).ephemeral(true)) + .await + { + tracing::error!("Error sending message: {:?}", why); + } + return Ok(()); + } + } + Err(_) => { + let embed = CreateEmbed::new() + .title("You are not an admin") + .color(colour::Color::RED) + .footer(footer); + if let Err(why) = ctx + .send(CreateReply::default().embed(embed).ephemeral(true)) + .await + { + tracing::error!("Error sending message: {:?}", why); + } + return Ok(()); + } + }; + + let (concour, success) = + match stop_concour(guild.get(), ctx.channel_id().get(), &mut cron_schedule).await { + Ok(concour) => { + if concour.is_none() { + ( + CreateEmbed::new() + .title("No concour created") + .color(colour::Color::RED), + false, + ) + } else { + let concour = concour.unwrap(); + let output = CreateEmbed::new() + .title(format!("Concour: {}", concour.title)) + .description(concour.description) + .field("Concour mis en pause ", "", false) + .color(colour::Color::DARK_GREEN); + (output, true) + } + } + Err(err) => ( + match err { + StopConcourError::AlreadyStopped => CreateEmbed::new() + .title("Concour already Stopped") + .color(colour::Color::RED), + StopConcourError::DoesntExist => CreateEmbed::new() + .title("Concour doesn't exist") + .color(colour::Color::RED), + StopConcourError::NotScheduled => CreateEmbed::new() + .title("Concour not scheduled") + .color(colour::Color::RED), + _ => CreateEmbed::new() + .title("Error while creating concour") + .field("Please contact your administrator", "", false) + .field("Your concour is possibly not initied correctly", "", false) + .color(colour::Color::RED), + }, + false, + ), + }; + let mut builder = CreateReply::default().ephemeral(!success); + builder = builder.embed(concour.footer(footer)); + if let Err(why) = ctx.send(builder).await { + tracing::error!("Error sending message: {:?}", why); + } + + Ok(()) +} diff --git a/src/botv2/domain/concour/add_keyword.rs b/src/botv2/domain/concour/add_keyword.rs index eb723d3..a6f8807 100644 --- a/src/botv2/domain/concour/add_keyword.rs +++ b/src/botv2/domain/concour/add_keyword.rs @@ -51,7 +51,7 @@ pub async fn add_keyword_concour( let text = res.text().await.unwrap(); let split_delimiter = delimiter.unwrap_or("\n".to_string()); text.split(&split_delimiter).for_each(|x| { - concour.keywords.push(x.to_string()); + concour.keywords.push(x.to_string().replace('\r', "")); }); } Err(err) => { @@ -61,7 +61,6 @@ pub async fn add_keyword_concour( )); } } - concour.keywords.push(url); } KeyWordSource::Text(text, delimiter) => { let split_delimiter = delimiter.unwrap_or("\n".to_string()); diff --git a/src/botv2/domain/concour/start_concour.rs b/src/botv2/domain/concour/start_concour.rs index 0118166..74785f9 100644 --- a/src/botv2/domain/concour/start_concour.rs +++ b/src/botv2/domain/concour/start_concour.rs @@ -1,19 +1,26 @@ use serde::{Deserialize, Serialize}; use tracing::{info, instrument}; -use crate::db::concour::Concour; +use crate::{ + db::concour::{Concour, ConcourStatus}, + event::schedule_job::ScheduleJob, +}; #[derive(Serialize, Deserialize, Debug, Clone)] pub enum StartConcourError { + AlreadyOnGoing, DoesntExist, + KeyWordListEmpty, + FinishedKeyWordList, FindError(String), UnknownError(String), } -#[instrument(level = "info")] +#[instrument(level = "info", skip(cron_scheduler))] pub async fn start_concour( server_id: u64, channel_id: u64, + cron_scheduler: &mut ScheduleJob, ) -> Result, StartConcourError> { let concour = match Concour::find_by_server_id_channel_id(&server_id, &channel_id).await { Ok(list_concour) => list_concour, @@ -29,19 +36,76 @@ pub async fn start_concour( info!("Concour doesn't exist"); return Err(StartConcourError::DoesntExist); } - todo!("Setup logic to start the waiting period for the concour"); - // let mut concour = concour.unwrap(); + let mut concour = concour.unwrap(); + if concour.keywords.is_empty() { + tracing::warn!("Keyword list is empty"); + return Err(StartConcourError::KeyWordListEmpty); + } + let keyword_len = concour.keywords.len() as u64; + if concour.index_keyword.gt(&keyword_len) { + tracing::warn!(keyword_len, concour.index_keyword, "Finished keyword list"); + return Err(StartConcourError::FinishedKeyWordList); + } + if concour.status == ConcourStatus::OnGoing { + tracing::warn!("Concour already OnGoing"); + return Err(StartConcourError::AlreadyOnGoing); + } + match cron_scheduler + .add_concour_cron_job(server_id, channel_id, "*/1 * * * * *".to_string()) + .await + { + Ok(_) => {} + Err(_) => { + tracing::error!("Error starting cron job"); + return Err(StartConcourError::UnknownError( + "Error starting cron job".to_string(), + )); + } + } + concour.status = ConcourStatus::OnGoing; - // // Update status to started - - // match concour.update().await { - // Ok(_) => {} - // Err(err) => { - // tracing::error!(error = err.to_string(), "Error updating concour"); - // return Err(StartConcourError::UnknownError( - // "Error updating concour".to_string(), - // )); - // } - // } - // Ok(Some(concour)) + match concour.update().await { + Ok(_) => {} + Err(err) => { + tracing::error!(error = err.to_string(), "Error updating concour"); + return Err(StartConcourError::UnknownError( + "Error updating concour".to_string(), + )); + } + } + Ok(Some(concour)) +} + +#[instrument(level = "info")] +pub async fn start_concour_step2( + server_id: u64, + channel_id: u64, + message_id: u64, +) -> Result<(), StartConcourError> { + let concour = match Concour::find_by_server_id_channel_id(&server_id, &channel_id).await { + Ok(list_concour) => list_concour, + Err(err) => { + tracing::error!(error = err.to_string(), "Error finding concour"); + return Err(StartConcourError::UnknownError( + "Error finding concour".to_string(), + )); + } + }; + + if concour.is_none() { + info!("Concour doesn't exist"); + return Err(StartConcourError::DoesntExist); + } + let mut concour = concour.unwrap(); + concour.last_message_id = Some(message_id); + match concour.update().await { + Ok(_) => {} + Err(err) => { + tracing::error!(error = err.to_string(), "Error updating concour"); + return Err(StartConcourError::UnknownError( + "Error updating concour".to_string(), + )); + } + } + Ok(()) } diff --git a/src/botv2/domain/concour/stop_concour.rs b/src/botv2/domain/concour/stop_concour.rs index 8add4b7..e52fd40 100644 --- a/src/botv2/domain/concour/stop_concour.rs +++ b/src/botv2/domain/concour/stop_concour.rs @@ -1,19 +1,25 @@ use serde::{Deserialize, Serialize}; use tracing::{info, instrument}; -use crate::db::concour::Concour; +use crate::{ + db::concour::{Concour, ConcourStatus}, + event::schedule_job::{ScheduleJob, StopScheduleJob}, +}; #[derive(Serialize, Deserialize, Debug, Clone)] pub enum StopConcourError { DoesntExist, + AlreadyStopped, + NotScheduled, FindError(String), UnknownError(String), } -#[instrument(level = "info")] +#[instrument(level = "info", skip(cron_scheduler))] pub async fn stop_concour( server_id: u64, channel_id: u64, + cron_scheduler: &mut ScheduleJob, ) -> Result, StopConcourError> { let concour = match Concour::find_by_server_id_channel_id(&server_id, &channel_id).await { Ok(list_concour) => list_concour, @@ -29,19 +35,43 @@ pub async fn stop_concour( info!("Concour doesn't exist"); return Err(StopConcourError::DoesntExist); } - todo!("Setup logic to stop the waiting period for the concour"); - // let mut concour = concour.unwrap(); + let mut concour = concour.unwrap(); - // // Update status to Paused + if concour.status == ConcourStatus::Paused { + return Err(StopConcourError::AlreadyStopped); + } + match cron_scheduler + .stop_scheduled_job(server_id, channel_id) + .await + { + Ok(_) => {} + Err(e) => { + tracing::error!(err = e.to_string(), "Error stopping cron job"); + match e { + StopScheduleJob::JobNotFound => { + tracing::error!("Job not found"); + return Err(StopConcourError::NotScheduled); + } + StopScheduleJob::RemoveFailed => { + tracing::error!("Remove failed"); + return Err(StopConcourError::UnknownError( + "Error stopping cron job".to_string(), + )); + } + } + } + } + concour.status = ConcourStatus::Paused; + concour.last_message_id = None; - // match concour.update().await { - // Ok(_) => {} - // Err(err) => { - // tracing::error!(error = err.to_string(), "Error updating concour"); - // return Err(StopConcourError::UnknownError( - // "Error updating concour".to_string(), - // )); - // } - // } - // Ok(Some(concour)) + match concour.update().await { + Ok(_) => {} + Err(err) => { + tracing::error!(error = err.to_string(), "Error updating concour"); + return Err(StopConcourError::UnknownError( + "Error updating concour".to_string(), + )); + } + } + Ok(Some(concour)) } diff --git a/src/db/concour.rs b/src/db/concour.rs index 572c513..1cecc3c 100644 --- a/src/db/concour.rs +++ b/src/db/concour.rs @@ -3,11 +3,13 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt; use surrealdb::opt::Resource; +use surrealdb::sql::Thing; +use tracing::instrument; use utoipa::ToSchema; const CONCOUR: &str = "concour"; -#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)] +#[derive(Debug, Serialize, Deserialize, Clone, ToSchema, PartialEq, Eq)] pub enum ConcourStatus { Created, Paused, @@ -21,6 +23,19 @@ impl fmt::Display for ConcourStatus { } } +#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)] +pub struct ConcourRecord { + pub id: Thing, + pub server_id: u64, + pub channel_id: u64, +} + +#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)] +pub struct ConcourWinner { + pub user_id: u64, + pub date: chrono::DateTime, +} + #[derive(Debug, Serialize, Deserialize, Clone, ToSchema)] pub struct Concour { pub server_id: u64, @@ -34,7 +49,8 @@ pub struct Concour { pub banner: Option, pub index_keyword: u64, pub status: ConcourStatus, - pub winner: HashMap)>, + pub winner: HashMap, + pub last_message_id: Option, } impl Default for Concour { @@ -52,6 +68,7 @@ impl Default for Concour { index_keyword: 0, status: ConcourStatus::Created, winner: HashMap::new(), + last_message_id: None, } } } @@ -71,8 +88,10 @@ impl Concour { index_keyword: 0, status: ConcourStatus::Created, winner: HashMap::new(), + last_message_id: None, }) } + #[instrument(level = "info")] pub async fn create(&self) -> Result<(), surrealdb::Error> { match DB.create(Resource::from(CONCOUR)).content(&self).await { Ok(_) => {} @@ -82,21 +101,61 @@ impl Concour { }; Ok(()) } - pub async fn update(&self) -> Result<(), surrealdb::Error> { + #[instrument(level = "info")] + pub async fn update(&self) -> Result { + let id = match Concour::get_records(&self.server_id, &self.channel_id).await { + Ok(Some(concour)) => concour.id, + Ok(None) => return Ok(false), + Err(e) => { + tracing::error!("Error getting concour id: {:?}", e); + return Err(e); + } + }; + match DB + .update::>((CONCOUR.to_string(), id)) + .merge(self) + .await + { + Ok(concour) => { + if concour.is_none() { + return Ok(false); + } + tracing::info!("Concour updated: {:?}", concour); + Ok(true) + } + Err(e) => { + tracing::error!("Error updating concour: {:?}", e); + Err(e) + } + } + } + #[instrument(level = "info")] + pub async fn get_records( + server_id: &u64, + channel_id: &u64, + ) -> Result, surrealdb::Error> { let sql = format!( - "UPDATE {} SET title = '{}', description = '{}', start_date = '{}', periode = '{}', role_récompense = {}, keywords = '{:?}', banner = '{:?}', index_keyword = {}, status = '{}', winner = '{:?}' WHERE server_id = {} and channel_id = {}", - CONCOUR, self.title, self.description, self.start_date, self.periode, self.role_recompense, self.keywords, self.banner, self.index_keyword, self.status, self.winner, self.server_id, self.channel_id + "SELECT id,server_id,channel_id FROM {} WHERE server_id = {} AND channel_id = {}", + CONCOUR, server_id, channel_id ); - match DB.query(sql).await { - Ok(res) => { - println!("{:?}", res); + let mut results = match DB.query(&sql).await { + Ok(results) => results, + Err(e) => { + return Err(e); + } + }; + let concour: ConcourRecord = match results.take(0) { + Ok(Some(concour)) => concour, + Ok(None) => { + return Ok(None); } Err(e) => { return Err(e); } }; - Ok(()) + Ok(Some(concour)) } + #[instrument(level = "info")] pub async fn find_by_server_id_channel_id( server_id: &u64, channel_id: &u64, @@ -122,6 +181,7 @@ impl Concour { }; Ok(Some(concour)) } + #[instrument(level = "info")] pub async fn find_by_server_id(server_id: &u64) -> Result, surrealdb::Error> { let sql = format!("SELECT * FROM {} WHERE server_id = {}", CONCOUR, server_id); let mut results = match DB.query(&sql).await { @@ -136,7 +196,7 @@ impl Concour { } Ok(concours) } - + #[instrument(level = "info")] pub async fn find_by_status(status: &ConcourStatus) -> Result, surrealdb::Error> { let sql = format!("SELECT * FROM {} WHERE status = '{}'", CONCOUR, status); let mut results = match DB.query(&sql).await { diff --git a/src/db/server_config.rs b/src/db/server_config.rs index 83b4eae..8af95fe 100644 --- a/src/db/server_config.rs +++ b/src/db/server_config.rs @@ -2,6 +2,7 @@ use super::init::DB; use serde::{Deserialize, Serialize}; use std::fmt; use surrealdb::opt::Resource; +use tracing::instrument; use utoipa::ToSchema; const SERVER_CONFIG: &str = "server_config"; @@ -37,6 +38,7 @@ impl ServerConfig { admin_role: Vec::new(), }) } + #[instrument(level = "info")] pub async fn create(&self) -> Result<(), surrealdb::Error> { match DB .create(Resource::from(SERVER_CONFIG)) @@ -47,6 +49,7 @@ impl ServerConfig { Err(e) => Err(e), } } + #[instrument(level = "info")] pub async fn update(&self) -> Result<(), surrealdb::Error> { let sql = format!( "UPDATE {} SET enable = {}, auto_meme = {}, auto_concour = {}, admin_role = {:?} WHERE server_id = {}", @@ -62,6 +65,7 @@ impl ServerConfig { Err(e) => Err(e), } } + #[instrument(level = "info")] pub async fn find_by_server_id( server_id: &u64, ) -> Result, surrealdb::Error> { diff --git a/src/db/user_image.rs b/src/db/user_image.rs index 76fb84e..55b4653 100644 --- a/src/db/user_image.rs +++ b/src/db/user_image.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use surrealdb::opt::Resource; +use tracing::instrument; use utoipa::ToSchema; use super::init::DB; @@ -21,7 +22,7 @@ impl User { enable, }) } - + #[instrument(level = "info")] pub async fn create(&self) -> Result<(), surrealdb::Error> { match DB.create(Resource::from(USERIMAGE)).content(&self).await { Ok(_) => {} @@ -31,7 +32,7 @@ impl User { }; Ok(()) } - + #[instrument(level = "info")] pub async fn update(&self) -> Result<(), surrealdb::Error> { let sql = format!( "UPDATE {} SET enable = {} WHERE server_id = {} AND user_id = {}", @@ -47,7 +48,7 @@ impl User { }; Ok(()) } - + #[instrument(level = "info")] pub async fn find_by_server_id_user_id( server_id: &u64, user_id: &u64, diff --git a/src/event/schedule_job.rs b/src/event/schedule_job.rs index d8a275e..7a3759a 100644 --- a/src/event/schedule_job.rs +++ b/src/event/schedule_job.rs @@ -1,16 +1,33 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + fmt::{self, Display}, + sync::Arc, +}; +use tokio::sync::RwLock; use tokio_cron_scheduler::{Job, JobScheduler}; use tracing::{error, info, instrument}; use uuid::Uuid; +#[derive(Debug)] +pub enum StopScheduleJob { + JobNotFound, + RemoveFailed, +} + +impl Display for StopScheduleJob { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + #[derive(Clone)] pub struct ScheduleJob { - pub job_id: HashMap<(u64, u64), Uuid>, + pub job_id: Arc>>, pub scheduler: JobScheduler, } impl ScheduleJob { - #[instrument()] + #[instrument(level = "info")] pub async fn start_cron_scheduler() -> Result { let scheduler = JobScheduler::new().await; let mut future_self = match scheduler { @@ -41,12 +58,14 @@ impl ScheduleJob { } } - #[instrument(skip(self))] + #[instrument(skip(self), level = "info")] pub async fn stop_cron_scheduler(&mut self) -> &mut Self { - for (server_id, channel_id) in self.job_id.keys() { + let job_id = self.job_id.write().await; + + for (server_id, channel_id) in job_id.keys() { match self .scheduler - .remove(self.job_id.get(&(*server_id, *channel_id)).unwrap()) + .remove(job_id.get(&(*server_id, *channel_id)).unwrap()) .await { Ok(_) => { @@ -57,6 +76,8 @@ impl ScheduleJob { } } } + + drop(job_id); match self.scheduler.shutdown().await { Ok(_) => { info!("Cron scheduler stopped"); @@ -69,8 +90,8 @@ impl ScheduleJob { } } - #[instrument(skip(self))] - pub async fn add_cron_job( + #[instrument(skip(self), level = "info")] + pub async fn add_concour_cron_job( &mut self, server_id: u64, channel_id: u64, @@ -91,7 +112,10 @@ impl ScheduleJob { match self.scheduler.add(job).await { Ok(job_uid) => { info!("Cron job added"); - self.job_id.insert((server_id, channel_id), job_uid); + { + let mut job_id = self.job_id.write().await; + job_id.insert((server_id, channel_id), job_uid); + } Ok(job_uid) } Err(e) => { @@ -100,19 +124,30 @@ impl ScheduleJob { } } } - #[instrument(skip(self))] - pub async fn stop_scheduled_job(&mut self, server_id: u64, channel_id: u64) { - match self.job_id.remove(&(server_id, channel_id)) { + #[instrument(skip(self), level = "info")] + pub async fn stop_scheduled_job( + &mut self, + server_id: u64, + channel_id: u64, + ) -> Result<(), StopScheduleJob> { + let remove_job = { + let mut job_id = self.job_id.write().await; + job_id.remove(&(server_id, channel_id)) + }; + match remove_job { Some(job_uid) => match self.scheduler.remove(&job_uid).await { Ok(_) => { info!("Cron job removed"); + Ok(()) } Err(e) => { error!("Error removing cron job: {:?}", e); + Err(StopScheduleJob::RemoveFailed) } }, None => { error!("Cron job not found"); + Err(StopScheduleJob::JobNotFound) } } } diff --git a/src/main.rs b/src/main.rs index bb704c4..711a866 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,7 +79,6 @@ async fn main() -> std::io::Result<()> { .bind(("0.0.0.0", port))? .run() .await?; - cron_scheduler.stop_scheduled_job(123, 123).await; info!("API Server stopped."); tx_bot.send(()).unwrap(); cron_scheduler.stop_cron_scheduler().await;