Updated roll to notify a user

This commit is contained in:
2024-12-18 18:32:49 -05:00
parent 56e44442e4
commit c82fee0dd3
2 changed files with 112 additions and 79 deletions

View File

@@ -1,34 +1,33 @@
use rand::Rng; use rand::Rng;
use serenity::all::{ use serenity::all::{
CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, Mentionable, UserId
}; };
use crate::bot::commands::{create_response, create_dm, edit_response}; use crate::bot::commands::{create_response, edit_response, user_id_dm};
pub async fn run(ctx: &Context, command: &CommandInteraction) { pub async fn run(ctx: &Context, command: &CommandInteraction) {
let hidden = match command.data.options.get(1) { // Check if the roll result is private
Some(o) => match o.value.as_bool() { let private = command.data.options.iter().find(|opt| opt.name == "private")
Some(b) => b, .and_then(|o| o.value.as_bool())
None => false, .unwrap_or(true);
},
None => false, // Retrieve the DM's name or ID from the options (optional)
}; let user = command.data.options.iter().find(|opt| opt.name == "user")
create_response(&ctx, &command, format!("Rolling..."), hidden).await; .and_then(|o| o.value.as_mentionable());
let dice_string = match command.data.options.get(0) {
Some(o) => match o.value.as_str() { create_response(&ctx, &command, format!("Rolling..."), private).await;
Some(s) => s.split_whitespace().collect::<String>(),
let dice_string = match command.data.options.get(0)
.and_then(|o| o.value.as_str())
.map(|s| s.split_whitespace().collect::<String>()) {
Some(dice_value) => dice_value,
None => { None => {
log::warn!("Missing dice option"); log::warn!("Missing or invalid dice option");
edit_response(&ctx, &command, format!("Dice option is missing")).await; let _ = edit_response(&ctx, &command, "Dice option is missing".to_string()).await;
return; return;
} }
}, };
None => {
log::warn!("Missing dice option");
edit_response(&ctx, &command, format!("Dice option is missing")).await;
return;
}
};
let dice = parse_dice(dice_string.as_str()); let dice = parse_dice(dice_string.as_str());
match dice { match dice {
Ok((count, sides, modifier)) => { Ok((count, sides, modifier)) => {
@@ -52,7 +51,15 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
"".to_string() "".to_string()
} }
); );
edit_response(&ctx, &command, response).await;
match user {
Some(id) => {
let user_id = UserId::new(id.get());
user_id_dm(&ctx, &user_id, format!("Dice roll from {}: {}", &command.user.mention(), response)).await;
edit_response(&ctx, command, format!("Sending dice roll results to {}", &user_id.mention())).await;
},
None => edit_response(&ctx, &command, response).await
};
} }
Err(why) => { Err(why) => {
edit_response(&ctx, &command, format!("Invalid dice string: {}", why)).await; edit_response(&ctx, &command, format!("Invalid dice string: {}", why)).await;
@@ -61,64 +68,72 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
} }
fn parse_dice(dice: &str) -> Result<(u32, u32, i32), String> { fn parse_dice(dice: &str) -> Result<(u32, u32, i32), String> {
let mut parts = dice.split("d"); // If the input is just a number (e.g., "20" or "6"), assume it's the number of sides
let count = match parts.next() { if let Ok(n) = dice.parse::<u32>() {
Some(c) => match c.parse::<u32>() { return Ok((1, n, 0)); // Assume 1 dice with 0 modifiers
Ok(n) => n, }
Err(_) => return Err(format!("Invalid dice count: {}", c)),
}, // If the input starts with "d", assume it's shorthand for "1dX"
None => return Err(format!("Invalid dice string: {}", dice)), let dice = if dice.starts_with("d") {
format!("1{}", dice) // Prepend "1"
} else {
dice.to_string()
}; };
let mut parts = dice.split(['d', '+', '-'].as_ref());
let mut positive_modifier = true; let mut positive_modifier = true;
let mut parts = match parts.next() {
Some(p) => { // Parse the dice count
// Check if contains a +/- modifier let count = match parts.next() {
if p.contains("+") { Some("") => 1, // Handle cases like "d6", assume 1 dice
positive_modifier = true; Some(c) => match c.parse::<u32>() {
p.split("+") Ok(n) => n,
} else if p.contains("-") { Err(_) => return Err(format!("Invalid dice count: {}", c)),
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::<u32>() {
Ok(n) => {
if n == 4 || n == 6 || n == 8 || n == 10 || n == 12 || n == 20 || n == 100 {
n
} else {
return Err(format!(
"Expected one of d4, d6, d8, d10, d12, d20, d100 but received d{}",
s
));
}
}
Err(_) => {
return Err(format!(
"Expected one of d4, d6, d8, d10, d12, d20, d100 but received d{}",
s
))
}
}, },
None => return Err(format!("Invalid dice string: {}", dice)), None => return Err(format!("Invalid dice string: {}", dice)),
}; };
// Parse the number of sides
let sides_part = parts.next().ok_or_else(|| format!("Invalid dice string: {}", dice))?;
let sides = match sides_part.parse::<u32>() {
Ok(n) => {
if [4, 6, 8, 10, 12, 20, 100].contains(&n) {
n
} else {
return Err(format!(
"Expected one of d4, d6, d8, d10, d12, d20, d100 but received d{}",
n
));
}
}
Err(_) => return Err(format!(
"Expected one of d4, d6, d8, d10, d12, d20, d100 but received d{}",
sides_part
)),
};
// Determine if there's a modifier (+ or -)
if dice.contains('+') {
positive_modifier = true;
} else if dice.contains('-') {
positive_modifier = false;
}
// Parse the modifier, if present
let modifier = match parts.next() { let modifier = match parts.next() {
Some(m) => match m.parse::<i32>() { Some(m) => match m.parse::<i32>() {
Ok(n) => { Ok(n) => {
if positive_modifier { if positive_modifier {
n n
} else { } else {
n * -1 -n
}
} }
} Err(_) => return Err(format!("Invalid dice modifier: {}", m)),
Err(_) => return Err(format!("Invalid dice modifier: {}", m)),
}, },
None => 0, None => 0, // No modifier found
}; };
Ok((count, sides, modifier)) Ok((count, sides, modifier))
} }
@@ -131,9 +146,13 @@ pub fn register() -> CreateCommand {
.add_option( .add_option(
CreateCommandOption::new( CreateCommandOption::new(
CommandOptionType::Boolean, CommandOptionType::Boolean,
"hidden", "private",
"Whether the roll should be hidden", "Make the roll private (Default: True)",
) )
.required(false), .required(false),
) )
.add_option(
CreateCommandOption::new(CommandOptionType::Mentionable, "user", "User to receive the roll results")
.required(false),
)
} }

