import os # OS Locations import yaml # YAML parser for Bot config files import asyncio # Discord Py Dependency import discord # Main Lib from discord.ext import commands # Commands module from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash Command Library from discord_slash.utils.manage_commands import create_choice, create_option, create_permission # Slash Command features from discord_slash.model import SlashCommandPermissionType import re from bot import configFile, yaml_load, yaml_dump, loadCog, unloadCog, reloadCog, cogsDir, slash, lookupFile ##### Configuration Cog class Configuration(commands.Cog, name='Configuration Commands'): def __init__(self, client): self.client = client guild_ids=[int(guildKey) for guildKey in yaml_load(configFile)] permissions = {} conf = yaml_load(configFile) for gStr in conf: permissions[int(gStr)] = [] permissions[int(gStr)].append(create_permission(id=conf[gStr]['owner'],id_type=SlashCommandPermissionType.USER,permission=True)) for admin in conf[gStr]['roles']['admin']: permissions[int(gStr)].append(create_permission(id=admin,id_type=SlashCommandPermissionType.ROLE,permission=True)) @cog_ext.cog_subcommand( base='config', # subcommand_group='role', name='roles', description='Designates the various key roles referenced by the Bot.', base_description='Commands for configuring the various parameters of the Guild.', base_default_permission=False, base_permissions=permissions, # subcommand_group_description='Designates the various key command roles for the guild.', guild_ids=guild_ids, options=[ create_option( name='key', description='The name of the role parameter being assigned.', option_type=3, required=True, choices=[ create_choice( name='`Bot` role', value='bot' ), create_choice( name='`Committee` role', value='committee' ), create_choice( name='`Newcomer` role', value='newcomer' ), create_choice( name='`Returning Player` role', value='returning_player' ), create_choice( name='`Student` role', value='student' ) ] ), create_option( name='role_exists', description='Whether or not a role for this parameter already exists', option_type=5, required=True ), create_option( name='role', description='The role assigned to the parameter.', option_type=8, required=False ) ] ) async def _config_roles(self, ctx:SlashContext, key:str, role_exists:bool, role:discord.Role=None): if role_exists and role is None: await ctx.send(f'```If the role you want to assign to `{key}` already exists, you must assign it. Please select a role to assign.```',hidden=True) return if not role_exists and role is not None: await ctx.send(f'```You have specified a role to assign to `{key}` but have also specified it does not exist. If a role does not exist, do not assign a role.```',hidden=True) return r = role if not role_exists: r = await ctx.guild.create_role( name=key, permissions=discord.Permissions(administrator=True) if key == 'committee' else discord.Permissions().none(), reason=f'`/config roles` command issued by {ctx.author.display_name}', colour = discord.Colour.orange() if key == 'bot' else discord.Colour.blue() if key == 'committee' else discord.Colour.default(), hoist=True if key == 'committee' else None, ) else: await r.edit( permissions=discord.Permissions(administrator=True) if key == 'committee' else discord.Permissions().none(), reason=f'`/config roles` command issued by {ctx.author.display_name}', colour = discord.Colour.orange() if key == 'bot' else discord.Colour.blue() if key == 'committee' else discord.Colour.default(), hoist=True if key == 'committee' or key == 'bot' else None, ) conf = yaml_load(configFile) guildStr = str(ctx.guild.id) if 'roles' not in conf[guildStr]: conf[guildStr]['roles'] = {} conf[guildStr]['roles'][key] = int(r.id) yaml_dump(conf, configFile) await ctx.send(f'```The `{key}` role for the guild `{ctx.guild.name}` has been set to `{r.name}`.```\n{r.mention}',hidden=True) if any(['bot' in yaml_load(configFile)[x]['roles'] for x in yaml_load(configFile)]): if any([yaml_load(configFile)[x]['timeslots'] for x in yaml_load(configFile)]): flag = False if self.client.get_cog('Game Create') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/game_create.py') flag = True if any([x for x in yaml_load(lookupFile).values()]): if self.client.get_cog('Game Management') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/game_management.py') flag = True if self.client.get_cog('Player Commands') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/player_commands.py') flag = True if self.client.get_cog('Pitch Command') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/pitch.py') flag = True if flag: await self.client.slash.sync_all_commands() @cog_ext.cog_subcommand( base='config', # subcommand_group='channel', name='channels', description='Designate the various key channels for the Bot to interact with.', # base_description='Commands for configuring the various parameters of the Guild', base_default_permission=False, # base_permissions=permissions, # subcommand_group_description='Designates the various key Bot channels for the guild.', guild_ids=guild_ids, options=[ create_option( name='key', description='The name of the channel parameter being assigned.', option_type=3, required=True, choices=[ create_choice( name='Help Channel', value='help' ), create_choice( name='Mod Channel', value='mod' ), create_choice( name='Signup Channel', value='signup' ) ] ), create_option( name='channel_exists', description='Does the channel for this parameter already exist?', option_type=5, required=True ), create_option( name='channel', description='The channel assigned to the parameter.', option_type=7, required=False ) ] ) async def _config_channels(self, ctx:SlashContext, key:str, channel_exists:bool, channel:discord.TextChannel=None): if channel_exists and channel is None: await ctx.send(f'```If the channel you want to assign to `{key}` already exists, you must assign it. Please select a role to assign.```',hidden=True) return if not channel_exists and channel is not None: await ctx.send(f'```You have specified a channel to assign to `{key}` but have also specified it does not exist. If a channel does not exist, do not assign a channel.```',hidden=True) return c = channel if not channel_exists: c = await ctx.guild.create_text_channel( name=key, overwrites={}, reason=f'`/config channels` command issued by {ctx.author.display_name}' ) else: await c.edit( name=key, overwrites={}, reason=f'`/config channels` command issued by {ctx.author.display_name}' ) conf = yaml_load(configFile) guildStr = str(ctx.guild.id) if 'channels' not in conf[guildStr]: conf[guildStr]['channels'] = {} conf[guildStr]['channels'][key] = int(c.id) yaml_dump(conf, configFile) await ctx.send(f'```The `{key}` channel for the guild `{ctx.guild.name}` has been set to `{c.name}`.\n\nAll permission overrides for the channel have been reset. Remember to set the appropriate permissions for your guild.```\n{c.mention}',hidden=True) @cog_ext.cog_subcommand( base='config', # subcommand_group='notifications', name='notifications', description='Configure monitoring and notifications to Committee for member query channels.', # base_description='Commands for configuring the various parameters of the Guild', base_default_permission=False, # base_permissions=permissions, # subcommand_group_description='Configures whether the bot monitors and responds to posts in key channels.', guild_ids=guild_ids, options=[ create_option( name='channel', description='Select which channel to change notifications for.', option_type=3, required=True, choices=[ create_choice( name='Help Channel', value='help' ), create_choice( name='Signup Channel', value='signup' ) ] ), create_option( name='notifications', description='Whether or not the bot monitors the channel for posts.', option_type=5, required=True ) ] ) async def _config_notifications(self, ctx:SlashContext, channel:str, notifications:bool): conf = yaml_load(configFile) guildStr = str(ctx.guild.id) if 'notifications' not in conf[guildStr]: conf[guildStr]['notifications'] = {} conf[guildStr]['notifications'][channel] = notifications yaml_dump(conf, configFile) await ctx.send(f'```Notifications for posts in the `{channel}` channel for the guild `{ctx.guild.name}` have been set to `{notifications}`.```',hidden=True) @cog_ext.cog_subcommand( base='config', subcommand_group='timeslots', name='add', description='Add a timeslot at which the Guild will host games.', # base_description='Commands for configuring the various parameters of the Guild', base_default_permission=False, # base_permissions=permissions, subcommand_group_description='Manages timeslots available for games on the guild.', guild_ids=guild_ids, options=[ create_option( name='key', description='Alphanumeric time code 10 chars max.', option_type=3, required=True, ), create_option( name='name', description='A longer, descriptive name of when the timeslot is', option_type=3, required=True ) ] ) async def _config_timeslots_add(self, ctx:SlashContext, key:str, name:str): sanitisedKey = re.sub(r"\W+",'', key[:9].lower()) if not key.isalnum(): await ctx.send(f'```Key value {key} is not a valid alphanumeric time code. Sanitising to `{sanitisedKey}`.```',hidden=True) conf = yaml_load(configFile) guildStr = str(ctx.guild.id) if 'timeslots' not in conf[guildStr]: conf[guildStr]['timeslots'] = {} if sanitisedKey in conf[guildStr]['timeslots']: await ctx.send(f'```Key value {sanitisedKey} has already been defined for guild `{ctx.guild.name}` for `{conf[guildStr]["timeslots"][sanitisedKey]}`. Please use the `remove` or `modify` sub-commands to amend it.```',hidden=True) return conf[guildStr]['timeslots'][sanitisedKey] = name yaml_dump(conf, configFile) await ctx.send(f'```Timeslot `{name}` with the key `{sanitisedKey}` has been added for the guild `{ctx.guild.name}`.```',hidden=True) if any([yaml_load(configFile)[x]['timeslots'] for x in yaml_load(configFile)]): flag = False if self.client.get_cog('Manipulate Timeslots') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/manipulate_timeslots.py') flag = True if any(['bot' in yaml_load(configFile)[x]['roles'] for x in yaml_load(configFile)]): if self.client.get_cog('Game Create') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/game_create.py') Flag = True if yaml_load(lookupFile): if any([x for x in yaml_load(lookupFile).values()]): if self.client.get_cog('Game Management') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/game_management.py') Flag = True if self.client.get_cog('Player Commands') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/player_commands.py') Flag = True if self.client.get_cog('Pitch Command') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/pitch.py') Flag = True if flag: await self.client.slash.sync_all_commands() @cog_ext.cog_subcommand( base='config', subcommand_group='membership', name='add', description='Add a membership type for the Guild.', # base_description='Commands for configuring the various parameters of the Guild', base_default_permission=False, # base_permissions=permissions, subcommand_group_description='Manages the different categories of membership available to the Guild.', guild_ids=guild_ids, options=[ create_option( name='name', description='Name of membership type.', option_type=3, required=True, ), create_option( name='role_exists', description='Does the role for this member type already exist?', option_type=5, required=True ), create_option( name='role', description='Assign the role if it already exists.', option_type=8, required=False ) ] ) async def _config_membership_add(self, ctx:SlashContext, name:str, role_exists:bool, role:discord.Role=None): if role_exists and role is None: await ctx.send(f'```If the role for membership type `{name}` already exists, you must assign it. If it does not exist, the Bot will create one.```') return if not role_exists and role is not None: await ctx.send(f'```You have specified a role for `{name}` does not already exist but have also specified a role to assign. Please either assign a role if it exists, or leave it blank if does not.```',hidden=True) return conf = yaml_load(configFile) guildStr = str(ctx.guild.id) if 'membership' not in conf[guildStr]: conf[guildStr]['membership'] = [] if role is not None: if role.id in conf[guildStr]['membership']: await ctx.send(f'```The role {name} has already been assigned to a membership type for guild `{ctx.guild.name}`. Please use the `remove` sub-command to delete it or assign a different role.```',hidden=True) return if any([ctx.guild.get_role(m).name == name for m in conf[str(ctx.guild.id)]['membership']]): await ctx.send(f'```The membership type {name} has already been assigned a role for guild `{ctx.guild.name}`. Please use the `remove` sub-command to delete the role or assign a different membership type.```',hidden=True) return if not role_exists: r = await ctx.guild.create_role( name=name, permissions=discord.Permissions(read_messages=True,use_slash_commands=True), mentionable=False, reason=f'`/config membership add` command issued by {ctx.author.display_name}' ) if role is not None: await role.edit( name=name, permissions=discord.Permissions(read_messages=True,use_slash_commands=True), mentionable=False, reason=f'`/config membership add` command issued by {ctx.author.display_name}' ) conf[guildStr]['membership'].append(role.id) if role is not None else conf[guildStr]['membership'].append(r.id) yaml_dump(conf, configFile) await ctx.send(f'```Membership type `{role.name if role is not None else r.name}` has been registered for the guild `{ctx.guild.name}`.```',hidden=True) if any([yaml_load(configFile)[x]['membership'] for x in yaml_load(configFile)]): if self.client.get_cog('Edit Membership') is None: loadCog(f'./{cogsDir}/slashcommands/secondary/edit_membership.py') await self.client.slash.sync_all_commands() @cog_ext.cog_subcommand( base='config', # subcommand_group='notifications', name='restrict', description='Toggle membership estriction for the guild.', # base_description='Commands for configuring the various parameters of the Guild', base_default_permission=False, # base_permissions=permissions, # subcommand_group_description='Configures whether the bot monitors and responds to posts in key channels.', guild_ids=guild_ids, options=[ create_option( name='value', description='Enable membership restrictions for the guild?', option_type=5, required=True ) ] ) async def _restrict( self, ctx:SlashContext, value:bool ): conf = yaml_load(configFile) conf[str(ctx.guild.id)]['restrict'] = value yaml_dump(conf, configFile) await ctx.send(f'```Membership restrictions for the guild `{ctx.guild.name}` have been set to `{value}`.```',hidden=True) def setup(client): client.add_cog(Configuration(client))