feat: init migration to poise framework

This commit is contained in:
Max batleforc 2024-05-22 02:55:31 +02:00
parent 7c29ff5ce0
commit 45ddc2f2f7
No known key found for this signature in database
GPG Key ID: 25D243AB4B6AC9E7
15 changed files with 486 additions and 13 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target /target
.env .env
/data

177
Cargo.lock generated
View File

@ -618,6 +618,7 @@ dependencies = [
"dotenvy", "dotenvy",
"once_cell", "once_cell",
"openssl", "openssl",
"poise",
"postgres-openssl", "postgres-openssl",
"rand", "rand",
"regex", "regex",
@ -686,6 +687,12 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "bytecount"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.14.2" version = "1.14.2"
@ -713,6 +720,37 @@ dependencies = [
"bytes", "bytes",
] ]
[[package]]
name = "camino"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
dependencies = [
"serde",
]
[[package]]
name = "cargo-platform"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.83"
@ -1042,6 +1080,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.17" version = "0.99.17"
@ -1198,6 +1247,15 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.5.3" version = "2.5.3"
@ -1537,6 +1595,12 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.23" version = "0.3.23"
@ -2032,6 +2096,21 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "mini-moka"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c325dfab65f261f386debee8b0969da215b3fa0037e74c8a1234db7ba986d803"
dependencies = [
"crossbeam-channel",
"crossbeam-utils",
"dashmap",
"skeptic",
"smallvec",
"tagptr",
"triomphe",
]
[[package]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.2.1" version = "0.2.1"
@ -2485,6 +2564,35 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "poise"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1819d5a45e3590ef33754abce46432570c54a120798bdbf893112b4211fa09a6"
dependencies = [
"async-trait",
"derivative",
"futures-util",
"parking_lot",
"poise_macros",
"regex",
"serenity",
"tokio",
"tracing",
]
[[package]]
name = "poise_macros"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fa2c123c961e78315cd3deac7663177f12be4460f5440dbf62a7ed37b1effea"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]] [[package]]
name = "postgres-openssl" name = "postgres-openssl"
version = "0.5.0" version = "0.5.0"
@ -2626,6 +2734,17 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "pulldown-cmark"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
dependencies = [
"bitflags 2.4.1",
"memchr",
"unicase",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.35" version = "1.0.35"
@ -3312,6 +3431,7 @@ dependencies = [
"base64", "base64",
"bitflags 2.4.1", "bitflags 2.4.1",
"bytes", "bytes",
"chrono",
"command_attr", "command_attr",
"dashmap", "dashmap",
"flate2", "flate2",
@ -3331,6 +3451,7 @@ dependencies = [
"tokio-tungstenite", "tokio-tungstenite",
"tracing", "tracing",
"typemap_rev", "typemap_rev",
"typesize",
"url", "url",
"uwl", "uwl",
] ]
@ -3434,6 +3555,21 @@ version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "skeptic"
version = "0.13.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8"
dependencies = [
"bytecount",
"cargo_metadata",
"error-chain",
"glob",
"pulldown-cmark",
"tempfile",
"walkdir",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -3763,6 +3899,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]] [[package]]
name = "tap" name = "tap"
version = "1.0.1" version = "1.0.1"
@ -4076,6 +4218,12 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "triomphe"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.5" version = "0.2.5"
@ -4114,6 +4262,35 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "typesize"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb704842c709bc76f63e99e704cb208beeccca2abbabd0d9aec02e48ca1cee0f"
dependencies = [
"chrono",
"dashmap",
"hashbrown 0.14.3",
"mini-moka",
"parking_lot",
"secrecy",
"serde_json",
"time",
"typesize-derive",
"url",
]
[[package]]
name = "typesize-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "905e88c2a4cc27686bd57e495121d451f027e441388a67f773be729ad4be1ea8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]] [[package]]
name = "ulid" name = "ulid"
version = "1.1.2" version = "1.1.2"

View File