View File

@@ -1,7 +1,6 @@
use serenity::prelude::*; use serenity::prelude::*;
use serenity::all::{ use serenity::all::{
CommandInteraction, CreateInteractionResponse, CreateInteractionResponseMessage, CreateMessage, CommandInteraction, CreateInteractionResponse, CreateInteractionResponseMessage, CreateMessage, EditInteractionResponse, InteractionResponseFlags, Message, User, UserId
EditInteractionResponse, InteractionResponseFlags, Message,
}; };
pub mod audio; pub mod audio;
@@ -14,13 +13,28 @@ pub async fn process_message(ctx: &Context, command: &CommandInteraction, privat
create_response(&ctx, &command, format!("Processing..."), private).await; create_response(&ctx, &command, format!("Processing..."), private).await;
} }
pub async fn create_dm( pub async fn user_id_dm(
ctx: &Context, ctx: &Context,
command: &CommandInteraction, user_id: &UserId,
content: String, content: String,
) -> Option<Message> { ) -> Option<Message> {
let data = CreateMessage::new().content(content.to_owned()); let data = CreateMessage::new().content(content.to_owned());
return match command.user.direct_message(ctx, data).await { return match user_id.dm(ctx, data).await {
Ok(message) => Some(message),
Err(err) => {
log::error!("Failed to create direct message for {content}\n{err}");
None
}
};
}
pub async fn user_dm(
ctx: &Context,
user: &User,
content: String,
) -> Option<Message> {
let data = CreateMessage::new().content(content.to_owned());
return match user.direct_message(ctx, data).await {
Ok(message) => Some(message), Ok(message) => Some(message),
Err(err) => { Err(err) => {
log::error!("Failed to create direct message for {content}\n{err}"); log::error!("Failed to create direct message for {content}\n{err}");