diff --git a/src/events/interaction_create.rs b/src/events/interaction_create.rs index 68edaf6..4139a4e 100644 --- a/src/events/interaction_create.rs +++ b/src/events/interaction_create.rs @@ -5,122 +5,88 @@ use crate::{ commands::verification::{StudentRow, VerificationModal}, }; use ::serenity::all::{ - ComponentInteraction, Context, CreateEmbed, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseFollowup, CreateInteractionResponseMessage, GuildId, RoleId + Context, CreateEmbed, CreateEmbedFooter, CreateInteractionResponse, + CreateInteractionResponseMessage, GuildId, RoleId, + collector::ModalInteractionCollector, }; use dotenv::dotenv; -use poise::{modal, serenity_prelude as serenity}; - -struct ContextRef<'a>(&'a Context); -impl AsRef for ContextRef<'_> { - fn as_ref(&self) -> &Context { - self.0 - } -} - -async fn embed_response( - ctx: &serenity::Context, - interaction: &ComponentInteraction, - title: impl Into, - description: impl Into, -) -> Result<(), Error> { - let embed = CreateEmbed::new().title(title).description(description); - - let response = CreateInteractionResponse::Message( - CreateInteractionResponseMessage::new() - .add_embed(embed) - .ephemeral(true), - ); - - interaction.create_response(ctx, response).await?; - - Ok(()) -} - -async fn embed_followup( - ctx: &serenity::Context, - interaction: &ComponentInteraction, - title: impl Into, - description: impl Into, -) -> Result<(), Error> { - let embed = CreateEmbed::new().title(title).description(description); - - let response = CreateInteractionResponseFollowup::new() - .add_embed(embed) - .ephemeral(true); - - interaction.create_followup(ctx, response).await?; - - Ok(()) -} +use poise::Modal as _; +use poise::serenity_prelude as serenity; pub async fn on_interaction_create( - ctx: &serenity::Context, + ctx: &Context, interaction: &serenity::Interaction, data: &Data, ) -> Result<(), Error> { - let Some(interaction) = interaction.as_message_component() else { + let Some(component_interaction) = interaction.as_message_component() else { return Ok(()); }; - if interaction.data.custom_id == "verify" { + if component_interaction.data.custom_id == "verify" { dotenv().ok(); - let guild_id = match interaction.guild_id { + let guild_id = match component_interaction.guild_id { Some(id) => id, None => { - // user not in a server at all - embed_response( - ctx, - interaction, - "Unable to perform action", - "Action can only be performed in the DSEC server", - ) - .await?; - + component_interaction.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(CreateEmbed::new() + .title("Unable to perform action") + .description("Action can only be performed in the DSEC server")) + .ephemeral(true), + )).await?; return Ok(()); } }; let role_id_string = std::env::var("VERIFIED_ROLE_ID").expect("missing VERIFIED_ROLE_ID"); - let role_id_u64: u64 = role_id_string .parse() .expect("Unable to parse VERIFIED_ROLE_ID into number"); - let user_id = interaction.user.id; + let user_id = component_interaction.user.id; let verified_role_id = RoleId::new(role_id_u64); - let discord_member = GuildId::member(guild_id, ctx, user_id).await?; - let has_role = discord_member.roles.contains(&verified_role_id); - - // Has role - if has_role { - embed_response( - ctx, - interaction, - "Already Verified ✅", - format!("You already have the <@&{}> role!", verified_role_id), - ) - .await?; + if discord_member.roles.contains(&verified_role_id) { + component_interaction.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(CreateEmbed::new() + .title("Already Verified ✅") + .description(format!("You already have the <@&{}> role!", verified_role_id))) + .ephemeral(true), + )).await?; return Ok(()); } - // modal - let timeout = Duration::from_secs(120); + let modal_custom_id = component_interaction.id.to_string(); - let modal_data = modal::execute_modal_on_component_interaction::( - ContextRef(ctx), - interaction.clone(), - None, - Some(timeout), - ) - .await?; + let modal = VerificationModal::create(None, modal_custom_id.clone()); + component_interaction.create_response(ctx, modal).await?; - let modal_data = match modal_data { - Some(data) => data, + let modal_submit = ModalInteractionCollector::new(&ctx.shard) + .filter(move |d| d.data.custom_id == modal_custom_id) + .timeout(Duration::from_secs(120)) + .await; + + let modal_submit = match modal_submit { + Some(x) => x, None => return Ok(()), }; + let modal_data = match VerificationModal::parse(modal_submit.data.clone()) { + Ok(data) => data, + Err(_) => { + modal_submit.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(CreateEmbed::new() + .title("Error") + .description("Failed to parse modal data. Please try again.")) + .ephemeral(true), + )).await?; + return Ok(()); + } + }; + let input_student_id: &str = &modal_data.student_id.to_lowercase(); let student_id = input_student_id .strip_prefix("s") @@ -129,9 +95,7 @@ pub async fn on_interaction_create( let state = &data.state; let student_in_cache: bool = { - let cache: std::sync::MutexGuard<'_, std::collections::HashMap> = - state.student_cache.lock().expect("Failed to get cache"); - + let cache = state.student_cache.lock().expect("Failed to get cache"); match cache.get(student_id) { Some(cached_name) => cached_name == &modal_data.name.to_lowercase(), None => false, @@ -141,7 +105,7 @@ pub async fn on_interaction_create( if student_in_cache { discord_member.add_role(ctx, verified_role_id).await?; - let verified_cache_embed = CreateEmbed::new() + let embed = CreateEmbed::new() .title("Verified ✅") .description(format!( "You have been assigned the <@&{}> role!", @@ -149,16 +113,15 @@ pub async fn on_interaction_create( )) .footer(CreateEmbedFooter::new("⚡ via cache")); - let verified_msg = CreateInteractionResponseFollowup::new() - .add_embed(verified_cache_embed) - .ephemeral(true); - - interaction.create_followup(ctx, verified_msg).await?; + modal_submit.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(embed) + .ephemeral(true), + )).await?; return Ok(()); } - // fetch from DB let student_data: Vec = state .supabase .database() @@ -171,19 +134,16 @@ pub async fn on_interaction_create( let result = student_data.iter().next(); if result.is_none() { - // TODO: add user to "don't use this command for 5 minutes" - - embed_followup(ctx, - interaction, - "Student ID not found!", - "Your student ID is not found. - It takes up to **a week** for your membership to be updated in the database since sign up. - Try again later.").await?; - + modal_submit.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(CreateEmbed::new() + .title("Student ID not found!") + .description("Your student ID is not found.\nIt takes up to **a week** for your membership to be updated in the database since sign up.\nTry again later.")) + .ephemeral(true), + )).await?; return Ok(()); } - // get name from result let result_name = &result.unwrap().full_name; { @@ -197,24 +157,26 @@ pub async fn on_interaction_create( if &result_name.to_lowercase() == &modal_data.name.to_lowercase() { discord_member.add_role(ctx, verified_role_id).await?; - embed_followup( - ctx, - interaction, - "Verified ✅", - format!("You have been assigned the <@&{}> role!", verified_role_id), - ) - .await?; + let embed = CreateEmbed::new() + .title("Verified ✅") + .description(format!( + "You have been assigned the <@&{}> role!", + verified_role_id + )); - // post to logs channel - + modal_submit.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(embed) + .ephemeral(true), + )).await?; } else { - embed_followup( - ctx, - interaction, - "Name mismatch ❌", - "Your student ID is present, however the name does not match. Try again.", - ) - .await?; + modal_submit.create_response(ctx, CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .add_embed(CreateEmbed::new() + .title("Name mismatch ❌") + .description("Your student ID is present, however the name does not match. Try again.")) + .ephemeral(true), + )).await?; } }