diff --git a/shop/shop.py b/shop/shop.py index ee91de7..f61c367 100644 --- a/shop/shop.py +++ b/shop/shop.py @@ -13,7 +13,7 @@ from itertools import zip_longest from typing import Literal # Shop -from .menu import ShopMenu +from .ui import ShopView, InventoryView, PurchaseView, UseItemView from .inventory import Inventory from .checks import Checks @@ -29,7 +29,7 @@ from redbot.core.errors import BalanceTooHigh log = logging.getLogger("red.shop") -__version__ = "3.1.13" +__version__ = "3.2.0" __author__ = "Redjumpman" @@ -114,16 +114,23 @@ class Shop(commands.Cog): instance = await self.get_instance(ctx, user=ctx.author) except AttributeError: return await ctx.send("You can't use this command in DMs when not in global mode.") - if not await instance.Inventory(): - return await ctx.send("You don't have any items to display.") - data = await instance.Inventory.all() - menu = Inventory(ctx, list(data.items())) - + + inventory = await instance.Inventory.all() + if not inventory: + return await ctx.send("Your inventory is empty.") + + view = InventoryView(ctx, inventory) + await ctx.send( + f"{ctx.author.mention}'s Inventory", + view=view + ) + try: - item = await menu.display() - except RuntimeError: - return - await self.pending_prompt(ctx, instance, data, item) + await view.wait() + if view.value: # An item was selected to use + await self.pending_prompt(ctx, instance, inventory, view.value) + except asyncio.TimeoutError: + await ctx.send("Inventory view timed out.") async def inv_hook(self, user): """Inventory Hook for outside cogs @@ -159,61 +166,79 @@ class Shop(commands.Cog): @shop.command() @commands.max_concurrency(1, commands.BucketType.user) async def buy(self, ctx, *purchase): - """Shop menu appears with no purchase order. - - When no argument is specified for purchase, it will bring up the - shop menu. - - Using the purchase argument allows direct purchases from a shop. - The order is "Shop Name" "Item Name" and names with spaces - must include quotes. - - Examples - -------- - [p]shop buy \"Secret Shop\" oil + """Opens the shop menu or directly purchases an item. + + When no argument is specified, opens an interactive shop menu. + For direct purchase, use: "Shop Name" "Item Name" + + Examples: + [p]shop buy "Secret Shop" oil [p]shop buy Junkyard tire - [p]shop buy \"Holy Temple\" \"Healing Potion\" + [p]shop buy "Holy Temple" "Healing Potion" """ try: instance = await self.get_instance(ctx, settings=True) except AttributeError: 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.") + if await instance.Settings.Closed(): return await ctx.send("The shop system is currently closed.") shops = await instance.Shops.all() - col = await self.check_availability(ctx, shops) - if not col: + 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, " + "Either no items have been created, you need a higher role, " "or this command should be used in a server and not DMs." ) + if purchase: try: shop, item = purchase except ValueError: return await ctx.send("Too many parameters passed. Use help on this command for more information.") + if shop not in shops: return await ctx.send("Either that shop does not exist, or you don't have access to it.") - - else: - style = await instance.Settings.Sorting() - menu = ShopMenu(ctx, shops, sorting=style) + + # 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}'.") + + view = PurchaseView(ctx, shop, item, item_data) + embed = view.build_embed() + await ctx.send(embed=embed, view=view) + try: - shop, item = await menu.display() - except RuntimeError: - return - - user_data = await self.get_instance(ctx, user=ctx.author) - sm = ShopManager(ctx, instance, user_data) - try: - await sm.order(shop, item) - except asyncio.TimeoutError: - await ctx.send("Request timed out.") - except ExitProcess: - await ctx.send("Transaction canceled.") + await view.wait() + if view.quantity: # A purchase was initiated + user_data = await self.get_instance(ctx, user=ctx.author) + sm = ShopManager(ctx, instance, user_data) + await sm.order(shop, item, view.quantity) + except asyncio.TimeoutError: + await ctx.send("Purchase menu timed out.") + + else: + # Open interactive shop menu + view = ShopView(ctx, shops) + await ctx.send( + f"Welcome to the shop, {ctx.author.mention}!", + view=view + ) + + try: + await view.wait() + if view.current_shop and view.current_item: # An item was selected + 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.") @commands.max_concurrency(1, commands.BucketType.user) @shop.command() @@ -372,18 +397,153 @@ class Shop(commands.Cog): instance = await self.get_instance(ctx, settings=True) if not await instance.Pending(): return await ctx.send("There are not any pending items.") + data = await instance.Pending.all() - menu = ShopMenu(ctx, data, mode=1, sorting="name") - - try: - user, item, = await menu.display() - except RuntimeError: - return - - try: - await self.clear_single_pending(ctx, instance, data, item, user) - except asyncio.TimeoutError: - await ctx.send("Request timed out.") + + class PendingView(discord.ui.View): + def __init__(self, cog, data, timeout=60): + super().__init__(timeout=timeout) + self.cog = cog + self.data = data + self.setup_user_select() + + def setup_user_select(self): + options = [] + for user_id, items in self.data.items(): + user = self.cog.bot.get_user(int(user_id)) + if user: + desc = f"{len(items)} pending items" + options.append(discord.SelectOption( + label=user.name, + description=desc, + value=user_id + )) + + if options: + user_select = discord.ui.Select( + placeholder="Select a user", + options=options[:25], # Discord limit + row=0 + ) + user_select.callback = self.user_selected + self.add_item(user_select) + + async def user_selected(self, interaction: discord.Interaction): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + + user_id = interaction.data["values"][0] + user_items = self.data[user_id] + + # Clear previous item select if it exists + for item in self.children[:]: + if isinstance(item, discord.ui.Select) and item.row == 1: + self.remove_item(item) + + # Add item select for this user + options = [] + for item_id, item_data in user_items.items(): + options.append(discord.SelectOption( + label=item_data["Item"][:25], + description=f"ID: {item_id[:8]}...", + value=f"{user_id}:{item_id}" + )) + + if options: + item_select = discord.ui.Select( + placeholder="Select an item to process", + options=options[:25], + row=1 + ) + item_select.callback = self.item_selected + self.add_item(item_select) + + await interaction.response.edit_message(view=self) + + async def item_selected(self, interaction: discord.Interaction): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + + user_id, item_id = interaction.data["values"][0].split(":") + user = self.cog.bot.get_user(int(user_id)) + item_data = self.data[user_id][item_id] + + # Create confirmation view + confirm_view = PendingConfirmView(self.cog, user, item_id, item_data["Item"]) + embed = discord.Embed( + title="Pending Item", + description=f"Process pending item for {user.mention}", + color=discord.Color.blue() + ) + embed.add_field(name="Item", value=item_data["Item"]) + embed.add_field(name="ID", value=item_id) + embed.add_field(name="Timestamp", value=item_data["Timestamp"]) + + await interaction.response.edit_message(embed=embed, view=confirm_view) + + class PendingConfirmView(discord.ui.View): + def __init__(self, cog, user, item_id, item_name, timeout=60): + super().__init__(timeout=timeout) + self.cog = cog + self.user = user + self.item_id = item_id + self.item_name = item_name + + @discord.ui.button(label="Approve", style=discord.ButtonStyle.green) + async def approve(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + + async with instance.Pending() as p: + del p[str(self.user.id)][self.item_id] + if not p[str(self.user.id)]: + del p[str(self.user.id)] + + await interaction.response.edit_message( + content=f"{self.item_name} was approved for {self.user.name}.", + embed=None, + view=None + ) + try: + await self.user.send(f"{ctx.author.name} approved your pending {self.item_name}!") + except discord.HTTPException: + pass + self.stop() + + @discord.ui.button(label="Deny", style=discord.ButtonStyle.red) + async def deny(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + + async with instance.Pending() as p: + del p[str(self.user.id)][self.item_id] + if not p[str(self.user.id)]: + del p[str(self.user.id)] + + await interaction.response.edit_message( + content=f"{self.item_name} was denied for {self.user.name}.", + embed=None, + view=None + ) + try: + await self.user.send(f"{ctx.author.name} denied your pending {self.item_name}.") + except discord.HTTPException: + pass + self.stop() + + @discord.ui.button(label="Cancel", style=discord.ButtonStyle.grey) + async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + await interaction.response.edit_message(content="Action cancelled.", embed=None, view=None) + self.stop() + + # Start the pending menu + view = PendingView(self, data) + await ctx.send( + "Select a user to view their pending items:", + view=view + ) @shop.command() @commands.guild_only() @@ -774,22 +934,90 @@ class Shop(commands.Cog): await sm.remove(item) await ctx.send("{} was granted the {} role.".format(ctx.author.mention, role.name)) + async def pending_prompt(self, ctx, instance, data, item): + """Handle item redemption with modern UI.""" + e = discord.Embed(color=await ctx.embed_colour()) + e.add_field(name=item, value=data[item]["Info"], inline=False) + + class RedeemView(discord.ui.View): + def __init__(self, cog, timeout=60): + super().__init__(timeout=timeout) + self.cog = cog + self.value = None + + @discord.ui.button(label="Redeem", style=discord.ButtonStyle.green) + async def redeem(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + self.value = True + self.stop() + await interaction.response.defer() + + @discord.ui.button(label="Cancel", style=discord.ButtonStyle.red) + async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user != ctx.author: + return await interaction.response.send_message("This menu is not for you!", ephemeral=True) + self.value = False + self.stop() + await interaction.response.defer() + + if data[item]["Type"].lower() == "role": + prompt = f"{ctx.author.mention} Do you wish to redeem {item}? This will grant you the role assigned to this item and it will be removed from your inventory permanently." + else: + prompt = f"{ctx.author.mention} Do you wish to redeem {item}? This will add the item to the pending list for an admin to review and grant. The item will be removed from your inventory while this is processing." + + view = RedeemView(self) + msg = await ctx.send(prompt, embed=e, view=view) + + try: + await view.wait() + if view.value is None: + await msg.edit(content="Redemption timed out.", view=None) + return + elif not view.value: + await msg.edit(content="Redemption cancelled.", view=None) + return + + if data[item]["Type"].lower() == "role": + await self.assign_role(ctx, instance, item, data[item]["Role"]) + else: + await self.pending_add(ctx, item) + + sm = ShopManager(ctx, instance=None, user_data=instance) + await sm.remove(item) + + except Exception as exc: + await msg.edit(content=f"An error occurred: {str(exc)}", view=None) + async def pending_add(self, ctx, item): + """Add an item to the pending list with modern UI.""" instance = await self.get_instance(ctx, settings=True) unique_id = str(uuid.uuid4())[:17] timestamp = ctx.message.created_at.now().strftime("%Y-%m-%d %H:%M:%S") + async with instance.Pending() as p: if str(ctx.author.id) in p: p[str(ctx.author.id)][unique_id] = {"Item": item, "Timestamp": timestamp} else: p[str(ctx.author.id)] = {unique_id: {"Item": item, "Timestamp": timestamp}} - msg = "{} added {} to your pending list.".format(ctx.author.mention, item) + + embed = discord.Embed( + title="Item Pending", + description=f"{ctx.author.mention} added {item} to the pending list.", + color=discord.Color.blue() + ) + embed.add_field(name="ID", value=unique_id) + embed.add_field(name="Timestamp", value=timestamp) + if await instance.Settings.Alerts(): alert_role = await instance.Settings.Alert_Role() role = discord.utils.get(ctx.guild.roles, name=alert_role) if role: - msg = "{}\n{}".format(role.mention, msg) - await ctx.send(msg) + await ctx.send(role.mention, embed=embed) + else: + await ctx.send(embed=embed) + else: + await ctx.send(embed=embed) async def change_mode(self, mode): await self.config.clear_all() @@ -879,40 +1107,6 @@ class Shop(commands.Cog): "list.".format(name.content) ) - async def pending_prompt(self, ctx, instance, data, item): - e = discord.Embed(color=await ctx.embed_colour()) - e.add_field(name=item, value=data[item]["Info"], inline=False) - if data[item]["Type"].lower() == "Role": - await ctx.send( - "{} Do you wish to redeem {}? This will grant you the role assigned to " - "this item and it will be removed from your inventory " - "permanently.".format(ctx.author.mention, item), - embed=e, - ) - else: - await ctx.send( - "{} Do you wish to redeem {}? This will add the item to the pending " - "list for an admin to review and grant. The item will be removed from " - "your inventory while this is " - "processing.".format(ctx.author.mention, item), - embed=e, - ) - try: - choice = await ctx.bot.wait_for("message", timeout=25, check=Checks(ctx).confirm) - except asyncio.TimeoutError: - return await ctx.send("No Response. Item redemption canceled.") - - if choice.content.lower() != "yes": - return await ctx.send("Canceled item redemption.") - - if data[item]["Type"].lower() == "role": - return await self.assign_role(ctx, instance, item, data[item]["Role"]) - else: - await self.pending_add(ctx, item) - - sm = ShopManager(ctx, instance=None, user_data=instance) - await sm.remove(item) - class ShopManager: def __init__(self, ctx, instance, user_data): @@ -954,7 +1148,8 @@ class ShopManager: await asyncio.sleep(2) # At least a little buffer to prevent rate limiting await self.ctx.author.send(chunk) - async def order(self, shop, item): + async def order(self, shop, item, quantity): + """Process a purchase order with the specified quantity.""" try: async with self.instance.Shops() as shops: if shop not in shops: @@ -972,55 +1167,27 @@ class ShopManager: if stock != "--" and stock <= 0: return await self.ctx.send(f"Sorry, {item} is out of stock.") - e = discord.Embed(color=await self.ctx.embed_colour()) - e.add_field(name=item, value=item_data["Info"], inline=False) - e.add_field(name="Cost", value=f"{cost} {cur}", inline=True) - e.add_field(name="Stock", value="Infinite" if stock == "--" else stock, inline=True) - - text = ( - f"How many {item} would you like to purchase?\n*If this " - f"is a random item, you can only buy 1 at a time.*" - ) - await self.ctx.send(content=text, embed=e) + # Validate quantity for random items + if _type == "random" and quantity != 1: + return await self.ctx.send("You can only buy 1 random item at a time.") - def predicate(m): - if m.author == self.ctx.author and self.ctx.channel == m.channel: - if m.content.isdigit(): - if _type == "random": - return int(m.content) == 1 - try: - amount = int(m.content) - if stock == "--": - return amount > 0 - return 0 < amount <= stock - except (TypeError, ValueError): - return False - elif m.content.lower() in ("exit", "cancel", "e", "x"): - return True - return False + # Check if enough stock + if stock != "--" and quantity > stock: + return await self.ctx.send(f"Not enough stock! Only {stock} available.") - try: - num = await self.ctx.bot.wait_for("message", timeout=25.0, check=predicate) - except asyncio.TimeoutError: - return await self.ctx.send("Purchase timed out.") - - if num.content.lower() in ("exit", "cancel", "e", "x"): - raise ExitProcess() - - amount = int(num.content) - total_cost = cost * amount + total_cost = cost * quantity try: await bank.withdraw_credits(self.ctx.author, total_cost) except ValueError: return await self.ctx.send( - f"You cannot afford {amount}x {item} for {total_cost} {cur}. Transaction ended." + f"You cannot afford {quantity}x {item} for {total_cost} {cur}. Transaction ended." ) # Handle different item types if _type == "auto": - await self.auto_handler(shop, item, amount) - await self.remove_stock(shop, item, stock, amount) + await self.auto_handler(shop, item, quantity) + await self.remove_stock(shop, item, stock, quantity) return await self.ctx.send("Message sent.") if _type == "random": @@ -1035,18 +1202,18 @@ class ShopManager: f"so {item} cannot be purchased." ) else: - await self.remove_stock(shop, item, stock, amount) + await self.remove_stock(shop, item, stock, quantity) item = new_item async with self.instance.Shops() as shops: item_data = deepcopy(shops[shop]["Items"][new_item]) stock = item_data["Qty"] # Update stock and add to inventory - await self.remove_stock(shop, item, stock, amount) - await self.add_to_inventory(item, item_data, amount) + await self.remove_stock(shop, item, stock, quantity) + await self.add_to_inventory(item, item_data, quantity) await self.ctx.send( - f"{self.ctx.author.mention} purchased {amount}x {item} for {total_cost} {cur}." + f"{self.ctx.author.mention} purchased {quantity}x {item} for {total_cost} {cur}." ) async def remove_stock(self, shop, item, current_stock, amount): diff --git a/shop/ui.py b/shop/ui.py new file mode 100644 index 0000000..6f4d472 --- /dev/null +++ b/shop/ui.py @@ -0,0 +1,248 @@ +import discord +from discord.ui import View, Select, Button, button +from typing import Optional, List, Dict, Any +from redbot.core.utils.chat_formatting import box, humanize_list + +class ShopView(View): + def __init__(self, ctx, shops: Dict[str, Any], timeout: int = 60): + super().__init__(timeout=timeout) + self.ctx = ctx + self.shops = shops + self.current_shop = None + self.current_item = None + self.setup_shop_select() + + def setup_shop_select(self): + options = [] + for shop_name, shop_data in self.shops.items(): + if shop_data["Items"]: # Only show shops with items + options.append(discord.SelectOption( + label=shop_name[:25], # Discord limit + description=f"{len(shop_data['Items'])} items", + value=shop_name + )) + + if options: + shop_select = Select( + placeholder="Select a shop", + options=options, + row=0 + ) + shop_select.callback = self.shop_selected + 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 + + # Clear previous item select if it exists + for item in self.children[:]: + if isinstance(item, Select) and item.row == 1: + self.remove_item(item) + + # Add new item select + items = self.shops[shop_name]["Items"] + options = [] + + 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}" + options.append(discord.SelectOption( + label=item_name[:25], + description=desc, + value=item_name + )) + + if options: + item_select = Select( + placeholder="Select an item", + options=options[:25], # Discord limit + row=1 + ) + item_select.callback = self.item_selected + self.add_item(item_select) + + await interaction.response.edit_message(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] + + # Create purchase confirmation view + purchase_view = PurchaseView(self.ctx, self.current_shop, item_name, item_data) + embed = purchase_view.build_embed() + + await interaction.response.edit_message(embed=embed, view=purchase_view) + +class PurchaseView(View): + def __init__(self, ctx, shop: str, item: str, item_data: Dict[str, Any], timeout: int = 60): + super().__init__(timeout=timeout) + self.ctx = ctx + self.shop = shop + self.item = item + self.item_data = item_data + self.quantity = 1 + + def build_embed(self) -> discord.Embed: + e = discord.Embed( + title=f"Purchase {self.item}", + 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) + 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) + if self.item_data["Type"] == "role": + e.add_field(name="Role", value=self.item_data["Role"], inline=True) + 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 + ) + + def check(m): + return ( + m.author == self.ctx.author + and m.channel == self.ctx.channel + and m.content.isdigit() + ) + + try: + msg = await self.ctx.bot.wait_for("message", timeout=30.0, check=check) + self.quantity = int(msg.content) + await self.handle_purchase(interaction) + except asyncio.TimeoutError: + await interaction.followup.send("Purchase cancelled - took too long to respond.", ephemeral=True) + + @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) + + async def handle_purchase(self, interaction: discord.Interaction): + # Validate quantity + if self.item_data["Type"] == "random" and self.quantity != 1: + await interaction.response.send_message( + "You can only buy 1 random item at a time.", + ephemeral=True + ) + return + + if self.item_data["Qty"] != "--" and self.quantity > self.item_data["Qty"]: + await interaction.response.send_message( + f"Not enough stock! Only {self.item_data['Qty']} available.", + ephemeral=True + ) + return + + total_cost = self.item_data["Cost"] * self.quantity + + # 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 + ) + +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.setup_inventory_select() + + def setup_inventory_select(self): + options = [] + for item_name, item_data in self.inventory.items(): + desc = f"Quantity: {item_data['Qty']} | Type: {item_data['Type']}" + options.append(discord.SelectOption( + label=item_name[:25], + description=desc, + value=item_name + )) + + if options: + inv_select = Select( + placeholder="Select an item to view/use", + options=options[:25], # Discord limit + row=0 + ) + inv_select.callback = self.item_selected + 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] + 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) + else: + await interaction.response.edit_message(embed=embed) + +class UseItemView(View): + def __init__(self, ctx, item: str, item_data: Dict[str, Any], timeout: int = 60): + super().__init__(timeout=timeout) + self.ctx = ctx + self.item = item + self.item_data = item_data + + @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) + + await interaction.response.edit_message( + content=f"Processing use of {self.item}...", + embed=None, + 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) + await interaction.response.edit_message(content="Cancelled.", embed=None, view=None) + self.stop() \ No newline at end of file