2021-07-19 15:30:04 +01:00
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
2021-07-21 11:49:29 +01:00
from bot import configFile , yaml_load , yaml_dump , cogsDir , unloadCog , dataFile , lookupFile , gmFile , loadCog , categoriesFile
2021-07-19 15:30:04 +01:00
#### Game Role and Channel Setup Command
class GameSetup ( commands . Cog , name = ' Game Setup ' ) :
def __init__ ( self , client ) :
self . client = client
conf = yaml_load ( configFile )
permissions = { }
2021-07-21 11:49:29 +01:00
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 ] ) ) )
2021-07-19 15:30:04 +01:00
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 ' ,
2021-07-19 21:22:25 +01:00
description = ' The maximum number of players the game can take. ' ,
2021-07-19 15:30:04 +01:00
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 ' ,
2021-07-19 21:22:25 +01:00
description = ' The minimum number of players the game can take. ' ,
2021-07-19 15:30:04 +01:00
option_type = 4 ,
required = False
) ,
create_option (
2021-07-20 00:44:03 +01:00
name = ' current_players ' ,
description = ' The number of players currently in this game. ' ,
2021-07-19 15:30:04 +01:00
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 ,
2021-07-20 00:44:03 +01:00
current_players : typing . Optional [ int ] = None ,
2021-07-19 15:30:04 +01:00
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 )
2021-07-21 11:49:29 +01:00
categories = yaml_load ( categoriesFile )
2021-07-19 15:30:04 +01:00
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 ' ] :
2021-07-21 01:41:08 +01:00
await ctx . send ( f ' ``` \ `Bot` role for guild ` { ctx . guild . name } ` has not been defined. Cannot configure game.``` ' , hidden = True )
2021-07-19 15:30:04 +01:00
return
if ' timeslots ' not in conf [ guildStr ] :
conf [ guildStr ] [ ' timeslots ' ] = { }
if time not in conf [ guildStr ] [ ' timeslots ' ] :
2021-07-21 01:41:08 +01:00
await ctx . send ( f ' ```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 )
2021-07-19 15:30:04 +01:00
return
2021-07-20 00:44:03 +01:00
if min_players and min_players > max_players :
2021-07-21 01:41:08 +01:00
await ctx . send ( f ' ```The minimum number of players cannot exceed the maximum number of players.``` ' , hidden = True )
2021-07-20 00:44:03 +01:00
return
if current_players and current_players > max_players :
2021-07-21 01:41:08 +01:00
await ctx . send ( f ' ```The number of reserved spaces cannot exceed the maximum number of players.``` ' , hidden = True )
2021-07-20 00:44:03 +01:00
return
if any ( x is not None and x < 0 for x in [ min_players , max_players , current_players ] ) :
2021-07-21 01:41:08 +01:00
await ctx . send ( f ' ```You cannot enter negative integers for the number of players.``` ' , hidden = True )
2021-07-19 15:30:04 +01:00
return
if guildStr not in lookup :
lookup [ guildStr ] = { }
2021-07-21 23:05:13 +01:00
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 ( ) ] :
2021-07-21 01:41:08 +01:00
await ctx . send ( f ' ```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 )
2021-07-19 15:30:04 +01:00
return
if guildStr not in data :
data [ guildStr ] = { }
if time not in data [ guildStr ] :
2021-07-20 00:44:03 +01:00
data [ guildStr ] [ time ] = { }
2021-07-19 15:30:04 +01:00
rExists , cExists = False , False
r = discord . utils . get ( ctx . guild . roles , name = f ' { time . upper ( ) } : { game_title } ' )
2021-07-20 00:44:03 +01:00
if not r :
2021-07-19 15:30:04 +01:00
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 ( ) ,
2021-07-19 21:22:25 +01:00
colour = discord . Colour . green ( )
2021-07-19 15:30:04 +01:00
)
else :
rExists = True
await r . edit (
mentionable = True ,
permissions = discord . Permissions . none ( ) ,
reason = f ' /game create command issued by ` { ctx . author . display_name } ` ' ,
2021-07-19 21:22:25 +01:00
colour = discord . Colour . green ( )
2021-07-19 15:30:04 +01:00
)
2021-07-19 21:22:25 +01:00
if r not in gm . roles :
await gm . add_roles ( r )
2021-07-19 15:30:04 +01:00
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 } ' )
2021-07-20 00:44:03 +01:00
if not c :
2021-07-19 15:30:04 +01:00
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
2021-07-20 00:44:03 +01:00
if not t :
2021-07-19 15:30:04 +01:00
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 :
2021-07-20 00:44:03 +01:00
pins = await t . pins ( )
2021-07-19 15:30:04 +01:00
if pins :
2021-07-20 00:44:03 +01:00
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 )
2021-07-19 21:22:25 +01:00
if hm is not None :
await hm . delete ( )
2021-07-19 15:30:04 +01:00
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 } ` '
)
2021-07-20 00:44:03 +01:00
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 :
2021-07-19 15:30:04 +01:00
result = ' ' . join ( [ result , f ' There was already a role that matched the game, so that role has been reconfigured. \n \n Note: 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 :
2021-07-20 00:44:03 +01:00
result = ' ' . join ( [ result , f ' A role for the game has been created. \n \n Note: 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 :
2021-07-19 21:22:25 +01:00
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 ' ] )
2021-07-19 15:30:04 +01:00
else :
result = ' ' . join ( [ result , f ' A channel category with the appropriate text and voice channels has been created. \n ' ] )
2021-07-21 23:05:13 +01:00
result = ' ' . join ( [ ' ``` ' , result , f ' ``` \n { gm . mention } | { r . mention } | { t . mention } ' ] )
2021-07-19 15:30:04 +01:00
output = f ' ```Hello { gm . display_name } ! Your game channels for ` { game_title } ` have been created. \n You 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 ' ' ] )
2021-07-20 00:44:03 +01:00
output = ' ' . join ( [ output , f ' Current Players: { current_players if current_players else " 0 " } \n ' ] )
2021-07-19 15:30:04 +01:00
output = ' ' . join ( [ output , f ' Platform: { platform } ``` ' if platform is not None else ' ``` ' ] )
2021-07-21 01:41:08 +01:00
output = ' ' . join ( [ output , f ' \n \n { gm . mention } | { r . mention } ' ] )
await ctx . send ( result , hidden = True )
2021-07-19 15:30:04 +01:00
o = await t . send ( output )
await o . pin ( reason = f ' /game create command issued by ` { ctx . author . display_name } ` ' )
2021-07-20 00:44:03 +01:00
data [ guildStr ] [ time ] [ str ( r . id ) ] = {
2021-07-19 21:22:25 +01:00
' game_title ' : game_title ,
' gm ' : gm . id ,
' max_players ' : max_players ,
' min_players ' : min_players ,
2021-07-20 00:44:03 +01:00
' current_players ' : current_players ,
2021-07-19 21:22:25 +01:00
' 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 ,
2021-07-20 00:44:03 +01:00
' game_title ' : game_title ,
' text_channel ' : t . id
2021-07-19 21:22:25 +01:00
}
2021-07-21 11:49:29 +01:00
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 ] = { }
2021-07-19 15:30:04 +01:00
gms [ guildStr ] [ str ( gm . id ) ] . append ( r . id )
2021-07-21 11:49:29 +01:00
if guildStr not in categories : categories [ guildStr ] = { }
2021-07-21 23:05:13 +01:00
categories [ guildStr ] [ str ( c . id ) ] = r . id
2021-07-19 15:30:04 +01:00
yaml_dump ( data , dataFile )
yaml_dump ( lookup , lookupFile )
yaml_dump ( gms , gmFile )
2021-07-21 11:49:29 +01:00
yaml_dump ( categories , categoriesFile )
# Enable the Game Management and Player commands
if any ( [ x for x in yaml_load ( lookupFile ) . values ( ) ] ) :
flag = False
2021-07-19 15:30:04 +01:00
if self . client . get_cog ( ' Game Management ' ) is None :
loadCog ( f ' ./ { cogsDir } /slashcommands/secondary/game_management.py ' )
2021-07-21 11:49:29 +01:00
flag = True
if self . client . get_cog ( ' Player Commands ' ) is None :
loadCog ( f ' ./ { cogsDir } /slashcommands/secondary/player_commands.py ' )
flag = True
if flag : await self . client . slash . sync_all_commands ( )
2021-07-19 15:30:04 +01:00
def setup ( client ) :
client . add_cog ( GameSetup ( client ) )