KaniumCogs/recruitmentCog/recruitment.py

228 lines
9.5 KiB
Python
Raw Normal View History

import asyncio
import datetime
2023-03-16 19:13:37 +01:00
import re
from typing import List
2023-03-16 19:56:28 +01:00
from datetime import timedelta
2023-03-16 19:13:37 +01:00
2023-03-12 22:55:57 +01:00
import discord
2023-03-12 22:58:29 +01:00
from redbot.core import Config, checks, commands
from redbot.core.bot import Red
2023-03-16 19:13:37 +01:00
from redbot.core.utils.antispam import AntiSpam
def sanitize_input(input_text: str) -> str:
"""Sanitize input to remove mentions, links, and special characters."""
sanitized_text = re.sub(r'<@!?&?(\d+)>', '', input_text)
sanitized_text = re.sub(r'http\S+', '', sanitized_text)
2023-03-16 19:28:00 +01:00
sanitized_text = re.sub(r'([^\w\s.,?!`~@#$%^&*()_+=-])', '', sanitized_text)
2023-03-16 19:13:37 +01:00
return sanitized_text
2023-03-12 21:41:08 +01:00
class Recruitment(commands.Cog):
2023-03-12 22:58:29 +01:00
"""A cog that lets a user send a membership application."""
2023-03-12 21:41:08 +01:00
2023-03-12 22:55:57 +01:00
def __init__(self, bot: Red):
self.bot = bot
2023-03-12 23:55:46 +01:00
self.message: str = ''
2023-03-16 19:13:37 +01:00
self.config = Config.get_conf(self, identifier=101101101101001110101) # Replace with your own unique identifier
default_guild = {"guild_id": 274657393936302080, "application_channel_id": None}
self.config.register_guild(**default_guild)
2023-03-16 19:13:37 +01:00
self.antispam = {}
async def cog_check(self, ctx: commands.Context):
2023-03-16 20:06:19 +01:00
if await ctx.bot.is_admin(ctx.author):
return True
2023-03-16 20:09:22 +01:00
2023-03-16 19:34:01 +01:00
guild_id = ctx.guild.id
if guild_id not in self.antispam:
2023-03-16 20:06:19 +01:00
self.antispam[guild_id] = AntiSpam([(datetime.timedelta(hours=1), 1)])
2023-03-16 19:34:01 +01:00
antispam = self.antispam[guild_id]
2023-03-16 20:09:22 +01:00
2023-03-16 19:55:12 +01:00
if antispam.spammy:
2023-03-16 20:09:22 +01:00
try:
2023-06-01 22:19:05 +02:00
await ctx.message.delete(delay=0)
2023-03-16 20:09:22 +01:00
except discord.Forbidden:
pass
2023-03-16 20:10:42 +01:00
await ctx.author.send("Please wait for an hour before sending another application.")
2023-03-16 19:39:10 +01:00
return False
2023-03-16 20:09:22 +01:00
2023-03-16 19:55:12 +01:00
antispam.stamp()
2023-03-16 19:39:10 +01:00
return True
2023-03-16 19:34:01 +01:00
2023-03-16 20:09:22 +01:00
@commands.guild_only()
@checks.admin_or_permissions(manage_guild=True)
@commands.group(name="setapplicationschannel", pass_context=True, no_pm=True)
async def setapplicationschannel(self, ctx: commands.Context):
"""Set the channel where applications will be sent."""
if ctx.invoked_subcommand is None:
guild = ctx.guild
channel = ctx.channel
await self.config.guild(guild).guild_id.set(guild.id)
await self.config.guild(guild).application_channel_id.set(channel.id)
await ctx.send(f"Application channel set to {channel.mention}.")
@setapplicationschannel.command(name="clear")
async def clear_application_channel(self, ctx: commands.Context):
"""Clear the current application channel."""
guild = ctx.guild
await self.config.guild(guild).clear_raw("application_channel_id")
await ctx.send("Application channel cleared.")
2023-03-12 22:58:29 +01:00
2023-03-12 23:55:46 +01:00
@commands.group(name="application", usage="[text]", invoke_without_command=True)
async def application(self, ctx: commands.Context, *, _application: str = ""):
2023-03-16 19:13:37 +01:00
# Input validation and sanitization for _application
_application = sanitize_input(_application)
if len(_application) > 2000:
await ctx.send("Your application is too long. Please limit it to 2000 characters.")
return
2023-03-13 18:44:48 +01:00
guild_id = await self.get_guild_id(ctx)
guild = discord.utils.get(self.bot.guilds, id=guild_id)
if guild is None:
await ctx.send(f"The guild with ID {guild_id} could not be found.")
return
2023-03-13 18:44:48 +01:00
author = ctx.author
if author.guild != guild:
await ctx.send(f"You need to be in the {guild.name} server to submit an application.")
return
2023-03-13 18:53:11 +01:00
if await self.check_author_is_member_and_channel_is_dm(ctx):
2023-03-13 13:10:07 +01:00
await self.interactive_application(author)
2023-03-13 18:36:33 +01:00
async def get_guild_id(self, ctx: commands.Context) -> int:
2023-03-13 18:48:50 +01:00
guild_id = await self.config.guild(ctx.author.guild).guild_id()
2023-03-13 18:36:33 +01:00
return guild_id
2023-03-13 18:53:11 +01:00
async def check_author_is_member_and_channel_is_dm(self, ctx: commands.Context) -> bool:
if not isinstance(ctx.author, discord.Member):
await ctx.send("You need to join the server before your application can be processed.")
return False
2023-03-12 21:41:08 +01:00
if not isinstance(ctx.channel, discord.DMChannel):
2023-03-13 18:44:48 +01:00
try:
await ctx.message.delete()
except:
pass
2023-03-13 18:53:11 +01:00
await self.interactive_application(ctx)
2023-03-13 13:10:07 +01:00
return False
return True
2023-03-12 22:58:29 +01:00
2023-03-13 18:44:48 +01:00
2023-03-13 18:53:11 +01:00
async def interactive_application(self, ctx: commands.Context):
2023-03-12 22:58:29 +01:00
"""Ask the user several questions to create an application."""
2023-03-13 18:53:11 +01:00
author = ctx.author
2023-03-13 13:33:26 +01:00
embed = discord.Embed(
2023-03-13 15:40:22 +01:00
title="+++ KANIUM APPLICATION SYSTEM +++",
2023-03-13 13:33:26 +01:00
description="Ah, you wish to apply for Kanium membership. Very well, understand that This process is very important to us so we expect you to put effort in and be glorious about it. Let us begin!",
color=discord.Color.green(),
)
await author.send(embed=embed)
2023-03-13 15:40:22 +01:00
answers = await self.ask_questions(author)
2023-03-12 22:55:57 +01:00
if not answers:
return
2023-03-12 22:55:57 +01:00
embeddedApplication = await self.format_application(answers, author)
2023-03-13 13:10:07 +01:00
# Call the sendApplication to check if the author is a member of the guild and send the application if they are
await self.sendApplication(author, embeddedApplication)
2023-03-12 23:14:26 +01:00
2023-03-12 22:55:57 +01:00
2023-03-13 13:10:07 +01:00
async def sendApplication(self, author: discord.Member, embeddedApplication: discord.Embed):
2023-03-13 01:21:02 +01:00
# Check if the author is a member of the guild
guild = author.guild
2023-03-13 01:21:02 +01:00
member = guild.get_member(author.id)
if member is None:
await author.send("You need to join the server before your application can be processed.")
return
2023-03-13 18:58:47 +01:00
2023-03-13 13:10:07 +01:00
# Send the embed to the application channel
2023-03-13 18:58:47 +01:00
application_channel_id = await self.config.guild(guild).application_channel_id()
if not application_channel_id:
await author.send("The application channel has not been set. Please use the `setapplicationschannel` command to set it.")
return
application_channel = guild.get_channel(application_channel_id)
if application_channel is None:
await author.send(f"The application channel with ID {application_channel_id} could not be found.")
return
try:
message = await application_channel.send(embed=embeddedApplication)
except discord.Forbidden:
await author.send("I do not have permission to send messages in the application channel.")
return
2023-03-13 13:10:07 +01:00
# Add reactions to the message
try:
await self.add_reactions(message)
except discord.Forbidden:
await author.send("I do not have permission to add reactions to messages in the application channel.")
return
2023-03-13 13:10:07 +01:00
2023-03-13 01:21:02 +01:00
# Assign the Trial role to the author
2023-03-13 01:10:31 +01:00
role = guild.get_role(531181363420987423)
try:
await member.add_roles(role)
except discord.Forbidden:
await author.send("I do not have permission to assign roles.")
return
2023-03-13 01:10:31 +01:00
await author.send("Thank you for submitting your application! You have been given the 'Trial' role.")
2023-03-13 13:10:07 +01:00
async def add_reactions(self, message: discord.Message):
reactions = ["", "", ""]
for reaction in reactions:
await message.add_reaction(reaction)
2023-03-12 22:55:57 +01:00
async def format_application(self, answers: List[str], author: discord.Member) -> discord.Embed:
2023-03-12 22:58:29 +01:00
"""Format the application answers into an embed."""
application_date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
trial_end_date = (datetime.datetime.now() + datetime.timedelta(days=30)).strftime("%Y-%m-%d")
2023-03-13 19:16:21 +01:00
embed = discord.Embed(title=f"Application from {author.name}#{author.discriminator}", color=discord.Color.green())
2023-06-01 22:19:05 +02:00
embed.set_thumbnail(url=author.avatar.url)
2023-03-12 23:05:14 +01:00
embed.add_field(name="Name", value=answers[0])
2023-03-12 22:55:57 +01:00
embed.add_field(name="Age", value=answers[1])
embed.add_field(name="Country", value=answers[2])
embed.add_field(name="Hobbies", value=answers[3])
embed.add_field(name="Specific game?", value=answers[4])
embed.add_field(name="\u200b", value="\u200b") # Empty field for spacing
embed.add_field(name="Motivation for wanting to join:", value=answers[5], inline=False)
embed.set_footer(text=f"Application received: {application_date}, Trial ends: {trial_end_date}")
2023-03-13 00:34:56 +01:00
2023-03-12 22:55:57 +01:00
return embed
2023-03-13 15:40:22 +01:00
async def ask_questions(self, author: discord.Member) -> List[str]:
2023-03-12 22:58:29 +01:00
"""Ask the user several questions and return the answers."""
questions = [
"First of all, what is your name?",
"What age are you?",
"Where are you from?",
"Do you have any hobbies?",
"Are you wishing to join because of a particular game? If so, which game?",
"Write out, in a free-style way, what your motivation is for wanting to join us in particular and how you would be a good fit for Kanium",
]
2023-03-12 22:55:57 +01:00
answers = []
for question in questions:
await author.send(question)
2023-03-13 15:40:22 +01:00
try:
answer = await asyncio.wait_for(self.get_answers(author), timeout=300.0)
answers.append(answer.content)
2023-03-13 15:40:22 +01:00
except asyncio.TimeoutError:
await author.send("You took too long to answer. Please start over by using the application command again.")
return []
2023-03-12 22:55:57 +01:00
2023-03-12 22:58:29 +01:00
return answers
2023-03-13 13:10:07 +01:00
async def get_answers(self, author: discord.Member) -> discord.Message:
2023-03-12 22:58:29 +01:00
"""Wait for the user to send a message."""
return await self.bot.wait_for(
2023-06-01 22:19:05 +02:00
"message", lambda m: m.author == author and m.guild is None
2023-03-13 15:40:22 +01:00
)