2021-08-24 17:42:00 +00:00
import discord , json , math , os , roman , games , asyncio , random , main_controller , threading , time , urllib , leagues , datetime , gametext , real_players , archetypes , sys , traceback
2020-12-20 00:08:09 +00:00
import database as db
2020-12-20 01:26:05 +00:00
import onomancer as ono
2021-07-06 01:26:55 +00:00
from league_storage import league_exists , season_save , season_restart , get_mods , get_team_mods , set_mods
2021-03-28 19:16:31 +00:00
from the_draft import Draft
2020-12-28 08:21:47 +00:00
from flask import Flask
2021-01-05 19:26:54 +00:00
from uuid import uuid4
2022-09-18 14:00:28 +00:00
from typing import Optional
from discord import app_commands
2021-02-21 08:03:25 +00:00
import weather
2020-12-28 08:21:47 +00:00
2021-01-06 10:53:51 +00:00
data_dir = " data "
config_filename = os . path . join ( data_dir , " config.json " )
2022-06-16 01:59:51 +00:00
app = main_controller . app
2020-12-20 00:08:09 +00:00
2022-09-18 14:00:28 +00:00
class newClient ( discord . Client ) :
def __init__ ( self , * , intents : discord . Intents ) :
super ( ) . __init__ ( intents = intents )
self . tree = app_commands . CommandTree ( self )
async def setup_hook ( self ) :
await self . tree . sync ( )
client = newClient ( intents = discord . Intents . default ( ) )
2020-12-27 08:42:09 +00:00
class Command :
def isauthorized ( self , user ) :
return True
2020-12-20 00:08:09 +00:00
2022-09-18 14:00:28 +00:00
async def execute ( self , int , command , flags ) :
2020-12-22 20:02:28 +00:00
return
2022-09-18 14:00:28 +00:00
async def reply ( self , interaction , message , embed = None , ephemeral = False ) :
await interaction . response . send_message ( message , embed = embed , ephemeral = ephemeral )
2022-09-18 15:08:24 +00:00
def process_flags ( flag_list ) :
flags = [ ]
if " - " in flag_list :
check = flag_list . split ( " - " ) [ 1 : ]
for flag in [ _ for _ in check if _ != " " ] :
try :
flags . append ( ( flag . split ( " " ) [ 0 ] [ 0 ] . lower ( ) , flag . split ( " " , 1 ) [ 1 ] . strip ( ) ) )
except IndexError :
flags . append ( ( flag . split ( " " ) [ 0 ] [ 0 ] . lower ( ) , None ) )
return flags
2021-01-04 23:52:34 +00:00
class DraftError ( Exception ) :
pass
class SlowDraftError ( DraftError ) :
pass
2020-12-28 09:09:02 +00:00
class CommandError ( Exception ) :
pass
2020-12-27 08:42:09 +00:00
class IntroduceCommand ( Command ) :
name = " introduce "
template = " "
description = " "
2020-12-22 20:02:28 +00:00
2020-12-27 08:42:09 +00:00
def isauthorized ( self , user ) :
return user . id in config ( ) [ " owners " ]
2020-12-20 01:26:05 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2022-09-18 14:00:28 +00:00
text = """ Our avatar was graciously provided to us, with permission, by @HetreaSky on Twitter.
2020-12-27 08:42:09 +00:00
"""
await msg . channel . send ( text )
2020-12-20 05:13:23 +00:00
2020-12-27 09:03:41 +00:00
class CountActiveGamesCommand ( Command ) :
name = " countactivegames "
template = " "
description = " "
2020-12-22 20:02:28 +00:00
2020-12-27 09:03:41 +00:00
def isauthorized ( self , user ) :
return user . id in config ( ) [ " owners " ]
2020-12-20 01:26:05 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-27 09:03:41 +00:00
await msg . channel . send ( f " There ' s { len ( gamesarray ) } active games right now, boss. " )
2020-12-20 05:13:23 +00:00
2020-12-27 08:42:09 +00:00
class RomanCommand ( Command ) :
name = " roman "
template = " m;roman [number] "
description = " Converts any natural number less than 4,000,000 into roman numerals. This one is just for fun. "
2020-12-20 05:13:23 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-20 10:28:47 +00:00
try :
2020-12-27 08:42:09 +00:00
await msg . channel . send ( roman . roman_convert ( command ) )
2020-12-20 10:28:47 +00:00
except ValueError :
2021-03-26 21:38:03 +00:00
raise CommandError ( f " \" { command } \" isn ' t an integer in Arabic numerals. " )
2020-12-27 08:42:09 +00:00
class ShowPlayerCommand ( Command ) :
name = " showplayer "
2022-09-18 14:00:28 +00:00
template = " showplayer [name] "
2021-01-04 16:42:02 +00:00
description = " Displays any name ' s stars in a nice discord embed, there ' s a limit of 70 characters. That should be *plenty*. Note: if you want to lookup a lot of different players you can do it on onomancer here instead of spamming this command a bunch and clogging up discord: <https://onomancer.sibr.dev/reflect> "
2020-12-27 08:42:09 +00:00
2022-09-18 14:00:28 +00:00
@client.tree.command ( )
@app_commands.rename ( command = " name " )
async def showplayer ( interaction : discord . Interaction , command : str ) :
2023-03-31 06:18:28 +00:00
""" Show a single player ' s stats. """
2022-09-18 15:08:24 +00:00
player_name = json . loads ( ono . get_stats ( command ) )
2022-09-18 14:00:28 +00:00
await interaction . response . send_message ( embed = build_star_embed ( player_name ) )
2020-12-24 09:51:38 +00:00
2020-12-27 08:42:09 +00:00
class StartGameCommand ( Command ) :
name = " startgame "
template = " m;startgame [away] [home] [innings] "
2021-01-08 18:10:01 +00:00
description = """ Starts a game with premade teams made using saveteam, use this command at the top of a list followed by each of these in a new line (shift+enter in discord, or copy+paste from notepad):
2020-12-28 16:05:19 +00:00
- the away team ' s name.
- the home team ' s name.
2021-01-08 18:10:01 +00:00
- and finally , optionally , the number of innings , which must be greater than 2 and less than 201. if not included it will default to 9.
2021-01-08 18:15:50 +00:00
- this command has fuzzy search so you don ' t need to type the full name of the team as long as you give enough to identify the team you ' re looking for . """
2020-12-24 09:51:38 +00:00
2022-09-18 15:08:24 +00:00
@client.tree.command ( )
2022-09-18 16:02:34 +00:00
@app_commands.rename ( wthr = " weather " )
@app_commands.choices ( wthr = weather . weather_choices ( ) )
async def startgame ( interaction , away : str , home : str , innings : Optional [ int ] = 9 , wthr : Optional [ app_commands . Choice [ str ] ] = None , flags : Optional [ str ] = " " ) :
2023-03-31 06:18:28 +00:00
""" Start a game with the given teams and, optionally, weather choice and custom inning number. """
2022-09-18 15:08:24 +00:00
league = None
day = None
voice = None
if config ( ) [ " game_freeze " ] :
raise CommandError ( " Patch incoming. We ' re not allowing new games right now. " )
2021-03-26 21:38:03 +00:00
2022-09-18 15:08:24 +00:00
flags = process_flags ( flags )
for flag in flags :
if flag [ 0 ] == " l " :
league = flag [ 1 ]
elif flag [ 0 ] == " d " :
try :
day = int ( flag [ 1 ] )
except :
raise CommandError ( " Make sure you put an integer after the -d flag. " )
elif flag [ 0 ] == " v " or flag [ 0 ] == " a " :
if flag [ 1 ] in gametext . all_voices ( ) :
voice = gametext . all_voices ( ) [ flag [ 1 ] ]
2021-03-26 21:38:03 +00:00
else :
2022-09-18 15:08:24 +00:00
raise CommandError ( " We can ' t find that broadcaster. " )
else :
raise CommandError ( " One or more of those flags wasn ' t right. Try and fix that for us and we ' ll see about sorting you out. " )
2021-03-26 21:38:03 +00:00
2022-09-18 15:08:24 +00:00
try :
team_name1 = away
team1 = get_team_fuzzy_search ( team_name1 )
team_name2 = home
team2 = get_team_fuzzy_search ( team_name2 )
except IndexError :
raise CommandError ( " We need at least three lines: startgame, away team, and home team are required. Optionally, the number of innings can go at the end, if you want a change of pace. " )
except IndexError :
pass
except ValueError :
raise CommandError ( " That number of innings isn ' t even an integer, chief. We can ' t do fractional innings, nor do we want to. " )
2020-12-26 05:27:14 +00:00
2022-09-18 15:08:24 +00:00
if innings is not None and innings < 2 and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " Anything less than 2 innings isn ' t even an outing. Try again. " )
2020-12-27 01:27:03 +00:00
2022-09-18 15:08:24 +00:00
elif innings is not None and innings > 200 and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " Y ' all can behave, so we ' ve upped the limit on game length to 200 innings. " )
if team1 is not None and team2 is not None :
game = games . game ( team1 . finalize ( ) , team2 . finalize ( ) , length = innings )
if day is not None :
game . teams [ ' away ' ] . set_pitcher ( rotation_slot = day )
game . teams [ ' home ' ] . set_pitcher ( rotation_slot = day )
if voice is not None :
game . voice = voice ( )
channel = interaction . channel
2020-12-27 05:52:34 +00:00
2022-09-18 16:02:34 +00:00
if wthr is not None :
game . weather = weather . all_weathers ( ) [ wthr . value ] ( game )
2021-02-11 23:34:26 +00:00
2022-09-18 15:08:24 +00:00
game_task = asyncio . create_task ( watch_game ( channel , game , user = interaction . user , league = league , interaction = interaction ) )
await game_task
else :
raise CommandError ( " We can ' t find one or both of those teams. Check your staging, chief. " )
2020-12-22 06:56:33 +00:00
2021-01-03 22:59:13 +00:00
class StartRandomGameCommand ( Command ) :
name = " randomgame "
2022-09-18 15:27:36 +00:00
template = " randomgame "
2021-01-04 16:42:02 +00:00
description = " Starts a 9-inning game between 2 entirely random teams. Embrace chaos! "
2021-01-03 22:59:13 +00:00
2022-09-18 15:27:36 +00:00
@client.tree.command ( )
async def randomgame ( interaction ) :
2023-03-31 06:18:28 +00:00
""" Start a game between two random teams. """
2022-09-18 15:27:36 +00:00
if config ( ) [ " game_freeze " ] :
raise CommandError ( " Patch incoming. We ' re not allowing new games right now. " )
2021-01-04 20:24:19 +00:00
2022-09-18 15:27:36 +00:00
channel = interaction . channel
await interaction . response . send_message ( " Rolling the bones... This might take a while. " )
teamslist = games . get_all_teams ( )
2021-01-03 22:59:13 +00:00
2022-09-18 15:27:36 +00:00
game = games . game ( random . choice ( teamslist ) . finalize ( ) , random . choice ( teamslist ) . finalize ( ) )
2021-01-03 22:59:13 +00:00
2022-09-18 15:27:36 +00:00
game_task = asyncio . create_task ( watch_game ( channel , game , user = " the winds of chaos " ) )
await game_task
2021-01-04 04:23:02 +00:00
2020-12-27 08:42:09 +00:00
class SaveTeamCommand ( Command ) :
name = " saveteam "
2023-03-31 06:18:28 +00:00
template = """ m;saveteam [bot ping]
2021-01-02 06:10:52 +00:00
[ name ]
[ slogan ]
[ lineup ]
2023-03-31 06:18:28 +00:00
[ rotation ]
"""
2021-01-02 06:10:52 +00:00
2020-12-28 16:05:19 +00:00
description = """ Saves a team to the database allowing it to be used for games. Send this command at the top of a list, with entries separated by new lines (shift+enter in discord, or copy+paste from notepad).
2021-01-04 16:42:02 +00:00
- the first line of the list is your team ' s name.
- the second line is the team ' s icon and slogan, generally this is an emoji followed by a space, followed by a short slogan.
2021-01-02 06:10:52 +00:00
- the third line must be blank .
2020-12-28 16:05:19 +00:00
- the next lines are your batters ' names in the order you want them to appear in your lineup, lineups can contain any number of batters between 1 and 12.
2021-01-02 06:10:52 +00:00
- there must be another blank line between your batters and your pitchers .
2021-01-04 16:42:02 +00:00
- the final lines are the names of the pitchers in your rotation , rotations can contain any number of pitchers between 1 and 8.
2023-03-31 06:18:28 +00:00
If you did it correctly , you ' ll get a team embed with a prompt to confirm. hit the 👍 and your team will be saved! " " "
2020-12-28 16:05:19 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-28 09:09:02 +00:00
if db . get_team ( command . split ( ' \n ' , 1 ) [ 1 ] . split ( " \n " ) [ 0 ] ) == None :
2020-12-29 21:10:57 +00:00
await msg . channel . send ( f " Fetching players... " )
2020-12-28 09:09:02 +00:00
team = team_from_message ( command )
save_task = asyncio . create_task ( save_team_confirm ( msg , team ) )
2020-12-26 09:46:42 +00:00
await save_task
else :
name = command . split ( ' \n ' , 1 ) [ 1 ] . split ( ' \n ' ) [ 0 ]
2021-03-26 21:38:03 +00:00
raise CommandError ( f " { name } already exists. Try a new name, maybe? " )
2020-12-22 06:56:33 +00:00
2023-03-31 06:18:28 +00:00
@client.tree.command ( )
async def newteam ( interaction ) :
""" Get new team instructions. Sent privately, don ' t worry! """
await interaction . response . send_message ( SaveTeamCommand . template + SaveTeamCommand . description , ephemeral = True )
2021-07-06 05:39:19 +00:00
class AssignArchetypeCommand ( Command ) :
name = " archetype "
template = " m;archetype [team name] \n [player name] \n [archetype name] "
2021-07-09 01:59:20 +00:00
description = """ Assigns an archetype to a player on your team. This can be changed at any time! For a description of a specific archetype, or a list of all archetypes, use m;archetypehelp. """
2021-07-06 05:39:19 +00:00
2022-09-18 15:49:50 +00:00
@client.tree.command ( )
@app_commands.choices ( archetype = archetypes . archetype_choices ( ) )
2023-03-31 06:18:28 +00:00
async def setarchetype ( interaction , team : str , player : str , archetype : app_commands . Choice [ str ] ) :
""" Assigns an archetype to a player on an owned team. Reversible. """
2022-09-18 15:49:50 +00:00
try :
team = get_team_fuzzy_search ( team )
player_name = player
archetype_name = archetype . value
except IndexError :
raise CommandError ( " You didn ' t give us enough info, boss. Check the help text. " )
if team is None :
raise CommandError ( " We can ' t find that team. " )
2021-07-06 05:39:19 +00:00
2022-09-18 15:49:50 +00:00
team , ownerid = games . get_team_and_owner ( team . name )
if ownerid != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " That team ain ' t yours, and we ' re not about to help you cheat. " )
2021-07-06 05:39:19 +00:00
2022-09-18 15:49:50 +00:00
player = team . find_player ( player_name ) [ 0 ]
if player is None :
raise CommandError ( " That player isn ' t on your team, boss. " )
2021-07-06 05:39:19 +00:00
2022-09-18 15:49:50 +00:00
archetype = archetypes . search_archetypes ( archetype_name )
if archetype is None :
raise CommandError ( " We can ' t find that archetype, chief. Try m;archetypehelp. " )
2021-07-06 05:39:19 +00:00
2022-09-18 15:49:50 +00:00
try :
team . archetypes [ player . name ] = archetype
except AttributeError :
team . archetypes = { player . name : archetype }
games . update_team ( team )
2021-07-06 05:39:19 +00:00
2022-09-18 15:49:50 +00:00
await interaction . response . send_message ( " Player specialization is a beautiful thing, ain ' t it? Here ' s hoping they like it. " )
2021-07-06 05:39:19 +00:00
2021-07-06 05:53:53 +00:00
class ArchetypeHelpCommand ( Command ) :
2021-07-09 01:59:20 +00:00
name = " archetypehelp "
2022-09-18 16:10:11 +00:00
template = " archetypehelp [archetype name] "
description = " Describes a given archetype. "
2021-07-06 05:53:53 +00:00
2022-09-18 16:10:11 +00:00
@client.tree.command ( )
@app_commands.choices ( archetype = archetypes . archetype_choices ( ) )
async def archetypehelp ( interaction , archetype : app_commands . Choice [ str ] ) :
2023-03-31 06:18:28 +00:00
""" Describes a specific archetype. """
2022-09-18 16:10:11 +00:00
arch = archetypes . search_archetypes ( archetype . value )
if arch is None :
raise CommandError ( " We don ' t know what that archetype is. If you ' re trying to break new ground, here isn ' t the time *or* the place. " )
await interaction . response . send_message ( f """ { arch . display_name }
2021-07-06 05:53:53 +00:00
Short name : { arch . name }
{ arch . description } """ )
2021-07-06 05:39:19 +00:00
2021-07-09 01:59:20 +00:00
class ViewArchetypesCommand ( Command ) :
name = " teamarchetypes "
2022-09-18 16:37:55 +00:00
template = " teamarchetypes [team name] "
2021-07-09 01:59:20 +00:00
description = " Lists the current archetypes on the given team. "
2022-09-18 16:23:38 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " )
2022-09-18 16:37:55 +00:00
async def teamarchetypes ( interaction , team_name : str ) :
2023-03-31 06:18:28 +00:00
""" Lists the current archetypes on a team. """
2022-09-18 16:23:38 +00:00
team = get_team_fuzzy_search ( team_name )
if team is None :
raise CommandError ( " We can ' t find that team, boss. " )
elif team . archetypes == { } :
raise CommandError ( " That team doesn ' t have any specializations set. " )
embed_string = " "
for player_name , archetype in team . archetypes . items ( ) :
embed_string + = f " ** { player_name } **: \n { archetype . display_name } \n \n "
embed = discord . Embed ( color = discord . Color . dark_green ( ) , title = f " { team . name } Archetypes " )
embed . add_field ( name = " - " , value = embed_string )
await interaction . reaction . send_message ( embed = embed )
2021-07-09 01:59:20 +00:00
2020-12-28 09:09:02 +00:00
class ImportCommand ( Command ) :
name = " import "
template = " m;import [onomancer collection URL] "
2022-09-18 16:37:55 +00:00
description = " Imports an onomancer collection as a new team. You can use the new onomancer simsim setting to ensure compatibility. Similarly to saveteam, you ' ll get a team embed with a prompt to confirm, hit the 👍 and your team will be saved! Only functions in bot DMs. "
2020-12-28 09:09:02 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-28 09:09:02 +00:00
team_raw = ono . get_collection ( command . strip ( ) )
if not team_raw == None :
team_json = json . loads ( team_raw )
if db . get_team ( team_json [ " fullName " ] ) == None :
team = team_from_collection ( team_json )
await asyncio . create_task ( save_team_confirm ( msg , team ) )
else :
2021-03-26 21:38:03 +00:00
raise CommandError ( f " { team_json [ ' fullName ' ] } already exists. Try a new name, maybe? " )
2020-12-28 09:09:02 +00:00
else :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Something went pear-shaped while we were looking for that collection. You certain it ' s a valid onomancer URL? " )
2020-12-28 09:09:02 +00:00
2020-12-27 08:42:09 +00:00
class ShowTeamCommand ( Command ) :
name = " showteam "
template = " m;showteam [name] "
2021-01-04 16:42:02 +00:00
description = " Shows the lineup, rotation, and slogan of any saved team in a discord embed with primary stat star ratings for all of the players. This command has fuzzy search so you don ' t need to type the full name of the team as long as you give enough to identify the team you ' re looking for. "
2020-12-27 08:42:09 +00:00
2022-09-18 16:37:55 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " )
async def showteam ( interaction , team_name : str ) :
2023-03-31 06:18:28 +00:00
""" Display a team ' s roster and relevant ratings. """
2022-09-18 16:37:55 +00:00
team = get_team_fuzzy_search ( team_name )
if team is not None :
await interaction . response . send_message ( embed = build_team_embed ( team ) )
return
raise CommandError ( " Can ' t find that team, boss. Typo? " )
2020-12-21 09:46:12 +00:00
2020-12-27 08:42:09 +00:00
class ShowAllTeamsCommand ( Command ) :
name = " showallteams "
template = " m;showallteams "
2020-12-28 16:05:19 +00:00
description = " Shows a paginated list of all teams available for games which can be scrolled through. "
2020-12-27 08:42:09 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-26 09:46:42 +00:00
list_task = asyncio . create_task ( team_pages ( msg , games . get_all_teams ( ) ) )
await list_task
2020-12-27 08:42:09 +00:00
class SearchTeamsCommand ( Command ) :
name = " searchteams "
template = " m;searchteams [searchterm] "
2020-12-28 16:05:19 +00:00
description = " Shows a paginated list of all teams whose names contain the given search term. "
2020-12-27 08:42:09 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-27 08:42:09 +00:00
search_term = command . strip ( )
2020-12-26 10:09:49 +00:00
if len ( search_term ) > 30 :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Team names can ' t even be that long, chief. Try something shorter. " )
2020-12-26 10:09:49 +00:00
list_task = asyncio . create_task ( team_pages ( msg , games . search_team ( search_term ) , search_term = search_term ) )
2020-12-26 09:46:42 +00:00
await list_task
2020-12-27 08:42:09 +00:00
class CreditCommand ( Command ) :
name = " credit "
template = " m;credit "
description = " Shows artist credit for matteo ' s avatar. "
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-20 05:17:05 +00:00
await msg . channel . send ( " Our avatar was graciously provided to us, with permission, by @HetreaSky on Twitter. " )
2020-12-20 05:13:23 +00:00
2023-03-31 06:18:28 +00:00
@client.tree.command ( )
async def credit ( interaction ) :
""" Show artist credit for the bot ' s avatar. """
await interaction . response . send_message ( " Our avatar was graciously provided to us, with permission, by @HetreaSky on Twitter. " )
2021-01-02 06:10:52 +00:00
class SwapPlayerCommand ( Command ) :
2021-01-02 09:02:57 +00:00
name = " swapsection "
template = """ m;swapsection
2021-01-02 06:10:52 +00:00
[ team name ]
[ player name ] """
2021-01-04 16:42:02 +00:00
description = " Swaps a player from your lineup to the end of your rotation or your rotation to the end of your lineup. Requires team ownership and exact spelling of team name. "
2021-01-02 06:10:52 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-01-03 08:55:03 +00:00
try :
team_name = command . split ( " \n " ) [ 1 ] . strip ( )
player_name = command . split ( " \n " ) [ 2 ] . strip ( )
team , owner_id = games . get_team_and_owner ( team_name )
if team is None :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Can ' t find that team, boss. Typo? " )
2023-03-31 06:18:28 +00:00
elif owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
2021-03-26 21:38:03 +00:00
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
2021-01-03 08:55:03 +00:00
elif not team . swap_player ( player_name ) :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Either we can ' t find that player, you ' ve got no space on the other side, or they ' re your last member of that side of the roster. Can ' t field an empty lineup, and we *do* have rules, chief. " )
2021-01-03 08:55:03 +00:00
else :
await msg . channel . send ( embed = build_team_embed ( team ) )
games . update_team ( team )
await msg . channel . send ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
except IndexError :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Three lines, remember? Command, then team, then name. " )
2021-01-02 09:02:57 +00:00
2023-03-31 06:18:28 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " , player_name = " player " )
async def swapplayer ( interaction , team_name : str , player_name : str ) :
""" Swaps a player on an owned team between lineup and rotation. """
team , owner_id = games . get_team_and_owner ( team_name )
if team is None :
raise CommandError ( " Can ' t find that team, boss. Typo? " )
elif owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
elif not team . swap_player ( player_name ) :
raise CommandError ( " Either we can ' t find that player, you ' ve got no space on the other side, or they ' re your last member of that side of the roster. Can ' t field an empty lineup, and we *do* have rules, chief. " )
else :
await interaction . channel . send ( embed = build_team_embed ( team ) )
games . update_team ( team )
await interaction . response . send_message ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
2021-01-02 09:02:57 +00:00
class MovePlayerCommand ( Command ) :
2021-01-03 21:29:56 +00:00
name = " moveplayer "
template = """ m;moveplayer
2021-01-02 09:02:57 +00:00
[ team name ]
[ player name ]
[ new lineup / rotation position number ] ( indexed with 1 being the top ) """
2021-01-04 16:42:02 +00:00
description = " Moves a player within your lineup or rotation. If you want to instead move a player from your rotation to your lineup or vice versa, use m;swapsection instead. Requires team ownership and exact spelling of team name. "
2021-01-02 09:02:57 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-01-02 09:02:57 +00:00
try :
2021-01-03 08:55:03 +00:00
team_name = command . split ( " \n " ) [ 1 ] . strip ( )
player_name = command . split ( " \n " ) [ 2 ] . strip ( )
2023-03-31 06:18:28 +00:00
2021-02-03 08:12:24 +00:00
2021-01-03 08:55:03 +00:00
except IndexError :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Four lines, remember? Command, then team, then name, and finally, new spot on the lineup or rotation. " )
2021-01-02 09:02:57 +00:00
2023-03-31 06:18:28 +00:00
@client.tree.command ( )
2023-03-31 17:22:10 +00:00
@app_commands.rename ( team_name = " team " , player_name = " player " , is_pitcher = " type " , new_pos = " newposition " )
@app_commands.choices ( is_pitcher = [ app_commands . Choice ( name = " pitcher " , value = 1 ) , app_commands . Choice ( name = " batter " , value = 0 ) ] )
async def moveplayer ( interaction , team_name : str , player_name : str , is_pitcher : app_commands . Choice [ int ] , new_pos : int ) :
2023-03-31 07:02:37 +00:00
""" Moves a player to a different position in your lineup or rotation. """
2023-03-31 06:18:28 +00:00
team , owner_id = games . get_team_and_owner ( team_name )
if new_pos < 0 :
raise CommandError ( " Hey, quit being cheeky. We ' re just trying to help. New position has to be a natural number, boss. " )
elif owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
elif team is None :
raise CommandError ( " We can ' t find that team, boss. Typo? " )
else :
if team . find_player ( player_name ) [ 2 ] is None or len ( team . find_player ( player_name ) [ 2 ] ) < new_pos :
raise CommandError ( " You either gave us a number that was bigger than your current roster, or we couldn ' t find the player on the team. Try again. " )
2023-03-31 17:22:10 +00:00
if is_pitcher . value == 0 :
2023-03-31 06:18:28 +00:00
roster = team . lineup
else :
roster = team . rotation
if ( roster is not None and team . slide_player_spec ( player_name , new_pos , roster ) ) or ( roster is None and team . slide_player ( player_name , new_pos ) ) :
2023-03-31 07:02:37 +00:00
await interaction . channel . send ( embed = build_team_embed ( team ) )
2023-03-31 06:18:28 +00:00
games . update_team ( team )
2023-03-31 07:02:37 +00:00
await interaction . response . send_message ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
2023-03-31 06:18:28 +00:00
else :
raise CommandError ( " You either gave us a number that was bigger than your current roster, or we couldn ' t find the player on the team. Try again. " )
2021-01-02 09:02:57 +00:00
class AddPlayerCommand ( Command ) :
name = " addplayer "
template = """ m;addplayer pitcher (or m;addplayer batter)
[ team name ]
[ player name ] """
2021-01-04 16:42:02 +00:00
description = " Adds a new player to the end of your team, either in the lineup or the rotation depending on which version you use. Requires team ownership and exact spelling of team name. "
2021-01-02 09:02:57 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-01-03 08:55:03 +00:00
try :
team_name = command . split ( " \n " ) [ 1 ] . strip ( )
player_name = command . split ( " \n " ) [ 2 ] . strip ( )
2023-03-31 07:02:37 +00:00
except IndexError :
raise CommandError ( " Three lines, remember? Command, then team, then name. " )
2021-01-02 09:02:57 +00:00
2023-03-31 07:02:37 +00:00
@client.tree.command ( )
@app_commands.choices ( is_pitcher = [ app_commands . Choice ( name = " pitcher " , value = 1 ) , app_commands . Choice ( name = " batter " , value = 0 ) ] )
@app_commands.rename ( team_name = " team " , player_name = " player " , is_pitcher = " type " )
async def addplayer ( interaction , team_name : str , player_name : str , is_pitcher : app_commands . Choice [ int ] ) :
""" Adds a new player to your team. """
if len ( player_name ) > 70 :
raise CommandError ( " 70 characters per player, boss. Quit being sneaky. " )
team , owner_id = games . get_team_and_owner ( team_name )
if team is None :
raise CommandError ( " We can ' t find that team, boss. Typo? " )
if owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
2021-01-02 09:02:57 +00:00
2023-03-31 07:02:37 +00:00
new_player = games . player ( ono . get_stats ( player_name ) )
2021-01-02 09:02:57 +00:00
2023-03-31 07:02:37 +00:00
if is_pitcher . value == 0 :
if not team . add_lineup ( new_player ) [ 0 ] :
raise CommandError ( " Too many batters 🎶 " )
else :
if not team . add_pitcher ( new_player ) :
raise CommandError ( " 8 pitchers is quite enough, we think. " )
await interaction . channel . send ( embed = build_team_embed ( team ) )
games . update_team ( team )
await interaction . response . send_message ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
2021-01-02 09:02:57 +00:00
2021-01-03 21:29:56 +00:00
class RemovePlayerCommand ( Command ) :
2021-01-02 09:02:57 +00:00
name = " removeplayer "
template = """ m;removeplayer
[ team name ]
[ player name ] """
2021-01-04 16:42:02 +00:00
description = " Removes a player from your team. If there are multiple copies of the same player on a team this will only delete the first one. Requires team ownership and exact spelling of team name. "
2021-01-02 09:02:57 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-01-03 08:55:03 +00:00
try :
team_name = command . split ( " \n " ) [ 1 ] . strip ( )
player_name = command . split ( " \n " ) [ 2 ] . strip ( )
2023-03-31 07:02:37 +00:00
2021-01-03 08:55:03 +00:00
except IndexError :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Three lines, remember? Command, then team, then name. " )
2021-01-02 09:02:57 +00:00
2023-03-31 07:02:37 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " , player_name = " player " )
async def removeplayer ( interaction , team_name : str , player_name : str ) :
""" Removes a player from your team. """
team , owner_id = games . get_team_and_owner ( team_name )
if owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
elif team is None :
raise CommandError ( " Can ' t find that team, boss. Typo? " )
if not team . delete_player ( player_name ) :
raise CommandError ( " We ' ve got bad news: that player isn ' t on your team. The good news is that... that player isn ' t on your team? " )
else :
await interaction . channel . send ( embed = build_team_embed ( team ) )
games . update_team ( team )
await interaction . response . send_message ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
2021-01-29 20:41:27 +00:00
class ReplacePlayerCommand ( Command ) :
name = " replaceplayer "
template = """ m;replaceplayer
[ team name ]
[ player name to * * remove * * ]
[ player name to * * add * * ] """
description = " Replaces a player on your team. If there are multiple copies of the same player on a team this will only replace the first one. Requires team ownership and exact spelling of team name. "
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-01-29 20:41:27 +00:00
try :
team_name = command . split ( " \n " ) [ 1 ] . strip ( )
remove_name = command . split ( " \n " ) [ 2 ] . strip ( )
add_name = command . split ( " \n " ) [ 3 ] . strip ( )
2021-03-09 17:55:47 +00:00
if len ( add_name ) > 70 :
2021-03-26 21:38:03 +00:00
raise CommandError ( " 70 characters per player, boss. Quit being sneaky. " )
2021-01-29 20:41:27 +00:00
team , owner_id = games . get_team_and_owner ( team_name )
if owner_id != msg . author . id and msg . author . id not in config ( ) [ " owners " ] :
2021-03-26 21:38:03 +00:00
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
2021-01-29 20:41:27 +00:00
old_player , old_pos , old_list = team . find_player ( remove_name )
new_player = games . player ( ono . get_stats ( add_name ) )
if old_player is None :
2021-03-26 21:38:03 +00:00
raise CommandError ( " We ' ve got bad news: that player isn ' t on your team. The good news is that... that player isn ' t on your team? " )
2021-01-29 20:41:27 +00:00
else :
if old_list == team . lineup :
team . delete_player ( remove_name )
team . add_lineup ( new_player )
team . slide_player ( add_name , old_pos + 1 )
else :
team . delete_player ( remove_name )
team . add_pitcher ( new_player )
team . slide_player ( add_name , old_pos + 1 )
await msg . channel . send ( embed = build_team_embed ( team ) )
games . update_team ( team )
await msg . channel . send ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
except IndexError :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Four lines, remember? Command, then team, then the two names. " )
2021-01-02 06:10:52 +00:00
2020-12-27 08:42:09 +00:00
class HelpCommand ( Command ) :
name = " help "
template = " m;help [command] "
2020-12-28 16:05:19 +00:00
description = " Shows the instructions from the readme for a given command. If no command is provided, we will instead provide a list of all of the commands that instructions can be provided for. "
2020-12-27 08:42:09 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2020-12-27 08:42:09 +00:00
query = command . strip ( )
if query == " " :
2020-12-26 21:35:18 +00:00
text = " Here ' s everything we know how to do; use `m;help [command]` for more info: "
2020-12-27 08:42:09 +00:00
for comm in commands :
if comm . isauthorized ( msg . author ) :
text + = f " \n - { comm . name } "
2020-12-26 21:35:18 +00:00
else :
2020-12-27 08:42:09 +00:00
try :
comm = next ( c for c in commands if c . name == query and c . isauthorized ( msg . author ) )
text = f " ` { comm . template } ` \n { comm . description } "
except :
2020-12-26 21:35:18 +00:00
text = " Can ' t find that command, boss; try checking the list with `m;help`. "
await msg . channel . send ( text )
2020-12-28 00:05:49 +00:00
class DeleteTeamCommand ( Command ) :
name = " deleteteam "
template = " m;deleteteam [name] "
2021-01-04 16:42:02 +00:00
description = " Allows you to delete the team with the provided name. You ' ll get an embed with a confirmation to prevent accidental deletions. Hit the 👍 and your team will be deleted.. Requires team ownership. If you are the owner and the bot is telling you it ' s not yours, contact xvi and xie can assist. "
2023-03-31 07:02:37 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " )
async def deleteteam ( interaction , team_name : str ) :
""" Deletes a team. Requires confirmation. """
team , owner_id = games . get_team_and_owner ( team_name )
if owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] : #returns if person is not owner and not bot mod
raise CommandError ( " That team ain ' t yours, chief. If you think that ' s not right, bug xvi about deleting it for you. " )
elif team is not None :
delete_task = asyncio . create_task ( team_delete_confirm ( interaction . channel , team , interaction . user ) )
await delete_task
2020-12-28 00:05:49 +00:00
class AssignOwnerCommand ( Command ) :
name = " assignowner "
template = " m;assignowner [mention] [team] "
description = " assigns a discord user as the owner for a team. "
def isauthorized ( self , user ) :
return user . id in config ( ) [ " owners " ]
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-03-06 17:27:27 +00:00
if self . isauthorized ( msg . author ) :
2021-03-06 17:23:52 +00:00
new_owner = msg . mentions [ 0 ]
team_name = command . strip ( ) . split ( " > " , 1 ) [ 1 ]
if db . assign_owner ( team_name , new_owner . id ) :
await msg . channel . send ( f " { team_name } is now owned by { new_owner . display_name } . Don ' t break it. " )
else :
2021-03-26 21:38:03 +00:00
raise CommandError ( " We couldn ' t find that team. Typo? " )
2021-01-03 11:06:51 +00:00
class StartTournamentCommand ( Command ) :
name = " starttournament "
2023-03-31 17:22:10 +00:00
template = """ m;starttournament [bot ping]
2021-01-04 04:18:45 +00:00
[ tournament name ]
2023-03-31 17:22:10 +00:00
[ list of teams , each on a new line ]
"""
2021-01-08 18:10:01 +00:00
description = " Starts a randomly seeded tournament with the provided teams, automatically adding byes as necessary. All series have a 5 minute break between games and by default there is a 10 minute break between rounds. The current tournament format is: \n Best of 5 until the finals, which are Best of 7. "
2021-01-03 11:06:51 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
round_delay = 10
series_length = 5
finals_series_length = 7
rand_seed = True
pre_seeded = False
2021-01-05 20:31:50 +00:00
2021-06-27 20:34:44 +00:00
list_of_team_names = command . split ( " \n " ) [ 2 : ]
2021-03-26 21:38:03 +00:00
if config ( ) [ " game_freeze " ] :
raise CommandError ( " Patch incoming. We ' re not allowing new games right now. " )
2021-01-05 20:31:50 +00:00
2021-03-26 21:38:03 +00:00
for flag in flags :
if flag [ 0 ] == " r " : #rounddelay
try :
round_delay = int ( flag [ 1 ] )
except ValueError :
raise CommandError ( " The delay between rounds should be a whole number. " )
if round_delay < 1 or round_delay > 120 :
raise CommandError ( " The delay between rounds has to bebetween 1 and 120 minutes. " )
elif flag [ 0 ] == " b " : #bestof
try :
series_length = int ( flag [ 1 ] )
if series_length % 2 == 0 or series_length < 0 :
raise ValueError
except ValueError :
raise CommandError ( " Series length has to be an odd positive integer. " )
if msg . author . id not in config ( ) [ " owners " ] and series_length > 21 :
raise CommandError ( " That ' s too long, boss. We have to run patches *some* time. " )
2021-06-27 20:34:44 +00:00
if len ( list_of_team_names ) == 2 :
raise CommandError ( " --bestof is only for non-finals matches! You probably want --finalsbestof, boss. -f works too, if you want to pay respects. " )
2021-03-26 21:38:03 +00:00
elif flag [ 0 ] == " f " : #pay respects (finalsbestof)
try :
finals_series_length = int ( flag [ 1 ] )
if finals_series_length % 2 == 0 or finals_series_length < 0 :
raise ValueError
except ValueError :
raise CommandError ( " Finals series length has to be an odd positive integer. " )
if msg . author . id not in config ( ) [ " owners " ] and finals_series_length > 21 :
raise CommandError ( " That ' s too long, boss. We have to run patches *some* time. " )
2021-04-18 16:05:11 +00:00
elif flag [ 0 ] == " s " : #seeding
2021-03-26 21:38:03 +00:00
if flag [ 1 ] == " stars " :
rand_seed = False
elif flag [ 1 ] == " given " :
rand_seed = False
pre_seeded = True
elif flag [ 1 ] == " random " :
pass
else :
raise CommandError ( " Valid seeding types are: ' random ' (default), ' stars ' , and ' given ' . " )
else :
raise CommandError ( " One or more of those flags wasn ' t right. Try and fix that for us and we ' ll see about sorting you out. " )
2021-01-04 04:18:45 +00:00
tourney_name = command . split ( " \n " ) [ 1 ]
team_dic = { }
for name in list_of_team_names :
team = get_team_fuzzy_search ( name . strip ( ) )
if team == None :
2021-03-26 21:38:03 +00:00
raise CommandError ( f " We couldn ' t find { name } . Try again? " )
2021-01-15 04:39:08 +00:00
add = True
for extant_team in team_dic . keys ( ) :
if extant_team . name == team . name :
add = False
if add :
team_dic [ team ] = { " wins " : 0 }
2021-01-04 04:18:45 +00:00
2021-01-04 21:17:55 +00:00
channel = msg . channel
if len ( team_dic ) < 2 :
await msg . channel . send ( " One team does not a tournament make. " )
return
2021-03-26 21:38:03 +00:00
tourney = leagues . tournament ( tourney_name , team_dic , series_length = series_length , finals_series_length = finals_series_length , secs_between_rounds = round_delay * 60 )
2021-01-05 20:31:50 +00:00
tourney . build_bracket ( random_sort = rand_seed )
2021-03-26 21:38:03 +00:00
2021-01-04 21:17:55 +00:00
await start_tournament_round ( channel , tourney )
2020-12-28 00:05:49 +00:00
2023-03-31 17:22:10 +00:00
@client.tree.command ( )
async def starttournament ( interaction ) :
""" Get tournament instructions. Sent privately, don ' t worry! """
await interaction . response . send_message ( StartTournamentCommand . template + StartTournamentCommand . description , ephemeral = True )
2020-12-28 00:05:49 +00:00
2021-01-04 23:52:34 +00:00
class DraftPlayerCommand ( Command ) :
name = " draft "
template = " m;draft [playername] "
description = " On your turn during a draft, use this command to pick your player. "
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-01-04 23:52:34 +00:00
"""
This is a no - op definition . ` StartDraftCommand ` handles the orchestration directly ,
this is just here to provide a help entry and so the command dispatcher recognizes it
as valid .
"""
pass
2021-07-01 21:35:17 +00:00
class DraftFlagsCommand ( Command ) :
name = " draftflags "
template = " m;draftflags "
description = " Shows all currently accepted flags for the startdraft command. "
async def execute ( self , msg , command , flags ) :
text = """ Currently accepted flags:
- - draftsize or - d : Sets the size of each draft pool .
2022-06-16 01:59:51 +00:00
- - refresh or - r : Sets the size at which the pool completely refreshes .
2021-07-01 21:35:17 +00:00
- - teamsize or - t : How big each team should be , including pitchers .
- - pitchercount or - p : How many pitchers each team should have .
- - wait or - w : Sets the timeout , in seconds , to wait for draftees to pick a player .
- - chaos or - c : The percentage of onomancer names in the pool . Higher numbers mean less real names , but faster pool generation . Accepts any number between 0 and 1.
"""
2021-07-06 01:26:55 +00:00
await msg . channel . send ( text )
2021-01-04 23:52:34 +00:00
class StartDraftCommand ( Command ) :
name = " startdraft "
2021-01-16 01:27:58 +00:00
template = " m;startdraft \n [mention] \n [teamname] \n [slogan] "
description = """ Starts a draft with an arbitrary number of participants. Send this command at the top of the list with each mention, teamname, and slogan on their own lines (shift+enter in discord).
2021-01-04 23:52:34 +00:00
- The draft will proceed in the order that participants were entered .
- 20 players will be available for draft at a time , and the pool will refresh automatically when it becomes small .
- Each participant will be asked to draft 12 hitters then finally one pitcher .
- The draft will start only once every participant has given a 👍 to begin .
2021-01-05 03:19:12 +00:00
- use the command ` d ` , ` draft ` , or ` m ; draft ` on your turn to draft someone
2021-01-04 23:52:34 +00:00
"""
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-03-28 19:16:31 +00:00
teamsize = 13
draftsize = 20
minsize = 4
pitchers = 3
2021-06-27 23:15:56 +00:00
ono_ratio = 0.5
timeout = 120
2021-03-28 19:16:31 +00:00
for flag in flags :
try :
2021-06-27 23:15:56 +00:00
if flag [ 0 ] == " t " :
2021-03-28 19:16:31 +00:00
teamsize = int ( flag [ 1 ] )
elif flag [ 0 ] == " d " :
draftsize = int ( flag [ 1 ] )
elif flag [ 0 ] == " r " : #refreshsize, default 4
minsize = int ( flag [ 1 ] )
elif flag [ 0 ] == " p " :
pitchers = int ( flag [ 1 ] )
2021-06-27 23:15:56 +00:00
elif flag [ 0 ] == " c " :
ono_ratio = float ( flag [ 1 ] )
if ono_ratio > 1 or ono_ratio < 0 :
raise CommandError ( " The Chaos value needs to be between 0 and 1, chief. Probability has rules. " )
elif flag [ 0 ] == " w " : #wait
timeout = int ( flag [ 1 ] )
2021-03-28 19:16:31 +00:00
else :
raise CommandError ( f " We don ' t recognize that { flag [ 0 ] } flag. " )
except ValueError :
2021-06-27 23:15:56 +00:00
raise CommandError ( f " Your { flag [ 0 ] } flag isn ' t a valid nummber, boss. " )
2021-03-28 19:16:31 +00:00
if teamsize - pitchers > 20 or pitchers > 8 :
raise CommandError ( " You can ' t fit that many players on a team, chief. Slow your roll. " )
2021-04-01 05:50:47 +00:00
if teamsize < 3 or pitchers < 1 or draftsize < 5 or minsize < 2 :
raise CommandError ( " One of those numbers is too low. Draft size has to be at least 5, the rest should be obvious. " )
if draftsize > 40 :
2021-03-28 19:16:31 +00:00
raise CommandError ( " 40 players is the max. We ' re not too confident about pushing for more. " )
2021-06-27 23:15:56 +00:00
await msg . channel . send ( " Got it, boss. Give me a sec to find all the paperwork. " )
try :
draft = Draft . make_draft ( teamsize , draftsize , minsize , pitchers , ono_ratio )
except ConnectionError :
await msg . channel . send ( " Baseball Reference isn ' t playing nice. Could you try again for us in a minute or so? " )
2021-01-04 23:52:34 +00:00
mentions = { f ' <@! { m . id } > ' for m in msg . mentions }
content = msg . content . split ( ' \n ' ) [ 1 : ] # drop command out of message
2021-01-16 18:36:23 +00:00
if not content or len ( content ) % 3 :
2021-01-04 23:52:34 +00:00
await msg . channel . send ( ' Invalid list ' )
raise ValueError ( ' Invalid length ' )
for i in range ( 0 , len ( content ) , 3 ) :
2021-01-05 03:19:12 +00:00
handle_token = content [ i ] . strip ( )
for mention in mentions :
if mention in handle_token :
handle = mention
break
2022-06-16 01:59:51 +00:00
else :
await msg . channel . send ( f " I don ' t recognize { handle_token } . " )
return
2021-01-04 23:52:34 +00:00
team_name = content [ i + 1 ] . strip ( )
if games . get_team ( team_name ) :
await msg . channel . send ( f ' Sorry { handle } , { team_name } already exists ' )
2021-01-05 03:19:12 +00:00
return
2021-01-04 23:52:34 +00:00
slogan = content [ i + 2 ] . strip ( )
draft . add_participant ( handle , team_name , slogan )
success = await self . wait_start ( msg . channel , mentions )
if not success :
return
draft . start_draft ( )
2021-01-05 07:38:53 +00:00
footer = f " The draft class of { random . randint ( 2007 , 2075 ) } "
2021-03-28 19:16:31 +00:00
while draft . round < = draft . DRAFT_ROUNDS :
message_prefix = f ' Round { draft . round } / { draft . DRAFT_ROUNDS } : '
if draft . round == draft . DRAFT_ROUNDS :
2021-01-05 07:38:53 +00:00
body = random . choice ( [
f " Now just choose a pitcher and we can finish off this paperwork for you, { draft . active_drafter } " ,
2021-03-03 16:53:13 +00:00
f " Pick a pitcher, { draft . active_drafter } , and we can all go home happy. ' Cept your players. They ' ll have to play baseball. " ,
2021-01-05 07:38:53 +00:00
f " Almost done, { draft . active_drafter } . Pick your pitcher. " ,
] )
message = f " ⚾️ { message_prefix } { body } "
2021-03-28 19:16:31 +00:00
elif draft . round < = draft . DRAFT_ROUNDS - draft . pitchers :
2021-01-05 07:38:53 +00:00
body = random . choice ( [
2021-03-28 19:16:31 +00:00
f " Choose a batter, { draft . active_drafter } . " ,
2021-01-05 07:38:53 +00:00
f " { draft . active_drafter } , your turn. Pick one. " ,
2021-03-28 19:16:31 +00:00
f " Pick one to fill your next lineup slot, { draft . active_drafter } . " ,
2021-01-05 07:38:53 +00:00
f " Alright, { draft . active_drafter } , choose a batter. " ,
] )
message = f " 🏏 { message_prefix } { body } "
2021-03-28 19:16:31 +00:00
else :
body = random . choice ( [
f " Warning: Pitcher Zone. Enter if you dare, { draft . active_drafter } . " ,
f " Time to pitch a picker, { draft . active_drafter } . \n Wait, that doesn ' t sound right. " ,
f " Choose a yeeter, { draft . active_drafter } . \n Did we use that word right? " ,
f " Choose a pitcher, { draft . active_drafter } . " ] )
message = f " ⚾️ { message_prefix } { body } "
2021-01-04 23:52:34 +00:00
await msg . channel . send (
2021-01-05 07:38:53 +00:00
message ,
embed = build_draft_embed ( draft . get_draftees ( ) , footer = footer ) ,
2021-01-04 23:52:34 +00:00
)
try :
2021-06-27 23:15:56 +00:00
draft_message = await self . wait_draft ( msg . channel , draft , timeout )
2021-01-04 23:54:28 +00:00
draft . draft_player ( f ' <@! { draft_message . author . id } > ' , draft_message . content . split ( ' ' , 1 ) [ 1 ] )
2021-01-04 23:52:34 +00:00
except SlowDraftError :
player = random . choice ( draft . get_draftees ( ) )
2021-01-05 07:38:53 +00:00
await msg . channel . send ( f " I ' m not waiting forever. You get { player } . Next. " )
2021-01-04 23:52:34 +00:00
draft . draft_player ( draft . active_drafter , player )
except ValueError as e :
await msg . channel . send ( str ( e ) )
2021-01-05 07:38:53 +00:00
except IndexError :
await msg . channel . send ( " Quit the funny business. " )
2021-01-04 23:52:34 +00:00
2021-01-05 05:41:51 +00:00
for handle , team in draft . get_teams ( ) :
2021-01-05 07:38:53 +00:00
await msg . channel . send (
random . choice ( [
f " Done and dusted, { handle } . Here ' s your squad. " ,
f " Behold the { team . name } , { handle } . Flawless, we think. " ,
f " Oh, huh. Interesting stat distribution. Good luck, { handle } . " ,
] ) ,
embed = build_team_embed ( team ) ,
)
2021-01-04 23:52:34 +00:00
try :
draft . finish_draft ( )
except Exception as e :
await msg . channel . send ( str ( e ) )
async def wait_start ( self , channel , mentions ) :
2021-01-05 07:38:53 +00:00
start_msg = await channel . send ( " Sound off, folks. 👍 if you ' re good to go " + " " . join ( mentions ) )
2021-01-04 23:52:34 +00:00
await start_msg . add_reaction ( " 👍 " )
await start_msg . add_reaction ( " 👎 " )
def react_check ( react , user ) :
return f ' <@! { user . id } > ' in mentions and react . message == start_msg
while True :
try :
react , _ = await client . wait_for ( ' reaction_add ' , timeout = 60.0 , check = react_check )
if react . emoji == " 👎 " :
2021-01-05 07:38:53 +00:00
await channel . send ( " We dragged out the photocopier for this! Fine, putting it back. " )
2021-01-04 23:52:34 +00:00
return False
if react . emoji == " 👍 " :
reactors = set ( )
async for user in react . users ( ) :
reactors . add ( f ' <@! { user . id } > ' )
if reactors . intersection ( mentions ) == mentions :
return True
except asyncio . TimeoutError :
await channel . send ( " Y ' all aren ' t ready. " )
return False
return False
2021-06-27 23:15:56 +00:00
async def wait_draft ( self , channel , draft , timeout ) :
2021-01-04 23:52:34 +00:00
def check ( m ) :
if m . channel != channel :
return False
2021-01-05 07:38:53 +00:00
if m . content . startswith ( ' d ' ) or m . content . startswith ( ' draft ' ) :
2021-01-05 03:19:12 +00:00
return True
2021-01-04 23:52:34 +00:00
for prefix in config ( ) [ ' prefix ' ] :
2021-01-05 07:38:53 +00:00
if m . content . startswith ( prefix + ' draft ' ) :
2021-01-04 23:52:34 +00:00
return True
return False
try :
2021-06-27 23:15:56 +00:00
draft_message = await client . wait_for ( ' message ' , timeout = timeout , check = check )
2021-01-04 23:52:34 +00:00
except asyncio . TimeoutError :
2021-06-27 23:15:56 +00:00
raise SlowDraftError ( ' Too slow, boss. ' )
2021-01-04 23:52:34 +00:00
return draft_message
2021-01-14 00:20:58 +00:00
2021-01-14 05:20:43 +00:00
class StartLeagueCommand ( Command ) :
name = " startleague "
2021-01-15 04:39:08 +00:00
template = " m;startleague [league name] \n [games per hour] "
2021-06-27 23:16:44 +00:00
description = """ Optional flags for the first line: `--queue X` or `-q X` to play X number of series before stopping; `--autopostseason` will automatically start postseason at the end of the season.
2021-02-04 16:08:38 +00:00
Starts games from a league with a given name , provided that league has been saved on the website and has been claimed using claimleague . The games per hour sets how often the games will start ( e . g . GPH 2 will start games at X : 00 and X : 30 ) . By default it will play the entire season followed by the postseason and then stop but this can be customized using the flags .
Not every team will play every series , due to how the scheduling algorithm is coded but it will all even out by the end . """
2023-03-31 17:22:10 +00:00
2021-01-13 09:23:48 +00:00
2023-03-31 17:22:10 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " , autoplay = " queue " , postseason_mode = " postseasonmode " )
@app_commands.describe ( gph = " Games per hour to play. " )
@app_commands.choices ( postseason_mode = [ app_commands . Choice ( name = " pause " , value = 0 ) , app_commands . Choice ( name = " auto " , value = 1 ) , app_commands . Choice ( name = " skip " , value = 2 ) ] )
async def startleague ( interaction , league_name : str , gph : int , autoplay : Optional [ int ] = None , postseason_mode : Optional [ app_commands . Choice [ int ] ] = 0 ) :
""" Starts up a league previously formed on the site. """
autopost = False
nopost = False
if config ( ) [ " game_freeze " ] :
raise CommandError ( " Patch incoming. We ' re not allowing new games right now. " )
if autoplay is not None :
if autoplay < = 0 :
raise CommandError ( " Sorry boss, the queue flag needs a natural number. Any whole number over 0 will do just fine. " )
else :
2021-03-26 21:38:03 +00:00
autoplay = - 1
2023-03-31 17:22:10 +00:00
if postseason_mode == 0 :
postseason_mode = app_commands . Choice ( name = " pause " , value = 0 )
2021-03-26 21:38:03 +00:00
2021-01-15 09:27:20 +00:00
2023-03-31 17:22:10 +00:00
if postseason_mode is None or postseason_mode . value == 0 : #noautopostseason
await interaction . response . send_message ( " We ' ll pause the games before postseason starts, when we get there. " )
elif postseason_mode . value == 1 : #autopostseason
await interaction . response . send_message ( " We ' ll automatically start postseason for you, when we get there. " )
autopost = True
elif postseason_mode . value == 2 : #skippostseason
await interaction . response . send_message ( " We ' ll **skip postseason** for you! Make sure you wanted to do this. " )
autopost = True
nopost = True
2021-01-15 04:39:08 +00:00
2023-03-31 17:22:10 +00:00
if gph < 1 or gph > 12 :
raise CommandError ( " Chief, we need a games per hour number between 1 and 12. We think that ' s reasonable. " )
2021-01-15 21:30:11 +00:00
2021-01-14 05:20:43 +00:00
2023-03-31 17:22:10 +00:00
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if autoplay == - 1 and not autopost :
autoplay = int ( list ( league . schedule . keys ( ) ) [ - 1 ] ) - league . day_to_series_num ( league . day ) + 1
if nopost :
league . postseason = False
if league . historic :
raise CommandError ( " That league is done and dusted, chief. Sorry. " )
for active_league in active_leagues :
if active_league . name == league . name :
raise CommandError ( " That league is already running, boss. Patience is a virtue, you know. " )
if ( league . owner is not None and interaction . user . id in league . owner ) or interaction . user . id in config ( ) [ " owners " ] or league . owner is None :
league . autoplay = autoplay
league . games_per_hour = gph
if str ( league . day_to_series_num ( league . day ) ) not in league . schedule . keys ( ) :
await league_postseason ( interaction . channel , league )
elif league . day % league . series_length == 1 :
await start_league_day ( interaction . channel , league )
2021-01-15 07:23:44 +00:00
else :
2023-03-31 17:22:10 +00:00
await start_league_day ( interaction . channel , league , partial = True )
2021-01-14 05:20:43 +00:00
else :
2023-03-31 17:22:10 +00:00
raise CommandError ( " You don ' t have permission to manage that league. " )
else :
raise CommandError ( " Couldn ' t find that league, boss. Did you save it on the website? " )
2021-01-04 23:52:34 +00:00
2021-07-06 01:26:55 +00:00
class LeagueSetPlayerModifiersCommand ( Command ) :
name = " setplayermods "
template = " m;setplayermods [league name] \n [team name] \n [player name] \n 4 numbers, seperated by a space, for batting pitching baserunning defense "
description = " Set a pack of modifiers to a specific player in your league. Commissioners only. "
2021-07-23 22:32:36 +00:00
def isauthorized ( self , user ) :
return user . id in config ( ) [ " owners " ] + config ( ) [ " ea " ]
2021-07-06 01:26:55 +00:00
async def execute ( self , msg , command , flags ) :
if league_exists ( command . split ( " \n " ) [ 0 ] . strip ( ) ) :
try :
league = leagues . load_league_file ( command . split ( " \n " ) [ 0 ] . strip ( ) )
except IndexError :
raise CommandError ( " League name goes on the second line now, boss. " )
else :
raise CommandError ( " Can ' t find that league, boss. " )
try :
team_name = command . split ( " \n " ) [ 1 ] . strip ( )
player_name = command . split ( " \n " ) [ 2 ] . strip ( )
mods_text_list = command . split ( " \n " ) [ 3 ] . strip ( ) . split ( " " )
keys = [ " batting_stars " , " pitching_stars " , " baserunning_stars " , " defense_stars " ]
mod_dic = { }
for i in range ( 0 , 4 ) :
mod_dic [ keys [ i ] ] = float ( mods_text_list [ i ] )
set_mods ( league . name , player_name , team_name , mod_dic )
await msg . channel . send ( " You got it, boss. Divine intervention, served by yours truly. " )
except IndexError :
raise CommandError ( " You didn ' t give us enough info, chief. Check the help text. " )
2021-07-23 22:34:32 +00:00
except ValueError :
raise CommandError ( " Those modifiers didn ' t make a lick of sense. Try again with sensible numbers this time. " )
2021-07-06 01:26:55 +00:00
2021-03-28 18:00:38 +00:00
class LeagueSubscribeCommand ( Command ) :
name = " leaguesub "
template = " m;leaguesub [league name] "
description = " Posts all league feed events to this channel, in addition to the channel the league was started in. Run again to unsubscribe. "
2023-03-31 17:22:10 +00:00
2021-03-28 18:00:38 +00:00
2023-03-31 17:22:10 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leaguesubscribe ( interaction , league_name : str ) :
""" Posts league feed events to this channel. Use again to unsub. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if interaction . channel . id in league . subbed_channels :
league . subbed_channels . pop ( league . subbed_channels . index ( interaction . channel . id ) )
await interaction . response . send_message ( " You ' re off the mailing list, boss. We promise. " )
2021-03-28 18:00:38 +00:00
else :
2023-03-31 17:22:10 +00:00
league . subbed_channels . append ( interaction . channel . id )
await interaction . response . send_message ( f " Thanks for signing up to the { league_name } newsletter. " )
leagues . save_league ( league )
else :
raise CommandError ( " That league doesn ' t exist, boss. " )
2021-01-04 23:52:34 +00:00
2021-01-15 04:39:08 +00:00
class LeagueDisplayCommand ( Command ) :
name = " leaguestandings "
2021-03-26 21:38:03 +00:00
template = " m;leaguestandings \n [league name] "
2021-01-16 01:15:52 +00:00
description = " Displays the current standings for the given league. Use `--season X` or `-s X` to get standings from season X of that league. "
2023-03-31 17:22:10 +00:00
2021-01-15 04:39:08 +00:00
2023-03-31 17:22:10 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leaguestandings ( interaction , league_name : str , season : Optional [ int ] = None ) :
""" Display a league ' s current (or historical) standings. """
try :
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if season is not None :
2021-04-01 05:53:01 +00:00
try :
2023-03-31 17:22:10 +00:00
season_num = season
await interaction . response . send_message ( embed = league . past_standings ( season_num ) )
return
except ValueError :
raise CommandError ( " Give us a proper number, boss. " )
except TypeError :
raise CommandError ( " That season hasn ' t been played yet, chief. " )
2021-04-01 05:53:01 +00:00
2023-03-31 17:22:10 +00:00
await interaction . response . send_message ( embed = league . standings_embed ( ) )
else :
raise CommandError ( " Can ' t find that league, boss. " )
except IndexError :
raise CommandError ( " League name goes on the second line now, boss. " )
2021-01-15 04:39:08 +00:00
2021-02-04 07:49:23 +00:00
class LeagueLeadersCommand ( Command ) :
name = " leagueleaders "
template = " m;leagueleaders [league name] \n [stat name/abbreviation] "
2021-02-04 16:08:38 +00:00
description = " Displays a league ' s leaders in the given stat. A list of the allowed stats can be found on the github readme. "
2023-03-31 17:22:10 +00:00
2021-02-04 07:49:23 +00:00
2023-03-31 17:22:10 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leagueleaders ( interaction , league_name : str , stat : str , season : Optional [ int ] = None ) :
""" Displays a league ' s leaders in the given stat. A list of the allowed stats can be found on the github readme. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
stat_name = stat
season_num = None
if season is not None :
2021-02-04 07:54:15 +00:00
try :
2023-03-31 17:22:10 +00:00
season_num = season
return
2022-06-28 15:40:10 +00:00
except ValueError :
2023-03-31 17:22:10 +00:00
raise CommandError ( " Give us a proper number, boss. " )
2021-02-04 07:54:15 +00:00
2023-03-31 17:22:10 +00:00
try :
stat_embed = league . stat_embed ( stat_name , season_num )
except IndexError :
raise CommandError ( " Nobody ' s played enough games to get meaningful stats in that category yet, chief. Try again after the next game or two. " )
except ValueError :
raise CommandError ( " That season hasn ' t been played yet. " )
2021-02-04 07:49:23 +00:00
2023-03-31 17:22:10 +00:00
if stat_embed is None :
raise CommandError ( " We don ' t know what that stat is, chief. " )
try :
await interaction . response . send_message ( embed = stat_embed )
return
except :
raise CommandError ( " Nobody ' s played enough games to get meaningful stats in that category yet, chief. Try again after the next game or two. " )
raise CommandError ( " Can ' t find that league, boss. " )
2021-02-04 07:49:23 +00:00
2021-02-03 09:00:20 +00:00
class LeagueDivisionDisplayCommand ( Command ) :
name = " divisionstandings "
2021-02-27 21:26:56 +00:00
template = " m;divisionstandings [league name] \n [division name] "
2021-02-03 09:00:20 +00:00
description = " Displays the current standings for the given division in the given league. "
2023-03-31 17:22:10 +00:00
2021-02-03 09:00:20 +00:00
2023-03-31 17:22:10 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " , division_name = " division " )
async def divisionstandings ( interaction , league_name : str , division_name : str ) :
2023-03-31 18:07:01 +00:00
""" Displays the current standings for the given division in the given league. """
2023-03-31 17:22:10 +00:00
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
division = None
for subleague in iter ( league . league . keys ( ) ) :
for div in iter ( league . league [ subleague ] . keys ( ) ) :
if div == division_name :
division = league . league [ subleague ] [ div ]
if division is None :
raise CommandError ( " Chief, that division doesn ' t exist in that league. " )
try :
await interaction . response . send_message ( embed = league . standings_embed_div ( division , division_name ) )
except :
raise CommandError ( " Something went wrong, boss. Check your staging. " )
else :
raise CommandError ( " Can ' t find that league, boss. " )
2021-02-03 09:00:20 +00:00
2021-01-15 04:39:08 +00:00
class LeagueWildcardCommand ( Command ) :
name = " leaguewildcard "
template = " m;leaguewildcard [league name] "
description = " Displays the current wildcard race for the given league, if the league has wildcard slots. "
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def wildcard ( interaction , league_name : str ) :
""" Displays the current wildcard race for the given league, if the league has wildcard slots. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if league . constraints [ " wild_cards " ] > 0 :
await interaction . response . send_message ( embed = league . wildcard_embed ( ) )
2021-01-15 04:39:08 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( " That league doesn ' t have wildcards, boss. " )
else :
raise CommandError ( " Can ' t find that league, boss. " )
2021-01-15 04:39:08 +00:00
class LeaguePauseCommand ( Command ) :
name = " pauseleague "
template = " m;pauseleague [league name] "
2021-02-04 16:08:38 +00:00
description = " Tells a currently running league to stop running after the current series. "
2023-03-31 18:07:01 +00:00
2021-01-15 04:39:08 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leaguepause ( interaction , league_name : str ) :
""" Tells a currently running league to stop running after the current series. """
for active_league in active_leagues :
if active_league . name == league_name :
if ( active_league . owner is not None and interaction . user . id in active_league . owner ) or interaction . user . id in config ( ) [ " owners " ] :
active_league . autoplay = 0
await interaction . response . send_message ( f " Loud and clear, chief. { league_name } will stop after this series is over. " )
return
else :
raise CommandError ( " You don ' t have permission to manage that league. " )
raise CommandError ( " That league either doesn ' t exist or isn ' t running. " )
2021-01-15 21:06:52 +00:00
class LeagueClaimCommand ( Command ) :
name = " claimleague "
template = " m;claimleague [league name] "
description = " Claims an unclaimed league. Do this as soon as possible after creating the league, or it will remain unclaimed. "
2023-03-31 18:07:01 +00:00
2021-01-15 21:06:52 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def claimleague ( interaction , league_name : str ) :
""" Claims an unclaimed league. Do this as soon as possible after creating the league, or it will remain unclaimed. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if league . owner is None :
league . owner = [ interaction . user . id ]
leagues . save_league ( league )
await interaction . response . send_message ( f " The { league . name } commissioner is doing a great job. That ' s you, by the way. " )
return
2021-01-15 21:08:39 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( " That league has already been claimed! " )
else :
raise CommandError ( " Can ' t find that league, boss. " )
2021-01-15 21:06:52 +00:00
class LeagueAddOwnersCommand ( Command ) :
name = " addleagueowner "
template = " m;addleagueowner [league name] \n [user mentions] "
description = " Adds additional owners to a league. "
2023-03-31 18:07:01 +00:00
2021-01-15 09:27:20 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " , newowner = " newownermention " )
async def addleagueowner ( interaction , league_name : str , newowner : str ) :
""" Adds an owner to a league. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if ( league . owner is not None and interaction . user . id in league . owner ) or ( league . owner is not None and interaction . user . id in config ( ) [ " owners " ] ) :
if int ( newowner [ 2 : - 1 ] ) not in league . owner :
league . owner . append ( int ( newowner [ 2 : - 1 ] ) )
leagues . save_league ( league )
await interaction . response . send ( f " The new { league . name } front office is now up and running. " )
return
2021-01-15 21:06:52 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( f " That league isn ' t yours, boss. " )
else :
raise CommandError ( " Can ' t find that league, boss. " )
2021-01-15 21:06:52 +00:00
2021-01-15 09:27:20 +00:00
class LeagueScheduleCommand ( Command ) :
name = " leagueschedule "
template = " m;leagueschedule [league name] "
description = " Sends an embed with the given league ' s schedule for the next 4 series. "
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leagueschedule ( interaction , league_name : str ) :
""" Show a league ' s 4-series schedule. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
current_series = league . day_to_series_num ( league . day )
if str ( current_series + 1 ) in league . schedule . keys ( ) :
sched_embed = discord . Embed ( title = f " { league . name } ' s Schedule: " , color = discord . Color . magenta ( ) )
days = [ 0 , 1 , 2 , 3 ]
for day in days :
embed_title = f " Days { ( ( current_series + day - 1 ) * league . series_length ) + 1 } - { ( current_series + day ) * ( league . series_length ) } "
parts = 1
if str ( current_series + day ) in league . schedule . keys ( ) :
schedule_text = " "
teams = league . team_names_in_league ( )
for game in league . schedule [ str ( current_series + day ) ] :
emojis = " "
for day_offset in range ( ( current_series + day - 1 ) * league . series_length , ( current_series + day ) * ( league . series_length ) ) :
try :
emojis + = weather . all_weathers ( ) [ league . weather_forecast [ game [ 1 ] ] [ day_offset ] ] . emoji + " "
except :
False
schedule_text + = f " ** { game [ 0 ] } ** @ ** { game [ 1 ] } ** { emojis } \n "
if len ( schedule_text ) > = 900 :
embed_title + = f " Part { parts } "
sched_embed . add_field ( name = embed_title , value = schedule_text , inline = False )
parts + = 1
embed_title = f " Days { ( ( current_series + day - 1 ) * league . series_length ) + 1 } - { ( current_series + day ) * ( league . series_length ) } Part { parts } "
schedule_text = " "
teams . pop ( teams . index ( game [ 0 ] ) )
teams . pop ( teams . index ( game [ 1 ] ) )
if len ( teams ) > 0 :
schedule_text + = " Resting: \n "
for team in teams :
schedule_text + = f " ** { team } ** \n "
2021-03-20 16:10:13 +00:00
if len ( schedule_text ) > = 900 :
embed_title + = f " Part { parts } "
sched_embed . add_field ( name = embed_title , value = schedule_text , inline = False )
parts + = 1
embed_title = f " Days { ( ( current_series + day - 1 ) * league . series_length ) + 1 } - { ( current_series + day ) * ( league . series_length ) } Part { parts } "
schedule_text = " "
2023-03-31 18:07:01 +00:00
sched_embed . add_field ( name = embed_title , value = schedule_text , inline = False )
await interaction . response . send_message ( embed = sched_embed )
2021-01-15 09:27:20 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( " That league ' s already finished with this season, boss. " )
else :
raise CommandError ( " We can ' t find that league. Typo? " )
2021-02-03 08:36:47 +00:00
class LeagueTeamScheduleCommand ( Command ) :
name = " teamschedule "
template = " m;teamschedule [league name] \n [team name] "
description = " Sends an embed with the given team ' s schedule in the given league for the next 7 series. "
2023-03-31 18:07:01 +00:00
2021-02-03 08:36:47 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " , team_name = " teamname " )
async def teamschedule ( interaction , league_name : str , team_name : str ) :
""" Shows a team ' s 7-series schedule in a specific league. """
team = get_team_fuzzy_search ( team_name )
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
current_series = league . day_to_series_num ( league . day )
if team . name not in league . team_names_in_league ( ) :
raise CommandError ( " Can ' t find that team in that league, chief. " )
if str ( current_series + 1 ) in league . schedule . keys ( ) :
sched_embed = discord . Embed ( title = f " { team . name } ' s Schedule for the { league . name } : " , color = discord . Color . purple ( ) )
days = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
for day in days :
if str ( current_series + day ) in league . schedule . keys ( ) :
schedule_text = " "
for game in league . schedule [ str ( current_series + day ) ] :
if team . name in game :
emojis = " "
for day_offset in range ( ( current_series + day - 1 ) * league . series_length , ( current_series + day ) * ( league . series_length ) ) :
emojis + = weather . all_weathers ( ) [ league . weather_forecast [ game [ 1 ] ] [ day_offset ] ] . emoji + " "
schedule_text + = f " ** { game [ 0 ] } ** @ ** { game [ 1 ] } ** { emojis } "
if schedule_text == " " :
schedule_text + = " Resting "
sched_embed . add_field ( name = f " Days { ( ( current_series + day - 1 ) * league . series_length ) + 1 } - { ( current_series + day ) * ( league . series_length ) } " , value = schedule_text , inline = False )
await interaction . response . send_message ( embed = sched_embed )
2021-02-03 08:36:47 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( " That league ' s already finished with this season, boss. " )
else :
raise CommandError ( " We can ' t find that league. Typo? " )
2021-01-15 09:27:20 +00:00
2021-02-03 11:55:04 +00:00
class LeagueRegenerateScheduleCommand ( Command ) :
name = " leagueseasonreset "
template = " m;leagueseasonreset [league name] "
description = " Completely scraps the given league ' s current season, resetting everything to day 1 of the current season. Requires ownership. "
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leaguereset ( interaction , league_name : str ) :
""" Completely scraps the given league ' s current season, resetting everything to day 1 of the current season. Requires ownership. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if ( league . owner is not None and interaction . user . id in league . owner ) or ( league . owner is not None and interaction . user . id in config ( ) [ " owners " ] ) :
await interaction . response . send_message ( " You got it, boss. Give us two seconds and a bucket of white-out. " )
season_restart ( league )
league . season - = 1
league . season_reset ( )
await asyncio . sleep ( 1 )
await interaction . channel . send ( " Done and dusted. Go ahead and start the league again whenever you want. " )
return
2021-02-03 23:49:38 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( " That league isn ' t yours, boss. " )
else :
raise CommandError ( " We can ' t find that league. Yay? " )
2021-02-04 06:25:52 +00:00
class LeagueForceStopCommand ( Command ) :
name = " leagueforcestop "
2023-03-31 18:07:01 +00:00
template = " m;leagueforcestop [league name] [bot mention] "
2021-02-04 06:25:52 +00:00
description = " Halts a league and removes it from the list of currently running leagues. To be used in the case of crashed loops. "
def isauthorized ( self , user ) :
return user . id in config ( ) [ " owners " ]
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2023-03-31 18:07:01 +00:00
league_name = command . split ( " \n " ) [ 0 ] . split ( " " ) [ 0 ] . strip ( )
2021-02-04 06:25:52 +00:00
for index in range ( 0 , len ( active_leagues ) ) :
if active_leagues [ index ] . name == league_name :
active_leagues . pop ( index )
await msg . channel . send ( " League halted, boss. We hope you did that on purpose. " )
return
2021-03-26 21:38:03 +00:00
raise CommandError ( " That league either doesn ' t exist or isn ' t in the active list. So, huzzah? " )
2021-02-04 06:25:52 +00:00
2021-03-28 18:07:34 +00:00
class LeagueReplaceTeamCommand ( Command ) :
name = " leaguereplaceteam "
template = " m;leaguereplaceteam [league name] \n [team to remove] \n [team to add] "
2021-02-12 00:26:33 +00:00
description = " Adds a team to a league, removing the old one in the process. Can only be executed by a league owner, and only before the start of a new season. "
2023-03-31 18:07:01 +00:00
2021-02-12 00:26:33 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leaguereplaceteam ( interaction , league_name : str , removeteam : str , addteam : str ) :
""" Removes a team from a league, replacing it with another. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if league . day != 1 :
await interaction . response . send ( " That league hasn ' t finished its current season yet, chief. Either reset it, or be patient. " )
return
if ( league . owner is not None and interaction . user . id in league . owner ) or ( league . owner is not None and interaction . user . id in config ( ) [ " owners " ] ) :
try :
team_del = get_team_fuzzy_search ( removeteam )
team_add = get_team_fuzzy_search ( addteam )
except IndexError :
raise CommandError ( " Three lines, boss. Make sure you give us the team to remove, then the team to add. " )
if team_add . name == team_del . name :
raise CommandError ( " Quit being cheeky. The teams have to be different. " )
2021-03-26 21:38:03 +00:00
2023-03-31 18:07:01 +00:00
if team_del is None or team_add is None :
raise CommandError ( " We couldn ' t find one or both of those teams, boss. Try again. " )
2021-02-12 00:30:48 +00:00
2023-03-31 18:07:01 +00:00
subleague , division = league . find_team ( team_del )
2021-02-12 00:30:48 +00:00
2023-03-31 18:07:01 +00:00
if subleague is None or division is None :
raise CommandError ( " That first team isn ' t in that league, chief. So, that ' s good, right? " )
2021-02-12 00:30:48 +00:00
2023-03-31 18:07:01 +00:00
if league . find_team ( team_add ) [ 0 ] is not None :
raise CommandError ( " That second team is already in that league, chief. No doubles. " )
for index in range ( 0 , len ( league . league [ subleague ] [ division ] ) ) :
if league . league [ subleague ] [ division ] [ index ] . name == team_del . name :
league . league [ subleague ] [ division ] . pop ( index )
league . league [ subleague ] [ division ] . append ( team_add )
league . schedule = { }
league . generate_schedule ( )
leagues . save_league_as_new ( league )
await interaction . response . send_message ( embed = league . standings_embed ( ) )
await interaction . channel . send ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
2021-02-12 00:52:43 +00:00
else :
2023-03-31 18:07:01 +00:00
raise CommandError ( " That league isn ' t yours, chief. " )
else :
raise CommandError ( " We can ' t find that league. " )
2021-02-12 00:52:43 +00:00
2021-03-28 18:07:34 +00:00
class LeagueSwapTeamCommand ( Command ) :
name = " leagueswapteams "
template = " m;leagueswapteams [league name] \n [team a] \n [team b] "
description = " Swaps two teams in any divisions or conferences of your league. "
async def execute ( self , msg , command , flags ) :
league_name = command . split ( " \n " ) [ 0 ] . strip ( )
2023-03-31 18:07:01 +00:00
2021-03-28 18:07:34 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
@app_commands.rename ( league_name = " leaguename " )
async def leagueswapteam ( interaction , league_name : str , teama : str , teamb : str ) :
""" Swaps two teams ' divisional assignments. """
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if league . day != 1 :
await interaction . response . send_message ( " That league hasn ' t finished its current season yet, chief. Either reset it, or be patient. " )
return
if ( league . owner is not None and interaction . user . id in league . owner ) or ( league . owner is not None and interaction . user . id in config ( ) [ " owners " ] ) :
try :
team_a = get_team_fuzzy_search ( teama )
team_b = get_team_fuzzy_search ( teamb )
except IndexError :
raise CommandError ( " Three lines, boss. Make sure you give us the team to remove, then the team to add. " )
if team_a . name == team_b . name :
raise CommandError ( " Quit being cheeky. The teams have to be different. " )
if team_a is None or team_b is None :
raise CommandError ( " We couldn ' t find one or both of those teams, boss. Try again. " )
a_subleague , a_division = league . find_team ( team_a )
b_subleague , b_division = league . find_team ( team_b )
if a_subleague is None or b_subleague is None :
raise CommandError ( " One of those teams isn ' t in the league. Try leaguereplaceteam instead. " )
for index in range ( 0 , len ( league . league [ a_subleague ] [ a_division ] ) ) :
if league . league [ a_subleague ] [ a_division ] [ index ] . name == team_a . name :
a_index = index
for index in range ( 0 , len ( league . league [ b_subleague ] [ b_division ] ) ) :
if league . league [ b_subleague ] [ b_division ] [ index ] . name == team_b . name :
b_index = index
league . league [ a_subleague ] [ a_division ] [ a_index ] = team_b
league . league [ b_subleague ] [ b_division ] [ b_index ] = team_a
league . schedule = { }
league . generate_schedule ( )
leagues . save_league_as_new ( league )
await interaction . response . send_message ( embed = league . standings_embed ( ) )
await interaction . channel . send ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
else :
raise CommandError ( " That league isn ' t yours, chief. " )
else :
raise CommandError ( " We can ' t find that league. " )
2021-02-12 00:52:43 +00:00
class LeagueRenameCommand ( Command ) :
name = " leaguerename "
2021-03-24 21:43:03 +00:00
template = " m;leaguerename [league name] \n [old conference/division name] \n [new conference/division name] "
description = " Changes the name of an existing conference or division. Can only be executed by a league owner, and only before the start of a new season. "
2021-02-12 00:52:43 +00:00
2021-03-26 21:38:03 +00:00
async def execute ( self , msg , command , flags ) :
2021-02-12 00:52:43 +00:00
league_name = command . split ( " \n " ) [ 0 ] . strip ( )
if league_exists ( league_name ) :
league = leagues . load_league_file ( league_name )
if league . day != 1 :
2021-03-26 21:38:03 +00:00
raise CommandError ( " That league hasn ' t finished its current season yet, chief. Either reset it, or be patient. " )
2021-02-12 00:52:43 +00:00
if ( league . owner is not None and msg . author . id in league . owner ) or ( league . owner is not None and msg . author . id in config ( ) [ " owners " ] ) :
try :
old_name = command . split ( " \n " ) [ 1 ] . strip ( )
new_name = command . split ( " \n " ) [ 2 ] . strip ( )
except IndexError :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Three lines, boss. Make sure you give us the old name, then the new name, on their own lines. " )
2021-02-12 00:52:43 +00:00
if old_name == new_name :
2021-03-26 21:38:03 +00:00
raise CommandError ( " Quit being cheeky. They have to be different names, clearly. " )
2021-02-12 00:52:43 +00:00
found = False
for subleague in league . league . keys ( ) :
if subleague == new_name :
found = True
break
for division in league . league [ subleague ] :
if division == new_name :
found = True
break
if found :
2021-03-26 21:38:03 +00:00
raise CommandError ( f " { new_name } is already present in that league, chief. They have to be different. " )
2021-02-12 00:52:43 +00:00
found = False
for subleague in league . league . keys ( ) :
if subleague == old_name :
league . league [ new_name ] = league . league . pop ( old_name )
found = True
break
for division in league . league [ subleague ] :
if division == old_name :
league . league [ subleague ] [ new_name ] = league . league [ subleague ] . pop ( old_name )
found = True
break
if not found :
2021-03-26 21:38:03 +00:00
raise CommandError ( f " We couldn ' t find { old_name } anywhere in that league, boss. " )
2021-02-12 00:52:43 +00:00
leagues . save_league_as_new ( league )
await msg . channel . send ( embed = league . standings_embed ( ) )
await msg . channel . send ( " Paperwork signed, stamped, copied, and faxed up to the goddess. Xie ' s pretty quick with this stuff. " )
else :
2021-03-26 21:38:03 +00:00
raise CommandError ( " That league isn ' t yours, chief. " )
2021-02-12 00:52:43 +00:00
else :
2021-03-26 21:38:03 +00:00
raise CommandError ( " We can ' t find that league. " )
2021-02-04 06:25:52 +00:00
2021-02-22 19:27:04 +00:00
class OBLExplainCommand ( Command ) :
name = " oblhelp "
template = " m;oblhelp "
description = " Explains the One Big League! "
2023-03-31 18:07:01 +00:00
2021-02-22 19:27:04 +00:00
2023-03-31 18:07:01 +00:00
@client.tree.command ( )
async def oblhelp ( interaction ) :
""" Explains the One Big League! """
await interaction . response . send_message ( """ The One Big League, or OBL, is an asynchronous league that includes every team in sim16 ' s database. To participate, just use the oblteam command with your team of choice. **No signup is required!** This will give you a list of five opponents; playing against one of them and winning nets you a point, and will refresh the list with five new opponents. **Losing results in no penalty!** Each meta-season will last for ~~a few weeks~~ *shrugs*, after which the leaderboards are reset to start the race again!
2021-02-22 19:27:04 +00:00
2023-03-31 18:07:01 +00:00
Look out for the people wrestling emoji , which indicates the potential for a 🤼 Wrassle Match 🤼 , where both teams are on each others ' lists and both have the opportunity to score a point. Team rankings and points can also be viewed in the oblteam command, and the overall OBL leaderboard can be checked with the oblstandings command. Best of luck!!
2021-02-22 19:27:04 +00:00
""" )
class OBLLeaderboardCommand ( Command ) :
name = " oblstandings "
template = " m;oblstandings "
description = " Displays the 15 teams with the most OBL points in this meta-season. "
2023-03-31 18:28:04 +00:00
@client.tree.command ( )
async def oblstandings ( interaction ) :
leaders_list = db . obl_leaderboards ( ) [ : 15 ]
leaders = [ ]
rank = 1
for team , points in leaders_list :
leaders . append ( { " name " : team , " points " : points } )
rank + = 1
embed = discord . Embed ( color = discord . Color . red ( ) , title = " The One Big League " )
for index in range ( 0 , len ( leaders ) ) :
embed . add_field ( name = f " { index + 1 } . { leaders [ index ] [ ' name ' ] } " , value = f " { leaders [ index ] [ ' points ' ] } points " , inline = False )
await interaction . response . send_message ( embed = embed )
2021-02-22 19:27:04 +00:00
class OBLTeamCommand ( Command ) :
name = " oblteam "
template = " m;oblteam [team name] "
description = " Displays a team ' s rank, current OBL points, and current opponent selection. "
2023-03-31 18:28:04 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " )
2023-03-31 18:29:38 +00:00
async def oblteam ( interaction , team_name : str ) :
2023-03-31 18:28:04 +00:00
""" Displays a team ' s rank, current OBL points, and current opponent selection. """
team = get_team_fuzzy_search ( team_name )
if team is None :
raise CommandError ( " Sorry boss, we can ' t find that team. " )
rival_team = None
points , beaten_teams_list , opponents_string , rank , rival_name = db . get_obl_stats ( team , full = True )
opponents_list = db . newline_string_to_list ( opponents_string )
for index in range ( 0 , len ( opponents_list ) ) :
oppteam = get_team_fuzzy_search ( opponents_list [ index ] )
opplist = db . get_obl_stats ( oppteam ) [ 1 ]
if team . name in opplist :
opponents_list [ index ] = opponents_list [ index ] + " 🤼 "
if rival_name == opponents_list [ index ] :
opponents_list [ index ] = opponents_list [ index ] + " 😈 "
if rival_name is not None :
rival_team = games . get_team ( rival_name )
opponents_string = db . list_to_newline_string ( opponents_list )
embed = discord . Embed ( color = discord . Color . red ( ) , title = f " { team . name } in the One Big League " )
embed . add_field ( name = " OBL Points " , value = points )
embed . add_field ( name = " Rank " , value = rank )
embed . add_field ( name = " Bounty Board " , value = opponents_string , inline = False )
if rival_team is not None :
r_points , r_beaten_teams_list , r_opponents_string , r_rank , r_rival_name = db . get_obl_stats ( rival_team , full = True )
embed . add_field ( name = " Rival " , value = f " ** { rival_team . name } **: Rank { r_rank } \n { rival_team . slogan } \n Points: { r_points } " )
if r_rival_name == team . name :
embed . set_footer ( text = " 🔥 " )
else :
embed . set_footer ( text = " Set a rival with m;oblrival! " )
await interaction . response . send_message ( embed = embed )
2021-02-22 19:27:04 +00:00
2021-02-22 23:35:40 +00:00
class OBLSetRivalCommand ( Command ) :
2021-02-22 23:49:36 +00:00
name = " oblrival "
2021-02-22 23:35:40 +00:00
template = " m;oblrival \n [your team name] \n [rival team name] "
description = " Sets your team ' s OBL rival. Can be changed at any time. Requires ownership. "
2023-03-31 18:28:04 +00:00
@client.tree.command ( )
@app_commands.rename ( owned_team_name = " yourteam " , rival_team_name = " newrivalteam " )
async def oblsetrival ( interaction , owned_team_name : str , rival_team_name : str ) :
""" Sets your team ' s OBL rival. Can be changed at any time. """
team_i = get_team_fuzzy_search ( owned_team_name )
team_r = get_team_fuzzy_search ( rival_team_name )
team , owner_id = games . get_team_and_owner ( team_i . name )
if team is None or team_r is None :
raise CommandError ( " Can ' t find one of those teams, boss. Typo? " )
elif owner_id != interaction . user . id and interaction . user . id not in config ( ) [ " owners " ] :
raise CommandError ( " You ' re not authorized to mess with this team. Sorry, boss. " )
try :
db . set_obl_rival ( team , team_r )
await interaction . response . send_message ( " One pair of mortal enemies, coming right up. Unless you ' re more of the ' enemies to lovers ' type. We can manage that too, don ' t worry. " )
except :
raise CommandError ( " Hm. We don ' t think that team has tried to do anything in the One Big League yet, so you ' ll have to wait for consent. Get them to check their bounty board. " )
2021-02-22 23:35:40 +00:00
2021-02-22 20:07:18 +00:00
class OBLConqueredCommand ( Command ) :
name = " oblwins "
template = " m;oblwins [team name] "
description = " Displays all teams that a given team has won points off of. "
2023-03-31 18:28:04 +00:00
@client.tree.command ( )
@app_commands.rename ( team_name = " team " )
async def oblconquestlist ( interaction , team_name : str ) :
team = get_team_fuzzy_search ( team_name )
if team is None :
raise CommandError ( " Sorry boss, we can ' t find that team. " )
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
points , teams , oppTeams , rank , rivalName = db . get_obl_stats ( team , full = True )
pages = [ ]
page_max = math . ceil ( len ( teams ) / 25 )
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
title_text = f " Rank { rank } : { team . name } "
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
for page in range ( 0 , page_max ) :
embed = discord . Embed ( color = discord . Color . red ( ) , title = title_text )
embed . set_footer ( text = f " { points } OBL Points " )
for i in range ( 0 , 25 ) :
try :
thisteam = games . get_team ( teams [ i + 25 * page ] )
if thisteam . slogan . strip ( ) != " " :
embed . add_field ( name = thisteam . name , value = thisteam . slogan )
else :
embed . add_field ( name = thisteam . name , value = " 404: Slogan not found " )
except :
break
pages . append ( embed )
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
teams_list = await interaction . channel . send ( embed = pages [ 0 ] )
current_page = 0
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
if page_max > 1 :
await teams_list . add_reaction ( " ◀ " )
await teams_list . add_reaction ( " ▶ " )
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
def react_check ( payload ) :
return payload . user_id == interaction . user . id and payload . message_id == teams_list . id
2021-02-22 20:07:18 +00:00
2023-03-31 18:28:04 +00:00
while True :
try :
payload = await client . wait_for ( ' raw_reaction_add ' , timeout = 60.0 , check = react_check )
if payload . emoji . name == " ◀ " and current_page > 0 :
current_page - = 1
elif payload . emoji . name == " ▶ " and current_page < ( page_max - 1 ) :
current_page + = 1
await teams_list . edit ( embed = pages [ current_page ] )
except asyncio . TimeoutError :
return
2021-02-04 06:25:52 +00:00
2021-03-28 19:16:31 +00:00
class OBLResetCommand ( Command ) :
name = " oblreset "
2023-03-31 18:28:04 +00:00
template = " m;oblreset [bot mention] "
2021-03-28 19:16:31 +00:00
description = " NUKES THE OBL BOARD. BE CAREFUL. "
def isauthorized ( self , user ) :
return user . id in config ( ) [ " owners " ]
async def execute ( self , msg , command , flags ) :
if self . isauthorized ( msg . author ) :
db . clear_obl ( )
await msg . channel . send ( " 💣 " )
2021-03-26 21:38:03 +00:00
class TeamsInfoCommand ( Command ) :
name = " teamcount "
2023-03-31 18:28:04 +00:00
template = " m;teamcount [bot mention] "
2021-03-26 21:38:03 +00:00
description = " Prints a readout of how many teams exist in the sim. "
async def execute ( self , msg , command , flags ) :
await msg . channel . send ( " Let us flip through our records for a second... " )
await msg . channel . send ( f " We ' ve got { len ( games . get_all_teams ( ) ) } teams! Thanks for asking. " )
2020-12-27 08:42:09 +00:00
commands = [
2023-03-31 06:18:28 +00:00
IntroduceCommand ( ) , #not needed
CountActiveGamesCommand ( ) , #owner only
2023-03-31 18:28:04 +00:00
TeamsInfoCommand ( ) , #workaround
2023-03-31 06:18:28 +00:00
AssignOwnerCommand ( ) , #owner only
2022-09-18 15:08:24 +00:00
ShowPlayerCommand ( ) , #done
2022-09-18 15:34:04 +00:00
SaveTeamCommand ( ) , #done
2022-09-18 15:49:50 +00:00
AssignArchetypeCommand ( ) , #done
2022-09-18 16:23:38 +00:00
ViewArchetypesCommand ( ) , #done
ArchetypeHelpCommand ( ) , #done
2020-12-28 09:09:02 +00:00
ImportCommand ( ) ,
2023-03-31 07:02:37 +00:00
SwapPlayerCommand ( ) , #done
MovePlayerCommand ( ) , #done
AddPlayerCommand ( ) , #done
RemovePlayerCommand ( ) , #done
ReplacePlayerCommand ( ) , #gonna delay
DeleteTeamCommand ( ) , #done
2022-09-18 16:37:55 +00:00
ShowTeamCommand ( ) , #done
2023-03-31 06:18:28 +00:00
SearchTeamsCommand ( ) , #not needed
2022-09-18 15:08:24 +00:00
StartGameCommand ( ) , #done
2022-09-18 15:34:04 +00:00
StartRandomGameCommand ( ) , #done
2023-03-31 17:22:10 +00:00
StartTournamentCommand ( ) , #workaround
2023-03-31 18:28:04 +00:00
OBLExplainCommand ( ) , #done
OBLTeamCommand ( ) , #done
OBLSetRivalCommand ( ) , #done
OBLConqueredCommand ( ) , #done
OBLLeaderboardCommand ( ) , #done
OBLResetCommand ( ) , #not needed
2023-03-31 18:07:01 +00:00
LeagueClaimCommand ( ) , #done
LeagueAddOwnersCommand ( ) , #done
2023-03-31 17:22:10 +00:00
LeagueSetPlayerModifiersCommand ( ) , #was a test command, never fully tested
StartLeagueCommand ( ) , #done
LeagueSubscribeCommand ( ) , #done
2023-03-31 18:07:01 +00:00
LeaguePauseCommand ( ) , #done
2023-03-31 17:22:10 +00:00
LeagueDisplayCommand ( ) , #done
LeagueLeadersCommand ( ) , #done
LeagueDivisionDisplayCommand ( ) , #done
2023-03-31 18:07:01 +00:00
LeagueWildcardCommand ( ) , #done
LeagueScheduleCommand ( ) , #done
LeagueTeamScheduleCommand ( ) , #done
LeagueRegenerateScheduleCommand ( ) , #done
LeagueSwapTeamCommand ( ) , #done
LeagueReplaceTeamCommand ( ) , #done
LeagueRenameCommand ( ) , #postponing
LeagueForceStopCommand ( ) , #not needed
2023-03-31 06:18:28 +00:00
CreditCommand ( ) , #done
RomanCommand ( ) , #not needed
2023-03-31 07:02:37 +00:00
HelpCommand ( ) ,
2021-01-04 23:52:34 +00:00
StartDraftCommand ( ) ,
2021-07-01 21:35:17 +00:00
DraftFlagsCommand ( ) ,
2021-02-04 06:28:33 +00:00
DraftPlayerCommand ( )
2020-12-27 08:42:09 +00:00
]
2020-12-26 21:35:18 +00:00
2021-02-11 23:34:26 +00:00
watching = False
2020-12-27 08:42:09 +00:00
gamesarray = [ ]
2021-01-04 04:18:45 +00:00
active_tournaments = [ ]
2021-01-11 11:17:14 +00:00
active_leagues = [ ]
2021-01-23 06:43:49 +00:00
active_standings = { }
2020-12-27 08:42:09 +00:00
setupmessages = { }
2020-12-26 21:35:18 +00:00
2020-12-28 08:21:47 +00:00
thread1 = threading . Thread ( target = main_controller . update_loop )
thread1 . start ( )
2020-12-27 08:42:09 +00:00
def config ( ) :
2021-01-06 10:53:51 +00:00
if not os . path . exists ( os . path . dirname ( config_filename ) ) :
os . makedirs ( os . path . dirname ( config_filename ) )
if not os . path . exists ( config_filename ) :
2020-12-27 08:42:09 +00:00
#generate default config
config_dic = {
" token " : " " ,
" owners " : [
0000
] ,
2021-07-23 22:32:36 +00:00
" ea " : [
0000
] ,
2021-08-13 19:00:31 +00:00
" blacklist " : [
0000
] ,
2020-12-27 08:42:09 +00:00
" prefix " : [ " m; " , " m! " ] ,
2020-12-31 08:32:01 +00:00
" simmadome_url " : " " ,
2020-12-27 08:42:09 +00:00
" soulscream channel id " : 0 ,
" game_freeze " : 0
}
2021-01-06 10:53:51 +00:00
with open ( config_filename , " w " ) as config_file :
2020-12-27 08:42:09 +00:00
json . dump ( config_dic , config_file , indent = 4 )
print ( " please fill in bot token and any bot admin discord ids to the new config.json file! " )
quit ( )
else :
2021-01-06 10:53:51 +00:00
with open ( config_filename ) as config_file :
2020-12-27 08:42:09 +00:00
return json . load ( config_file )
2020-12-20 05:13:23 +00:00
2020-12-27 08:42:09 +00:00
@client.event
async def on_ready ( ) :
2021-02-11 23:34:26 +00:00
global watching
2020-12-27 08:42:09 +00:00
db . initialcheck ( )
2021-01-18 07:36:24 +00:00
print ( f " logged in as { client . user } with token { config ( ) [ ' token ' ] } to { len ( client . guilds ) } servers " )
2021-02-11 23:19:58 +00:00
if not watching :
watching = True
watch_task = asyncio . create_task ( game_watcher ( ) )
await watch_task
2020-12-27 08:42:09 +00:00
2022-09-18 15:08:24 +00:00
@client.tree.error
async def on_app_command_error ( interaction , error ) :
await interaction . response . send_message ( str ( error . __cause__ ) , ephemeral = True )
2021-01-13 09:23:48 +00:00
2020-12-27 08:42:09 +00:00
@client.event
async def on_reaction_add ( reaction , user ) :
if reaction . message in setupmessages . keys ( ) :
game = setupmessages [ reaction . message ]
try :
if str ( reaction . emoji ) == " 🔼 " and not user == client . user :
new_player = games . player ( ono . get_stats ( db . get_user_player ( user ) [ " name " ] ) )
game . teams [ " away " ] . add_lineup ( new_player )
await reaction . message . channel . send ( f " { new_player } { new_player . star_string ( ' batting_stars ' ) } takes spot # { len ( game . teams [ ' away ' ] . lineup ) } on the away lineup. " )
elif str ( reaction . emoji ) == " 🔽 " and not user == client . user :
new_player = games . player ( ono . get_stats ( db . get_user_player ( user ) [ " name " ] ) )
game . teams [ " home " ] . add_lineup ( new_player )
await reaction . message . channel . send ( f " { new_player } { new_player . star_string ( ' batting_stars ' ) } takes spot # { len ( game . teams [ ' home ' ] . lineup ) } on the home lineup. " )
except :
2022-09-18 14:00:28 +00:00
await reaction . message . channel . send ( f " { user . display_name } , we can ' t find your idol. Maybe you don ' t have one yet? " )
2022-09-18 15:35:03 +00:00
@client.event
async def on_message ( msg ) :
if msg . author == client . user or not msg . webhook_id is None or msg . author . id in config ( ) [ " blacklist " ] :
return
command_b = False
for prefix in config ( ) [ " prefix " ] :
if msg . content . startswith ( prefix ) :
command_b = True
command = msg . content . split ( prefix , 1 ) [ 1 ]
if not command_b :
return
if msg . channel . id == config ( ) [ " soulscream channel id " ] :
await msg . channel . send ( ono . get_scream ( msg . author . display_name ) )
else :
try :
comm = next ( c for c in commands if command . split ( " " , 1 ) [ 0 ] . split ( " \n " , 1 ) [ 0 ] . lower ( ) == c . name )
send_text = command [ len ( comm . name ) : ]
first_line = send_text . split ( " \n " ) [ 0 ]
flags = [ ]
if " - " in first_line :
check = first_line . split ( " - " ) [ 1 : ]
for flag in [ _ for _ in check if _ != " " ] :
try :
flags . append ( ( flag . split ( " " ) [ 0 ] [ 0 ] . lower ( ) , flag . split ( " " , 1 ) [ 1 ] . strip ( ) ) )
except IndexError :
flags . append ( ( flag . split ( " " ) [ 0 ] [ 0 ] . lower ( ) , None ) )
if comm . isauthorized ( msg . author ) : #only execute command if authorized
await comm . execute ( msg , send_text , flags )
except StopIteration :
await msg . channel . send ( " Can ' t find that command, boss; try checking the list with `m;help`. " )
except CommandError as ce :
await msg . channel . send ( str ( ce ) )
2020-12-23 01:20:58 +00:00
async def setup_game ( channel , owner , newgame ) :
newgame . owner = owner
2020-12-27 22:22:11 +00:00
await channel . send ( f " Game sucessfully created! \n Start any commands for this game with ` { newgame . name } ` so we know who ' s talking about what. " )
2020-12-23 01:20:58 +00:00
await asyncio . sleep ( 1 )
await channel . send ( " Who ' s pitching for the away team? " )
def input ( msg ) :
return msg . content . startswith ( newgame . name ) and msg . channel == channel #if author or willing participant and in correct channel
while newgame . teams [ " home " ] . pitcher == None :
def nameinput ( msg ) :
return msg . content . startswith ( newgame . name ) and msg . channel == channel #if author or willing participant and in correct channel
2020-12-27 01:18:27 +00:00
2020-12-23 01:20:58 +00:00
while newgame . teams [ " away " ] . pitcher == None :
try :
namemsg = await client . wait_for ( ' message ' , check = input )
new_pitcher_name = discord . utils . escape_mentions ( namemsg . content . split ( f " { newgame . name } " ) [ 1 ] )
if len ( new_pitcher_name ) > 70 :
await channel . send ( " That player name is too long, chief. 70 or less. " )
else :
new_pitcher = games . player ( ono . get_stats ( new_pitcher_name ) )
newgame . teams [ " away " ] . set_pitcher ( new_pitcher )
await channel . send ( f " { new_pitcher } { new_pitcher . star_string ( ' pitching_stars ' ) } , pitching for the away team! \n Now, the home team ' s pitcher. Same dance, folks. " )
except NameError :
await channel . send ( " Uh. " )
try :
namemsg = await client . wait_for ( ' message ' , check = input )
new_pitcher_name = discord . utils . escape_mentions ( namemsg . content . split ( f " { newgame . name } " ) [ 1 ] )
if len ( new_pitcher_name ) > 70 :
await channel . send ( " That player name is too long, chief. 70 or less. " )
else :
new_pitcher = games . player ( ono . get_stats ( new_pitcher_name ) )
newgame . teams [ " home " ] . set_pitcher ( new_pitcher )
await channel . send ( f " And { new_pitcher } { new_pitcher . star_string ( ' pitching_stars ' ) } , pitching for the home team. " )
except :
await channel . send ( " Uh. " )
#pitchers assigned!
2020-12-27 22:22:11 +00:00
team_join_message = await channel . send ( f """ Now, the lineups! We need somewhere between 1 and 12 batters. Cloning helps a lot with this sort of thing.
2020-12-23 01:20:58 +00:00
React to this message with 🔼 to have your idol join the away team , or 🔽 to have them join the home team .
You can also enter names like you did for the pitchers , with a slight difference : ` away [ name ] ` or ` home [ name ] ` instead of just the name .
Creator , type ` { newgame . name } done ` to finalize lineups . """ )
await team_join_message . add_reaction ( " 🔼 " )
await team_join_message . add_reaction ( " 🔽 " )
setupmessages [ team_join_message ] = newgame
#emoji_task = asyncio.create_task(watch_for_reacts(team_join_message, ready, newgame))
#msg_task = asyncio.create_task(watch_for_messages(channel, ready, newgame))
#await asyncio.gather(
# watch_for_reacts(team_join_message, newgame),
# watch_for_messages(channel, newgame)
# )
def messagecheck ( msg ) :
return ( msg . content . startswith ( newgame . name ) ) and msg . channel == channel and msg . author != client . user
while not newgame . ready :
2020-12-27 01:09:19 +00:00
try :
msg = await client . wait_for ( ' message ' , timeout = 120.0 , check = messagecheck )
except asyncio . TimeoutError :
await channel . send ( " Game timed out. 120 seconds between players is a bit much, see? " )
2020-12-27 02:15:19 +00:00
del setupmessages [ team_join_message ]
del newgame
2020-12-27 01:09:19 +00:00
return
2020-12-23 01:20:58 +00:00
new_player = None
if msg . author == newgame . owner and msg . content == f " { newgame . name } done " :
if newgame . teams [ ' home ' ] . finalize ( ) and newgame . teams [ ' away ' ] . finalize ( ) :
newgame . ready = True
break
else :
side = None
if msg . content . split ( f " { newgame . name } " ) [ 1 ] . split ( " " , 1 ) [ 0 ] == " home " :
side = " home "
elif msg . content . split ( f " { newgame . name } " ) [ 1 ] . split ( " " , 1 ) [ 0 ] == " away " :
side = " away "
if side is not None :
new_player_name = discord . utils . escape_mentions ( msg . content . split ( f " { newgame . name } " ) [ 1 ] . split ( " " , 1 ) [ 1 ] )
if len ( new_player_name ) > 70 :
await channel . send ( " That player name is too long, chief. 70 or less. " )
else :
new_player = games . player ( ono . get_stats ( new_player_name ) )
try :
if new_player is not None :
newgame . teams [ side ] . add_lineup ( new_player )
await channel . send ( f " { new_player } { new_player . star_string ( ' batting_stars ' ) } takes spot # { len ( newgame . teams [ side ] . lineup ) } on the { side } lineup. " )
except :
True
del setupmessages [ team_join_message ] #cleanup!
await channel . send ( " Name the away team, creator. " )
def ownercheck ( msg ) :
return msg . author == newgame . owner
while newgame . teams [ " home " ] . name == None :
while newgame . teams [ " away " ] . name == None :
newname = await client . wait_for ( ' message ' , check = ownercheck )
if len ( newname . content ) < 30 :
newgame . teams [ ' away ' ] . name = newname . content
await channel . send ( f " Stepping onto the field, the visitors: { newname . content } ! \n Finally, the home team, and we can begin. " )
else :
await channel . send ( " Hey, keep these to 30 characters or less please. Discord messages have to stay short. " )
newname = await client . wait_for ( ' message ' , check = ownercheck )
if len ( newname . content ) < 30 :
newgame . teams [ ' home ' ] . name = newname . content
await channel . send ( f " Next on the diamond, your home team: { newname . content } ! " )
else :
await channel . send ( " Hey, keep these to 30 characters or less please. Discord messages have to stay short. " )
await asyncio . sleep ( 3 )
await channel . send ( f " ** { newgame . teams [ ' away ' ] . name } at { newgame . teams [ ' home ' ] . name } ** " )
game_task = asyncio . create_task ( watch_game ( channel , newgame ) )
await game_task
2022-09-18 15:08:24 +00:00
async def watch_game ( channel , newgame , user = None , league = None , interaction = None ) :
2021-01-03 11:06:51 +00:00
newgame , state_init = prepare_game ( newgame )
2020-12-27 01:18:27 +00:00
2021-01-03 11:06:51 +00:00
if league is not None :
discrim_string = league
state_init [ " is_league " ] = True
elif user is not None :
2021-01-04 04:18:45 +00:00
if isinstance ( user , str ) :
discrim_string = f " Started by { user } "
else :
discrim_string = f " Started by { user . name } "
2021-01-03 11:06:51 +00:00
state_init [ " is_league " ] = False
else :
discrim_string = " Unclaimed game. "
state_init [ " is_league " ] = False
2021-01-05 19:26:54 +00:00
id = str ( uuid4 ( ) )
ext = " ?game= " + id
2021-01-03 11:06:51 +00:00
if league is not None :
ext + = " &league= " + urllib . parse . quote_plus ( league )
2022-09-18 15:08:24 +00:00
if interaction is None :
await channel . send ( f " { newgame . teams [ ' away ' ] . name } vs. { newgame . teams [ ' home ' ] . name } , starting at { config ( ) [ ' simmadome_url ' ] + ext } " )
else :
await interaction . response . send_message ( f " { newgame . teams [ ' away ' ] . name } vs. { newgame . teams [ ' home ' ] . name } , starting at { config ( ) [ ' simmadome_url ' ] + ext } " )
2021-01-05 19:26:54 +00:00
gamesarray . append ( ( newgame , channel , user , id ) )
2021-01-03 11:06:51 +00:00
2021-01-05 19:26:54 +00:00
main_controller . master_games_dic [ id ] = ( newgame , state_init , discrim_string )
2021-01-03 11:06:51 +00:00
def prepare_game ( newgame , league = None , weather_name = None ) :
2021-02-21 23:45:37 +00:00
if weather_name is None and newgame . weather . name == " Sunny " :
2021-02-21 08:03:25 +00:00
weathers = weather . all_weathers ( )
2021-02-21 18:02:47 +00:00
newgame . weather = weathers [ random . choice ( list ( weathers . keys ( ) ) ) ] ( newgame )
2020-12-26 05:08:45 +00:00
2021-03-27 23:14:20 +00:00
if newgame . voice is None :
2021-04-01 02:01:20 +00:00
newgame . voice = random . choices ( gametext . weighted_voices ( ) [ 0 ] , weights = gametext . weighted_voices ( ) [ 1 ] ) [ 0 ] ( )
2021-03-25 00:04:03 +00:00
2020-12-28 08:21:47 +00:00
state_init = {
" away_name " : newgame . teams [ ' away ' ] . name ,
" home_name " : newgame . teams [ ' home ' ] . name ,
" max_innings " : newgame . max_innings ,
" update_pause " : 0 ,
" top_of_inning " : True ,
" victory_lap " : False ,
" weather_emoji " : newgame . weather . emoji ,
" weather_text " : newgame . weather . name ,
2021-03-25 00:04:03 +00:00
" start_delay " : 5 ,
2021-01-05 00:42:42 +00:00
" end_delay " : 9
2020-12-28 08:21:47 +00:00
}
2021-01-03 11:06:51 +00:00
if league is None :
state_init [ " is_league " ] = False
else :
state_init [ " is_league " ] = True
2021-07-06 01:26:55 +00:00
newgame . teams [ ' away ' ] . apply_team_mods ( league . name )
newgame . teams [ ' home ' ] . apply_team_mods ( league . name )
2021-01-03 11:06:51 +00:00
return newgame , state_init
2020-12-28 08:21:47 +00:00
2021-01-03 11:06:51 +00:00
async def start_tournament_round ( channel , tourney , seeding = None ) :
current_games = [ ]
if tourney . bracket is None :
if seeding is None :
tourney . build_bracket ( random_sort = True )
2020-12-26 05:08:45 +00:00
2021-01-03 11:06:51 +00:00
games_to_start = tourney . bracket . get_bottom_row ( )
2021-01-02 16:51:47 +00:00
2021-01-03 11:06:51 +00:00
for pair in games_to_start :
if pair [ 0 ] is not None and pair [ 1 ] is not None :
2021-01-15 08:18:41 +00:00
team_a = get_team_fuzzy_search ( pair [ 0 ] . name )
team_b = get_team_fuzzy_search ( pair [ 1 ] . name )
if tourney . league is not None :
2021-01-15 11:18:29 +00:00
if tourney . day is None :
tourney . day = tourney . league . day
team_a . set_pitcher ( rotation_slot = tourney . day )
team_b . set_pitcher ( rotation_slot = tourney . day )
2021-01-15 08:18:41 +00:00
this_game = games . game ( team_a . finalize ( ) , team_b . finalize ( ) , length = tourney . game_length )
2021-01-03 11:06:51 +00:00
this_game , state_init = prepare_game ( this_game )
2021-01-02 16:51:47 +00:00
2021-01-03 11:06:51 +00:00
state_init [ " is_league " ] = True
2021-01-05 20:31:50 +00:00
if tourney . round_check ( ) :
series_string = f " Best of { tourney . finals_length } : "
else :
series_string = f " Best of { tourney . series_length } : "
state_init [ " title " ] = f " { series_string } 0 - 0 "
2021-01-03 11:06:51 +00:00
discrim_string = tourney . name
2021-01-01 17:08:11 +00:00
2021-01-05 19:26:54 +00:00
id = str ( uuid4 ( ) )
current_games . append ( ( this_game , id ) )
main_controller . master_games_dic [ id ] = ( this_game , state_init , discrim_string )
2020-12-26 05:08:45 +00:00
2021-01-03 11:06:51 +00:00
ext = " ?league= " + urllib . parse . quote_plus ( tourney . name )
2021-03-28 18:00:38 +00:00
if tourney . round_check ( ) : #if finals
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , f " The { tourney . name } finals are starting now, at { config ( ) [ ' simmadome_url ' ] + ext } " )
else :
await channel . send ( f " The { tourney . name } finals are starting now, at { config ( ) [ ' simmadome_url ' ] + ext } " )
2021-01-04 04:18:45 +00:00
finals = True
2021-01-04 04:23:02 +00:00
2021-03-28 18:00:38 +00:00
else :
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , f " { len ( current_games ) } games started for the { tourney . name } tournament, at { config ( ) [ ' simmadome_url ' ] + ext } " )
else :
await channel . send ( f " { len ( current_games ) } games started for the { tourney . name } tournament, at { config ( ) [ ' simmadome_url ' ] + ext } " )
2021-01-04 04:18:45 +00:00
finals = False
await tourney_round_watcher ( channel , tourney , current_games , config ( ) [ ' simmadome_url ' ] + ext , finals )
async def continue_tournament_series ( tourney , queue , games_list , wins_in_series ) :
for oldgame in queue :
2021-03-28 17:16:53 +00:00
away_team = games . get_team ( oldgame . teams [ " home " ] . name )
home_team = games . get_team ( oldgame . teams [ " away " ] . name )
2021-01-15 08:18:41 +00:00
if tourney . league is not None :
2021-01-15 11:18:29 +00:00
if tourney . day is None :
tourney . day = tourney . league . day
2021-01-15 11:21:25 +00:00
away_team . set_pitcher ( rotation_slot = tourney . day )
home_team . set_pitcher ( rotation_slot = tourney . day )
2021-01-15 09:27:20 +00:00
2021-01-15 08:18:41 +00:00
2021-01-04 04:18:45 +00:00
this_game = games . game ( away_team . finalize ( ) , home_team . finalize ( ) , length = tourney . game_length )
this_game , state_init = prepare_game ( this_game )
state_init [ " is_league " ] = True
2021-01-05 20:31:50 +00:00
if tourney . round_check ( ) :
series_string = f " Best of { tourney . finals_length } : "
else :
series_string = f " Best of { tourney . series_length } : "
2021-03-28 18:00:38 +00:00
state_init [ " title " ] = f " { series_string } { wins_in_series [ away_team . name ] } - { wins_in_series [ home_team . name ] } "
2021-01-04 04:18:45 +00:00
discrim_string = tourney . name
2021-01-05 19:26:54 +00:00
id = str ( uuid4 ( ) )
games_list . append ( ( this_game , id ) )
main_controller . master_games_dic [ id ] = ( this_game , state_init , discrim_string )
2021-01-04 04:18:45 +00:00
return games_list
async def tourney_round_watcher ( channel , tourney , games_list , filter_url , finals = False ) :
2021-01-03 11:06:51 +00:00
tourney . active = True
2021-01-04 04:18:45 +00:00
active_tournaments . append ( tourney )
wins_in_series = { }
winner_list = [ ]
while tourney . active :
queued_games = [ ]
while len ( games_list ) > 0 :
try :
for i in range ( 0 , len ( games_list ) ) :
game , key = games_list [ i ]
2021-01-15 04:39:08 +00:00
if game . over and ( ( key in main_controller . master_games_dic . keys ( ) and main_controller . master_games_dic [ key ] [ 1 ] [ " end_delay " ] < = 8 ) or not key in main_controller . master_games_dic . keys ( ) ) :
2021-01-04 04:18:45 +00:00
if game . teams [ ' home ' ] . name not in wins_in_series . keys ( ) :
wins_in_series [ game . teams [ " home " ] . name ] = 0
if game . teams [ ' away ' ] . name not in wins_in_series . keys ( ) :
wins_in_series [ game . teams [ " away " ] . name ] = 0
winner_name = game . teams [ ' home ' ] . name if game . teams [ ' home ' ] . score > game . teams [ ' away ' ] . score else game . teams [ ' away ' ] . name
if winner_name in wins_in_series . keys ( ) :
wins_in_series [ winner_name ] + = 1
else :
wins_in_series [ winner_name ] = 1
final_embed = game_over_embed ( game )
2021-01-14 03:34:46 +00:00
final_embed . add_field ( name = " Series score: " , value = f " { wins_in_series [ game . teams [ ' away ' ] . name ] } - { wins_in_series [ game . teams [ ' home ' ] . name ] } " )
2021-03-28 18:00:38 +00:00
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , f " A { tourney . name } game just ended! " )
await league_subscriber_update ( tourney . league , channel , final_embed )
else :
await channel . send ( f " A { tourney . name } game just ended! " )
await channel . send ( embed = final_embed )
2021-01-04 04:18:45 +00:00
if wins_in_series [ winner_name ] > = int ( ( tourney . series_length + 1 ) / 2 ) and not finals :
winner_list . append ( winner_name )
elif wins_in_series [ winner_name ] > = int ( ( tourney . finals_length + 1 ) / 2 ) :
winner_list . append ( winner_name )
else :
queued_games . append ( game )
games_list . pop ( i )
break
except :
print ( " something went wrong in tourney_watcher " )
await asyncio . sleep ( 4 )
2021-01-15 11:18:29 +00:00
if tourney . league is not None :
tourney . day + = 1
2021-01-04 04:18:45 +00:00
if len ( queued_games ) > 0 :
2021-01-15 08:18:41 +00:00
if tourney . league is not None :
now = datetime . datetime . now ( )
validminutes = [ int ( ( 60 * div ) / tourney . league . games_per_hour ) for div in range ( 0 , tourney . league . games_per_hour ) ]
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
2021-01-15 09:31:31 +00:00
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
2021-03-28 18:00:38 +00:00
wait_seconds = ( next_start - now ) . seconds
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , f " The next batch of games for the { tourney . name } will start in { math . ceil ( wait_seconds / 60 ) } minutes. " )
else :
await channel . send ( f " The next batch of games for the { tourney . name } will start in { math . ceil ( wait_seconds / 60 ) } minutes. " )
2021-01-15 08:18:41 +00:00
await asyncio . sleep ( wait_seconds )
else :
2021-03-28 18:00:38 +00:00
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , f " The next batch of games for { tourney . name } will start in { int ( tourney . delay / 60 ) } minutes. " )
else :
await channel . send ( f " The next batch of games for { tourney . name } will start in { int ( tourney . delay / 60 ) } minutes. " )
2021-01-15 08:18:41 +00:00
await asyncio . sleep ( tourney . delay )
2021-03-28 18:00:38 +00:00
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , f " { len ( queued_games ) } games for { tourney . name } , starting at { filter_url } " )
else :
await channel . send ( f " { len ( queued_games ) } games for { tourney . name } , starting at { filter_url } " )
2021-01-04 04:18:45 +00:00
games_list = await continue_tournament_series ( tourney , queued_games , games_list , wins_in_series )
else :
tourney . active = False
if finals : #if this last round was finals
embed = discord . Embed ( color = discord . Color . dark_purple ( ) , title = f " { winner_list [ 0 ] } win the { tourney . name } finals! " )
2021-02-20 00:04:40 +00:00
if tourney . league is not None and tourney . day > tourney . league . day :
2021-01-15 11:18:29 +00:00
tourney . league . day = tourney . day
2021-03-28 18:00:38 +00:00
if tourney . league is not None :
await league_subscriber_update ( tourney . league , channel , embed )
else :
await channel . send ( embed = embed )
2021-01-15 07:12:30 +00:00
tourney . winner = get_team_fuzzy_search ( winner_list [ 0 ] )
2021-01-04 04:18:45 +00:00
active_tournaments . pop ( active_tournaments . index ( tourney ) )
return
tourney . bracket . set_winners_dive ( winner_list )
winners_string = " "
for game in tourney . bracket . get_bottom_row ( ) :
winners_string + = f " { game [ 0 ] . name } \n { game [ 1 ] . name } \n "
2021-01-15 08:18:41 +00:00
if tourney . league is not None :
now = datetime . datetime . now ( )
validminutes = [ int ( ( 60 * div ) / tourney . league . games_per_hour ) for div in range ( 0 , tourney . league . games_per_hour ) ]
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
2021-01-15 09:31:31 +00:00
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
2021-01-15 08:18:41 +00:00
wait_seconds = ( next_start - now ) . seconds
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( tourney . league , channel , f """ This round of games for the { tourney . name } is now complete! The next round will start in { math . ceil ( wait_seconds / 60 ) } minutes.
2021-01-15 08:18:41 +00:00
Advancing teams :
{ winners_string } """ )
await asyncio . sleep ( wait_seconds )
else :
await channel . send ( f """
2021-01-04 04:18:45 +00:00
This round of games for { tourney . name } is now complete ! The next round will be starting in { int ( tourney . round_delay / 60 ) } minutes .
Advancing teams :
{ winners_string } """ )
2021-01-15 08:18:41 +00:00
await asyncio . sleep ( tourney . round_delay )
2021-01-04 04:18:45 +00:00
await start_tournament_round ( channel , tourney )
2021-01-03 11:30:07 +00:00
2020-12-27 05:52:34 +00:00
2020-12-28 00:05:49 +00:00
async def team_delete_confirm ( channel , team , owner ) :
team_msg = await channel . send ( embed = build_team_embed ( team ) )
checkmsg = await channel . send ( " Is this the team you want to axe, boss? " )
await checkmsg . add_reaction ( " 👍 " )
await checkmsg . add_reaction ( " 👎 " )
2023-03-31 07:02:37 +00:00
def react_check ( payload ) :
2023-04-01 04:13:00 +00:00
return payload . user_id == owner . id and payload . message_id == checkmsg . id
2020-12-28 00:05:49 +00:00
try :
2023-03-31 07:02:37 +00:00
payload = await client . wait_for ( ' raw_reaction_add ' , timeout = 20.0 , check = react_check )
2023-04-01 04:13:00 +00:00
if payload . emoji . name == " 👍 " :
2020-12-28 00:05:49 +00:00
await channel . send ( " Step back, this could get messy. " )
if db . delete_team ( team ) :
await asyncio . sleep ( 2 )
await channel . send ( " Job ' s done. We ' ll clean up on our way out, don ' t worry. " )
else :
await asyncio . sleep ( 2 )
await channel . send ( " Huh. Didn ' t quite work. Tell xvi next time you see xer. " )
return
2023-04-01 04:13:00 +00:00
elif payload . emoji . name == " 👎 " :
2020-12-28 00:05:49 +00:00
await channel . send ( " Message received. Pumping brakes, turning this car around. " )
return
except asyncio . TimeoutError :
await channel . send ( " Guessing you got cold feet, so we ' re putting the axe away. Let us know if we need to fetch it again, aye? " )
return
2020-12-27 05:52:34 +00:00
2020-12-27 01:18:27 +00:00
2021-01-05 05:41:51 +00:00
def build_draft_embed ( names , title = " The Draft " , footer = " You must choose " ) :
embed = discord . Embed ( color = discord . Color . purple ( ) , title = title )
2021-01-04 23:52:34 +00:00
column_size = 7
for i in range ( 0 , len ( names ) , column_size ) :
draft = ' \n ' . join ( names [ i : i + column_size ] )
embed . add_field ( name = " - " , value = draft , inline = True )
2021-01-05 05:41:51 +00:00
embed . set_footer ( text = footer )
2021-01-04 23:52:34 +00:00
return embed
2020-12-24 09:51:38 +00:00
def build_team_embed ( team ) :
embed = discord . Embed ( color = discord . Color . purple ( ) , title = team . name )
lineup_string = " "
for player in team . lineup :
lineup_string + = f " { player . name } { player . star_string ( ' batting_stars ' ) } \n "
2021-01-02 06:10:52 +00:00
rotation_string = " "
for player in team . rotation :
rotation_string + = f " { player . name } { player . star_string ( ' pitching_stars ' ) } \n "
embed . add_field ( name = " Rotation: " , value = rotation_string , inline = False )
2020-12-24 09:51:38 +00:00
embed . add_field ( name = " Lineup: " , value = lineup_string , inline = False )
2021-02-03 07:51:01 +00:00
embed . add_field ( name = " █a██: " , value = str ( abs ( hash ( team . name ) ) % ( 10 * * 4 ) ) )
2020-12-24 09:51:38 +00:00
embed . set_footer ( text = team . slogan )
return embed
2020-12-20 00:08:09 +00:00
2020-12-20 12:25:25 +00:00
def build_star_embed ( player_json ) :
starkeys = { " batting_stars " : " Batting " , " pitching_stars " : " Pitching " , " baserunning_stars " : " Baserunning " , " defense_stars " : " Defense " }
embed = discord . Embed ( color = discord . Color . purple ( ) , title = player_json [ " name " ] )
2021-06-30 20:03:33 +00:00
if player_json [ " name " ] == " Tim Locastro " : #the tim easter egg
for key in [ " batting_stars " , " pitching_stars " , " baserunning_stars " , " defense_stars " ] :
#random star value between 0 and 6.5
stars = random . randint ( 0 , 6 )
half_star = random . random ( ) < 0.5 #half star addition
if half_star :
stars = half_star + 0.5
player_json [ key ] = stars
2020-12-20 12:25:25 +00:00
for key in starkeys . keys ( ) :
2020-12-20 19:05:31 +00:00
embedstring = " "
2020-12-20 12:25:25 +00:00
starstring = str ( player_json [ key ] )
2020-12-27 01:18:01 +00:00
starnum = int ( starstring [ 0 ] )
addhalf = " .5 " in starstring
2020-12-21 05:23:02 +00:00
embedstring + = " ⭐ " * starnum
2020-12-20 12:25:25 +00:00
if addhalf :
embedstring + = " ✨ "
2020-12-27 01:18:01 +00:00
elif starnum == 0 : # why check addhalf twice, amirite
embedstring + = " ⚪️ "
2020-12-20 12:25:25 +00:00
embed . add_field ( name = starkeys [ key ] , value = embedstring , inline = False )
return embed
2020-12-28 15:27:20 +00:00
def team_from_collection ( newteam_json ) :
2020-12-28 09:09:02 +00:00
# verify collection against our own restrictions
if len ( newteam_json [ " fullName " ] ) > 30 :
raise CommandError ( " Team names have to be less than 30 characters! Try again. " )
if len ( newteam_json [ " slogan " ] ) > 100 :
raise CommandError ( " We ' ve given you 100 characters for the slogan. Discord puts limits on us and thus, we put limits on you. C ' est la vie. " )
if len ( newteam_json [ " lineup " ] ) > 20 :
raise CommandError ( " 20 players in the lineup, maximum. We ' re being really generous here. " )
2021-01-05 17:15:54 +00:00
if len ( newteam_json [ " rotation " ] ) > 8 :
2021-01-04 04:18:45 +00:00
raise CommandError ( " 8 pitchers on the rotation, max. That ' s a *lot* of pitchers. " )
2020-12-28 09:09:02 +00:00
for player in newteam_json [ " lineup " ] + newteam_json [ " rotation " ] :
if len ( player [ " name " ] ) > 70 :
raise CommandError ( f " { player [ ' name ' ] } is too long, chief. 70 or less. " )
#actually build the team
newteam = games . team ( )
newteam . name = newteam_json [ " fullName " ]
newteam . slogan = newteam_json [ " slogan " ]
for player in newteam_json [ " lineup " ] :
newteam . add_lineup ( games . player ( json . dumps ( player ) ) )
2021-01-05 17:09:41 +00:00
for player in newteam_json [ " rotation " ] :
newteam . add_pitcher ( games . player ( json . dumps ( player ) ) )
2020-12-28 09:09:02 +00:00
return newteam
2020-12-20 12:25:25 +00:00
2020-12-28 09:09:02 +00:00
def team_from_message ( command ) :
2020-12-24 09:51:38 +00:00
newteam = games . team ( )
roster = command . split ( " \n " , 1 ) [ 1 ] . split ( " \n " )
2021-01-05 22:28:45 +00:00
newteam . name = roster [ 0 ] . strip ( ) #first line is team name
newteam . slogan = roster [ 1 ] . strip ( ) #second line is slogan
2021-01-02 06:10:52 +00:00
if not roster [ 2 ] . strip ( ) == " " :
raise CommandError ( " The third line should be blank. It wasn ' t, so just in case, we ' ve not done anything on our end. " )
pitchernum = len ( roster ) - 2
for rosternum in range ( 3 , len ( roster ) - 1 ) :
2020-12-24 09:51:38 +00:00
if roster [ rosternum ] != " " :
2020-12-26 05:27:14 +00:00
if len ( roster [ rosternum ] ) > 70 :
2020-12-28 09:09:02 +00:00
raise CommandError ( f " { roster [ rosternum ] } is too long, chief. 70 or less. " )
2020-12-26 23:12:38 +00:00
newteam . add_lineup ( games . player ( ono . get_stats ( roster [ rosternum ] . rstrip ( ) ) ) )
2021-01-02 06:10:52 +00:00
else :
pitchernum = rosternum + 1
break
for rosternum in range ( pitchernum , len ( roster ) ) :
if len ( roster [ rosternum ] ) > 70 :
raise CommandError ( f " { roster [ len ( roster ) - 1 ] } is too long, chief. 70 or less. " )
newteam . add_pitcher ( games . player ( ono . get_stats ( roster [ rosternum ] . rstrip ( ) ) ) )
2020-12-21 05:23:02 +00:00
2020-12-24 09:51:38 +00:00
if len ( newteam . name ) > 30 :
2020-12-28 09:09:02 +00:00
raise CommandError ( " Team names have to be less than 30 characters! Try again. " )
2020-12-24 09:51:38 +00:00
elif len ( newteam . slogan ) > 100 :
2020-12-28 09:09:02 +00:00
raise CommandError ( " We ' ve given you 100 characters for the slogan. Discord puts limits on us and thus, we put limits on you. C ' est la vie. " )
2020-12-24 09:51:38 +00:00
2020-12-28 09:09:02 +00:00
return newteam
async def save_team_confirm ( message , newteam ) :
2020-12-24 09:51:38 +00:00
await message . channel . send ( embed = build_team_embed ( newteam ) )
checkmsg = await message . channel . send ( " Does this look good to you, boss? " )
await checkmsg . add_reaction ( " 👍 " )
await checkmsg . add_reaction ( " 👎 " )
2023-03-31 07:02:37 +00:00
def react_check ( payload ) :
return payload . user_id == message . author . id and payload . message_id == checkmsg . id
2020-12-24 09:51:38 +00:00
try :
2023-03-31 07:02:37 +00:00
payload = await client . wait_for ( ' raw_reaction_add ' , timeout = 20.0 , check = react_check )
if payload . emoji . name == " 👍 " :
2020-12-24 09:51:38 +00:00
await message . channel . send ( " You got it, chief. Saving now. " )
2020-12-27 02:53:46 +00:00
games . save_team ( newteam , message . author . id )
2020-12-24 09:51:38 +00:00
await message . channel . send ( " Saved! Thank you for flying Air Matteo. We hope you had a pleasant data entry. " )
return
2023-03-31 07:02:37 +00:00
elif payload . emoji . name == " 👎 " :
2020-12-24 09:51:38 +00:00
await message . channel . send ( " Message received. Pumping brakes, turning this car around. Try again, chief. " )
return
except asyncio . TimeoutError :
2020-12-27 22:22:11 +00:00
await message . channel . send ( " Look, we don ' t have all day. 20 seconds is long enough, right? Try again. " )
2020-12-24 09:51:38 +00:00
return
2020-12-26 09:46:42 +00:00
2020-12-26 10:09:49 +00:00
async def team_pages ( msg , all_teams , search_term = None ) :
2020-12-26 09:46:42 +00:00
pages = [ ]
page_max = math . ceil ( len ( all_teams ) / 25 )
2020-12-26 10:09:49 +00:00
if search_term is not None :
2020-12-26 10:23:01 +00:00
title_text = f " All teams matching \" { search_term } \" : "
2020-12-26 10:09:49 +00:00
else :
title_text = " All Teams "
2020-12-26 09:46:42 +00:00
for page in range ( 0 , page_max ) :
2020-12-26 10:09:49 +00:00
embed = discord . Embed ( color = discord . Color . purple ( ) , title = title_text )
2020-12-26 09:46:42 +00:00
embed . set_footer ( text = f " Page { page + 1 } of { page_max } " )
for i in range ( 0 , 25 ) :
try :
2021-01-03 22:16:05 +00:00
if all_teams [ i + 25 * page ] . slogan . strip ( ) != " " :
embed . add_field ( name = all_teams [ i + 25 * page ] . name , value = all_teams [ i + 25 * page ] . slogan )
else :
embed . add_field ( name = all_teams [ i + 25 * page ] . name , value = " 404: Slogan not found " )
2020-12-26 09:46:42 +00:00
except :
break
pages . append ( embed )
teams_list = await msg . channel . send ( embed = pages [ 0 ] )
current_page = 0
if page_max > 1 :
await teams_list . add_reaction ( " ◀ " )
await teams_list . add_reaction ( " ▶ " )
def react_check ( react , user ) :
return user == msg . author and react . message == teams_list
while True :
try :
2020-12-27 01:09:19 +00:00
react , user = await client . wait_for ( ' reaction_add ' , timeout = 60.0 , check = react_check )
2020-12-26 09:46:42 +00:00
if react . emoji == " ◀ " and current_page > 0 :
current_page - = 1
2020-12-27 08:21:48 +00:00
await react . remove ( user )
2020-12-27 01:11:05 +00:00
elif react . emoji == " ▶ " and current_page < ( page_max - 1 ) :
2020-12-26 09:46:42 +00:00
current_page + = 1
2020-12-27 08:21:48 +00:00
await react . remove ( user )
2020-12-26 09:46:42 +00:00
await teams_list . edit ( embed = pages [ current_page ] )
except asyncio . TimeoutError :
return
2020-12-31 08:32:01 +00:00
async def game_watcher ( ) :
while True :
2021-01-01 21:24:05 +00:00
try :
this_array = gamesarray . copy ( )
for i in range ( 0 , len ( this_array ) ) :
game , channel , user , key = this_array [ i ]
2021-01-15 04:39:08 +00:00
if game . over and ( ( key in main_controller . master_games_dic . keys ( ) and main_controller . master_games_dic [ key ] [ 1 ] [ " end_delay " ] < = 8 ) or not key in main_controller . master_games_dic . keys ( ) ) :
2021-01-03 11:06:51 +00:00
final_embed = game_over_embed ( game )
2021-01-05 01:23:32 +00:00
if isinstance ( user , str ) :
2021-01-04 20:24:19 +00:00
await channel . send ( f " A game started by { user } just ended. " )
elif user is not None :
2021-01-01 21:24:05 +00:00
await channel . send ( f " { user . mention } ' s game just ended. " )
else :
2021-01-03 11:06:51 +00:00
await channel . send ( " A game started from this channel just ended. " )
2021-01-01 21:24:05 +00:00
await channel . send ( embed = final_embed )
gamesarray . pop ( i )
break
except :
print ( " something broke in game_watcher " )
2021-01-03 11:06:51 +00:00
await asyncio . sleep ( 4 )
2020-12-31 08:32:01 +00:00
2021-01-03 11:06:51 +00:00
def game_over_embed ( game ) :
2021-04-18 18:39:34 +00:00
if game . inning != 2 :
title_string = f " { game . teams [ ' away ' ] . name } at { game . teams [ ' home ' ] . name } ended after { game . inning - 1 } innings "
else :
title_string = f " { game . teams [ ' away ' ] . name } at { game . teams [ ' home ' ] . name } ended after 1 inning "
2021-01-03 11:06:51 +00:00
if ( game . inning - 1 ) > game . max_innings : #if extra innings
2021-01-03 21:57:41 +00:00
title_string + = f " with { game . inning - ( game . max_innings + 1 ) } extra innings. \n "
2021-01-03 11:06:51 +00:00
else :
2021-01-03 21:57:41 +00:00
title_string + = " . \n "
2021-01-03 11:06:51 +00:00
winning_team = game . teams [ ' home ' ] . name if game . teams [ ' home ' ] . score > game . teams [ ' away ' ] . score else game . teams [ ' away ' ] . name
2021-03-02 00:39:16 +00:00
homestring = str ( game . teams [ " home " ] . score ) + ( " ☄ " if game . teams [ " home " ] . score == 16 else " " )
awaystring = ( " ☄ " if game . teams [ " away " ] . score == 16 else " " ) + str ( game . teams [ " away " ] . score )
winstring = f " { awaystring } to { homestring } \n "
2021-01-03 11:06:51 +00:00
if game . victory_lap and winning_team == game . teams [ ' home ' ] . name :
winstring + = f " { winning_team } wins with a victory lap! "
elif winning_team == game . teams [ ' home ' ] . name :
2021-03-24 21:56:50 +00:00
winstring + = f " { winning_team } wins with a partial victory lap! "
2021-01-03 11:06:51 +00:00
else :
2021-03-24 21:56:50 +00:00
winstring + = f " { winning_team } wins on the road! "
2020-12-26 09:46:42 +00:00
2021-01-03 11:06:51 +00:00
embed = discord . Embed ( color = discord . Color . dark_purple ( ) , title = title_string )
2021-01-14 01:55:12 +00:00
embed . add_field ( name = " Final score: " , value = winstring , inline = False )
embed . add_field ( name = f " { game . teams [ ' away ' ] . name } pitcher: " , value = game . teams [ ' away ' ] . pitcher . name )
embed . add_field ( name = f " { game . teams [ ' home ' ] . name } pitcher: " , value = game . teams [ ' home ' ] . pitcher . name )
embed . set_footer ( text = game . weather . emoji + game . weather . name )
2021-01-03 11:06:51 +00:00
return embed
2021-01-03 21:48:51 +00:00
def get_team_fuzzy_search ( team_name ) :
team = games . get_team ( team_name )
if team is None :
teams = games . search_team ( team_name . lower ( ) )
if len ( teams ) == 1 :
team = teams [ 0 ]
return team
2021-01-04 04:18:45 +00:00
2021-01-15 07:23:44 +00:00
async def start_league_day ( channel , league , partial = False ) :
2021-01-11 11:17:14 +00:00
current_games = [ ]
2021-01-14 00:20:58 +00:00
games_to_start = league . schedule [ str ( league . day_to_series_num ( league . day ) ) ]
2021-01-11 11:17:14 +00:00
if league . game_length is None :
game_length = games . config ( ) [ " default_length " ]
else :
game_length = league . game_length
2021-03-08 20:03:10 +00:00
weather_check_result = league . weather_event_check ( )
2021-01-11 11:17:14 +00:00
for pair in games_to_start :
if pair [ 0 ] is not None and pair [ 1 ] is not None :
2021-01-13 09:23:48 +00:00
away = get_team_fuzzy_search ( pair [ 0 ] )
2021-01-14 00:20:58 +00:00
away . set_pitcher ( rotation_slot = league . day )
2021-01-13 09:23:48 +00:00
home = get_team_fuzzy_search ( pair [ 1 ] )
2021-01-14 00:20:58 +00:00
home . set_pitcher ( rotation_slot = league . day )
2021-01-13 11:32:28 +00:00
2021-01-14 00:20:58 +00:00
this_game = games . game ( away . finalize ( ) , home . finalize ( ) , length = game_length )
2021-03-07 23:39:20 +00:00
this_game . weather = league . get_weather_now ( home . name ) ( this_game )
2021-07-06 01:26:55 +00:00
this_game , state_init = prepare_game ( this_game , league = league )
2021-01-11 11:17:14 +00:00
2021-01-15 07:23:44 +00:00
if not partial :
series_string = " Series score: "
state_init [ " title " ] = f " { series_string } 0 - 0 "
else :
state_init [ " title " ] = " Interrupted series! "
2021-01-11 11:17:14 +00:00
discrim_string = league . name
id = str ( uuid4 ( ) )
current_games . append ( ( this_game , id ) )
main_controller . master_games_dic [ id ] = ( this_game , state_init , discrim_string )
ext = " ?league= " + urllib . parse . quote_plus ( league . name )
2021-03-08 20:03:10 +00:00
if weather_check_result == 2 :
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The entire league is struck by a { league . weather_override . emoji } { league . weather_override . name } ! The games must go on. " )
2021-03-08 20:03:10 +00:00
elif weather_check_result == 1 :
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The { league . weather_override . emoji } { league . weather_override . name } continues to afflict the league. " )
2021-01-11 11:17:14 +00:00
if league . last_series_check ( ) : #if finals
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The final series of the { league . name } regular season is starting now, at { config ( ) [ ' simmadome_url ' ] + ext } " )
2021-01-11 11:17:14 +00:00
last = True
else :
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The day { league . day } series of the { league . name } is starting now, at { config ( ) [ ' simmadome_url ' ] + ext } " )
2021-01-11 11:17:14 +00:00
last = False
2021-01-15 07:23:44 +00:00
if partial :
2021-01-15 11:08:52 +00:00
missed_games = ( league . day % league . series_length ) - 1
2021-01-16 23:39:08 +00:00
if missed_games == - 1 :
missed_games = league . series_length - 1
2021-01-15 07:23:44 +00:00
await league_day_watcher ( channel , league , current_games , config ( ) [ ' simmadome_url ' ] + ext , last , missed = missed_games )
else :
await league_day_watcher ( channel , league , current_games , config ( ) [ ' simmadome_url ' ] + ext , last )
2021-01-11 11:17:14 +00:00
2021-01-15 07:23:44 +00:00
async def league_day_watcher ( channel , league , games_list , filter_url , last = False , missed = 0 ) :
2021-01-11 11:17:14 +00:00
league . active = True
2021-01-15 04:39:08 +00:00
league . autoplay - = 1
2021-01-14 05:20:43 +00:00
if league not in active_leagues :
active_leagues . append ( league )
2021-01-13 09:23:48 +00:00
series_results = { }
2021-01-11 11:17:14 +00:00
while league . active :
queued_games = [ ]
while len ( games_list ) > 0 :
2021-01-15 04:39:08 +00:00
try :
for i in range ( 0 , len ( games_list ) ) :
game , key = games_list [ i ]
if game . over and ( ( key in main_controller . master_games_dic . keys ( ) and main_controller . master_games_dic [ key ] [ 1 ] [ " end_delay " ] < = 8 ) or not key in main_controller . master_games_dic . keys ( ) ) :
if game . teams [ ' home ' ] . name not in series_results . keys ( ) :
series_results [ game . teams [ " home " ] . name ] = { }
series_results [ game . teams [ " home " ] . name ] [ " wins " ] = 0
series_results [ game . teams [ " home " ] . name ] [ " losses " ] = 0
series_results [ game . teams [ " home " ] . name ] [ " run_diff " ] = 0
if game . teams [ ' away ' ] . name not in series_results . keys ( ) :
series_results [ game . teams [ " away " ] . name ] = { }
series_results [ game . teams [ " away " ] . name ] [ " wins " ] = 0
series_results [ game . teams [ " away " ] . name ] [ " losses " ] = 0
series_results [ game . teams [ " away " ] . name ] [ " run_diff " ] = 0
winner_name = game . teams [ ' home ' ] . name if game . teams [ ' home ' ] . score > game . teams [ ' away ' ] . score else game . teams [ ' away ' ] . name
loser_name = game . teams [ ' away ' ] . name if game . teams [ ' home ' ] . score > game . teams [ ' away ' ] . score else game . teams [ ' home ' ] . name
rd = int ( math . fabs ( game . teams [ ' home ' ] . score - game . teams [ ' away ' ] . score ) )
series_results [ winner_name ] [ " wins " ] + = 1
series_results [ winner_name ] [ " run_diff " ] + = rd
winner_dic = { " wins " : 1 , " run_diff " : rd }
series_results [ loser_name ] [ " losses " ] + = 1
series_results [ loser_name ] [ " run_diff " ] - = rd
loser_dic = { " losses " : 1 , " run_diff " : - rd }
league . add_stats_from_game ( game . get_team_specific_stats ( ) )
league . update_standings ( { winner_name : winner_dic , loser_name : loser_dic } )
leagues . save_league ( league )
final_embed = game_over_embed ( game )
final_embed . add_field ( name = " Day: " , value = league . day )
final_embed . add_field ( name = " Series score: " , value = f " { series_results [ game . teams [ ' away ' ] . name ] [ ' wins ' ] } - { series_results [ game . teams [ ' home ' ] . name ] [ ' wins ' ] } " )
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " A { league . name } game just ended! " )
await league_subscriber_update ( league , channel , final_embed )
2021-01-15 07:23:44 +00:00
if series_results [ winner_name ] [ " wins " ] + series_results [ winner_name ] [ " losses " ] + missed < league . series_length :
2021-01-15 04:39:08 +00:00
queued_games . append ( game )
games_list . pop ( i )
break
except :
2021-08-24 17:42:00 +00:00
print ( " something went wrong in league_day_watcher: " + str ( sys . exc_info ( ) [ 0 ] ) + str ( sys . exc_info ( ) [ 1 ] ) + " \n " + traceback . print_tb ( sys . exc_info ( ) [ 2 ] ) )
2021-01-15 04:39:08 +00:00
await asyncio . sleep ( 2 )
2021-01-13 11:32:28 +00:00
league . day + = 1
2021-01-11 11:17:14 +00:00
if len ( queued_games ) > 0 :
now = datetime . datetime . now ( )
2021-08-15 23:52:32 +00:00
validminutes = [ 0 ] + [ int ( ( 60 * div ) / league . games_per_hour ) for div in range ( 1 , league . games_per_hour ) ]
2021-04-03 17:52:46 +00:00
delta = datetime . timedelta ( )
2021-01-11 11:17:14 +00:00
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
2021-01-15 04:39:08 +00:00
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
2021-01-11 11:17:14 +00:00
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
2021-01-14 00:20:58 +00:00
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
2021-01-11 11:17:14 +00:00
wait_seconds = ( next_start - now ) . seconds
2021-06-27 20:19:40 +00:00
2021-08-15 23:52:32 +00:00
if wait_seconds > 3600 : #there's never a situation to wait longer than an hour so hoo ray bugfixes the easy way
2021-06-27 20:19:40 +00:00
wait_seconds = 60
2021-01-11 11:17:14 +00:00
2021-01-14 00:20:58 +00:00
leagues . save_league ( league )
2021-01-23 06:43:49 +00:00
active_standings [ league ] = await channel . send ( embed = league . standings_embed ( ) )
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The day { league . day } games for the { league . name } will start in { math . ceil ( wait_seconds / 60 ) } minutes. " )
2021-03-08 20:03:10 +00:00
weather_check_result = league . weather_event_check ( )
if weather_check_result == 2 :
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The entire league is struck by a { league . weather_override . emoji } { league . weather_override . name } ! The games must go on. " )
2021-03-08 20:03:10 +00:00
elif weather_check_result == 1 :
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The { league . weather_override . emoji } { league . weather_override . name } continues to afflict the league. " )
2021-01-11 11:17:14 +00:00
await asyncio . sleep ( wait_seconds )
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " A { league . name } series is continuing now at { filter_url } " )
2021-01-15 10:54:06 +00:00
games_list = await continue_league_series ( league , queued_games , games_list , series_results , missed )
2021-01-11 11:17:14 +00:00
else :
league . active = False
2021-02-24 21:19:11 +00:00
if league . autoplay == 0 or config ( ) [ " game_freeze " ] : #if number of series to autoplay has been reached
2021-03-28 18:00:38 +00:00
active_standings [ league ] = await league_subscriber_update ( league , channel , league . standings_embed ( ) )
await league_subscriber_update ( league , channel , f " The { league . name } is no longer autoplaying. " )
2021-01-16 03:00:46 +00:00
if config ( ) [ " game_freeze " ] :
await channel . send ( " Patch incoming. " )
leagues . save_league ( league )
active_leagues . pop ( active_leagues . index ( league ) )
return
2021-01-13 09:23:48 +00:00
2021-01-15 07:12:30 +00:00
if last : #if last game of the season
2021-01-15 08:18:41 +00:00
now = datetime . datetime . now ( )
validminutes = [ int ( ( 60 * div ) / league . games_per_hour ) for div in range ( 0 , league . games_per_hour ) ]
2021-04-03 17:52:46 +00:00
delta = datetime . timedelta ( )
2021-01-15 08:18:41 +00:00
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
2021-01-15 09:31:31 +00:00
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
2021-01-15 08:18:41 +00:00
wait_seconds = ( next_start - now ) . seconds
2021-03-28 18:41:30 +00:00
await league_subscriber_update ( league , channel , f " This { league . name } season is now over! The postseason (with any necessary tiebreakers) will be starting in { math . ceil ( wait_seconds / 60 ) } minutes. (unless you skipped it, that is.) " )
2021-01-15 08:18:41 +00:00
await asyncio . sleep ( wait_seconds )
2021-01-15 07:23:44 +00:00
await league_postseason ( channel , league )
#need to reset league to new season here
2021-01-15 07:12:30 +00:00
return
2021-01-13 09:23:48 +00:00
2021-01-14 00:20:58 +00:00
2021-01-15 07:12:30 +00:00
2021-01-16 03:00:46 +00:00
2021-01-11 11:17:14 +00:00
2021-01-15 07:12:30 +00:00
2021-01-11 11:17:14 +00:00
now = datetime . datetime . now ( )
validminutes = [ int ( ( 60 * div ) / league . games_per_hour ) for div in range ( 0 , league . games_per_hour ) ]
2021-04-03 17:52:46 +00:00
delta = datetime . timedelta ( )
2021-01-11 11:17:14 +00:00
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
2021-01-15 04:39:08 +00:00
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
2021-01-11 11:17:14 +00:00
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
2021-01-15 09:31:31 +00:00
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
2021-01-11 11:17:14 +00:00
wait_seconds = ( next_start - now ) . seconds
2021-01-14 00:20:58 +00:00
leagues . save_league ( league )
2021-03-28 18:00:38 +00:00
active_standings [ league ] = await league_subscriber_update ( league , channel , league . standings_embed ( ) )
await league_subscriber_update ( league , channel , f """ This { league . name } series is now complete! The next series will be starting in { int ( wait_seconds / 60 ) } minutes. """ )
2021-01-11 11:17:14 +00:00
await asyncio . sleep ( wait_seconds )
2021-01-15 04:39:08 +00:00
await start_league_day ( channel , league )
2021-01-13 09:23:48 +00:00
2021-01-15 10:54:06 +00:00
async def continue_league_series ( league , queue , games_list , series_results , missed ) :
2021-01-11 11:17:14 +00:00
for oldgame in queue :
away_team = games . get_team ( oldgame . teams [ " away " ] . name )
2021-01-13 11:32:28 +00:00
away_team . set_pitcher ( rotation_slot = league . day )
2021-01-11 11:17:14 +00:00
home_team = games . get_team ( oldgame . teams [ " home " ] . name )
2021-01-13 11:32:28 +00:00
home_team . set_pitcher ( rotation_slot = league . day )
this_game = games . game ( away_team . finalize ( ) , home_team . finalize ( ) , length = league . game_length )
2021-03-07 23:39:20 +00:00
this_game . weather = league . get_weather_now ( home_team . name ) ( this_game )
2021-07-06 01:26:55 +00:00
this_game , state_init = prepare_game ( this_game , league = league )
2021-01-11 11:17:14 +00:00
series_string = f " Series score: "
2021-01-15 10:54:06 +00:00
if missed < = 0 :
series_string = " Series score: "
state_init [ " title " ] = f " { series_string } { series_results [ away_team . name ] [ ' wins ' ] } - { series_results [ home_team . name ] [ ' wins ' ] } "
else :
state_init [ " title " ] = " Interrupted series! "
2021-01-11 11:17:14 +00:00
discrim_string = league . name
id = str ( uuid4 ( ) )
games_list . append ( ( this_game , id ) )
main_controller . master_games_dic [ id ] = ( this_game , state_init , discrim_string )
return games_list
2021-01-15 07:23:44 +00:00
async def league_postseason ( channel , league ) :
embed = league . standings_embed ( )
embed . set_footer ( text = " Final Standings " )
await channel . send ( embed = embed )
2021-03-28 17:14:34 +00:00
if league . postseason :
tiebreakers = league . tiebreaker_required ( )
if tiebreakers != [ ] :
await channel . send ( " Tiebreakers required! " )
await asyncio . gather ( * [ start_tournament_round ( channel , tourney ) for tourney in tiebreakers ] )
for tourney in tiebreakers :
league . update_standings ( { tourney . winner . name : { " wins " : 1 } } )
leagues . save_league ( league )
now = datetime . datetime . now ( )
2021-01-15 07:23:44 +00:00
2021-03-28 17:14:34 +00:00
validminutes = [ int ( ( 60 * div ) / league . games_per_hour ) for div in range ( 0 , league . games_per_hour ) ]
2021-04-03 17:52:46 +00:00
delta = datetime . timedelta ( )
2021-03-28 17:14:34 +00:00
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
wait_seconds = ( next_start - now ) . seconds
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " Tiebreakers complete! Postseason starting in { math . ceil ( wait_seconds / 60 ) } minutes. " )
2021-03-28 17:14:34 +00:00
await asyncio . sleep ( wait_seconds )
tourneys = league . champ_series ( )
await asyncio . gather ( * [ start_tournament_round ( channel , tourney ) for tourney in tourneys ] )
champs = { }
for tourney in tourneys :
for team in tourney . teams . keys ( ) :
if team . name == tourney . winner . name :
champs [ tourney . winner ] = { " wins " : tourney . teams [ team ] [ " wins " ] }
world_series = leagues . tournament ( f " { league . name } Championship Series " , champs , series_length = 7 , secs_between_games = int ( 3600 / league . games_per_hour ) , secs_between_rounds = int ( 7200 / league . games_per_hour ) )
world_series . build_bracket ( by_wins = True )
world_series . league = league
2021-01-15 08:18:41 +00:00
now = datetime . datetime . now ( )
validminutes = [ int ( ( 60 * div ) / league . games_per_hour ) for div in range ( 0 , league . games_per_hour ) ]
2021-04-03 17:52:46 +00:00
delta = datetime . timedelta ( )
2021-01-15 08:18:41 +00:00
for i in range ( 0 , len ( validminutes ) ) :
if now . minute > validminutes [ i ] :
if i < = len ( validminutes ) - 3 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 2 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
elif i < = len ( validminutes ) - 2 :
if validminutes [ i + 1 ] == now . minute :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( validminutes [ i + 1 ] - now . minute ) )
else :
delta = datetime . timedelta ( minutes = ( 60 - now . minute ) )
2021-01-15 09:31:31 +00:00
next_start = ( now + delta ) . replace ( second = 0 , microsecond = 0 )
2021-01-15 08:18:41 +00:00
wait_seconds = ( next_start - now ) . seconds
2021-03-28 18:00:38 +00:00
await league_subscriber_update ( league , channel , f " The { league . name } Championship Series is starting in { math . ceil ( wait_seconds / 60 ) } minutes! " )
2021-01-15 08:18:41 +00:00
await asyncio . sleep ( wait_seconds )
2021-03-28 17:14:34 +00:00
await start_tournament_round ( channel , world_series )
league . champion = world_series . winner . name
2021-01-15 08:18:41 +00:00
2021-01-15 10:44:59 +00:00
leagues . save_league ( league )
2021-01-16 01:15:52 +00:00
season_save ( league )
league . season_reset ( )
2021-01-11 11:17:14 +00:00
2021-03-28 18:00:38 +00:00
async def league_subscriber_update ( league , start_channel , message ) :
2021-03-28 18:41:30 +00:00
channel_list = list ( filter ( lambda chan : chan . id in league . subbed_channels , client . get_all_channels ( ) ) )
2021-03-28 18:00:38 +00:00
channel_list . append ( start_channel )
for channel in channel_list :
if isinstance ( message , discord . Embed ) :
await channel . send ( embed = message )
else :
await channel . send ( message )
2022-06-16 01:59:51 +00:00
client . run ( config ( ) [ " token " ] )