diff --git a/Cargo.toml b/Cargo.toml index 8e9652b..7f30de7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,5 @@ regex = "1.7.3" custom_error = "1.9.2" native-tls = "0.2.11" tokio-tungstenite = {version = "0.18.0", features = ["native-tls"]} -futures-util = "0.3.28" \ No newline at end of file +futures-util = "0.3.28" +http = "0.2.9" \ No newline at end of file diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 082d822..b63d9d3 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -1,8 +1,11 @@ pub mod messages { - use reqwest::Client; + use http::header::CONTENT_DISPOSITION; + use http::{header, HeaderMap}; + use reqwest::{multipart, Client}; use serde_json::to_string; use crate::api::types::{Message, PartialDiscordFileAttachment, User}; + use crate::errors::InstanceServerError; use crate::limit::LimitedRequester; impl Message { @@ -17,7 +20,6 @@ pub mod messages { # Errors * [`InstanceServerError`] - If the message cannot be sent. */ - pub async fn send<'a>( url_api: &String, channel_id: &String, @@ -26,6 +28,7 @@ pub mod messages { token: &String, user: &mut User<'a>, ) -> Result { + let file_attachments_static: &'static [PartialDiscordFileAttachment]; let mut requester = LimitedRequester::new().await; let user_rate_limits = &mut user.limits; let instance_rate_limits = &mut user.belongs_to.limits; @@ -44,10 +47,65 @@ pub mod messages { ) .await } else { - Err(crate::errors::InstanceServerError::InvalidFormBodyError { - error_type: "Not implemented".to_string(), - error: "Not implemented".to_string(), - }) + let mut form = reqwest::multipart::Form::new(); + let payload_json = to_string(message).unwrap(); + let mut payload_field = + reqwest::multipart::Part::text(payload_json).file_name("payload_json"); + payload_field = match payload_field.mime_str("application/json") { + Ok(part) => part, + Err(e) => { + return Err(InstanceServerError::MultipartCreationError { + error: e.to_string(), + }) + } + }; + + form = form.part("payload_json", payload_field); + + if let Some(file_attachments) = files { + let boxed_attachments = file_attachments.into_boxed_slice(); + file_attachments_static = Box::leak(boxed_attachments); + for (index, attachment) in file_attachments_static.iter().enumerate() { + let part_name = format!("files[{}]", index); + let content_disposition = format!( + "form-data; name=\"{}\"'; filename=\"{}\"", + part_name, + attachment.filename.as_deref().unwrap_or("file") + ); + let mut header_map = HeaderMap::new(); + header_map + .insert(CONTENT_DISPOSITION, content_disposition.parse().unwrap()) + .unwrap(); + + let mut part = multipart::Part::bytes(Vec::new()) + .file_name(attachment.filename.as_deref().unwrap_or("file")) + .headers(header_map); + + part = match part.mime_str("application/octet-stream") { + Ok(part) => part, + Err(e) => { + return Err(InstanceServerError::MultipartCreationError { + error: e.to_string(), + }) + } + }; + + form = form.part(part_name, part); + } + } + let message_request = Client::new() + .post(format!("{}/channels/{}/messages/", url_api, channel_id)) + .bearer_auth(token) + .multipart(form); + + requester + .send_request( + message_request, + crate::api::limits::LimitType::Channel, + instance_rate_limits, + user_rate_limits, + ) + .await } } } diff --git a/src/errors.rs b/src/errors.rs index f74c7cd..76d16bd 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -18,6 +18,7 @@ custom_error! { CantGetInfoError{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}", InvalidFormBodyError{error_type: String, error:String} = "The server responded with: {error_type}: {error}", RateLimited = "Ratelimited.", + MultipartCreationError{error: String} = "Got an error whilst creating the form: {}", } custom_error! {