From dab195c57f1911cc31d7e4292ccd68073b662e0f Mon Sep 17 00:00:00 2001 From: Valerie Date: Mon, 26 May 2025 00:46:18 -0400 Subject: [PATCH] Implement user-specific interaction checks in ShopView, PurchaseView, and InventoryView to prevent unauthorized access. Update timeout handling to provide clearer feedback messages upon inactivity. Refactor button layout for better organization and improve embed field formatting for item details. --- shop/ui.py | 104 +++++++++++++++++++++-------------------------------- 1 file changed, 41 insertions(+), 63 deletions(-) diff --git a/shop/ui.py b/shop/ui.py index dc83842..5198f21 100644 --- a/shop/ui.py +++ b/shop/ui.py @@ -2,6 +2,7 @@ import discord from discord.ui import View, Select, Button, button, Item from typing import Optional, List, Dict, Any from redbot.core.utils.chat_formatting import box, humanize_list +import asyncio class ShopView(View): def __init__(self, ctx, shops: Dict[str, Any], timeout: int = 180): # 3 minutes timeout @@ -50,6 +51,9 @@ 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 @@ -89,6 +93,9 @@ class ShopView(View): 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] @@ -106,8 +113,7 @@ class ShopView(View): async def on_timeout(self) -> None: if self.message: - await self.message.reply("Shop menu timed out after 3 minutes of inactivity.", mention_author=True) - await self.message.edit(view=None) + await self.message.edit(content="Shop menu timed out after 3 minutes of inactivity.", view=None) class PurchaseView(View): def __init__(self, ctx, shop: str, item: str, item_data: Dict[str, Any], timeout: int = 180): # 3 minutes timeout @@ -120,19 +126,19 @@ class PurchaseView(View): self.message = None # Store message for timeout handling # Add buttons with explicit row assignments - buy_one = Button(label="Buy 1", style=discord.ButtonStyle.green, row=1) + buy_one = Button(label="Buy 1", style=discord.ButtonStyle.green, row=0) buy_one.callback = self.buy_one self.add_item(buy_one) - buy_five = Button(label="Buy 5", style=discord.ButtonStyle.green, row=1) + buy_five = Button(label="Buy 5", style=discord.ButtonStyle.green, row=0) buy_five.callback = self.buy_five self.add_item(buy_five) - custom = Button(label="Custom Amount", style=discord.ButtonStyle.blurple, row=1) + custom = Button(label="Custom Amount", style=discord.ButtonStyle.blurple, row=0) custom.callback = self.custom_amount self.add_item(custom) - cancel = Button(label="Cancel", style=discord.ButtonStyle.red, row=1) + cancel = Button(label="Cancel", style=discord.ButtonStyle.red, row=0) cancel.callback = self.cancel self.add_item(cancel) @@ -148,26 +154,27 @@ class PurchaseView(View): description=self.item_data["Info"], color=discord.Color.blue() ) - qty = "Infinite" if self.item_data["Qty"] == "--" else self.item_data["Qty"] - e.add_field(name="Stock", value=qty, inline=True) + + # First row of fields + e.add_field(name="Stock", value="Infinite" if self.item_data["Qty"] == "--" else self.item_data["Qty"], inline=True) e.add_field(name="Cost", value=self.item_data["Cost"], inline=True) e.add_field(name="Type", value=self.item_data["Type"].title(), inline=True) + + # Add role in a new row if it's a role type item if self.item_data["Type"] == "role": - e.add_field(name="Role", value=self.item_data["Role"], inline=True) + e.add_field(name="Role", value=self.item_data["Role"], inline=False) + 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): + async def buy_one(self, interaction: discord.Interaction): 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): + async def buy_five(self, interaction: discord.Interaction): 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): embed = discord.Embed( title="Custom Purchase Amount", @@ -191,8 +198,7 @@ class PurchaseView(View): await interaction.message.reply("Custom amount entry timed out after 1 minute.", mention_author=True) self.stop() - @button(label="Cancel", style=discord.ButtonStyle.red, row=1) - async def cancel(self, interaction: discord.Interaction, button: Button): + async def cancel(self, interaction: discord.Interaction): await interaction.response.edit_message(content="Purchase cancelled.", embed=None, view=None) self.stop() @@ -235,8 +241,7 @@ class PurchaseView(View): async def on_timeout(self) -> None: if self.message: - await self.message.reply("Purchase menu timed out after 3 minutes of inactivity.", mention_author=True) - await self.message.edit(view=None) + await self.message.edit(content="Purchase menu timed out after 3 minutes of inactivity.", view=None) class InventoryView(View): def __init__(self, ctx, inventory: Dict[str, Any], timeout: int = 180): # 3 minutes timeout @@ -281,67 +286,40 @@ class InventoryView(View): self.add_item(inv_select) async def item_selected(self, interaction: discord.Interaction): - item_name = interaction.data["values"][0] - self.selected_item = item_name - item_data = self.inventory[item_name] - - embed = discord.Embed( - title=item_name, - description=item_data["Info"], - color=discord.Color.blue() - ) - embed.add_field(name="Quantity", value=item_data["Qty"], inline=True) - embed.add_field(name="Type", value=item_data["Type"].title(), inline=True) - - if item_data["Type"] == "role": - use_view = UseItemView(self.ctx, item_name, item_data) - await interaction.response.edit_message(embed=embed, view=use_view) + if interaction.user != self.ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) - # Wait for the use view to complete - await use_view.wait() - if use_view.value: # If item was used - self.stop() # Stop the inventory view - else: - await interaction.response.edit_message(embed=embed) + self.selected_item = interaction.data["values"][0] + self.stop() async def on_timeout(self) -> None: if self.message: - await self.message.reply("Inventory menu timed out after 3 minutes of inactivity.", mention_author=True) - await self.message.edit(view=None) + await self.message.edit(content="Inventory menu timed out after 3 minutes of inactivity.", view=None) class UseItemView(View): - def __init__(self, ctx, item: str, item_data: Dict[str, Any], timeout: int = 180): # 3 minutes timeout + def __init__(self, ctx, item_name: str, timeout: int = 60): super().__init__(timeout=timeout) self.ctx = ctx - self.item = item - self.item_data = item_data - self.value = False - self.message = None # Store message for timeout handling + self.item_name = item_name + self.value = None + self.message = None - async def interaction_check(self, interaction: discord.Interaction) -> bool: + @button(label="Use", style=discord.ButtonStyle.green) + async def use(self, interaction: discord.Interaction, button: Button): 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): + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) 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() + await interaction.response.defer() @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 - await interaction.response.edit_message(content="Cancelled.", embed=None, view=None) self.stop() - + await interaction.response.defer() + async def on_timeout(self) -> None: if self.message: - await self.message.reply("Item use menu timed out after 3 minutes of inactivity.", mention_author=True) - await self.message.edit(view=None) \ No newline at end of file + await self.message.edit(content="Item use menu timed out.", view=None) \ No newline at end of file