feat: Mise en place de cronjob fonctionne avec une logique concour qui fonctionne
This commit is contained in:
parent
9c21b0dfdf
commit
0e4a8ae580
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -690,6 +690,7 @@ dependencies = [
|
|||||||
"actix-cors",
|
"actix-cors",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"cron",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"openssl",
|
"openssl",
|
||||||
@ -709,6 +710,7 @@ dependencies = [
|
|||||||
"surrealdb",
|
"surrealdb",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-cron",
|
||||||
"tokio-cron-scheduler",
|
"tokio-cron-scheduler",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -4277,6 +4279,19 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-cron"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b29e8f172aedef409e518b9b88ea7aeb47fd6248cf7673f0a8991c0913601a55"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"cron",
|
||||||
|
"futures",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-cron-scheduler"
|
name = "tokio-cron-scheduler"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
@ -48,6 +48,8 @@ tokio-cron-scheduler = { version = "0.10", features = [
|
|||||||
"signal",
|
"signal",
|
||||||
] }
|
] }
|
||||||
serde_with = "3.8.1"
|
serde_with = "3.8.1"
|
||||||
|
tokio-cron = "0.1.3"
|
||||||
|
cron = "0.12.1"
|
||||||
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::botv2::{
|
use crate::botv2::{
|
||||||
domain::concour::{
|
domain::concour::{
|
||||||
check_if_allowed::check_if_allowed,
|
check_if_allowed::check_if_allowed,
|
||||||
@ -5,11 +7,13 @@ use crate::botv2::{
|
|||||||
},
|
},
|
||||||
init::{Context, Error},
|
init::{Context, Error},
|
||||||
};
|
};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use cron::Schedule;
|
||||||
use poise::{
|
use poise::{
|
||||||
serenity_prelude::{model::colour, CreateEmbed, CreateEmbedFooter, Mentionable, RoleId},
|
serenity_prelude::{model::colour, CreateEmbed, CreateEmbedFooter, Mentionable, RoleId},
|
||||||
CreateReply,
|
CreateReply,
|
||||||
};
|
};
|
||||||
use tracing::{info, instrument};
|
use tracing::{info, instrument, warn};
|
||||||
|
|
||||||
/// Update the step duration of a concour (only for admin)
|
/// Update the step duration of a concour (only for admin)
|
||||||
#[instrument(skip(ctx), level = "info", fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))]
|
#[instrument(skip(ctx), level = "info", fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))]
|
||||||
@ -21,7 +25,8 @@ use tracing::{info, instrument};
|
|||||||
)]
|
)]
|
||||||
pub async fn period(
|
pub async fn period(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "Dureer d'une étape du concours"] step: u64,
|
#[description = "Dureer d'une étape du concours au format CRON (min: 6H d'interval)"]
|
||||||
|
cron_schedule: String,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let guild = match ctx.guild_id() {
|
let guild = match ctx.guild_id() {
|
||||||
Some(guild) => guild,
|
Some(guild) => guild,
|
||||||
@ -59,12 +64,11 @@ pub async fn period(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let duration = match step.try_into() {
|
let schedule = match Schedule::from_str(&cron_schedule) {
|
||||||
Ok(val) => time::Duration::new(val, 0),
|
Ok(schedule) => schedule,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
info!("Invalid Duration input");
|
|
||||||
let embed = CreateEmbed::new()
|
let embed = CreateEmbed::new()
|
||||||
.title("Invalid duration input")
|
.title("Invalid CRON format")
|
||||||
.color(colour::Color::RED)
|
.color(colour::Color::RED)
|
||||||
.footer(footer);
|
.footer(footer);
|
||||||
if let Err(why) = ctx
|
if let Err(why) = ctx
|
||||||
@ -76,7 +80,37 @@ pub async fn period(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let concour = match set_periode(guild.get(), ctx.channel_id().get(), duration).await {
|
let next_to_come = schedule.upcoming(Utc).take(10);
|
||||||
|
// Check if the next_to_come has at least 6 hours of interval
|
||||||
|
let mut last: Option<DateTime<Utc>> = None;
|
||||||
|
for next in next_to_come {
|
||||||
|
if let Some(last) = last {
|
||||||
|
if next.timestamp() - last.timestamp() < 6 * 60 * 60 {
|
||||||
|
let embed = CreateEmbed::new()
|
||||||
|
.title("Interval between steps is too short")
|
||||||
|
.description("Please provide a CRON schedule with at least 6 hours of interval")
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
warn!(
|
||||||
|
cron_schedule = cron_schedule,
|
||||||
|
"Interval between steps is too short"
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = Some(next);
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
cron_schedule = cron_schedule.as_str(),
|
||||||
|
"Setting concour periode"
|
||||||
|
);
|
||||||
|
let concour = match set_periode(guild.get(), ctx.channel_id().get(), cron_schedule).await {
|
||||||
Ok(concour) => {
|
Ok(concour) => {
|
||||||
if concour.is_none() {
|
if concour.is_none() {
|
||||||
CreateEmbed::new()
|
CreateEmbed::new()
|
||||||
|
@ -57,7 +57,6 @@ pub async fn start(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (concour, success) = match start_concour(
|
let (concour, success) = match start_concour(
|
||||||
guild.get(),
|
guild.get(),
|
||||||
ctx.channel_id().get(),
|
ctx.channel_id().get(),
|
||||||
@ -85,11 +84,11 @@ pub async fn start(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
concour.index_keyword + 1
|
concour.index_keyword + 1
|
||||||
))
|
))
|
||||||
.description(concour.description)
|
.description(concour.description)
|
||||||
.field("Mot du jours ", keyword.to_string(), false)
|
.field("Word of the day ", keyword.to_string(), false)
|
||||||
.field("Good luck !", "", false)
|
.field("Good luck !", "", false)
|
||||||
.field(
|
.field(
|
||||||
"Vous avez jusqu'a demain 17h",
|
"Please see when the concour end in the concour get command",
|
||||||
"HARD CODED FOR THE MOMENT",
|
"",
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.color(colour::Color::DARK_GREEN);
|
.color(colour::Color::DARK_GREEN);
|
||||||
@ -110,6 +109,9 @@ pub async fn start(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
StartConcourError::FinishedKeyWordList => CreateEmbed::new()
|
StartConcourError::FinishedKeyWordList => CreateEmbed::new()
|
||||||
.title("Finished keyword list, add new one")
|
.title("Finished keyword list, add new one")
|
||||||
.color(colour::Color::RED),
|
.color(colour::Color::RED),
|
||||||
|
StartConcourError::RoleRecompenseEmpty => CreateEmbed::new()
|
||||||
|
.title("Role recompense not defined")
|
||||||
|
.color(colour::Color::RED),
|
||||||
_ => CreateEmbed::new()
|
_ => CreateEmbed::new()
|
||||||
.title("Error while creating concour")
|
.title("Error while creating concour")
|
||||||
.field("Please contact your administrator", "", false)
|
.field("Please contact your administrator", "", false)
|
||||||
|
@ -14,7 +14,7 @@ pub enum SetPeriodeConcourError {
|
|||||||
pub async fn set_periode(
|
pub async fn set_periode(
|
||||||
server_id: u64,
|
server_id: u64,
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
periode: time::Duration,
|
periode: String,
|
||||||
) -> Result<Option<Concour>, SetPeriodeConcourError> {
|
) -> Result<Option<Concour>, SetPeriodeConcourError> {
|
||||||
let concour = match Concour::find_by_server_id_channel_id(&server_id, &channel_id).await {
|
let concour = match Concour::find_by_server_id_channel_id(&server_id, &channel_id).await {
|
||||||
Ok(list_concour) => list_concour,
|
Ok(list_concour) => list_concour,
|
||||||
|
@ -13,6 +13,7 @@ pub enum StartConcourError {
|
|||||||
DoesntExist,
|
DoesntExist,
|
||||||
KeyWordListEmpty,
|
KeyWordListEmpty,
|
||||||
FinishedKeyWordList,
|
FinishedKeyWordList,
|
||||||
|
RoleRecompenseEmpty,
|
||||||
FindError(String),
|
FindError(String),
|
||||||
UnknownError(String),
|
UnknownError(String),
|
||||||
}
|
}
|
||||||
@ -39,6 +40,10 @@ pub async fn start_concour(
|
|||||||
return Err(StartConcourError::DoesntExist);
|
return Err(StartConcourError::DoesntExist);
|
||||||
}
|
}
|
||||||
let mut concour = concour.unwrap();
|
let mut concour = concour.unwrap();
|
||||||
|
if concour.role_recompense == 0 {
|
||||||
|
tracing::warn!("Role recompense is empty");
|
||||||
|
return Err(StartConcourError::RoleRecompenseEmpty);
|
||||||
|
}
|
||||||
if concour.keywords.is_empty() {
|
if concour.keywords.is_empty() {
|
||||||
tracing::warn!("Keyword list is empty");
|
tracing::warn!("Keyword list is empty");
|
||||||
return Err(StartConcourError::KeyWordListEmpty);
|
return Err(StartConcourError::KeyWordListEmpty);
|
||||||
@ -53,7 +58,7 @@ pub async fn start_concour(
|
|||||||
return Err(StartConcourError::AlreadyOnGoing);
|
return Err(StartConcourError::AlreadyOnGoing);
|
||||||
}
|
}
|
||||||
match cron_scheduler
|
match cron_scheduler
|
||||||
.add_concour_cron_job(server_id, channel_id, "*/1 * * * * *".to_string(), http)
|
.add_concour_cron_job(server_id, channel_id, concour.periode.clone(), http)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -4,8 +4,9 @@ use poise::serenity_prelude as serenity;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serenity::all::{CreateAttachment, CreateMessage};
|
use serenity::all::{CreateAttachment, CreateMessage};
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tracing::info;
|
use tracing::{info, instrument};
|
||||||
|
|
||||||
|
#[instrument(skip(ctx, _framework, data), err, level = "trace")]
|
||||||
pub async fn event_handler(
|
pub async fn event_handler(
|
||||||
ctx: &serenity::Context,
|
ctx: &serenity::Context,
|
||||||
event: &serenity::FullEvent,
|
event: &serenity::FullEvent,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::init::DB;
|
use super::init::DB;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use surrealdb::opt::Resource;
|
use surrealdb::opt::Resource;
|
||||||
use surrealdb::sql::Thing;
|
use surrealdb::sql::Thing;
|
||||||
@ -34,6 +33,7 @@ pub struct ConcourRecord {
|
|||||||
pub struct ConcourWinner {
|
pub struct ConcourWinner {
|
||||||
pub user_id: u64,
|
pub user_id: u64,
|
||||||
pub date: chrono::DateTime<chrono::Utc>,
|
pub date: chrono::DateTime<chrono::Utc>,
|
||||||
|
pub keyword: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||||
@ -43,13 +43,13 @@ pub struct Concour {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub start_date: chrono::DateTime<chrono::Utc>,
|
pub start_date: chrono::DateTime<chrono::Utc>,
|
||||||
pub periode: time::Duration,
|
pub periode: String,
|
||||||
pub role_recompense: u64,
|
pub role_recompense: u64,
|
||||||
pub keywords: Vec<String>,
|
pub keywords: Vec<String>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub index_keyword: u64,
|
pub index_keyword: u64,
|
||||||
pub status: ConcourStatus,
|
pub status: ConcourStatus,
|
||||||
pub winner: HashMap<String, ConcourWinner>,
|
pub winner: Vec<ConcourWinner>,
|
||||||
pub last_message_id: Option<u64>,
|
pub last_message_id: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,13 +61,13 @@ impl Default for Concour {
|
|||||||
title: String::new(),
|
title: String::new(),
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
start_date: chrono::Utc::now(),
|
start_date: chrono::Utc::now(),
|
||||||
periode: time::Duration::days(1),
|
periode: "0 0 17 * * * *".to_string(),
|
||||||
role_recompense: 0,
|
role_recompense: 0,
|
||||||
keywords: Vec::new(),
|
keywords: Vec::new(),
|
||||||
banner: None,
|
banner: None,
|
||||||
index_keyword: 0,
|
index_keyword: 0,
|
||||||
status: ConcourStatus::Created,
|
status: ConcourStatus::Created,
|
||||||
winner: HashMap::new(),
|
winner: Vec::new(),
|
||||||
last_message_id: None,
|
last_message_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,13 +81,13 @@ impl Concour {
|
|||||||
title: String::new(),
|
title: String::new(),
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
start_date: chrono::Utc::now(),
|
start_date: chrono::Utc::now(),
|
||||||
periode: time::Duration::days(1),
|
periode: "0 0 17 * * * *".to_string(),
|
||||||
role_recompense: 0,
|
role_recompense: 0,
|
||||||
keywords: Vec::new(),
|
keywords: Vec::new(),
|
||||||
banner: None,
|
banner: None,
|
||||||
index_keyword: 0,
|
index_keyword: 0,
|
||||||
status: ConcourStatus::Created,
|
status: ConcourStatus::Created,
|
||||||
winner: HashMap::new(),
|
winner: Vec::new(),
|
||||||
last_message_id: None,
|
last_message_id: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,2 +1 @@
|
|||||||
pub mod concour;
|
|
||||||
pub mod schedule_job;
|
pub mod schedule_job;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use poise::serenity_prelude::Http;
|
use poise::serenity_prelude::{
|
||||||
|
Color, CreateEmbed, CreateMessage, Http, Mentionable, MessagePagination,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
@ -6,10 +8,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio_cron_scheduler::{Job, JobScheduler};
|
use tokio_cron_scheduler::{Job, JobScheduler};
|
||||||
use tracing::{error, info, instrument};
|
use tracing::{error, info, info_span, instrument, Instrument};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::db::concour::{Concour, ConcourStatus};
|
use crate::db::concour::{Concour, ConcourStatus, ConcourWinner};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StopScheduleJob {
|
pub enum StopScheduleJob {
|
||||||
@ -23,12 +25,20 @@ impl Display for StopScheduleJob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ScheduleJob {
|
pub struct ScheduleJob {
|
||||||
pub job_id: Arc<RwLock<HashMap<(u64, u64), Uuid>>>,
|
pub job_id: Arc<RwLock<HashMap<(u64, u64), Uuid>>>,
|
||||||
pub scheduler: JobScheduler,
|
pub scheduler: JobScheduler,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for ScheduleJob {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
ScheduleJob {
|
||||||
|
job_id: self.job_id.clone(),
|
||||||
|
scheduler: self.scheduler.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ScheduleJob {
|
impl ScheduleJob {
|
||||||
#[instrument(level = "info")]
|
#[instrument(level = "info")]
|
||||||
pub async fn start_cron_scheduler() -> Result<Self, ()> {
|
pub async fn start_cron_scheduler() -> Result<Self, ()> {
|
||||||
@ -72,12 +82,7 @@ impl ScheduleJob {
|
|||||||
};
|
};
|
||||||
for concour in concours {
|
for concour in concours {
|
||||||
match self
|
match self
|
||||||
.add_concour_cron_job(
|
.add_concour_cron_job(concour.server_id, concour.channel_id, concour.periode, http)
|
||||||
concour.server_id,
|
|
||||||
concour.channel_id,
|
|
||||||
"0 0 17 * * *".to_string(),
|
|
||||||
http,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
@ -124,30 +129,293 @@ impl ScheduleJob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, _http), level = "info")]
|
#[instrument(skip(self, http), level = "info")]
|
||||||
pub async fn add_concour_cron_job(
|
pub async fn add_concour_cron_job(
|
||||||
&mut self,
|
&mut self,
|
||||||
server_id: u64,
|
server_id: u64,
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
cron_expression: String,
|
cron_expression: String,
|
||||||
_http: &Http,
|
http: &Http,
|
||||||
) -> Result<Uuid, ()> {
|
) -> Result<Uuid, ()> {
|
||||||
let job = match Job::new_async(cron_expression.as_str(), |uuid, _l| {
|
let http = Arc::new(Http::new(http.token()));
|
||||||
Box::pin(async move {
|
let job = match Job::new_cron_job_async_tz(
|
||||||
// Send the message to announce the end of the concour
|
cron_expression.as_str(),
|
||||||
// Get concour data
|
chrono::Local,
|
||||||
// Get All message since the announcement
|
move |uuid, _l| {
|
||||||
// filter out the bot's message
|
Box::pin(
|
||||||
// count the number of reactions per message
|
{
|
||||||
// get the user comment with the highest reaction
|
let http = http.clone();
|
||||||
// announce the winner
|
Box::pin(async move {
|
||||||
// Give the winner the role reward
|
info!(id = uuid.to_string(), "Cron job fired");
|
||||||
// update concour with the winner and increment the index
|
// Get concour data
|
||||||
// Announce the next concour
|
let concour = match Concour::find_by_server_id_channel_id(
|
||||||
// Or not if there is no more keyword
|
&server_id,
|
||||||
info!("Cron job fired: {:?}", uuid);
|
&channel_id,
|
||||||
})
|
)
|
||||||
}) {
|
.await
|
||||||
|
{
|
||||||
|
Ok(concour) => concour,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error getting concour: {:?}", e);
|
||||||
|
// Disable the concour ?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if concour.is_none() {
|
||||||
|
error!("Concour not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut concour = concour.unwrap();
|
||||||
|
// Send the message to announce the end of the concour
|
||||||
|
let current_keyword = concour
|
||||||
|
.keywords
|
||||||
|
.get(concour.index_keyword as usize)
|
||||||
|
.unwrap();
|
||||||
|
let embed = CreateEmbed::default()
|
||||||
|
.title(format!(
|
||||||
|
"Concour has ended Day {} has ended",
|
||||||
|
concour.index_keyword + 1
|
||||||
|
))
|
||||||
|
.description("Processing the results...")
|
||||||
|
.field("Title", concour.title.clone(), false)
|
||||||
|
.field("Description", concour.description.clone(), false)
|
||||||
|
.field("Word of the day", current_keyword, false)
|
||||||
|
.color(Color::DARK_GREEN);
|
||||||
|
let reply = CreateMessage::default().embed(embed);
|
||||||
|
let last_id =
|
||||||
|
match http.send_message(channel_id.into(), vec![], &reply).await {
|
||||||
|
Ok(message) => message.id,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error sending message: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Check if the concour is still enabledl
|
||||||
|
if concour.status != ConcourStatus::OnGoing
|
||||||
|
|| concour.last_message_id.is_none()
|
||||||
|
{
|
||||||
|
info!("Concour is not enabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get All message since the announcement
|
||||||
|
let message_pagination =
|
||||||
|
MessagePagination::After(concour.last_message_id.unwrap().into());
|
||||||
|
let mut messages = match http
|
||||||
|
.get_messages(channel_id.into(), Some(message_pagination), None)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(messages) => messages,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error getting messages: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if messages.is_empty() {
|
||||||
|
error!("No message found");
|
||||||
|
let embed: CreateEmbed = CreateEmbed::default()
|
||||||
|
.title("An error has occured while fetching the messages")
|
||||||
|
.color(Color::DARK_RED);
|
||||||
|
let reply = CreateMessage::default().embed(embed);
|
||||||
|
if let Err(err) =
|
||||||
|
http.send_message(channel_id.into(), vec![], &reply).await
|
||||||
|
{
|
||||||
|
error!("Error sending message: {:?}", err);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if messages.last().unwrap().id != last_id {
|
||||||
|
info!("Fetching more messages because last one is not the last id");
|
||||||
|
loop {
|
||||||
|
let message_pagination = MessagePagination::After(
|
||||||
|
messages.last().unwrap().id.into(),
|
||||||
|
);
|
||||||
|
let mut new_messages = match http
|
||||||
|
.get_messages(
|
||||||
|
channel_id.into(),
|
||||||
|
Some(message_pagination),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(messages) => messages,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error getting messages: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if new_messages.is_empty() {
|
||||||
|
info!("No more messages found");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
messages.append(&mut new_messages);
|
||||||
|
if messages.last().unwrap().id == last_id {
|
||||||
|
info!("Last message found");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// filter out the bot's message
|
||||||
|
messages.retain(|message| !message.author.bot);
|
||||||
|
info!(
|
||||||
|
nbr_message = messages.len(),
|
||||||
|
"{} messages found",
|
||||||
|
messages.len()
|
||||||
|
);
|
||||||
|
// count the number of reactions per message
|
||||||
|
let mut max_reaction = 0;
|
||||||
|
let mut max_winner = None;
|
||||||
|
messages.into_iter().for_each(|msg| {
|
||||||
|
// test
|
||||||
|
let count = msg
|
||||||
|
.reactions
|
||||||
|
.into_iter()
|
||||||
|
.fold(0, |acc, reaction| acc + reaction.count);
|
||||||
|
if count > max_reaction {
|
||||||
|
max_reaction = count;
|
||||||
|
max_winner = Some(msg.author);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// announce the winner
|
||||||
|
let winner = match max_winner {
|
||||||
|
Some(winner) => winner,
|
||||||
|
None => {
|
||||||
|
let embed = CreateEmbed::default()
|
||||||
|
.title("No winner found, What happened ?")
|
||||||
|
.color(Color::DARK_RED);
|
||||||
|
let reply = CreateMessage::default().embed(embed);
|
||||||
|
if let Err(err) =
|
||||||
|
http.send_message(channel_id.into(), vec![], &reply).await
|
||||||
|
{
|
||||||
|
error!("Error sending message: {:?}", err);
|
||||||
|
}
|
||||||
|
error!("No winner found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let embed = CreateEmbed::default()
|
||||||
|
.title("Winner")
|
||||||
|
.description(format!("The winner is {}", winner.mention()))
|
||||||
|
.color(Color::DARK_GREEN);
|
||||||
|
let reply = CreateMessage::default().embed(embed);
|
||||||
|
if let Err(err) =
|
||||||
|
http.send_message(channel_id.into(), vec![], &reply).await
|
||||||
|
{
|
||||||
|
error!("Error sending message: {:?}", err);
|
||||||
|
}
|
||||||
|
let (add, previous) = match concour.winner.last() {
|
||||||
|
Some(previous_winner) => (
|
||||||
|
previous_winner.user_id != winner.id.get(),
|
||||||
|
previous_winner.user_id,
|
||||||
|
),
|
||||||
|
None => (true, 0),
|
||||||
|
};
|
||||||
|
// Remove the role from the previous winner
|
||||||
|
// Give the winner the role reward
|
||||||
|
if add {
|
||||||
|
if previous != 0 {
|
||||||
|
match http
|
||||||
|
.remove_member_role(
|
||||||
|
server_id.into(),
|
||||||
|
previous.into(),
|
||||||
|
concour.role_recompense.into(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Role removed from the previous winner");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"Error removing role from the previous winner: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match http
|
||||||
|
.add_member_role(
|
||||||
|
server_id.into(),
|
||||||
|
winner.id,
|
||||||
|
concour.role_recompense.into(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Role added to the winner");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error adding role to the winner: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update concour with the winner and increment the index
|
||||||
|
concour.winner.push(ConcourWinner {
|
||||||
|
user_id: winner.id.get(),
|
||||||
|
date: chrono::Utc::now(),
|
||||||
|
keyword: current_keyword.to_string(),
|
||||||
|
});
|
||||||
|
concour.index_keyword += 1;
|
||||||
|
if concour.index_keyword as usize >= concour.keywords.len() {
|
||||||
|
concour.status = ConcourStatus::Finished;
|
||||||
|
let embed = CreateEmbed::default()
|
||||||
|
.title("Concour has ended")
|
||||||
|
.description("The concour has ended, no more keyword")
|
||||||
|
.color(Color::DARK_GREEN);
|
||||||
|
let reply = CreateMessage::default().embed(embed);
|
||||||
|
if let Err(err) =
|
||||||
|
http.send_message(channel_id.into(), vec![], &reply).await
|
||||||
|
{
|
||||||
|
error!("Error sending message: {:?}", err);
|
||||||
|
}
|
||||||
|
info!("Concour has ended, no more keyword");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let next_keyword = concour
|
||||||
|
.keywords
|
||||||
|
.get(concour.index_keyword as usize)
|
||||||
|
.unwrap();
|
||||||
|
let output = CreateEmbed::new()
|
||||||
|
.title(format!(
|
||||||
|
"Concour: {} Jour : {}",
|
||||||
|
concour.title.clone(),
|
||||||
|
concour.index_keyword + 1
|
||||||
|
))
|
||||||
|
.description(concour.description.clone())
|
||||||
|
.field("Word of the day ", next_keyword.to_string(), false)
|
||||||
|
.field("Good luck !", "", false)
|
||||||
|
.field(
|
||||||
|
"Please see when the concour end in the concour get command",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.color(Color::DARK_GREEN);
|
||||||
|
let reply = CreateMessage::default().embed(output);
|
||||||
|
let last_id =
|
||||||
|
match http.send_message(channel_id.into(), vec![], &reply).await {
|
||||||
|
Ok(message) => message.id,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error sending message: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
concour.last_message_id = Some(last_id.get());
|
||||||
|
match concour.update().await {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Concour updated");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error updating concour: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.instrument(info_span!("ConcourJob", id = uuid.to_string())),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
Ok(job) => job,
|
Ok(job) => job,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error creating cron job: {:?}", e);
|
error!("Error creating cron job: {:?}", e);
|
||||||
|
Loading…
Reference in New Issue
Block a user