From 685e4d953e65a84329e4124a6db8b8cc31518117 Mon Sep 17 00:00:00 2001 From: stijndcl Date: Mon, 12 Feb 2024 20:07:57 +0100 Subject: [PATCH] Re-enable currency, add withdraw and cf --- database/crud/currency.py | 37 ++++++++++- didier/cogs/currency.py | 46 ++++++++++---- didier/cogs/gambling.py | 73 ++++++++++++++++++++++ didier/cogs/owner.py | 45 ++++++++++++- didier/didier.py | 4 -- didier/utils/discord/converters/numbers.py | 2 +- 6 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 didier/cogs/gambling.py diff --git a/database/crud/currency.py b/database/crud/currency.py index da0ff84..63bb959 100644 --- a/database/crud/currency.py +++ b/database/crud/currency.py @@ -15,12 +15,14 @@ from database.utils.math.currency import ( __all__ = [ "add_dinks", "claim_nightly", + "gamble_dinks", "get_bank", "get_nightly_data", "invest", "upgrade_capacity", "upgrade_interest", "upgrade_rob", + "withdraw", "NIGHTLY_AMOUNT", ] @@ -40,7 +42,7 @@ async def get_nightly_data(session: AsyncSession, user_id: int) -> NightlyData: async def invest(session: AsyncSession, user_id: int, amount: Union[str, int]) -> int: - """Invest all your Dinks""" + """Invest some of your Dinks""" bank = await get_bank(session, user_id) if amount == "all": amount = bank.dinks @@ -57,6 +59,22 @@ async def invest(session: AsyncSession, user_id: int, amount: Union[str, int]) - return amount +async def withdraw(session: AsyncSession, user_id: int, amount: Union[str, int]) -> int: + """Withdraw your invested Dinks""" + bank = await get_bank(session, user_id) + if amount == "all": + amount = bank.invested + + # Don't allow withdrawing more dinks than you own + amount = min(bank.invested, int(amount)) + + bank.dinks += amount + bank.invested -= amount + + await session.commit() + return amount + + async def add_dinks(session: AsyncSession, user_id: int, amount: int): """Increase the Dinks counter for a user""" bank = await get_bank(session, user_id) @@ -135,3 +153,20 @@ async def upgrade_rob(session: AsyncSession, user_id: int) -> int: await session.commit() return bank.rob_level + + +async def gamble_dinks( + session: AsyncSession, user_id: int, amount: Union[str, int], payout_factor: int, won: bool +) -> int: + """Gamble some of your Dinks""" + bank = await get_bank(session, user_id) + if amount == "all": + amount = bank.dinks + + amount = min(amount, bank.dinks) + + sign = 1 if won else -1 + factor = (payout_factor - 1) if won else 1 + await add_dinks(session, user_id, sign * amount * factor) + + return amount * factor diff --git a/didier/cogs/currency.py b/didier/cogs/currency.py index 4049654..93856f1 100644 --- a/didier/cogs/currency.py +++ b/didier/cogs/currency.py @@ -1,3 +1,4 @@ +# flake8: noqa: E800 import typing import discord @@ -38,7 +39,7 @@ class Currency(commands.Cog): await crud.add_dinks(session, user.id, amount) plural = pluralize("Didier Dink", amount) await ctx.reply( - f"**{ctx.author.display_name}** has awarded **{user.display_name}** **{amount}** {plural}.", + f"**{ctx.author.display_name}** has awarded **{user.display_name}** with **{amount}** {plural}.", mention_author=False, ) @@ -48,7 +49,8 @@ class Currency(commands.Cog): async with self.client.postgres_session as session: bank = await crud.get_bank(session, ctx.author.id) - embed = discord.Embed(title=f"{ctx.author.display_name}'s Bank", colour=discord.Colour.blue()) + embed = discord.Embed(title="Bank of Didier", colour=discord.Colour.blue()) + embed.set_author(name=ctx.author.display_name) if ctx.author.avatar is not None: embed.set_thumbnail(url=ctx.author.avatar.url) @@ -59,9 +61,9 @@ class Currency(commands.Cog): await ctx.reply(embed=embed, mention_author=False) - @bank.group( # type: ignore[arg-type] - name="upgrade", aliases=["u", "upgrades"], case_insensitive=True, invoke_without_command=True - ) + # @bank.group( # type: ignore[arg-type] + # name="upgrade", aliases=["u", "upgrades"], case_insensitive=True, invoke_without_command=True + # ) async def bank_upgrades(self, ctx: commands.Context): """List the upgrades you can buy & their prices.""" async with self.client.postgres_session as session: @@ -81,7 +83,7 @@ class Currency(commands.Cog): await ctx.reply(embed=embed, mention_author=False) - @bank_upgrades.command(name="capacity", aliases=["c"]) # type: ignore[arg-type] + # @bank_upgrades.command(name="capacity", aliases=["c"]) # type: ignore[arg-type] async def bank_upgrade_capacity(self, ctx: commands.Context): """Upgrade the capacity level of your bank.""" async with self.client.postgres_session as session: @@ -92,7 +94,7 @@ class Currency(commands.Cog): await ctx.reply("You don't have enough Didier Dinks to do this.", mention_author=False) await self.client.reject_message(ctx.message) - @bank_upgrades.command(name="interest", aliases=["i"]) # type: ignore[arg-type] + # @bank_upgrades.command(name="interest", aliases=["i"]) # type: ignore[arg-type] async def bank_upgrade_interest(self, ctx: commands.Context): """Upgrade the interest level of your bank.""" async with self.client.postgres_session as session: @@ -103,7 +105,7 @@ class Currency(commands.Cog): await ctx.reply("You don't have enough Didier Dinks to do this.", mention_author=False) await self.client.reject_message(ctx.message) - @bank_upgrades.command(name="rob", aliases=["r"]) # type: ignore[arg-type] + # @bank_upgrades.command(name="rob", aliases=["r"]) # type: ignore[arg-type] async def bank_upgrade_rob(self, ctx: commands.Context): """Upgrade the rob level of your bank.""" async with self.client.postgres_session as session: @@ -122,12 +124,12 @@ class Currency(commands.Cog): plural = pluralize("Didier Dink", bank.dinks) await ctx.reply(f"**{ctx.author.display_name}** has **{bank.dinks}** {plural}.", mention_author=False) - @commands.command(name="invest", aliases=["deposit", "dep"]) # type: ignore[arg-type] + @commands.command(name="invest", aliases=["deposit", "dep", "i"]) # type: ignore[arg-type] async def invest(self, ctx: commands.Context, amount: typing.Annotated[typing.Union[str, int], abbreviated_number]): """Invest `amount` Didier Dinks into your bank. The `amount`-argument can take both raw numbers, and abbreviations of big numbers. Additionally, passing - `all` as the value will invest all of your Didier Dinks. + `all` or `*` as the value will invest all of your Didier Dinks. Example usage: ``` @@ -137,6 +139,9 @@ class Currency(commands.Cog): didier invest 5.3b ``` """ + if isinstance(amount, int) and amount <= 0: + return await ctx.reply("Amount of Didier Dinks to invest must be a strictly positive integer.") + async with self.client.postgres_session as session: invested = await crud.invest(session, ctx.author.id, amount) plural = pluralize("Didier Dink", invested) @@ -144,9 +149,24 @@ class Currency(commands.Cog): if invested == 0: await ctx.reply("You don't have any Didier Dinks to invest.", mention_author=False) else: - await ctx.reply( - f"**{ctx.author.display_name}** has invested **{invested}** {plural}.", mention_author=False - ) + await ctx.reply(f"You have invested **{invested}** {plural}.", mention_author=False) + + @commands.command(name="withdraw", aliases=["uninvest", "w"]) # type: ignore[arg-type] + async def withdraw( + self, ctx: commands.Context, amount: typing.Annotated[typing.Union[str, int], abbreviated_number] + ): + """Withdraw some of your invested Didier Dinks from your bank.""" + if isinstance(amount, int) and amount <= 0: + return await ctx.reply("Amount of Didier Dinks to invest must be a strictly positive integer.") + + async with self.client.postgres_session as session: + withdrawn = await crud.withdraw(session, ctx.author.id, amount) + plural = pluralize("Didier Dink", withdrawn) + + if withdrawn == 0: + await ctx.reply("You don't have any Didier Dinks to withdraw.", mention_author=False) + else: + await ctx.reply(f"You have withdrawn **{withdrawn}** {plural}.", mention_author=False) @commands.hybrid_command(name="nightly") # type: ignore[arg-type] async def nightly(self, ctx: commands.Context): diff --git a/didier/cogs/gambling.py b/didier/cogs/gambling.py new file mode 100644 index 0000000..03a431c --- /dev/null +++ b/didier/cogs/gambling.py @@ -0,0 +1,73 @@ +import random +from typing import Annotated, Optional, Union + +from discord.ext import commands + +from database.crud.currency import gamble_dinks +from didier import Didier +from didier.utils.discord.converters import abbreviated_number +from didier.utils.types.string import pluralize + + +class Gambling(commands.Cog): + """Cog for various games""" + + client: Didier + + def __init__(self, client: Didier): + self.client = client + + @commands.command(name="coinflip", aliases=["cf", "flip"]) # type: ignore[arg-type] + async def coinflip( + self, + ctx: commands.Context, + amount: Optional[Annotated[Union[str, int], abbreviated_number]] = None, + guess: Optional[str] = None, + ): + """Toss a coin, optionally wagering some Didier Dinks. + + Passing an argument for `amount` but not `guess` will cause the guess to be randomized. + """ + result: str = random.choice(["heads", "tails"]) + + # No stakes + if guess is None: + if amount is None: + return await ctx.reply(f"{result.capitalize()}!", mention_author=False) + else: + guess = random.choice(["heads", "tails"]) + + guess = guess.lower() + + if guess not in ( + "h", + "heads", + "t", + "tails", + ): + return await ctx.reply('Guess must be one of "h", "heads", "t" or "tails".', mention_author=False) + + if isinstance(amount, int) and amount <= 0: + return await ctx.reply( + "Amount of Didier Dinks to wager must be a strictly positive integer.", mention_author=False + ) + + won = guess[0] == result[0] + + async with self.client.postgres_session as session: + received = await gamble_dinks(session, ctx.author.id, amount, 2, won) + + if received == 0: + return await ctx.reply("You don't have any Didier Dinks to wager.", mention_author=False) + + plural = pluralize("Didier Dink", received) + + if won: + await ctx.reply(f"{result.capitalize()}! You won **{received}** {plural}!", mention_author=False) + else: + await ctx.reply(f"{result.capitalize()}! You lost **{received}** {plural}!", mention_author=False) + + +async def setup(client: Didier): + """Load the cog""" + await client.add_cog(Gambling(client)) diff --git a/didier/cogs/owner.py b/didier/cogs/owner.py index 1f72eff..23a9a3c 100644 --- a/didier/cogs/owner.py +++ b/didier/cogs/owner.py @@ -54,14 +54,55 @@ class Owner(commands.Cog): """Raise an exception for debugging purposes""" raise Exception(message) + @commands.command(name="unload") + async def unload(self, ctx: commands.Context, *cogs: str): + """Unload the cogs passed as an argument""" + unloaded = [] + skipped = [] + + for cog in cogs: + try: + await self.client.unload_extension(f"didier.cogs.{cog}") + unloaded.append(cog) + except commands.ExtensionNotLoaded: + skipped.append(cog) + + await self.client.confirm_message(ctx.message) + loaded_message = ", ".join(unloaded) if unloaded else "none" + skipped_message = ", ".join(skipped) if skipped else "none" + + await ctx.reply(f"Successfully unloaded {loaded_message} (skipped {skipped_message}).", mention_author=False) + + @commands.command(name="load") + async def load(self, ctx: commands.Context, *cogs: str): + """Load the cogs passed as an argument""" + loaded = [] + skipped = [] + + for cog in cogs: + try: + await self.client.load_extension(f"didier.cogs.{cog}") + loaded.append(cog) + except commands.ExtensionAlreadyLoaded: + skipped.append(cog) + + await self.client.confirm_message(ctx.message) + loaded_message = ", ".join(loaded) if loaded else "none" + skipped_message = ", ".join(skipped) if skipped else "none" + + await ctx.reply(f"Successfully loaded {loaded_message} (skipped {skipped_message}).", mention_author=False) + @commands.command(name="Reload") async def reload(self, ctx: commands.Context, *cogs: str): """Reload the cogs passed as an argument""" for cog in cogs: - await self.client.reload_extension(f"didier.cogs.{cog}") + try: + await self.client.reload_extension(f"didier.cogs.{cog}") + except commands.ExtensionNotLoaded: + await self.client.load_extension(f"didier.cogs.{cog}") await self.client.confirm_message(ctx.message) - return await ctx.reply(f"Successfully reloaded {', '.join(cogs)}.", mention_author=False) + await ctx.reply(f"Successfully reloaded {', '.join(cogs)}.", mention_author=False) @commands.command(name="Sync") async def sync( diff --git a/didier/didier.py b/didier/didier.py index 33e6e4b..839fe5d 100644 --- a/didier/didier.py +++ b/didier/didier.py @@ -121,10 +121,6 @@ class Didier(commands.Bot): # Allow checking against initial extensions more easily full_name = parent_path + file - # The currency cog is disabled - if "currency" in file: - continue - # Only take Python files, and ignore the ones starting with an underscore (like __init__ and __pycache__) # Also ignore the files that we have already loaded previously if file.endswith(".py") and not file.startswith("_") and not full_name.startswith(self.initial_extensions): diff --git a/didier/utils/discord/converters/numbers.py b/didier/utils/discord/converters/numbers.py index 61d991a..74fd4b5 100644 --- a/didier/utils/discord/converters/numbers.py +++ b/didier/utils/discord/converters/numbers.py @@ -14,7 +14,7 @@ def abbreviated_number(argument: str) -> Union[str, int]: if not argument: raise ValueError - if argument.lower() == "all": + if argument.lower() == "all" or argument == "*": return "all" if argument.isdecimal():