diff --git a/shop/shop.py b/shop/shop.py index 589556d..5028bad 100644 --- a/shop/shop.py +++ b/shop/shop.py @@ -108,7 +108,6 @@ class Shop(commands.Cog): # -----------------------COMMANDS------------------------------------- @commands.command() - @commands.max_concurrency(1, commands.BucketType.user) async def inventory(self, ctx): """Displays your purchased items.""" try: @@ -118,20 +117,26 @@ class Shop(commands.Cog): inventory = await instance.Inventory.all() if not inventory: - return await ctx.send("Your inventory is empty.") + embed = discord.Embed( + title="Empty Inventory", + description="Your inventory is empty.", + color=discord.Color.red() + ) + return await ctx.send(embed=embed) - view = InventoryView(ctx, inventory) - await ctx.send( - f"{ctx.author.mention}'s Inventory", - view=view + embed = discord.Embed( + title=f"{ctx.author.display_name}'s Inventory", + color=discord.Color.blue() ) + view = InventoryView(ctx, inventory) + await ctx.send(embed=embed, view=view) try: await view.wait() if view.selected_item: # An item was selected to use await self.pending_prompt(ctx, instance, inventory, view.selected_item) except asyncio.TimeoutError: - await ctx.send("Inventory view timed out.") + await ctx.send("Inventory view timed out.", ephemeral=True) async def inv_hook(self, user): """Inventory Hook for outside cogs @@ -165,7 +170,6 @@ class Shop(commands.Cog): pass @shop.command() - @commands.max_concurrency(1, commands.BucketType.user) async def buy(self, ctx, *purchase): """Opens the shop menu or directly purchases an item. @@ -183,33 +187,60 @@ class Shop(commands.Cog): return await ctx.send("You can't use this command in DMs when not in global mode.") if not await instance.Shops(): - return await ctx.send("No shops have been created yet.") + embed = discord.Embed( + title="No Shops Available", + description="No shops have been created yet.", + color=discord.Color.red() + ) + return await ctx.send(embed=embed) if await instance.Settings.Closed(): - return await ctx.send("The shop system is currently closed.") + embed = discord.Embed( + title="Shops Closed", + description="The shop system is currently closed.", + color=discord.Color.red() + ) + return await ctx.send(embed=embed) shops = await instance.Shops.all() available_shops = await self.check_availability(ctx, shops) if not available_shops: - return await ctx.send( - "Either no items have been created, you need a higher role, " - "or this command should be used in a server and not DMs." + embed = discord.Embed( + title="No Access", + description="Either no items have been created, you need a higher role, or this command should be used in a server and not DMs.", + color=discord.Color.red() ) + return await ctx.send(embed=embed) if purchase: try: shop, item = purchase except ValueError: - return await ctx.send("Too many parameters passed. Use help on this command for more information.") + embed = discord.Embed( + title="Invalid Parameters", + description="Too many parameters passed. Use help on this command for more information.", + color=discord.Color.red() + ) + return await ctx.send(embed=embed) if shop not in shops: - return await ctx.send("Either that shop does not exist, or you don't have access to it.") + embed = discord.Embed( + title="Shop Not Found", + description="Either that shop does not exist, or you don't have access to it.", + color=discord.Color.red() + ) + return await ctx.send(embed=embed) # Create purchase view directly for the specified item item_data = shops[shop]["Items"].get(item) if not item_data: - return await ctx.send(f"Item '{item}' not found in shop '{shop}'.") + embed = discord.Embed( + title="Item Not Found", + description=f"Item '{item}' not found in shop '{shop}'.", + color=discord.Color.red() + ) + return await ctx.send(embed=embed) view = PurchaseView(ctx, shop, item, item_data) embed = view.build_embed() @@ -222,24 +253,26 @@ class Shop(commands.Cog): sm = ShopManager(ctx, instance, user_data) await sm.order(shop, item, view.quantity) except asyncio.TimeoutError: - await ctx.send("Purchase menu timed out.") + await ctx.send("Purchase menu timed out.", ephemeral=True) else: # Open interactive shop menu - view = ShopView(ctx, shops) - await ctx.send( - f"Welcome to the shop, {ctx.author.mention}!", - view=view + embed = discord.Embed( + title="Welcome to the Shop", + description=f"Welcome {ctx.author.mention}! Please select a shop to browse.", + color=discord.Color.blue() ) + view = ShopView(ctx, shops) + await ctx.send(embed=embed, view=view) try: await view.wait() - if view.current_shop and view.current_item: # An item was selected + if view.current_shop and view.current_item and view.quantity: user_data = await self.get_instance(ctx, user=ctx.author) sm = ShopManager(ctx, instance, user_data) await sm.order(view.current_shop, view.current_item, view.quantity) except asyncio.TimeoutError: - await ctx.send("Shop menu timed out.") + await ctx.send("Shop menu timed out.", ephemeral=True) @commands.max_concurrency(1, commands.BucketType.user) @shop.command() diff --git a/shop/ui.py b/shop/ui.py index 602a390..57cf3a5 100644 --- a/shop/ui.py +++ b/shop/ui.py @@ -10,8 +10,23 @@ class ShopView(View): self.shops = shops self.current_shop = None self.current_item = None - self.quantity = None # Add quantity attribute + self.quantity = None self.setup_shop_select() + self.add_item(Button(label="Cancel", style=discord.ButtonStyle.red, custom_id="cancel")) + + async def interaction_check(self, interaction: discord.Interaction) -> bool: + if interaction.user != self.ctx.author: + await interaction.response.send_message("This menu is not for you!", ephemeral=True) + return False + return True + + async def on_error(self, interaction: discord.Interaction, error: Exception, item: Item) -> None: + await interaction.response.send_message(f"An error occurred: {str(error)}", ephemeral=True) + + @discord.ui.button(label="Cancel", style=discord.ButtonStyle.red, custom_id="cancel") + async def cancel(self, interaction: discord.Interaction, button: Button): + await interaction.response.edit_message(content="Shop menu cancelled.", embed=None, view=None) + self.stop() def setup_shop_select(self): options = [] @@ -33,9 +48,6 @@ class ShopView(View): self.add_item(shop_select) async def shop_selected(self, interaction: discord.Interaction): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - shop_name = interaction.data["values"][0] self.current_shop = shop_name @@ -48,6 +60,12 @@ class ShopView(View): items = self.shops[shop_name]["Items"] options = [] + embed = discord.Embed( + title=f"Shop: {shop_name}", + description="Select an item to purchase", + color=discord.Color.blue() + ) + for item_name, item_data in items.items(): qty = "∞" if item_data["Qty"] == "--" else item_data["Qty"] desc = f"Cost: {item_data['Cost']} | Stock: {qty}" @@ -66,12 +84,9 @@ class ShopView(View): item_select.callback = self.item_selected self.add_item(item_select) - await interaction.response.edit_message(view=self) + await interaction.response.edit_message(embed=embed, view=self) async def item_selected(self, interaction: discord.Interaction): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - item_name = interaction.data["values"][0] self.current_item = item_name item_data = self.shops[self.current_shop]["Items"][item_name] @@ -94,8 +109,14 @@ class PurchaseView(View): self.shop = shop self.item = item self.item_data = item_data - self.quantity = 1 + self.quantity = None + async def interaction_check(self, interaction: discord.Interaction) -> bool: + if interaction.user != self.ctx.author: + await interaction.response.send_message("This menu is not for you!", ephemeral=True) + return False + return True + def build_embed(self) -> discord.Embed: e = discord.Embed( title=f"Purchase {self.item}", @@ -108,31 +129,27 @@ class PurchaseView(View): e.add_field(name="Type", value=self.item_data["Type"].title(), inline=True) if self.item_data["Type"] == "role": e.add_field(name="Role", value=self.item_data["Role"], inline=True) + e.set_footer(text="Select a quantity to purchase") return e @button(label="Buy 1", style=discord.ButtonStyle.green, row=1) async def buy_one(self, interaction: discord.Interaction, button: Button): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) self.quantity = 1 await self.handle_purchase(interaction) @button(label="Buy 5", style=discord.ButtonStyle.green, row=1) async def buy_five(self, interaction: discord.Interaction, button: Button): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) self.quantity = 5 await self.handle_purchase(interaction) @button(label="Custom Amount", style=discord.ButtonStyle.blurple, row=1) async def custom_amount(self, interaction: discord.Interaction, button: Button): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - - await interaction.response.send_message( - "How many would you like to buy? Type a number:", - ephemeral=True + embed = discord.Embed( + title="Custom Purchase Amount", + description="How many would you like to buy? Type a number in chat.", + color=discord.Color.blue() ) + await interaction.response.edit_message(embed=embed) def check(m): return ( @@ -147,12 +164,12 @@ class PurchaseView(View): await self.handle_purchase(interaction) except asyncio.TimeoutError: await interaction.followup.send("Purchase cancelled - took too long to respond.", ephemeral=True) + self.stop() @button(label="Cancel", style=discord.ButtonStyle.red, row=1) async def cancel(self, interaction: discord.Interaction, button: Button): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) await interaction.response.edit_message(content="Purchase cancelled.", embed=None, view=None) + self.stop() async def handle_purchase(self, interaction: discord.Interaction): # Validate quantity @@ -172,21 +189,36 @@ class PurchaseView(View): total_cost = self.item_data["Cost"] * self.quantity + embed = discord.Embed( + title="Processing Purchase", + description=f"Processing purchase of {self.quantity}x {self.item}...", + color=discord.Color.green() + ) + embed.add_field(name="Total Cost", value=total_cost) + # This will be handled by the Shop cog's order method self.stop() - await interaction.response.edit_message( - content=f"Processing purchase of {self.quantity}x {self.item}...", - embed=None, - view=None - ) + await interaction.response.edit_message(embed=embed, view=None) class InventoryView(View): def __init__(self, ctx, inventory: Dict[str, Any], timeout: int = 60): super().__init__(timeout=timeout) self.ctx = ctx self.inventory = inventory - self.selected_item = None # Track selected item + self.selected_item = None self.setup_inventory_select() + self.add_item(Button(label="Close", style=discord.ButtonStyle.red, custom_id="close")) + + async def interaction_check(self, interaction: discord.Interaction) -> bool: + if interaction.user != self.ctx.author: + await interaction.response.send_message("This menu is not for you!", ephemeral=True) + return False + return True + + @discord.ui.button(label="Close", style=discord.ButtonStyle.red, custom_id="close") + async def close(self, interaction: discord.Interaction, button: Button): + await interaction.response.edit_message(content="Inventory closed.", embed=None, view=None) + self.stop() def setup_inventory_select(self): options = [] @@ -208,11 +240,8 @@ class InventoryView(View): self.add_item(inv_select) async def item_selected(self, interaction: discord.Interaction): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - item_name = interaction.data["values"][0] - self.selected_item = item_name # Store selected item + self.selected_item = item_name item_data = self.inventory[item_name] embed = discord.Embed( @@ -240,25 +269,27 @@ class UseItemView(View): self.ctx = ctx self.item = item self.item_data = item_data - self.value = False # Track if item was used + self.value = False + async def interaction_check(self, interaction: discord.Interaction) -> bool: + if interaction.user != self.ctx.author: + await interaction.response.send_message("This menu is not for you!", ephemeral=True) + return False + return True + @button(label="Use Item", style=discord.ButtonStyle.green) async def use_item(self, interaction: discord.Interaction, button: Button): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - - self.value = True # Item was used - await interaction.response.edit_message( - content=f"Processing use of {self.item}...", - embed=None, - view=None + self.value = True + embed = discord.Embed( + title="Using Item", + description=f"Processing use of {self.item}...", + color=discord.Color.green() ) + await interaction.response.edit_message(embed=embed, view=None) self.stop() @button(label="Cancel", style=discord.ButtonStyle.red) async def cancel(self, interaction: discord.Interaction, button: Button): - if interaction.user != self.ctx.author: - return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - self.value = False # Item was not used + self.value = False await interaction.response.edit_message(content="Cancelled.", embed=None, view=None) self.stop() \ No newline at end of file