302 lines
13 KiB
Python
302 lines
13 KiB
Python
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
|
|
from discord_slash.client import SlashCommand
|
|
import typing
|
|
import re
|
|
|
|
from bot import configFile, yaml_load, yaml_dump, cogsDir, unloadCog, dataFile, lookupFile, gmFile, loadCog, categoriesFile
|
|
|
|
#### Game Role and Channel Setup Command
|
|
|
|
class GameCreate(commands.Cog, name='Game Create'):
|
|
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_subcommand(
|
|
base='game',
|
|
# subcommand_group='',
|
|
name='create',
|
|
description='Create a new game role and accompanying category, text, and voice channels.',
|
|
base_description='Commands for setting up and removing games on the Guild.',
|
|
base_default_permission=False,
|
|
base_permissions=permissions,
|
|
# subcommand_group_description='Adds a time slot available to the channel for games.',
|
|
guild_ids=guild_ids,
|
|
options=[
|
|
create_option(
|
|
name='timeslot',
|
|
description='The timeslot code for when the game will run.',
|
|
option_type=3,
|
|
required=True
|
|
),
|
|
create_option(
|
|
name='gm',
|
|
description='The person who will be running the game.',
|
|
option_type=6,
|
|
required=True
|
|
),
|
|
create_option(
|
|
name='max_players',
|
|
description='The maximum number of players the game can take.',
|
|
option_type=4,
|
|
required=True
|
|
),
|
|
create_option(
|
|
name='game_title',
|
|
description='What the game is called.',
|
|
option_type=3,
|
|
required=True
|
|
),
|
|
create_option(
|
|
name='min_players',
|
|
description='The minimum number of players the game can take.',
|
|
option_type=4,
|
|
required=False
|
|
),
|
|
create_option(
|
|
name='current_players',
|
|
description='The number of players currently in this game.',
|
|
option_type=4,
|
|
required=False
|
|
),
|
|
create_option(
|
|
name='system',
|
|
description='What system the game is using.',
|
|
option_type=3,
|
|
required=False
|
|
),
|
|
create_option(
|
|
name='platform',
|
|
description='What platform the game will be running on.',
|
|
option_type=3,
|
|
required=False
|
|
)
|
|
]
|
|
)
|
|
async def _game_create(
|
|
self,
|
|
ctx:SlashContext,
|
|
timeslot:str,
|
|
gm:discord.Member,
|
|
max_players:int,
|
|
game_title:str,
|
|
min_players: typing.Optional[int]=None,
|
|
current_players: typing.Optional[int]=None,
|
|
system:typing.Optional[str]= None,
|
|
platform:typing.Optional[str]=None
|
|
):
|
|
await ctx.channel.trigger_typing()
|
|
conf = yaml_load(configFile)
|
|
data = yaml_load(dataFile)
|
|
gms = yaml_load(gmFile)
|
|
lookup = yaml_load(lookupFile)
|
|
categories = yaml_load(categoriesFile)
|
|
guildStr = str(ctx.guild.id)
|
|
time = re.sub(r"\W+",'', timeslot[:9].lower())
|
|
if 'roles' not in conf[guildStr]:
|
|
conf[guildStr]['roles'] = {}
|
|
if 'bot' not in conf[guildStr]['roles']:
|
|
await ctx.send(f'```Error: `Bot` role for guild `{ctx.guild.name}` has not been defined. Cannot configure game.```',hidden=True)
|
|
return
|
|
if 'timeslots' not in conf[guildStr]:
|
|
conf[guildStr]['timeslots'] = {}
|
|
if time not in conf[guildStr]['timeslots']:
|
|
await ctx.send(f'```Error: Time code `{timeslot}` is not recognised. Please enter a valid time code to register the game. use `/config timeslots list` to get a list of valid time codes.```',hidden=True)
|
|
return
|
|
if min_players and min_players > max_players:
|
|
await ctx.send(f'```Error: The minimum number of players cannot exceed the maximum number of players.```',hidden=True)
|
|
return
|
|
if current_players and current_players > max_players:
|
|
await ctx.send(f'```Error: The number of reserved spaces cannot exceed the maximum number of players.```',hidden=True)
|
|
return
|
|
if any(x is not None and x < 0 for x in [min_players, max_players, current_players]):
|
|
await ctx.send(f'```Error: You cannot enter negative integers for the number of players.```',hidden=True)
|
|
return
|
|
if guildStr not in lookup:
|
|
lookup[guildStr] = {}
|
|
if game_title in [x['game_title'] for x in lookup[str(ctx.guild.id)].values()] and time in [x['time'] for x in lookup[str(ctx.guild.id)].values()]:
|
|
await ctx.send(f'```Error: Game `{game_title}` has already been created for the time slot `{conf[guildStr]["timeslots"][time]}`. Please avoud duplicates, or use the `modify` sub-command to edit the existing game.```',hidden=True)
|
|
return
|
|
if guildStr not in data: data[guildStr] = {}
|
|
if time not in data[guildStr]: data[guildStr][time] = {}
|
|
rExists, cExists = False, False
|
|
r = discord.utils.get(ctx.guild.roles, name=f'{time.upper()}: {game_title}')
|
|
if not r:
|
|
r = await ctx.guild.create_role(
|
|
name=f'{time.upper()}: {game_title}',
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`',
|
|
mentionable=True,
|
|
permissions=discord.Permissions.none(),
|
|
colour=discord.Colour.green()
|
|
)
|
|
else:
|
|
rExists = True
|
|
await r.edit(
|
|
mentionable=True,
|
|
permissions=discord.Permissions.none(),
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`',
|
|
colour=discord.Colour.green()
|
|
)
|
|
if r not in gm.roles:
|
|
await gm.add_roles(r)
|
|
permissions = {
|
|
ctx.guild.default_role: discord.PermissionOverwrite(read_messages=False),
|
|
r: discord.PermissionOverwrite(read_messages=True),
|
|
ctx.guild.get_role(conf[guildStr]['roles']['bot']): discord.PermissionOverwrite(read_messages=True),
|
|
gm: discord.PermissionOverwrite(
|
|
read_messages=True,
|
|
manage_messages=True,
|
|
manage_channels=True,
|
|
manage_permissions=True,
|
|
priority_speaker=True,
|
|
move_members=True,
|
|
mute_members=True,
|
|
deafen_members=True
|
|
)
|
|
}
|
|
c = discord.utils.get(ctx.guild.categories, name=f'{time.upper()}: {game_title}')
|
|
if not c:
|
|
c = await ctx.guild.create_category(
|
|
name=f'{time.upper()}: {game_title}',
|
|
overwrites=permissions,
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
await c.create_voice_channel(
|
|
name=f'voice: {game_title}',
|
|
topic=f'Default voice channel for the game `{game_title}`` taking place at `{conf[guildStr]["timeslots"][time]}`, with GM `{gm.display_name}`.',
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
t = await c.create_text_channel(
|
|
name=f'text: {game_title}',
|
|
topic=f'Default text channel for the game `{game_title}`` taking place at `{conf[guildStr]["timeslots"][time]}`, with GM `{gm.display_name}`.',
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
else:
|
|
cExists= True
|
|
await c.edit(
|
|
overwrites=permissions,
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`',
|
|
)
|
|
tPos = len(ctx.guild.channels)
|
|
t = None
|
|
v = False
|
|
for tc in c.text_channels:
|
|
if tc.position <= tPos:
|
|
t, tPos = tc, tc.position
|
|
await t.edit(
|
|
sync_permissions=True,
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
for vc in c.voice_channels:
|
|
await vc.edit(
|
|
sync_permissions=True,
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
v = True
|
|
if not t:
|
|
t = await c.create_text_channel(
|
|
name=f'text: {game_title}',
|
|
topic=f'Default text channel for the game `{game_title}`` taking place at `{conf[guildStr]["timeslots"][time]}`, with GM `{gm.display_name}`.',
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
else:
|
|
pins = await t.pins()
|
|
if pins:
|
|
hm = discord.utils.find(lambda x: x.content.startswith('```Hello ') and 'Your game channels for ' in x.content and x.author.id == self.client.user.id, pins)
|
|
if hm is not None:
|
|
await hm.delete()
|
|
if not v:
|
|
await c.create_voice_channel(
|
|
name=f'voice: {game_title}',
|
|
topic=f'Default voice channel for the game `{game_title}`` taking place at `{conf[guildStr]["timeslots"][time]}`, with GM `{gm.display_name}`.',
|
|
reason=f'/game create command issued by `{ctx.author.display_name}`'
|
|
)
|
|
result = f'Game `{game_title}` has been created for timeslot `{conf[guildStr]["timeslots"][time]}`` with GM `{gm.display_name}` and space for {max_players} players (with {current_players if current_players else "0"} currently occupied).\n'
|
|
if rExists:
|
|
result = ''.join([result,f'There was already a role that matched the game, so that role has been reconfigured.\n\nNote: Editing this role will synchronise changes with the game channels, and deleting the role will delete the game and all its data.\n\n'])
|
|
else:
|
|
result = ''.join([result,f'A role for the game has been created.\n\nNote: Editing this role will synchronise changes with the game channels, and deleting the role will delete the game and all its data.\n\n'])
|
|
if cExists:
|
|
result = ''.join([result,f'There was already a channel category that matched the game, so it has been reconfigured with the appropriate permissions and text and voice channels.\n'])
|
|
else:
|
|
result = ''.join([result,f'A channel category with the appropriate text and voice channels has been created.\n'])
|
|
result = ''.join(['```',result,f'```\n{gm.mention} | {r.mention} | {t.mention}'])
|
|
output = f'```Hello {gm.display_name}! Your game channels for `{game_title}` have been created.\nYou can ping your players or edit the game settings by interacting with the game role through the Bot commands. Have fun!```\n'
|
|
output = ''.join([output,f'```Game Title: {game_title}\n'])
|
|
output = ''.join([output,f'GM: {gm.display_name}\n'])
|
|
output = ''.join([output,f'Time: {conf[guildStr]["timeslots"][time]}\n'])
|
|
output = ''.join([output,f'System: {system}\n'if system is not None else ''])
|
|
output = ''.join([output,f'Max Players: {max_players}\n'])
|
|
output = ''.join([output,f'Min Players: {min_players}\n'if min_players is not None else ''])
|
|
output = ''.join([output,f'Current Players: {current_players if current_players else "0"}\n'])
|
|
output = ''.join([output,f'Platform: {platform}```' if platform is not None else '```'])
|
|
output = ''.join([output,f'\n\n{gm.mention} | {r.mention}'])
|
|
await ctx.send(result,hidden=True)
|
|
o = await t.send(output)
|
|
await o.pin(reason=f'/game create command issued by `{ctx.author.display_name}`')
|
|
data[guildStr][time][str(r.id)] = {
|
|
'game_title': game_title,
|
|
'gm': gm.id,
|
|
'max_players': max_players,
|
|
'min_players': min_players,
|
|
'current_players': current_players if current_players is not None else 0,
|
|
'system': system,
|
|
'platform': platform,
|
|
'role': r.id,
|
|
'category': c.id,
|
|
'text_channel': t.id,
|
|
'header_message': o.id
|
|
}
|
|
lookup[guildStr][str(r.id)] = {
|
|
'category': c.id,
|
|
'gm': gm.id,
|
|
'time': time,
|
|
'game_title': game_title,
|
|
'text_channel': t.id
|
|
}
|
|
if guildStr not in gms: gms[guildStr] = {}
|
|
if str(gm.id) not in gms[guildStr]: gms[guildStr][str(gm.id)] = []
|
|
if str(guildStr) not in categories: categories[guildStr] = {}
|
|
gms[guildStr][str(gm.id)].append(r.id)
|
|
if guildStr not in categories: categories[guildStr] = {}
|
|
categories[guildStr][str(c.id)] = r.id
|
|
yaml_dump(data,dataFile)
|
|
yaml_dump(lookup,lookupFile)
|
|
yaml_dump(gms,gmFile)
|
|
yaml_dump(categories,categoriesFile)
|
|
# Enable the Game Management and Player commands
|
|
if any([x for x in yaml_load(lookupFile).values()]):
|
|
flag = False
|
|
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('T-Card Command') is None:
|
|
loadCog(f'./{cogsDir}/slashcommands/secondary/tcard.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()
|
|
|
|
def setup(client):
|
|
client.add_cog(GameCreate(client)) |