2021-07-19 02:26:35 +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 re
2021-07-19 15:30:04 +01:00
from bot import configFile , yaml_load , yaml_dump , reloadCog , cogsDir , unloadCog , dataFile , lookupFile , gmFile
2021-07-19 02:26:35 +01:00
2021-07-19 15:30:04 +01:00
class GameManagement ( commands . Cog , name = ' Game Management ' ) :
2021-07-19 02:26:35 +01:00
def __init__ ( self , client ) :
self . client = client
2021-07-19 15:30:04 +01:00
conf = yaml_load ( configFile )
lookup = yaml_load ( lookupFile )
data = yaml_load ( dataFile )
guild_ids = [ int ( x ) for x in list ( lookup ) ]
### Move delete, Modify, and Reset commands to a separate secondary cog to enable when games exist?
2021-07-19 02:26:35 +01:00
@cog_ext.cog_subcommand (
base = ' game ' ,
# subcommand_group='',
name = ' delete ' ,
description = ' Deletes a game role and accompanying category, text, voice channels, and data. ' ,
# 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 = ' game_role ' ,
description = ' The role representing the game you want to interact with. ' ,
option_type = 8 ,
required = True
)
]
)
async def _game_delete ( self , ctx : SlashContext , game_role : discord . Role ) :
await ctx . channel . trigger_typing ( )
conf = yaml_load ( configFile )
data = yaml_load ( dataFile )
2021-07-19 15:30:04 +01:00
gms = yaml_load ( gmFile )
2021-07-19 02:26:35 +01:00
lookup = yaml_load ( lookupFile )
guildStr = str ( ctx . guild . id )
if str ( game_role . id ) not in lookup [ guildStr ] :
2021-07-19 15:30:04 +01:00
await ctx . send ( f ' ```This is not a valid game role. Please mention a role that is associated with a game.``` ' )
2021-07-19 02:26:35 +01:00
return
2021-07-19 15:30:04 +01:00
game_title = lookup [ guildStr ] [ str ( game_role . id ) ] [ ' game_title ' ]
2021-07-19 02:26:35 +01:00
time = lookup [ guildStr ] [ str ( game_role . id ) ] [ ' time ' ]
c = ctx . guild . get_channel ( lookup [ guildStr ] [ str ( game_role . id ) ] [ ' category ' ] )
2021-07-19 15:30:04 +01:00
gm = lookup [ guildStr ] [ str ( game_role . id ) ] [ ' gm ' ]
channelsFound = False
2021-07-19 02:26:35 +01:00
for g in list ( data [ guildStr ] [ time ] ) :
if game_role . id in g . values ( ) :
data [ guildStr ] [ time ] . remove ( g )
break
2021-07-19 15:30:04 +01:00
if c is not None :
channelsFound = True
for t in ctx . guild . text_channels :
if t . category == c :
await t . delete ( reason = f ' /game delete command issued by ` { ctx . author . display_name } ` ' )
for v in ctx . guild . voice_channels :
if v . category == c :
await v . delete ( reason = f ' /game delete command issued by ` { ctx . author . display_name } ` ' )
await c . delete ( reason = f ' /game delete command issued by ` { ctx . author . display_name } ` ' )
2021-07-19 02:26:35 +01:00
lookup [ guildStr ] . pop ( str ( game_role . id ) , None )
2021-07-19 15:30:04 +01:00
gm_m = await ctx . guild . fetch_member ( gm )
output = f ' The game ` { game_title } ` for timeslot ` { conf [ guildStr ] [ " timeslots " ] [ time ] } ` and with GM ` { gm_m . display_name } ` has been deleted. '
if channelsFound :
output = ' ' . join ( [ output , ' All associated text, voice, and category channels have been deleted. ' ] )
else :
output = ' ' . join ( [ output , ' No associated text, voice, or category channels were found. Please delete them manually if they still persist. ' ] )
await ctx . send ( f ' ``` { output } ``` ' )
2021-07-19 02:26:35 +01:00
await game_role . delete ( reason = f ' /game delete command issued by ` { ctx . author . display_name } ` ' )
2021-07-19 15:30:04 +01:00
gms [ guildStr ] [ str ( gm ) ] . remove ( game_role . id )
if not gms [ guildStr ] [ str ( gm ) ] :
gms [ guildStr ] . pop ( str ( gm ) )
2021-07-19 02:26:35 +01:00
yaml_dump ( lookup , lookupFile )
yaml_dump ( data , dataFile )
2021-07-19 15:30:04 +01:00
yaml_dump ( gms , gmFile )
if not any ( [ x for x in yaml_load ( lookupFile ) . values ( ) ] ) :
unloadCog ( f ' ./ { cogsDir } /slashcommands/secondary/game_management.py ' )
await self . client . slash . sync_all_commands ( )
2021-07-19 21:22:25 +01:00
@cog_ext.cog_subcommand (
base = ' game ' ,
# subcommand_group='',
name = ' modify ' ,
description = ' Edit the information of an existing game channel. ' ,
# 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 = ' game_role ' ,
description = ' The role of the game you are trying to edit. ' ,
option_type = 8 ,
required = True ,
) ,
create_option (
name = ' timeslot ' ,
description = ' The new timeslot, if you are changing timeslots. ' ,
option_type = 3 ,
required = False
) ,
create_option (
name = ' gm ' ,
description = ' The new GM, if the GM is changing. ' ,
option_type = 6 ,
required = False
) ,
create_option (
name = ' max_players ' ,
description = ' The maximum number of players the game can take. ' ,
option_type = 4 ,
required = False
) ,
create_option (
name = ' game_title ' ,
description = ' The new title if the title is changing. ' ,
option_type = 3 ,
required = False
) ,
create_option (
name = ' min_players ' ,
description = ' The minimum number of players the gane can take. ' ,
option_type = 4 ,
required = False
) ,
create_option (
name = ' reserved_spaces ' ,
description = ' The number of spaces the GM is reserving in the 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_modify (
self ,
ctx : SlashContext ,
game_role : discord . Role ,
timeslot : str = None ,
gm : discord . User = None ,
max_players : int = None ,
game_title : str = None ,
min_players : int = None ,
reserved_spaces : int = None ,
system : str = None ,
platform : str = None
) :
await ctx . channel . trigger_typing ( )
if all ( x is None for x in [ timeslot , gm , max_players , game_title , min_players , reserved_spaces , system , platform ] ) :
await ctx . send ( f ' ```No parameters have been entered to modify the game.``` ' )
return
conf = yaml_load ( configFile )
data = yaml_load ( dataFile )
gms = yaml_load ( gmFile )
lookup = yaml_load ( lookupFile )
guildStr = str ( ctx . guild . id )
if guildStr not in lookup :
lookup [ guildStr ] = { }
if guildStr not in data :
data [ guildStr ] = { }
r = game_role
if timeslot is not None :
time = re . sub ( r " \ W+ " , ' ' , timeslot [ : 9 ] . lower ( ) )
if time not in conf [ guildStr ] [ ' timeslots ' ] :
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.``` ' )
return
if time not in data [ guildStr ] :
data [ guildStr ] [ time ] = [ ]
else :
time = lookup [ guildStr ] [ str ( r . id ) ] [ ' time ' ]
g_title = game_title if game_title is not None else lookup [ guildStr ] [ str ( r . id ) ] [ ' game_title ' ]
if str ( r . id ) not in lookup [ guildStr ] :
await ctx . send ( f ' ```This is not a valid game role. Please mention a role that is associated with a game.``` ' )
return
if ' roles ' not in conf [ guildStr ] :
conf [ guildStr ] [ ' roles ' ] = { }
if ' bot ' not in conf [ guildStr ] [ ' roles ' ] :
await ctx . send ( f ' ``` \ `Bot` role for guild ` { ctx . guild . name } ` has not been defined. Cannot configure game.``` ' )
return
if ' timeslots ' not in conf [ guildStr ] :
conf [ guildStr ] [ ' timeslots ' ] = { }
if any ( x is not None and x < 0 for x in [ min_players , max_players , reserved_spaces ] ) :
await ctx . send ( f ' ```You cannot enter negative integers for the number of players.``` ' )
return
if min_players is not None and max_players is not None :
if min_players > max_players :
await ctx . send ( f ' ```The minimum number of players cannot exceed the maximum number of players.``` ' )
return
if reserved_spaces is not None and max_players is not None :
if reserved_spaces > max_players :
await ctx . send ( f ' ```The number of reserved spaces cannot exceed the maximum number of players.``` ' )
return
if game_title is not None :
if game_title in [ x [ ' game_title ' ] for x in lookup [ guildStr ] . values ( ) ] and time in [ x [ ' time ' ] for x in lookup [ guildStr ] . values ( ) ] :
await ctx . send ( f ' ```The target 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.``` ' )
return
mc = gm if gm is not None else ctx . guild . get_member ( lookup [ guildStr ] [ str ( r . id ) ] [ ' gm ' ] )
#### Retrieve Old Values
oldTime = lookup [ guildStr ] [ str ( r . id ) ] [ ' time ' ]
for g in data [ guildStr ] [ oldTime ] :
if game_role . id in g . values ( ) :
oldDict = g . copy ( )
break
gDict = {
' game_title ' : game_title ,
' gm ' : mc . id ,
' max_players ' : max_players ,
' min_players ' : min_players ,
' current_players ' : reserved_spaces ,
' system ' : system ,
' platform ' : platform ,
# 'role': r.id,
# 'category': c.id,
# 'text_channel': t.id,
# 'header_message': o.id
}
d = [ k for k in gDict if gDict [ k ] != oldDict [ k ] and gDict [ k ] != None ]
result = f ' The game { g_title } has been updated. \n '
if time != oldTime :
for g in list ( data [ guildStr ] [ oldTime ] ) :
if game_role . id in g . values ( ) :
data [ guildStr ] [ oldTime ] . remove ( g )
break
data [ guildStr ] [ oldTime ] . pop ( str ( r . id ) , None )
result = ' ' . join ( [ result , f ' The time slot for the game has been updated to { conf [ guildStr ] [ " timeslots " ] [ time ] } . \n ' ] )
if ' gm ' in d :
if r not in mc . roles :
await mc . add_roles ( r )
if guildStr not in gms :
gms [ guildStr ] = { }
if str ( mc . id ) not in gms [ guildStr ] :
gms [ guildStr ] [ str ( gm . id ) ] = [ ]
gms [ guildStr ] [ str ( mc . id ) ] . append ( r . id )
gms [ guildStr ] [ str ( lookup [ guildStr ] [ str ( r . id ) ] [ ' gm ' ] ) ] . remove ( r . id )
if not gms [ guildStr ] [ str ( lookup [ guildStr ] [ str ( r . id ) ] [ ' gm ' ] ) ] :
gms [ guildStr ] . pop ( str ( lookup [ guildStr ] [ str ( r . id ) ] [ ' gm ' ] ) , None )
result = ' ' . join ( [ result , f ' The GM has been updated to { mc . display_name } . \n ' ] )
c = await self . client . fetch_channel ( lookup [ guildStr ] [ str ( r . id ) ] [ ' category ' ] )
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
)
}
print ( ' So far so good. ' )
if time != oldTime or ' game_title ' in d :
await r . edit (
name = f ' { time . upper ( ) } : { g_title } ' ,
reason = f ' /game modify command issued by ` { ctx . author . display_name } ` ' ,
mentionable = True ,
permissions = discord . Permissions . none ( ) ,
)
result = ' ' . join ( [ result , f ' The names of the role and channel category have been updated to match the new { " game title " if " game_title " in d and time == oldTime else " time slot " if " game_title " not in d and time != oldTime else " game title and time slot " } . \n ' ] )
if c is None :
c = await ctx . guild . create_category (
name = f ' { time . upper ( ) } : { g_title } ' ,
overwrites = permissions ,
reason = f ' /game modify command issued by ` { ctx . author . display_name } ` '
)
await c . create_voice_channel (
name = f ' voice: { g_title } ' ,
topic = f ' Default voice channel for the game ` { g_title } `` taking place at ` { conf [ guildStr ] [ " timeslots " ] [ time ] } `, with GM ` { mc . display_name } `. ' ,
reason = f ' /game create command issued by ` { ctx . author . display_name } ` '
)
t = await c . create_text_channel (
name = f ' text: { g_title } ' ,
topic = f ' Default text channel for the game ` { g_title } `` taking place at ` { conf [ guildStr ] [ " timeslots " ] [ time ] } `, with GM ` { mc . display_name } `. ' ,
reason = f ' /game create command issued by ` { ctx . author . display_name } ` '
)
else :
print ( type ( c ) )
await c . edit (
name = f ' { time . upper ( ) } : { g_title } ' ,
overwrites = permissions ,
reason = f ' /game modify 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 modify command issued by ` { ctx . author . display_name } ` '
)
for vc in c . voice_channels :
await vc . edit (
sync_permissions = True ,
reason = f ' /game modify command issued by ` { ctx . author . display_name } ` '
)
v = True
if t is None :
t = await c . create_text_channel (
name = f ' text: { g_title } ' ,
topic = f ' Default text channel for the game ` { g_title } `` taking place at ` { conf [ guildStr ] [ " timeslots " ] [ time ] } `, with GM ` { mc . display_name } `. ' ,
reason = f ' /game modify command issued by ` { ctx . author . display_name } ` '
)
else :
hm = await t . fetch_message ( oldDict [ ' header_message ' ] )
if hm is not None :
await hm . delete ( )
else :
pins = await t . pins
if pins :
hm = discord . utils . find ( lambda x : x . text . startswith ( ' ```Hello ' and ' Your game channels for ' in x . text 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: { g_title } ' ,
topic = f ' Default voice channel for the game ` { g_title } `` taking place at ` { conf [ guildStr ] [ " timeslots " ] [ time ] } `, with GM ` { mc . display_name } `. ' ,
reason = f ' /game modify command issued by ` { ctx . author . display_name } ` '
)
result = ' ' . join ( [ result , f ' The game has space for { max_players } players (with { reserved_spaces if reserved_spaces is not None else str ( 0 ) } currently occupied). \n ' ] )
sys = system if system is not None else oldDict [ ' system ' ]
max_p = max_players if max_players is not None else oldDict [ ' max_players ' ]
min_p = min_players if min_players is not None else oldDict [ ' min_players ' ]
cur_p = reserved_spaces if reserved_spaces is not None else oldDict [ ' current_players ' ]
plat = platform if platform is not None else oldDict [ ' platform ' ]
output = f ' ```Hello { mc . display_name } ! Your game channels for ` { g_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: { g_title } \n ' ] )
output = ' ' . join ( [ output , f ' GM: { mc . display_name } \n ' ] )
output = ' ' . join ( [ output , f ' Time: { conf [ guildStr ] [ " timeslots " ] [ time ] } \n ' ] )
output = ' ' . join ( [ output , f ' System: { sys } \n ' if sys is not None else ' ' ] )
output = ' ' . join ( [ output , f ' Max Players: { max_p } \n ' ] )
output = ' ' . join ( [ output , f ' Min Players: { min_p } \n ' if min_p is not None else ' ' ] )
output = ' ' . join ( [ output , f ' Current Players: { cur_p } \n ' if cur_p is not None else ' 0 \n ' ] )
output = ' ' . join ( [ output , f ' Platform: { plat } ``` ' if plat is not None else ' ``` ' ] )
result = ' ' . join ( [ ' ``` ' , result , f ' ``` \n { mc . mention } { r . mention } { t . mention } ' ] )
await ctx . send ( result )
o = await t . send ( output )
await o . pin ( reason = f ' /game modift command issued by ` { ctx . author . display_name } ` ' )
gDict = {
' game_title ' : g_title ,
' gm ' : mc . id ,
' max_players ' : max_p ,
' min_players ' : min_p ,
' current_players ' : cur_p ,
' system ' : sys ,
' platform ' : plat ,
' role ' : r . id ,
' category ' : c . id ,
' text_channel ' : t . id ,
' header_message ' : o . id
}
if time != oldTime :
data [ guildStr ] [ time ] . append ( gDict )
lookup [ guildStr ] [ str ( r . id ) ] = {
' category ' : c . id ,
' gm ' : mc . id ,
' time ' : time ,
' game_title ' : g_title
}
yaml_dump ( data , dataFile )
yaml_dump ( lookup , lookupFile )
yaml_dump ( gms , gmFile )
2021-07-19 02:26:35 +01:00
def setup ( client ) :
2021-07-19 15:30:04 +01:00
client . add_cog ( GameManagement ( client ) )