silly goober bot
at main 128 lines 3.6 kB view raw
1use crate::types::Context; 2use color_eyre::Result; 3use poise::serenity_prelude::all::{EditMember, Timestamp, User}; 4use regex::Regex; 5 6#[poise::command(slash_command, guild_only, required_permissions = "MODERATE_MEMBERS")] 7pub async fn timeout( 8 ctx: Context<'_>, 9 user: User, 10 #[description = "how long the time out is for. (5s, 2m, 12h, 3d, 2w)"] duration: String, 11 reason: Option<String>, 12) -> Result<()> { 13 let guild = ctx 14 .serenity_context() 15 .http 16 .get_guild(ctx.guild_id().unwrap()) 17 .await?; 18 19 let timeout_max_value = 2419200; 20 21 let user_member = guild.member(ctx, user.id).await.unwrap(); 22 let bot_member = guild.member(ctx, ctx.framework().bot_id).await.unwrap(); 23 let author_member = guild.member(ctx, ctx.author()).await.unwrap(); 24 25 let user_highest_role = user_member 26 .roles 27 .iter() 28 .filter_map(|role| guild.roles.get(role)) 29 .max_by_key(|role| role.position); 30 31 let author_highest_role = author_member 32 .roles 33 .iter() 34 .filter_map(|role| guild.roles.get(role)) 35 .max_by_key(|role| role.position); 36 37 let bot_highest_role = bot_member 38 .roles 39 .iter() 40 .filter_map(|role| guild.roles.get(role)) 41 .max_by_key(|role| role.position); 42 43 if guild 44 .user_permissions_in( 45 &ctx.guild_channel().await.unwrap(), 46 &guild.member(ctx, user.id).await.unwrap(), 47 ) 48 .administrator() 49 { 50 ctx.say("Target user has permission: ``Administrator``") 51 .await?; 52 return Ok(()); 53 } 54 55 if !guild 56 .user_permissions_in( 57 &ctx.guild_channel().await.unwrap(), 58 &guild.member(ctx, ctx.framework().bot_id).await.unwrap(), 59 ) 60 .moderate_members() 61 { 62 ctx.say("Bot missing permission: ``Moderate Members``") 63 .await?; 64 return Ok(()); 65 } 66 67 if author_highest_role < user_highest_role { 68 ctx.say("User has higher role then you.").await?; 69 return Ok(()); 70 } 71 72 if bot_highest_role < user_highest_role { 73 ctx.say("User has higher role then bot!").await?; 74 return Ok(()); 75 } 76 77 let Some(seconds) = parse_duration(&duration) else { 78 ctx.say("Invalid duration! (Valid durations: 5s, 2m, 12h, 3d, 2w)") 79 .await?; 80 return Ok(()); 81 }; 82 83 if seconds > timeout_max_value { 84 ctx.say("Duration too long! (Max duration: 28d)").await?; 85 return Ok(()); 86 } 87 88 let timeout_until = 89 Timestamp::from_unix_timestamp(Timestamp::now().unix_timestamp() + seconds as i64)?; 90 91 guild 92 .member(ctx, user.id) 93 .await? 94 .edit( 95 ctx, 96 EditMember::new() 97 .disable_communication_until(timeout_until.to_string()) 98 .audit_log_reason(&reason.unwrap_or("No reason provided.".to_string())), 99 ) 100 .await?; 101 102 ctx.say(format!("Timed out <@{}> for {}!", user.id, duration)) 103 .await?; 104 105 Ok(()) 106} 107 108fn parse_duration(input: &str) -> Option<u64> { 109 let re = Regex::new(r"(\d+)([smhdw])").unwrap(); 110 111 if let Some(caps) = re.captures(input) { 112 let value: u64 = caps[1].parse().ok()?; 113 let unit = &caps[2]; 114 115 let seconds = match unit { 116 "s" => value, 117 "m" => value * 60, 118 "h" => value * 3600, 119 "d" => value * 86400, 120 "w" => value * 604800, 121 _ => return None, 122 }; 123 124 Some(seconds) 125 } else { 126 None 127 } 128}