diff --git a/src/bot/discord/commands/dice.py b/src/bot/discord/commands/dice.py index 299fb12..eb74fa2 100644 --- a/src/bot/discord/commands/dice.py +++ b/src/bot/discord/commands/dice.py @@ -1,14 +1,32 @@ +import typing +from datetime import datetime + +import disnake from disnake.ext import commands -from src.dice.dice import DiceRoller +from src.dice.dice import DiceRoller, DieExpressionResult class DiceCog(commands.Cog): def __init__(self, bot): self.bot = bot + @staticmethod + def format_die_result_to_fields( + die_result: DieExpressionResult, + ) -> list[typing.Tuple[str, str]]: + roll_fields = [] + for index, die in enumerate(die_result.dies): + roll_fields.append( + ( + f"- #{index+1} 🎲 ", + f"Res: {die.result}, Mod: {die.modifier}, Rolls: {die.rolls}", + ) + ) + return roll_fields + @commands.command(name="roll", aliases=["r"]) - async def roll(self, ctx, dice_expression: str): + async def roll(self, ctx, _dice_expression: str): """ A die can be rolled using the following expression: - 1d20 will roll a 20-faceted die and output the result a random number between 1 and 20. @@ -16,13 +34,30 @@ class DiceCog(commands.Cog): - 2d20 will roll a two d20 dies and multiply the result by two. - 2d20+5 will roll a two d20 dies and multiply the result by two and ads 5. """ - if dice_expression == "": - return - if dice_expression == "0/0": # easter eggs - return await ctx.send("What do you expect me to do, destroy the universe?") - try: - roll_result = DiceRoller.roll_simple(dice_expression) - await ctx.send(f"You rolled: {roll_result}") + message: str = ctx.message.clean_content + dice_expression = message.split(" ", 1)[1] + if dice_expression == "": + return + if dice_expression == "0/0": # easter eggs + return await ctx.send( + "What do you expect me to do, destroy the universe?" + ) + + die_result: DieExpressionResult = DiceRoller.roll(dice_expression) + + embed = disnake.Embed( + title="", + description=f"{dice_expression} = {die_result.total}", + timestamp=datetime.now(), + ) + embed_fields = self.format_die_result_to_fields(die_result) + for title, value in embed_fields: + embed.add_field(title, value, inline=False) + + await ctx.send( + f"The mighty **{ctx.author.name}** has rolled the dice for a total of **{die_result.total}**!", + embed=embed, + ) except ValueError as e: await ctx.send(f"Roll failed: {e}") diff --git a/src/dice/dice.py b/src/dice/dice.py index 16679e3..2b2f778 100644 --- a/src/dice/dice.py +++ b/src/dice/dice.py @@ -57,4 +57,15 @@ class DiceRoller: :return: The die result. """ result = DiceRoller._parser.parse(expression) - return DieExpressionResult(**result) + + dies = [] + for die in result.get("dies", []): + dies.append( + DieRollResult( + modifier=die.get("modifier", 0), + result=die.get("result"), + rolls=die.get("rolls"), + type=die.get("type"), + ) + ) + return DieExpressionResult(total=result.get("total"), dies=dies) diff --git a/src/dice/semantics.py b/src/dice/semantics.py index ad506db..ec06ff4 100644 --- a/src/dice/semantics.py +++ b/src/dice/semantics.py @@ -15,7 +15,10 @@ class DieSemantics: return {"total": die.get("result"), "dies": [die]} elif isinstance(die, list): return_value = {"total": 0, "dies": copy.deepcopy(die)} - operators = deque(ast.get("op", [])) + operators = ast.get("op", []) + if not isinstance(operators, list): + operators = [operators] + operators = deque(operators) die_results = deque(map(lambda x: x.get("result"), die)) # Note: we may need to use a dequeue, the ops are quite inefficient. diff --git a/tests/bot/__init__.py b/tests/bot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/bot/discord/__init__.py b/tests/bot/discord/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/bot/discord/commands/__init__.py b/tests/bot/discord/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/bot/discord/commands/test_dice.py b/tests/bot/discord/commands/test_dice.py new file mode 100644 index 0000000..ce70c70 --- /dev/null +++ b/tests/bot/discord/commands/test_dice.py @@ -0,0 +1,18 @@ +from src.bot.discord.commands.dice import DiceCog +from src.dice.dice import DieExpressionResult, DieRollResult + + +def test_format_die_result_to_message(): + message = DiceCog.format_die_result_to_fields( + DieExpressionResult( + total=25, + dies=[ + DieRollResult(result=10, modifier=5, rolls=[10], type="d"), + DieRollResult(result=15, modifier=0, rolls=[10, 5], type="d"), + ], + ), + ) + assert message == [ + ("- #1 🎲 ", "Res: 10, Mod: 5, Rolls: [10]"), + ("- #2 🎲 ", "Res: 15, Mod: 0, Rolls: [10, 5]"), + ]