From 17b6ebac898aee0d6fbe1deed2a07deb5e36ada0 Mon Sep 17 00:00:00 2001 From: Stijn De Clercq Date: Sun, 24 Sep 2023 22:05:34 +0200 Subject: [PATCH 1/5] Add image processing --- database/scripts/db06_image_processing.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 database/scripts/db06_image_processing.py diff --git a/database/scripts/db06_image_processing.py b/database/scripts/db06_image_processing.py new file mode 100644 index 0000000..3db1a9a --- /dev/null +++ b/database/scripts/db06_image_processing.py @@ -0,0 +1,22 @@ +from sqlalchemy.ext.asyncio import AsyncSession + +from database.engine import DBSession +from database.schemas import UforaCourse, UforaCourseAlias + +__all__ = ["main"] + + +async def main(): + """Add the Image Processing course""" + session: AsyncSession + async with DBSession() as session: + image_proc = UforaCourse( + code="E010310", name="Image Processing", year=6, compulsory=False, role_id=1155595071228498134 + ) + + session.add(image_proc) + await session.commit() + + image_proc_alias = UforaCourseAlias(course_id=image_proc.course_id, alias="Beeldverwerking") + session.add(image_proc_alias) + await session.commit() From dc4ee08b63db7b84042fc0c45865cf1487c7416e Mon Sep 17 00:00:00 2001 From: stijndcl Date: Mon, 25 Dec 2023 23:00:48 +0100 Subject: [PATCH 2/5] Fix free games --- .tool-versions | 1 + didier/data/embeds/free_games.py | 12 +++++------- didier/data/rss_feeds/free_games.py | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..bb44e0d --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +python 3.9.5 diff --git a/didier/data/embeds/free_games.py b/didier/data/embeds/free_games.py index f159157..b0667da 100644 --- a/didier/data/embeds/free_games.py +++ b/didier/data/embeds/free_games.py @@ -15,7 +15,7 @@ from didier.utils.types.string import abbreviate __all__ = ["SEPARATOR", "FreeGameEmbed"] -SEPARATOR = " • Free • " +SEPARATOR = " is free to claim at " def _get_store_info(store: str) -> tuple[Optional[str], discord.Colour]: @@ -48,7 +48,7 @@ def _get_store_info(store: str) -> tuple[Optional[str], discord.Colour]: class FreeGameEmbed(EmbedPydantic): """Embed for free games""" - dc_identifier: int + id: int link: str title: str @@ -103,15 +103,13 @@ class FreeGameEmbed(EmbedPydantic): inline=False, ) - embed.add_field(name="Open in browser", value=f"[{self.link}]({self.link})") + embed.add_field(name="Open in browser", value=self.link) if self.store_page.xdg_open_url is not None: - embed.add_field( - name="Open in app", value=f"[{self.store_page.xdg_open_url}]({self.store_page.xdg_open_url})" - ) + embed.add_field(name="Open in app", value=self.store_page.xdg_open_url) else: embed.title = self.name - embed.add_field(name="Open in browser", value=f"[{self.link}]({self.link})") + embed.add_field(name="Open in browser", value=self.link) embed.url = self.link diff --git a/didier/data/rss_feeds/free_games.py b/didier/data/rss_feeds/free_games.py index 24a1ecf..44b1c9e 100644 --- a/didier/data/rss_feeds/free_games.py +++ b/didier/data/rss_feeds/free_games.py @@ -16,7 +16,7 @@ __all__ = ["fetch_free_games"] async def fetch_free_games(http_session: ClientSession, database_session: AsyncSession) -> list[FreeGameEmbed]: """Get a fresh list of free games""" - url = "https://pepeizqdeals.com/?call_custom_simple_rss=1&csrp_cat=12" + url = "https://pepeizqdeals.com/rss-en.xml" async with http_session.get(url) as response: if response.status != HTTPStatus.OK: logger.error("Free games GET-request failed with status code %d." % response.status) @@ -34,7 +34,7 @@ async def fetch_free_games(http_session: ClientSession, database_session: AsyncS game = FreeGameEmbed.model_validate(entry) games.append(game) - game_ids.append(game.dc_identifier) + game_ids.append(game.id) # Filter out games that we already know filtered_ids = await filter_present_games(database_session, game_ids) @@ -42,7 +42,7 @@ async def fetch_free_games(http_session: ClientSession, database_session: AsyncS # Insert new games into the database await add_free_games(database_session, filtered_ids) - games = list(filter(lambda x: x.dc_identifier in filtered_ids, games)) + games = list(filter(lambda x: x.id in filtered_ids, games)) # Look up additional info for game in games: From 685e4d953e65a84329e4124a6db8b8cc31518117 Mon Sep 17 00:00:00 2001 From: stijndcl Date: Mon, 12 Feb 2024 20:07:57 +0100 Subject: [PATCH 3/5] 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(): From cc75dd8909be974baa25fcc2cf20c1db75884978 Mon Sep 17 00:00:00 2001 From: stijndcl Date: Mon, 12 Feb 2024 20:16:52 +0100 Subject: [PATCH 4/5] Fix typing --- database/crud/currency.py | 2 +- didier/cogs/gambling.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/database/crud/currency.py b/database/crud/currency.py index 63bb959..9bdc681 100644 --- a/database/crud/currency.py +++ b/database/crud/currency.py @@ -163,7 +163,7 @@ async def gamble_dinks( if amount == "all": amount = bank.dinks - amount = min(amount, bank.dinks) + amount = min(int(amount), bank.dinks) sign = 1 if won else -1 factor = (payout_factor - 1) if won else 1 diff --git a/didier/cogs/gambling.py b/didier/cogs/gambling.py index 03a431c..f8ba028 100644 --- a/didier/cogs/gambling.py +++ b/didier/cogs/gambling.py @@ -31,11 +31,11 @@ class Gambling(commands.Cog): result: str = random.choice(["heads", "tails"]) # No stakes + if amount is None: + return await ctx.reply(f"{result.capitalize()}!", mention_author=False) + 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 = random.choice(["heads", "tails"]) guess = guess.lower() From 165be35f8e6c420fdfd9cb5ff76bef48b290f6bf Mon Sep 17 00:00:00 2001 From: stijndcl Date: Mon, 12 Feb 2024 22:55:35 +0100 Subject: [PATCH 5/5] Fix name formatting --- didier/cogs/currency.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/didier/cogs/currency.py b/didier/cogs/currency.py index 93856f1..5a5d855 100644 --- a/didier/cogs/currency.py +++ b/didier/cogs/currency.py @@ -39,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}** with **{amount}** {plural}.", + f"{ctx.author.display_name} has awarded **{user.display_name}** with **{amount}** {plural}.", mention_author=False, ) @@ -122,7 +122,7 @@ class Currency(commands.Cog): async with self.client.postgres_session as session: bank = await crud.get_bank(session, ctx.author.id) plural = pluralize("Didier Dink", bank.dinks) - await ctx.reply(f"**{ctx.author.display_name}** has **{bank.dinks}** {plural}.", mention_author=False) + await ctx.reply(f"You have **{bank.dinks}** {plural}.", mention_author=False) @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]): @@ -174,7 +174,9 @@ class Currency(commands.Cog): async with self.client.postgres_session as session: try: await crud.claim_nightly(session, ctx.author.id) - await ctx.reply(f"You've claimed your daily **{crud.NIGHTLY_AMOUNT}** Didier Dinks.") + await ctx.reply( + f"You've claimed your daily **{crud.NIGHTLY_AMOUNT}** Didier Dinks.", mention_author=False + ) except DoubleNightly: await ctx.reply( "You've already claimed your Didier Nightly today.", mention_author=False, ephemeral=True