Example #1
0
    async def _beg_autocomplete(
        self, ctx: commands.SlashContext, interaction: discord.Interaction
    ) -> typing.Any:
        """
        Autocomplete coroutine, responsible for sending the list of locations
        """

        try:
            async with vbu.DatabaseConnection() as db:
                cache: utils.CachedUser = await utils.get_user_cache(
                    ctx.author.id,
                    db=db,
                    bot=self.bot,
                    logger=self.logger,
                )
                begging_skill = cache.get_skill("BEGGING")
                locations = utils.BeggingLocations(
                    begging_skill.level, *self.bot.begging["locations"]
                )

                # try:
                #     value: str = interaction.data["options"][0]["value"]

                # # Somehow no options were passed, so just default to an empty string
                # except (AttributeError, IndexError, KeyError):
                #     value: str = ""

                print(interaction.data, type(interaction.data), sep="\n")
                if interaction.options is not None:
                    # value: str = interaction.data["options"][0]["value"]  # type: ignore
                    value: typing.Optional[str] = interaction.options[0].value
                else:
                    value = None

                if value is None:
                    result = [
                        discord.ApplicationCommandOptionChoice(
                            f"Level {l.level}: {l.name}  -  {l.description}", l.id
                        )
                        for l in locations.locations
                    ]
                else:
                    result = [
                        discord.ApplicationCommandOptionChoice(
                            f"Level {l.level}: {l.name}  -  {l.description}", l.id
                        )
                        for l in locations.locations
                        if value.lower()
                        in "".join(
                            {l.name, l.description, l.id, str(l.emoji), str(l.level)}
                        ).lower()
                    ]
                return await interaction.response.send_autocomplete(result)
        except Exception:
            self.logger.exception("Autocomplete raised exception")  # F**k you, python.
Example #2
0
    async def update_db_from_user_cache(self) -> None:
        """
        This task updates the database from the user cache every minute.
        """

        self.logger.info("Updating database from user cache...")

        # Establish a connection to the database
        async with vbu.DatabaseConnection() as db:

            # Iterate through all cached users (convert to list to avoid RuntimeError: dictionary changed size during iteration)
            for user_id, user_cache in list(self.bot.user_cache.items()):

                # Iterate through all of their skills
                skill: utils.Skill
                for skill in user_cache.skills:

                    # Update the user's skill
                    await db(
                        """INSERT INTO user_skill VALUES ($1, $2, $3)
                        ON CONFLICT (user_id, name) DO UPDATE SET
                        experience = user_skill.experience""",
                        user_id,
                        skill.name,
                        skill.experience,
                    )

                    # Log our update
                    self.logger.info(
                        f"Updating user cache for {user_id} - {skill.name!r}... success"
                    )

                # Update the user's pp
                await db(
                    """INSERT INTO user_pp VALUES ($1, $2, $3, $4)
                    ON CONFLICT (user_id) DO UPDATE SET name = $2,
                    size = $3, multiplier = $4""",
                    user_id,
                    user_cache.pp.name,
                    user_cache.pp.size,
                    user_cache.pp.multiplier,
                )

                # Log our update
                self.logger.info(
                    f"Updating user cache for {user_id}'s pp: {user_cache!r}... success"
                )

                if user_id not in self.bot.commands_in_use:
                    self.bot.user_cache.pop(user_id)
Example #3
0
 async def _show_pp(self, ctx: commands.SlashContext) -> None:
     async with vbu.DatabaseConnection() as db:
         cache: utils.CachedUser = await utils.get_user_cache(
             ctx.author.id,
             db=db,
             bot=self.bot,
             logger=self.logger,
         )
         with vbu.Embed() as embed:
             embed.colour = utils.BLUE
             embed.set_author(
                 name=f"{ctx.author.display_name}'s PP",
                 icon_url=ctx.author.avatar.url,
             )
             embed.description = f"8{('='*(cache.pp.size // 50))[:1000]}D"
             embed.add_field(
                 "Stats",
                 f"Size - {cache.pp.size}\nMultiplier - {cache.pp.multiplier}",
             )
         await ctx.interaction.response.send_message(embed=embed)
