feat: add feature: add admin, handling of the user has administrator perm, and start working on the function that the bot has

This commit is contained in:
Max batleforc 2024-06-21 10:16:38 +02:00
parent 7524fe07e2
commit f7fefb1ab6
No known key found for this signature in database
GPG Key ID: 25D243AB4B6AC9E7
19 changed files with 291 additions and 32 deletions

View File

@ -1,6 +1,7 @@
use utoipa::OpenApi;
use crate::api::bot::info;
use crate::db::{concour, server_config, user_image};
#[derive(OpenApi)]
#[openapi(
@ -18,6 +19,10 @@ use crate::api::bot::info;
schemas(
info::Info,
info::InfoGuild,
server_config::ServerConfig,
user_image::User,
concour::Concour,
concour::ConcourStatus,
)
),
paths(

View File

@ -27,16 +27,20 @@ pub async fn answer(
if let Ok(answer) = answer {
match answer {
AnswerResult::Embed(embeds, attachments) => {
let mut reply = CreateReply::default();
reply.embeds = embeds;
reply.attachments = attachments;
let reply = CreateReply {
embeds,
attachments,
..Default::default()
};
if let Err(why) = ctx.send(reply).await {
tracing::error!("Error sending message: {:?}", why);
}
}
AnswerResult::Animated(attachments) => {
let mut reply = CreateReply::default();
reply.attachments = attachments;
let reply = CreateReply {
attachments,
..Default::default()
};
if let Err(why) = ctx.send(reply).await {
tracing::error!("Error sending message: {:?}", why);
}

View File

@ -30,13 +30,13 @@ pub async fn list(ctx: Context<'_>) -> Result<(), Error> {
.fields(chunks.to_vec())
.footer(footer)
});
let mut reply = CreateReply::default();
reply.embeds = embed_vec.collect();
let reply = CreateReply {
embeds: embed_vec.collect(),
..Default::default()
};
if let Err(why) = ctx.send(reply).await {
tracing::error!("Error sending message: {:?}", why);
}
Ok(())
}
// https://github.com/serenity-rs/poise/blob/current/examples/fluent_localization/main.rs

View File

@ -0,0 +1,88 @@
use crate::botv2::{
domain::server_config::{
admin_role::admin_role,
check_if_server_enable_and_admin::check_if_server_enable_and_user_admin,
},
init::{Context, Error},
};
use poise::{
serenity_prelude::{model::colour, CreateEmbed, CreateEmbedFooter, Role},
CreateReply,
};
use tracing::instrument;
/// add/remove admin role
#[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 admin(
ctx: Context<'_>,
#[description = "Add admin role"] add: Option<Role>,
#[description = "Remove admin role"] remove: Option<Role>,
) -> Result<(), Error> {
let guild = match ctx.guild_id() {
Some(guild) => guild,
None => return Ok(()),
};
let entity_name = ctx.data().entity_name.clone();
let footer = CreateEmbedFooter::new(entity_name.clone());
match check_if_server_enable_and_user_admin(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 role_add = match add {
Some(role) => Some(role.id.get()),
None => None,
};
let role_remove = match remove {
Some(role) => Some(role.id.get()),
None => None,
};
let mut output = match admin_role(guild.get(), role_add, role_remove).await {
Ok(_) => CreateEmbed::new()
.title("Success handling admin role")
.color(colour::Color::DARK_GREEN),
Err(_) => CreateEmbed::new()
.title("Error handling admin role")
.color(colour::Color::RED),
};
output = output.footer(footer);
let mut builder = CreateReply::default().ephemeral(true);
builder = builder.embed(output);
if let Err(why) = ctx.send(builder).await {
tracing::error!("Error sending message: {:?}", why);
}
Ok(())
}

View File

@ -1,7 +1,8 @@
use crate::botv2::{
domain::server_config::{
change_enable_server::change_enable_server,
check_if_server_enable_and_admin::check_if_server_enable_and_user_admin,
enable_feature_auto_meme::enable_feature_meme,
enable_feature_concour::enable_feature_concour,
},
init::{Context, Error},
};
@ -63,7 +64,7 @@ pub async fn feature(
}
};
let answer_auto_meme = match auto_meme {
Some(enable) => match change_enable_server(guild.get(), enable).await {
Some(enable) => match enable_feature_meme(guild.get(), enable).await {
Ok(_) => Some(
CreateEmbed::new()
.title(format!("Auto meme feature initialized: {}", enable))
@ -78,7 +79,7 @@ pub async fn feature(
None => None,
};
let answer_enable_concour = match concour {
Some(enable) => match change_enable_server(guild.get(), enable).await {
Some(enable) => match enable_feature_concour(guild.get(), enable).await {
Ok(_) => Some(
CreateEmbed::new()
.title(format!("Concour feature initialized: {}", enable))

View File

@ -1,3 +1,4 @@
pub mod admin;
pub mod feature;
pub mod init_server;
pub mod server;

View File

@ -1,5 +1,5 @@
use crate::botv2::{
cmd::server_config::{feature::feature, init_server::init},
cmd::server_config::{admin::admin, feature::feature, init_server::init},
domain::server_config::{
check_if_server_enable_and_admin::check_if_server_enable_and_user_admin,
get_server_config::get_server_config,
@ -18,7 +18,7 @@ use tracing::instrument;
slash_command,
prefix_command,
category = "server_config",
subcommands("init", "info", "feature"),
subcommands("init", "info", "feature", "admin"),
guild_only = true
)]
pub async fn server(

View File

@ -0,0 +1,53 @@
use serde::{Deserialize, Serialize};
use tracing::{info, instrument};
use crate::db::concour::Concour;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum CreateConcourError {
AlreadyExist,
FindError(String),
UnknownError(String),
}
#[instrument(level = "info")]
pub async fn create_concour(
server_id: u64,
channel_id: u64,
title: String,
description: String,
) -> Result<Option<Concour>, CreateConcourError> {
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(CreateConcourError::UnknownError(
"Error finding concour".to_string(),
));
}
};
if concour.is_some() {
info!("Concour already exist");
return Err(CreateConcourError::AlreadyExist);
}
let concour = Concour {
server_id,
channel_id,
title,
description,
..Default::default()
};
match concour.create().await {
Ok(_) => {}
Err(err) => {
tracing::error!(error = err.to_string(), "Error creating concour");
return Err(CreateConcourError::UnknownError(
"Error creating concour".to_string(),
));
}
}
Ok(Some(concour))
}

View File

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
use tracing::instrument;
use crate::db::concour::Concour;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum GetChannelConcourError {
FindError(String),
UnknownError(String),
}
#[instrument(level = "info")]
pub async fn get_channel_concour(
server_id: u64,
channel_id: u64,
) -> Result<Option<Concour>, GetChannelConcourError> {
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(GetChannelConcourError::UnknownError(
"Error finding concour".to_string(),
));
}
};
Ok(concour)
}

View File

@ -1,2 +1,4 @@
pub mod check_if_allowed;
pub mod create_concour;
pub mod get_channel_concour;
pub mod list_concour;

View File

@ -0,0 +1,45 @@
use crate::db::server_config::ServerConfig;
use serde::{Deserialize, Serialize};
use tracing::{info, instrument};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum AddAdminConfigError {
UnknownError(String),
}
#[instrument(level = "info")]
pub async fn admin_role(
server_id: u64,
role_add_id: Option<u64>,
role_remove_id: Option<u64>,
) -> Result<(), AddAdminConfigError> {
let server_config = match ServerConfig::find_by_server_id(&server_id).await {
Ok(server_config) => server_config,
Err(err) => {
tracing::error!(error = err.to_string(), "Error finding server config");
return Err(AddAdminConfigError::UnknownError(
"Error finding server config".to_string(),
));
}
};
if let Some(mut config) = server_config {
if let Some(role_id) = role_add_id {
info!(id = role_id, "Adding role to admin role");
config.admin_role.push(role_id);
}
if let Some(role_id) = role_remove_id {
info!(id = role_id, "Remove role to admin role");
config.admin_role.retain(|&x| x != role_id);
}
match config.update().await {
Ok(_) => {}
Err(err) => {
tracing::error!(error = err.to_string(), "Error updating server config");
return Err(AddAdminConfigError::UnknownError(
"Error updating server config".to_string(),
));
}
}
}
Ok(())
}

View File

@ -34,6 +34,14 @@ pub async fn check_if_server_enable_and_user_admin(
match guild.member(http, UserId::new(user_id)).await {
Ok(member) => {
info!("Checking if user is admin");
if member
.permissions
.into_iter()
.any(|perm| perm.administrator())
{
info!("User has administarator permission");
return Ok((true, Some(server_config)));
}
Ok((
server_config
.clone()

View File

@ -1,3 +1,4 @@
pub mod admin_role;
pub mod change_enable_server;
pub mod check_if_server_enable;
pub mod check_if_server_enable_and_admin;

View File

@ -19,7 +19,7 @@ pub async fn event_handler(
serenity::FullEvent::Message { new_message } => {
if new_message.author.bot
|| new_message.content.starts_with(&data.config.prefix.clone())
|| new_message.content.len() == 0
|| new_message.content.is_empty()
{
return Ok(());
}
@ -53,7 +53,7 @@ pub async fn event_handler(
if !user_in_db.enable {
return Ok(());
}
if config_img.keyword.len() == 0 || new_message.content.len() > 50 {
if config_img.keyword.is_empty() || new_message.content.len() > 50 {
return Ok(());
}

View File

@ -61,7 +61,7 @@ pub async fn start_bot(config: Config, rx: oneshot::Receiver<()>) -> Arc<Http> {
.options(poise::FrameworkOptions {
commands: vec![age(), ping(), help(), list(), enable(), answer(), server()],
prefix_options: poise::PrefixFrameworkOptions {
prefix: Some(prefix.into()),
prefix: Some(prefix),
..Default::default()
},
event_handler: |ctx, event, framework, data| {
@ -78,7 +78,7 @@ pub async fn start_bot(config: Config, rx: oneshot::Receiver<()>) -> Arc<Http> {
entity_name: format!(
"{}-{}",
config.bot_name.clone(),
env!("CARGO_PKG_VERSION").to_string()
env!("CARGO_PKG_VERSION")
),
})
})

View File

@ -3,10 +3,11 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use surrealdb::opt::Resource;
use utoipa::ToSchema;
const CONCOUR: &str = "concour";
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub enum ConcourStatus {
Created,
Paused,
@ -20,7 +21,7 @@ impl fmt::Display for ConcourStatus {
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub struct Concour {
pub server_id: u64,
pub enable: bool,
@ -37,12 +38,12 @@ pub struct Concour {
pub winner: HashMap<String, (u64, chrono::DateTime<chrono::Utc>)>,
}
impl Concour {
pub fn new(server_id: u64, channel_id: u64, enable: bool) -> Result<Self, surrealdb::Error> {
Ok(Self {
server_id,
enable,
channel_id,
impl Default for Concour {
fn default() -> Self {
Self {
server_id: 0,
enable: true,
channel_id: 0,
title: String::new(),
description: String::new(),
start_date: chrono::Utc::now(),
@ -53,6 +54,26 @@ impl Concour {
index_keyword: 0,
status: ConcourStatus::Created,
winner: HashMap::new(),
}
}
}
impl Concour {
pub fn new(server_id: u64, channel_id: u64) -> Result<Self, surrealdb::Error> {
Ok(Self {
server_id,
enable: true,
channel_id,
title: String::new(),
description: String::new(),
start_date: chrono::Utc::now(),
periode: time::Duration::days(1),
role_récompense: 0,
keywords: Vec::new(),
banner: None,
index_keyword: 0,
status: ConcourStatus::Created,
winner: HashMap::new(),
})
}
pub async fn create(&self) -> Result<(), surrealdb::Error> {

View File

@ -2,13 +2,14 @@ use super::init::DB;
use serde::{Deserialize, Serialize};
use std::fmt;
use surrealdb::opt::Resource;
use utoipa::ToSchema;
const SERVER_CONFIG: &str = "server_config";
const fn default_false() -> bool {
false
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub struct ServerConfig {
#[serde(default = "default_false")]
pub enable: bool,

View File

@ -1,11 +1,12 @@
use serde::{Deserialize, Serialize};
use surrealdb::opt::Resource;
use utoipa::ToSchema;
use super::init::DB;
const USERIMAGE: &str = "userimage";
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub struct User {
pub server_id: u64,
pub user_id: u64,

View File

@ -1,6 +1,6 @@
use poise::serenity_prelude::prelude::TypeMapKey;
use regex::Regex;
use serde::Deserialize;
use poise::serenity_prelude::prelude::TypeMapKey;
use std::fmt::Debug;
use walkdir::{DirEntry, WalkDir};
@ -18,18 +18,18 @@ impl KeyWordItem {
})
}
pub fn output_folder_content(path: String) -> Vec<DirEntry> {
let file_folder: Vec<DirEntry> = WalkDir::new(&path)
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;
true
})
.filter_map(|e| e.ok())
.collect();
if file_folder.len() == 0 {
if file_folder.is_empty() {
return vec![];
}
file_folder