Refactor Shop and UI components to improve timeout handling for interactive views. Update timeout duration to 3 minutes and implement message replies for user feedback upon timeout in ShopView, PurchaseView, and InventoryView.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-25 23:58:22 -04:00
parent 3ee6707cc5
commit 85de2c70c1
2 changed files with 38 additions and 16 deletions

View file

@ -129,14 +129,14 @@ class Shop(commands.Cog):
color=discord.Color.blue() color=discord.Color.blue()
) )
view = InventoryView(ctx, inventory) view = InventoryView(ctx, inventory)
await ctx.send(embed=embed, view=view) view.message = await ctx.send(embed=embed, view=view)
try: try:
await view.wait() await view.wait()
if view.selected_item: # An item was selected to use if view.selected_item: # An item was selected to use
await self.pending_prompt(ctx, instance, inventory, view.selected_item) await self.pending_prompt(ctx, instance, inventory, view.selected_item)
except asyncio.TimeoutError: except asyncio.TimeoutError:
await ctx.send("Inventory view timed out.", ephemeral=True) pass # Handled by view's on_timeout
async def inv_hook(self, user): async def inv_hook(self, user):
"""Inventory Hook for outside cogs """Inventory Hook for outside cogs
@ -244,7 +244,7 @@ class Shop(commands.Cog):
view = PurchaseView(ctx, shop, item, item_data) view = PurchaseView(ctx, shop, item, item_data)
embed = view.build_embed() embed = view.build_embed()
await ctx.send(embed=embed, view=view) view.message = await ctx.send(embed=embed, view=view)
try: try:
await view.wait() await view.wait()
@ -253,7 +253,7 @@ class Shop(commands.Cog):
sm = ShopManager(ctx, instance, user_data) sm = ShopManager(ctx, instance, user_data)
await sm.order(shop, item, view.quantity) await sm.order(shop, item, view.quantity)
except asyncio.TimeoutError: except asyncio.TimeoutError:
await ctx.send("Purchase menu timed out.", ephemeral=True) pass # Handled by view's on_timeout
else: else:
# Open interactive shop menu # Open interactive shop menu
@ -263,7 +263,7 @@ class Shop(commands.Cog):
color=discord.Color.blue() color=discord.Color.blue()
) )
view = ShopView(ctx, shops) view = ShopView(ctx, shops)
await ctx.send(embed=embed, view=view) view.message = await ctx.send(embed=embed, view=view)
try: try:
await view.wait() await view.wait()
@ -272,7 +272,7 @@ class Shop(commands.Cog):
sm = ShopManager(ctx, instance, user_data) sm = ShopManager(ctx, instance, user_data)
await sm.order(view.current_shop, view.current_item, view.quantity) await sm.order(view.current_shop, view.current_item, view.quantity)
except asyncio.TimeoutError: except asyncio.TimeoutError:
await ctx.send("Shop menu timed out.", ephemeral=True) pass # Handled by view's on_timeout
@commands.max_concurrency(1, commands.BucketType.user) @commands.max_concurrency(1, commands.BucketType.user)
@shop.command() @shop.command()

View file