Example #4
0
    async def update_settings(self, key, value):

        # There is no safe, dynamic way to do this. :RIP:
        # View config/database.pgsql for reference.
        valid_columns = ["current_begging_location"]
        if key not in valid_columns:
            raise KeyError(
                f"{key} is not a valid column. View config/database.pgsql for reference."
            )

        # Don't open a costly database connection if we don't need to
        try:
            if self.settings[key] == value:
                return
        except KeyError:
            pass

        self.settings[key] = value
        async with vbu.DatabaseConnection() as db:
            await db(
                f"UPDATE user_settings SET {key} = $1 WHERE user_id = $2",
                value,
                self.user_id,
            )
Example #5
0
    async def _inventory_command(self, ctx: commands.SlashContext) -> None:
        """
        View the items in your inventory!
        """

        async with vbu.DatabaseConnection() as db:
            async with utils.Inventory.fetch(self.bot, db,
                                             ctx.author.id) as inventory:

                def formatter(
                        menu: utils.Paginator,
                        items: typing.List[utils.LootableItem]) -> vbu.Embed:
                    with vbu.Embed() as embed:
                        output = []
                        for item in items:
                            output.append(
                                f"2x {item.emoji} **{item.name}** ─ {item.type.replace('_', ' ').title()}"
                                +
                                f"\n **{item.rarity.replace('_', ' ').title()}**"
                                + f"  ─ `{item.id}` {item.description}")
                        embed.set_author(
                            name=f"{ctx.author.display_name}'s inventory",
                            icon_url=ctx.author.avatar.url,
                        )
                        embed.description = (
                            f"use [/item-info [item]]({self.bot.hyperlink}) for more information.\n\n"
                            + "\n\n".join(output))
                        embed.set_footer(
                            f"Page {menu.current_page + 1}/{menu.max_pages}")
                    return embed

                sorters = utils.Sorters(
                    "ALPHABETICAL",
                    utils.Sorter(
                        "name (A ➞ Z)",
                        "Sort items alphabetically",
                        "ALPHABETICAL",
                        lambda i: sorted(i, key=lambda x: x.name),
                    ),
                    utils.Sorter(
                        "name (Z ➞ A)",
                        "Sort items reverse-alphabetically",
                        "REVERSE_ALPHABETICAL",
                        lambda i: sorted(i, key=lambda x: x.name, reverse=True
                                         ),
                    ),
                    utils.Sorter(
                        "rarity (GODLIKE ➞ COMMON)",
                        "Sort items based on their rarity from highest to lowest",
                        "RARITY",
                        lambda i: sorted(
                            i,
                            key=lambda x: {
                                "ADMIN-ABUSE": 0,
                                "GODLIKE": 1,
                                "LEGENDARY": 2,
                                "RARE": 3,
                                "UNCOMMON": 4,
                                "COMMON": 5,
                            }[x.rarity],
                        ),
                    ),
                    utils.Sorter(
                        "rarity (COMMON ➞ GODLIKE)",
                        "Sort items based on their rarity from lowest to highest",
                        "REVERSE_RARITY",
                        lambda i: sorted(
                            i,
                            key=lambda x: {
                                "COMMON": 0,
                                "UNCOMMON": 1,
                                "RARE": 2,
                                "LEGENDARY": 3,
                                "GODLIKE": 4,
                                "ADMIN-ABUSE": 5,
                            }[x.rarity],
                        ),
                    ),
                )

                filters = utils.Filters(
                    utils.Filter(
                        "Crafting reagents",
                        "CREATING_REAGENT",
                        filterer=lambda i: list(
                            filter(lambda x: x.type == "CRAFTING_REAGENT", i)),
                    ),
                    utils.Filter(
                        "Tools",
                        "TOOL",
                        filterer=lambda i: list(
                            filter(lambda x: x.type == "TOOL", i)),
                    ),
                    utils.Filter(
                        "Potions",
                        "POTION",
                        filterer=lambda i: list(
                            filter(lambda x: x.type == "POTION", i)),
                    ),
                )

                paginator = utils.Paginator(
                    inventory.items,
                    per_page=5,
                    formatter=formatter,
                    sorters=sorters,
                    filters=filters,
                )
                await paginator.start(ctx, timeout=10)
