New postion sub logic

This commit is contained in:
Cal Corum 2025-03-09 00:59:10 -06:00
parent 35a62362ce
commit a416e90db9
3 changed files with 165 additions and 47 deletions

View File

@ -6,15 +6,15 @@ logger = logging.getLogger('discord_app')
def log_errors(func): def log_errors(func):
""" """
This wrapper function will force all exceptions to be logged with executiona and stack info. This wrapper function will force all exceptions to be logged with execution and stack info.
""" """
def wrap(*args, **kwargs): def wrap(*args, **kwargs):
try: try:
result = func(*args, **kwargs) result = func(*args, **kwargs)
except Exception as e: except Exception as e:
logger.error(func.__name__)
log_exception(e) log_exception(e)
logger.info(func.__name__)
return result return result
return wrap return wrap
@ -113,3 +113,7 @@ class MissingRosterException(GameException):
class LegalityCheckNotRequired(GameException): class LegalityCheckNotRequired(GameException):
pass pass
class InvalidResponder(GameException):
pass

View File

@ -4,11 +4,19 @@ from typing import Coroutine, Literal
from dice import ab_roll, jump_roll from dice import ab_roll, jump_roll
from exceptions import * from exceptions import *
from helpers import random_insult
from in_game.gameplay_models import Game, Play, Team from in_game.gameplay_models import Game, Play, Team
logger = logging.getLogger('discord_app') logger = logging.getLogger('discord_app')
# def check_responder(func):
# def wrap(*args, **kwargs):
# try:
# result = func(*args, **kwargs)
# except Exc
class Confirm(discord.ui.View): class Confirm(discord.ui.View):
def __init__(self, responders: list, timeout: float = 300.0, label_type: Literal['yes', 'confirm'] = 'confirm'): def __init__(self, responders: list, timeout: float = 300.0, label_type: Literal['yes', 'confirm'] = 'confirm'):
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
@ -182,6 +190,107 @@ class ButtonOptions(discord.ui.View):
await interaction.edit_original_response(view=self) await interaction.edit_original_response(view=self)
class SelectPosition(discord.ui.View):
def __init__(self, *, timeout: int = 30, responders: list[discord.User]):
if not isinstance(responders, list):
raise TypeError('responders must be a list')
super().__init__(timeout=timeout)
self.value = None
self.responders = responders
async def check_responders(self, interaction: discord.Interaction):
if interaction.user not in self.responders:
await interaction.response.send_message(
content=random_insult(),
ephemeral=True,
delete_after=10.0
)
log_exception(InvalidResponder, f'{interaction.user.name} not in responders: {self.responders}')
async def button_press(self, interaction: discord.Interaction, this_position: str):
await self.check_responders(interaction)
self.stop()
self.value = this_position
await interaction.edit_original_response(view=self)
@discord.ui.button(label='LF', style=discord.ButtonStyle.blurple, row=0)
async def left_field(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='CF', style=discord.ButtonStyle.blurple, row=0)
async def center_field(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='RF', style=discord.ButtonStyle.blurple, row=0)
async def right_field(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='3B', style=discord.ButtonStyle.green, row=1)
async def third_base(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='SS', style=discord.ButtonStyle.green, row=1)
async def shortstop(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='2B', style=discord.ButtonStyle.green, row=1)
async def second_base(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='1B', style=discord.ButtonStyle.green, row=1)
async def first_base(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='C', style=discord.ButtonStyle.gray, row=2)
async def catcher(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='P', style=discord.ButtonStyle.gray, row=2)
async def pitcher(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
@discord.ui.button(label='PH/PR', style=discord.ButtonStyle.gray, row=2)
async def pinch_hitter(self, interaction: discord.Interaction, button: discord.ui.Button):
await self.button_press(interaction, button.label)
async def ask_position(interaction: discord.Interaction):
view = SelectPosition(
responders=[interaction.user],
timeout=15
)
p_message = await interaction.channel.send(
content='Please select a position',
view=view
)
await view.wait()
if view.value:
await p_message.delete()
return view.value
else:
await p_message.edit(
content='To move things along, I will set the position to PH.',
view=None
)
return 'PH'
async def ask_confirm(interaction: discord.Interaction, question: str, label_type: Literal['yes', 'confirm'] = 'confirm', timeout: int = 60, delete_question: bool = True, custom_confirm_label: str = None, custom_cancel_label: str = None, embed: discord.Embed = None, delete_embed: bool = False) -> bool: async def ask_confirm(interaction: discord.Interaction, question: str, label_type: Literal['yes', 'confirm'] = 'confirm', timeout: int = 60, delete_question: bool = True, custom_confirm_label: str = None, custom_cancel_label: str = None, embed: discord.Embed = None, delete_embed: bool = False) -> bool:
""" """
button_callbacks: keys are button values, values are async functions button_callbacks: keys are button values, values are async functions

View File

@ -14,7 +14,7 @@ from helpers import get_card_embeds, random_insult
from in_game.game_helpers import legal_check from in_game.game_helpers import legal_check
from in_game.gameplay_models import Game, Lineup, Play, Team from in_game.gameplay_models import Game, Lineup, Play, Team
from in_game.gameplay_queries import get_one_lineup, get_position, get_card_or_none from in_game.gameplay_queries import get_one_lineup, get_position, get_card_or_none
from utilities.buttons import ask_confirm from utilities.buttons import ask_confirm, ask_position
from utilities.embeds import image_embed from utilities.embeds import image_embed
@ -369,12 +369,17 @@ class SelectBatterSub(discord.ui.Select):
if same_position: if same_position:
logger.info(f'same_position is True') logger.info(f'same_position is True')
position = last_lineup.position position = last_lineup.position
pos_text = '' # pos_text = ''
view = None # view = None
else: else:
logger.info(f'same_position is False') logger.info(f'same_position is False')
position = 'PH' position = await ask_position(interaction)
pos_text = 'What position will they play?'
if position == 'PH/PR':
if this_play.batter == last_lineup:
position = 'PH'
else:
position = 'PR'
logger.info(f'Deactivating last_lineup') logger.info(f'Deactivating last_lineup')
last_lineup.active = False last_lineup.active = False
@ -409,57 +414,57 @@ class SelectBatterSub(discord.ui.Select):
# self.session.commit() # self.session.commit()
logger.info(f'Inserted {human_bat_lineup.card.player.name_with_desc} in the {self.batting_order} spot') logger.info(f'Inserted {human_bat_lineup.card.player.name_with_desc} in the {self.batting_order} spot')
this_play.batter = human_bat_lineup if this_play.batter == last_lineup:
this_play.batter_pos = position logger.info(f'Setting new sub to current play batter')
this_play.batter = human_bat_lineup
this_play.batter_pos = position
logger.info(f'Adding play to session: {this_play}') logger.info(f'Adding play to session: {this_play}')
self.session.add(this_play) self.session.add(this_play)
self.session.commit() self.session.commit()
if not same_position: # if not same_position:
pos_dict_list = { # pos_dict_list = {
'Pinch Hitter': 'PH', # 'Pinch Hitter': 'PH',
'Catcher': 'C', # 'Catcher': 'C',
'First Base': '1B', # 'First Base': '1B',
'Second Base': '2B', # 'Second Base': '2B',
'Third Base': '3B', # 'Third Base': '3B',
'Shortstop': 'SS', # 'Shortstop': 'SS',
'Left Field': 'LF', # 'Left Field': 'LF',
'Center Field': 'CF', # 'Center Field': 'CF',
'Right Field': 'RF', # 'Right Field': 'RF',
'Pinch Runner': 'PR', # 'Pinch Runner': 'PR',
'Pitcher': 'P' # 'Pitcher': 'P'
} # }
logger.info(f'Prepping sub position') # logger.info(f'Prepping sub position')
pos_text = f'What position will {human_batter_card.player.name} play?' # pos_text = f'What position will {human_batter_card.player.name} play?'
options=[ # options=[
SelectOption( # SelectOption(
label=f'{pos_name}', # label=f'{pos_name}',
value=pos_dict_list[pos_name], # value=pos_dict_list[pos_name],
default=pos_name=='Pinch Hitter' # default=pos_name=='Pinch Hitter'
) # )
for pos_name in pos_dict_list.keys() # for pos_name in pos_dict_list.keys()
] # ]
logger.info(f'options: {options}') # logger.info(f'options: {options}')
view = SelectSubPosition( # view = SelectSubPosition(
self.session, # self.session,
this_lineup=human_bat_lineup, # this_lineup=human_bat_lineup,
placeholder='Select Position', # placeholder='Select Position',
options=options, # options=options,
responders=[interaction.user] # responders=[interaction.user]
) # )
logger.info(f'view: {view}') # logger.info(f'view: {view}')
logger.info(f'SelectSubPosition view is ready') # logger.info(f'SelectSubPosition view is ready')
logger.info(f'Posting final sub message') logger.info(f'Posting final sub message')
this_content = f'{human_batter_card.player.name_with_desc} has entered in the {self.batting_order} spot. {pos_text}' this_content = f'{human_batter_card.player.name_with_desc} has entered in the {self.batting_order} spot.\n\nIf you have additional subs to make, run `/substitution` again or run `/gamestate` to see the current plate appearance.'
await interaction.edit_original_response( await interaction.edit_original_response(
content=this_content, content=this_content
view=view
) )
await interaction.channel.send(content='If you have additional subs to make, run `/substitution` again or run `/gamestate` to see the current plate appearance.')
class SelectPokemonEvolution(discord.ui.Select): class SelectPokemonEvolution(discord.ui.Select):