""" Discord confirmation and interaction UI components. Contains Question, Confirm, and ButtonOptions classes for user interactions. """ import asyncio import discord from typing import Literal class Question: def __init__(self, bot, channel, prompt, qtype, timeout, embed=None): """ Version 0.4 :param bot, discord bot object :param channel, discord Channel object :param prompt, string, prompt message to post :param qtype, string, 'yesno', 'int', 'float', 'text', or 'url' :param timeout, float, time to wait for a response """ if not prompt and not embed: raise TypeError('prompt and embed may not both be None') self.bot = bot self.channel = channel self.prompt = prompt self.qtype = qtype self.timeout = timeout self.embed = embed async def ask(self, responders: list): """ Params: responder, list of discord User objects Returns: True/False if confirm question; full response otherwise """ yes = ['yes', 'y', 'ye', 'yee', 'yerp', 'yep', 'yeet', 'yip', 'yup', 'yarp', 'si'] no = ['no', 'n', 'nope', 'nah', 'nyet', 'nein'] if type(responders) is not list: raise TypeError('Responders must be a list of Members') def yesno(mes): return mes.channel == self.channel and mes.author in responders and mes.content.lower() in yes + no def text(mes): return mes.channel == self.channel and mes.author in responders await self.channel.send(content=self.prompt, embed=self.embed) try: resp = await self.bot.wait_for( 'message', timeout=self.timeout, check=yesno if self.qtype == 'yesno' else text ) except asyncio.TimeoutError: return None except Exception as e: await self.channel.send(f'Yuck, do you know what this means?\n\n{e}') return None if self.qtype == 'yesno': if resp.content.lower() in yes: return True else: return False elif self.qtype == 'int': return int(resp.content) elif self.qtype == 'float': return float(resp.content) elif self.qtype == 'url': if resp.content[:7] == 'http://' or resp.content[:8] == 'https://': return resp.content else: return False else: return resp.content class Confirm(discord.ui.View): def __init__(self, responders: list, timeout: float = 300.0, label_type: Literal['yes', 'confirm'] = None): 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: return 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: return self.value = False self.clear_items() self.stop() class ButtonOptions(discord.ui.View): def __init__(self, responders: list, timeout: float = 300.0, labels=None): super().__init__(timeout=timeout) if not isinstance(responders, list): raise TypeError('responders must be a list') self.value = None self.responders = responders self.options = labels 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) @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: return self.value = self.options[0] self.clear_items() self.stop() @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: return self.value = self.options[1] self.clear_items() self.stop() @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: return self.value = self.options[2] self.clear_items() self.stop() @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: return self.value = self.options[3] self.clear_items() self.stop() @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: return self.value = self.options[4] self.clear_items() self.stop()