Example #6
0
    async def _blackjack_command(self, ctx: commands.SlashContext,
                                 amount: int):
        with utils.UsingCommand(ctx):
            async with vbu.DatabaseConnection() as db:
                cache: utils.CachedUser = await utils.get_user_cache(
                    ctx.author.id,
                    db=db,
                    bot=self.bot,
                    logger=self.logger,
                )

                if amount < 25:
                    return await ctx.interaction.response.send_message(
                        content=
                        f"No can do, you need to gamble atleast **25 inches**")

                if amount > cache.pp.size:
                    return await ctx.interaction.response.send_message(
                        content=
                        f"How you gamble that amount when you dont even have that many inches LMAO. You're missing {utils.format_rewards(inches=amount - cache.pp.size)}"
                    )

                game = utils.BlackjackGame(utils.Deck())
                cache.pp.size -= amount

                with vbu.Embed() as embed:
                    embed.colour = 0x2C82C9
                    kwargs = {"name": f"{ctx.author.name}'s game of Blackjack"}
                    if ctx.author.avatar:
                        kwargs["icon_url"] = ctx.author.avatar.url
                    embed.set_author(**kwargs)
                    embed.add_field(
                        name=f"{ctx.author.name} 🎮",
                        value=
                        f"Hand - {game.player}\nTotal - `{game.player.total_value()}`",
                    )
                    embed.add_field(
                        name="Pp bot <:ppevil:871396299830861884>",
                        value=f"Hand - {game.dealer.hidden()}\nTotal - `?`",
                    )

                components = discord.ui.MessageComponents(
                    discord.ui.ActionRow(
                        discord.ui.Button(
                            label="Hit",
                            custom_id="HIT",
                            style=discord.ui.ButtonStyle.success,
                        ),
                        discord.ui.Button(
                            label="Stand",
                            custom_id="STAND",
                            style=discord.ui.ButtonStyle.danger,
                        ),
                    ))

                if game.state == utils.BlackjackState.PLAYER_BLACKJACK:
                    reward = int(amount * 2.5)
                    cache.pp.size += reward
                    with embed:
                        embed.colour = utils.GREEN
                        embed.description = f"**BLACKJACK!**\n{ctx.author.name} walks away with {utils.format_rewards(inches=reward)} (50% bonus)"
                        embed.edit_field_by_index(
                            1,
                            name="Pp bot <:ppevil:871396299830861884>",
                            value=
                            f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                        )
                    return await ctx.interaction.response.send_message(
                        embed=embed,
                        components=components.disable_components())

                elif game.state == utils.BlackjackState.DEALER_BLACKJACK:
                    with embed:
                        embed.colour = utils.RED
                        embed.description = f"**DEALER BLACKJACK!**\n{ctx.author.name} loses {utils.format_rewards(inches=-amount)}"
                        embed.edit_field_by_index(
                            1,
                            name="Pp bot <:ppevil:871396299830861884>",
                            value=
                            f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                        )
                    return await ctx.interaction.response.send_message(
                        embed=embed,
                        components=components.disable_components())

                elif game.state == utils.BlackjackState.PUSH:
                    cache.pp.size += amount
                    with embed:
                        embed.colour = utils.YELLOW
                        embed.description = f"**PUSH!**\nSomehow you both got a blackjack LMAO, it's a tie"
                    return await ctx.interaction.response.send_message(
                        embed=embed,
                        components=components.disable_components())

                await ctx.interaction.response.send_message(
                    embed=embed, components=components)

                original_message: discord.InteractionMessage = (
                    await ctx.interaction.original_message())

                actions: typing.List[str] = []

                def formatted_actions() -> str:
                    if not actions:
                        return ""
                    reversed_actions = list(reversed(actions))
                    if len(actions) > 2:
                        return "```diff\n{}\n{} previous {}...```".format(
                            "\n".join(reversed_actions[:2]),
                            len(reversed_actions) - 2,
                            "actions" if len(reversed_actions) -
                            3 else "action",
                        )
                    return "```diff\n{}```".format("\n".join(reversed_actions))

                def action_check(
                        action_interaction: discord.Interaction) -> bool:
                    if (action_interaction.message is not None
                            and action_interaction.message.id !=
                            original_message.id
                            or action_interaction.user != ctx.author):
                        return False

                    if action_interaction.user != ctx.author:
                        self.bot.loop.create_task(
                            action_interaction.response.send_message(
                                content="Bro this is not meant for you LMAO",
                                ephemeral=True,
                            ))
                        return False

                    if (action_interaction.data is not None
                            and not utils.BlackjackAction.get(
                                typing.cast(
                                    str,
                                    action_interaction.data.get(
                                        "custom_id", "")))):
                        x = action_interaction.data["custom_id"]
                        self.bot.loop.create_task(
                            action_interaction.response.send_message(
                                content=
                                "Something went wrong lmao try using this command again with different button",
                                ephemeral=True,
                            ))
                        raise asyncio.TimeoutError()
                        return

                    return True

                while game.state == utils.BlackjackState.PLAYER_TURN:
                    try:
                        action_interaction: discord.Interaction = (
                            await self.bot.wait_for("component_interaction",
                                                    check=action_check,
                                                    timeout=15))

                    except asyncio.TimeoutError:
                        game.state = utils.BlackjackState.TIMEOUT
                        break

                    assert action_interaction.data is not None
                    action = getattr(utils.BlackjackAction,
                                     action_interaction.data["custom_id"])

                    game.player_action(action)

                    if action == utils.BlackjackAction.HIT:
                        actions.append(
                            f"+ {ctx.author.name} hits and received a {game.player.cards[-1]}."
                        )
                    elif action == utils.BlackjackAction.STAND:
                        actions.append(f"! {ctx.author.name} stands.")
                    else:
                        actions.append(
                            f"? {ctx.author.name} {action.name.lower()}s.")

                    with embed:
                        embed.edit_field_by_index(
                            0,
                            name=f"{ctx.author.name} 🎮",
                            value=
                            f"Hand - {game.player}\nTotal - `{game.player.total_value()}`",
                        )
                        embed.edit_field_by_index(
                            1,
                            name="Pp bot <:ppevil:871396299830861884>",
                            value=f"Hand - {game.dealer.hidden()}\nTotal - `?`",
                        )
                        embed.description = formatted_actions() or None

                    await action_interaction.response.edit_message(embed=embed)

                if game.state == utils.BlackjackState.TIMEOUT:
                    actions.append(f"- {ctx.author.name} doesn't respond.")
                    with embed:
                        embed.colour = utils.YELLOW
                        embed.description = f"**TIMED OUT!**\nWhile {ctx.author.name} was AFK, the dealer ran away with his {utils.format_rewards(inches=-amount)}"
                        embed.edit_field_by_index(
                            1,
                            name="Pp bot <:ppevil:871396299830861884>",
                            value=
                            f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                        )
                        if actions:
                            embed.description += formatted_actions()

                elif game.state == utils.BlackjackState.PLAYER_BUST:
                    actions.append(f"- {ctx.author.name} busts.")
                    with embed:
                        embed.colour = utils.RED
                        embed.description = f"**BUST!**\n{ctx.author.name} got a bit to greedy, and busted. You lose {utils.format_rewards(inches=-amount)}"
                        embed.edit_field_by_index(
                            1,
                            name="Pp bot <:ppevil:871396299830861884>",
                            value=
                            f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                        )
                        if actions:
                            embed.description += formatted_actions()

                else:
                    while game.state == utils.BlackjackState.DEALER_TURN:

                        with embed:
                            embed.edit_field_by_index(
                                1,
                                name="Pp bot <:ppevil:871396299830861884>",
                                value=
                                f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                            )
                            embed.description = formatted_actions() or None

                        await original_message.edit(
                            embed=embed,
                            components=components.disable_components())

                        await asyncio.sleep(1)
                        game.dealer_action()
                        actions.append(
                            f"+ pp bot hits and received a {game.dealer.cards[-1]}."
                        )

                    if game.state == utils.BlackjackState.DEALER_BUST:
                        actions.append(f"- pp bot busts.")
                        reward = amount * 2
                        cache.pp.size += reward
                        with embed:
                            embed.colour = utils.GREEN
                            embed.description = f"**DEALER BUST!**\npp bot got absolutely destroyed by {ctx.author.name}. You win {utils.format_rewards(inches=reward)}"
                            embed.edit_field_by_index(
                                1,
                                name="Pp bot <:ppevil:871396299830861884>",
                                value=
                                f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                            )
                            if actions:
                                embed.description += formatted_actions()
                        await original_message.edit(
                            embed=embed,
                            components=components.disable_components())

                    elif game.state == utils.BlackjackState.DEALER_WIN:
                        actions.append(f"+ pp bot wins.")
                        with embed:
                            embed.colour = utils.RED
                            embed.description = f"**DEALER WIN!**\n{ctx.author.name} got dunked on by pp bot. You lose {utils.format_rewards(inches=-amount)}"
                            embed.edit_field_by_index(
                                1,
                                name="Pp bot <:ppevil:871396299830861884>",
                                value=
                                f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                            )
                            if actions:
                                embed.description += formatted_actions()

                    elif game.state == utils.BlackjackState.PLAYER_WIN:
                        actions.append(f"+ {ctx.author.name} wins.")
                        reward = amount * 2
                        cache.pp.size += reward
                        with embed:
                            embed.colour = utils.GREEN
                            embed.description = f"**YOU WIN!**\n{ctx.author.name} has proved their extreme gambling skill against pp bot. You win {utils.format_rewards(inches=reward)}"
                            embed.edit_field_by_index(
                                1,
                                name="Pp bot <:ppevil:871396299830861884>",
                                value=
                                f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                            )
                            if actions:
                                embed.description += formatted_actions()

                    else:
                        actions.append(f"+ {ctx.author.name} and pp bot push.")
                        with embed:
                            cache.pp.size += amount
                            embed.colour = utils.YELLOW
                            embed.description = f"**PUSH!**\n{ctx.author.name} and pp bot ended up in a tie. You win {utils.format_rewards(inches=0)}"
                            embed.edit_field_by_index(
                                1,
                                name="Pp bot <:ppevil:871396299830861884>",
                                value=
                                f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`",
                            )
                            if actions:
                                embed.description += formatted_actions()

                await original_message.edit(
                    embed=embed, components=components.disable_components())
Example #7
0
    async def _beg_command(
        self, ctx: commands.SlashContext, location_name: typing.Optional[str] = None
    ) -> typing.Any:
        """
        Beg for some sweet sweet inches lmao
        """

        with utils.UsingCommand(ctx):
            async with vbu.DatabaseConnection() as db:

                cache: utils.CachedUser = await utils.get_user_cache(
                    ctx.author.id,
                    db=db,
                    bot=self.bot,
                    logger=self.logger,
                )
                begging_skill = cache.get_skill("BEGGING")

                if location_name is None:
                    location_name = cache.settings.get("current_begging_location")

                locations = utils.BeggingLocations(
                    begging_skill.level, *self.bot.begging["locations"]
                )

                # No location chosen in autocomplete and no default location set
                if location_name is None:

                    components = discord.ui.MessageComponents(
                        discord.ui.ActionRow(locations.to_select_menu())
                    )

                    with vbu.Embed() as embed:
                        embed.colour = utils.colours.BLUE
                        embed.set_author(
                            name=random.choice(
                                [
                                    "Where you begging at?",
                                    "Where are you begging?",
                                    "Where you begging?",
                                ]
                            )
                        )
                        embed.description = "\n".join(
                            (
                                "Btw, level the `Begging` skill to unlock new locations",
                                f"Current level: {utils.int_to_roman(begging_skill.level)}",
                            )
                        )

                    await ctx.interaction.response.send_message(
                        embed=embed, components=components
                    )

                    try:
                        original_message = await ctx.interaction.original_message()

                        def check(
                            interaction: discord.Interaction,
                        ) -> typing.Union[bool, None]:

                            # Check if the interaction is used the original context interaction message.
                            if (
                                interaction.message
                                and interaction.message.id != original_message.id
                            ):
                                return False

                            if interaction.user != ctx.author:
                                self.bot.loop.create_task(
                                    interaction.response.send_message(
                                        content="Bro this is not meant for you LMAO",
                                        ephemeral=True,
                                    )
                                )
                                return False

                            return True

                        # Wait for a response
                        payload: discord.Interaction = await self.bot.wait_for(
                            "component_interaction",
                            check=check,
                            timeout=15.0,
                        )

                    except asyncio.TimeoutError:
                        with embed:
                            embed.description = (
                                "**You took too long to respond, type faster bro**\n\n"
                                + embed.description
                            )
                        return await utils.responses.send_or_edit_response(
                            ctx.interaction,
                            embed=embed,
                            components=components.disable_components(),
                        )

                    # Get the selected location
                    location = locations.get_location_from_interaction(payload)

                    # Location not found?
                    if location is None:
                        return await utils.responses.send_or_edit_response(
                            ctx.interaction,
                            embed=discord.Embed(
                                title="Location not found",
                                description="Please try again",
                                colour=utils.colours.RED,
                            ),
                            components=components.disable_components(),
                        )

                else:
                    location = locations.get_location_from_id(location_name)
                    if location is None:
                        return await ctx.interaction.response.send_message(
                            "You have to pick one of the options lmao", ephemeral=True
                        )

                await cache.update_settings("current_begging_location", location.id)

                # 5% chance of fill in the blank minigame
                # if (random_percentage := random.random()) < 0.05:
                if random_percentage := random.random():
                    return await self.play_fill_in_the_blank(
                        ctx, cache=cache, location=location, db=db
                    )

                # 5% chance of scramble minigame
                elif random_percentage < 0.1:
                    with vbu.Embed() as embed:
                        embed.colour = utils.BLUE
                        embed.set_author(
                            name=f"{ctx.author.display_name}'s minigame",
                            icon_url=ctx.author.avatar.url,
                        )
                        embed.title = "Scramble!"
                        scramble = location.quotes.minigames.scramble

                        unscrambled = random.choice(
                            [
                                "bitch",
                                "peepee",
                                "balls",
                                "taxes",
                                "tax evasion",
                                "pp bot",
                                "multiplier",
                                "supercalifragilisticexpialidocious",
                                "amogus",
                                "testicles",
                                "karen",
                                "schlopp",
                                "i love balls",
                                "doin ur mom",
                                "try harder lmao",
                                "small c**k",
                            ]
                        )

                        scrambled = utils.scramble(unscrambled)

                        embed.description = f"{scramble.context}\n\n{scramble.approacher}: [`{scrambled}`]({self.bot.hyperlink})"
                        embed.set_footer("Respond to this message with the answer")

                    await utils.responses.send_or_edit_response(
                        ctx.interaction,
                        embed=embed,
                        components=None,
                    )

                    attempts_left = 3

                    def scramble_check(message: discord.Message) -> bool:

                        if (
                            message.author != ctx.author
                            or message.channel != ctx.channel
                        ):
                            return False

                        if message.content.lower() != unscrambled.lower():
                            nonlocal attempts_left
                            attempts_left -= 1
                            self.bot.loop.create_task(
                                message.reply(
                                    f"That's not the correct answer lol. You have **{attempts_left} attempts** left"
                                )
                            )
                            if not attempts_left:
                                raise asyncio.TimeoutError
                            return False

                        return True

                    while attempts_left:
                        try:
                            await self.bot.wait_for(
                                "message",
                                check=scramble_check,
                                timeout=15.0,
                            )
                            break
                        except asyncio.TimeoutError:
                            with embed:
                                embed.description = f"You should work on your scrambling skills, the answer was `{unscrambled}`. You get {utils.format_rewards()}"
                            return await utils.responses.send_or_edit_response(
                                ctx.interaction, embed=embed
                            )

                    with vbu.Embed() as embed:
                        embed.colour = utils.PINK

                        # Generate rewards and give them to the user
                        loot = location.loot_table.get_random_loot(
                            self.bot, boosted=True
                        )
                        async with utils.Inventory.fetch(
                            self.bot, db, ctx.author.id, update_values=True
                        ) as inv:
                            inv.add_items(*loot)

                        growth = int(random.randint(1000, 2000) * cache.pp.multiplier)
                        cache.pp.size += growth

                        embed.description = f"**GG!** You win {utils.format_rewards(inches=growth, items=loot)}!"

                    # Update the message
                    return await utils.responses.send_or_edit_response(
                        ctx.interaction,
                        embed=embed,
                        components=None,
                    )

                # 5% chance of retype event
                elif random_percentage < 0.15:
                    with vbu.Embed() as embed:
                        embed.colour = utils.BLUE
                        embed.set_author(
                            name=f"{ctx.author.display_name}'s minigame",
                            icon_url=ctx.author.avatar.url,
                        )
                        embed.title = "Retype!"
                        retype = location.quotes.minigames.retype

                        # Get a random sentence
                        sentence = random.choice(retype.sentences)
                        uncopyable_sentence = utils.uncopyable(sentence)

                        embed.description = f"{retype.context}\n\nQuickly! Retype this sentence is chat: [`{uncopyable_sentence}`]({self.bot.hyperlink})"
                        embed.set_footer("Respond to this message with the sentence")

                    await utils.responses.send_or_edit_response(
                        ctx.interaction,
                        embed=embed,
                        components=None,
                    )

                    attempts_left = 3

                    def retype_check(message: discord.Message) -> bool:

                        if (
                            message.author != ctx.author
                            or message.channel != ctx.channel
                        ):
                            return False

                        if message.content.lower() != sentence.lower():
                            nonlocal attempts_left
                            attempts_left -= 1

                            if message.content == uncopyable_sentence:
                                self.bot.loop.create_task(
                                    message.reply(
                                        f"Did you really think you could get away with copy-pasting? LMAO, you have **{attempts_left} attempts** left"
                                    )
                                )
                            else:
                                self.bot.loop.create_task(
                                    message.reply(
                                        f"That's not the correct answer lol. You have **{attempts_left} attempts** left"
                                    )
                                )
                            if not attempts_left:
                                raise asyncio.TimeoutError
                            return False

                        return True

                    while attempts_left:
                        try:
                            await self.bot.wait_for(
                                "message",
                                check=retype_check,
                                timeout=30.0,
                            )
                            break
                        except asyncio.TimeoutError:
                            embed.description = f"Wow, you're a slow typer. You get {utils.format_rewards()}. Cry about it"
                            return await utils.responses.send_or_edit_response(
                                ctx.interaction, embed=embed
                            )

                    with vbu.Embed() as embed:
                        embed.colour = utils.PINK

                        # Generate rewards and give them to the user
                        loot = location.loot_table.get_random_loot(
                            self.bot, boosted=True
                        )
                        async with utils.Inventory.fetch(
                            self.bot, db, ctx.author.id, update_values=True
                        ) as inv:
                            inv.add_items(*loot)

                        growth = int(random.randint(1000, 2000) * cache.pp.multiplier)
                        cache.pp.size += growth

                        embed.description = f"**GG!** Nice typing skills bro, you win {utils.format_rewards(inches=growth, items=loot)}!"

                    # Update the message
                    return await utils.responses.send_or_edit_response(
                        ctx.interaction,
                        embed=embed,
                        components=None,
                    )