import discord 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?") payment_status = discord.ui.TextInput(label="Is this a Free or Paid commission?") description = discord.ui.TextInput(label="Description", style=discord.TextStyle.paragraph) questions = discord.ui.TextInput(label="Any questions?", style=discord.TextStyle.paragraph, required=False) def __init__(self, cog: "KofiShop"): super().__init__() self.cog = cog async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild if not guild: return channel_id = await self.cog.config.guild(guild).order_channel() if not channel_id: 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("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) if self.questions.value: embed.add_field(name="Questions", value=self.questions.value, inline=False) 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) review_text = discord.ui.TextInput(label="Your Review", style=discord.TextStyle.paragraph, max_length=1000) def __init__(self, cog: "KofiShop"): super().__init__() self.cog = cog async def on_submit(self, interaction: discord.Interaction): guild = interaction.guild if not guild: return channel_id = await self.cog.config.guild(guild).review_channel() if not channel_id: 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("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()) embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar.url) embed.add_field(name="Rating", value=f"{self.rating.value}/10") embed.add_field(name="Review", value=self.review_text.value, inline=False) await channel.send(embed=embed) 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_entries": {} # For DataManager } self.config.register_guild(**default_guild) @app_commands.command() @app_commands.guild_only() async def order(self, interaction: discord.Interaction): """Place an order for a commission.""" await interaction.response.send_modal(OrderModal(self)) @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)) @commands.hybrid_command() # type: ignore @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: 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) waitlist_channel = ctx.guild.get_channel(waitlist_channel_id) if not isinstance(waitlist_channel, discord.TextChannel): return await ctx.send("The configured waitlist channel is invalid.", ephemeral=True) message_to_send = f"**{item}** ིྀ {target_user.mention} ✧ in {ctx.channel.mention if isinstance(ctx.channel, discord.TextChannel) else 'this ticket'}" 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() @commands.admin_or_permissions(manage_guild=True) async def kofiset(self, ctx: commands.Context): """Configure KofiShop settings.""" pass @kofiset.command(name="orderchannel") async def set_order_channel(self, ctx: commands.Context, channel: discord.TextChannel): """Set the channel for new orders.""" if not ctx.guild: return await self.config.guild(ctx.guild).order_channel.set(channel.id) await ctx.send(f"Order channel set to {channel.mention}") @kofiset.command(name="reviewchannel") async def set_review_channel(self, ctx: commands.Context, channel: discord.TextChannel): """Set the channel for new reviews.""" if not ctx.guild: return await self.config.guild(ctx.guild).review_channel.set(channel.id) await ctx.send(f"Review channel set to {channel.mention}") @kofiset.command(name="waitlistchannel") async def set_waitlist_channel(self, ctx: commands.Context, channel: discord.TextChannel): """Set the channel for waitlist notifications.""" if not ctx.guild: return await self.config.guild(ctx.guild).waitlist_channel.set(channel.id) await ctx.send(f"Waitlist channel set to {channel.mention}") async def setup(bot: "Red"): await bot.add_cog(KofiShop(bot))