diff --git a/cogs/serverconfig.py b/cogs/serverconfig.py index 6f4f8e1..c719e50 100644 --- a/cogs/serverconfig.py +++ b/cogs/serverconfig.py @@ -1,5 +1,7 @@ # Imports import discord +import random +import json from discord import option, ApplicationContext from discord.commands import SlashCommandGroup from discord.ext import commands @@ -12,6 +14,10 @@ class ServerConfig(commands.Cog): def __init__(self, bot): self.bot = bot + + # Load Verification Database + with open("database/serververification.json", 'r', encoding="utf-8") as f: + self.verification_db: dict = json.load(f) serverconfig_cmds = SlashCommandGroup(name="serverconfig", description="Commands related to server customization and configuration.") @@ -65,6 +71,98 @@ async def autorole(self, ctx: ApplicationContext, channel: discord.TextChannel = ) await ctx.respond(embed=localembed) + # Server Member Verification System + @serverconfig_cmds.command( + name="enable_verification", + description="Enable new member verification for this server." + ) + @option(name="verified_role", description="The role to provide to all verified members.", type=discord.Role) + async def enable_verification(self, ctx: ApplicationContext, verified_role: discord.Role): + """Enable new user verification for this server.""" + if not ctx.author.guild_permissions.administrator: + return await ctx.respond("You can't use this command! You need the `Administrator` permission to run this.", ephemeral=True) + serverconf.set_verification_role(ctx.guild.id, verified_role.id) + localembed = discord.Embed( + title=f":white_check_mark: Server Member Verification successfully enabled for **{ctx.guild.name}**!", + description=f"From now onwards, all new members will have to verify with `/verify` command, and will receive the {verified_role.mention} once verified.", + color=discord.Color.green() + ) + await ctx.respond(embed=localembed) + + @serverconfig_cmds.command( + name="disable_verification", + description="Disable new member verification for this server." + ) + async def disable_verification(self, ctx: ApplicationContext): + """Disable new member verification for this server.""" + if not ctx.author.guild_permissions.administrator: + return await ctx.respond("You can't use this command! You need the `Administrator` permission to run this.", ephemeral=True) + serverconf.set_verification_role(ctx.guild.id, None) + localembed = discord.Embed( + title=f":white_check_mark: Server Member Verification successfully disabled for **{ctx.guild.name}**", + description=f"New members now won't have to verify in the server.", + color=discord.Color.green() + ) + await ctx.respond(embed=localembed) + + @commands.slash_command( + name="start_verification", + description="Start your verification process in this server." + ) + @commands.guild_only() + async def start_verification(self, ctx: ApplicationContext): + """Start your verification process in this server.""" + verification_role = serverconf.fetch_verification_role(ctx.guild.id) + if verification_role is None: + return await ctx.respond(":warning: Verification system is disabled for this server!", ephemeral=True) + if ctx.author.get_role(verification_role) is not None: + return await ctx.respond(":warning: You are already verified in this server!", ephemeral=True) + + # Construct verification data + verify_code = random.randint(100000, 999999) + if str(ctx.author.id) not in self.verification_db: + self.verification_db[str(ctx.author.id)] = {} + + for code in self.verification_db[str(ctx.author.id)]: + if self.verification_db[str(ctx.author.id)][str(code)]["guild_id"] == ctx.guild.id: + return await ctx.respond("Your verification process is already ongoing in this server!", ephemeral=True) + + self.verification_db[str(ctx.author.id)][str(verify_code)] = {"guild_id": ctx.guild.id} + with open("database/serververification.json", 'w+', encoding="utf-8") as f: + json.dump(self.verification_db, f, indent=4) + + localembed = discord.Embed( + title=f"Verification for {ctx.author.name} in {ctx.guild.name} has started", + description=f"Your one-time verification code is `{verify_code}`. **DO NOT share this code with anyone!**\n\nGo to isobot's DMs, and run the `/verify` command entering your verification code.", + color=discord.Color.orange() + ) + await ctx.respond(embed=localembed, ephemeral=True) + + @commands.slash_command( + name="verify", + description="Enter your one-time verification code to verify membership in a server. (DM-ONLY)" + ) + @commands.dm_only() + @option(name="verification_code", description="Your one-time verification code. (6-digit number)", type=int) + async def verify(self, ctx: ApplicationContext, verification_code: int): + """Enter your one-time verification code to verify membership in a server.""" + if str(ctx.author.id) not in self.verification_db.keys(): + return await ctx.respond("You are not pending verification in any servers.", ephemeral=True) + if str(verification_code) not in self.verification_db[str(ctx.author.id)].keys(): + return await ctx.respond(":x: This verification code is invalid. Please double-check and try a different code!", ephemeral=True) + + verification_role_id = serverconf.fetch_verification_role(self.verification_db[str(ctx.author.id)][str(verification_code)]["guild_id"]) + vcode_guild: discord.Guild = self.bot.get_guild(self.verification_db[str(ctx.author.id)][str(verification_code)]["guild_id"]) + verification_role = discord.Guild.get_role(vcode_guild, verification_role_id) + server_context_user: discord.Member = vcode_guild.get_member(ctx.author.id) + await server_context_user.add_roles(verification_role, reason="Member has been successfully verified in server.") + + del self.verification_db[str(ctx.author.id)][str(verification_code)] + with open("database/serververification.json", 'w+', encoding="utf-8") as f: + json.dump(self.verification_db, f, indent=4) + + return await ctx.respond(f"You have been successfully verified in **{vcode_guild.name}**!") + def setup(bot): bot.add_cog(ServerConfig(bot)) \ No newline at end of file diff --git a/config/commands.json b/config/commands.json index dafcaa2..e5eabd2 100644 --- a/config/commands.json +++ b/config/commands.json @@ -848,5 +848,75 @@ "usable_by": "everyone", "disabled": false, "bugged": false + }, + "howgay": { + "name": "Gay Rating", + "description": "See the gay percentage of a person!", + "type": "fun", + "cooldown": null, + "args": null, + "usable_by": "everyone", + "disabled": false, + "bugged": false + }, + "serverconfig autorole": { + "name": "ServerConfig Autorole", + "description": "Set a role to provide to all newly-joined members of the server.", + "type": "serverconfig", + "cooldown": null, + "args": ["role (leave blank to disable)"], + "usable_by": "moderators with `manage guild` permissions", + "disabled": false, + "bugged": false + }, + "serverconfig levelup_override_channel": { + "name": "ServerConfig Level-up Override Channel", + "description": "Set a server channel to send level-up messages to, instead of DMs.", + "type": "serverconfig", + "cooldown": null, + "args": ["channel (leave blank to disable)"], + "usable_by": "moderators with `manage guild` permissions", + "disabled": false, + "bugged": false + }, + "serverconfig enable_verification": { + "name": "ServerConfig Enable Verification", + "description": "Enable new member verification for this server.", + "type": "serverconfig", + "cooldown": null, + "args": ["verified_role"], + "usable_by": "server admins", + "disabled": false, + "bugged": false + }, + "serverconfig disable_verification": { + "name": "ServerConfig Disable Verification", + "description": "Disable new member verification for this server.", + "type": "serverconfig", + "cooldown": null, + "args": null, + "usable_by": "server admins", + "disabled": false, + "bugged": false + }, + "start_verification": { + "name": "Start Verification", + "description": "Start your verification process in the server. (SERVER-ONLY)", + "type": "general utilities", + "cooldown": null, + "args": null, + "usable_by": "everyone", + "disabled": false, + "bugged": false + }, + "verify": { + "name": "Verify", + "description": "Enter your one-time verification code to verify membership in a server. (DM-ONLY)", + "type": "general utilities", + "cooldown": null, + "args": null, + "usable_by": "everyone (in DMs)", + "disabled": false, + "bugged": false } } diff --git a/framework/isobot/db/serverconfig.py b/framework/isobot/db/serverconfig.py index b18242b..380c5a5 100644 --- a/framework/isobot/db/serverconfig.py +++ b/framework/isobot/db/serverconfig.py @@ -33,7 +33,8 @@ def generate(self, server_id: int) -> int: "channel": None, "message": None }, - "level_up_override_channel": None + "level_up_override_channel": None, + "verification_role": None } self.save(serverconf) return 0 @@ -58,6 +59,10 @@ def fetch_goodbye_message(self, server_id: int) -> dict: def fetch_levelup_override_channel(self, server_id: int) -> str: """Fetches the level-up override channel for the specified guild. Returns `None` if not set.""" return self.fetch_raw(server_id)["level_up_override_channel"] + + def fetch_verification_role(self, server_id: int) -> str: + """Fetches the verified member role for the specified guild. Returns `None` if server verification system is disabled.""" + return self.fetch_raw(server_id)["verification_role"] def set_autorole(self, server_id: int, role_id: int) -> int: """Sets a role id to use as autorole for the specified guild. Returns `0` if successful.""" @@ -84,3 +89,9 @@ def set_levelup_override_channel(self, server_id: int, channel_id: int) -> int: serverconf = self.load() serverconf[str(server_id)]["level_up_override_channel"] = channel_id self.save(serverconf) + + def set_verification_role(self, server_id: int, role_id: int) -> int: + """Sets a verified member role id for the specified guild for the specified guild, and enables server member verification. Returns `0` if successful.""" + serverconf = self.load() + serverconf[str(server_id)]["verification_role"] = role_id + self.save(serverconf) diff --git a/main.py b/main.py index b60f8a5..a15cb09 100644 --- a/main.py +++ b/main.py @@ -48,6 +48,7 @@ def initial_setup(): "items", "levels", "serverconfig", + "serververification", "warnings", "presence", "user_data", @@ -285,6 +286,7 @@ async def on_application_command_error(ctx: ApplicationContext, error: discord.D elif isinstance(error, commands.BotMissingPermissions): await ctx.respond(":x: I don\'t have the required permissions to use this.\nIf you think this is a mistake, please go to server settings and fix isobot's role permissions.") elif isinstance(error, commands.BadBoolArgument): await ctx.respond(":x: Invalid true/false argument.", ephemeral=True) elif isinstance(error, commands.NoPrivateMessage): await ctx.respond(":x: You can only use this command in a server!", ephemeral=True) + elif isinstance(error, commands.PrivateMessageOnly): await ctx.respond(":x: You can only use this command in isobot's DMs!", ephemeral=True) else: logger.error(f"Command failure: An uncaught error occured while running the command.\n >>> {error}", module="main/Client") await ctx.respond(f"An uncaught error occured while running the command. (don't worry, developers will fix this soon)\n```\n{error}\n```") @@ -320,6 +322,7 @@ async def help_list(ctx: ApplicationContext, search: str = None): reddit_commands = str() afk_commands = str() automod_moderation_commands = str() + serverconfig_commands = str() maths_commands = str() other_commands = str() for _command in commandsdb: @@ -332,10 +335,11 @@ async def help_list(ctx: ApplicationContext, search: str = None): elif command_type == "reddit media": reddit_commands += f"`/{_command}` " elif command_type == "AFK system": afk_commands += f"`/{_command}` " elif command_type == "automod" or command_type == "moderation": automod_moderation_commands += f"`/{_command}` " + elif command_type == "serverconfig": serverconfig_commands += f"`/{_command}` " elif command_type == "maths": maths_commands += f"`/{_command}` " else: other_commands += f"`/{_command}` " - commands_list = f"**:money_with_wings: Economy System:**\n{economy_commands}\n\n**:arrow_up: Levelling System:**\n{levelling_commands}\n\n**:toolbox: Utilities:**\n{utility_commands}\n\n**:joy: Fun Commands:**\n{fun_commands}\n\n**:crescent_moon: AFK System**\n{afk_commands}\n\n**:tools: Moderation and Automod:**\n{automod_moderation_commands}\n\n**:1234: Maths Commands:**\n{maths_commands}\n\n**:frame_photo: Reddit Media Commands:**\n{reddit_commands}\n\n**:sparkles: Miscellaneous:**\n{other_commands}" + commands_list = f"**:money_with_wings: Economy System:**\n{economy_commands}\n\n**:arrow_up: Levelling System:**\n{levelling_commands}\n\n**:toolbox: Utilities:**\n{utility_commands}\n\n**:joy: Fun Commands:**\n{fun_commands}\n\n**:crescent_moon: AFK System**\n{afk_commands}\n\n**:tools: Moderation and Automod:**\n{automod_moderation_commands}\n\n**:gear: Server Configuration:**\n{serverconfig_commands}\n\n**:1234: Maths Commands:**\n{maths_commands}\n\n**:frame_photo: Reddit Media Commands:**\n{reddit_commands}\n\n**:sparkles: Miscellaneous:**\n{other_commands}" localembed = discord.Embed(title="Isobot Command Help", description=commands_list, color=color) localembed.set_footer(text="Run \"/help info\" to get more information on a command.") await ctx.respond(embed=localembed)