From 0542874cfc9220e0e1b7df343e257573f85d79d9 Mon Sep 17 00:00:00 2001 From: youngcoder45 Date: Wed, 20 May 2026 23:57:18 +0530 Subject: [PATCH] Staff App fix --- cogs/staff_applications.py | 411 +++++++++++++++++++++++-------------- 1 file changed, 256 insertions(+), 155 deletions(-) diff --git a/cogs/staff_applications.py b/cogs/staff_applications.py index a6850bd..c271154 100644 --- a/cogs/staff_applications.py +++ b/cogs/staff_applications.py @@ -1,14 +1,15 @@ -import discord -from discord.ext import commands -from discord import app_commands -import aiosqlite import asyncio -import time -from pathlib import Path import logging +import time from datetime import datetime +from pathlib import Path from typing import Optional, cast +import aiosqlite +import discord +from discord import app_commands +from discord.ext import commands + logger = logging.getLogger(__name__) # Constants @@ -58,53 +59,51 @@ async def set_review_channel_id(channel_id: int) -> None: ) await db.commit() + QUESTIONS = [ { - "title": "Why do you want to become staff in CodeVerse Hub specifically, and not just any Discord server?", - "description": "Explain what you understand about this server and what makes it different from others." + "title": "Why do you want to be staff in CodeVerse Hub?", + "description": "What made you apply here specifically?", }, { - "title": "What do you think is the main purpose of CodeVerse Hub?", - "description": "In your own words, explain what this server is meant to be and what it should never turn into." + "title": "What do you think this server should mainly focus on?", + "description": "Explain what kind of community you think CodeVerse Hub should be.", }, { - "title": "How would you handle a situation where a popular or senior member is breaking rules?", - "description": "Explain clearly what steps you would take and why." + "title": "How would you handle a member breaking rules?", + "description": 'Especially if they are "well-known" or active in the server.', }, { - "title": "CodeVerse Hub focuses on quality over noise", - "description": "How would you deal with spam, low-effort content, or repeated off-topic discussions without killing community engagement." + "title": "How would you deal with spam or low-effort chatting?", + "description": "We want active chats, but not constant nonsense. Like AI generated replies/ botted messages", }, { - "title": "We have technical projects like a Linux distro, bots, and a website", - "description": "Why is staff responsibility higher in such a server compared to a casual community server." + "title": "What would you do if two members started a serious argument?", + "description": "Explain how you would calm the situation professionally. Examples of Serious Argument are like Hate towards Country/Race/Gende, etc", }, { - "title": "If you disagree with another staff member or admin’s decision, what would you do?", - "description": "Explain how you would handle disagreement without creating drama or division." + "title": "How would you help beginners without spoon-feeding them?", + "description": "We Focus on helping someone learn by themselves than spoon-feeding them something.", }, { - "title": "How would you help new members who are beginners in programming without spoon-feeding them?", - "description": "Give a practical example." + "title": "What would you do if another staff member disagreed with you?", + "description": "How would you handle it without causing drama?", }, { - "title": "What does “server reputation” mean to you?", - "description": "Why do you think CodeVerse Hub cares so much about public image, moderation quality, and external perception." + "title": "What would you do if you made a moderation mistake?", + "description": "For example warning the wrong person.", }, { - "title": "Have you ever made a mistake while moderating or managing a community?", - "description": "If yes, explain what happened and what you learned from it.\nIf no, explain how you would handle making a mistake as staff." + "title": "What qualities make someone a bad staff member?", + "description": "Mention behaviors that damage a community.", }, { - "title": "Staff here are not above the rules", - "description": "How would you react if you personally were warned or corrected by another staff member." + "title": "Why should we trust you with staff permissions?", + "description": "Convince us you can handle responsibility properly.", }, - { - "title": "Do you understand that staff actions affect long-term trust?", - "description": "Why do you think rushing decisions, abusing power, or ignoring guidelines is dangerous for a server like this." - } ] + class ApplicationReasonModal(discord.ui.Modal): def __init__( self, @@ -120,46 +119,46 @@ def __init__( self.bot = bot self.original_view = view self.review_message = review_message - + self.reason = discord.ui.TextInput( label="Reason", style=discord.TextStyle.paragraph, placeholder=f"Enter reason for {action}ing this application...", required=True, - max_length=1000 + max_length=1000, ) self.add_item(self.reason) async def on_submit(self, interaction: discord.Interaction): await interaction.response.defer() - + reason_text = self.reason.value guild = interaction.guild member = guild.get_member(self.user_id) if guild else None - + # Determine status status = "accepted" if self.action == "accept" else "denied" color = discord.Color.green() if status == "accepted" else discord.Color.red() - + # Update DB async with aiosqlite.connect(DB_PATH) as db: await db.execute( "UPDATE applications SET status = ?, reason = ? WHERE user_id = ? AND status = 'pending'", - (status, reason_text, self.user_id) + (status, reason_text, self.user_id), ) await db.commit() - + # Notify User if member: try: embed = discord.Embed( title=f"Staff Application {status.capitalize()}", - description=f"Your staff application for CodeVerse Hub has been **{status}**.", - color=color + description=f"Your staff application for CodeVerse Hub has been **{status}**. Thankyou for Applying To Codeverse Our Staff will reach you out soon, Have a Nice Day!", + color=color, ) embed.add_field(name="Reason", value=reason_text) await member.send(embed=embed) - + if status == "accepted": if guild: role = guild.get_role(STAFF_ROLE_ID) @@ -169,14 +168,14 @@ async def on_submit(self, interaction: discord.Interaction): logger.error(f"Role with ID {STAFF_ROLE_ID} not found.") except discord.Forbidden: logger.warning(f"Could not DM user {self.user_id}") - + # Update Review Message try: review_message = self.review_message if review_message is None: await interaction.followup.send( f"Application {status} saved, but I couldn't update the review message.", - ephemeral=True + ephemeral=True, ) return @@ -198,18 +197,23 @@ async def on_submit(self, interaction: discord.Interaction): ) await review_message.edit(embed=embed, view=self.original_view) - await interaction.followup.send(f"Application {status} successfully.", ephemeral=True) - + await interaction.followup.send( + f"Application {status} successfully.", ephemeral=True + ) + except Exception as e: logger.error(f"Error updating review message: {e}") - await interaction.followup.send("Error updating application status.", ephemeral=True) + await interaction.followup.send( + "Error updating application status.", ephemeral=True + ) + class ReviewView(discord.ui.View): def __init__(self, user_id: int, bot: commands.Bot): super().__init__(timeout=None) self.user_id = user_id self.bot = bot - + # Update custom IDs for the buttons to include the user_id for item in self.children: if isinstance(item, discord.ui.Button) and item.custom_id: @@ -218,18 +222,37 @@ def __init__(self, user_id: int, bot: commands.Bot): elif item.custom_id.startswith("staff_app:deny"): item.custom_id = f"staff_app:deny:{user_id}" - @discord.ui.button(label="Accept", style=discord.ButtonStyle.green, custom_id="staff_app:accept_template") - async def accept_btn(self, interaction: discord.Interaction, button: discord.ui.Button): + @discord.ui.button( + label="Accept", + style=discord.ButtonStyle.green, + custom_id="staff_app:accept_template", + ) + async def accept_btn( + self, interaction: discord.Interaction, button: discord.ui.Button + ): await interaction.response.send_modal( - ApplicationReasonModal("accept", self.user_id, self.bot, self, review_message=interaction.message) + ApplicationReasonModal( + "accept", + self.user_id, + self.bot, + self, + review_message=interaction.message, + ) ) - @discord.ui.button(label="Deny", style=discord.ButtonStyle.red, custom_id="staff_app:deny_template") - async def deny_btn(self, interaction: discord.Interaction, button: discord.ui.Button): + @discord.ui.button( + label="Deny", style=discord.ButtonStyle.red, custom_id="staff_app:deny_template" + ) + async def deny_btn( + self, interaction: discord.Interaction, button: discord.ui.Button + ): await interaction.response.send_modal( - ApplicationReasonModal("deny", self.user_id, self.bot, self, review_message=interaction.message) + ApplicationReasonModal( + "deny", self.user_id, self.bot, self, review_message=interaction.message + ) ) + class PanelView(discord.ui.View): def __init__(self, bot: commands.Bot): super().__init__(timeout=None) @@ -237,34 +260,49 @@ def __init__(self, bot: commands.Bot): async def check_monthly_limit(self, user_id: int) -> bool: """Check if user has reached monthly application limit. Returns True if limit reached.""" - current_month_start = int(datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0).timestamp()) - + current_month_start = int( + datetime.now() + .replace(day=1, hour=0, minute=0, second=0, microsecond=0) + .timestamp() + ) + async with aiosqlite.connect(DB_PATH) as db: async with db.execute( "SELECT COUNT(*) FROM applications WHERE user_id = ? AND timestamp >= ? AND status != 'denied'", - (user_id, current_month_start) + (user_id, current_month_start), ) as cursor: row = await cursor.fetchone() count = row[0] if row else 0 - + return count >= MAX_APPLICATIONS_PER_MONTH - @discord.ui.button(label="Start Application", style=discord.ButtonStyle.secondary, custom_id="staff_app:start") - async def start_app(self, interaction: discord.Interaction, button: discord.ui.Button): + @discord.ui.button( + label="Start Application", + style=discord.ButtonStyle.secondary, + custom_id="staff_app:start", + ) + async def start_app( + self, interaction: discord.Interaction, button: discord.ui.Button + ): user = interaction.user - + # Check active application async with aiosqlite.connect(DB_PATH) as db: - async with db.execute("SELECT status FROM applications WHERE user_id = ? AND status = 'pending'", (user.id,)) as cursor: + async with db.execute( + "SELECT status FROM applications WHERE user_id = ? AND status = 'pending'", + (user.id,), + ) as cursor: if await cursor.fetchone(): - await interaction.response.send_message("You already have a pending application.", ephemeral=True) + await interaction.response.send_message( + "You already have a pending application.", ephemeral=True + ) return - + # Check monthly limit if await self.check_monthly_limit(user.id): await interaction.response.send_message( f"You have reached the maximum limit of {MAX_APPLICATIONS_PER_MONTH} staff applications per month. Please try again next month.", - ephemeral=True + ephemeral=True, ) return @@ -275,22 +313,32 @@ async def start_app(self, interaction: discord.Interaction, button: discord.ui.B "You will have **45 mins** to complete the application. Once completed it will be auto-submitted to the team.\n" "Please reply with `yes`, `sure`, or `yesss` to start." ) - await interaction.response.send_message("The bot has sent you a DM for the staff application. Make sure bot DMs are not blocked.", ephemeral=True) + await interaction.response.send_message( + "The bot has sent you a DM for the staff application. Make sure bot DMs are not blocked.", + ephemeral=True, + ) except discord.Forbidden: - await interaction.response.send_message("I couldn't DM you. Please enable DMs from this server and try again.", ephemeral=True) + await interaction.response.send_message( + "I couldn't DM you. Please enable DMs from this server and try again.", + ephemeral=True, + ) return def check(m): return m.author == user and m.channel == dm_channel try: - msg = await self.bot.wait_for('message', timeout=300.0, check=check) + msg = await self.bot.wait_for("message", timeout=300.0, check=check) content = msg.content.lower() - if content not in ['yes', 'sure', 'ye', 'yesss', 'yeah']: - await dm_channel.send("Application cancelled. Please restart when you are ready.") + if content not in ["yes", "sure", "ye", "yesss", "yeah"]: + await dm_channel.send( + "Application cancelled. Please restart when you are ready." + ) return except asyncio.TimeoutError: - await dm_channel.send("Time's up! You didn't reply in time. Application cancelled.") + await dm_channel.send( + "Time's up! You didn't reply in time. Application cancelled." + ) return # Start Questions @@ -300,36 +348,46 @@ def check(m): embed = discord.Embed( title=f"Question {i}/{len(QUESTIONS)}", description=f"**{q['title']}**\n\n{q['description']}", - color=discord.Color.blue() + color=discord.Color.blue(), ) await dm_channel.send(embed=embed) - - msg = await self.bot.wait_for('message', timeout=2700.0, check=check) # 45 mins total? prompts says 45 mins to complete app. maybe per question or total? - # User prompt says "45 mins to complete application". + + msg = await self.bot.wait_for( + "message", timeout=2700.0, check=check + ) # 45 mins total? prompts says 45 mins to complete app. maybe per question or total? + # User prompt says "45 mins to complete application". # Implementing simple timeout per question might be easier but strictly it's total time. - # simpler approach: 2700s timeout per question is generous enough to cover the whole session practically, - # but technically allows 45 min idle per question. + # simpler approach: 2700s timeout per question is generous enough to cover the whole session practically, + # but technically allows 45 min idle per question. # For strict total time, we'd need to track start time. - + if len(msg.content) > 2000: - await dm_channel.send("Your answer is too long (max 2000 chars). Please try again shorter.") - msg = await self.bot.wait_for('message', timeout=2700.0, check=check) - + await dm_channel.send( + "Your answer is too long (max 2000 chars). Please try again shorter." + ) + msg = await self.bot.wait_for( + "message", timeout=2700.0, check=check + ) + answers.append(f"**Q{i}: {q['title']}**\n{msg.content}") # Submit - completed_embed = discord.Embed(title="Application Submitted", description="Your application has been submitted to the staff team for review.", color=discord.Color.green()) + completed_embed = discord.Embed( + title="Application Submitted", + description="Your application has been submitted to the staff team for review.", + color=discord.Color.green(), + ) await dm_channel.send(embed=completed_embed) - + # Save to DB full_answers = "\n\n".join(answers) async with aiosqlite.connect(DB_PATH) as db: await db.execute( "INSERT INTO applications (user_id, status, answers, timestamp) VALUES (?, ?, ?, ?)", - (user.id, "pending", full_answers, int(time.time())) + (user.id, "pending", full_answers, int(time.time())), ) await db.commit() - + # Post to Review Channel review_channel_id = await get_review_channel_id() review_channel = self.bot.get_channel(review_channel_id) @@ -338,59 +396,71 @@ def check(m): review_channel = interaction.guild.get_channel(review_channel_id) if review_channel and isinstance(review_channel, discord.abc.Messageable): - review_embed = discord.Embed(title=f"New Staff Application: {user.name}", color=0x000000) + review_embed = discord.Embed( + title=f"New Staff Application: {user.name}", color=0x000000 + ) review_embed.set_thumbnail(url=user.display_avatar.url) - + # Split answers if too long for one field # Discord field max is 1024. Description is 4096. # We can put answers in description or multiple fields. # Given 11 questions, it will be long. - - # Strategy: Create a new Embed for the content if it's too long, + + # Strategy: Create a new Embed for the content if it's too long, # but user wants "bot makes embed of that message". # Let's try to fit efficiently. - + current_chunk = "" for char in full_answers: - if len(current_chunk) > 3800: - review_embed.description = current_chunk - current_chunk = "" - # Send first part? No, embed limit. - current_chunk += char - + if len(current_chunk) > 3800: + review_embed.description = current_chunk + current_chunk = "" + # Send first part? No, embed limit. + current_chunk += char + # Better: Use fields for questions? 11 fields ok (max 25). - review_embed = discord.Embed(title=f"New Staff Application: {user.name} ({user.id})", color=0x000000) + review_embed = discord.Embed( + title=f"New Staff Application: {user.name} ({user.id})", + color=0x000000, + ) review_embed.set_thumbnail(url=user.display_avatar.url) - + for i, q_data in enumerate(QUESTIONS): - # Construct the full question text for the field name - # Combining Title and Description to ensure context is clear, truncated to fit 256 char limit - full_q_text = f"{q_data['title']} - {q_data['description']}" - if len(full_q_text) > 250: - full_q_text = full_q_text[:247] + "..." - - answer_full_content = answers[i] - # Split off the header (first line) to get just the user's answer - try: - answer_only = answer_full_content.split('\n', 1)[1] - except IndexError: + # Construct the full question text for the field name + # Combining Title and Description to ensure context is clear, truncated to fit 256 char limit + full_q_text = f"{q_data['title']} - {q_data['description']}" + if len(full_q_text) > 250: + full_q_text = full_q_text[:247] + "..." + + answer_full_content = answers[i] + # Split off the header (first line) to get just the user's answer + try: + answer_only = answer_full_content.split("\n", 1)[1] + except IndexError: answer_only = "No answer provided." - - if len(answer_only) > 1024: - answer_only = answer_only[:1021] + "..." - - review_embed.add_field(name=f"Q{i+1}: {full_q_text}", value=answer_only, inline=False) + + if len(answer_only) > 1024: + answer_only = answer_only[:1021] + "..." + + review_embed.add_field( + name=f"Q{i + 1}: {full_q_text}", value=answer_only, inline=False + ) view = ReviewView(user.id, self.bot) - await review_channel.send(content="@here", embed=review_embed, view=view) + await review_channel.send( + content="@here", embed=review_embed, view=view + ) elif review_channel: - logger.error(f"Review channel {review_channel_id} is not messageable: {type(review_channel)}") + logger.error( + f"Review channel {review_channel_id} is not messageable: {type(review_channel)}" + ) else: logger.error(f"Review channel not found: {review_channel_id}") except asyncio.TimeoutError: await dm_channel.send("Application timed out. Please try again.") + class StaffApplications(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot @@ -400,7 +470,7 @@ async def cog_load(self): # Init DB if not DB_PATH.parent.exists(): DB_PATH.parent.mkdir(parents=True, exist_ok=True) - + async with aiosqlite.connect(DB_PATH) as db: await db.execute(""" CREATE TABLE IF NOT EXISTS applications ( @@ -415,27 +485,27 @@ async def cog_load(self): await db.commit() await _ensure_settings_table() - + # Add persistent views self.bot.add_view(PanelView(self.bot)) # Note: We can't easily re-register all dynamic ReviewViews without fetching active messages or encoding data in custom_id fully. # But we can register a listener for the component interaction if we use a consistent custom_id pattern? # Using self.bot.add_view(ReviewView(user_id=... hard?)) # Standard approach for persistent dynamic views: - # Create a generic dynamic view handler or rely on the fact the bot is running. + # Create a generic dynamic view handler or rely on the fact the bot is running. # But if bot restarts, the old "Accept" buttons won't have a handler unless we register one. # I'll modify ReviewView to handle parsing from custom_id in a generic way if possible, or just be acceptable that it might not persist perfectly without complex logic. # However, `custom_id` based persistence is possible. - # I'll simple register a generic ReviewView handler that catches the pattern? + # I'll simple register a generic ReviewView handler that catches the pattern? # No, `add_view` needs an instance. # I will create a factory ReviewView that can handle any user_id if I parse it from custom_id? # No, Interaction dispatch regex is not built-in easily. - # I'll just skip complex persistence restoration for now, assuming high uptime or admin can handle legacy manually. + # I'll just skip complex persistence restoration for now, assuming high uptime or admin can handle legacy manually. # Wait, the user requirement "make it good and keep all perfect" suggests I should try. # Code: `self.bot.add_view(PersistentReviewView(self.bot))` where PersistentReviewView buttons have known custom_ids but I need unique user IDs. # The standard solution is to use `custom_id="staff_app:accept:12345"` and a `Item` with dynamic callback? # Actually, if I use `self.bot.add_view(ReviewView(user_id=0, bot=self.bot))` logic? No. - + # A workaround for persistence without restoring every view object: # Register a global view with wildcard custom_id? Not supported. # Simply: Don't use `discord.ui.button` decorator for dynamic IDs if we want persistence easily without restoring all. @@ -445,10 +515,16 @@ async def cog_load(self): # BUT, let's try to do it right. I will add a listener for interactions. pass - @app_commands.command(name="setapps", description="Set the channel where staff applications are sent") + @app_commands.command( + name="setapps", description="Set the channel where staff applications are sent" + ) @app_commands.describe(channel="Channel to send staff applications to") @app_commands.checks.has_permissions(administrator=True) - async def setapps(self, interaction: discord.Interaction, channel: Optional[discord.TextChannel] = None): + async def setapps( + self, + interaction: discord.Interaction, + channel: Optional[discord.TextChannel] = None, + ): if not interaction.guild: return await interaction.response.send_message( "This command can only be used in a server.", @@ -481,68 +557,92 @@ async def on_interaction(self, interaction: discord.Interaction): if interaction.type == discord.InteractionType.component: if not interaction.data or not isinstance(interaction.data, dict): return - custom_id = cast(dict, interaction.data).get('custom_id', '') - if custom_id.startswith('staff_app:accept:') or custom_id.startswith('staff_app:deny:'): + custom_id = cast(dict, interaction.data).get("custom_id", "") + if custom_id.startswith("staff_app:accept:") or custom_id.startswith( + "staff_app:deny:" + ): # This handles buttons from previous sessions if view is not found in memory (persistence) # But to make it work, I need to respond. # If the view was attached via `add_view`, it handles it. If not, this global listener does. - + # We need to recreate the modal logic here manually since the View instance is lost # But `send_modal` needs a Modal instance. # Checking if the interaction was already handled (if View was attached)? # If `interaction.response.is_done()` is false, we can handle it. - + # But wait, if I register the view in `cog_load` for *every* pending application, that works. # Let's load pending apps and register views. pass - + async def register_persistent_views(self): # Fetch pending applications to restore views try: async with aiosqlite.connect(DB_PATH) as db: - async with db.execute("SELECT user_id FROM applications WHERE status = 'pending'") as cursor: - async for row in cursor: - self.bot.add_view(ReviewView(row[0], self.bot)) + async with db.execute( + "SELECT user_id FROM applications WHERE status = 'pending'" + ) as cursor: + async for row in cursor: + self.bot.add_view(ReviewView(row[0], self.bot)) except Exception as e: logger.error(f"Failed to load persistent views: {e}") - @commands.hybrid_command(name="panel", description="Post the staff application panel") + @commands.hybrid_command( + name="panel", description="Post the staff application panel" + ) @commands.has_permissions(administrator=True) async def panel(self, ctx): embed = discord.Embed( title="Staff Application Open : CodeVerse Hub", - description="Are you passionate about making a difference and helping shape a positive, engaging environment within Codeverse Hub? We’re excited to announce that applications for staff positions are now open! This is your opportunity to contribute, build your leadership skills, and become a vital part of our growing community.\n\n" - "**Why Join the Codeverse Hub Staff Team?**\n" - "• **Support Community Growth:** Help welcome new members, foster meaningful discussions, and maintain a supportive atmosphere.\n" - "• **Enforce Server Guidelines:** Play a key role in ensuring that everyone enjoys a safe, inclusive, and respectful space.\n" - "• **Gain Experience:** Develop valuable moderation, organizational, and communication skills by working alongside a dedicated team.\n" - "• **Make Your Mark:** Influence the future direction of Codeverse Hub by sharing your ideas and feedback.\n\n" - "**How to Apply**\n" - "Interested candidates should click the Staff Application button below and carefully complete the form provided. Please take your time to answer each question thoughtfully–we want to understand your strengths, interests, and vision for the server.\n\n" - "**Step 1:** Click the \"Staff Application\" button below.\n" - "**Step 2:** Fill out the application form with detailed, honest answers.\n" - "**Step 3:** Submit your application and await our review.\n\n" - "**Important Notes**\n" - "• All applications will be thoroughly reviewed by our leadership team.\n" - "• Selected candidates will be contacted directly for further steps\n" - "• If you have any questions, feel free to reach out to existing staff members via tickets\n\n" - "Thank you for showing interest in joining our staff team! Your commitment helps keep Codeverse Hub welcoming, fun, and safe for everyone.", - color=discord.Color.blue() # Or whatever color fits "agrey liek transparent button" mood + description="Are you interested in helping manage and improve CodeVerse Hub? " + "We are currently looking for responsible, active, and mature members to join the staff team.\n\n" + "**Eligibility Requirements**\n" + "- Must be reasonably active in the server\n" + "- Must be atleast 16 years old OR Mature Enough\n" + "- Must understand and follow server rules properly\n" + "- Must communicate respectfully with members and staff\n" + "- Previous moderation experience is helpful but not necesarily required\n\n" + "**How to Apply**\n" + '**Step 1:** Click the "Staff Application" button below\n' + "**Step 2:** Carefully answer all questions honestly and properly\n" + "**Step 3:** Submit your application and wait for review\n\n" + "**Please Read Before Applying**\n" + "If you are looking to apply, please make sure you read the questions correctly and answer them properly.\n\n" + "We **will reject** applications that fail to answer the actual questions being asked.\n" + "Even if you write something in the answer field, it still needs to properly address the question.\n\n" + "We can usually tell when applications are heavily written by ChatGPT or other AI tools. " + "Using AI for grammar fixes or formatting is completely fine, but the actual thoughts and answers should still be your own.\n\n" + "**Important Notes**\n" + "- Applications are reviewed carefully by the Management\n" + "- Staff members are expected to stay professional and mature\n" + "- Abuse of permissions or irresponsible behavior will not be tolerated\n" + "- Selected applicants may be contacted for further discussion\n" + "- If you have questions, contact existing staff through tickets ONLY\n\n" + "Thank you for your interest in helping grow and improve CodeVerse Hub.", + color=discord.Color(0xF90473), ) await ctx.send(embed=embed, view=PanelView(self.bot)) - @app_commands.command(name="applications", description="View a user's staff applications") + @app_commands.command( + name="applications", description="View a user's staff applications" + ) @app_commands.describe(user="The user to check applications for") async def applications(self, interaction: discord.Interaction, user: discord.User): async with aiosqlite.connect(DB_PATH) as db: - async with db.execute("SELECT id, status, timestamp, reason FROM applications WHERE user_id = ? ORDER BY timestamp DESC", (user.id,)) as cursor: + async with db.execute( + "SELECT id, status, timestamp, reason FROM applications WHERE user_id = ? ORDER BY timestamp DESC", + (user.id,), + ) as cursor: rows = await cursor.fetchall() - + if not rows: - await interaction.response.send_message(f"No applications found for {user.mention}.", ephemeral=True) + await interaction.response.send_message( + f"No applications found for {user.mention}.", ephemeral=True + ) return - embed = discord.Embed(title=f"Applications for {user.name}", color=discord.Color.blurple()) + embed = discord.Embed( + title=f"Applications for {user.name}", color=discord.Color.blurple() + ) for row in rows: app_id, status, timestamp, reason = row date_str = f"" @@ -550,11 +650,12 @@ async def applications(self, interaction: discord.Interaction, user: discord.Use embed.add_field( name=f"ID: {app_id} - {status.upper()}", value=f"Date: {date_str}{reason_str}", - inline=False + inline=False, ) - + await interaction.response.send_message(embed=embed, ephemeral=True) + async def setup(bot: commands.Bot): cog = StaffApplications(bot) await bot.add_cog(cog)