diff --git a/examples/api_trait_implementation.rs b/examples/api_trait_implementation.rs index 853f4f8..82329d5 100644 --- a/examples/api_trait_implementation.rs +++ b/examples/api_trait_implementation.rs @@ -1,8 +1,8 @@ use frankenstein::ErrorResponse; +use frankenstein::FileUploadForm; use frankenstein::SendMessageParams; use frankenstein::TelegramApi; use isahc::{prelude::*, Request}; -use std::path::PathBuf; static TOKEN: &str = "TOKEN"; static BASE_API_URL: &str = "https://api.telegram.org/bot"; @@ -106,7 +106,7 @@ impl TelegramApi for Api { &self, _method: &str, _params: T1, - _files: Vec<(&str, PathBuf)>, + _files: Vec, ) -> Result { let error = HttpError { code: 500, diff --git a/src/api/async_telegram_api_impl.rs b/src/api/async_telegram_api_impl.rs index 7d2e1c3..f4dd6c1 100644 --- a/src/api/async_telegram_api_impl.rs +++ b/src/api/async_telegram_api_impl.rs @@ -1,11 +1,12 @@ use super::Error; use super::HttpError; +use crate::api_params::FileUpload; +use crate::api_params::FileUploadForm; use crate::api_traits::AsyncTelegramApi; use crate::api_traits::ErrorResponse; use async_trait::async_trait; use reqwest::multipart; use serde_json::Value; -use std::path::PathBuf; use std::time::Duration; use tokio::fs::File; use typed_builder::TypedBuilder; @@ -133,21 +134,11 @@ impl AsyncTelegramApi for AsyncApi { &self, method: &str, params: T1, - files: Vec<(&str, PathBuf)>, + files: Vec, ) -> Result { let json_string = Self::encode_params(¶ms)?; let json_struct: Value = serde_json::from_str(&json_string).unwrap(); - let file_keys: Vec<&str> = files.iter().map(|(key, _)| *key).collect(); - let files_with_paths: Vec<(String, &str, String)> = files - .iter() - .map(|(key, path)| { - ( - (*key).to_string(), - path.to_str().unwrap(), - path.file_name().unwrap().to_str().unwrap().to_string(), - ) - }) - .collect(); + let file_keys: Vec<&str> = files.iter().map(|(key, _)| key.as_str()).collect(); let mut form = multipart::Form::new(); for (key, val) in json_struct.as_object().unwrap() { @@ -161,11 +152,25 @@ impl AsyncTelegramApi for AsyncApi { } } - for (parameter_name, file_path, file_name) in files_with_paths { - let file = File::open(file_path).await?; + for (parameter_name, file) in files { + match file { + FileUpload::InputFile(input_file) => { + let file_path = input_file.path; + let file = File::open(&file_path).await?; + let file_name = file_path.file_name().unwrap().to_str().unwrap().to_string(); - let part = multipart::Part::stream(file).file_name(file_name); - form = form.part(parameter_name, part); + let part = multipart::Part::stream(file).file_name(file_name); + form = form.part(parameter_name, part); + } + FileUpload::InputBuf(input_buf) => { + let buf = input_buf.data.clone(); + let file_name = input_buf.file_name.clone(); + + let part = multipart::Part::stream(buf).file_name(file_name); + form = form.part(parameter_name, part); + } + FileUpload::String(_) => continue, + } } let url = format!("{}/{method}", self.api_url); diff --git a/src/api/telegram_api_impl.rs b/src/api/telegram_api_impl.rs index d5c76c7..8c2905e 100644 --- a/src/api/telegram_api_impl.rs +++ b/src/api/telegram_api_impl.rs @@ -1,10 +1,11 @@ use super::Error; use super::HttpError; +use crate::api_params::FileUpload; +use crate::api_params::FileUploadForm; use crate::api_traits::ErrorResponse; use crate::api_traits::TelegramApi; use multipart::client::lazy::Multipart; use serde_json::Value; -use std::path::PathBuf; use std::time::Duration; use typed_builder::TypedBuilder; use ureq::Response; @@ -123,15 +124,11 @@ impl TelegramApi for Api { &self, method: &str, params: T1, - files: Vec<(&str, PathBuf)>, + files: Vec, ) -> Result { let json_string = Self::encode_params(¶ms)?; let json_struct: Value = serde_json::from_str(&json_string).unwrap(); - let file_keys: Vec<&str> = files.iter().map(|(key, _)| *key).collect(); - let files_with_names: Vec<(&str, Option<&str>, PathBuf)> = files - .iter() - .map(|(key, path)| (*key, path.file_name().unwrap().to_str(), path.clone())) - .collect(); + let file_keys: Vec<&str> = files.iter().map(|(key, _)| key.as_str()).collect(); let mut form = Multipart::new(); for (key, val) in json_struct.as_object().unwrap() { @@ -145,15 +142,26 @@ impl TelegramApi for Api { } } - for (parameter_name, file_name, file_path) in files_with_names { - let file = std::fs::File::open(&file_path).unwrap(); - let file_extension = file_path - .extension() - .and_then(std::ffi::OsStr::to_str) - .unwrap_or(""); - let mime = mime_guess::from_ext(file_extension).first_or_octet_stream(); + for (parameter_name, file) in &files { + match file { + FileUpload::InputFile(input_file) => { + let file = std::fs::File::open(&input_file.path).unwrap(); + let file_name = input_file.path.file_name().unwrap().to_str(); + let file_extension = + input_file.path.extension().unwrap().to_str().unwrap_or(""); + let mime = mime_guess::from_ext(file_extension).first_or_octet_stream(); - form.add_stream(parameter_name, file, file_name, Some(mime)); + form.add_stream(parameter_name, file, file_name, Some(mime)); + } + FileUpload::InputBuf(input_buf) => { + let file = std::io::Cursor::>::new(input_buf.data.clone()); + let file_extension = input_buf.file_name.rsplit_once('.').unwrap_or(("", "")).1; + let mime = mime_guess::from_ext(file_extension).first_or_octet_stream(); + + form.add_stream(parameter_name, file, Some(&input_buf.file_name), Some(mime)); + } + FileUpload::String(_) => continue, + } } let url = format!("{}/{method}", self.api_url); diff --git a/src/api_params.rs b/src/api_params.rs index 07e80cd..e20a506 100644 --- a/src/api_params.rs +++ b/src/api_params.rs @@ -25,9 +25,22 @@ use typed_builder::TypedBuilder as Builder; #[serde(untagged)] pub enum FileUpload { InputFile(InputFile), + InputBuf(InputBuf), String(String), } +pub type FileUploadForm = (String, FileUpload); + +impl FileUpload { + pub fn to_form_with_key(&self, key: &str) -> FileUploadForm { + (key.to_string(), self.clone()) + } + + pub fn is_input(&self) -> bool { + matches!(self, FileUpload::InputFile(_) | FileUpload::InputBuf(_)) + } +} + impl From for FileUpload { fn from(path: PathBuf) -> Self { let input_file = InputFile { path }; @@ -36,12 +49,36 @@ impl From for FileUpload { } } +impl From<(String, Vec)> for FileUpload { + fn from((file_name, data): (String, Vec)) -> Self { + Self::InputBuf(InputBuf { file_name, data }) + } +} + impl From for FileUpload { fn from(file: InputFile) -> Self { Self::InputFile(file) } } +impl From<&InputFile> for FileUpload { + fn from(file: &InputFile) -> Self { + Self::InputFile(file.clone()) + } +} + +impl From for FileUpload { + fn from(input: InputBuf) -> Self { + Self::InputBuf(input) + } +} + +impl From<&InputBuf> for FileUpload { + fn from(input: &InputBuf) -> Self { + Self::InputBuf(input.clone()) + } +} + impl From for FileUpload { fn from(file: String) -> Self { Self::String(file) @@ -220,6 +257,12 @@ pub struct InputFile { pub path: PathBuf, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Builder)] +pub struct InputBuf { + pub file_name: String, + pub data: Vec, +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Builder)] pub struct GetUpdatesParams { #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/api_traits/async_telegram_api.rs b/src/api_traits/async_telegram_api.rs index 01cb6dd..65192dc 100644 --- a/src/api_traits/async_telegram_api.rs +++ b/src/api_traits/async_telegram_api.rs @@ -37,6 +37,7 @@ use crate::api_params::EditMessageReplyMarkupParams; use crate::api_params::EditMessageTextParams; use crate::api_params::ExportChatInviteLinkParams; use crate::api_params::FileUpload; +use crate::api_params::FileUploadForm; use crate::api_params::ForwardMessageParams; use crate::api_params::ForwardMessagesParams; use crate::api_params::GetBusinessConnectionParams; @@ -148,7 +149,6 @@ use crate::objects::WebhookInfo; use crate::Sticker; use crate::UnpinAllGeneralForumTopicMessagesParams; use async_trait::async_trait; -use std::path::PathBuf; #[async_trait] pub trait AsyncTelegramApi { @@ -231,10 +231,10 @@ pub trait AsyncTelegramApi { params: &SendPhotoParams, ) -> Result, Self::Error> { let method_name = "sendPhoto"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.photo { - files.push(("photo", input_file.path.clone())); + if params.photo.is_input() { + files.push(params.photo.to_form_with_key("photo")); } self.request_with_possible_form_data(method_name, params, files) @@ -246,14 +246,16 @@ pub trait AsyncTelegramApi { params: &SendAudioParams, ) -> Result, Self::Error> { let method_name = "sendAudio"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.audio { - files.push(("audio", input_file.path.clone())); + if params.audio.is_input() { + files.push(params.audio.to_form_with_key("audio")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -265,7 +267,7 @@ pub trait AsyncTelegramApi { params: &SendMediaGroupParams, ) -> Result>, Self::Error> { let method_name = "sendMediaGroup"; - let mut files: Vec<(String, PathBuf)> = vec![]; + let mut files: Vec = vec![]; let mut new_medias: Vec = vec![]; let mut file_idx = 0; @@ -274,25 +276,24 @@ pub trait AsyncTelegramApi { Media::Audio(audio) => { let mut new_audio = audio.clone(); - if let FileUpload::InputFile(input_file) = &audio.media { + if audio.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_audio.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_audio.media = FileUpload::String(format!("attach://{name}")); + files.push(audio.media.to_form_with_key(&name)); + } - if let Some(FileUpload::InputFile(input_file)) = &audio.thumbnail { - let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); - file_idx += 1; + if let Some(thumbnail) = &audio.thumbnail { + if thumbnail.is_input() { + let name = format!("file{file_idx}"); + file_idx += 1; - new_audio.thumbnail = Some(FileUpload::String(attach_name)); - - files.push((name, input_file.path.clone())); - }; + new_audio.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(&name)); + } + } new_medias.push(Media::Audio(new_audio)); } @@ -300,30 +301,27 @@ pub trait AsyncTelegramApi { Media::Document(document) => { let mut new_document = document.clone(); - if let FileUpload::InputFile(input_file) = &document.media { + if document.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_document.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_document.media = FileUpload::String(format!("attach://{name}")); + files.push(document.media.to_form_with_key(&name)); + } new_medias.push(Media::Document(new_document)); } + Media::Photo(photo) => { let mut new_photo = photo.clone(); - if let FileUpload::InputFile(input_file) = &photo.media { + if photo.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_photo.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_photo.media = FileUpload::String(format!("attach://{name}")); + files.push(photo.media.to_form_with_key(&name)); + } new_medias.push(Media::Photo(new_photo)); } @@ -331,25 +329,24 @@ pub trait AsyncTelegramApi { Media::Video(video) => { let mut new_video = video.clone(); - if let FileUpload::InputFile(input_file) = &video.media { + if video.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_video.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_video.media = FileUpload::String(format!("attach://{name}")); + files.push(video.media.to_form_with_key(&name)); + } - if let Some(FileUpload::InputFile(input_file)) = &video.thumbnail { - let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); - file_idx += 1; + if let Some(thumbnail) = &video.thumbnail { + if thumbnail.is_input() { + let name = format!("file{file_idx}"); + file_idx += 1; - new_video.thumbnail = Some(FileUpload::String(attach_name)); - - files.push((name, input_file.path.clone())); - }; + new_video.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(&name)); + } + } new_medias.push(Media::Video(new_video)); } @@ -359,12 +356,7 @@ pub trait AsyncTelegramApi { let mut new_params = params.clone(); new_params.media = new_medias; - let files_with_str_names: Vec<(&str, PathBuf)> = files - .iter() - .map(|(key, path)| (key.as_str(), path.clone())) - .collect(); - - self.request_with_possible_form_data(method_name, &new_params, files_with_str_names) + self.request_with_possible_form_data(method_name, &new_params, files) .await } @@ -373,14 +365,16 @@ pub trait AsyncTelegramApi { params: &SendDocumentParams, ) -> Result, Self::Error> { let method_name = "sendDocument"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.document { - files.push(("document", input_file.path.clone())); + if params.document.is_input() { + files.push(params.document.to_form_with_key("document")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -392,14 +386,16 @@ pub trait AsyncTelegramApi { params: &SendVideoParams, ) -> Result, Self::Error> { let method_name = "sendVideo"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.video { - files.push(("video", input_file.path.clone())); + if params.video.is_input() { + files.push(params.video.to_form_with_key("video")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -411,14 +407,16 @@ pub trait AsyncTelegramApi { params: &SendAnimationParams, ) -> Result, Self::Error> { let method_name = "sendAnimation"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.animation { - files.push(("animation", input_file.path.clone())); + if params.animation.is_input() { + files.push(params.animation.to_form_with_key("animation")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -430,10 +428,10 @@ pub trait AsyncTelegramApi { params: &SendVoiceParams, ) -> Result, Self::Error> { let method_name = "sendVoice"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.voice { - files.push(("voice", input_file.path.clone())); + if params.voice.is_input() { + files.push(params.voice.to_form_with_key("voice")); } self.request_with_possible_form_data(method_name, params, files) @@ -445,14 +443,16 @@ pub trait AsyncTelegramApi { params: &SendVideoNoteParams, ) -> Result, Self::Error> { let method_name = "sendVideoNote"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.video_note { - files.push(("video_note", input_file.path.clone())); + if params.video_note.is_input() { + files.push(params.video_note.to_form_with_key("video_note")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -646,10 +646,14 @@ pub trait AsyncTelegramApi { &self, params: &SetChatPhotoParams, ) -> Result, Self::Error> { - let photo = ¶ms.photo; + let photo = FileUpload::from(¶ms.photo); - self.request_with_form_data("setChatPhoto", params, vec![("photo", photo.path.clone())]) - .await + self.request_with_form_data( + "setChatPhoto", + params, + vec![photo.to_form_with_key("photo")], + ) + .await } async fn delete_chat_photo( @@ -937,28 +941,25 @@ pub trait AsyncTelegramApi { params: &EditMessageMediaParams, ) -> Result { let method_name = "editMessageMedia"; - let mut files: Vec<(String, PathBuf)> = vec![]; + let mut files: Vec = vec![]; let new_media = match ¶ms.media { InputMedia::Animation(animation) => { let mut new_animation = animation.clone(); - if let FileUpload::InputFile(input_file) = &animation.media { - let name = "animation".to_string(); - let attach_name = format!("attach://{name}"); - - new_animation.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &animation.thumbnail { - let name = "animation_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_animation.thumbnail = Some(FileUpload::String(attach_name)); + if animation.media.is_input() { + let name = "animation"; + new_animation.media = FileUpload::String(format!("attach://{name}")); + files.push(animation.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &animation.thumbnail { + if thumbnail.is_input() { + let name = "animation_thumb"; + new_animation.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Animation(new_animation) @@ -966,22 +967,19 @@ pub trait AsyncTelegramApi { InputMedia::Document(document) => { let mut new_document = document.clone(); - if let FileUpload::InputFile(input_file) = &document.media { - let name = "document".to_string(); - let attach_name = format!("attach://{name}"); - - new_document.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &document.thumbnail { - let name = "document_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_document.thumbnail = Some(FileUpload::String(attach_name)); + if document.media.is_input() { + let name = "document"; + new_document.media = FileUpload::String(format!("attach://{name}")); + files.push(document.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &document.thumbnail { + if thumbnail.is_input() { + let name = "document_thumb"; + new_document.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Document(new_document) @@ -989,22 +987,18 @@ pub trait AsyncTelegramApi { InputMedia::Audio(audio) => { let mut new_audio = audio.clone(); - if let FileUpload::InputFile(input_file) = &audio.media { - let name = "audio".to_string(); - let attach_name = format!("attach://{name}"); - - new_audio.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &audio.thumbnail { - let name = "audio_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_audio.thumbnail = Some(FileUpload::String(attach_name)); + if audio.media.is_input() { + let name = "audio"; + new_audio.media = FileUpload::String(format!("attach://{name}")); + files.push(audio.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &audio.thumbnail { + if thumbnail.is_input() { + let name = "audio_thumb"; + new_audio.thumbnail = Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Audio(new_audio) @@ -1012,36 +1006,29 @@ pub trait AsyncTelegramApi { InputMedia::Photo(photo) => { let mut new_photo = photo.clone(); - if let FileUpload::InputFile(input_file) = &photo.media { - let name = "photo".to_string(); - let attach_name = format!("attach://{name}"); - - new_photo.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + if photo.media.is_input() { + let name = "photo"; + new_photo.media = FileUpload::String(format!("attach://{name}")); + files.push(photo.media.to_form_with_key(name)); + } InputMedia::Photo(new_photo) } InputMedia::Video(video) => { let mut new_video = video.clone(); - if let FileUpload::InputFile(input_file) = &video.media { - let name = "video".to_string(); - let attach_name = format!("attach://{name}"); - - new_video.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &video.thumbnail { - let name = "video_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_video.thumbnail = Some(FileUpload::String(attach_name)); + if video.media.is_input() { + let name = "video"; + new_video.media = FileUpload::String(format!("attach://{name}")); + files.push(video.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &video.thumbnail { + if thumbnail.is_input() { + let name = "video_thumb"; + new_video.thumbnail = Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Video(new_video) @@ -1051,12 +1038,7 @@ pub trait AsyncTelegramApi { let mut new_params = params.clone(); new_params.media = new_media; - let files_with_str_names: Vec<(&str, PathBuf)> = files - .iter() - .map(|(key, path)| (key.as_str(), path.clone())) - .collect(); - - self.request_with_possible_form_data(method_name, &new_params, files_with_str_names) + self.request_with_possible_form_data(method_name, &new_params, files) .await } @@ -1093,10 +1075,10 @@ pub trait AsyncTelegramApi { params: &SendStickerParams, ) -> Result, Self::Error> { let method_name = "sendSticker"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.sticker { - files.push(("sticker", input_file.path.clone())); + if params.sticker.is_input() { + files.push(params.sticker.to_form_with_key("sticker")); } self.request_with_possible_form_data(method_name, params, files) @@ -1114,12 +1096,12 @@ pub trait AsyncTelegramApi { &self, params: &UploadStickerFileParams, ) -> Result, Self::Error> { - let sticker = ¶ms.sticker; + let sticker = FileUpload::from(¶ms.sticker); self.request_with_form_data( "uploadStickerFile", params, - vec![("sticker", sticker.path.clone())], + vec![sticker.to_form_with_key("sticker")], ) .await } @@ -1130,21 +1112,20 @@ pub trait AsyncTelegramApi { ) -> Result, Self::Error> { let method_name = "createNewStickerSet"; let mut new_stickers: Vec = vec![]; - let mut files: Vec<(String, PathBuf)> = vec![]; + let mut files: Vec = vec![]; let mut file_idx = 0; for sticker in ¶ms.stickers { let mut new_sticker = sticker.clone(); - if let FileUpload::InputFile(input_file) = &sticker.sticker { + if sticker.sticker.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_sticker.sticker = FileUpload::String(attach_name); + new_sticker.sticker = FileUpload::String(format!("attach://{name}")); - files.push((name, input_file.path.clone())); - }; + files.push(sticker.sticker.to_form_with_key(&name)); + } new_stickers.push(new_sticker); } @@ -1152,12 +1133,7 @@ pub trait AsyncTelegramApi { let mut new_params = params.clone(); new_params.stickers = new_stickers; - let files_with_str_names: Vec<(&str, PathBuf)> = files - .iter() - .map(|(key, path)| (key.as_str(), path.clone())) - .collect(); - - self.request_with_possible_form_data(method_name, &new_params, files_with_str_names) + self.request_with_possible_form_data(method_name, &new_params, files) .await } @@ -1167,15 +1143,16 @@ pub trait AsyncTelegramApi { ) -> Result>, Self::Error> { self.request("getCustomEmojiStickers", Some(params)).await } + async fn add_sticker_to_set( &self, params: &AddStickerToSetParams, ) -> Result, Self::Error> { let method_name = "addStickerToSet"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.sticker.sticker { - files.push(("sticker", input_file.path.clone())); + if params.sticker.sticker.is_input() { + files.push(params.sticker.sticker.to_form_with_key("sticker")); } self.request_with_possible_form_data(method_name, params, files) @@ -1236,10 +1213,12 @@ pub trait AsyncTelegramApi { params: &SetStickerSetThumbnailParams, ) -> Result, Self::Error> { let method_name = "setStickerSetThumbnail"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -1394,7 +1373,7 @@ pub trait AsyncTelegramApi { &self, method_name: &str, params: T1, - files: Vec<(&str, PathBuf)>, + files: Vec, ) -> Result { if files.is_empty() { self.request(method_name, Some(params)).await @@ -1411,6 +1390,6 @@ pub trait AsyncTelegramApi { &self, method: &str, params: T1, - files: Vec<(&str, PathBuf)>, + files: Vec, ) -> Result; } diff --git a/src/api_traits/telegram_api.rs b/src/api_traits/telegram_api.rs index 24aa058..a2a9ec0 100644 --- a/src/api_traits/telegram_api.rs +++ b/src/api_traits/telegram_api.rs @@ -37,6 +37,7 @@ use crate::api_params::EditMessageReplyMarkupParams; use crate::api_params::EditMessageTextParams; use crate::api_params::ExportChatInviteLinkParams; use crate::api_params::FileUpload; +use crate::api_params::FileUploadForm; use crate::api_params::ForwardMessageParams; use crate::api_params::ForwardMessagesParams; use crate::api_params::GetBusinessConnectionParams; @@ -147,7 +148,6 @@ use crate::objects::WebhookInfo; use crate::GetCustomEmojiStickersParams; use crate::Sticker; use crate::UnpinAllGeneralForumTopicMessagesParams; -use std::path::PathBuf; pub trait TelegramApi { type Error; @@ -223,10 +223,10 @@ pub trait TelegramApi { fn send_photo(&self, params: &SendPhotoParams) -> Result, Self::Error> { let method_name = "sendPhoto"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.photo { - files.push(("photo", input_file.path.clone())); + if params.photo.is_input() { + files.push(params.photo.to_form_with_key("photo")); } self.request_with_possible_form_data(method_name, params, files) @@ -234,14 +234,16 @@ pub trait TelegramApi { fn send_audio(&self, params: &SendAudioParams) -> Result, Self::Error> { let method_name = "sendAudio"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.audio { - files.push(("audio", input_file.path.clone())); + if params.audio.is_input() { + files.push(params.audio.to_form_with_key("audio")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -252,7 +254,7 @@ pub trait TelegramApi { params: &SendMediaGroupParams, ) -> Result>, Self::Error> { let method_name = "sendMediaGroup"; - let mut files: Vec<(String, PathBuf)> = vec![]; + let mut files: Vec = vec![]; let mut new_medias: Vec = vec![]; let mut file_idx = 0; @@ -261,25 +263,24 @@ pub trait TelegramApi { Media::Audio(audio) => { let mut new_audio = audio.clone(); - if let FileUpload::InputFile(input_file) = &audio.media { + if audio.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_audio.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_audio.media = FileUpload::String(format!("attach://{name}")); + files.push(audio.media.to_form_with_key(&name)); + } - if let Some(FileUpload::InputFile(input_file)) = &audio.thumbnail { - let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); - file_idx += 1; + if let Some(thumbnail) = &audio.thumbnail { + if thumbnail.is_input() { + let name = format!("file{file_idx}"); + file_idx += 1; - new_audio.thumbnail = Some(FileUpload::String(attach_name)); - - files.push((name, input_file.path.clone())); - }; + new_audio.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(&name)); + } + } new_medias.push(Media::Audio(new_audio)); } @@ -287,30 +288,27 @@ pub trait TelegramApi { Media::Document(document) => { let mut new_document = document.clone(); - if let FileUpload::InputFile(input_file) = &document.media { + if document.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_document.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_document.media = FileUpload::String(format!("attach://{name}")); + files.push(document.media.to_form_with_key(&name)); + } new_medias.push(Media::Document(new_document)); } + Media::Photo(photo) => { let mut new_photo = photo.clone(); - if let FileUpload::InputFile(input_file) = &photo.media { + if photo.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_photo.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_photo.media = FileUpload::String(format!("attach://{name}")); + files.push(photo.media.to_form_with_key(&name)); + } new_medias.push(Media::Photo(new_photo)); } @@ -318,25 +316,24 @@ pub trait TelegramApi { Media::Video(video) => { let mut new_video = video.clone(); - if let FileUpload::InputFile(input_file) = &video.media { + if video.media.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_video.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + new_video.media = FileUpload::String(format!("attach://{name}")); + files.push(video.media.to_form_with_key(&name)); + } - if let Some(FileUpload::InputFile(input_file)) = &video.thumbnail { - let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); - file_idx += 1; + if let Some(thumbnail) = &video.thumbnail { + if thumbnail.is_input() { + let name = format!("file{file_idx}"); + file_idx += 1; - new_video.thumbnail = Some(FileUpload::String(attach_name)); - - files.push((name, input_file.path.clone())); - }; + new_video.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(&name)); + } + } new_medias.push(Media::Video(new_video)); } @@ -346,12 +343,7 @@ pub trait TelegramApi { let mut new_params = params.clone(); new_params.media = new_medias; - let files_with_str_names: Vec<(&str, PathBuf)> = files - .iter() - .map(|(key, path)| (key.as_str(), path.clone())) - .collect(); - - self.request_with_possible_form_data(method_name, &new_params, files_with_str_names) + self.request_with_possible_form_data(method_name, &new_params, files) } fn send_document( @@ -359,14 +351,16 @@ pub trait TelegramApi { params: &SendDocumentParams, ) -> Result, Self::Error> { let method_name = "sendDocument"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.document { - files.push(("document", input_file.path.clone())); + if params.document.is_input() { + files.push(params.document.to_form_with_key("document")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -374,14 +368,16 @@ pub trait TelegramApi { fn send_video(&self, params: &SendVideoParams) -> Result, Self::Error> { let method_name = "sendVideo"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.video { - files.push(("video", input_file.path.clone())); + if params.video.is_input() { + files.push(params.video.to_form_with_key("video")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -392,14 +388,16 @@ pub trait TelegramApi { params: &SendAnimationParams, ) -> Result, Self::Error> { let method_name = "sendAnimation"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.animation { - files.push(("animation", input_file.path.clone())); + if params.animation.is_input() { + files.push(params.animation.to_form_with_key("animation")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -407,10 +405,10 @@ pub trait TelegramApi { fn send_voice(&self, params: &SendVoiceParams) -> Result, Self::Error> { let method_name = "sendVoice"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.voice { - files.push(("voice", input_file.path.clone())); + if params.voice.is_input() { + files.push(params.voice.to_form_with_key("voice")); } self.request_with_possible_form_data(method_name, params, files) @@ -421,14 +419,16 @@ pub trait TelegramApi { params: &SendVideoNoteParams, ) -> Result, Self::Error> { let method_name = "sendVideoNote"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.video_note { - files.push(("video_note", input_file.path.clone())); + if params.video_note.is_input() { + files.push(params.video_note.to_form_with_key("video_note")); } - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -608,9 +608,13 @@ pub trait TelegramApi { &self, params: &SetChatPhotoParams, ) -> Result, Self::Error> { - let photo = ¶ms.photo; + let photo = FileUpload::from(¶ms.photo); - self.request_with_form_data("setChatPhoto", params, vec![("photo", photo.path.clone())]) + self.request_with_form_data( + "setChatPhoto", + params, + vec![photo.to_form_with_key("photo")], + ) } fn delete_chat_photo( @@ -889,28 +893,25 @@ pub trait TelegramApi { params: &EditMessageMediaParams, ) -> Result { let method_name = "editMessageMedia"; - let mut files: Vec<(String, PathBuf)> = vec![]; + let mut files: Vec = vec![]; let new_media = match ¶ms.media { InputMedia::Animation(animation) => { let mut new_animation = animation.clone(); - if let FileUpload::InputFile(input_file) = &animation.media { - let name = "animation".to_string(); - let attach_name = format!("attach://{name}"); - - new_animation.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &animation.thumbnail { - let name = "animation_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_animation.thumbnail = Some(FileUpload::String(attach_name)); + if animation.media.is_input() { + let name = "animation"; + new_animation.media = FileUpload::String(format!("attach://{name}")); + files.push(animation.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &animation.thumbnail { + if thumbnail.is_input() { + let name = "animation_thumb"; + new_animation.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Animation(new_animation) @@ -918,22 +919,19 @@ pub trait TelegramApi { InputMedia::Document(document) => { let mut new_document = document.clone(); - if let FileUpload::InputFile(input_file) = &document.media { - let name = "document".to_string(); - let attach_name = format!("attach://{name}"); - - new_document.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &document.thumbnail { - let name = "document_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_document.thumbnail = Some(FileUpload::String(attach_name)); + if document.media.is_input() { + let name = "document"; + new_document.media = FileUpload::String(format!("attach://{name}")); + files.push(document.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &document.thumbnail { + if thumbnail.is_input() { + let name = "document_thumb"; + new_document.thumbnail = + Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Document(new_document) @@ -941,22 +939,18 @@ pub trait TelegramApi { InputMedia::Audio(audio) => { let mut new_audio = audio.clone(); - if let FileUpload::InputFile(input_file) = &audio.media { - let name = "audio".to_string(); - let attach_name = format!("attach://{name}"); - - new_audio.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &audio.thumbnail { - let name = "audio_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_audio.thumbnail = Some(FileUpload::String(attach_name)); + if audio.media.is_input() { + let name = "audio"; + new_audio.media = FileUpload::String(format!("attach://{name}")); + files.push(audio.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &audio.thumbnail { + if thumbnail.is_input() { + let name = "audio_thumb"; + new_audio.thumbnail = Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Audio(new_audio) @@ -964,36 +958,29 @@ pub trait TelegramApi { InputMedia::Photo(photo) => { let mut new_photo = photo.clone(); - if let FileUpload::InputFile(input_file) = &photo.media { - let name = "photo".to_string(); - let attach_name = format!("attach://{name}"); - - new_photo.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; + if photo.media.is_input() { + let name = "photo"; + new_photo.media = FileUpload::String(format!("attach://{name}")); + files.push(photo.media.to_form_with_key(name)); + } InputMedia::Photo(new_photo) } InputMedia::Video(video) => { let mut new_video = video.clone(); - if let FileUpload::InputFile(input_file) = &video.media { - let name = "video".to_string(); - let attach_name = format!("attach://{name}"); - - new_video.media = FileUpload::String(attach_name); - - files.push((name, input_file.path.clone())); - }; - - if let Some(FileUpload::InputFile(input_file)) = &video.thumbnail { - let name = "video_thumb".to_string(); - let attach_name = format!("attach://{name}"); - - new_video.thumbnail = Some(FileUpload::String(attach_name)); + if video.media.is_input() { + let name = "video"; + new_video.media = FileUpload::String(format!("attach://{name}")); + files.push(video.media.to_form_with_key(name)); + } - files.push((name, input_file.path.clone())); + if let Some(thumbnail) = &video.thumbnail { + if thumbnail.is_input() { + let name = "video_thumb"; + new_video.thumbnail = Some(FileUpload::String(format!("attach://{name}"))); + files.push(thumbnail.to_form_with_key(name)); + } }; InputMedia::Video(new_video) @@ -1003,12 +990,7 @@ pub trait TelegramApi { let mut new_params = params.clone(); new_params.media = new_media; - let files_with_str_names: Vec<(&str, PathBuf)> = files - .iter() - .map(|(key, path)| (key.as_str(), path.clone())) - .collect(); - - self.request_with_possible_form_data(method_name, &new_params, files_with_str_names) + self.request_with_possible_form_data(method_name, &new_params, files) } fn edit_message_reply_markup( @@ -1041,10 +1023,10 @@ pub trait TelegramApi { params: &SendStickerParams, ) -> Result, Self::Error> { let method_name = "sendSticker"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.sticker { - files.push(("sticker", input_file.path.clone())); + if params.sticker.is_input() { + files.push(params.sticker.to_form_with_key("sticker")); } self.request_with_possible_form_data(method_name, params, files) @@ -1061,12 +1043,11 @@ pub trait TelegramApi { &self, params: &UploadStickerFileParams, ) -> Result, Self::Error> { - let sticker = ¶ms.sticker; - + let sticker = FileUpload::from(¶ms.sticker); self.request_with_form_data( "uploadStickerFile", params, - vec![("sticker", sticker.path.clone())], + vec![sticker.to_form_with_key("sticker")], ) } @@ -1076,21 +1057,20 @@ pub trait TelegramApi { ) -> Result, Self::Error> { let method_name = "createNewStickerSet"; let mut new_stickers: Vec = vec![]; - let mut files: Vec<(String, PathBuf)> = vec![]; + let mut files: Vec = vec![]; let mut file_idx = 0; for sticker in ¶ms.stickers { let mut new_sticker = sticker.clone(); - if let FileUpload::InputFile(input_file) = &sticker.sticker { + if sticker.sticker.is_input() { let name = format!("file{file_idx}"); - let attach_name = format!("attach://{name}"); file_idx += 1; - new_sticker.sticker = FileUpload::String(attach_name); + new_sticker.sticker = FileUpload::String(format!("attach://{name}")); - files.push((name, input_file.path.clone())); - }; + files.push(sticker.sticker.to_form_with_key(&name)); + } new_stickers.push(new_sticker); } @@ -1098,12 +1078,7 @@ pub trait TelegramApi { let mut new_params = params.clone(); new_params.stickers = new_stickers; - let files_with_str_names: Vec<(&str, PathBuf)> = files - .iter() - .map(|(key, path)| (key.as_str(), path.clone())) - .collect(); - - self.request_with_possible_form_data(method_name, &new_params, files_with_str_names) + self.request_with_possible_form_data(method_name, &new_params, files) } fn get_custom_emoji_stickers( @@ -1118,10 +1093,10 @@ pub trait TelegramApi { params: &AddStickerToSetParams, ) -> Result, Self::Error> { let method_name = "addStickerToSet"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let FileUpload::InputFile(input_file) = ¶ms.sticker.sticker { - files.push(("sticker", input_file.path.clone())); + if params.sticker.sticker.is_input() { + files.push(params.sticker.sticker.to_form_with_key("sticker")); } self.request_with_possible_form_data(method_name, params, files) @@ -1181,10 +1156,12 @@ pub trait TelegramApi { params: &SetStickerSetThumbnailParams, ) -> Result, Self::Error> { let method_name = "setStickerSetThumbnail"; - let mut files: Vec<(&str, PathBuf)> = vec![]; + let mut files: Vec = vec![]; - if let Some(FileUpload::InputFile(input_file)) = ¶ms.thumbnail { - files.push(("thumbnail", input_file.path.clone())); + if let Some(thumbnail) = ¶ms.thumbnail { + if thumbnail.is_input() { + files.push(thumbnail.to_form_with_key("thumbnail")); + } } self.request_with_possible_form_data(method_name, params, files) @@ -1322,7 +1299,7 @@ pub trait TelegramApi { &self, method_name: &str, params: &T1, - files: Vec<(&str, PathBuf)>, + files: Vec, ) -> Result { if files.is_empty() { self.request(method_name, Some(params)) @@ -1338,7 +1315,7 @@ pub trait TelegramApi { &self, method: &str, params: T1, - files: Vec<(&str, PathBuf)>, + files: Vec, ) -> Result; fn request(