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, ButtonStyle from discord_slash.client import SlashCommand from discord_slash.utils.manage_components import create_select, create_select_option, create_actionrow, wait_for_component, create_button, create_actionrow from bot import configFile, yaml_load, yaml_dump, cogsDir, unloadCog, dataFile, lookupFile, gmFile, categoriesFile #### Pitch Command class Pitch(commands.Cog, name='Pitch Command'): def __init__(self, client): self.client = client conf=yaml_load(configFile) permissions={} guild_ids = list(set.intersection(set([int(guildKey) for guildKey in yaml_load(configFile) if yaml_load(configFile)[guildKey]['timeslots']]),set([int(guildKey) for guildKey in yaml_load(configFile) if 'bot' in yaml_load(configFile)[guildKey]['roles'] and type(yaml_load(configFile)[guildKey]['roles']['bot']) is int]))) for guildID in guild_ids: permissions[guildID] = [] permissions[guildID].append(create_permission(id=conf[str(guildID)]['owner'],id_type=SlashCommandPermissionType.USER,permission=True)) for admin in conf[str(guildID)]['roles']['admin']: permissions[guildID].append(create_permission(id=admin,id_type=SlashCommandPermissionType.ROLE,permission=True)) permissions[guildID] = [] permissions[guildID].append(create_permission(id=conf[str(guildID)]['owner'],id_type=SlashCommandPermissionType.USER,permission=True)) for admin in conf[str(guildID)]['roles']['admin']: permissions[guildID].append(create_permission(id=admin,id_type=SlashCommandPermissionType.ROLE,permission=True)) @cog_ext.cog_slash( name='pitch', description='Designates the various key roles referenced by the Bot.', default_permission=False, permissions=permissions, guild_ids=guild_ids, ) async def _pitch(self, ctx:SlashContext): await ctx.channel.trigger_typing() conf = yaml_load(configFile) data = yaml_load(dataFile) lookup = yaml_load(lookupFile) gms = yaml_load(gmFile) categories = yaml_load(categoriesFile) pitches = {} guildStr = str(ctx.guild.id) if 'timeslots' not in conf[guildStr]: conf[guildStr]['timeslots'] = {} tsDict = {k: conf[guildStr]['timeslots'][k] for k in data[guildStr] if data[guildStr][k]} optionsList = [create_select_option(label=tsDict[x], value=x, description=x) for x in tsDict] try: m = await ctx.send( content='```Select which time slot for which you would like to run pitches for.```', delete_after=5, components=[ create_actionrow( create_select( placeholder='Time Slot', options= optionsList, min_values=1, max_values=1 ) ) ] ) while True: select_ctx = await wait_for_component(self.client,messages=m, timeout=5) if select_ctx.author != ctx.author: await select_ctx.send(f'```Invalid response: you are not the person who issued the command.```', hidden=True) else: break await m.delete() [timeslot] = select_ctx.selected_options except asyncio.TimeoutError: await ctx.send(f'```Error: Command timed out.```', hidden=True) return await ctx.channel.trigger_typing() p = discord.PermissionOverwrite() p.read_messages = False p.send_messages = False await ctx.channel.edit( reason=f'/pitch command issued by {ctx.author.display_name}', overwrites = { ctx.guild.default_role: p } ) if guildStr not in pitches: pitches[guildStr] = {} if timeslot not in pitches[guildStr]: pitches[guildStr][timeslot] = {} pitches[guildStr][timeslot]['entries'] = [x for x in data[guildStr][timeslot].values()] pitches[guildStr][timeslot]['entries'].sort(key= lambda x: x['game_title']) header_message = await ctx.channel.send( content=f'**Game listing for {conf[guildStr]["timeslots"][timeslot]}**\n_ _```The following are the games that are being pitched. Please select which game you would like to join by clicking on the `Join` button below.```' ) pitches[guildStr][timeslot]['messages'] = [] pitches[guildStr][timeslot]['roles'] = {} for index, element in enumerate(pitches[guildStr][timeslot]['entries']): gm = await self.client.fetch_user(element["gm"]) o = f'_ _\n***{element["game_title"]}*** (GM: {gm.mention})\n```\n' if element['system'] is not None: o = ''.join([o,f'System: {element["system"]}\n']) if element['min_players'] is not None: o = ''.join([o,f'Minimum Players: {str(element["min_players"])} ']) if element['max_players'] is not None: o = ''.join([o,f'Maximum Players: {str(element["max_players"])}\n']) if element['platform'] is not None: o = ''.join([o,f'Platform: {element["platform"]}\n']) o = ''.join([o,f'```']) spaces_remaining = element["max_players"] - element["current_players"] o = ''.join([o,f'~~Spaces Remaining: {str(0)}~~'])if spaces_remaining <= 0 else ''.join([o,f'Spaces Remaining: {str(spaces_remaining)}']) m = await ctx.channel.send( content=o, components=[ create_actionrow( create_button( style=ButtonStyle.green, label='Join', emoji='🉑', custom_id=f'join_{index}' ), create_button( style=ButtonStyle.red, label='Leave', emoji='🈳', custom_id=f'leave_{index}' ) ) ] ) pitches[guildStr][timeslot]['messages'].append(m) pitches[guildStr][timeslot]['roles'][index] = discord.utils.find(lambda x: x.id == element['role'],ctx.guild.roles) newcomer = returning_player = None if 'newcomer' in conf[guildStr]['roles']: newcomer = discord.utils.find(lambda x: x.id == conf[guildStr]['roles']['newcomer'], ctx.guild.roles) if 'returning_player' in conf[guildStr]['roles']: returning_player = discord.utils.find(lambda x: x.id == conf[guildStr]['roles']['returning_player'], ctx.guild.roles) buttons = [] if returning_player is not None: buttons.append( create_button( style= ButtonStyle.grey, label= 'Allow Returning Players', emoji= '🔁', custom_id='allow_returning' ) ) if newcomer is not None: buttons.append( create_button( style= ButtonStyle.grey, label= 'Allow Newcomers', emoji= '🆕', custom_id='allow_newcomers' ) ) buttons.append( create_button( style= ButtonStyle.green, label= 'Allow All', emoji='🚪', custom_id='allow_all' ) ) buttons.append( create_button( style= ButtonStyle.red, label= 'Close Pitches', emoji='🔒', custom_id='close_pitches' ) ) control = await ctx.channel.send( content='_ _\n```Control Panel:\nFor Admin Use Only```', components=[ create_actionrow( *buttons ) ] ) while True: button_ctx = await wait_for_component( self.client, messages=pitches[guildStr][timeslot]['messages'] + [control] ) if button_ctx.origin_message.id == control.id: if not (set(ctx.author.roles) & set([ctx.guild.get_role(x) for x in conf[str(ctx.guild.id)]['roles']['admin']]) or ctx.author == ctx.guild.owner): await button_ctx.send(f'```Error: You are not authorised to do this. The control panel may only be issued by an administrator.```',hidden=True) else: if button_ctx.custom_id == 'allow_returning': await ctx.channel.set_permissions(reason=f'/pitch command issued by {ctx.author.display_name}', target=returning_player, read_messages=True) await button_ctx.send(f'```Returning Players have now been allowed access to the pitch menu.```', hidden=True) if button_ctx.custom_id == 'allow_newcomers': await ctx.channel.set_permissions(reason=f'/pitch command issued by {ctx.author.display_name}', target=newcomer, read_messages=True) await button_ctx.send(f'```Newcomers have now been allowed access to the pitch menu.```', hidden=True) if button_ctx.custom_id == 'allow_all': await ctx.channel.set_permissions(reason=f'/pitch command issued by {ctx.author.display_name}', target=ctx.guild.default_role, read_messages= True, send_messages=False) await button_ctx.send(f'```All members have now been allowed access to the pitch menu.```', hidden=True) if button_ctx.custom_id == 'close_pitches': break else: index = int(button_ctx.custom_id.split('_',1)[1]) if button_ctx.custom_id.startswith('join_'): if set(button_ctx.author.roles) & set(pitches[guildStr][timeslot]['roles']): for role in list(set(button_ctx.author.roles) & set(pitches[guildStr][timeslot]['roles'])): if role != pitches[guildStr][timeslot]['roles'][index]: await button_ctx.author.remove_roles(role,reason=f'/pitch interaction by {button_ctx.author.display_name}') data[guildStr][timeslot][str(role.id)]['current_players'] -= 1 element = pitches[guildStr][timeslot]['entries'][index] gm = await self.client.fetch_user(element['gm']) o = f'_ _\n***{element["game_title"]}*** (GM: {gm.mention})\n```\n' if element['system'] is not None: o = ''.join([o,f'System: {element["system"]}\n']) if element['min_players'] is not None: o = ''.join([o,f'Minimum Players: {str(element["min_players"])} ']) if element['max_players'] is not None: o = ''.join([o,f'Maximum Players: {str(element["max_players"])}\n']) if element['platform'] is not None: o = ''.join([o,f'Platform: {element["platform"]}\n']) o = ''.join([o,f'```']) spaces_remaining = element["max_players"] - element["current_players"] o = ''.join([o,f'~~Spaces Remaining: {str(0)}~~'])if spaces_remaining <= 0 else ''.join([o,f'Spaces Remaining: {str(spaces_remaining)}']) await pitches[guildStr][timeslot]['messages'][index].edit(content=o) tc = discord.utils.find(lambda x: x.id == lookup[guildStr][str(role.id)]['text_channel'],ctx.guild.text_channels) if tc is None: c = discord.utils.find(lambda x: x.id == lookup[guildStr][str(role.id)]['category'],ctx.guild.categories) if c is not None: tPos = len(ctx.guild.channels) for t in c.text_channels: if t.position <= tPos: tc = t tPos = t.position if tc is not None: await tc.send(f'```{button_ctx.author.display_name} has left the game.```') role = pitches[guildStr][timeslot]['roles'][index] if role in button_ctx.author.roles: await button_ctx.send(f'```Error: You are already in the game `{lookup[guildStr][str(role.id)]["game_title"]}`.```', hidden=True) else: await button_ctx.author.add_roles(role,reason=f'/pitch interaction by {button_ctx.author.display_name}') data[guildStr][timeslot][str(role.id)]['current_players'] += 1 element = pitches[guildStr][timeslot]['entries'][index] gm = await self.client.fetch_user(element['gm']) o = f'_ _\n***{element["game_title"]}*** (GM: {gm.mention})\n```\n' if element['system'] is not None: o = ''.join([o,f'System: {element["system"]}\n']) if element['min_players'] is not None: o = ''.join([o,f'Minimum Players: {str(element["min_players"])} ']) if element['max_players'] is not None: o = ''.join([o,f'Maximum Players: {str(element["max_players"])}\n']) if element['platform'] is not None: o = ''.join([o,f'Platform: {element["platform"]}\n']) o = ''.join([o,f'```']) spaces_remaining = element["max_players"] - element["current_players"] o = ''.join([o,f'~~Spaces Remaining: {str(0)}~~'])if spaces_remaining <= 0 else ''.join([o,f'Spaces Remaining: {str(spaces_remaining)}']) await pitches[guildStr][timeslot]['messages'][index].edit(content=o) await button_ctx.send(f'You have joined the game `{lookup[guildStr][str(role.id)]["game_title"]}`.',hidden=True) tc = discord.utils.find(lambda x: x.id == lookup[guildStr][str(role.id)]['text_channel'],ctx.guild.text_channels) if tc is None: c = discord.utils.find(lambda x: x.id == lookup[guildStr][str(role.id)]['category'],ctx.guild.categories) if c is not None: tPos = len(ctx.guild.channels) for t in c.text_channels: if t.position <= tPos: tc = t tPos = t.position if tc is not None: await tc.send(f'```{button_ctx.author.display_name} has joined the game.```') elif button_ctx.custom_id.startswith('leave_'): role = pitches[guildStr][timeslot]['roles'][index] if role not in button_ctx.author.roles: await button_ctx.send(f'```Error: You are not in the game `{lookup[guildStr][str(role.id)]["game_title"]}`.```', hidden=True) else: await button_ctx.author.remove_roles(role,reason=f'/pitch interaction by {button_ctx.author.display_name}') data[guildStr][timeslot][str(role.id)]['current_players'] -= 1 element = pitches[guildStr][timeslot]['entries'][index] gm = await self.client.fetch_user(element['gm']) o = f'_ _\n***{element["game_title"]}*** (GM: {gm.mention})\n```\n' if element['system'] is not None: o = ''.join([o,f'System: {element["system"]}\n']) if element['min_players'] is not None: o = ''.join([o,f'Minimum Players: {str(element["min_players"])} ']) if element['max_players'] is not None: o = ''.join([o,f'Maximum Players: {str(element["max_players"])}\n']) if element['platform'] is not None: o = ''.join([o,f'Platform: {element["platform"]}\n']) o = ''.join([o,f'```']) spaces_remaining = element["max_players"] - element["current_players"] o = ''.join([o,f'~~Spaces Remaining: {str(0)}~~'])if spaces_remaining <= 0 else ''.join([o,f'Spaces Remaining: {str(spaces_remaining)}']) await pitches[guildStr][timeslot]['messages'][index].edit(content=o) await button_ctx.send(f'You have left the game `{lookup[guildStr][str(role.id)]["game_title"]}`.',hidden=True) tc = discord.utils.find(lambda x: x.id == lookup[guildStr][str(role.id)]['text_channel'],ctx.guild.text_channels) if tc is None: c = discord.utils.find(lambda x: x.id == lookup[guildStr][str(role.id)]['category'],ctx.guild.categories) if c is not None: tPos = len(ctx.guild.channels) for t in c.text_channels: if t.position <= tPos: tc = t tPos = t.position if tc is not None: await tc.send(f'```{button_ctx.author.display_name} has left the game.```') yaml_dump(data, dataFile) await header_message.delete() for message in pitches[guildStr][timeslot]['messages']: await message.delete() await control.delete() await ctx.channel.edit(reason=f'/pitch command issued by {ctx.author.display_name}', overwrites={}) await button_ctx.send('```Pitch menu cleared. Pitches have now concluded.```') def setup(client): client.add_cog(Pitch(client))