264 lines
No EOL
11 KiB
Python
264 lines
No EOL
11 KiB
Python
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.quantity = None # Add quantity attribute
|
|
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)
|
|
|
|
# Wait for the purchase view to complete
|
|
await purchase_view.wait()
|
|
self.quantity = purchase_view.quantity # Store the quantity from purchase view
|
|
self.stop() # Stop the shop view since we're done
|
|
|
|
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.selected_item = None # Track selected item
|
|
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]
|
|
self.selected_item = item_name # Store selected item
|
|
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)
|
|
|
|
# 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)
|
|
|
|
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
|
|
self.value = False # Track if item was used
|
|
|
|
@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.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
|
|
await interaction.response.edit_message(content="Cancelled.", embed=None, view=None)
|
|
self.stop() |