use log::{error, warn}; use rand::Rng; use serenity::{ builder::CreateApplicationCommand, client::Context, model::application::{ command::CommandOptionType, interaction::application_command::ApplicationCommandInteraction, }, }; use crate::bot::commands::audio::edit_response; use super::audio::create_response; pub async fn run(ctx: &Context, command: &ApplicationCommandInteraction) { if let Err(why) = create_response(&ctx, &command, format!("Processing command...")).await { error!("Failed to create response message: {}", why); return; } let dice_string: String = match command.data.options.get(0) { Some(o) => match &o.value { Some(v) => match v.as_str() { Some(s) => s.split_whitespace().collect::(), None => { warn!("Missing dice option"); if let Err(why) = edit_response(&ctx, &command, format!("Dice option is missing")).await { error!("Failed to create response message: {}", why); } return; } }, None => { warn!("Missing dice option"); if let Err(why) = edit_response(&ctx, &command, format!("Dice option is missing")).await { error!("Failed to create response message: {}", why); } return; } }, None => { warn!("Missing dice option"); if let Err(why) = edit_response(&ctx, &command, format!("Dice option is missing")).await { error!("Failed to create response message: {}", why); } return; } }; let dice = parse_dice(dice_string.as_str()); match dice { Ok((count, sides, modifier)) => { let mut rolls = Vec::new(); let mut total = 0; for _ in 0..count { let roll = rand::thread_rng().gen_range(1..=sides); total += roll; rolls.push(roll); } let response = format!( "{}d{}{} = {}", count, sides, if modifier > 0 { format!("+{}", modifier) } else if modifier < 0 { format!("-{}", modifier) } else { "".to_string() }, total + (modifier as u32) ); if let Err(why) = edit_response(&ctx, &command, response).await { error!("Failed to create response message: {}", why); } } Err(why) => { if let Err(why) = edit_response(&ctx, &command, format!("Invalid dice string: {}", why)).await { error!("Failed to create response message: {}", why); } } } } fn parse_dice(dice: &str) -> Result<(u32, u32, i32), String> { let mut parts = dice.split("d"); let count = match parts.next() { Some(c) => match c.parse::() { Ok(n) => n, Err(_) => return Err(format!("Invalid dice count: {}", c)), }, None => return Err(format!("Invalid dice string: {}", dice)), }; let mut positive_modifier = true; let mut parts = match parts.next() { Some(p) => { // Check if contains a +/- modifier if p.contains("+") { positive_modifier = true; p.split("+") } else if p.contains("-") { positive_modifier = false; p.split("-") } else { p.split("+") } } None => return Err(format!("Invalid dice string: {}", dice)), }; let sides = match parts.next() { Some(s) => match s.parse::() { Ok(n) => { if n == 4 || n == 6 || n == 8 || n == 10 || n == 12 || n == 20 || n == 100 { n } else { return Err(format!("Invalid dice sides: {}", s)); } } Err(_) => return Err(format!("Invalid dice sides: {}", s)), }, None => return Err(format!("Invalid dice string: {}", dice)), }; let modifier = match parts.next() { Some(m) => match m.parse::() { Ok(n) => { if positive_modifier { n } else { n * -1 } } Err(_) => return Err(format!("Invalid dice modifier: {}", m)), }, None => 0, }; Ok((count, sides, modifier)) } pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command .name("roll") .description("Rolls D&D dice") .create_option(|option| { option .name("dice") .description("Dice to roll") .kind(CommandOptionType::String) .required(true) }) }