import logging import discord from typing import Coroutine, Literal # from dice import ab_roll, jump_roll # from exceptions import * # from in_game.gameplay_models import Game, Play, Team logger = logging.getLogger('discord_app') class Confirm(discord.ui.View): def __init__(self, responders: list, timeout: float = 300.0, label_type: Literal['yes', 'confirm'] = 'confirm'): super().__init__(timeout=timeout) if not isinstance(responders, list): raise TypeError('responders must be a list') self.value = None self.responders = responders if label_type == 'yes': self.confirm.label = 'Yes' self.cancel.label = 'No' # When the confirm button is pressed, set the inner value to `True` and # stop the View from listening to more input. # We also send the user an ephemeral message that we're confirming their choice. @discord.ui.button(label='Confirm', style=discord.ButtonStyle.green) async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.value = True self.clear_items() self.stop() # This one is similar to the confirmation button except sets the inner value to `False` @discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey) async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.value = False self.clear_items() self.stop() class ButtonOptions(discord.ui.View): def __init__(self, labels: list[str], responders: list, timeout: float = 300.0, disable_chosen: bool = False): logger.info(f'ButtonOptions - labels: {labels} / responders: {responders} / timeout: {timeout} / disable_chosen: {disable_chosen}') super().__init__(timeout=timeout) if not isinstance(responders, list): raise TypeError('responders must be a list') if len(labels) > 5 or len(labels) < 1: log_exception(ValueError, 'ButtonOptions support between 1 and 5 options') self.value = None self.responders = responders self.options = labels self.disable_chosen = disable_chosen # if len(labels) == 5: # for count, x in enumerate(labels): # if count == 0: # self.option1.label = x # if x is None or x.lower() == 'na' or x == 'N/A': # self.remove_item(self.option1) # if count == 1: # self.option2.label = x # if x is None or x.lower() == 'na' or x == 'N/A': # self.remove_item(self.option2) # if count == 2: # self.option3.label = x # if x is None or x.lower() == 'na' or x == 'N/A': # self.remove_item(self.option3) # if count == 3: # self.option4.label = x # if x is None or x.lower() == 'na' or x == 'N/A': # self.remove_item(self.option4) # if count == 4: # self.option5.label = x # if x is None or x.lower() == 'na' or x == 'N/A': # self.remove_item(self.option5) # else: all_options = [self.option1, self.option2, self.option3, self.option4, self.option5] logger.info(f'all_options: {all_options}') for count, x in enumerate(labels): if x is None or x.lower() == 'na' or x.lower() == 'n/a': self.remove_item(all_options[count]) else: all_options[count].label = x if len(labels) < 2: self.remove_item(self.option2) if len(labels) < 3: self.remove_item(self.option3) if len(labels) < 4: self.remove_item(self.option4) if len(labels) < 5: self.remove_item(self.option5) @discord.ui.button(label='Option 1', style=discord.ButtonStyle.primary) async def option1(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.stop() if self.disable_chosen: button.disabled = True self.value = self.options[0] await interaction.edit_original_response(view=self) @discord.ui.button(label='Option 2', style=discord.ButtonStyle.primary) async def option2(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.stop() if self.disable_chosen: button.disabled = True self.value = self.options[1] await interaction.edit_original_response(view=self) @discord.ui.button(label='Option 3', style=discord.ButtonStyle.primary) async def option3(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.stop() if self.disable_chosen: button.disabled = True self.value = self.options[2] await interaction.edit_original_response(view=self) @discord.ui.button(label='Option 4', style=discord.ButtonStyle.primary) async def option4(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.stop() if self.disable_chosen: button.disabled = True self.value = self.options[3] await interaction.edit_original_response(view=self) @discord.ui.button(label='Option 5', style=discord.ButtonStyle.primary) async def option5(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user not in self.responders: await interaction.response.send_message( content='Get out of here', ephemeral=True, delete_after=10.0 ) self.stop() if self.disable_chosen: button.disabled = True self.value = self.options[4] await interaction.edit_original_response(view=self) 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 """ try: view = Confirm(responders=[interaction.user], timeout=timeout, label_type=label_type) except AttributeError: view = Confirm(responders=[interaction.author], timeout=timeout, label_type=label_type) if custom_confirm_label: view.confirm.label = custom_confirm_label if custom_cancel_label: view.cancel.label = custom_cancel_label q_message = await interaction.channel.send(question, view=view) await view.wait() if view.value: if delete_question: await q_message.delete() else: await q_message.edit(content=question, view=None) return True else: if delete_question: await q_message.delete() else: await q_message.edit(content=question, view=None) return False async def ask_with_buttons(interaction: discord.Interaction, button_options: list[str], question: str = None, timeout: int = 60, delete_question: bool = True, embeds: list[discord.Embed] = None, delete_embeds: bool = False, edit_original_interaction: bool = False, none_okay: bool = False) -> str: """ Returns text of button pressed """ logger.info(f'ask_with_buttons - button_options: {button_options} / question: {question} / timeout: {timeout} / delete_question: {delete_question} / embeds: {embeds} / delete_embeds: {delete_embeds} / edit_original_transaction: {edit_original_interaction}') if question is None and embeds is None: log_exception(KeyError, 'At least one of question or embed must be provided') view = ButtonOptions( responders=[interaction.user], timeout=timeout, labels=button_options ) logger.info(f'view: {view}') # if edit_original_interaction: # logger.info(f'editing message') # q_message = await interaction.edit_original_response( # content=question, # view=view, # embeds=embeds # ) # logger.info(f'edited') # else: # logger.info(f'posting message') # q_message = await interaction.channel.send( # content=question, # view=view, # embeds=embeds # ) logger.info(f'posting message') q_message = await interaction.channel.send( content=question, view=view, embeds=embeds ) await view.wait() if view.value: return_val = view.value else: return_val = None if question is not None and embeds is not None: logger.info(f'checking for deletion with question and embeds') if delete_question and delete_embeds: logger.info(f'delete it all') await q_message.delete() elif delete_question: logger.info(f'delete question') await q_message.edit( content=None ) elif delete_embeds: logger.info(f'delete embeds') await q_message.edit( embeds=None ) elif return_val is None: logger.info(f'remove view') await q_message.edit( view=None ) elif (question is not None and delete_question) or (embeds is not None and delete_embeds): logger.info(f'deleting message') await q_message.delete() elif return_val is None: logger.info(f'No reponse, remove view') await q_message.edit( view=None ) if return_val is not None or none_okay: return return_val log_exception(ButtonOptionNotChosen, 'Selecting an option is mandatory') # class ScorebugButtons(discord.ui.View): # def __init__(self, play: Play, embed: discord.Embed, timeout: float = 30): # super().__init__(timeout=timeout) # self.value = None # self.batting_team = play.batter.team # self.pitching_team = play.pitcher.team # self.pitcher_card_url = play.pitcher.player.pitcher_card_url # self.batter_card_url = play.batter.player.batter_card_url # self.team = play.batter.team # self.play = play # self.had_chaos = False # self.embed = embed # if play.on_base_code == 0: # self.remove_item(self.button_jump) # async def interaction_check(self, interaction: discord.Interaction[discord.Client]) -> bool: # logger.info(f'user id: {interaction.user.id} / batting_team: {self.batting_team}') # if interaction.user.id == self.batting_team.gmid: # logger.info(f'User {interaction.user.id} rolling in Game {self.play.game.id}') # return True # elif self.batting_team.is_ai and interaction.user.id == self.pitching_team.gmid: # logger.info(f'User {interaction.user.id} rolling for AI in Game {self.play.game.id}') # return True # logger.info(f'User {interaction.user.id} rejected in Game {self.play.game.id}') # await interaction.response.send_message( # content='Get out of here', # ephemeral=True, # delete_after=5.0 # ) # return False # # async def on_timeout(self) -> Coroutine[Any, Any, None]: # # await self.interaction # @discord.ui.button(label='Roll AB', style=discord.ButtonStyle.primary) # async def button_ab(self, interaction: discord.Interaction, button: discord.ui.Button): # logger.info(f'User {interaction.user.id} rolling AB in Game {self.play.game.id}') # this_roll = ab_roll(self.team, self.play.game, allow_chaos=not self.had_chaos) # logger.info(f'this_roll: {this_roll}') # if this_roll.is_chaos: # logger.info('AB Roll Is Chaos') # self.had_chaos = True # else: # button.disabled = True # await interaction.channel.send(content=None, embeds=this_roll.embeds) # if this_roll.d_six_one > 3: # logger.info(f'ScorebugButton - updating embed card to pitcher') # self.embed.set_image(url=self.pitcher_card_url) # logger.debug(f'embed image url: {self.embed.image}') # logger.debug(f'new embed: {self.embed}') # await interaction.response.edit_message(view=self, embed=self.embed) # @discord.ui.button(label='Check Jump', style=discord.ButtonStyle.secondary) # async def button_jump(self, interaction: discord.Interaction, button: discord.ui.Button): # logger.info(f'User {interaction.user.id} rolling jump in Game {self.play.game.id}') # this_roll = jump_roll(self.team, self.play.game) # button.disabled = True # await interaction.channel.send(content=None, embeds=this_roll.embeds) # await interaction.response.edit_message(view=self)