use crate::{Context, Error}; use crate::shared_functions::get_all_admin_users; use poise::serenity_prelude as sere; use tracing::{error, info, debug, trace}; #[derive(Debug, poise::ChoiceParameter)] pub enum AdminOperationType { #[name = "Add user"] AddUser, #[name = "Remove user"] RemoveUser, #[name = "List users"] ListUsers, #[name = "Add role"] AddRole, #[name = "Remove role"] RemoveRole, #[name = "List roles"] ListRoles, } /// Admin command #[poise::command(slash_command, prefix_command)] pub async fn manage_admins(ctx: Context<'_>, #[description = "Operation to perform"] operation: Option, #[description = "User"] user: Option, #[description = "Role"] role: Option ) -> Result<(), Error> { trace!("manage_admins command called by user {}", ctx.author().id.get()); let response_message = ctx.say("Running command...").await?; if !is_admin(ctx).await? { debug!("User {} is not an admin, denying access", ctx.author().id.get()); response_message.edit(ctx, poise::CreateReply::default().content("You are not allowed to run this command.")).await?; return Ok(()); } match operation { Some(AdminOperationType::AddUser) => { if let Some(s_user) = user { add_admin_user(ctx, &response_message, s_user).await?; } else { edit_response_message(&response_message, ctx, "Please provide a user".to_string(), false).await?; } } Some(AdminOperationType::RemoveUser) => { if let Some(s_user) = user { remove_admin_user(ctx, &response_message, s_user).await?; } else { edit_response_message(&response_message, ctx, "Please provide a user".to_string(), false).await?; } } Some(AdminOperationType::ListUsers) => { list_admin_users(ctx, &response_message).await?; } Some(AdminOperationType::AddRole) => { if let Some(s_role) = role { add_admin_role(ctx, &response_message, s_role).await?; } else { edit_response_message(&response_message, ctx, "Please provide a role".to_string(), false).await?; } } Some(AdminOperationType::RemoveRole) => { if let Some(s_role) = role { remove_admin_role(ctx, &response_message, s_role).await?; } else { edit_response_message(&response_message, ctx, "Please provide a role".to_string(), false).await?; } } Some(AdminOperationType::ListRoles) => { list_admin_roles(ctx, &response_message).await?; } None => { edit_response_message(&response_message, ctx, "Please provide an operation".to_string(), false).await?; } } Ok(()) } async fn is_admin(ctx: Context<'_>) -> Result { let user_running_command = ctx.author().id.get() as i64; let admins = get_all_admin_users(ctx).await?; for user in admins { if user.id.get() as i64 == user_running_command { return Ok(true); } } Ok(false) } async fn add_admin_user<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>, user: sere::Member) -> Result<(), Error> { trace!("Executing add_admin_user for user {}", user.user.name); if is_target_admin_user(ctx, &user).await? { trace!("User {} is already an admin", user.user.name); edit_response_message(msg, ctx, format!("{} is already an admin", user.user.name), false).await?; return Ok(()); } let query = sqlx::query!("INSERT INTO admin_users (guild_id, user_id) VALUES ($1, $2);", ctx.guild_id().unwrap().get() as i64, user.user.id.get() as i64).execute(&ctx.data().pool).await; match query { Ok(_) => { info!("Added user {} as admin in guild {}", user.user.name, ctx.guild_id().unwrap().get()); edit_response_message(msg, ctx, format!("Added {} as admin", user.user.name), false).await?; } Err(e) => { error!("Failed to add user {} as admin in guild {}: {:?}", user.user.name, ctx.guild_id().unwrap().get(), e); edit_response_message(msg, ctx, format!("Failed to add {} as admin: {}", user.user.name, e), false).await?; } } Ok(()) } async fn edit_response_message<'a>(response_message: &poise::ReplyHandle<'a>, ctx: Context<'_>, content: String, silent: bool) -> Result<(), Error> { if silent { response_message.edit(ctx, poise::CreateReply::default().content(content).allowed_mentions(serenity::all::CreateAllowedMentions::new().empty_users())).await?; } else { response_message.edit(ctx, poise::CreateReply::default().content(content)).await?; } Ok(()) } async fn is_target_admin_user(ctx: Context<'_>, user: &sere::Member) -> Result { let admins = get_all_admin_users(ctx).await?; for admin in admins { if admin.id.get() as i64 == user.user.id.get() as i64 { return Ok(true); } } Ok(false) } async fn remove_admin_user<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>, user: sere::Member) -> Result<(), Error> { trace!("Executing remove_admin_user for user {}", user.user.name); if !is_target_admin_user(ctx, &user).await? { trace!("User {} is not an admin", user.user.name); edit_response_message(msg, ctx, format!("{} is not an admin", user.user.name), false).await?; return Ok(()); } let query = sqlx::query!("DELETE FROM admin_users WHERE guild_id = $1 AND user_id = $2;", ctx.guild_id().unwrap().get() as i64, user.user.id.get() as i64).execute(&ctx.data().pool).await; match query { Ok(_) => { info!("Removed user {} as admin in guild {}", user.user.name, ctx.guild_id().unwrap().get()); edit_response_message(msg, ctx, format!("Removed {} as admin", user.user.name), false).await?; } Err(e) => { error!("Failed to remove user {} as admin in guild {}: {:?}", user.user.name, ctx.guild_id().unwrap().get(), e); edit_response_message(msg, ctx, format!("Failed to remove {} as admin: {}", user.user.name, e), false).await?; } } Ok(()) } async fn list_admin_users<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>) -> Result<(), Error> { let admins = get_all_admin_users(ctx).await?; let admin_strings: Vec = admins.iter().map(|user| format!("<@{}>", user.id.get())).collect(); edit_response_message(msg, ctx, format!("Admin users: {}", admin_strings.join(", ")), true).await?; Ok(()) } async fn add_admin_role<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>, role: sere::Role) -> Result<(), Error> { trace!("Executing add_admin_role for role {}", role.name); if is_target_admin_role(ctx, &role).await? { trace!("Role {} is already an admin role", role.name); edit_response_message(msg, ctx, format!("{} is already an admin role", role.name), false).await?; return Ok(()); } let query = sqlx::query!("INSERT INTO admin_roles (guild_id, role_id) VALUES ($1, $2);", ctx.guild_id().unwrap().get() as i64, role.id.get() as i64).execute(&ctx.data().pool).await; match query { Ok(_) => { info!("Added role {} as admin role in guild {}", role.name, ctx.guild_id().unwrap().get()); edit_response_message(msg, ctx, format!("Added {} as admin role", role.name), false).await?; } Err(e) => { error!("Failed to add role {} as admin role in guild {}: {:?}", role.name, ctx.guild_id().unwrap().get(), e); edit_response_message(msg, ctx, format!("Failed to add {} as admin role: {}", role.name, e), false).await?; } } Ok(()) } async fn remove_admin_role<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>, role: sere::Role) -> Result<(), Error> { trace!("Executing remove_admin_role for role {}", role.name); if !is_target_admin_role(ctx, &role).await? { trace!("Role {} is not an admin role", role.name); edit_response_message(msg, ctx, format!("{} is not an admin role", role.name), false).await?; return Ok(()); } let query = sqlx::query!("DELETE FROM admin_roles WHERE guild_id = $1 AND role_id = $2;", ctx.guild_id().unwrap().get() as i64, role.id.get() as i64).execute(&ctx.data().pool).await; match query { Ok(_) => { info!("Removed role {} as admin role in guild {}", role.name, ctx.guild_id().unwrap().get()); edit_response_message(msg, ctx, format!("Removed {} as admin role", role.name), false).await?; } Err(e) => { error!("Failed to remove role {} as admin role in guild {}: {:?}", role.name, ctx.guild_id().unwrap().get(), e); edit_response_message(msg, ctx, format!("Failed to remove {} as admin role: {}", role.name, e), false).await?; } } Ok(()) } async fn list_admin_roles<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>) -> Result<(), Error> { let roles = sqlx::query!("SELECT role_id FROM admin_roles WHERE guild_id = $1", ctx.guild_id().unwrap().get() as i64).fetch_all(&ctx.data().pool).await?; let role_strings: Vec = roles.iter().map(|role| format!("<@&{}>", role.role_id)).collect(); edit_response_message(msg, ctx, format!("Admin roles: {}", role_strings.join(", ")), true).await?; Ok(()) } async fn get_admin_roles(ctx: Context<'_>) -> Result, Error> { let roles = sqlx::query!("SELECT role_id FROM admin_roles WHERE guild_id = $1", ctx.guild_id().unwrap().get() as i64).fetch_all(&ctx.data().pool).await?; let mut admin_roles = Vec::new(); for role in roles { admin_roles.push(ctx.guild().unwrap().roles.get(&sere::RoleId::new(role.role_id as u64)).unwrap().clone()); } Ok(admin_roles) } async fn is_target_admin_role<'a>(ctx: Context<'a>, role: &sere::Role) -> Result { let roles = get_admin_roles(ctx).await?; for r in roles { if r.id.get() as i64 == role.id.get() as i64 { return Ok(true); } } Ok(false) }