From 9c0b51db2bb68a565b6050aa584de3bb505befb7 Mon Sep 17 00:00:00 2001 From: Suya1671 Date: Sat, 21 Jun 2025 13:22:22 +0200 Subject: [PATCH] refactor: refactor interactions --- src/interactions/mod.rs | 180 ++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 61 deletions(-) diff --git a/src/interactions/mod.rs b/src/interactions/mod.rs index 89e5cd7..f488ad8 100755 --- a/src/interactions/mod.rs +++ b/src/interactions/mod.rs @@ -4,12 +4,13 @@ use std::error::Error; use std::sync::Arc; use axum::Extension; +use error_stack::Report; use member::{create_member, edit_member}; use slack_morphism::prelude::*; use tracing::{debug, error}; use trigger::{create_trigger, edit_trigger}; -use crate::fields; +use crate::BOT_TOKEN; use crate::models::system::System; use crate::models::{self, Trusted, user}; @@ -34,69 +35,72 @@ async fn interaction_event( ) -> Result<(), Box> { match event { SlackInteractionEvent::ViewSubmission(slack_interaction_view_submission_event) => { - match slack_interaction_view_submission_event.view.view { - SlackView::Home(view) => { - debug!(?view, "Received home view"); - Ok(()) - } - SlackView::Modal(ref view) => { - debug!(?view, "Received modal view"); - - let user_id: user::Id = - slack_interaction_view_submission_event.user.id.into(); - let states = states.read().await; - let user_state = states.get_user_state::().unwrap(); - - fields!(user_id = %&user_id); - - let Some(view_state) = slack_interaction_view_submission_event - .view - .state_params - .state - else { - error!("No state found in view submission"); - return Ok(()); - }; - - handle_modal_view( - client, - view_state, - user_state, - user_id, - view.external_id.as_deref(), - ) - .await - } - } + handle_view_submission(slack_interaction_view_submission_event, client, states).await + } + SlackInteractionEvent::Shortcut(shortcut) => { + debug!(?shortcut, "Received shortcut event"); + todo!() } event => { - debug!("Received interaction event: {:#?}", event); + debug!(?event, "Received interaction event",); Ok(()) } } } -#[tracing::instrument(skip(client, view_state, user_state))] +async fn handle_view_submission( + view_submission: SlackInteractionViewSubmissionEvent, + client: Arc, + states: SlackClientEventsUserState, +) -> Result<(), Box> { + match view_submission.view.view { + SlackView::Home(view) => { + debug!(?view, "Received home view"); + Ok(()) + } + SlackView::Modal(view) => { + debug!(?view, "Received modal view"); + + let user_id: user::Id = view_submission.user.id.into(); + + let Some(view_state) = view_submission.view.state_params.state else { + error!("No state found in modal view submission"); + return Ok(()); + }; + + handle_modal_view(client, view, view_state, states, user_id).await; + + Ok(()) + } + } +} + +#[tracing::instrument(skip(client, view, states))] async fn handle_modal_view( client: Arc, + view: SlackModalView, view_state: SlackViewState, - user_state: &user::State, + states: SlackClientEventsUserState, user_id: user::Id, - external_id: Option<&str>, -) -> Result<(), Box> { +) { + let states = states.read().await; + let user_state = states.get_user_state::().unwrap(); + let external_id = view.external_id.as_deref(); + match external_id { None => { error!( "No external id found in modal view. To the person that created the modal: How do you expect the bot to figure out what to do?" ); - Ok(()) } Some("create_member") => { debug!("Received create member modal view"); - create_member(view_state, &client, user_state, user_id).await?; - - Ok(()) + if let Err(error) = + create_member(view_state, &client, user_state, user_id.clone()).await + { + handle_user_error(error, user_id.into(), client).await; + } } Some(id) if id.starts_with("edit_member_") => { debug!("Received edit member modal view"); @@ -111,22 +115,31 @@ async fn handle_modal_view( id, "Failed to parse member id from external id. Bailing in case this was a malicious call", ); - return Ok(()); + return; }; - let Some(trusted_member_id) = - member_id.validate_by_user(&user_id, &user_state.db).await? + // TO-DO: better handling of Err case + let Ok(Some(trusted_member_id)) = + member_id.validate_by_user(&user_id, &user_state.db).await else { error!( id, "Failed to validate member id from external id. Bailing in case this was a malicious call", ); - return Ok(()); + return; }; - edit_member(view_state, &client, user_state, user_id, trusted_member_id).await?; - - Ok(()) + if let Err(error) = edit_member( + view_state, + &client, + user_state, + user_id.clone(), + trusted_member_id, + ) + .await + { + handle_user_error(error, user_id.into(), client).await; + } } Some(id) if id.starts_with("create_trigger_") => { debug!("Creating trigger"); @@ -138,18 +151,28 @@ async fn handle_modal_view( .map(models::member::Id::new) .expect("Failed to parse member id from external id"); - let Some(trusted_member_id) = - member_id.validate_by_user(&user_id, &user_state.db).await? + // TO-DO: Better handling of Err case + let Ok(Some(trusted_member_id)) = + member_id.validate_by_user(&user_id, &user_state.db).await else { error!( id, "Failed to validate member id from external id. Bailing in case this was a malicious call", ); - return Ok(()); + return; }; - create_trigger(view_state, &client, user_state, user_id, trusted_member_id).await?; - Ok(()) + if let Err(error) = create_trigger( + view_state, + &client, + user_state, + user_id.clone(), + trusted_member_id, + ) + .await + { + handle_user_error(error, user_id.into(), client).await; + }; } Some(id) if id.starts_with("edit_trigger_") => { debug!("Editing trigger"); @@ -170,7 +193,7 @@ async fn handle_modal_view( %user_id, "Failed to fetch system id for user id. Bailing in case this was a malicious call" ); - return Ok(()); + return; }; let Ok(trusted_trigger_id) = trigger_id @@ -181,15 +204,50 @@ async fn handle_modal_view( "Failed to validate member id from external id {}. Bailing in case this was a malicious call", id ); - return Ok(()); + return; }; - edit_trigger(view_state, &client, user_state, user_id, trusted_trigger_id).await?; - Ok(()) + if let Err(error) = edit_trigger( + view_state, + &client, + user_state, + user_id.clone(), + trusted_trigger_id, + ) + .await + { + handle_user_error(error, user_id.into(), client.clone()).await; + } } Some(id) => { error!("receieved unknown external id: {id}"); - Ok(()) } } } + +pub async fn handle_user_error( + error: Report, + user: SlackUserId, + client: Arc, +) where + E: std::error::Error + Send + Sync + 'static, +{ + error!(?error); + + let session = client.open_session(&BOT_TOKEN); + + let conversation = session + .conversations_open(&SlackApiConversationsOpenRequest::new().with_users(vec![user.clone()])) + .await + .expect("Expected to be able to open conversation") + .channel; + + session + .chat_post_ephemeral(&SlackApiChatPostEphemeralRequest::new( + conversation.id, + user, + SlackMessageContent::new().with_text(format!("An error occured! {error}",)), + )) + .await + .expect("Expected to be able to post ephemeral message"); +}