@ -45,6 +45,7 @@ rand = "0.8.5"
walkdir = "2.4.0" walkdir = "2.4.0"
surrealdb = "1.1.1" surrealdb = "1.1.1"
once_cell = "1.19.0" once_cell = "1.19.0"
poise = "0.6.1"
[[bin]] [[bin]]

View File

@ -24,6 +24,10 @@ Main lib used : [serenity-rs/serenity](https://github.com/serenity-rs/serenity)
- The bot has to be able to be deployed on a k8s cluster - 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) - The bot has to be OPT-IN (the user has to enable the bot on his server with a command)
## Important to do
- Migrate to Trace
## previous project ## previous project
- [TestDiscord](https://git.weebo.fr/sandbox/TestDiscord): Bot that check if a world match with one in the image warehouse and answer with a picture - [TestDiscord](https://git.weebo.fr/sandbox/TestDiscord): Bot that check if a world match with one in the image warehouse and answer with a picture

19
compose.yaml Normal file
View File

@ -0,0 +1,19 @@
services:
surrealdb:
image: surrealdb/surrealdb:latest
container_name: mongo
environment:
SURREAL_AUTH: true
SURREAL_USER: bot_root
SURREAL_PASS: bot_password_azertdsq
SURREAL_PATH: file:/appdata/bot.db
user: "1000:1000"
command:
- start
ports:
- "8000:8000"
volumes:
- db:/appdata
volumes:
db:

View File

@ -17,10 +17,8 @@ variables:
components: components:
- name: tools - name: tools
container: container:
image: harbor.weebo.fr/batleforc/che-rust:0.1.13 image: harbor.weebo.fr/batleforc/che-rust:latest
memoryLimit: 8Gi memoryLimit: 10Gi
command: ["tail"]
args: ["-f", "/dev/null"]
mountSources: true mountSources: true
endpoints: endpoints:
- name: 5437-http - name: 5437-http

View File

@ -14,6 +14,7 @@ use crate::{
img::config_file::{ConfigImgGlobal, KeyWordItem}, img::config_file::{ConfigImgGlobal, KeyWordItem},
}; };
pub struct Handler; pub struct Handler;
#[async_trait] #[async_trait]

34
src/botv2/cmd/help.rs Normal file
View File

@ -0,0 +1,34 @@
use poise::samples::HelpConfiguration;
use crate::botv2::init::{Context,Error};
/// Show help message
#[poise::command(prefix_command, track_edits, category = "Utility")]
pub async fn help(
ctx: Context<'_>,
#[description = "Command to get help for"]
#[rest]
mut command: Option<String>,
) -> Result<(), Error> {
// This makes it possible to just make `help` a subcommand of any command
// `/fruit help` turns into `/help fruit`
// `/fruit help apple` turns into `/help fruit apple`
if ctx.invoked_command_name() != "help" {
command = match command {
Some(c) => Some(format!("{} {}", ctx.invoked_command_name(), c)),
None => Some(ctx.invoked_command_name().to_string()),
};
}
let extra_text_at_bottom = "\
Provided by Batleforc with love and too much coffee ";
let config = HelpConfiguration {
show_subcommands: true,
show_context_menu_commands: true,
ephemeral: true,
extra_text_at_bottom,
..Default::default()
};
poise::builtins::help(ctx, command.as_deref(), config).await?;
Ok(())
}

2
src/botv2/cmd/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod ping;
pub mod help;

11
src/botv2/cmd/ping.rs Normal file
View File

@ -0,0 +1,11 @@
use crate::botv2::init::{Context,Error};
/// Ping command
#[poise::command(
slash_command,
prefix_command,
)]
pub async fn ping(ctx: Context<'_>) -> Result<(), Error> {
ctx.say("Pong from framework!").await?;
Ok(())
}

110
src/botv2/handler.rs Normal file
View File

