import discord from discord.ext.commands import Cog, command, Context from discord.commands import slash_command, Option import httpx from decouple import config from collections import Counter import re GUILD = config("DISCORD_GUILD_ID", cast=int) def setup(bot: discord.Bot): bot.add_cog(Random(bot)) class Random(Cog): """Use random.org to provide truly random results""" def __init__(self, bot: discord.Bot): self.bot: discord.Bot = bot print("Initialized Random cog") # ============================================================================== @staticmethod def roll(faces: int = 6, number: int = 1) -> str: """Roll die(s) using random.org integer generation""" columns = number if number < 11 else 10 return httpx.get(f'https://www.random.org/integers/?num={number}&min=1&max={faces}&col={columns}&base=10&format=plain&rnd=new').text @staticmethod def roll_results(faces: int = 6, number: int = 1) -> str: """Returns a formatted result of dice outcomes""" result = Random.roll(faces=int(faces), number=int(number)) if faces == 100: # 00 - 99 result = re.sub('100', '00', result) result = re.sub(r'\b(\d){1}\b', r'0\1', result) # zero pad single digit return f"*You rolled {number}d{faces} and got:*\n**{result}**" @command( name='roll', aliases=['dice'] ) async def roll_prefix(self, ctx: Context, query: str = '1d6'): """Roll some dice (default 1d6)""" number, faces = query.split('d') if not number: # handle raw dN rolls, e.g. 'd6' or 'd20' number = 1 return await ctx.send(Random.roll_results(faces=int(faces), number=int(number))) @slash_command( name='roll', guild_ids=[GUILD] ) async def roll_slash(self, ctx: discord.ApplicationContext, faces: Option(int, "How many faces are on each die?", min_value=1, max_value=1_000_000_000, default=6), number: Option(int, "How many dies should be rolled?", min_value=1, max_value=10_000, default=1) ): """Roll some dice (default 1d6)""" return await ctx.respond(Random.roll_results(faces=faces, number=number)) # ============================================================================== @staticmethod def flip(number: int = 1) -> str: """Returns a raw list of Heads or Tails outcomes""" result: str = httpx.get( f'https://www.random.org/integers/?num={number}&min=1&max=2&col=1&base=10&format=plain&rnd=new' ).text result = result.replace('1', 'Heads') result = result.replace('2', 'Tails') return result @staticmethod def flip_results(number: int = 1) -> str: """Returns a formatted result of Heads and Tails counts""" result: str = Random.flip(number) if len(result) == 6: # \n counts as a character and we don't strip it return ( f"*You flipped {number} coins and got:*\n" f"**{result}**" ) c = Counter(result.split('\n')) return ( f"*You flipped {number} coins and got:*\n" f"**{c['Heads']}** Heads\n" f"**{c['Tails']}** Tails" ) @command( name='flip', aliases=['coin'] ) async def flip_prefix(self, ctx: Context, number: str = 1): """Flip a coin (default 1)""" return await ctx.send(self.flip_results(number)) @slash_command( name='flip', guild_ids=[GUILD] ) async def flip_slash(self, ctx: discord.ApplicationContext, number: Option(int, "How many coins should be flipped?", min_value=1, max_value=10_000, default=1) ): """Flip a coin (default 1)""" return await ctx.respond(self.flip_results(number))