"""Views used for submission process.""" from typing import Optional import nextcord from nextcord.ext import commands from sqlalchemy import select from sqlalchemy.orm import Session, aliased # pylint: disable=import-error from classes.submission import Submission, SubmissionState from highscores import submission_objects from utilities.sql_data_classes import Category, CategoryGroup, Guild, engine from .verification_view import VerificationView def category_select_options(group_id: int) -> "list[nextcord.SelectOption]": """Returns list of categories for a given boss.""" options: "list[nextcord.SelectOption]" = [] with Session(engine) as session: subq = select(CategoryGroup).where(CategoryGroup.GroupId == group_id).subquery() cat_group_alias = aliased(CategoryGroup, subq) stmt = select(Category).where(Category.GroupId == cat_group_alias.GroupId) categories_list = session.scalars(stmt) for category in categories_list: options.append(nextcord.SelectOption(label=category.CategoryName, value=str(category.CategoryId))) return options ############ Items used in submission process ############ class SubmissionDropdown(nextcord.ui.Select): """Defines dropdown menu for submission""" def __init__( self, bot: commands.Bot, options: "list[nextcord.SelectOption]", ): super().__init__(placeholder="Choose submission category.", min_values=1, max_values=1, options=options) self._bot = bot async def callback(self, interaction: nextcord.Interaction): submission = submission_objects.get_user_submission(interaction.user.id) submission.category_id = self.values[0] embeds = submission.get_embeds() await interaction.message.edit( embeds=embeds, ) class BackButton(nextcord.ui.Button): """Defines view for back button.""" def __init__(self, bot: commands.Bot): super().__init__(label="Back", style=nextcord.ButtonStyle.primary, custom_id="back") self._bot = bot async def callback(self, interaction: nextcord.Interaction): submission = submission_objects.get_user_submission(interaction.user.id) state: SubmissionState = submission.submission_state state = SubmissionState(state.value - 1) submission.submission_state = state embeds = submission.get_embeds() await interaction.edit(embeds=embeds, view=SubmissionView(self._bot, interaction.user.id)) class CancelButton(nextcord.ui.Button): """Deletes message to cancel interaction..""" def __init__(self): super().__init__(label="Cancel", style=nextcord.ButtonStyle.red, custom_id="cancel") async def callback(self, interaction: nextcord.Interaction): submission_objects.remove_user_submission(interaction.user.id) await interaction.message.delete() class NextButton(nextcord.ui.Button): """Defines view for next button.""" def __init__(self, bot: commands.Bot): super().__init__(label="Next", style=nextcord.ButtonStyle.primary, custom_id="next") self._bot = bot async def callback(self, interaction: nextcord.Interaction): # check if fields have been filled out for each state submission = submission_objects.get_user_submission(interaction.user.id) state: SubmissionState = submission.submission_state if state == SubmissionState.CATEGORY and not submission.category_id: await interaction.send("Please choose a category.", ephemeral=True) elif state == SubmissionState.SCORE and not submission.score: await interaction.send("Please send a score.", ephemeral=True) elif state == SubmissionState.PROOF and not len(interaction.message.attachments) > 0: await interaction.send("Please attach picture for proof.", ephemeral=True) else: state = SubmissionState(state.value + 1) submission.submission_state = state embeds = submission.get_embeds() await interaction.edit(embeds=embeds, view=SubmissionView(self._bot, interaction.user.id)) class SubmitButton(nextcord.ui.Button): """Defines button to submit a user's score.""" def __init__(self, bot: commands.Bot): super().__init__(label="Submit", style=nextcord.ButtonStyle.green, custom_id="submit") self._bot = bot async def callback(self, interaction: nextcord.Interaction): with Session(engine) as session: stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id) verification_channel_id = session.scalar(stmt) channel = interaction.guild.get_channel(verification_channel_id) submission = submission_objects.get_user_submission(interaction.user.id) error_response = "Registered Verification channel does not exist or was never registered. \ Contact server admins." assert channel is not None, await channel.send(error_response) submission.submission_state = SubmissionState.VERIFY embeds = submission.get_embeds() await channel.send( files=[await attch.to_file() for attch in interaction.message.attachments], view=VerificationView(), embeds=embeds, content=f"Submission from <@{interaction.user.id}>.", ) await interaction.message.edit(content=f"<@{interaction.user.id}> Submitted!", view=None) submission_objects.remove_user_submission(interaction.user.id) ############ Submission Views ############ class SubmissionCreateButton(nextcord.ui.View): """Defines view for score submission button.""" def __init__(self, bot: commands.Bot): super().__init__(timeout=None) self._bot = bot @nextcord.ui.button(label="Submit", style=nextcord.ButtonStyle.primary, custom_id="submitcreate") async def submitcreate_button( self, button: nextcord.ui.Button, interaction: nextcord.Interaction ): # pylint: disable=unused-argument """Button for score submission.""" # Don't allow a user to start a new submission if one already exists if submission_objects.has_user(interaction.user.id): await interaction.send("Please finish in progress submission first.", ephemeral=True) return with Session(engine) as session: stmt = ( select(CategoryGroup.GroupId) .where(CategoryGroup.GuildId == interaction.guild_id) .where(CategoryGroup.MessageId == interaction.message.id) ) group_id = session.scalar(stmt) stmt = select(Guild.SubmissionChannel).where(Guild.GuildId == interaction.guild_id) channel = interaction.guild.get_channel(session.scalar(stmt)) # a user should only have 1 submission active at once. # split username on pipe. Decided to store whole username... username: str = interaction.user.display_name.split("|")[0] submission: Submission = Submission(interaction.guild_id, group_id, username) submission_objects.add_submission(interaction.user.id, submission) embeds = submission.get_embeds() submission_message = await channel.send( f"<@{interaction.user.id}>", view=SubmissionView( self._bot, interaction.user.id, ), embeds=embeds, ) # update the message_id once its created submission.message_id = submission_message.id class SubmissionView(nextcord.ui.View): """Defines view submitting. Adjusts to all states.""" def __init__( self, bot: commands.Bot, submission_user: int, *, timeout: Optional[float] = 300, ): super().__init__(timeout=timeout) self._submission_user = submission_user self._bot = bot self._state = submission_objects.get_user_submission(self._submission_user).submission_state self._group_id = submission_objects.get_user_submission(self._submission_user).group_id self._guild_id = submission_objects.get_user_submission(self._submission_user).guild_id # add items depending on given state if self._state is SubmissionState.CATEGORY: self.add_item(SubmissionDropdown(bot, category_select_options(self._group_id))) self.add_item(CancelButton()) self.add_item(NextButton(self._bot)) elif ( self._state is SubmissionState.SCORE or self._state is SubmissionState.PROOF or self._state is SubmissionState.NAME ): self.add_item(BackButton(self._bot)) self.add_item(NextButton(self._bot)) elif self._state is SubmissionState.SUBMIT: self.add_item(BackButton(self._bot)) self.add_item(SubmitButton(self._bot)) async def interaction_check(self, interaction: nextcord.Interaction) -> bool: check = self._submission_user == interaction.user.id if not check: await interaction.send("This is not your submission.", ephemeral=True) return check # async def on_timeout(self) -> None: # # remove user from submission objects after time out. Allows user to start new submission. # submission_objects.remove_user_submission(self._submission_user)