From 5a31115458b7f39c46657ea2b30317b58a8f300f Mon Sep 17 00:00:00 2001 From: Unstable Kitsune Date: Tue, 23 Sep 2025 03:26:32 -0400 Subject: [PATCH] Improve modals and waitlist handling in KofiShop Updated modal handling for order and review submissions with more user-friendly error messages. Added `waitlist_entries` to manage waitlist data. Refactored the `waitlist` command for better user mention handling and added error handling for permission issues. Overall enhancements improve functionality and user experience. --- kofishop/kofishop.py | 85 +++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/kofishop/kofishop.py b/kofishop/kofishop.py index d005f0e..1fed60e 100644 --- a/kofishop/kofishop.py +++ b/kofishop/kofishop.py @@ -1,7 +1,12 @@ import discord -from redbot.core import commands, Config -from redbot.core.bot import Red -from typing import Optional +from redbot.core import commands, Config, app_commands +from typing import Optional, TYPE_CHECKING +import datetime + +if TYPE_CHECKING: + from redbot.core.bot import Red + +# --- Modals for the Forms --- class OrderModal(discord.ui.Modal, title="Commission Order Form"): commission_type = discord.ui.TextInput(label="What type of commission?") @@ -16,19 +21,20 @@ class OrderModal(discord.ui.Modal, title="Commission Order Form"): async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild if not guild: - return - + return + channel_id = await self.cog.config.guild(guild).order_channel() if not channel_id: - await interaction.response.send_message("Order channel not set.", ephemeral=True) + await interaction.response.send_message("The order channel has not been set by an admin.", ephemeral=True) return - + channel = guild.get_channel(channel_id) if not isinstance(channel, discord.TextChannel): - await interaction.response.send_message("Invalid order channel.", ephemeral=True) + await interaction.response.send_message("The configured order channel is invalid.", ephemeral=True) return embed = discord.Embed(title=f"New Order from {interaction.user.name}", color=discord.Color.blurple()) + embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar.url) embed.add_field(name="Commission Type", value=self.commission_type.value, inline=False) embed.add_field(name="Payment Status", value=self.payment_status.value, inline=False) embed.add_field(name="Description", value=self.description.value, inline=False) @@ -38,6 +44,7 @@ class OrderModal(discord.ui.Modal, title="Commission Order Form"): await channel.send(embed=embed) await interaction.response.send_message("Your order has been submitted!", ephemeral=True) + class ReviewModal(discord.ui.Modal, title="Shop Review"): item_name = discord.ui.TextInput(label="What item/commission are you reviewing?") rating = discord.ui.TextInput(label="Rating (out of 10)", max_length=2) @@ -54,12 +61,12 @@ class ReviewModal(discord.ui.Modal, title="Shop Review"): channel_id = await self.cog.config.guild(guild).review_channel() if not channel_id: - await interaction.response.send_message("Review channel not set.", ephemeral=True) + await interaction.response.send_message("The review channel has not been set by an admin.", ephemeral=True) return channel = guild.get_channel(channel_id) if not isinstance(channel, discord.TextChannel): - await interaction.response.send_message("Invalid review channel.", ephemeral=True) + await interaction.response.send_message("The configured review channel is invalid.", ephemeral=True) return embed = discord.Embed(title=f"New Review for {self.item_name.value}", color=discord.Color.gold()) @@ -71,17 +78,21 @@ class ReviewModal(discord.ui.Modal, title="Shop Review"): await interaction.response.send_message("Thank you for your review!", ephemeral=True) +# --- Main Cog Class --- + class KofiShop(commands.Cog): """ An interactive front-end for a Ko-fi store. """ + def __init__(self, bot: "Red"): self.bot = bot self.config = Config.get_conf(self, identifier=1234567894, force_registration=True) default_guild = { "order_channel": None, "review_channel": None, - "waitlist_channel": None + "waitlist_channel": None, + "waitlist_entries": {} # For DataManager } self.config.register_guild(**default_guild) @@ -91,34 +102,21 @@ class KofiShop(commands.Cog): """Place an order for a commission.""" await interaction.response.send_modal(OrderModal(self)) - @app_commands.command() + @app_commands.command(name="rev") @app_commands.guild_only() async def review(self, interaction: discord.Interaction): """Leave a review for a completed commission.""" await interaction.response.send_modal(ReviewModal(self)) - @app_commands.command() - @app_commands.guild_only() - async def waitlist(self, interaction: discord.Interaction, *, item: str): - """Add an item to the waitlist.""" - guild = interaction.guild - if not guild: - return - - channel_id = await self.config.guild(guild).waitlist_channel() - if not channel_id: - await interaction.response.send_message("Waitlist channel not set.", ephemeral=True) - return - await ctx.interaction.response.send_modal(ReviewModal(self)) - @commands.hybrid_command() # type: ignore - @commands.guild_only() - @commands.admin_or_permissions(manage_guild=True) - async def waitlist(self, ctx: commands.Context, user: discord.Member, *, item: str): + @app_commands.guild_only() + async def waitlist(self, ctx: commands.Context, user: Optional[discord.Member], *, item: str): """Add a user and their requested item to the waitlist.""" - if not ctx.guild or not isinstance(ctx.channel, discord.TextChannel): - return await ctx.send("This command must be used in a server's text channel.", ephemeral=True) + if not ctx.guild: + return + target_user = user or ctx.author + waitlist_channel_id = await self.config.guild(ctx.guild).waitlist_channel() if not waitlist_channel_id: return await ctx.send("The waitlist channel has not been set by an admin.", ephemeral=True) @@ -127,16 +125,22 @@ class KofiShop(commands.Cog): if not isinstance(waitlist_channel, discord.TextChannel): return await ctx.send("The configured waitlist channel is invalid.", ephemeral=True) - message = f"**{item}** ིྀ {user.mention} ✧ in {ctx.channel.mention}" + message_to_send = f"**{item}** ིྀ {target_user.mention} ✧ in {ctx.channel.mention if isinstance(ctx.channel, discord.TextChannel) else 'this ticket'}" - channel = guild.get_channel(channel_id) - if not isinstance(channel, discord.TextChannel): - await interaction.response.send_message("Invalid waitlist channel.", ephemeral=True) - return - - embed = discord.Embed(description=f"{item} ིྀ {interaction.user.mention}✧ {interaction.channel.mention if isinstance(interaction.channel, discord.TextChannel) else ''}") - await channel.send(embed=embed) - await interaction.response.send_message("You have been added to the waitlist!", ephemeral=True) + try: + sent_message = await waitlist_channel.send(message_to_send) + # For DataManager + async with self.config.guild(ctx.guild).waitlist_entries() as entries: + entries[str(sent_message.id)] = datetime.datetime.now(datetime.timezone.utc).isoformat() + + await ctx.send(f"{target_user.mention} has been added to the waitlist for '{item}'.", ephemeral=True) + # Also send confirmation to user's ticket if it's a ticket channel + if isinstance(ctx.channel, discord.TextChannel) and "ticket" in ctx.channel.name.lower(): + await ctx.channel.send(f"You have been added to the waitlist for **{item}**.") + + except discord.Forbidden: + await ctx.send("I do not have permission to send messages in the waitlist channel.", ephemeral=True) + @commands.group(aliases=["kset"]) # type: ignore @commands.guild_only() @@ -171,3 +175,4 @@ class KofiShop(commands.Cog): async def setup(bot: "Red"): await bot.add_cog(KofiShop(bot)) +