paper-dynasty-discord/utilities/buttons.py
2024-12-24 00:22:30 -06:00

365 lines
14 KiB
Python

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)