"""Contains various views to be used in the high council nomination process.""" import nextcord from nextcord.interactions import Interaction from sqlalchemy import select from sqlalchemy.orm import Session from sqlmodel import col from dakiny_bot.dakiny_settings import settings from dakiny_bot.database.db import engine from dakiny_bot.database.models import CouncilMember from dakiny_bot.logger import dakiny_logger from dakiny_bot.utils import ( NOMINEE_SPLITTER, decorate_cl_nomination, decorate_dm_nomination, get_election_name, ) class NominateDMModal(nextcord.ui.Modal): """ Modal for nominating oneself. User writes a speech and then the callback sends the speech into the correct channel. """ def __init__( self, usr_id: int, usr_nominated: bool, # noqa: FBT001 speech_content: str, ): super().__init__( "Discord Manager Nomination", timeout=5 * 60, # 5 minutes ) self.usr_id: int = usr_id self.usr_nominated = usr_nominated self.speech = nextcord.ui.TextInput( label="Nomination Speech", style=nextcord.TextInputStyle.paragraph, placeholder="Please write your speech here.", required=True, max_length=1500, default_value=speech_content, ) self.add_item(self.speech) async def callback(self, interaction: Interaction) -> None: """ Sends the nomination speech into the voting channel and enters the user into the voting database. Args: interaction (Interaction): Interaction with user. """ if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel): # ignore none... Don't know what situation this is. # if not textchannel, don't know how callback happened return speech = decorate_dm_nomination(interaction.user.id, self.speech.value if self.speech.value else "") with Session(engine) as session: # user already in table election_name = get_election_name() stmt = ( select(CouncilMember) .where(col(CouncilMember.election_name == election_name)) .where(col(CouncilMember.member_id == self.usr_id)) ) council_elect = session.scalar(stmt) if council_elect is None: dakiny_logger.error("Did not find member (id: %s) in election %s.", self.usr_id, election_name) return if not self.usr_nominated or council_elect.discord_manager_speech_id is None: msg = await interaction.channel.send(speech) council_elect.discord_manager_speech_id = msg.id session.commit() else: msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.discord_manager_speech_id) await msg.edit( content=decorate_dm_nomination(interaction.user.id, self.speech.value if self.speech.value else "") ) class NominateCLModal(nextcord.ui.Modal): """ Modal for nominating oneself. User writes a speech and then the callback sends the speech into the correct channel. """ def __init__( self, usr_id: int, usr_nominated: bool, # noqa: FBT001 speech_content: str, ): super().__init__( "Clan Leader Nomination", timeout=5 * 60, # 5 minutes ) self.usr_id: int = usr_id self.usr_nominated = usr_nominated self.speech = nextcord.ui.TextInput( label="Nomination Speech", style=nextcord.TextInputStyle.paragraph, placeholder="Please write your speech here.", required=True, max_length=1500, default_value=speech_content, ) self.add_item(self.speech) async def callback(self, interaction: Interaction) -> None: """ Sends the nomination speech into the voting channel and enters the user into the voting database. Args: interaction (Interaction): Interaction with user. """ if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel): # ignore none... Don't know what situation this is. # if not textchannel, don't know how callback happened return speech = decorate_cl_nomination(interaction.user.id, self.speech.value if self.speech.value else "") with Session(engine) as session: # user already in table election_name = get_election_name() stmt = ( select(CouncilMember) .where(col(CouncilMember.election_name == election_name)) .where(col(CouncilMember.member_id == self.usr_id)) ) council_elect = session.scalar(stmt) if council_elect is None: dakiny_logger.error("Did not find member (id: %s) in election %s.", self.usr_id, election_name) return if not self.usr_nominated or council_elect.clan_leader_speech_id is None: msg = await interaction.channel.send(speech) council_elect.clan_leader_speech_id = msg.id session.commit() else: msg: nextcord.Message = await interaction.channel.fetch_message(council_elect.clan_leader_speech_id) await msg.edit( content=decorate_cl_nomination(interaction.user.id, self.speech.value if self.speech.value else "") ) class NominateHighCouncilView(nextcord.ui.View): """The buttons to press to start your High Council nomination.""" def __init__(self, guild: nextcord.Guild): super().__init__(timeout=None) self.guild: nextcord.Guild = guild @nextcord.ui.button( label="Nominate for Discord Manager", style=nextcord.ButtonStyle.primary, custom_id="nominate-dm", ) async def nominate_dm(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002 """ Button for nominating oneself for Discord Manager. Gathers previous information about the user if they have already nominated themselves, then launches the nomination modal to get their speech. Args: button (nextcord.ui.Button): Unused. idk why its needed in the decorator interaction (nextcord.Interaction): Interaction of user who pressed the button """ if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel): # ignore none... Don't know what situation this is. # if not textchannel, don't know how callback happened return # enforce council elect only if ( isinstance(interaction.user, nextcord.User) or self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles ): await interaction.response.send_message( f"You must have the <@&{settings.role_ids.clan_council_elect}> role to vote.", ephemeral=True, ) return election_name = get_election_name() usr_nominated = False speech_content = "" with Session(engine) as session: stmt = ( select(CouncilMember) .where(col(CouncilMember.election_name == election_name)) .where(col(CouncilMember.member_id == interaction.user.id)) ) council_elect = session.scalar(stmt) if council_elect is None: dakiny_logger.error("Did not find member (id: %s) in election %s.", interaction.user.id, election_name) return # user needs to exist to get here anyways... if council_elect.discord_manager_speech_id is not None: usr_nominated = True msg = await interaction.channel.fetch_message(council_elect.discord_manager_speech_id) speech_content = msg.content.split(NOMINEE_SPLITTER)[1] await interaction.response.send_modal(NominateDMModal(interaction.user.id, usr_nominated, speech_content)) @nextcord.ui.button( label="Nominate for Clan Leader", style=nextcord.ButtonStyle.primary, custom_id="nominate-cl", ) async def nominate_cl(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002 """ Button for nominating oneself for Clan Leader. Gathers previous information about the user if they have already nominated themselves, then launches the nomination modal to get their speech. Args: button (nextcord.ui.Button): Unused. idk why its needed in the decorator interaction (nextcord.Interaction): Interaction of user who pressed the button """ if interaction.user is None or not isinstance(interaction.channel, nextcord.TextChannel): # ignore none... Don't know what situation this is. # if not textchannel, don't know how callback happened return # enforce council elect only if ( isinstance(interaction.user, nextcord.User) or self.guild.get_role(settings.role_ids.clan_council_elect) not in interaction.user.roles ): await interaction.response.send_message( f"You must have the <@&{settings.role_ids.clan_council_elect}> role to vote.", ephemeral=True, ) return election_name = get_election_name() usr_nominated = False speech_content = "" with Session(engine) as session: stmt = ( select(CouncilMember) .where(col(CouncilMember.election_name == election_name)) .where(col(CouncilMember.member_id == interaction.user.id)) ) council_elect = session.scalar(stmt) if council_elect is None: dakiny_logger.error("Did not find member (id: %s) in election %s.", interaction.user.id, election_name) return # user needs to exist to get here anyways... if council_elect.clan_leader_speech_id is not None: usr_nominated = True msg = await interaction.channel.fetch_message(council_elect.clan_leader_speech_id) speech_content = msg.content.split(NOMINEE_SPLITTER)[1] await interaction.response.send_modal(NominateCLModal(interaction.user.id, usr_nominated, speech_content))