update interaction create

This commit is contained in:
liyunze 2026-07-06 21:53:48 +10:00
parent fb4c133ba6
commit c818ddd8e5

View file

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