feat: WIP

This commit is contained in:
max 2024-01-31 00:54:06 +00:00
parent 6f2b15493d
commit 358fe2a9ce
9 changed files with 199 additions and 5 deletions

22
Cargo.lock generated
View File

@ -379,10 +379,12 @@ dependencies = [
"dotenvy",
"openssl",
"postgres-openssl",
"rand",
"regex",
"reqwest",
"serde",
"serde_json",
"serde_yaml",
"serenity",
"serial_test",
"tokio",
@ -391,6 +393,7 @@ dependencies = [
"utoipa",
"utoipa-swagger-ui",
"uuid",
"walkdir",
]
[[package]]
@ -1856,6 +1859,19 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "serenity"
version = "0.12.0"
@ -2392,6 +2408,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
[[package]]
name = "untrusted"
version = "0.9.0"

View File

@ -8,6 +8,7 @@ default-run = "botdiscord"
[dependencies]
serde = "1.0"
serde_yaml = "0.9"
uuid = { version = "1.4", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
utoipa = { version = "4", features = ["actix_extras", "chrono", "uuid"] }
@ -40,6 +41,8 @@ serenity = { version = "0.12", default-features = false, features = [
"cache",
] }
tokio = { version = "1.35", features = ["macros", "rt-multi-thread"] }
rand = "0.8.5"
walkdir = "2.4.0"
[[bin]]

View File

@ -1,7 +1,17 @@
use rand::Rng;
use serenity::prelude::*;
use serenity::{
all::{Message, Ready},
async_trait,
client::{Context, EventHandler},
builder::{CreateAttachment, CreateMessage},
client::Context,
};
use tokio::fs::File;
use walkdir::{DirEntry, WalkDir};
use crate::{
config::ConfigGlobal,
img::config_file::{ConfigImgGlobal, KeyWordItem},
};
pub struct Handler;
@ -9,7 +19,7 @@ pub struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
if msg.author.bot {
if msg.author.bot || msg.content.starts_with("!") {
return;
}
if msg.content == "&ping" {
@ -18,6 +28,64 @@ impl EventHandler for Handler {
}
return;
}
let (config_img, config) = {
let data_read = ctx.data.read().await;
let config_img = data_read
.get::<ConfigImgGlobal>()
.expect("Config img not found")
.clone();
let config = data_read
.get::<ConfigGlobal>()
.expect("Main config not found")
.clone();
(config_img, config)
};
let mut rng = rand::thread_rng();
let folder_container = match config_img
.keyword
.iter()
.find(|keyword| keyword.does_value_match(msg.content.clone()))
{
Some(keyword_matching) => {
println!("{} match {:?}", msg.content, keyword_matching);
let keyword_path = match keyword_matching.path.len() {
0 => keyword_matching.path[0].clone(),
_ => {
let id = rng.gen_range(0..keyword_matching.path.len());
keyword_matching.path[id].clone()
}
};
keyword_path.clone()
}
None => return,
};
let path = format!("{}/{}", config.image.path.clone(), folder_container);
let file_folder = KeyWordItem::output_folder_content(path.clone());
let id_rand: usize = 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,
};
let file_path = format!("{}/{}", path, filename.clone());
let file = match File::open(file_path).await {
Ok(file) => file,
Err(why) => {
println!("Error opening file: {:?}", why);
return;
}
};
let attachment = match CreateAttachment::file(&file, filename).await {
Ok(attachment) => attachment,
Err(why) => {
println!("Error creating attachment: {:?}", why);
return;
}
};
let builder = CreateMessage::new().add_file(attachment);
if let Err(why) = msg.channel_id.send_message(&ctx.http, builder).await {
println!("Error sending message: {:?}", why);
}
}
async fn ready(&self, _: Context, ready: Ready) {

View File

@ -1,6 +1,7 @@
use super::cmd::{help::HELP, ping::PING_COMMAND};
use super::handler::Handler;
use crate::config::Config;
use crate::config::{Config, ConfigGlobal};
use crate::img::config_file::{ConfigFile, ConfigImgGlobal};
use serenity::framework::standard::Configuration;
use serenity::{
all::GatewayIntents,
@ -9,6 +10,7 @@ use serenity::{
Client,
};
use std::collections::HashSet;
use std::fs;
use tokio::task::spawn_blocking;
#[group]
@ -23,6 +25,23 @@ pub fn start_bot(config: Config) {
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
@ -53,7 +72,7 @@ pub fn start_bot(config: Config) {
framework.configure(
Configuration::new()
.with_whitespace(true)
.prefix(config.prefix)
.prefix(config.prefix.clone())
.owners(owners),
);
@ -62,6 +81,14 @@ pub fn start_bot(config: Config) {
.framework(framework)
.await
.expect("Err creating client");
{
// Open the data lock in write mode, so keys can be inserted to it.
let mut data = client.data.write().await;
data.insert::<ConfigImgGlobal>(config_parsed.clone());
data.insert::<ConfigGlobal>(config.clone());
}
if let Err(why) = client.start().await {
println!("Client error: {why:?}");
}

View File

@ -1,5 +1,6 @@
use dotenvy::dotenv;
use serde::Deserialize;
use serenity::prelude::TypeMapKey;
use std::env;
use std::fs::read_to_string;
use std::path::PathBuf;
@ -22,6 +23,12 @@ const RUST_ENV: &str = "RUST_ENV";
const PORT: &str = "PORT";
pub struct ConfigGlobal;
impl TypeMapKey for ConfigGlobal {
type Value = Config;
}
#[derive(Deserialize, Clone)]
pub struct Config {
pub bot_name: String,

64
src/img/config_file.rs Normal file
View File

@ -0,0 +1,64 @@
use regex::Regex;
use serde::Deserialize;
use serenity::prelude::TypeMapKey;
use std::fmt::Debug;
use walkdir::{DirEntry, WalkDir};
#[derive(Deserialize, Clone)]
pub struct KeyWordItem {
pub value: Vec<String>,
pub path: Vec<String>,
}
impl KeyWordItem {
pub fn does_value_match(&self, haystack: String) -> bool {
self.value.clone().into_iter().any(|val| {
let re = Regex::new(&val).unwrap();
re.is_match(&haystack)
})
}
pub fn output_folder_content(path: String) -> Vec<DirEntry> {
let file_folder: Vec<DirEntry> = WalkDir::new(&path)
.into_iter()
.filter_entry(|_e| true)
.filter(|e| {
if let Some(file) = &e.as_ref().ok() {
return !file.metadata().unwrap().is_dir();
}
return true;
})
.filter_map(|e| e.ok())
.collect();
if file_folder.len() == 0 {
return vec![];
}
file_folder
}
}
impl Debug for KeyWordItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KeyWordItem")
.field("value", &self.value)
.field("path", &self.path)
.finish()
}
}
#[derive(Deserialize, Clone)]
pub struct ConfigFile {
pub keyword: Vec<KeyWordItem>,
}
impl ConfigFile {
pub fn parse_config(content: String) -> Result<ConfigFile, serde_yaml::Error> {
let config: ConfigFile = serde_yaml::from_str(&content)?;
Ok(config)
}
}
pub struct ConfigImgGlobal;
impl TypeMapKey for ConfigImgGlobal {
type Value = ConfigFile;
}

1
src/img/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod config_file;

View File

@ -1,2 +1,3 @@
pub mod bot;
pub mod config;
pub mod bot;
pub mod img;

View File

@ -1,5 +1,6 @@
mod bot;
mod config;
mod img;
use actix_cors::Cors;
use actix_web::{App, HttpServer};