From c506186da412585046c753a472c8eacbb1d3f4ad Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Sat, 11 Jan 2025 14:03:45 +0100 Subject: [PATCH] feat(http): Make it possible to use callback?with_response=true --- .../interaction/create_response.rs | 15 +- .../create_response_with_response.rs | 128 ++++++++++++++++++ .../request/application/interaction/mod.rs | 6 +- twilight-http/src/request/try_into_request.rs | 5 +- twilight-http/src/routing.rs | 11 +- 5 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 twilight-http/src/request/application/interaction/create_response_with_response.rs diff --git a/twilight-http/src/request/application/interaction/create_response.rs b/twilight-http/src/request/application/interaction/create_response.rs index 245564a11d6..8b8718fbc8d 100644 --- a/twilight-http/src/request/application/interaction/create_response.rs +++ b/twilight-http/src/request/application/interaction/create_response.rs @@ -1,7 +1,10 @@ use crate::{ client::Client, error::Error, - request::{attachment::AttachmentManager, Request, TryIntoRequest}, + request::{ + application::interaction::CreateResponseWithResponse, attachment::AttachmentManager, + Request, TryIntoRequest, + }, response::{marker::EmptyBody, Response, ResponseFuture}, routing::Route, }; @@ -36,6 +39,15 @@ impl<'a> CreateResponse<'a> { http, } } + + pub const fn with_response(self) -> CreateResponseWithResponse<'a> { + CreateResponseWithResponse::new( + self.http, + self.interaction_id, + self.interaction_token, + self.response, + ) + } } impl IntoFuture for CreateResponse<'_> { @@ -58,6 +70,7 @@ impl TryIntoRequest for CreateResponse<'_> { let mut request = Request::builder(&Route::InteractionCallback { interaction_id: self.interaction_id.get(), interaction_token: self.interaction_token, + with_response: false, }); // Interaction executions don't need the authorization token, only the diff --git a/twilight-http/src/request/application/interaction/create_response_with_response.rs b/twilight-http/src/request/application/interaction/create_response_with_response.rs new file mode 100644 index 00000000000..415d053bf02 --- /dev/null +++ b/twilight-http/src/request/application/interaction/create_response_with_response.rs @@ -0,0 +1,128 @@ +use crate::{ + client::Client, + error::Error, + request::{attachment::AttachmentManager, Request, TryIntoRequest}, + response::{Response, ResponseFuture}, + routing::Route, +}; +use std::future::IntoFuture; +use twilight_model::{ + http::interaction::InteractionResponse, + id::{marker::InteractionMarker, Id}, +}; + +/// Respond to an interaction, by its ID and token. +/// +/// This endpoint is not bound to the application's global rate limit. +#[must_use = "requests must be configured and executed"] +pub struct CreateResponseWithResponse<'a> { + interaction_id: Id, + interaction_token: &'a str, + response: &'a InteractionResponse, + http: &'a Client, +} + +impl<'a> CreateResponseWithResponse<'a> { + pub(crate) const fn new( + http: &'a Client, + interaction_id: Id, + interaction_token: &'a str, + response: &'a InteractionResponse, + ) -> Self { + Self { + interaction_id, + interaction_token, + response, + http, + } + } +} + +impl IntoFuture for CreateResponseWithResponse<'_> { + type Output = Result, Error>; + + type IntoFuture = ResponseFuture; + + fn into_future(self) -> Self::IntoFuture { + let http = self.http; + + match self.try_into_request() { + Ok(request) => http.request(request), + Err(source) => ResponseFuture::error(source), + } + } +} + +impl TryIntoRequest for CreateResponseWithResponse<'_> { + fn try_into_request(self) -> Result { + let mut request = Request::builder(&Route::InteractionCallback { + interaction_id: self.interaction_id.get(), + interaction_token: self.interaction_token, + with_response: true, + }); + + // Interaction executions don't need the authorization token, only the + // interaction token. + request = request.use_authorization_token(false); + + // Determine whether we need to use a multipart/form-data body or a JSON + // body. + if let Some(attachments) = self + .response + .data + .as_ref() + .and_then(|data| data.attachments.as_ref()) + { + let fields = crate::json::to_vec(&self.response).map_err(Error::json)?; + + let form = AttachmentManager::new() + .set_files(attachments.iter().collect()) + .build_form(&fields); + + request = request.form(form); + } else { + request = request.json(&self.response); + } + + request.build() + } +} + +#[cfg(test)] +mod tests { + use crate::{client::Client, request::TryIntoRequest}; + use std::error::Error; + use twilight_http_ratelimiting::Path; + use twilight_model::{ + http::interaction::{InteractionResponse, InteractionResponseType}, + id::Id, + }; + + #[test] + fn interaction_callback() -> Result<(), Box> { + let application_id = Id::new(1); + let interaction_id = Id::new(2); + let token = "foo".to_owned().into_boxed_str(); + + let client = Client::new(String::new()); + + let response = InteractionResponse { + kind: InteractionResponseType::DeferredUpdateMessage, + data: None, + }; + + let req = client + .interaction(application_id) + .create_response(interaction_id, &token, &response) + .with_response() + .try_into_request()?; + + assert!(!req.use_authorization_token()); + assert_eq!( + &Path::InteractionCallback(interaction_id.get()), + req.ratelimit_path() + ); + + Ok(()) + } +} diff --git a/twilight-http/src/request/application/interaction/mod.rs b/twilight-http/src/request/application/interaction/mod.rs index c02578438ff..75efc5f50b8 100644 --- a/twilight-http/src/request/application/interaction/mod.rs +++ b/twilight-http/src/request/application/interaction/mod.rs @@ -1,5 +1,6 @@ mod create_followup; mod create_response; +mod create_response_with_response; mod delete_followup; mod delete_response; mod get_followup; @@ -9,6 +10,7 @@ mod update_response; pub use self::{ create_followup::CreateFollowup, create_response::CreateResponse, - delete_followup::DeleteFollowup, delete_response::DeleteResponse, get_followup::GetFollowup, - get_response::GetResponse, update_followup::UpdateFollowup, update_response::UpdateResponse, + create_response_with_response::CreateResponseWithResponse, delete_followup::DeleteFollowup, + delete_response::DeleteResponse, get_followup::GetFollowup, get_response::GetResponse, + update_followup::UpdateFollowup, update_response::UpdateResponse, }; diff --git a/twilight-http/src/request/try_into_request.rs b/twilight-http/src/request/try_into_request.rs index a86b7a6cce4..ef5df69d413 100644 --- a/twilight-http/src/request/try_into_request.rs +++ b/twilight-http/src/request/try_into_request.rs @@ -19,8 +19,8 @@ mod private { UpdateApplicationEmoji, }, interaction::{ - CreateFollowup, CreateResponse, DeleteFollowup, DeleteResponse, GetFollowup, - GetResponse, UpdateFollowup, UpdateResponse, + CreateFollowup, CreateResponse, CreateResponseWithResponse, DeleteFollowup, + DeleteResponse, GetFollowup, GetResponse, UpdateFollowup, UpdateResponse, }, monetization::{ create_test_entitlement::CreateTestEntitlement, get_entitlements::GetEntitlements, @@ -139,6 +139,7 @@ mod private { impl Sealed for CreatePrivateChannel<'_> {} impl Sealed for CreateReaction<'_> {} impl Sealed for CreateResponse<'_> {} + impl Sealed for CreateResponseWithResponse<'_> {} impl Sealed for CreateRole<'_> {} impl Sealed for CreateStageInstance<'_> {} impl Sealed for CreateTemplate<'_> {} diff --git a/twilight-http/src/routing.rs b/twilight-http/src/routing.rs index cffa735f22b..6e903d4207a 100644 --- a/twilight-http/src/routing.rs +++ b/twilight-http/src/routing.rs @@ -929,6 +929,8 @@ pub enum Route<'a> { interaction_id: u64, /// The token for the interaction. interaction_token: &'a str, + /// If response should be retrived. + with_response: bool, }, /// Route information to join a thread as the current user. JoinThread { @@ -2918,13 +2920,20 @@ impl Display for Route<'_> { Route::InteractionCallback { interaction_id, interaction_token, + with_response, } => { f.write_str("interactions/")?; Display::fmt(interaction_id, f)?; f.write_str("/")?; f.write_str(interaction_token)?; - f.write_str("/callback") + f.write_str("/callback")?; + + if *with_response { + f.write_str("?with_response=true")?; + } + + Ok(()) } Route::JoinThread { channel_id } | Route::LeaveThread { channel_id } => { f.write_str("channels/")?;