@ -4,15 +4,15 @@ from typing import Optional, List, Dict, Any
from redbot.core.utils.chat_formatting import box, humanize_list from redbot.core.utils.chat_formatting import box, humanize_list
class ShopView(View): class ShopView(View):
def __init__(self, ctx, shops: Dict[str, Any], timeout: int = 60): def __init__(self, ctx, shops: Dict[str, Any], timeout: int = 180): # 3 minutes timeout
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
self.ctx = ctx self.ctx = ctx
self.shops = shops self.shops = shops
self.current_shop = None self.current_shop = None
self.current_item = None self.current_item = None
self.quantity = None self.quantity = None
self.message = None # Store message for timeout handling
self.setup_shop_select() self.setup_shop_select()
# Add cancel button to a different row
cancel_button = Button(label="Cancel", style=discord.ButtonStyle.red, custom_id="cancel", row=4) cancel_button = Button(label="Cancel", style=discord.ButtonStyle.red, custom_id="cancel", row=4)
cancel_button.callback = self.cancel cancel_button.callback = self.cancel
self.add_item(cancel_button) self.add_item(cancel_button)
@ -104,14 +104,20 @@ class ShopView(View):
self.quantity = purchase_view.quantity # Store the quantity from purchase view self.quantity = purchase_view.quantity # Store the quantity from purchase view
self.stop() # Stop the shop view since we're done self.stop() # Stop the shop view since we're done
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)
class PurchaseView(View): class PurchaseView(View):
def __init__(self, ctx, shop: str, item: str, item_data: Dict[str, Any], timeout: int = 60): def __init__(self, ctx, shop: str, item: str, item_data: Dict[str, Any], timeout: int = 180): # 3 minutes timeout
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
self.ctx = ctx self.ctx = ctx
self.shop = shop self.shop = shop
self.item = item self.item = item
self.item_data = item_data self.item_data = item_data
self.quantity = None self.quantity = None
self.message = None # Store message for timeout handling
# Add buttons with explicit row assignments # 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=1)
@ -162,7 +168,7 @@ class PurchaseView(View):
await self.handle_purchase(interaction) await self.handle_purchase(interaction)
@button(label="Custom Amount", style=discord.ButtonStyle.blurple, row=1) @button(label="Custom Amount", style=discord.ButtonStyle.blurple, row=1)
async def custom_amount(self, interaction: discord.Interaction, button: Button): async def custom_amount(self, interaction: discord.Interaction):
embed = discord.Embed( embed = discord.Embed(
title="Custom Purchase Amount", title="Custom Purchase Amount",
description="How many would you like to buy? Type a number in chat.", description="How many would you like to buy? Type a number in chat.",
@ -178,11 +184,11 @@ class PurchaseView(View):
) )
try: try:
msg = await self.ctx.bot.wait_for("message", timeout=30.0, check=check) msg = await self.ctx.bot.wait_for("message", timeout=60.0, check=check) # 1 minute to type amount
self.quantity = int(msg.content) self.quantity = int(msg.content)
await self.handle_purchase(interaction) await self.handle_purchase(interaction)
except asyncio.TimeoutError: except asyncio.TimeoutError:
await interaction.followup.send("Purchase cancelled - took too long to respond.", ephemeral=True) await interaction.message.reply("Custom amount entry timed out after 1 minute.", mention_author=True)
self.stop() self.stop()
@button(label="Cancel", style=discord.ButtonStyle.red, row=1) @button(label="Cancel", style=discord.ButtonStyle.red, row=1)
@ -227,14 +233,19 @@ class PurchaseView(View):
self.stop() self.stop()
await interaction.response.edit_message(embed=embed, view=None) await interaction.response.edit_message(embed=embed, view=None)
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)
class InventoryView(View): class InventoryView(View):
def __init__(self, ctx, inventory: Dict[str, Any], timeout: int = 60): def __init__(self, ctx, inventory: Dict[str, Any], timeout: int = 180): # 3 minutes timeout
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
self.ctx = ctx self.ctx = ctx
self.inventory = inventory self.inventory = inventory
self.selected_item = None self.selected_item = None
self.message = None # Store message for timeout handling
self.setup_inventory_select() self.setup_inventory_select()
# Add close button to a different row
close_button = Button(label="Close", style=discord.ButtonStyle.red, custom_id="close", row=4) close_button = Button(label="Close", style=discord.ButtonStyle.red, custom_id="close", row=4)
close_button.callback = self.close close_button.callback = self.close
self.add_item(close_button) self.add_item(close_button)
@ -293,13 +304,19 @@ class InventoryView(View):
else: else:
await interaction.response.edit_message(embed=embed) await interaction.response.edit_message(embed=embed)
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)
class UseItemView(View): class UseItemView(View):
def __init__(self, ctx, item: str, item_data: Dict[str, Any], timeout: int = 60): def __init__(self, ctx, item: str, item_data: Dict[str, Any], timeout: int = 180): # 3 minutes timeout
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
self.ctx = ctx self.ctx = ctx
self.item = item self.item = item
self.item_data = item_data self.item_data = item_data
self.value = False self.value = False
self.message = None # Store message for timeout handling
async def interaction_check(self, interaction: discord.Interaction) -> bool: async def interaction_check(self, interaction: discord.Interaction) -> bool:
if interaction.user != self.ctx.author: if interaction.user != self.ctx.author:
@ -322,4 +339,9 @@ class UseItemView(View):
async def cancel(self, interaction: discord.Interaction, button: Button): async def cancel(self, interaction: discord.Interaction, button: Button):
self.value = False self.value = False
await interaction.response.edit_message(content="Cancelled.", embed=None, view=None) await interaction.response.edit_message(content="Cancelled.", embed=None, view=None)
self.stop() self.stop()
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)