@ -0,0 +1,110 @@
use tokio::fs::File;
use poise::serenity_prelude as serenity;
use serenity::all::{CreateAttachment,CreateMessage};
use rand::Rng;
use crate::{db::user_image::UserImage, img::config_file::KeyWordItem};
use super::init::{Data, Error};
pub async fn event_handler(
ctx: &serenity::Context,
event: &serenity::FullEvent,
_framework: poise::FrameworkContext<'_, Data, Error>,
data: &Data,
) -> Result<(), Error> {
match event {
serenity::FullEvent::Ready { data_about_bot, .. } => {
println!("{} 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.len() == 0 {
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 UserImage::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 = UserImage::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 {
println!("User image is not enable");
return Ok(());
}
if config_img.keyword.len() == 0 || 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(())
}

115
src/botv2/init.rs Normal file
View File

@ -0,0 +1,115 @@
use poise::serenity_prelude as serenity;
use std::fs;
use serenity::GatewayIntents;
use crate::botv2::cmd::{ping::ping,help::help};
use crate::{botv2::handler::event_handler, img::config_file::ConfigFile};
use crate::config::Config;
use tokio::sync::oneshot;
use tokio::task::spawn_blocking;
pub struct Data{
pub config_img: ConfigFile,
pub config: Config,
}
// Types used by all command functions
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type Context<'a> = poise::Context<'a, Data, Error>;
#[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 fn start_bot(config: Config, rx: oneshot::Receiver<()>){
if config.token != "" {
spawn_blocking(move||{
let rt = tokio::runtime::Handle::current();
rt.block_on(async move{
let local = tokio::task::LocalSet::new();
let _ = local.run_until(async move{
let config_img = match fs::read_to_string(format!(
"{}/config.yaml",
config.image.path
)) {
Ok(content) => content,
Err(err) => {
println!("Error while opening config.yaml : {:?}", err);
return;
}
};
let config_parsed = match ConfigFile::parse_config(config_img) {
Ok(config) => config,
Err(err) => {
println!("Error while parsing config.yaml : {:?}", err);
return;
}
};
let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT
| GatewayIntents::GUILD_VOICE_STATES
| GatewayIntents::GUILDS
| GatewayIntents::GUILD_MEMBERS
| GatewayIntents::GUILD_PRESENCES
| GatewayIntents::GUILD_MESSAGE_REACTIONS;
let token = config.token.clone();
let prefix = config.prefix.clone();
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
commands: vec![age(), ping(),help()],
prefix_options: poise::PrefixFrameworkOptions {
prefix: Some(prefix.into()),
..Default::default()
},
event_handler: |ctx, event, framework, data| {
Box::pin(event_handler(ctx, event, framework, data))
},
..Default::default()
})
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Data {
config_img: config_parsed,
config: config.clone(),
})
})
})
.build();
let mut client = serenity::ClientBuilder::new(token, intents)
.framework(framework)
.await.expect("Error creating client");
let shard_manager = client.shard_manager.clone();
let client_start = client.start_autosharded();
tokio::spawn(async move {
match rx.await {
Ok(_) => {
println!("Received shutdown signal");
shard_manager.shutdown_all().await;
println!("Shutting down bot");
}
Err(_) => {
println!("Channel dropped signal");
shard_manager.shutdown_all().await;
println!("Shutting down bot");
}
}
});
println!("Bot is running...");
if let Err(why) = client_start.await {
println!("Client error: {why:?}");
}
println!("Bot is stopped...");
}).await;
})
});
}
}

3
src/botv2/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod init;
pub mod handler;
pub mod cmd;

View File

@ -2,3 +2,4 @@ pub mod bot;
pub mod config; pub mod config;
pub mod db; pub mod db;
pub mod img; pub mod img;
pub mod botv2;

View File

@ -1,15 +1,11 @@
mod bot; extern crate botdiscord;
mod config;
mod db;
mod img;
use std::{process, time::Duration}; use std::{process, time::Duration};
use actix_cors::Cors; use actix_cors::Cors;
use actix_web::{App, HttpServer}; use actix_web::{App, HttpServer};
use bot::init::start_bot; use botdiscord::botv2::init::start_bot;
use config::parse_local_config; use botdiscord::config::parse_local_config;
use tokio::{sync::oneshot, time::sleep}; use tokio::{sync::oneshot, time::sleep};
use botdiscord::db;
#[tokio::main] // or #[actix_web::main] #[tokio::main] // or #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {