nicer looking output

This commit is contained in:
Lilith Schier 2026-08-11 11:31:37 +02:00
parent 0ebe9e3be3
commit b437cefe5c
2 changed files with 51 additions and 31 deletions

View file

@ -5,7 +5,8 @@ use anyhow::bail;
use dotenv::dotenv;
use rand::{Rng, SeedableRng};
use serenity::all::{
Command, CommandInteraction, CommandOptionType, CreateCommandOption, CreateComponent, FullEvent, Interaction, MessageFlags, ResolvedOption, ResolvedValue,
Command, CommandInteraction, CommandOptionType, CreateCommandOption, CreateComponent,
FullEvent, Interaction, MessageFlags, ResolvedOption, ResolvedValue,
};
use serenity::builder::{
CreateCommand, CreateContainer, CreateContainerComponent, CreateInteractionResponse,
@ -80,7 +81,7 @@ impl EventHandler for Handler {
).required(true))
.add_option(CreateCommandOption::new(
CommandOptionType::Integer,
"multi",
"repeat",
"Repeat the rolled formula this many times. Useful if you're controlling too many units!",
))
.add_option(CreateCommandOption::new(
@ -92,6 +93,11 @@ impl EventHandler for Handler {
CommandOptionType::Boolean,
"private",
"Keep the roll private and hidden, only for yourself.",
))
.add_option(CreateCommandOption::new(
CommandOptionType::String,
"seed",
"Roll with a fixed seed. This bot uses xoshiro256++.",
)),
)
.await;
@ -114,12 +120,12 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
else {
bail!("Formula was not provided.")
};
let multi = options
let repeat = options
.iter()
.find(|option| option.name == "multi")
.find(|option| option.name == "repeat")
.and_then(|option| {
if let ResolvedValue::Integer(multi) = option.value {
Some(multi)
if let ResolvedValue::Integer(repeat) = option.value {
Some(repeat)
} else {
None
}
@ -147,6 +153,17 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
None
}
});
let fixed_seed = options
.iter()
.find(|option| option.name == "seed")
.and_then(|option| {
if let ResolvedValue::String(fixed_seed) = option.value {
Some(fixed_seed)
} else {
None
}
})
.and_then(|s| s.parse::<u64>().ok());
let result = parsing::parse(&formula);
let expression = match result {
@ -171,24 +188,27 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
command.defer(&ctx.http).await?;
}
let seed = rand::rng().next_u64();
let seed = fixed_seed.unwrap_or_else(|| rand::rng().next_u64());
let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(seed);
let ev = (expression.average(&mut rng)? * 10.0).round() / 10.0;
let mut text_components = Vec::with_capacity((multi + 2) as usize);
text_components.push(CreateContainerComponent::TextDisplay(
CreateTextDisplay::new(if let Some(description) = description {
format!("### {} rolled: *{description}*\n", command.user.mention())
} else {
format!("### {} rolled\n", command.user.mention())
}),
));
for _ in 0..multi {
let mut text_components = Vec::with_capacity((repeat + 2) as usize);
if let Some(description) = description {
text_components.push(CreateContainerComponent::TextDisplay(
CreateTextDisplay::new(format!(">>> {description}\n")),
));
}
for _ in 0..repeat {
text_components.push(CreateContainerComponent::TextDisplay(
CreateTextDisplay::new(format!("{}\n", expression.evaluate(&mut rng)?)),
));
}
if fixed_seed.is_some() {
text_components.push(CreateContainerComponent::TextDisplay(
CreateTextDisplay::new("***⚠️ This is not a fair roll. The result was generated based on a fixed seed and is thus fully reproducible.***".to_string()),
));
}
text_components.push(CreateContainerComponent::TextDisplay(
CreateTextDisplay::new(format!("-# Expected value: {ev} | Seed: {seed}")),
));