import logging import discord from sqlmodel import Session, select from typing import Literal from exceptions import * from in_game.game_helpers import legal_check from in_game.gameplay_models import Game, Lineup, Team, Play from in_game.gameplay_queries import get_card_or_none, get_channel_game_or_none, get_one_lineup, get_team_or_none, get_players_last_pa from utilities.buttons import ButtonOptions, Confirm from utilities.embeds import image_embed from utilities.pages import Pagination def complete_play(session:Session, this_play: Play): this_play.locked = False this_play.complete = True session.add(this_play) session.commit() async def get_lineups_from_sheets(session: Session, sheets, this_game: Game, this_team: Team, lineup_num: int, roster_num: int) -> list[Lineup]: logging.debug(f'sheets: {sheets}') this_sheet = sheets.open_by_key(this_team.gsheet) logging.debug(f'this_sheet: {this_sheet}') r_sheet = this_sheet.worksheet_by_title('My Rosters') logging.debug(f'r_sheet: {r_sheet}') if lineup_num == 1: row_start = 9 row_end = 17 else: row_start = 18 row_end = 26 if roster_num == 1: l_range = f'H{row_start}:I{row_end}' elif roster_num == 2: l_range = f'J{row_start}:K{row_end}' else: l_range = f'L{row_start}:M{row_end}' logging.debug(f'l_range: {l_range}') raw_cells = r_sheet.range(l_range) logging.debug(f'raw_cells: {raw_cells}') try: lineup_cells = [(row[0].value, int(row[1].value)) for row in raw_cells] logging.debug(f'lineup_cells: {lineup_cells}') except ValueError as e: logging.error(f'Could not pull roster for {this_team.abbrev}: {e}') raise ValueError(f'Uh oh. Looks like your roster might not be saved. I am reading blanks when I try to get the card IDs') all_lineups = [] all_pos = [] card_ids = [] for index, row in enumerate(lineup_cells): if '' in row: break if row[0].upper() not in all_pos: all_pos.append(row[0].upper()) else: raise SyntaxError(f'You have more than one {row[0].upper()} in this lineup. Please update and set the lineup again.') this_card = await get_card_or_none(session, card_id=int(row[1])) if this_card is None: raise LookupError( f'Your {row[0].upper()} has a Card ID of {int(row[1])} and I cannot find that card. Did you sell it by chance? Or maybe you sold a duplicate and the bot sold the one you were using?' ) if this_card.team_id != this_team.id: raise SyntaxError(f'Easy there, champ. Looks like card ID {row[1]} belongs to the {this_card.team.lname}. Try again with only cards you own.') card_id = row[1] card_ids.append(str(card_id)) this_lineup = Lineup( position=row[0].upper(), batting_order=index + 1, game=this_game, team=this_team, player=this_card.player, card=this_card ) all_lineups.append(this_lineup) legal_data = await legal_check([card_ids], difficulty_name=this_game.game_type) logging.debug(f'legal_data: {legal_data}') if not legal_data['legal']: raise CardLegalityException(f'The following cards appear to be illegal for this game mode:\n{legal_data["error_string"]}') if len(all_lineups) != 9: raise Exception(f'I was only able to pull in {len(all_lineups)} batters from Sheets. Please check your saved lineup and try again.') return all_lineups async def checks_log_interaction(session: Session, interaction: discord.Interaction, command_name: str) -> tuple[Game, Team, Play]: await interaction.response.defer(thinking=True) this_game = get_channel_game_or_none(session, interaction.channel_id) if this_game is None: raise GameNotFoundException('I don\'t see an active game in this channel.') owner_team = await get_team_or_none(session, gm_id=interaction.user.id) if owner_team is None: logging.exception(f'{command_name} command: No team found for GM ID {interaction.user.id}') raise TeamNotFoundException(f'Do I know you? I cannot find your team.') if 'gauntlet' in this_game.game_type: gauntlet_abbrev = f'Gauntlet-{owner_team.abbrev}' owner_team = await get_team_or_none(session, team_abbrev=gauntlet_abbrev) if owner_team is None: logging.exception(f'{command_name} command: No gauntlet team found with abbrev {gauntlet_abbrev}') raise TeamNotFoundException(f'Hm, I was not able to find a gauntlet team for you.') if not owner_team.id in [this_game.away_team_id, this_game.home_team_id]: logging.exception(f'{interaction.user.display_name} tried to run a command in Game {this_game.id} when they aren\'t a GM in the game.') raise TeamNotFoundException('Bruh. Only GMs of the active teams can log plays.') this_play = this_game.current_play_or_none(session) if this_play is None: logging.error(f'{command_name} command: No play found for Game ID {this_game.id} - attempting to initialize play') this_play = this_game.initialize_play(session) return this_game, owner_team, this_play def log_run_scored(session: Session, runner: Lineup, is_earned: bool = True): last_ab = get_players_last_pa(session, lineup_member=runner) last_ab.run = 1 last_ab.e_run = 1 if is_earned else 0 session.add(last_ab) session.commit() return True def advance_runners(session: Session, this_play: Play, num_bases: int, is_error: bool = False, only_forced: bool = False): this_play.rbi = 0 if only_forced: if not this_play.on_first: if this_play.on_second: this_play.on_second_final = 2 if this_play.on_third: this_play.on_third_final = 3 return if this_play.on_second: if this_play.on_third: if num_bases > 0: this_play.on_third_final = 4 log_run_scored(session, this_play.on_third) this_play.rbi += 1 if not is_error else 0 if num_bases > 1: this_play.on_second_final = 4 log_run_scored(session, this_play.on_second) this_play.rbi += 1 if not is_error else 0 elif num_bases == 1: this_play.on_second_final = 3 else: this_play.on_second_final = 2 else: if this_play.on_third: this_play.on_third_final = 3 if num_bases > 2: this_play.on_first_final = 4 log_run_scored(session, this_play.on_first) this_play.rbi += 1 if not is_error else 0 elif num_bases == 2: this_play.on_first_final = 3 elif num_bases == 1: this_play.on_first_final = 2 else: this_play.on_first_final = 1 else: if this_play.on_third: if num_bases > 0: this_play.on_third_final = 4 log_run_scored(session, this_play.on_third) this_play.rbi += 1 if not is_error else 0 else: this_play.on_third_final = 3 if this_play.on_second: if num_bases > 1: this_play.on_second_final = 4 log_run_scored(session, this_play.on_second) this_play.rbi += 1 if not is_error else 0 elif num_bases == 1: this_play.on_second_final = 3 else: this_play.on_second_final = 2 if this_play.on_first: if num_bases > 2: this_play.on_first_final = 4 log_run_scored(session, this_play.on_first) this_play.rbi += 1 if not is_error else 0 elif num_bases == 2: this_play.on_first_final = 3 elif num_bases == 1: this_play.on_first_final = 2 else: this_play.on_first_final = 1 if num_bases == 4: this_play.batter_final = 4 this_play.rbi += 1 this_play.run = 1 async def show_outfield_cards(session: Session, interaction: discord.Interaction, this_play: Play): lf = get_one_lineup(session, this_game=this_play.game, this_team=this_play.pitcher.team, position='LF') cf = get_one_lineup(session, this_game=this_play.game, this_team=this_play.pitcher.team, position='CF') rf = get_one_lineup(session, this_game=this_play.game, this_team=this_play.pitcher.team, position='RF') this_team = this_play.pitcher.team logging.debug(f'lf: {lf.player.name_with_desc}\n\ncf: {cf.player.name_with_desc}\n\nrf: {rf.player.name_with_desc}\n\nteam: {this_team.lname}') view = Pagination([interaction.user], timeout=10) view.left_button.label = f'Left Fielder' view.left_button.style = discord.ButtonStyle.secondary lf_embed = image_embed( image_url=lf.player.image, title=f'{this_team.sname} LF', color=this_team.color, desc=lf.player.name, author_name=this_team.lname, author_icon=this_team.logo ) view.cancel_button.label = f'Center Fielder' view.cancel_button.style = discord.ButtonStyle.blurple cf_embed = image_embed( image_url=cf.player.image, title=f'{this_team.sname} CF', color=this_team.color, desc=cf.player.name, author_name=this_team.lname, author_icon=this_team.logo ) view.right_button.label = f'Right Fielder' view.right_button.style = discord.ButtonStyle.secondary rf_embed = image_embed( image_url=rf.player.image, title=f'{this_team.sname} RF', color=this_team.color, desc=rf.player.name, author_name=this_team.lname, author_icon=this_team.logo ) page_num = 1 embeds = [lf_embed, cf_embed, rf_embed] msg = await interaction.channel.send(embed=embeds[page_num], view=view) await view.wait() if view.value: if view.value == 'left': page_num = 0 if view.value == 'cancel': page_num = 1 if view.value == 'right': page_num = 2 else: await msg.edit(content=None, embed=embeds[page_num], view=None) view.value = None if page_num == 0: view.left_button.style = discord.ButtonStyle.blurple view.cancel_button.style = discord.ButtonStyle.secondary view.right_button.style = discord.ButtonStyle.secondary if page_num == 1: view.left_button.style = discord.ButtonStyle.secondary view.cancel_button.style = discord.ButtonStyle.blurple view.right_button.style = discord.ButtonStyle.secondary if page_num == 2: view.left_button.style = discord.ButtonStyle.secondary view.cancel_button.style = discord.ButtonStyle.secondary view.right_button.style = discord.ButtonStyle.blurple view.left_button.disabled = True view.cancel_button.disabled = True view.right_button.disabled = True await msg.edit(content=None, embed=embeds[page_num], view=view) return [lf, cf, rf][page_num] async def flyballs(session: Session, interaction: discord.Interaction, this_game: Game, this_play: Play, flyball_type: Literal['a', 'ballpark', 'b', 'b?', 'c'], comp_play: bool = True) -> Play: num_outs = 1 if flyball_type == 'a': this_play.pa = 1 this_play.ab = 1 this_play.outs = 1 if this_play.starting_outs < 2: advance_runners(session, this_play, num_bases=1) if this_play.on_third: this_play.ab = 0 session.add(this_play) session.commit() elif flyball_type == 'b' or flyball_type == 'ballpark': this_play.pa, this_play.ab, this_play.outs = 1, 1, 1 this_play.bpfo = 1 if flyball_type == 'ballpark' else 0 advance_runners(session, this_play, num_bases=0) if this_play.starting_outs < 2 and this_play.on_third: this_play.ab = 0 this_play.rbi = 1 this_play.on_third_final = 4 log_run_scored(session, this_play.on_third) if this_play.starting_outs < 2 and this_play.on_second: logging.debug(f'calling of embed') await show_outfield_cards(session, interaction, this_play) logging.debug(f'done with of embed') runner = this_play.on_second.player view = Confirm(responders=[interaction.user], timeout=60, label_type='yes') if this_play.ai_is_batting: tag_resp = this_play.managerai.tag_from_second(session, this_game) q_text = f'{runner.name} will attempt to advance to third if the safe range is **{tag_resp.min_safe}+**, are they going?' else: q_text = f'Is {runner.name} attempting to tag up to third?' question = await interaction.channel.send( content=q_text, view=view ) await view.wait() if view.value: await question.delete() view = ButtonOptions( responders=[interaction.user], timeout=60, labels=['Tagged Up', 'Hold at 2nd', 'Out at 3rd', None, None] ) question = await interaction.channel.send( f'What was the result of {runner.name} tagging from second?', view=view ) await view.wait() if view.value: await question.delete() if view.value == 'Tagged Up': this_play.on_second_final = 3 elif view.value == 'Out at 3rd': num_outs += 1 this_play.on_second_final = None this_play.outs = num_outs else: await question.delete() else: await question.delete() session.add(this_play) session.commit() elif flyball_type == 'b?': this_play.pa, this_play.ab, this_play.outs = 1, 1, 1 if this_play.starting_outs < 2 and this_play.on_third: logging.debug(f'calling of embed') await show_outfield_cards(interaction, this_play) logging.debug(f'done with of embed') runner = this_play.on_second.player view = Confirm(responders=[interaction.user], timeout=60, label_type='yes') if this_play.ai_is_batting: tag_resp = this_play.managerai.tag_from_second(session, this_game) q_text = f'{runner.name} will attempt to advance home if the safe range is **{tag_resp.min_safe}+**, are they going?' else: q_text = f'Is {runner.name} attempting to tag up and go home?' question = await interaction.channel.send( content=q_text, view=view ) await view.wait() if view.value: await question.delete() view = Confirm(responders=[interaction.user], timeout=60, label_type='yes') question = await interaction.channel.send( f'Was {runner.name} thrown out?', view=view ) await view.wait() if view.value: await question.delete() num_outs += 1 this_play.on_third_final = 99 this_play.outs = num_outs else: await question.delete() this_play.ab = 0 this_play.rbi = 1 this_play.on_third_final = 4 log_run_scored(session, this_play.on_third) else: await question.delete() elif flyball_type == 'c': patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1) advance_runners(this_play.id, num_bases=0) if comp_play: complete_play(this_play.id) session.refresh(this_play) return this_play