Compare commits

..

No commits in common. "a9f4240710049a96917674b57c1b7bf3ff459a0b" and "1b2f0e25acef7928e33564ed8e6a146a88863227" have entirely different histories.

10 changed files with 163 additions and 149 deletions

17
Cargo.lock generated
View File

@ -3535,15 +3535,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cow"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e84ce5596a72f0c4c60759a10ff8c22d5eaf227b0dc2789c8746193309058b"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
@ -3644,12 +3635,13 @@ dependencies = [
[[package]]
name = "serenity"
version = "0.12.2"
source = "git+https://github.com/serenity-rs/serenity?branch=current#060ee3281b44f7e532f3ea5863c5df57340e1ec9"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64da29158bb55d70677cacd4f4f8eab1acef005fb830d9c3bea411b090e96a9"
dependencies = [
"arrayvec",
"async-trait",
"base64 0.22.1",
"base64 0.21.7",
"bitflags 2.5.0",
"bytes",
"chrono",
@ -3663,7 +3655,6 @@ dependencies = [
"reqwest",
"secrecy",
"serde",
"serde_cow",
"serde_json",
"time",
"tokio",

View File

@ -51,8 +51,6 @@ serde_with = "3.8.1"
tokio-cron = "0.1.3"
cron = "0.12.1"
[patch.crates-io]
serenity = { git = "https://github.com/serenity-rs/serenity", branch = "current" }
[[bin]]
name = "botdiscord"

View File

@ -10,7 +10,7 @@ Main lib used : [serenity-rs/serenity](https://github.com/serenity-rs/serenity)
- [DONE] ImageWareHouse (like TestDiscord)
- ImageWareHouse V2
- Mise en place d'autre source de gif comme [Teno](https://developers.google.com/tenor/guides/quickstart?hl=fr)
- Mise en place d'autre source de gif OPT
- SoundBoard (like UnlabeledBot)
- Notification (read an event topic and send the message to the expected chan)
- Some Administration command
@ -24,9 +24,9 @@ Main lib used : [serenity-rs/serenity](https://github.com/serenity-rs/serenity)
- Monitor the bot
- Authentification Discord
- Detecter controle
- [DONE] Integrate with the Opentelemetry project
- [DONE] The bot has to be able to be deployed on a k8s cluster
- [DONE] The bot has to be OPT-IN (the user has to enable the bot on his server with a command)
- [WIP] Integrate with the Opentelemetry project
- The bot has to be able to be deployed on a k8s cluster
- The bot has to be OPT-IN (the user has to enable the bot on his server with a command)
- Mise en place de metric OpenTelemetry
## previous project

View File

@ -1,14 +1,11 @@
use std::str::FromStr;
use crate::{
botv2::{
domain::concour::{
check_if_allowed::check_if_allowed,
set_periode::{set_periode, SetPeriodeConcourError},
},
init::{Context, Error},
use crate::botv2::{
domain::concour::{
check_if_allowed::check_if_allowed,
set_periode::{set_periode, SetPeriodeConcourError},
},
db::concour::ConcourStatus,
init::{Context, Error},
};
use chrono::{DateTime, Utc};
use cron::Schedule;
@ -69,8 +66,7 @@ pub async fn period(
};
let schedule = match Schedule::from_str(&cron_schedule) {
Ok(schedule) => schedule,
Err(err) => {
warn!(err = err.to_string(), "Cron format is invalid");
Err(_) => {
let embed = CreateEmbed::new()
.title("Invalid CRON format")
.color(colour::Color::RED)
@ -122,55 +118,16 @@ pub async fn period(
.color(colour::Color::RED)
} else {
let concour = concour.unwrap();
let mut output = CreateEmbed::new()
CreateEmbed::new()
.title(concour.title)
.description(concour.description)
.field("Start date", concour.start_date.to_string(), false)
.field("Periode", concour.periode.to_string(), false);
if concour.role_recompense == 0 {
output = output.field("Role récompense", "Aucun", false);
} else {
output = output.field(
.field("Periode", concour.periode.to_string(), false)
.field(
"Role récompense",
RoleId::new(concour.role_recompense).mention().to_string(),
false,
);
}
// Restart the cronjob if concour is ongoing
if concour.status == ConcourStatus::OnGoing {
{
let mut scheduler = ctx.data().scheduler.clone();
match scheduler
.stop_scheduled_job(concour.server_id, concour.channel_id)
.await
{
Ok(_) => {
info!("Cronjob stopped");
match scheduler
.add_concour_cron_job(
concour.server_id,
concour.channel_id,
concour.periode,
ctx.http(),
)
.await
{
Ok(_) => {
info!("Cronjob restarted");
}
Err(_) => {
warn!("Error restarting cronjob");
}
};
}
Err(err) => {
warn!(err = err.to_string(), "Error stopping cronjob");
}
}
}
}
output
)
}
}
Err(err) => match err {

View File

@ -83,11 +83,6 @@ pub async fn start(ctx: Context<'_>) -> Result<(), Error> {
Some(role) => RoleId::new(role).mention().to_string(),
None => "".to_string(),
};
let role_recompense = if concour.role_recompense != 0 {
RoleId::new(concour.role_recompense).mention().to_string()
} else {
"(Pas encore définis)".to_string()
};
let text = format!("
Bonsoir !
@ -95,14 +90,14 @@ Bonsoir !
📜 Les règles : Pas de loli, ni de shoota, ni de irl, ni de zoo.
Celui ou celle qui a le plus de votes gagne, comme récompense elle aura le rôle {} pour une durée de 48h.
Celui ou celle qui a le plus de votes gagne, comme récompense elle aura le rôle @ROLE pour une durée de 48h.
Le concours ce termine dans deux jours.
Ceux qui votent pour leur propre photo, cela ne sera pas pris en compte, une photo par personne.
À vos photos !
{}
", keyword,role_recompense,ping_concour);
", keyword.to_string(),ping_concour);
let output = CreateEmbed::new()
.title(format!(
"Concour: {} Jour : {}",

View File

@ -1,18 +0,0 @@
use crate::botv2::{
cmd::meme::{answer::answer, enable::enable, list::list},
init::{Context, Error},
};
use tracing::instrument;
/// Handle meme command
#[instrument(skip(ctx), level = "info", fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))]
#[poise::command(
slash_command,
prefix_command,
category = "mem",
subcommands("answer", "enable", "list"),
guild_only = true
)]
pub async fn meme(ctx: Context<'_>) -> Result<(), Error> {
Ok(())
}

View File

@ -1,4 +1,3 @@
pub mod answer;
pub mod enable;
pub mod list;
pub mod main;

View File

@ -1,18 +1,121 @@
use super::init::{Data, Error};
use crate::{db::user_image::User, img::config_file::KeyWordItem};
use poise::serenity_prelude as serenity;
use rand::Rng;
use serenity::all::{CreateAttachment, CreateMessage};
use tokio::fs::File;
use tracing::{info, instrument};
#[allow(clippy::single_match)]
#[instrument(skip(_ctx, _framework, _data), err, level = "trace")]
#[instrument(skip(ctx, _framework, data), err, level = "trace")]
pub async fn event_handler(
_ctx: &serenity::Context,
ctx: &serenity::Context,
event: &serenity::FullEvent,
_framework: poise::FrameworkContext<'_, Data, Error>,
_data: &Data,
data: &Data,
) -> Result<(), Error> {
match event {
serenity::FullEvent::Ready { data_about_bot, .. } => {
info!("{} is connected !", data_about_bot.user.name);
}
serenity::FullEvent::Message { new_message } => {
if new_message.author.bot
|| new_message.content.starts_with(&data.config.prefix.clone())
|| new_message.content.is_empty()
{
return Ok(());
}
let config_img = data.config_img.clone();
let config = data.config.clone();
let guild = match new_message.guild_id {
Some(guild) => guild,
None => return Ok(()),
};
let user_in_db =
match User::find_by_server_id_user_id(&guild.get(), &new_message.author.id.get())
.await
{
Ok(Some(user_in_db)) => user_in_db.clone(),
Ok(None) => {
let user_in_db =
User::new(guild.get(), new_message.author.id.get(), false).unwrap();
match user_in_db.create().await {
Ok(_) => user_in_db,
Err(e) => {
println!("Error saving user image: {:?}", e);
return Ok(());
}
}
}
Err(e) => {
println!("Error finding user image: {:?}", e);
return Ok(());
}
};
if !user_in_db.enable {
return Ok(());
}
if config_img.keyword.is_empty() || new_message.content.len() > 50 {
return Ok(());
}
let folder_container = match config_img
.keyword
.iter()
.find(|keyword| keyword.does_value_match(new_message.content.clone()))
{
Some(keyword_matching) => {
println!("{} match {:?}", new_message.content, keyword_matching);
let keyword_path = match keyword_matching.path.len() {
0 => keyword_matching.path[0].clone(),
_ => {
let id: usize = {
let mut rng = rand::thread_rng();
rng.gen_range(0..keyword_matching.path.len())
};
keyword_matching.path[id].clone()
}
};
keyword_path.clone()
}
None => return Ok(()),
};
let path = format!("{}/{}", config.image.path.clone(), folder_container);
let file_folder = KeyWordItem::output_folder_content(path.clone());
let id_rand: usize = {
let mut rng = rand::thread_rng();
rng.gen_range(0..file_folder.len())
};
let filename = match file_folder.get(id_rand) {
Some(file) => file.file_name().to_str().unwrap(),
None => return Ok(()),
};
let file_path = format!("{}/{}", path, filename);
let file = match File::open(file_path).await {
Ok(file) => file,
Err(why) => {
println!("Error opening file: {:?}", why);
return Ok(());
}
};
let attachment = match CreateAttachment::file(&file, filename).await {
Ok(attachment) => attachment,
Err(why) => {
println!("Error creating attachment: {:?}", why);
return Ok(());
}
};
let builder = CreateMessage::new().add_file(attachment);
if let Err(why) = new_message
.channel_id
.send_message(&ctx.http, builder)
.await
{
println!("Error sending message: {:?}", why);
}
}
_ => {}
}
Ok(())

View File

@ -1,4 +1,4 @@
use crate::botv2::cmd::meme::main::meme;
use crate::botv2::cmd::meme::{answer::answer, enable::enable, list::list};
use crate::botv2::cmd::server_config::server::server;
use crate::botv2::cmd::{help::help, ping::ping};
use crate::config::Config;
@ -9,7 +9,7 @@ use serenity::GatewayIntents;
use std::fs;
use std::sync::Arc;
use tokio::sync::oneshot;
use tracing::info;
use tracing::{info, instrument};
use super::cmd::concour::main::concour;
@ -24,6 +24,18 @@ pub struct Data {
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type Context<'a> = poise::Context<'a, Data, Error>;
#[instrument(skip(ctx), level = "info")]
#[poise::command(slash_command, prefix_command)]
async fn age(
ctx: Context<'_>,
#[description = "Selected user"] user: Option<serenity::User>,
) -> Result<(), Error> {
let u = user.as_ref().unwrap_or_else(|| ctx.author());
let response = format!("{}'s account was created at {}", u.name, u.created_at());
ctx.say(response).await?;
Ok(())
}
pub async fn start_bot(
config: Config,
rx: oneshot::Receiver<()>,
@ -55,7 +67,16 @@ pub async fn start_bot(
let prefix = config.prefix.clone();
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
commands: vec![ping(), help(), meme(), server(), concour()],
commands: vec![
age(),
ping(),
help(),
list(),
enable(),
answer(),
server(),
concour(),
],
prefix_options: poise::PrefixFrameworkOptions {
prefix: Some(prefix),
..Default::default()

View File

@ -1,5 +1,5 @@
use poise::serenity_prelude::{
Color, CreateEmbed, CreateMessage, Http, Mentionable, MessagePagination, RoleId,
Color, CreateEmbed, CreateMessage, Http, Mentionable, MessagePagination,
};
use std::{
collections::HashMap,
@ -291,16 +291,16 @@ impl ScheduleJob {
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 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(),
@ -376,33 +376,6 @@ impl ScheduleJob {
.keywords
.get(concour.index_keyword as usize)
.unwrap();
let ping_role = match concour.ping_concour {
Some(role_id) => RoleId::new(role_id).mention().to_string(),
None => "".to_string(),
};
let role_recompense = if concour.role_recompense != 0 {
RoleId::new(concour.role_recompense).mention().to_string()
} else {
"(Pas encore définis)".to_string()
};
let answer = format!(
"Bonsoir !
🏆 Bravo à {} pour ses réactions sous son image.
👹 Le thème de ce soir est : {}
📜 Les règles : Pas de loli, ni de shoota, ni de irl, ni de zoo.
Celui ou celle qui a le plus de votes gagne, comme récompense elle aura le rôle {} pour une durée de 48h.
Le concours ce termine dans deux jours.
Ceux qui votent pour leur propre photo, cela ne sera pas pris en compte, une photo par personne.
À vos photos !
{}",
winner.id.mention(), next_keyword,role_recompense, ping_role
);
let output = CreateEmbed::new()
.title(format!(
"Concour: {} Jour : {}",
@ -418,12 +391,7 @@ Ceux qui votent pour leur propre photo, cela ne sera pas pris en compte, une pho
false,
)
.color(Color::DARK_GREEN);
let mut reply = CreateMessage::default();
if !answer.is_empty() {
reply = reply.content(answer);
}else{
reply = reply.embed(output);
}
let reply = CreateMessage::default().embed(output);
let last_id =
match http.send_message(channel_id.into(), vec![], &reply).await {
Ok(message) => message.id,