diff --git a/Cargo.toml b/Cargo.toml index 8d8f824..cd2b812 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "telexide" -version = "0.1.11" +version = "0.1.12" description = "An async Rust library for the telegram bot API." documentation = "https://docs.rs/telexide" repository = "https://github.com/callieve/telexide" diff --git a/README.md b/README.md index 0b8fa05..90927f7 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Add the following to your `Cargo.toml` file: ```toml [dependencies] -telexide = "0.1.11" +telexide = "0.1.12" ``` ## Supported Rust Versions diff --git a/examples/repeat_image/Cargo.toml b/examples/repeat_image/Cargo.toml index ccb4c98..5cc9357 100644 --- a/examples/repeat_image/Cargo.toml +++ b/examples/repeat_image/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] telexide = { path = "../../" } tokio = { version = "1", features = ["full"] } -typemap_rev = "0.2" +typemap_rev = "0.3.0" parking_lot = "0.12" diff --git a/examples/repeat_image/src/main.rs b/examples/repeat_image/src/main.rs index 7fb0d2c..6236d3e 100644 --- a/examples/repeat_image/src/main.rs +++ b/examples/repeat_image/src/main.rs @@ -48,9 +48,7 @@ async fn handle_next(context: Context, update: Update) { } let image = match message.content { - MessageContent::Photo { - ref content, .. - } => content.first(), + MessageContent::Photo { ref content, .. } => content.first(), _ => return, }; diff --git a/examples/upload_image/Cargo.toml b/examples/upload_image/Cargo.toml index bf67b6d..754d88c 100644 --- a/examples/upload_image/Cargo.toml +++ b/examples/upload_image/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] telexide = { path = "../../" } tokio = { version = "1", features = ["full"] } -env_logger = "0.9" +env_logger = "0.10.0" log = "0.4" diff --git a/src/api/api.rs b/src/api/api.rs index 0371d73..727a62a 100644 --- a/src/api/api.rs +++ b/src/api/api.rs @@ -133,6 +133,54 @@ pub trait API: Sync { .into() } + /// Use this method to change the bot's description, which is shown in the + /// chat with the bot if the chat is empty. Returns True on success. + async fn set_my_description(&self, data: SetMyDescription) -> Result { + self.post( + APIEndpoint::SetMyDescription, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to get the current bot description for the given user + /// language. Returns [`BotDescription`] on success. + async fn get_my_description(&self, data: GetMyDescription) -> Result { + self.get( + APIEndpoint::GetMyDescription, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to change the bot's short description, which is shown on + /// the bot's profile page and is sent together with the link when users + /// share the bot. Returns True on success. + async fn set_my_short_description(&self, data: SetMyShortDescription) -> Result { + self.post( + APIEndpoint::SetMyShortDescription, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to get the current bot short description for the given + /// user language. Returns [`BotShortDescription`] on success. + async fn get_my_short_description( + &self, + data: GetMyShortDescription, + ) -> Result { + self.get( + APIEndpoint::GetMyShortDescription, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + /// Use this method to change the bot's menu button in a private chat, or /// the default menu button. Returns True on success. async fn set_chat_menu_button(&self, data: SetChatMenuButton) -> Result { @@ -252,8 +300,8 @@ pub trait API: Sync { if let InputFile::File(f) = &data.audio { files.push(f.clone()); } - if data.thumb.is_some() { - if let InputFile::File(f) = data.thumb.as_ref().unwrap() { + if data.thumbnail.is_some() { + if let InputFile::File(f) = data.thumbnail.as_ref().unwrap() { files.push(f.clone()); } } @@ -276,8 +324,8 @@ pub trait API: Sync { files.push(f.clone()); } - if data.thumb.is_some() { - if let InputFile::File(f) = data.thumb.as_ref().unwrap() { + if data.thumbnail.is_some() { + if let InputFile::File(f) = data.thumbnail.as_ref().unwrap() { files.push(f.clone()); } } @@ -301,8 +349,8 @@ pub trait API: Sync { files.push(f.clone()); } - if data.thumb.is_some() { - if let InputFile::File(f) = data.thumb.as_ref().unwrap() { + if data.thumbnail.is_some() { + if let InputFile::File(f) = data.thumbnail.as_ref().unwrap() { files.push(f.clone()); } } @@ -326,8 +374,8 @@ pub trait API: Sync { files.push(f.clone()); } - if data.thumb.is_some() { - if let InputFile::File(f) = data.thumb.as_ref().unwrap() { + if data.thumbnail.is_some() { + if let InputFile::File(f) = data.thumbnail.as_ref().unwrap() { files.push(f.clone()); } } @@ -371,8 +419,8 @@ pub trait API: Sync { files.push(f.clone()); } - if data.thumb.is_some() { - if let InputFile::File(f) = data.thumb.as_ref().unwrap() { + if data.thumbnail.is_some() { + if let InputFile::File(f) = data.thumbnail.as_ref().unwrap() { files.push(f.clone()); } } @@ -1211,7 +1259,7 @@ pub trait API: Sync { /// createNewStickerSet and addStickerToSet methods (can be used /// multiple times). Returns the uploaded [File] on success. async fn upload_sticker_file(&self, data: UploadStickerFile) -> Result { - match &data.png_sticker { + match &data.sticker { InputFile::File(f) => self .post_file( APIEndpoint::UploadStickerFile, @@ -1232,32 +1280,25 @@ pub trait API: Sync { /// You must use exactly one of the fields png_sticker or tgs_sticker. /// Returns True on success. async fn create_new_sticker_set(&self, data: CreateNewStickerSet) -> Result { - if (data.png_sticker.is_some() && data.tgs_sticker.is_some()) - || (data.png_sticker.is_none() && data.tgs_sticker.is_none()) - { + if data.stickers.is_empty() || data.stickers.len() > 50 { return Err(TelegramError::InvalidArgument( - "You must use exactly one of the fields png_sticker or tgs_sticker".to_owned(), + "You must pass between 1 and 50 initial stickers for the set".to_owned(), ) .into()); } let mut files = Vec::new(); - if data.png_sticker.is_some() { - if let InputFile::File(f) = data.png_sticker.as_ref().unwrap() { - files.push(f.clone()); - } - } - - if data.tgs_sticker.is_some() { - match data.tgs_sticker.as_ref().unwrap() { - InputFile::File(f) => files.push(f.clone()), - InputFile::String(_) => { + for sticker in &data.stickers { + match sticker.sticker { + InputFile::File(ref f) => files.push(f.clone()), + InputFile::String(_) if data.sticker_format != StickerFormat::Static => { return Err(TelegramError::InvalidArgument( - "tgs_sticker only accepts files, not urls/ids".to_owned(), + "video or animated stickers only accept files, not urls/ids".to_owned(), ) .into()) }, + InputFile::String(_) => {}, } } @@ -1276,33 +1317,9 @@ pub trait API: Sync { /// them. Animated sticker sets can have up to 50 stickers. Static /// sticker sets can have up to 120 stickers. Returns True on success. async fn add_sticker_to_set(&self, data: AddStickerToSet) -> Result { - if (data.png_sticker.is_some() && data.tgs_sticker.is_some()) - || (data.png_sticker.is_none() && data.tgs_sticker.is_none()) - { - return Err(TelegramError::InvalidArgument( - "You must use exactly one of the fields png_sticker or tgs_sticker.".to_owned(), - ) - .into()); - } - let mut files = Vec::new(); - - if data.png_sticker.is_some() { - if let InputFile::File(f) = data.png_sticker.as_ref().unwrap() { - files.push(f.clone()); - } - } - - if data.tgs_sticker.is_some() { - match data.tgs_sticker.as_ref().unwrap() { - InputFile::File(f) => files.push(f.clone()), - InputFile::String(_) => { - return Err(TelegramError::InvalidArgument( - "tgs_sticker only accepts files, not urls/ids.".to_owned(), - ) - .into()) - }, - } + if let InputFile::File(ref f) = data.sticker.sticker { + files.push(f.clone()); } self.post_file( @@ -1336,21 +1353,70 @@ pub trait API: Sync { .into() } + /// Use this method to change the list of emoji assigned to a regular or + /// custom emoji sticker. The sticker must belong to a sticker set + /// created by the bot. Returns True on success. + async fn set_sticker_emoji_list(&self, data: SetStickerEmojiList) -> Result { + self.post( + APIEndpoint::SetStickerEmojiList, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to change search keywords assigned to a regular or + /// custom emoji sticker. The sticker must belong to a sticker set + /// created by the bot. Returns True on success. + async fn set_sticker_keywords(&self, data: SetStickerKeywords) -> Result { + self.post( + APIEndpoint::SetStickerKeywords, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to change the [mask position] of a mask sticker. The + /// sticker must belong to a sticker set that was created by the bot. + /// Returns True on success. + /// + /// [mask position]: https://core.telegram.org/bots/api#maskposition + async fn set_sticker_mask_position(&self, data: SetStickerMaskPosition) -> Result { + self.post( + APIEndpoint::SetStickerMaskPosition, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to set the title of a created sticker set. Returns True + /// on success. + async fn set_sticker_set_title(&self, data: SetStickerSetTitle) -> Result { + self.post( + APIEndpoint::SetStickerSetTitle, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + /// Use this method to set the thumbnail of a sticker set. /// Animated thumbnails can be set for animated sticker sets only. Returns /// True on success. - async fn set_sticker_set_thumb(&self, data: SetStickerSetThumb) -> Result { - match &data.thumb { + async fn set_sticker_set_thumbnail(&self, data: SetStickerSetThumbnail) -> Result { + match &data.thumbnail { Some(InputFile::String(_)) | None => self .post( - APIEndpoint::SetStickerSetThumb, + APIEndpoint::SetStickerSetThumbnail, Some(serde_json::to_value(&data)?), ) .await? .into(), Some(InputFile::File(f)) => self .post_file( - APIEndpoint::SetStickerSetThumb, + APIEndpoint::SetStickerSetThumbnail, Some(serde_json::to_value(&data)?), Some(vec![f.clone()]), ) @@ -1359,6 +1425,31 @@ pub trait API: Sync { } } + /// Use this method to set the thumbnail of a custom emoji sticker set. + /// Returns True on success. + async fn set_custom_emoji_sticker_set_thumbnail( + &self, + data: SetCustomEmojiStickerSetThumbnail, + ) -> Result { + self.post( + APIEndpoint::SetCustomEmojiStickerSetThumbnail, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + + /// Use this method to delete a sticker set that was created by the bot. + /// Returns True on success. + async fn delete_sticker_set(&self, data: DeleteStickerSet) -> Result { + self.post( + APIEndpoint::DeleteStickerSet, + Some(serde_json::to_value(data)?), + ) + .await? + .into() + } + /// Use this method to send answers to an inline query. On success, True is /// returned. No more than 50 results per query are allowed. async fn answer_inline_query(&self, data: AnswerInlineQuery) -> Result { diff --git a/src/api/api_client.rs b/src/api/api_client.rs index 2568284..d971b50 100644 --- a/src/api/api_client.rs +++ b/src/api/api_client.rs @@ -1,10 +1,6 @@ use super::{api::API, endpoints::APIEndpoint, response::Response}; use crate::utils::{ - encode_multipart_form_data, - result::Result, - AsFormData, - FormDataFile, - BOUNDARY, + encode_multipart_form_data, result::Result, AsFormData, FormDataFile, BOUNDARY, }; use async_trait::async_trait; use hyper::{body::HttpBody, client::HttpConnector, Body, Client, Request}; @@ -169,7 +165,7 @@ impl API for APIClient { let req_builder = Request::post(self.parse_endpoint(&endpoint)) .header( "content-type", - format!("multipart/form-data; boundary={}", BOUNDARY), + format!("multipart/form-data; boundary={BOUNDARY}"), ) .header("accept", "application/json"); diff --git a/src/api/endpoints.rs b/src/api/endpoints.rs index 542704f..f93f7fa 100644 --- a/src/api/endpoints.rs +++ b/src/api/endpoints.rs @@ -10,6 +10,10 @@ pub enum APIEndpoint { SendMessage, SetMyCommands, GetMyCommands, + SetMyDescription, + GetMyDescription, + SetMyShortDescription, + GetMyShortDescription, SetChatMenuButton, GetChatMenuButton, SetMyDefaultAdministratorRights, @@ -90,7 +94,13 @@ pub enum APIEndpoint { AddStickerToSet, SetStickerPositionInSet, DeleteStickerFromSet, - SetStickerSetThumb, + SetStickerEmojiList, + SetStickerKeywords, + SetStickerMaskPosition, + SetStickerSetTitle, + SetStickerSetThumbnail, + SetCustomEmojiStickerSetThumbnail, + DeleteStickerSet, AnswerInlineQuery, AnswerWebAppQuery, SendInvoice, @@ -117,6 +127,10 @@ impl APIEndpoint { Self::SendMessage => "sendMessage", Self::SetMyCommands => "setMyCommands", Self::GetMyCommands => "getMyCommands", + Self::SetMyDescription => "setMyDescription", + Self::GetMyDescription => "getMyDescription", + Self::SetMyShortDescription => "setMyShortDescription", + Self::GetMyShortDescription => "getMyShortDescription", Self::SetChatMenuButton => "setChatMenuButton", Self::GetChatMenuButton => "getChatMenuButton", Self::SetMyDefaultAdministratorRights => "setMyDefaultAdministratorRights", @@ -197,7 +211,13 @@ impl APIEndpoint { Self::AddStickerToSet => "addStickerToSet", Self::SetStickerPositionInSet => "setStickerPositionInSet", Self::DeleteStickerFromSet => "deleteStickerFromSet", - Self::SetStickerSetThumb => "setStickerSetThumb", + Self::SetStickerEmojiList => "setStickerEmojiList", + Self::SetStickerKeywords => "setStickerKeywords", + Self::SetStickerMaskPosition => "setStickerMaskPosition", + Self::SetStickerSetTitle => "setStickerSetTitle", + Self::SetStickerSetThumbnail => "setStickerSetThumbnail", + Self::SetCustomEmojiStickerSetThumbnail => "setCustomEmojiStickerSetThumbnail", + Self::DeleteStickerSet => "deleteStickerSet", Self::AnswerInlineQuery => "answerInlineQuery", Self::AnswerWebAppQuery => "answerWebAppQuery", Self::SendGame => "sendGame", diff --git a/src/api/types/inline.rs b/src/api/types/inline.rs index 1aea47b..0df0e2d 100644 --- a/src/api/types/inline.rs +++ b/src/api/types/inline.rs @@ -108,13 +108,13 @@ pub struct InlineQueryResultArticle { pub description: Option, /// Url of the thumbnail for the result #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_url: Option, + pub thumbnail_url: Option, /// Thumbnail width #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_width: Option, + pub thumbnail_width: Option, /// Thumbnail height #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_height: Option, + pub thumbnail_height: Option, } /// Represents a link to a photo. By default, this photo will be sent by the @@ -130,7 +130,7 @@ pub struct InlineQueryResultPhoto { /// not exceed 5MB pub photo_url: String, /// Url of the thumbnail for the photo - pub thumb_url: String, + pub thumbnail_url: String, /// Photo width #[serde(skip_serializing_if = "Option::is_none")] pub photo_width: Option, @@ -176,7 +176,7 @@ pub struct InlineQueryResultGif { pub gif_url: String, /// URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the /// result - pub thumb_url: String, + pub thumbnail_url: String, /// Width of the GIF #[serde(skip_serializing_if = "Option::is_none")] pub gif_width: Option, @@ -189,7 +189,7 @@ pub struct InlineQueryResultGif { /// MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or /// “video/mp4”. Defaults to “image/jpeg” #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_mime_type: Option, + pub thumbnail_mime_type: Option, /// Title of the result #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, @@ -225,7 +225,7 @@ pub struct InlineQueryResultMpeg4Gif { pub mpeg4_url: String, /// URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the /// result - pub thumb_url: String, + pub thumbnail_url: String, /// Width of the video #[serde(skip_serializing_if = "Option::is_none")] pub mpeg4_width: Option, @@ -238,7 +238,7 @@ pub struct InlineQueryResultMpeg4Gif { /// MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or /// “video/mp4”. Defaults to “image/jpeg” #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_mime_type: Option, + pub thumbnail_mime_type: Option, /// Title of the result #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, @@ -274,7 +274,7 @@ pub struct InlineQueryResultVideo { /// A valid URL for the embedded video player or video file pub video_url: String, /// URL of the thumbnail (jpeg only) for the video - pub thumb_url: String, + pub thumbnail_url: String, /// Mime type of the content of video url, “text/html” or “video/mp4” pub mime_type: String, /// Width of the video @@ -427,13 +427,13 @@ pub struct InlineQueryResultDocument { pub reply_markup: Option, /// URL of the thumbnail (jpeg only) for the file #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_url: Option, + pub thumbnail_url: Option, /// Thumbnail width #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_width: Option, + pub thumbnail_width: Option, /// Thumbnail height #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_height: Option, + pub thumbnail_height: Option, } /// Represents a location on a map. By default, the location will be sent by the @@ -474,13 +474,13 @@ pub struct InlineQueryResultLocation { pub reply_markup: Option, /// Url of the thumbnail for the result #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_url: Option, + pub thumbnail_url: Option, /// Thumbnail width #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_width: Option, + pub thumbnail_width: Option, /// Thumbnail height #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_height: Option, + pub thumbnail_height: Option, } /// Represents a venue. By default, the venue will be sent by the user. @@ -527,13 +527,13 @@ pub struct InlineQueryResultVenue { pub reply_markup: Option, /// Url of the thumbnail for the result #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_url: Option, + pub thumbnail_url: Option, /// Thumbnail width #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_width: Option, + pub thumbnail_width: Option, /// Thumbnail height #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_height: Option, + pub thumbnail_height: Option, } /// Represents a contact with a phone number. By default, this contact will be @@ -562,13 +562,13 @@ pub struct InlineQueryResultContact { pub reply_markup: Option, /// Url of the thumbnail for the result #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_url: Option, + pub thumbnail_url: Option, /// Thumbnail width #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_width: Option, + pub thumbnail_width: Option, /// Thumbnail height #[serde(skip_serializing_if = "Option::is_none")] - pub thumb_height: Option, + pub thumbnail_height: Option, } /// Represents a Game. diff --git a/src/api/types/other.rs b/src/api/types/other.rs index fd95236..97bd62e 100644 --- a/src/api/types/other.rs +++ b/src/api/types/other.rs @@ -227,3 +227,67 @@ pub struct GetMyDefaultAdministratorRights { #[serde(skip_serializing_if = "Option::is_none")] pub channels: Option, } + +/// struct for holding data needed to call +/// [`set_my_description`] +/// +/// [`set_my_description`]: +/// ../../api/trait.API.html#method.set_my_description +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct SetMyDescription { + /// New bot description; 0-512 characters. Pass an empty string to remove + /// the dedicated description for the given language. + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + /// A two-letter ISO 639-1 language code. If empty, the description will be + /// applied to all users for whose language there is no dedicated + /// description. + #[serde(skip_serializing_if = "Option::is_none")] + pub language_code: Option, +} + +/// struct for holding data needed to call +/// [`get_my_description`] +/// +/// [`get_my_description`]: +/// ../../api/trait.API.html#method.get_my_description +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct GetMyDescription { + /// A two-letter ISO 639-1 language code + #[serde(skip_serializing_if = "Option::is_none")] + pub language_code: Option, +} + +/// struct for holding data needed to call +/// [`set_my_short_description`] +/// +/// [`set_my_short_description`]: +/// ../../api/trait.API.html#method.set_my_short_description +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct SetMyShortDescription { + /// New bot description; 0-120 characters. Pass an empty string to remove + /// the dedicated description for the given language. + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + /// A two-letter ISO 639-1 language code. If empty, the description will be + /// applied to all users for whose language there is no dedicated + /// description. + #[serde(skip_serializing_if = "Option::is_none")] + pub language_code: Option, +} + +/// struct for holding data needed to call +/// [`get_my_short_description`] +/// +/// [`get_my_short_description`]: +/// ../../api/trait.API.html#method.get_my_short_description +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct GetMyShortDescription { + /// A two-letter ISO 639-1 language code + #[serde(skip_serializing_if = "Option::is_none")] + pub language_code: Option, +} diff --git a/src/api/types/send_messages.rs b/src/api/types/send_messages.rs index 2bceb10..ddd202a 100644 --- a/src/api/types/send_messages.rs +++ b/src/api/types/send_messages.rs @@ -280,7 +280,7 @@ pub struct SendAudio { /// should not exceed 320. Ignored if the file is not uploaded using /// multipart/form-data. #[serde(skip_serializing_if = "Option::is_none")] - pub thumb: Option, + pub thumbnail: Option, /// Audio caption (may also be used when resending audio files by file_id), /// 0-1024 characters after entities parsing #[serde(skip_serializing_if = "Option::is_none")] @@ -327,7 +327,7 @@ impl SendAudio { chat_id, audio: InputFile::from_path(path)?, message_thread_id: None, - thumb: None, + thumbnail: None, caption: None, caption_entities: None, performer: None, @@ -367,7 +367,7 @@ pub struct SendDocument { /// should not exceed 320. Ignored if the file is not uploaded using /// multipart/form-data. #[serde(skip_serializing_if = "Option::is_none")] - pub thumb: Option, + pub thumbnail: Option, /// Document caption (may also be used when resending documents by file_id), /// 0-1024 characters after entities parsing #[serde(skip_serializing_if = "Option::is_none")] @@ -409,7 +409,7 @@ impl SendDocument { chat_id, document: InputFile::from_path(path)?, message_thread_id: None, - thumb: None, + thumbnail: None, caption: None, caption_entities: None, parse_mode: None, @@ -447,7 +447,7 @@ pub struct SendVideo { /// should not exceed 320. Ignored if the file is not uploaded using /// multipart/form-data. #[serde(skip_serializing_if = "Option::is_none")] - pub thumb: Option, + pub thumbnail: Option, /// Video caption (may also be used when resending video files by file_id), /// 0-1024 characters after entities parsing #[serde(skip_serializing_if = "Option::is_none")] @@ -506,7 +506,7 @@ impl SendVideo { chat_id, video: InputFile::from_path(path)?, message_thread_id: None, - thumb: None, + thumbnail: None, caption: None, caption_entities: None, duration: None, @@ -550,7 +550,7 @@ pub struct SendAnimation { /// should not exceed 320. Ignored if the file is not uploaded using /// multipart/form-data. #[serde(skip_serializing_if = "Option::is_none")] - pub thumb: Option, + pub thumbnail: Option, /// Animation caption (may also be used when resending animation files by /// file_id), 0-1024 characters after entities parsing #[serde(skip_serializing_if = "Option::is_none")] @@ -606,7 +606,7 @@ impl SendAnimation { chat_id, animation: InputFile::from_path(path)?, message_thread_id: None, - thumb: None, + thumbnail: None, caption: None, caption_entities: None, duration: None, @@ -720,7 +720,7 @@ pub struct SendVideoNote { /// should not exceed 320. Ignored if the file is not uploaded using /// multipart/form-data. #[serde(skip_serializing_if = "Option::is_none")] - pub thumb: Option, + pub thumbnail: Option, /// Duration of the voice message in seconds #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, @@ -752,7 +752,7 @@ impl SendVideoNote { chat_id, video_note: InputFile::from_path(path)?, message_thread_id: None, - thumb: None, + thumbnail: None, duration: None, length: None, disable_notification: None, diff --git a/src/api/types/stickers.rs b/src/api/types/stickers.rs index cef5391..2919dcc 100644 --- a/src/api/types/stickers.rs +++ b/src/api/types/stickers.rs @@ -1,5 +1,12 @@ use super::InputFile; -use crate::model::{utils::IntegerOrString, MaskPosition, ReplyMarkup, StickerType}; +use crate::model::{ + utils::IntegerOrString, + InputSticker, + MaskPosition, + ReplyMarkup, + StickerFormat, + StickerType, +}; use serde::{Deserialize, Serialize}; use telexide_proc_macros::build_struct; @@ -21,6 +28,9 @@ pub struct SendSticker { /// the Telegram servers (recommended), pass an HTTP URL as a String for /// Telegram to get a .WEBP file from the Internet, or upload a new one pub sticker: InputFile, + /// Emoji associated with the sticker; only for just uploaded stickers + #[serde(skip_serializing_if = "Option::is_none")] + pub emoji: Option, /// Sends the message silently. Users will receive a notification with no /// sound. #[serde(skip_serializing_if = "Option::is_none")] @@ -31,6 +41,10 @@ pub struct SendSticker { /// If the message is a reply, ID of the original message #[serde(skip_serializing_if = "Option::is_none")] pub reply_to_message_id: Option, + /// Pass True if the message should be sent even if the specified replied-to + /// message is not found + #[serde(skip_serializing_if = "Option::is_none")] + pub allow_sending_without_reply: Option, /// Additional interface options. #[serde(skip_serializing_if = "Option::is_none")] pub reply_markup: Option, @@ -58,10 +72,11 @@ pub struct GetStickerSet { pub struct UploadStickerFile { /// User identifier of sticker file owner pub user_id: i64, - /// Png image with the sticker, must be up to 512 kilobytes in size, - /// dimensions must not exceed 512px, and either width or height must be - /// exactly 512px. - pub png_sticker: InputFile, + /// A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. + /// See https://core.telegram.org/stickers for technical requirements. + pub sticker: InputFile, + /// Format of the sticker + pub sticker_format: StickerFormat, } /// struct for holding data needed to call @@ -82,30 +97,20 @@ pub struct CreateNewStickerSet { pub name: String, /// Sticker set title, 1-64 characters pub title: String, - /// PNG image with the sticker, must be up to 512 kilobytes in size, - /// dimensions must not exceed 512px, and either width or height must be - /// exactly 512px. Pass a file_id as a String to send a file that - /// already exists on the Telegram servers, ass an HTTP URL as a String - /// for Telegram to get a file from the Internet, or upload a new one - #[serde(skip_serializing_if = "Option::is_none")] - pub png_sticker: Option, - /// TGS animation with the sticker, uploaded using multipart/form-data. - /// See for technical requirements - #[serde(skip_serializing_if = "Option::is_none")] - pub tgs_sticker: Option, - /// WEBM video with the sticker, uploaded using multipart/form-data. See for technical requirements - #[serde(skip_serializing_if = "Option::is_none")] - pub webm_sticker: Option, + /// A list of 1-50 initial stickers to be added to the sticker set + pub stickers: Vec, + /// Format of stickers in the set + pub sticker_format: StickerFormat, /// Type of stickers in the set, pass “regular” or “mask”. Custom emoji /// sticker sets can't be created via the Bot API at the moment. By default, /// a regular sticker set is created. #[serde(skip_serializing_if = "Option::is_none")] pub sticker_type: Option, - /// One or more emoji corresponding to the sticker - pub emojis: String, - /// position where the mask should be placed on faces - #[serde(skip_serializing_if = "Option::is_none")] - pub mask_position: Option, + /// Pass true if stickers in the sticker set must be repainted to the color + /// of text when used in messages, the accent color if used as emoji status, + /// white on chat photos, or another appropriate color based on context; for + /// custom emoji sticker sets only + pub needs_repainting: Option, } /// struct for holding data needed to call @@ -120,25 +125,9 @@ pub struct AddStickerToSet { pub user_id: i64, /// Name of the sticker set pub name: String, - /// PNG image with the sticker, must be up to 512 kilobytes in size, - /// dimensions must not exceed 512px, and either width or height must be - /// exactly 512px. Pass a file_id as a String to send a file that - /// already exists on the Telegram servers, ass an HTTP URL as a String - /// for Telegram to get a file from the Internet, or upload a new one - #[serde(skip_serializing_if = "Option::is_none")] - pub png_sticker: Option, - /// TGS animation with the sticker, uploaded using multipart/form-data. - /// See for technical requirements - #[serde(skip_serializing_if = "Option::is_none")] - pub tgs_sticker: Option, - /// WEBM video with the sticker, uploaded using multipart/form-data. See for technical requirements - #[serde(skip_serializing_if = "Option::is_none")] - pub webm_sticker: Option, - /// One or more emoji corresponding to the sticker - pub emojis: String, - /// position where the mask should be placed on faces - #[serde(skip_serializing_if = "Option::is_none")] - pub mask_position: Option, + /// An object with information about the added sticker. If exactly the same + /// sticker had already been added to the set, then the set isn't changed. + pub sticker: InputSticker, } /// struct for holding data needed to call @@ -168,26 +157,112 @@ pub struct DeleteStickerFromSet { } /// struct for holding data needed to call -/// [`set_sticker_set_thumb`] +/// [`set_sticker_emoji_list`] +/// +/// [`set_sticker_emoji_list`]: +/// ../../api/trait.API.html#method.set_sticker_emoji_list +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct SetStickerEmojiList { + /// File identifier of the sticker + pub sticker: String, + /// A list of 1-20 emoji associated with the sticker + pub keywords: Vec, +} + +/// struct for holding data needed to call +/// [`set_sticker_keywords`] +/// +/// [`set_sticker_keywords`]: +/// ../../api/trait.API.html#method.set_sticker_keywords +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct SetStickerKeywords { + /// File identifier of the sticker + pub sticker: String, + /// A list of 0-20 search keywords for the sticker with total length of up + /// to 64 characters + pub keywords: Option>, +} + +/// struct for holding data needed to call +/// [`set_sticker_mask_position`] +/// +/// [`set_sticker_mask_position`]: +/// ../../api/trait.API.html#method.set_sticker_mask_position +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct SetStickerMaskPosition { + /// File identifier of the sticker + pub sticker: String, + /// The position where the mask should be placed on faces. + /// Omit the parameter to remove the mask position. + pub mask_position: Option, +} + +/// struct for holding data needed to call +/// [`set_sticker_set_title`] +/// +/// [`set_sticker_set_title`]: +/// ../../api/trait.API.html#method.set_sticker_set_title +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct SetStickerSetTitle { + /// Sticker set name + pub name: String, + /// Sticker set title, 1-64 characters + pub title: String, +} + +/// struct for holding data needed to call +/// [`set_sticker_set_thumbnail`] /// -/// [`set_sticker_set_thumb`]: -/// ../../api/trait.API.html#method.set_sticker_set_thumb +/// [`set_sticker_set_thumbnail`]: +/// ../../api/trait.API.html#method.set_sticker_set_thumbnail #[build_struct] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct SetStickerSetThumb { +pub struct SetStickerSetThumbnail { /// Sticker set name pub name: String, /// User identifier of the sticker set owner pub user_id: i64, /// A PNG image with the thumbnail, must be up to 128 kilobytes in size and /// have width and height exactly 100px, or a TGS animation with the - /// thumbnail up to 32 kilobytes in size; see for animated sticker technical requirements. + /// thumbnail up to 32 kilobytes in size; see + /// for animated sticker technical requirements. /// Pass a file_id as a String to send a file that already exists on the /// Telegram servers, pass an HTTP URL as a String for Telegram to get a /// file from the Internet, or upload a new one. Animated sticker set /// thumbnail can't be uploaded via HTTP URL. #[serde(skip_serializing_if = "Option::is_none")] - pub thumb: Option, + pub thumbnail: Option, +} + +/// struct for holding data needed to call +/// [`set_custom_emoji_sticker_set_thumbnail`] +/// +/// [`set_custom_emoji_sticker_set_thumbnail`]: +/// ../../api/trait.API.html#method.set_custom_emoji_sticker_set_thumbnail +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct SetCustomEmojiStickerSetThumbnail { + /// Sticker set name + pub name: String, + /// Custom emoji identifier of a sticker from the sticker set; pass an empty + /// string to drop the thumbnail and use the first sticker as the thumbnail. + pub custom_emoji_id: Option, +} + +/// struct for holding data needed to call +/// [`delete_sticker_set`] +/// +/// [`delete_sticker_set`]: +/// ../../api/trait.API.html#method.delete_sticker_set +#[build_struct] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct DeleteStickerSet { + /// Sticker set name + pub name: String, } /// struct for holding data needed to call diff --git a/src/client/builder.rs b/src/client/builder.rs index 8a1928b..4cb6767 100644 --- a/src/client/builder.rs +++ b/src/client/builder.rs @@ -22,6 +22,7 @@ pub struct ClientBuilder { impl ClientBuilder { /// Creates a bare builder + // Providing a default gives the impression that is enough, but it is not #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { @@ -52,7 +53,7 @@ impl ClientBuilder { } /// Sets the token to be used in authorizing the API requests of your bot - #[allow(clippy::needless_pass_by_value)] + #[allow(clippy::needless_pass_by_value)] // Otherwise string literals don't work pub fn set_token(&mut self, token: impl ToString) -> &mut Self { self.token = Some(token.to_string()); self diff --git a/src/client/client.rs b/src/client/client.rs index a6ba097..c5262a1 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1,12 +1,6 @@ use super::{ - APIConnector, - ClientBuilder, - Context, - EventHandlerFunc, - RawEventHandlerFunc, - UpdatesStream, - Webhook, - WebhookOptions, + APIConnector, ClientBuilder, Context, EventHandlerFunc, RawEventHandlerFunc, UpdatesStream, + Webhook, WebhookOptions, }; use crate::{ api::{ @@ -219,13 +213,13 @@ impl Client { for h in self.raw_event_handlers.clone() { let ctx = Context::new(self.api_client.clone(), self.data.clone()); let u = update.clone(); - tokio::spawn(async move { h(ctx, u.into()).await }); + tokio::spawn(h(ctx, u.into())); } for h in self.event_handlers.clone() { let ctx = Context::new(self.api_client.clone(), self.data.clone()); let u = update.clone(); - tokio::spawn(async move { h(ctx, u).await }); + tokio::spawn(h(ctx, u)); } if self.framework.is_some() { diff --git a/src/client/stream.rs b/src/client/stream.rs index 908104a..865ff74 100644 --- a/src/client/stream.rs +++ b/src/client/stream.rs @@ -105,6 +105,7 @@ impl Stream for UpdatesStream { } impl UpdatesStream { + #[allow(clippy::redundant_async_block)] // It is not redundant fn poll_telegram(&mut self) { let mut data = GetUpdates::new(); data.set_limit(self.limit) diff --git a/src/framework/framework.rs b/src/framework/framework.rs index 96af021..6f3287b 100644 --- a/src/framework/framework.rs +++ b/src/framework/framework.rs @@ -23,16 +23,11 @@ impl Framework { } fn match_command(&self, message: &Message, name: &str) -> bool { - if let MessageContent::Text { - entities, - content, - } = &message.content - { + if let MessageContent::Text { entities, content } = &message.content { for entity in entities { if let MessageEntity::BotCommand(ref t) = entity { let t = t.get_text(content); - return t == format!("/{}", name) - || t == format!("/{}@{}", name, &self.bot_name); + return t == format!("/{name}") || t == format!("/{}@{}", name, &self.bot_name); } } } diff --git a/src/model/chat.rs b/src/model/chat.rs index f631725..94a0045 100644 --- a/src/model/chat.rs +++ b/src/model/chat.rs @@ -225,6 +225,7 @@ pub struct ChannelChat { /// This object represents a chat. It can be a private, group, supergroup or /// channel chat +#[allow(clippy::large_enum_variant)] // Using a box makes it more user-unfriendly #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(tag = "type")] pub enum Chat { diff --git a/src/model/games.rs b/src/model/games.rs index cdf9789..1c0c250 100644 --- a/src/model/games.rs +++ b/src/model/games.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// This object represents a game. Use [@BotFather](https://t.me/botfather) to create and edit games, /// their short names will act as unique identifiers. -#[allow(clippy::doc_markdown)] +#[allow(clippy::doc_markdown)] // BotFather is a thing #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Game { /// Title of the game diff --git a/src/model/message.rs b/src/model/message.rs index eced91f..c50917f 100644 --- a/src/model/message.rs +++ b/src/model/message.rs @@ -2,16 +2,8 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::{ - message_contents::*, - message_entity::*, - raw::*, - Game, - InlineKeyboardMarkup, - Invoice, - PassportData, - Sticker, - SuccessfulPayment, - User, + message_contents::*, message_entity::*, raw::*, Game, InlineKeyboardMarkup, Invoice, + PassportData, Sticker, SuccessfulPayment, User, }; /// This object represents a message. @@ -65,7 +57,7 @@ pub struct Message { } /// The content of a [`Message`] -#[allow(clippy::large_enum_variant)] +#[allow(clippy::large_enum_variant)] // Using a box makes it more user-unfriendly #[derive(Debug, Clone, PartialEq)] pub enum MessageContent { Text { @@ -329,34 +321,20 @@ pub struct ForwardData { impl Message { pub fn get_text(&self) -> Option { match self.content { - MessageContent::Text { - ref content, .. - } => Some(content.clone()), - MessageContent::Audio { - ref caption, .. - } - | MessageContent::Document { - ref caption, .. - } - | MessageContent::Animation { - ref caption, .. - } - | MessageContent::Video { - ref caption, .. - } - | MessageContent::Voice { - ref caption, .. - } - | MessageContent::Photo { - ref caption, .. - } => caption.clone(), + MessageContent::Text { ref content, .. } => Some(content.clone()), + MessageContent::Audio { ref caption, .. } + | MessageContent::Document { ref caption, .. } + | MessageContent::Animation { ref caption, .. } + | MessageContent::Video { ref caption, .. } + | MessageContent::Voice { ref caption, .. } + | MessageContent::Photo { ref caption, .. } => caption.clone(), _ => None, } } } impl From for Message { - #[allow(clippy::too_many_lines)] + #[allow(clippy::too_many_lines)] // Splitting it up makes it less readable fn from(raw: RawMessage) -> Message { let message_id = raw.message_id; let message_thread_id = raw.message_thread_id; @@ -457,9 +435,7 @@ impl From for Message { macro_rules! content { ($data:expr, $kind:ident) => { if let Some(c) = $data { - return fill_in_content(MessageContent::$kind { - content: c, - }); + return fill_in_content(MessageContent::$kind { content: c }); } }; } @@ -534,7 +510,7 @@ impl From for Message { } impl From for RawMessage { - #[allow(clippy::too_many_lines)] + #[allow(clippy::too_many_lines)] // Splitting it up makes it less readable fn from(message: Message) -> RawMessage { let mut ret = Self { message_id: message.message_id, @@ -626,10 +602,7 @@ impl From for RawMessage { } match message.content { - MessageContent::Text { - content, - entities, - } => { + MessageContent::Text { content, entities } => { ret.text = Some(content); ret.entities = Some(entities); ret @@ -704,171 +677,115 @@ impl From for RawMessage { ret.has_media_spoiler = has_spoiler; ret }, - MessageContent::Game { - content, - } => { + MessageContent::Game { content } => { ret.game = Some(content); ret }, - MessageContent::Sticker { - content, - } => { + MessageContent::Sticker { content } => { ret.sticker = Some(content); ret }, - MessageContent::VideoNote { - content, - } => { + MessageContent::VideoNote { content } => { ret.video_note = Some(content); ret }, - MessageContent::Contact { - content, - } => { + MessageContent::Contact { content } => { ret.contact = Some(content); ret }, - MessageContent::Location { - content, - } => { + MessageContent::Location { content } => { ret.location = Some(content); ret }, - MessageContent::Venue { - content, - } => { + MessageContent::Venue { content } => { ret.venue = Some(content); ret }, - MessageContent::Poll { - content, - } => { + MessageContent::Poll { content } => { ret.poll = Some(content); ret }, - MessageContent::Dice { - content, - } => { + MessageContent::Dice { content } => { ret.dice = Some(content); ret }, - MessageContent::NewChatMembers { - content, - } => { + MessageContent::NewChatMembers { content } => { ret.new_chat_members = Some(content); ret }, - MessageContent::LeftChatMember { - content, - } => { + MessageContent::LeftChatMember { content } => { ret.left_chat_member = Some(content); ret }, - MessageContent::NewChatTitle { - content, - } => { + MessageContent::NewChatTitle { content } => { ret.new_chat_title = Some(content); ret }, - MessageContent::NewChatPhoto { - content, - } => { + MessageContent::NewChatPhoto { content } => { ret.new_chat_photo = Some(content); ret }, - MessageContent::MessageAutoDeleteTimerChanged { - content, - } => { + MessageContent::MessageAutoDeleteTimerChanged { content } => { ret.message_auto_delete_timer_changed = Some(content); ret }, - MessageContent::MigrateToChatID { - content, - } => { + MessageContent::MigrateToChatID { content } => { ret.migrate_to_chat_id = Some(content); ret }, - MessageContent::MigrateFromChatID { - content, - } => { + MessageContent::MigrateFromChatID { content } => { ret.migrate_from_chat_id = Some(content); ret }, - MessageContent::Invoice { - content, - } => { + MessageContent::Invoice { content } => { ret.invoice = Some(content); ret }, - MessageContent::SuccessfulPayment { - content, - } => { + MessageContent::SuccessfulPayment { content } => { ret.successful_payment = Some(content); ret }, - MessageContent::UserShared { - content, - } => { + MessageContent::UserShared { content } => { ret.user_shared = Some(content); ret }, - MessageContent::ChatShared { - content, - } => { + MessageContent::ChatShared { content } => { ret.chat_shared = Some(content); ret }, - MessageContent::PinnedMessage { - content, - } => { + MessageContent::PinnedMessage { content } => { ret.pinned_message = Some(Box::new((*content).into())); ret }, - MessageContent::ProximityAlertTriggered { - content, - } => { + MessageContent::ProximityAlertTriggered { content } => { ret.proximity_alert_triggered = Some(content); ret }, - MessageContent::VideoChatScheduled { - content, - } => { + MessageContent::VideoChatScheduled { content } => { ret.voice_chat_scheduled = Some(content); ret }, - MessageContent::VideoChatStarted { - content, - } => { + MessageContent::VideoChatStarted { content } => { ret.voice_chat_started = Some(content); ret }, - MessageContent::VideoChatEnded { - content, - } => { + MessageContent::VideoChatEnded { content } => { ret.voice_chat_ended = Some(content); ret }, - MessageContent::VideoChatParticipantsInvited { - content, - } => { + MessageContent::VideoChatParticipantsInvited { content } => { ret.voice_chat_participants_invited = Some(content); ret }, - MessageContent::WebAppData { - content, - } => { + MessageContent::WebAppData { content } => { ret.web_app_data = Some(content); ret }, - MessageContent::ForumTopicCreated { - content, - } => { + MessageContent::ForumTopicCreated { content } => { ret.forum_topic_created = Some(content); ret }, - MessageContent::ForumTopicEdited { - content, - } => { + MessageContent::ForumTopicEdited { content } => { ret.forum_topic_edited = Some(content); ret }, diff --git a/src/model/message_contents.rs b/src/model/message_contents.rs index 4ac6253..49ea219 100644 --- a/src/model/message_contents.rs +++ b/src/model/message_contents.rs @@ -27,7 +27,7 @@ pub struct Audio { /// File size pub file_size: Option, /// Thumbnail of the album cover to which the music file belongs - pub thumb: Option, + pub thumbnail: Option, } /// This object represents a general file (as opposed to [photos][PhotoSize], @@ -42,7 +42,7 @@ pub struct Document { /// file. pub file_unique_id: String, /// Document thumbnail as defined by sender - pub thumb: Option, + pub thumbnail: Option, /// Original filename as defined by sender pub file_name: Option, /// MIME type of the file as defined by sender @@ -69,7 +69,7 @@ pub struct Animation { /// Duration of the video in seconds as defined by sender pub duration: usize, /// Animation thumbnail as defined by sender - pub thumb: Option, + pub thumbnail: Option, /// MIME type of the file as defined by sender pub mime_type: Option, /// File size @@ -113,7 +113,7 @@ pub struct Video { /// Duration of the video in seconds as defined by sender pub duration: usize, /// Video thumbnail - pub thumb: Option, + pub thumbnail: Option, /// Original filename as defined by sender pub file_name: Option, /// Mime type of a file as defined by sender @@ -156,7 +156,7 @@ pub struct VideoNote { /// Duration of the video in seconds as defined by sender pub duration: usize, /// Video thumbnail - pub thumb: Option, + pub thumbnail: Option, /// File size pub file_size: Option, } diff --git a/src/model/other.rs b/src/model/other.rs index 69c90bf..f5071f0 100644 --- a/src/model/other.rs +++ b/src/model/other.rs @@ -1,12 +1,6 @@ use super::{ - utils::unix_date_formatting, - ForceReply, - InlineKeyboardMarkup, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - User, - WebAppInfo, + utils::unix_date_formatting, ForceReply, InlineKeyboardMarkup, Message, ReplyKeyboardMarkup, + ReplyKeyboardRemove, User, WebAppInfo, }; use crate::api::types::UpdateType; use chrono::{DateTime, Utc}; @@ -99,7 +93,7 @@ pub enum ChatAction { /// Enum object for an inline keyboard, custom reply keyboard, instructions to /// remove reply keyboard or to force a reply from the user. -#[allow(clippy::large_enum_variant)] +#[allow(clippy::large_enum_variant)] // Using a box makes it more user-unfriendly #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum ReplyMarkup { @@ -192,3 +186,17 @@ pub enum MenuButton { web_app: WebAppInfo, }, } + +/// This object represents the bot's description. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct BotDescription { + /// The bot's description + description: String, +} + +/// This object represents the bot's short description. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct BotShortDescription { + /// The bot's short description + description: String, +} diff --git a/src/model/stickers.rs b/src/model/stickers.rs index bc0e11f..50c067b 100644 --- a/src/model/stickers.rs +++ b/src/model/stickers.rs @@ -1,5 +1,8 @@ +use crate::api::types::InputFile; + use super::{File, PhotoSize}; use serde::{Deserialize, Serialize}; +use telexide_proc_macros::build_struct; /// This object represents a sticker. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] @@ -26,7 +29,7 @@ pub struct Sticker { #[serde(default)] pub is_video: bool, /// Sticker thumbnail in the .WEBP or .JPG format - pub thumb: Option, + pub thumbnail: Option, /// Emoji associated with the sticker pub emoji: Option, /// Name of the sticker set to which the sticker belongs @@ -37,6 +40,11 @@ pub struct Sticker { pub mask_position: Option, /// For custom emoji stickers, unique identifier of the custom emoji pub custom_emoji_id: Option, + /// True, if the sticker must be repainted to a text color in messages, the + /// color of the Telegram Premium badge in emoji status, white color on chat + /// photos, or another appropriate color in other places + #[serde(default)] + pub needs_repainting: bool, /// File size pub file_size: Option, } @@ -56,12 +64,35 @@ pub struct StickerSet { /// List of all set stickers pub stickers: Vec, /// Optional. Sticker set thumbnail in the .WEBP or .TGS format - pub thumb: Option, + pub thumbnail: Option, +} + +/// This object describes a sticker to be added to a sticker set. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[build_struct] +pub struct InputSticker { + /// The added sticker. Pass a file_id as a String to send a file that + /// already exists on the Telegram servers, pass an HTTP URL as a String for + /// Telegram to get a file from the Internet, or upload a new one using + /// multipart/form-data. Animated and video stickers can't be uploaded via + /// HTTP URL. + pub sticker: InputFile, + /// List of 1-20 emoji associated with the sticker. + pub emoji_list: Vec, + /// position where the mask should be placed on faces. For “mask” stickers + /// only. + #[serde(skip_serializing_if = "Option::is_none")] + pub mask_position: Option, + /// List of 0-20 search keywords for the sticker with total length of up to + /// 64 characters. For “regular” and “custom_emoji” stickers only. + #[serde(skip_serializing_if = "Option::is_none")] + pub keywords: Option>, } /// This object describes the position on faces where a mask should be placed by /// default. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[build_struct] pub struct MaskPosition { /// The part of the face relative to which the mask should be placed pub point: MaskPoint, @@ -100,3 +131,14 @@ pub enum StickerType { #[serde(rename = "custom_emoji")] CustomEmoji, } + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(untagged)] +pub enum StickerFormat { + #[serde(rename = "static")] + Static, + #[serde(rename = "animated")] + Animated, + #[serde(rename = "video")] + Video, +} diff --git a/src/model/update.rs b/src/model/update.rs index 923e542..df2baac 100644 --- a/src/model/update.rs +++ b/src/model/update.rs @@ -1,15 +1,6 @@ use super::{ - raw::RawUpdate, - CallbackQuery, - ChatJoinRequest, - ChatMemberUpdated, - ChosenInlineResult, - InlineQuery, - Message, - Poll, - PollAnswer, - PreCheckoutQuery, - ShippingQuery, + raw::RawUpdate, CallbackQuery, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, + InlineQuery, Message, Poll, PollAnswer, PreCheckoutQuery, ShippingQuery, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -29,7 +20,7 @@ pub struct Update { } /// The content of an [`Update`] -#[allow(clippy::large_enum_variant)] +#[allow(clippy::large_enum_variant)] // Using a box makes it more user-unfriendly #[derive(Debug, Clone, PartialEq)] pub enum UpdateContent { /// New incoming message of any kind — text, photo, sticker, etc. @@ -81,10 +72,7 @@ pub enum UpdateContent { impl From for Update { fn from(raw: RawUpdate) -> Update { let update_id = raw.update_id; - let make_update = |content: UpdateContent| Self { - update_id, - content, - }; + let make_update = |content: UpdateContent| Self { update_id, content }; macro_rules! set_content { ($data:expr, $kind:ident) => { diff --git a/src/utils/form_data.rs b/src/utils/form_data.rs index 5b188bb..3c16f25 100644 --- a/src/utils/form_data.rs +++ b/src/utils/form_data.rs @@ -55,7 +55,7 @@ pub fn encode_multipart_form_data(files: &[FormDataFile]) -> Result> { let mut data = Vec::new(); for file in files { - write!(&mut data, "--{}\r\n", BOUNDARY)?; + write!(&mut data, "--{BOUNDARY}\r\n")?; if file.file_name.is_some() { write!( @@ -87,7 +87,7 @@ pub fn encode_multipart_form_data(files: &[FormDataFile]) -> Result> { write!(&mut data, "\r\n")?; } - write!(&mut data, "--{}--\r\n", BOUNDARY)?; + write!(&mut data, "--{BOUNDARY}--\r\n")?; Ok(data) } diff --git a/src/utils/result.rs b/src/utils/result.rs index 504c77a..084d6ff 100644 --- a/src/utils/result.rs +++ b/src/utils/result.rs @@ -53,11 +53,11 @@ impl TelegramError { TelegramError::InvalidCommandType => { "This action cannot be done on this command type".to_owned() }, - TelegramError::InvalidArgument(ref e) => format!("Invalid argument provided: {}", e), + TelegramError::InvalidArgument(ref e) => format!("Invalid argument provided: {e}"), TelegramError::APIResponseError(ref e) => { - format!("the telegram api returned an error: {}", e) + format!("the telegram api returned an error: {e}") }, - TelegramError::Unknown(ref e) => format!("unknown error occurred: {}", e), + TelegramError::Unknown(ref e) => format!("unknown error occurred: {e}"), } } }