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.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-26 00:46:18 -04:00
parent 35e0ac182d
commit dab195c57f

View file

@ -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)
await self.message.edit(content="Item use menu timed out.", view=None)