Add DataManager and Logging cogs; enhance existing features
- Updated `.gitignore` to include `cython_debug/`. - Introduced `DataManager` class for data retention policies. - Added logging functionality for server events in `logging.py`. - Refactored `hiring.py` and improved order submission in `kofishop.py`. - Added new cogs: `MORS`, `ServiceReview`, and `StaffMsg`. - Updated `info.json` for new cogs and modified `__init__.py` files for setup.
This commit is contained in:
276
hiring/hiring.py
276
hiring/hiring.py
@@ -1,10 +1,10 @@
|
||||
import discord
|
||||
from redbot.core import commands, Config
|
||||
from redbot.core.bot import Red
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from redbot.core import commands, Config, app_commands
|
||||
from typing import Literal, Optional, TYPE_CHECKING, Type
|
||||
import datetime
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from hiring.hiring import Hiring
|
||||
from redbot.core.bot import Red
|
||||
|
||||
# --- Modals for the Application Forms ---
|
||||
|
||||
@@ -12,203 +12,224 @@ class StaffApplicationModal(discord.ui.Modal, title="Staff Application"):
|
||||
chosen_plan = discord.ui.TextInput(label="What plan are you interested in?")
|
||||
tox_level = discord.ui.TextInput(label="What is your toxicity level tolerance?")
|
||||
describe_server = discord.ui.TextInput(label="Please describe your server.", style=discord.TextStyle.paragraph)
|
||||
server_link = discord.ui.TextInput(label="Server Link")
|
||||
server_link = discord.ui.TextInput(label="Server Invite Link")
|
||||
|
||||
def __init__(self, ticket_channel: discord.TextChannel):
|
||||
super().__init__()
|
||||
self.ticket_channel = ticket_channel
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
embed = discord.Embed(
|
||||
title="New Staff Application",
|
||||
description=f"Submitted by {interaction.user.mention}",
|
||||
color=0xadd8e6
|
||||
)
|
||||
cog: Optional["Hiring"] = interaction.client.get_cog("Hiring") # type: ignore
|
||||
if not cog:
|
||||
await interaction.response.send_message("Hiring cog not loaded.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(title="New Staff Application", color=discord.Color.blue())
|
||||
embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar.url)
|
||||
embed.add_field(name="Chosen Plan", value=self.chosen_plan.value, inline=False)
|
||||
embed.add_field(name="Toxicity Level", value=self.tox_level.value, inline=False)
|
||||
embed.add_field(name="Server Description", value=self.describe_server.value, inline=False)
|
||||
embed.add_field(name="Server Link", value=self.server_link.value, inline=False)
|
||||
|
||||
await self.ticket_channel.send(embed=embed)
|
||||
await interaction.response.send_message("Your staff application has been submitted.", ephemeral=True)
|
||||
|
||||
await interaction.response.send_message("Your application has been submitted!", ephemeral=True)
|
||||
if isinstance(interaction.channel, discord.TextChannel):
|
||||
await interaction.channel.send(embed=embed)
|
||||
|
||||
|
||||
class PMApplicationModal(discord.ui.Modal, title="PM Application"):
|
||||
ad = discord.ui.TextInput(label="Please provide your server ad.", style=discord.TextStyle.paragraph)
|
||||
reqs = discord.ui.TextInput(label="What are your requirements?")
|
||||
ad = discord.ui.TextInput(label="Your Ad", style=discord.TextStyle.paragraph)
|
||||
reqs = discord.ui.TextInput(label="Your Requirements", style=discord.TextStyle.paragraph)
|
||||
tox_level = discord.ui.TextInput(label="What is your toxicity level tolerance?")
|
||||
chosen_plan = discord.ui.TextInput(label="What plan are you interested in?")
|
||||
|
||||
def __init__(self, ticket_channel: discord.TextChannel):
|
||||
super().__init__()
|
||||
self.ticket_channel = ticket_channel
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
embed = discord.Embed(
|
||||
title="New PM Application",
|
||||
description=f"Submitted by {interaction.user.mention}",
|
||||
color=0xadd8e6
|
||||
)
|
||||
embed.add_field(name="Server Ad", value=f"```\n{self.ad.value}\n```", inline=False)
|
||||
embed.add_field(name="Requirements", value=self.reqs.value, inline=False)
|
||||
cog: Optional["Hiring"] = interaction.client.get_cog("Hiring") # type: ignore
|
||||
if not cog:
|
||||
await interaction.response.send_message("Hiring cog not loaded.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(title="New PM Application", color=discord.Color.green())
|
||||
embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar.url)
|
||||
embed.add_field(name="Ad", value=f"```\n{self.ad.value}\n```", inline=False)
|
||||
embed.add_field(name="Requirements", value=f"```\n{self.reqs.value}\n```", inline=False)
|
||||
embed.add_field(name="Toxicity Level", value=self.tox_level.value, inline=False)
|
||||
embed.add_field(name="Chosen Plan", value=self.chosen_plan.value, inline=False)
|
||||
await interaction.response.send_message("Your application has been submitted!", ephemeral=True)
|
||||
if isinstance(interaction.channel, discord.TextChannel):
|
||||
await interaction.channel.send(embed=embed)
|
||||
await self.ticket_channel.send(embed=embed)
|
||||
await interaction.response.send_message("Your PM application has been submitted.", ephemeral=True)
|
||||
|
||||
class HPMApplicationModal(discord.ui.Modal, title="HPM Application"):
|
||||
ad = discord.ui.TextInput(label="Please provide your server ad.", style=discord.TextStyle.paragraph)
|
||||
reqs = discord.ui.TextInput(label="What are your requirements?")
|
||||
|
||||
ad = discord.ui.TextInput(label="Your Ad", style=discord.TextStyle.paragraph)
|
||||
reqs = discord.ui.TextInput(label="Your Requirements", style=discord.TextStyle.paragraph)
|
||||
|
||||
def __init__(self, ticket_channel: discord.TextChannel):
|
||||
super().__init__()
|
||||
self.ticket_channel = ticket_channel
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
embed = discord.Embed(
|
||||
title="New HPM Application",
|
||||
description=f"Submitted by {interaction.user.mention}",
|
||||
color=0xadd8e6
|
||||
)
|
||||
embed.add_field(name="Server Ad", value=f"```\n{self.ad.value}\n```", inline=False)
|
||||
embed.add_field(name="Requirements", value=self.reqs.value, inline=False)
|
||||
await interaction.response.send_message("Your application has been submitted!", ephemeral=True)
|
||||
if isinstance(interaction.channel, discord.TextChannel):
|
||||
await interaction.channel.send(embed=embed)
|
||||
cog: Optional["Hiring"] = interaction.client.get_cog("Hiring") # type: ignore
|
||||
if not cog:
|
||||
await interaction.response.send_message("Hiring cog not loaded.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(title="New HPM Application", color=discord.Color.purple())
|
||||
embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar.url)
|
||||
embed.add_field(name="Ad", value=f"```\n{self.ad.value}\n```", inline=False)
|
||||
embed.add_field(name="Requirements", value=f"```\n{self.reqs.value}\n```", inline=False)
|
||||
await self.ticket_channel.send(embed=embed)
|
||||
await interaction.response.send_message("Your HPM application has been submitted.", ephemeral=True)
|
||||
|
||||
# --- Reusable Ticket Creation Logic ---
|
||||
async def create_ticket(interaction: discord.Interaction, role_type: str, category_id: Optional[int], modal: discord.ui.Modal):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This interaction must be used in a server.", ephemeral=True)
|
||||
# --- Views with Buttons ---
|
||||
|
||||
async def create_ticket(interaction: discord.Interaction, ticket_type: str, modal_class: Type[discord.ui.Modal]):
|
||||
"""Helper function to create a ticket, used by multiple views."""
|
||||
cog: Optional["Hiring"] = interaction.client.get_cog("Hiring") # type: ignore
|
||||
if not cog:
|
||||
await interaction.response.send_message("The Hiring cog is not loaded.", ephemeral=True)
|
||||
return
|
||||
|
||||
if not category_id:
|
||||
await interaction.response.send_message(f"The category for '{role_type}' applications has not been set by an admin.", ephemeral=True)
|
||||
guild = interaction.guild
|
||||
if not guild:
|
||||
await interaction.response.send_message("This action can only be performed in a server.", ephemeral=True)
|
||||
return
|
||||
|
||||
category = interaction.guild.get_channel(category_id)
|
||||
if not isinstance(category, discord.CategoryChannel):
|
||||
await interaction.response.send_message(f"The category for '{role_type}' applications is invalid or has been deleted.", ephemeral=True)
|
||||
|
||||
category_id_raw = await cog.config.guild(guild).get_raw(f"{ticket_type}_category")
|
||||
if not isinstance(category_id_raw, int):
|
||||
await interaction.response.send_message(f"The category for '{ticket_type}' applications has not been set correctly.", ephemeral=True)
|
||||
return
|
||||
|
||||
ticket_name = f"{role_type}-app-{interaction.user.name}"
|
||||
|
||||
category = guild.get_channel(category_id_raw)
|
||||
if not isinstance(category, discord.CategoryChannel):
|
||||
await interaction.response.send_message(f"The configured category for '{ticket_type}' is invalid.", ephemeral=True)
|
||||
return
|
||||
|
||||
assert isinstance(interaction.user, discord.Member)
|
||||
|
||||
try:
|
||||
thread_name = f"{ticket_type}-application-{interaction.user.name}"
|
||||
overwrites = {
|
||||
interaction.guild.default_role: discord.PermissionOverwrite(read_messages=False),
|
||||
interaction.user: discord.PermissionOverwrite(read_messages=True, send_messages=True),
|
||||
interaction.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True)
|
||||
guild.default_role: discord.PermissionOverwrite(read_messages=False),
|
||||
interaction.user: discord.PermissionOverwrite(read_messages=True, send_messages=True)
|
||||
}
|
||||
ticket_channel = await interaction.guild.create_text_channel(
|
||||
name=ticket_name,
|
||||
category=category,
|
||||
overwrites=overwrites
|
||||
)
|
||||
await ticket_channel.send(f"Welcome {interaction.user.mention}! Please fill out the form to complete your application.")
|
||||
await interaction.response.send_modal(modal)
|
||||
ticket_channel = await category.create_text_channel(name=thread_name, overwrites=overwrites)
|
||||
|
||||
modal_instance = modal_class(ticket_channel) # type: ignore
|
||||
await interaction.response.send_modal(modal_instance)
|
||||
|
||||
async with cog.config.guild(guild).closed_applications() as closed_apps:
|
||||
closed_apps[str(ticket_channel.id)] = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
|
||||
except discord.Forbidden:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send("I don't have permission to create channels in that category.", ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message("I don't have permission to create channels in that category.", ephemeral=True)
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.send_message("I don't have permission to create channels in that category.", ephemeral=True)
|
||||
except Exception as e:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(f"An unexpected error occurred: {e}", ephemeral=True)
|
||||
else:
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.send_message(f"An unexpected error occurred: {e}", ephemeral=True)
|
||||
|
||||
|
||||
# --- Button Views for Commands ---
|
||||
|
||||
class HireView(discord.ui.View):
|
||||
def __init__(self, cog: "Hiring"):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.cog = cog
|
||||
|
||||
@discord.ui.button(label="Staff", style=discord.ButtonStyle.primary, custom_id="staff_apply_button")
|
||||
@discord.ui.button(label="Staff", style=discord.ButtonStyle.primary, custom_id="staff_apply_button_persistent")
|
||||
async def staff_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if not interaction.guild: return
|
||||
category_id = await self.cog.config.guild(interaction.guild).staff_category()
|
||||
await create_ticket(interaction, "staff", category_id, StaffApplicationModal())
|
||||
await create_ticket(interaction, "staff", StaffApplicationModal)
|
||||
|
||||
@discord.ui.button(label="PM", style=discord.ButtonStyle.secondary, custom_id="pm_apply_button")
|
||||
@discord.ui.button(label="PM", style=discord.ButtonStyle.secondary, custom_id="pm_apply_button_persistent")
|
||||
async def pm_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if not interaction.guild: return
|
||||
category_id = await self.cog.config.guild(interaction.guild).pm_category()
|
||||
await create_ticket(interaction, "pm", category_id, PMApplicationModal())
|
||||
await create_ticket(interaction, "pm", PMApplicationModal)
|
||||
|
||||
@discord.ui.button(label="HPM", style=discord.ButtonStyle.success, custom_id="hpm_apply_button")
|
||||
@discord.ui.button(label="HPM", style=discord.ButtonStyle.success, custom_id="hpm_apply_button_persistent")
|
||||
async def hpm_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if not interaction.guild: return
|
||||
category_id = await self.cog.config.guild(interaction.guild).hpm_category()
|
||||
await create_ticket(interaction, "hpm", category_id, HPMApplicationModal())
|
||||
await create_ticket(interaction, "hpm", HPMApplicationModal)
|
||||
|
||||
|
||||
class WorkView(discord.ui.View):
|
||||
def __init__(self, cog: "Hiring"):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.cog = cog
|
||||
|
||||
@discord.ui.button(label="Apply for PM Position", style=discord.ButtonStyle.blurple, custom_id="work_apply_button")
|
||||
async def work_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if not interaction.guild: return
|
||||
category_id = await self.cog.config.guild(interaction.guild).pm_category()
|
||||
await create_ticket(interaction, "pm", category_id, PMApplicationModal())
|
||||
@discord.ui.button(label="Apply for PM Position", style=discord.ButtonStyle.green, custom_id="work_apply_pm_persistent")
|
||||
async def work_apply_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
await create_ticket(interaction, "pm", PMApplicationModal)
|
||||
|
||||
|
||||
# --- Main Cog Class ---
|
||||
|
||||
class Hiring(commands.Cog):
|
||||
"""
|
||||
A cog for managing staff and PM applications.
|
||||
A cog for handling staff hiring applications.
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
def __init__(self, bot: "Red"):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=1122334455, force_registration=True)
|
||||
|
||||
default_guild = {
|
||||
"hpm_category": None,
|
||||
"pm_category": None,
|
||||
"staff_category": None,
|
||||
"work_channel": None # New setting for the /work command channel
|
||||
"pm_category": None,
|
||||
"hpm_category": None,
|
||||
"work_channel": None,
|
||||
"closed_applications": {}
|
||||
}
|
||||
self.config.register_guild(**default_guild)
|
||||
# We need to make sure the views are persistent so buttons work after a restart
|
||||
self.bot.add_view(HireView(self))
|
||||
self.bot.add_view(WorkView(self))
|
||||
|
||||
async def cog_load(self):
|
||||
self.bot.add_view(HireView())
|
||||
self.bot.add_view(WorkView())
|
||||
|
||||
# --- Commands ---
|
||||
@commands.hybrid_command() # type: ignore
|
||||
@commands.guild_only()
|
||||
@app_commands.guild_only()
|
||||
@commands.admin_or_permissions(manage_guild=True)
|
||||
async def hire(self, ctx: commands.Context):
|
||||
"""Post the application message with buttons in the current channel."""
|
||||
"""Sends the hiring application view."""
|
||||
view = HireView()
|
||||
embed = discord.Embed(
|
||||
title="Start an Application",
|
||||
description="Click a button below to open a ticket for the role you are interested in.",
|
||||
color=0xadd8e6
|
||||
title="Hiring Applications",
|
||||
description="Please select the position you are applying for below.",
|
||||
color=await ctx.embed_color()
|
||||
)
|
||||
await ctx.send(embed=embed, view=HireView(self))
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
@commands.hybrid_command() # type: ignore
|
||||
@commands.guild_only()
|
||||
@app_commands.guild_only()
|
||||
@commands.admin_or_permissions(manage_guild=True)
|
||||
async def work(self, ctx: commands.Context):
|
||||
"""Post the PM hiring message in the configured work channel."""
|
||||
if not ctx.guild:
|
||||
return
|
||||
work_channel_id = await self.config.guild(ctx.guild).work_channel()
|
||||
if not work_channel_id:
|
||||
await ctx.send("The work channel has not been set. Please use `[p]hiringset workchannel` to set it.", ephemeral=True)
|
||||
return
|
||||
|
||||
work_channel = ctx.guild.get_channel(work_channel_id)
|
||||
if not isinstance(work_channel, discord.TextChannel):
|
||||
await ctx.send("The configured work channel is invalid or has been deleted.", ephemeral=True)
|
||||
"""Posts the persistent PM application button."""
|
||||
guild = ctx.guild
|
||||
if not guild:
|
||||
return
|
||||
|
||||
work_channel_id = await self.config.guild(guild).work_channel()
|
||||
if not work_channel_id:
|
||||
await ctx.send("The work channel has not been set. Please use `/hiringset workchannel`.")
|
||||
return
|
||||
|
||||
channel = guild.get_channel(work_channel_id)
|
||||
if not isinstance(channel, discord.TextChannel):
|
||||
await ctx.send("The configured work channel is invalid.")
|
||||
return
|
||||
|
||||
view = WorkView()
|
||||
embed = discord.Embed(
|
||||
title="Now Hiring: Partnership Managers",
|
||||
description="We are for talented Partnership Managers (PMs) to join our team. If you are interested in applying, please click the button below to begin the application process.",
|
||||
color=0xadd8e6
|
||||
title="Partnership Manager Applications",
|
||||
description="Click the button below to apply for a Partnership Manager (PM) position.",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
try:
|
||||
await work_channel.send(embed=embed, view=WorkView(self))
|
||||
await ctx.send(f"Hiring message posted in {work_channel.mention}.", ephemeral=True)
|
||||
await channel.send(embed=embed, view=view)
|
||||
await ctx.send(f"Application message posted in {channel.mention}.", ephemeral=True)
|
||||
except discord.Forbidden:
|
||||
await ctx.send(f"I don't have permission to send messages in {work_channel.mention}.", ephemeral=True)
|
||||
await ctx.send("I don't have permission to send messages in that channel.", ephemeral=True)
|
||||
|
||||
|
||||
# --- Settings Commands ---
|
||||
@@ -216,38 +237,41 @@ class Hiring(commands.Cog):
|
||||
@commands.guild_only()
|
||||
@commands.admin_or_permissions(manage_guild=True)
|
||||
async def hiringset(self, ctx: commands.Context):
|
||||
"""Configure the Hiring cog settings."""
|
||||
"""Configure Hiring settings."""
|
||||
pass
|
||||
|
||||
@hiringset.command(name="staffcategory")
|
||||
async def set_staff_category(self, ctx: commands.Context, category: discord.CategoryChannel):
|
||||
"""Set the category for Staff applications."""
|
||||
if not ctx.guild: return
|
||||
if not ctx.guild:
|
||||
return
|
||||
await self.config.guild(ctx.guild).staff_category.set(category.id)
|
||||
await ctx.send(f"Staff application category set to **{category.name}**.")
|
||||
|
||||
@hiringset.command(name="pmcategory")
|
||||
async def set_pm_category(self, ctx: commands.Context, category: discord.CategoryChannel):
|
||||
"""Set the category for PM applications."""
|
||||
if not ctx.guild: return
|
||||
if not ctx.guild:
|
||||
return
|
||||
await self.config.guild(ctx.guild).pm_category.set(category.id)
|
||||
await ctx.send(f"PM application category set to **{category.name}**.")
|
||||
|
||||
@hiringset.command(name="hpmcategory")
|
||||
async def set_hpm_category(self, ctx: commands.Context, category: discord.CategoryChannel):
|
||||
"""Set the category for HPM applications."""
|
||||
if not ctx.guild: return
|
||||
if not ctx.guild:
|
||||
return
|
||||
await self.config.guild(ctx.guild).hpm_category.set(category.id)
|
||||
await ctx.send(f"HPM application category set to **{category.name}**.")
|
||||
|
||||
|
||||
@hiringset.command(name="workchannel")
|
||||
async def set_work_channel(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
"""Set the channel where the /work hiring message will be posted."""
|
||||
if not ctx.guild: return
|
||||
"""Set the channel for the /work command announcements."""
|
||||
if not ctx.guild:
|
||||
return
|
||||
await self.config.guild(ctx.guild).work_channel.set(channel.id)
|
||||
await ctx.send(f"The work channel has been set to {channel.mention}.")
|
||||
await ctx.send(f"Work announcement channel set to {channel.mention}.")
|
||||
|
||||
|
||||
async def setup(bot: Red):
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Hiring(bot))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user