diff --git a/ballsdex/__init__.py b/ballsdex/__init__.py index def8f78cd..000408e08 100755 --- a/ballsdex/__init__.py +++ b/ballsdex/__init__.py @@ -1 +1 @@ -__version__ = "2.23.1" +__version__ = "2.24.0" diff --git a/ballsdex/core/image_generator/image_gen.py b/ballsdex/core/image_generator/image_gen.py index 514ed0624..e68ebdfd8 100755 --- a/ballsdex/core/image_generator/image_gen.py +++ b/ballsdex/core/image_generator/image_gen.py @@ -25,13 +25,22 @@ stats_font = ImageFont.truetype(str(SOURCES_PATH / "Bobby Jones Soft.otf"), 130) credits_font = ImageFont.truetype(str(SOURCES_PATH / "arial.ttf"), 40) +credits_color_cache = {} + + +def get_credit_color(image: Image.Image, region: tuple) -> tuple: + image = image.crop(region) + brightness = sum(image.convert("L").getdata()) / image.width / image.height + return (0, 0, 0, 255) if brightness > 100 else (255, 255, 255, 255) + def draw_card(ball_instance: "BallInstance", media_path: str = "./admin_panel/media/"): ball = ball_instance.countryball ball_health = (237, 115, 101, 255) ball_credits = ball.credits - + card_name = ball.cached_regime.name if special_image := ball_instance.special_card: + card_name = getattr(ball_instance.specialcard, card_name) image = Image.open(media_path + special_image) if ball_instance.specialcard and ball_instance.specialcard.credits: ball_credits += f" • {ball_instance.specialcard.credits}" @@ -86,13 +95,20 @@ def draw_card(ball_instance: "BallInstance", media_path: str = "./admin_panel/me stroke_fill=(0, 0, 0, 255), anchor="ra", ) + if card_name in credits_color_cache: + credits_color = credits_color_cache[card_name] + else: + credits_color = get_credit_color( + image, (0, int(image.height * 0.8), image.width, image.height) + ) + credits_color_cache[card_name] = credits_color draw.text( (30, 1870), # Modifying the line below is breaking the licence as you are removing credits # If you don't want to receive a DMCA, just don't "Created by El Laggron\n" f"Artwork author: {ball_credits}", font=credits_font, - fill=(0, 0, 0, 255), + fill=credits_color, stroke_width=0, stroke_fill=(255, 255, 255, 255), ) diff --git a/ballsdex/core/utils/transformers.py b/ballsdex/core/utils/transformers.py index d0a18bd9d..90c12b2e1 100644 --- a/ballsdex/core/utils/transformers.py +++ b/ballsdex/core/utils/transformers.py @@ -178,17 +178,23 @@ async def get_options( balls_queryset = balls_queryset.filter( locked__isnull=False, locked__gt=tortoise_now() - timedelta(minutes=30) ) - balls_queryset = ( - balls_queryset.select_related("ball") - .annotate( - searchable=RawSQL( - "to_hex(ballinstance.id) || ' ' || ballinstance__ball.country || " - "' ' || ballinstance__ball.catch_names" + + if value.startswith("="): + ball_name = value[1:] + balls_queryset = balls_queryset.filter(ball__country__iexact=ball_name) + else: + balls_queryset = ( + balls_queryset.select_related("ball") + .annotate( + searchable=RawSQL( + "to_hex(ballinstance.id) || ' ' || ballinstance__ball.country || ' ' || " + "COALESCE(ballinstance__ball.catch_names, '') || ' ' || " + "COALESCE(ballinstance__ball.translations, '')" + ) ) + .filter(searchable__icontains=value.replace(".", "")) ) - .filter(searchable__icontains=value.replace(".", "")) - .limit(25) - ) + balls_queryset = balls_queryset.limit(25) choices: list[app_commands.Choice] = [ app_commands.Choice(name=x.description(bot=interaction.client), value=str(x.pk)) diff --git a/ballsdex/packages/admin/history.py b/ballsdex/packages/admin/history.py index c4e58de8d..db824e24a 100644 --- a/ballsdex/packages/admin/history.py +++ b/ballsdex/packages/admin/history.py @@ -8,6 +8,7 @@ from ballsdex.core.bot import BallsDexBot from ballsdex.core.models import BallInstance, Player, Trade from ballsdex.core.utils.paginator import Pages +from ballsdex.core.utils.transformers import BallEnabledTransform from ballsdex.packages.trade.display import TradeViewFormat, fill_trade_embed_fields from ballsdex.packages.trade.trade_user import TradingUser from ballsdex.settings import settings @@ -30,7 +31,8 @@ async def history_user( self, interaction: discord.Interaction["BallsDexBot"], user: discord.User, - sorting: app_commands.Choice[str], + sorting: app_commands.Choice[str] | None = None, + countryball: BallEnabledTransform | None = None, user2: discord.User | None = None, days: int | None = None, ): @@ -41,21 +43,25 @@ async def history_user( ---------- user: discord.User The user you want to check the history of. - sorting: str + sorting: str | None The sorting method you want to use. + countryball: BallEnabledTransform | None + The countryball you want to filter the history by. user2: discord.User | None The second user you want to check the history of. days: Optional[int] Retrieve trade history from last x days. """ await interaction.response.defer(ephemeral=True, thinking=True) + sort_value = sorting.value if sorting else "-date" + if days is not None and days < 0: await interaction.followup.send( "Invalid number of days. Please provide a non-negative value.", ephemeral=True ) return - queryset = Trade.all() + queryset = Trade.filter() try: player1 = await Player.get(discord_id=user.id) if user2: @@ -72,12 +78,15 @@ async def history_user( await interaction.followup.send("One or more players are not registered by the bot.") return + if countryball: + queryset = queryset.filter(Q(tradeobjects__ballinstance__ball=countryball)).distinct() + if days is not None and days > 0: end_date = datetime.datetime.now() start_date = end_date - datetime.timedelta(days=days) queryset = queryset.filter(date__range=(start_date, end_date)) - queryset = queryset.order_by(sorting.value).prefetch_related("player1", "player2") + queryset = queryset.order_by(sort_value).prefetch_related("player1", "player2") history = await queryset if not history: diff --git a/ballsdex/packages/balls/cog.py b/ballsdex/packages/balls/cog.py index 5e917fc84..d43424028 100644 --- a/ballsdex/packages/balls/cog.py +++ b/ballsdex/packages/balls/cog.py @@ -544,7 +544,8 @@ async def give( ephemeral=True, ) return - if countryball.favorite: + favorite = countryball.favorite + if favorite: view = ConfirmChoiceView( interaction, accept_message=f"{settings.collectible_name.title()} donated.", @@ -624,10 +625,22 @@ async def give( countryball.description(short=True, include_emoji=True, bot=self.bot, is_trade=True) + f" (`{countryball.attack_bonus:+}%/{countryball.health_bonus:+}%`)" ) - await interaction.followup.send( - f"You just gave the {settings.collectible_name} {cb_txt} to {user.mention}!", - allowed_mentions=discord.AllowedMentions(users=new_player.can_be_mentioned), - ) + mentions = [] + if new_player.can_be_mentioned: + mentions.append(new_player.discord_id) + if old_player.can_be_mentioned: + mentions.append(old_player.discord_id) + if favorite: + await interaction.followup.send( + f"{interaction.user.mention}, you just gave the " + f"{settings.collectible_name} {cb_txt} to {user.mention}!", + allowed_mentions=discord.AllowedMentions(users=mentions), + ) + else: + await interaction.followup.send( + f"You just gave the {settings.collectible_name} {cb_txt} to {user.mention}!", + allowed_mentions=discord.AllowedMentions(users=mentions), + ) await countryball.unlock() @app_commands.command() diff --git a/ballsdex/packages/trade/menu.py b/ballsdex/packages/trade/menu.py index deeb09a90..3e56f02cf 100644 --- a/ballsdex/packages/trade/menu.py +++ b/ballsdex/packages/trade/menu.py @@ -70,17 +70,32 @@ async def lock(self, interaction: discord.Interaction, button: Button): @button(label="Reset", emoji="\N{DASH SYMBOL}", style=discord.ButtonStyle.secondary) async def clear(self, interaction: discord.Interaction, button: Button): trader = self.trade._get_trader(interaction.user) + await interaction.response.defer(thinking=True, ephemeral=True) + if trader.locked: - await interaction.response.send_message( + await interaction.followup.send( "You have locked your proposal, it cannot be edited! " "You can click the cancel button to stop the trade instead.", ephemeral=True, ) - else: - for countryball in trader.proposal: - await countryball.unlock() - trader.proposal.clear() - await interaction.response.send_message("Proposal cleared.", ephemeral=True) + + view = ConfirmChoiceView( + interaction, + accept_message="Clearing your proposal...", + cancel_message="This request has been cancelled.", + ) + await interaction.followup.send( + "Are you sure you want to clear your proposal?", view=view, ephemeral=True + ) + await view.wait() + if not view.value: + return + + for countryball in trader.proposal: + await countryball.unlock() + + trader.proposal.clear() + await interaction.followup.send("Proposal cleared.", ephemeral=True) @button( label="Cancel trade", @@ -88,8 +103,22 @@ async def clear(self, interaction: discord.Interaction, button: Button): style=discord.ButtonStyle.danger, ) async def cancel(self, interaction: discord.Interaction, button: Button): + await interaction.response.defer(thinking=True, ephemeral=True) + + view = ConfirmChoiceView( + interaction, + accept_message="Cancelling the trade...", + cancel_message="This request has been cancelled.", + ) + await interaction.followup.send( + "Are you sure you want to cancel this trade?", view=view, ephemeral=True + ) + await view.wait() + if not view.value: + return + await self.trade.user_cancel(self.trade._get_trader(interaction.user)) - await interaction.response.send_message("Trade has been cancelled.", ephemeral=True) + await interaction.followup.send("Trade has been cancelled.", ephemeral=True) class ConfirmView(View): @@ -151,8 +180,22 @@ async def accept_button(self, interaction: discord.Interaction, button: Button): emoji="\N{HEAVY MULTIPLICATION X}\N{VARIATION SELECTOR-16}", ) async def deny_button(self, interaction: discord.Interaction, button: Button): + await interaction.response.defer(thinking=True, ephemeral=True) + + view = ConfirmChoiceView( + interaction, + accept_message="Cancelling the trade...", + cancel_message="This request has been cancelled.", + ) + await interaction.followup.send( + "Are you sure you want to cancel this trade?", view=view, ephemeral=True + ) + await view.wait() + if not view.value: + return + await self.trade.user_cancel(self.trade._get_trader(interaction.user)) - await interaction.response.send_message("Trade has been cancelled.", ephemeral=True) + await interaction.followup.send("Trade has been cancelled.", ephemeral=True) class TradeMenu: