diff --git a/src/enums/content_type.rs b/src/enums/content_type.rs index 26b58dcd..bb7f2984 100644 --- a/src/enums/content_type.rs +++ b/src/enums/content_type.rs @@ -27,8 +27,6 @@ pub enum ContentType { VideoNote, #[strum(serialize = "voice")] Voice, - #[strum(serialize = "has_media_spoiler")] - HasMediaSpoiler, #[strum(serialize = "contact")] Contact, #[strum(serialize = "dice")] @@ -93,6 +91,14 @@ pub enum ContentType { GeneralForumTopicHidden, #[strum(serialize = "general_forum_topic_unhidden")] GeneralForumTopicUnhidden, + #[strum(serialize = "giveaway_created")] + GiveawayCreated, + #[strum(serialize = "giveaway")] + Giveaway, + #[strum(serialize = "giveaway_winners")] + GiveawayWinners, + #[strum(serialize = "giveaway_completed")] + GiveawayCompleted, #[strum(serialize = "video_chat_scheduled")] VideoChatScheduled, #[strum(serialize = "video_chat_started")] @@ -103,13 +109,11 @@ pub enum ContentType { VideoChatParticipantsInvited, #[strum(serialize = "web_app_data")] WebAppData, - #[strum(serialize = "empty")] - Empty, } impl ContentType { #[must_use] - pub const fn all() -> [ContentType; 49] { + pub const fn all() -> [ContentType; 51] { [ ContentType::Text, ContentType::Animation, @@ -121,7 +125,6 @@ impl ContentType { ContentType::Video, ContentType::VideoNote, ContentType::Voice, - ContentType::HasMediaSpoiler, ContentType::Contact, ContentType::Dice, ContentType::Game, @@ -154,12 +157,15 @@ impl ContentType { ContentType::ForumTopicReopened, ContentType::GeneralForumTopicHidden, ContentType::GeneralForumTopicUnhidden, + ContentType::GiveawayCreated, + ContentType::Giveaway, + ContentType::GiveawayWinners, + ContentType::GiveawayCompleted, ContentType::VideoChatScheduled, ContentType::VideoChatStarted, ContentType::VideoChatEnded, ContentType::VideoChatParticipantsInvited, ContentType::WebAppData, - ContentType::Empty, ] } } @@ -215,7 +221,7 @@ impl From<&Message> for ContentType { Message::Pinned(_) => ContentType::PinnedMessage, Message::Invoice(_) => ContentType::Invoice, Message::SuccessfulPayment(_) => ContentType::SuccessfulPayment, - Message::UserShared(_) => ContentType::UserShared, + Message::UsersShared(_) => ContentType::UserShared, Message::ChatShared(_) => ContentType::ChatShared, Message::ConnectedWebsite(_) => ContentType::ConnectedWebsite, Message::WriteAccessAllowed(_) => ContentType::WriteAccessAllowed, @@ -227,12 +233,15 @@ impl From<&Message> for ContentType { Message::ForumTopicReopened(_) => ContentType::ForumTopicReopened, Message::GeneralForumTopicHidden(_) => ContentType::GeneralForumTopicHidden, Message::GeneralForumTopicUnhidden(_) => ContentType::GeneralForumTopicUnhidden, + Message::GiveawayCreated(_) => ContentType::GiveawayCreated, + Message::Giveaway(_) => ContentType::Giveaway, + Message::GiveawayWinners(_) => ContentType::GiveawayWinners, + Message::GiveawayCompleted(_) => ContentType::GiveawayCompleted, Message::VideoChatScheduled(_) => ContentType::VideoChatScheduled, Message::VideoChatStarted(_) => ContentType::VideoChatStarted, Message::VideoChatEnded(_) => ContentType::VideoChatEnded, Message::VideoChatParticipantsInvited(_) => ContentType::VideoChatParticipantsInvited, Message::WebAppData(_) => ContentType::WebAppData, - Message::Empty(_) => ContentType::Empty, } } } diff --git a/src/enums/observer_name.rs b/src/enums/observer_name.rs index 24f101f0..c11222a2 100644 --- a/src/enums/observer_name.rs +++ b/src/enums/observer_name.rs @@ -17,6 +17,10 @@ pub enum Telegram { EditedMessage, #[strum(serialize = "edited_channel_post")] EditedChannelPost, + #[strum(serialize = "message_reaction")] + MessageReaction, + #[strum(serialize = "message_reaction_count")] + MessageReactionCount, #[strum(serialize = "shipping_query")] ShippingQuery, #[strum(serialize = "pre_checkout_query")] @@ -31,13 +35,17 @@ pub enum Telegram { ChatMember, #[strum(serialize = "chat_join_request")] ChatJoinRequest, + #[strum(serialize = "chat_boost")] + ChatBoost, + #[strum(serialize = "removed_chat_boost")] + RemovedChatBoost, #[strum(serialize = "update")] Update, } impl Telegram { #[must_use] - pub const fn all() -> [Telegram; 15] { + pub const fn all() -> [Telegram; 19] { [ Telegram::Message, Telegram::InlineQuery, @@ -46,6 +54,8 @@ impl Telegram { Telegram::ChannelPost, Telegram::EditedMessage, Telegram::EditedChannelPost, + Telegram::MessageReaction, + Telegram::MessageReactionCount, Telegram::ShippingQuery, Telegram::PreCheckoutQuery, Telegram::Poll, @@ -53,6 +63,8 @@ impl Telegram { Telegram::MyChatMember, Telegram::ChatMember, Telegram::ChatJoinRequest, + Telegram::ChatBoost, + Telegram::RemovedChatBoost, Telegram::Update, ] } diff --git a/src/enums/update_type.rs b/src/enums/update_type.rs index 16908550..a4ef9732 100644 --- a/src/enums/update_type.rs +++ b/src/enums/update_type.rs @@ -22,6 +22,10 @@ pub enum UpdateType { EditedMessage, #[strum(serialize = "edited_channel_post")] EditedChannelPost, + #[strum(serialize = "message_reaction")] + MessageReaction, + #[strum(serialize = "message_reaction_count")] + MessageReactionCount, #[strum(serialize = "shipping_query")] ShippingQuery, #[strum(serialize = "pre_checkout_query")] @@ -36,11 +40,15 @@ pub enum UpdateType { ChatMember, #[strum(serialize = "chat_join_request")] ChatJoinRequest, + #[strum(serialize = "chat_boost")] + ChatBoost, + #[strum(serialize = "removed_chat_boost")] + RemovedChatBoost, } impl UpdateType { #[must_use] - pub const fn all() -> &'static [UpdateType; 14] { + pub const fn all() -> &'static [UpdateType; 17] { &[ UpdateType::Message, UpdateType::InlineQuery, @@ -49,6 +57,7 @@ impl UpdateType { UpdateType::ChannelPost, UpdateType::EditedMessage, UpdateType::EditedChannelPost, + UpdateType::MessageReaction, UpdateType::ShippingQuery, UpdateType::PreCheckoutQuery, UpdateType::Poll, @@ -56,6 +65,8 @@ impl UpdateType { UpdateType::MyChatMember, UpdateType::ChatMember, UpdateType::ChatJoinRequest, + UpdateType::ChatBoost, + UpdateType::RemovedChatBoost, ] } } @@ -85,6 +96,8 @@ impl<'a> From<&'a UpdateKind> for UpdateType { UpdateKind::EditedMessage(_) => UpdateType::EditedMessage, UpdateKind::ChannelPost(_) => UpdateType::ChannelPost, UpdateKind::EditedChannelPost(_) => UpdateType::EditedChannelPost, + UpdateKind::MessageReaction(_) => UpdateType::MessageReaction, + UpdateKind::MessageReactionCount(_) => UpdateType::MessageReactionCount, UpdateKind::InlineQuery(_) => UpdateType::InlineQuery, UpdateKind::ChosenInlineResult(_) => UpdateType::ChosenInlineResult, UpdateKind::CallbackQuery(_) => UpdateType::CallbackQuery, @@ -95,6 +108,8 @@ impl<'a> From<&'a UpdateKind> for UpdateType { UpdateKind::MyChatMember(_) => UpdateType::MyChatMember, UpdateKind::ChatMember(_) => UpdateType::ChatMember, UpdateKind::ChatJoinRequest(_) => UpdateType::ChatJoinRequest, + UpdateKind::ChatBoost(_) => UpdateType::ChatBoost, + UpdateKind::RemovedChatBoost(_) => UpdateType::RemovedChatBoost, } } } diff --git a/src/extractors/types.rs b/src/extractors/types.rs index e0dc7b16..cbb872be 100644 --- a/src/extractors/types.rs +++ b/src/extractors/types.rs @@ -5,21 +5,23 @@ use crate::{ context::Context, errors::ConvertToTypeError, types::{ - CallbackQuery, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InlineQuery, - Message, MessageAnimation, MessageAudio, MessageChannelChatCreated, MessageChatShared, - MessageConnectedWebsite, MessageContact, MessageDeleteChatPhoto, MessageDice, - MessageDocument, MessageEmpty, MessageForumTopicClosed, MessageForumTopicCreated, - MessageForumTopicEdited, MessageForumTopicReopened, MessageForward, MessageForwardedFrom, - MessageGame, MessageGeneralForumTopicHidden, MessageGeneralForumTopicUnhidden, + CallbackQuery, ChatBoostRemoved, ChatBoostUpdated, ChatJoinRequest, ChatMemberUpdated, + ChosenInlineResult, InlineQuery, Message, MessageAnimation, MessageAudio, + MessageChannelChatCreated, MessageChatShared, MessageConnectedWebsite, MessageContact, + MessageDeleteChatPhoto, MessageDice, MessageDocument, MessageForumTopicClosed, + MessageForumTopicCreated, MessageForumTopicEdited, MessageForumTopicReopened, MessageGame, + MessageGeneralForumTopicHidden, MessageGeneralForumTopicUnhidden, MessageGiveaway, + MessageGiveawayCompleted, MessageGiveawayCreated, MessageGiveawayWinners, MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageLocation, MessageMessageAutoDeleteTimerChanged, MessageMigrateFromChat, MessageMigrateToChat, MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, - MessagePhoto, MessagePinned, MessagePoll, MessageProximityAlertTriggered, MessageSticker, - MessageStory, MessageSuccessfulPayment, MessageSupergroupChatCreated, MessageText, - MessageUserShared, MessageVenue, MessageVideo, MessageVideoChatEnded, - MessageVideoChatParticipantsInvited, MessageVideoChatScheduled, MessageVideoChatStarted, - MessageVideoNote, MessageVoice, MessageWebAppData, MessageWriteAccessAllowed, Poll, - PollAnswer, PollQuiz, PollRegular, PreCheckoutQuery, ShippingQuery, Update, UpdateKind, + MessagePhoto, MessagePinned, MessagePoll, MessageProximityAlertTriggered, + MessageReactionCountUpdated, MessageReactionUpdated, MessageSticker, MessageStory, + MessageSuccessfulPayment, MessageSupergroupChatCreated, MessageText, MessageUsersShared, + MessageVenue, MessageVideo, MessageVideoChatEnded, MessageVideoChatParticipantsInvited, + MessageVideoChatScheduled, MessageVideoChatStarted, MessageVideoNote, MessageVoice, + MessageWebAppData, MessageWriteAccessAllowed, Poll, PollAnswer, PollQuiz, PollRegular, + PreCheckoutQuery, ShippingQuery, Update, UpdateKind, }, }; @@ -107,7 +109,7 @@ try_from_update!([Client], MessageText); try_from_update!([Client], MessageAnimation); try_from_update!([Client], MessageAudio); try_from_update!([Client], MessageChannelChatCreated); -try_from_update!([Client], MessageUserShared); +try_from_update!([Client], MessageUsersShared); try_from_update!([Client], MessageChatShared); try_from_update!([Client], MessageConnectedWebsite); try_from_update!([Client], MessageContact); @@ -118,8 +120,6 @@ try_from_update!([Client], MessageForumTopicClosed); try_from_update!([Client], MessageForumTopicCreated); try_from_update!([Client], MessageForumTopicEdited); try_from_update!([Client], MessageForumTopicReopened); -try_from_update!([Client], MessageForward); -try_from_update!([Client], MessageForwardedFrom); try_from_update!([Client], MessageGame); try_from_update!([Client], MessageGeneralForumTopicHidden); try_from_update!([Client], MessageGeneralForumTopicUnhidden); @@ -152,7 +152,12 @@ try_from_update!([Client], MessageVideoNote); try_from_update!([Client], MessageVoice); try_from_update!([Client], MessageWebAppData); try_from_update!([Client], MessageWriteAccessAllowed); -try_from_update!([Client], MessageEmpty); +try_from_update!([Client], MessageGiveawayCreated); +try_from_update!([Client], MessageGiveaway); +try_from_update!([Client], MessageGiveawayCompleted); +try_from_update!([Client], MessageGiveawayWinners); +try_from_update!([Client], MessageReactionUpdated); +try_from_update!([Client], MessageReactionCountUpdated); // To be able to use [`Poll`] and all [`PollKind`] variants in handler arguments try_from_update!([Client], Poll); @@ -183,6 +188,12 @@ try_from_update!([Client], ChatJoinRequest); // To be able to use [`InlineQuery`] in handler arguments try_from_update!([Client], InlineQuery); +// To be able to use [`ChatBoostUpdated`] in handler arguments +try_from_update!([Client], ChatBoostUpdated); + +// To be able to use [`ChatBoostRemoved`] in handler arguments +try_from_update!([Client], ChatBoostRemoved); + #[cfg(test)] mod tests { use super::*; @@ -212,7 +223,7 @@ mod tests { assert_impl_handler(|_: MessageAnimation| async { unreachable!() }); assert_impl_handler(|_: MessageAudio| async { unreachable!() }); assert_impl_handler(|_: MessageChannelChatCreated| async { unreachable!() }); - assert_impl_handler(|_: MessageUserShared| async { unreachable!() }); + assert_impl_handler(|_: MessageUsersShared| async { unreachable!() }); assert_impl_handler(|_: MessageChatShared| async { unreachable!() }); assert_impl_handler(|_: MessageConnectedWebsite| async { unreachable!() }); assert_impl_handler(|_: MessageContact| async { unreachable!() }); @@ -223,8 +234,6 @@ mod tests { assert_impl_handler(|_: MessageForumTopicCreated| async { unreachable!() }); assert_impl_handler(|_: MessageForumTopicEdited| async { unreachable!() }); assert_impl_handler(|_: MessageForumTopicReopened| async { unreachable!() }); - assert_impl_handler(|_: MessageForward| async { unreachable!() }); - assert_impl_handler(|_: MessageForwardedFrom| async { unreachable!() }); assert_impl_handler(|_: MessageGame| async { unreachable!() }); assert_impl_handler(|_: MessageGeneralForumTopicHidden| async { unreachable!() }); assert_impl_handler(|_: MessageGeneralForumTopicUnhidden| async { unreachable!() }); @@ -257,7 +266,13 @@ mod tests { assert_impl_handler(|_: MessageVoice| async { unreachable!() }); assert_impl_handler(|_: MessageWebAppData| async { unreachable!() }); assert_impl_handler(|_: MessageWriteAccessAllowed| async { unreachable!() }); - assert_impl_handler(|_: MessageEmpty| async { unreachable!() }); + assert_impl_handler(|_: MessageGiveawayCreated| async { unreachable!() }); + assert_impl_handler(|_: MessageGiveaway| async { unreachable!() }); + assert_impl_handler(|_: MessageGiveawayCompleted| async { unreachable!() }); + assert_impl_handler(|_: MessageGiveawayWinners| async { unreachable!() }); + assert_impl_handler(|_: MessageUsersShared| async { unreachable!() }); + assert_impl_handler(|_: MessageReactionUpdated| async { unreachable!() }); + assert_impl_handler(|_: MessageReactionCountUpdated| async { unreachable!() }); assert_impl_handler(|_: Poll| async { unreachable!() }); assert_impl_handler(|_: PollRegular| async { unreachable!() }); assert_impl_handler(|_: PollQuiz| async { unreachable!() }); @@ -269,6 +284,8 @@ mod tests { assert_impl_handler(|_: ChatMemberUpdated| async { unreachable!() }); assert_impl_handler(|_: ChatJoinRequest| async { unreachable!() }); assert_impl_handler(|_: InlineQuery| async { unreachable!() }); + assert_impl_handler(|_: ChatBoostUpdated| async { unreachable!() }); + assert_impl_handler(|_: ChatBoostRemoved| async { unreachable!() }); } #[allow(clippy::too_many_lines)] diff --git a/src/methods.rs b/src/methods.rs index d4331c48..07a9f2d3 100644 --- a/src/methods.rs +++ b/src/methods.rs @@ -44,6 +44,7 @@ pub mod base; pub mod close_forum_topic; pub mod close_general_forum_topic; pub mod copy_message; +pub mod copy_messages; pub mod create_chat_invite_link; pub mod create_forum_topic; pub mod create_invoice_link; @@ -53,6 +54,7 @@ pub mod delete_chat_photo; pub mod delete_chat_sticker_set; pub mod delete_forum_topic; pub mod delete_message; +pub mod delete_messages; pub mod delete_my_commands; pub mod delete_sticker_from_set; pub mod delete_sticker_set; @@ -66,6 +68,7 @@ pub mod edit_message_reply_markup; pub mod edit_message_text; pub mod export_chat_invite_link; pub mod forward_message; +pub mod forward_messages; pub mod get_chat; pub mod get_chat_administrators; pub mod get_chat_member; @@ -83,6 +86,7 @@ pub mod get_my_name; pub mod get_my_short_description; pub mod get_sticker_set; pub mod get_updates; +pub mod get_user_chat_boosts; pub mod get_user_profile_photos; pub mod hide_general_forum_topic; pub mod leave_chat; @@ -119,6 +123,7 @@ pub mod set_chat_sticker_set; pub mod set_chat_title; pub mod set_custom_emoji_sticker_set_thumbnail; pub mod set_game_score; +pub mod set_message_reaction; pub mod set_my_commands; pub mod set_my_default_administrator_rights; pub mod set_my_description; @@ -155,6 +160,7 @@ pub use base::{Request, Response, TelegramMethod}; pub use close_forum_topic::CloseForumTopic; pub use close_general_forum_topic::CloseGeneralForumTopic; pub use copy_message::CopyMessage; +pub use copy_messages::CopyMessages; pub use create_chat_invite_link::CreateChatInviteLink; pub use create_forum_topic::CreateForumTopic; pub use create_invoice_link::CreateInvoiceLink; @@ -164,6 +170,7 @@ pub use delete_chat_photo::DeleteChatPhoto; pub use delete_chat_sticker_set::DeleteChatStickerSet; pub use delete_forum_topic::DeleteForumTopic; pub use delete_message::DeleteMessage; +pub use delete_messages::DeleteMessages; pub use delete_my_commands::DeleteMyCommands; pub use delete_sticker_from_set::DeleteStickerFromSet; pub use delete_sticker_set::DeleteStickerSet; @@ -177,6 +184,7 @@ pub use edit_message_reply_markup::EditMessageReplyMarkup; pub use edit_message_text::EditMessageText; pub use export_chat_invite_link::ExportChatInviteLink; pub use forward_message::ForwardMessage; +pub use forward_messages::ForwardMessages; pub use get_chat::GetChat; pub use get_chat_administrators::GetChatAdministrators; pub use get_chat_member::GetChatMember; @@ -194,6 +202,7 @@ pub use get_my_name::GetMyName; pub use get_my_short_description::GetMyShortDescription; pub use get_sticker_set::GetStickerSet; pub use get_updates::GetUpdates; +pub use get_user_chat_boosts::GetUserChatBoosts; pub use get_user_profile_photos::GetUserProfilePhotos; pub use hide_general_forum_topic::HideGeneralForumTopic; pub use leave_chat::LeaveChat; @@ -230,6 +239,7 @@ pub use set_chat_sticker_set::SetChatStickerSet; pub use set_chat_title::SetChatTitle; pub use set_custom_emoji_sticker_set_thumbnail::SetCustomEmojiStickerSetThumbnail; pub use set_game_score::SetGameScore; +pub use set_message_reaction::SetMessageReaction; pub use set_my_commands::SetMyCommands; pub use set_my_default_administrator_rights::SetMyDefaultAdministratorRights; pub use set_my_description::SetMyDescription; diff --git a/src/methods/add_sticker_to_set.rs b/src/methods/add_sticker_to_set.rs index a4c77c21..f7e3c777 100644 --- a/src/methods/add_sticker_to_set.rs +++ b/src/methods/add_sticker_to_set.rs @@ -3,7 +3,6 @@ use super::base::{prepare_input_sticker, Request, TelegramMethod}; use crate::{client::Bot, types::InputSticker}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to add a new sticker to a set created by the bot. /// The format of the added sticker must match the format of the other stickers in the set. @@ -14,7 +13,6 @@ use serde_with::skip_serializing_none; /// /// # Returns /// `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, PartialEq, Serialize)] pub struct AddStickerToSet<'a> { /// User identifier of sticker set owner diff --git a/src/methods/answer_pre_checkout_query.rs b/src/methods/answer_pre_checkout_query.rs index 21ed3900..b11baba1 100644 --- a/src/methods/answer_pre_checkout_query.rs +++ b/src/methods/answer_pre_checkout_query.rs @@ -17,9 +17,9 @@ use serde_with::skip_serializing_none; pub struct AnswerPreCheckoutQuery { /// Unique identifier for the query to be answered pub pre_checkout_query_id: String, - /// Specify `true` if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use `False` if there are any problems. + /// Specify `true` if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use `false` if there are any problems. pub ok: bool, - /// Required if `ok` is `False`. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. + /// Required if `ok` is `false`. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. pub error_message: Option, } diff --git a/src/methods/answer_shipping_query.rs b/src/methods/answer_shipping_query.rs index 24ccff13..c4e1b1e2 100644 --- a/src/methods/answer_shipping_query.rs +++ b/src/methods/answer_shipping_query.rs @@ -15,11 +15,11 @@ use serde_with::skip_serializing_none; pub struct AnswerShippingQuery { /// Unique identifier for the query to be answered pub shipping_query_id: String, - /// Pass `true` if delivery to the specified address is possible and `False` if there are any problems (for example, if delivery to the specified address is not possible) + /// Pass `true` if delivery to the specified address is possible and `false` if there are any problems (for example, if delivery to the specified address is not possible) pub ok: bool, /// Required if `ok` is `true`. A JSON-serialized array of available shipping options. pub shipping_options: Option>, - /// Required if `ok` is `False`. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. + /// Required if `ok` is `false`. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. pub error_message: Option, } diff --git a/src/methods/answer_web_app_query.rs b/src/methods/answer_web_app_query.rs index 2175fbd6..7d515323 100644 --- a/src/methods/answer_web_app_query.rs +++ b/src/methods/answer_web_app_query.rs @@ -6,14 +6,12 @@ use crate::{ }; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to set the result of an interaction with a [`Web App`](https://core.telegram.org/bots/webapps) and send a corresponding message on behalf of the user to the chat from which the query originated. /// # Documentation /// /// # Returns /// On success, a [`SentWebAppMessage`] object is returned -#[skip_serializing_none] #[derive(Debug, Clone, PartialEq, Serialize)] pub struct AnswerWebAppQuery { /// Unique identifier for the query to be answered diff --git a/src/methods/approve_chat_join_request.rs b/src/methods/approve_chat_join_request.rs index 0779b3f0..64600f3a 100644 --- a/src/methods/approve_chat_join_request.rs +++ b/src/methods/approve_chat_join_request.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the `can_invite_users` administrator right. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct ApproveChatJoinRequest { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/ban_chat_member.rs b/src/methods/ban_chat_member.rs index 76f5c935..ea453c96 100644 --- a/src/methods/ban_chat_member.rs +++ b/src/methods/ban_chat_member.rs @@ -19,7 +19,7 @@ pub struct BanChatMember { pub user_id: i64, /// Date when the user will be unbanned; Unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever. Applied for supergroups and channels only. pub until_date: Option, - /// Pass `true` to delete all messages from the chat for the user that is being removed. If `False`, the user will be able to see messages in the group that were sent before the user was removed. Always `true` for supergroups and channels. + /// Pass `true` to delete all messages from the chat for the user that is being removed. If `false`, the user will be able to see messages in the group that were sent before the user was removed. Always `true` for supergroups and channels. pub revoke_messages: Option, } diff --git a/src/methods/ban_chat_sender_chat.rs b/src/methods/ban_chat_sender_chat.rs index 639f7bd0..e62e3118 100644 --- a/src/methods/ban_chat_sender_chat.rs +++ b/src/methods/ban_chat_sender_chat.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to ban a channel chat in a supergroup or a channel. Until the chat is [`unbanned`](crate::methods::UnbanChatSenderChat), the owner of the banned chat won't be able to send messages on behalf of **any of their channels**. The bot must be an administrator in the supergroup or channel for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct BanChatSenderChat { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/close_forum_topic.rs b/src/methods/close_forum_topic.rs index 24c42da7..3297cfa6 100644 --- a/src/methods/close_forum_topic.rs +++ b/src/methods/close_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to close an open topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights, unless it is the creator of the topic. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct CloseForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/close_general_forum_topic.rs b/src/methods/close_general_forum_topic.rs index 2efde4a0..03dd20c1 100644 --- a/src/methods/close_general_forum_topic.rs +++ b/src/methods/close_general_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to close an open `General` topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct CloseGeneralForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/copy_message.rs b/src/methods/copy_message.rs index f03b3ab9..12671de8 100644 --- a/src/methods/copy_message.rs +++ b/src/methods/copy_message.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, MessageEntity, MessageId, ReplyMarkup}, + types::{ChatIdKind, MessageEntity, MessageId, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -34,10 +34,8 @@ pub struct CopyMessage { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -59,8 +57,7 @@ impl CopyMessage { caption_entities: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -158,17 +155,9 @@ impl CopyMessage { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -241,17 +230,9 @@ impl CopyMessage { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/copy_messages.rs b/src/methods/copy_messages.rs new file mode 100644 index 00000000..960539df --- /dev/null +++ b/src/methods/copy_messages.rs @@ -0,0 +1,165 @@ +use super::base::{Request, TelegramMethod}; + +use crate::{ + client::Bot, + types::{ChatIdKind, MessageId}, +}; + +use serde::Serialize; +use serde_with::skip_serializing_none; + +/// Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz [`poll`](crate::types::Poll) can be copied only if the value of the field `correct_option_id` is known to the bot. The method is analogous to the method [`ForwardMessages`](crate::methods::ForwardMessages), but the copied messages don't have a link to the original message. Album grouping is kept for copied messages. +/// # Documentation +/// +/// # Returns +/// On success, an array of [`MessageId`] of the sent messages is returned. +#[skip_serializing_none] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] +pub struct CopyMessages { + /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) + pub chat_id: ChatIdKind, + /// Unique identifier for the target message thread (topic) of the forum; for forum supergroups only + pub message_thread_id: Option, + /// Unique identifier for the chat where the original messages were sent (or channel username in the format `@channelusername`) + pub from_chat_id: ChatIdKind, + /// MIdentifiers of 1-100 messages in the chat `from_chat_id` to copy. The identifiers must be specified in a strictly increasing order. + pub message_ids: Vec, + /// Sends the messages [silently](https://telegram.org/blog/channels-2-0#silent-messages). Users will receive a notification with no sound + pub disable_notification: Option, + /// Protects the contents of the sent messages from forwarding and saving + pub protect_content: Option, + /// Pass `true` to copy the messages without their captions + pub remove_caption: Option, +} + +impl CopyMessages { + #[must_use] + pub fn new( + chat_id: impl Into, + from_chat_id: impl Into, + message_ids: impl IntoIterator, + ) -> Self { + Self { + chat_id: chat_id.into(), + message_thread_id: None, + from_chat_id: from_chat_id.into(), + message_ids: message_ids.into_iter().collect(), + disable_notification: None, + protect_content: None, + remove_caption: None, + } + } + + #[must_use] + pub fn chat_id(self, val: impl Into) -> Self { + Self { + chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn message_thread_id(self, val: i64) -> Self { + Self { + message_thread_id: Some(val), + ..self + } + } + + #[must_use] + pub fn from_chat_id(self, val: impl Into) -> Self { + Self { + from_chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn message_id(self, val: i64) -> Self { + Self { + message_ids: self.message_ids.into_iter().chain(Some(val)).collect(), + ..self + } + } + + #[must_use] + pub fn message_ids(self, val: impl IntoIterator) -> Self { + Self { + message_ids: self.message_ids.into_iter().chain(val).collect(), + ..self + } + } + + #[must_use] + pub fn disable_notification(self, val: bool) -> Self { + Self { + disable_notification: Some(val), + ..self + } + } + + #[must_use] + pub fn protect_content(self, val: bool) -> Self { + Self { + protect_content: Some(val), + ..self + } + } + + #[must_use] + pub fn remove_caption(self, val: bool) -> Self { + Self { + remove_caption: Some(val), + ..self + } + } +} + +impl CopyMessages { + #[must_use] + pub fn message_thread_id_option(self, val: Option) -> Self { + Self { + message_thread_id: val, + ..self + } + } + + #[must_use] + pub fn disable_notification_option(self, val: Option) -> Self { + Self { + disable_notification: val, + ..self + } + } + + #[must_use] + pub fn protect_content_option(self, val: Option) -> Self { + Self { + protect_content: val, + ..self + } + } + + #[must_use] + pub fn remove_caption_option(self, val: Option) -> Self { + Self { + remove_caption: val, + ..self + } + } +} + +impl TelegramMethod for CopyMessages { + type Method = Self; + type Return = Box<[MessageId]>; + + fn build_request(&self, _bot: &Bot) -> Request { + Request::new("copyMessages", self, None) + } +} + +impl AsRef for CopyMessages { + fn as_ref(&self) -> &Self { + self + } +} diff --git a/src/methods/decline_chat_join_request.rs b/src/methods/decline_chat_join_request.rs index 1b076058..bb8c41f2 100644 --- a/src/methods/decline_chat_join_request.rs +++ b/src/methods/decline_chat_join_request.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to decline a chat join request. The bot must be an administrator in the chat for this to work and must have the `can_invite_users` administrator right. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeclineChatJoinRequest { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/delete_chat_photo.rs b/src/methods/delete_chat_photo.rs index e76ec540..a7a9b1db 100644 --- a/src/methods/delete_chat_photo.rs +++ b/src/methods/delete_chat_photo.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeleteChatPhoto { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/delete_chat_sticker_set.rs b/src/methods/delete_chat_sticker_set.rs index 86c8a61c..a09931e3 100644 --- a/src/methods/delete_chat_sticker_set.rs +++ b/src/methods/delete_chat_sticker_set.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field `can_set_sticker_set` optionally returned in [`GetChat`](crate::methods::GetChat) requests to check if the bot can use this method. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeleteChatStickerSet { /// Unique identifier for the target chat or username of the target supergroup (in the format `@channelusername`) diff --git a/src/methods/delete_forum_topic.rs b/src/methods/delete_forum_topic.rs index 6804e2c4..e43a012c 100644 --- a/src/methods/delete_forum_topic.rs +++ b/src/methods/delete_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to delete a forum topic along with all its messages in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_delete_messages` administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeleteForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/delete_message.rs b/src/methods/delete_message.rs index 8c21b56a..514a6af6 100644 --- a/src/methods/delete_message.rs +++ b/src/methods/delete_message.rs @@ -3,7 +3,6 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to delete a message, including service messages, with the following limitations: /// Use this method to delete a message, including service messages, with the following limitations: @@ -12,14 +11,13 @@ use serde_with::skip_serializing_none; /// - A dice message in a private chat can only be deleted if it was sent more than 24 hours ago. /// - Bots can delete outgoing messages in private chats, groups, and supergroups. /// - Bots can delete incoming messages in private chats. -/// - Bots granted can_post_messages permissions can delete outgoing messages in channels. +/// - Bots granted `can_post_messages` permissions can delete outgoing messages in channels. /// - If the bot is an administrator of a group, it can delete any message there. -/// - If the bot has can_delete_messages permission in a supergroup or a channel, it can delete any message there. +/// - If the bot has `can_delete_messages` permission in a supergroup or a channel, it can delete any message there. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeleteMessage { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/delete_messages.rs b/src/methods/delete_messages.rs new file mode 100644 index 00000000..4368b1cf --- /dev/null +++ b/src/methods/delete_messages.rs @@ -0,0 +1,67 @@ +use super::base::{Request, TelegramMethod}; + +use crate::{client::Bot, types::ChatIdKind}; + +use serde::Serialize; + +/// Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. +/// # Documentation +/// +/// # Returns +/// Returns `true` on success +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] +pub struct DeleteMessages { + /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) + pub chat_id: ChatIdKind, + /// Identifiers of 1-100 messages to delete. See [`crate::methods::DeleteMessage`] for limitations on which messages can be deleted + pub message_ids: Vec, +} + +impl DeleteMessages { + #[must_use] + pub fn new(chat_id: impl Into, message_ids: impl IntoIterator) -> Self { + Self { + chat_id: chat_id.into(), + message_ids: message_ids.into_iter().collect(), + } + } + + #[must_use] + pub fn chat_id(self, val: impl Into) -> Self { + Self { + chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn message_id(self, val: i64) -> Self { + Self { + message_ids: self.message_ids.into_iter().chain(Some(val)).collect(), + ..self + } + } + + #[must_use] + pub fn message_ids(self, val: impl IntoIterator) -> Self { + Self { + message_ids: self.message_ids.into_iter().chain(val).collect(), + ..self + } + } +} + +impl TelegramMethod for DeleteMessages { + type Method = Self; + type Return = bool; + + fn build_request(&self, _bot: &Bot) -> Request { + Request::new("deleteMessages", self, None) + } +} + +impl AsRef for DeleteMessages { + fn as_ref(&self) -> &Self { + self + } +} diff --git a/src/methods/delete_sticker_from_set.rs b/src/methods/delete_sticker_from_set.rs index a3cf4eac..398a6541 100644 --- a/src/methods/delete_sticker_from_set.rs +++ b/src/methods/delete_sticker_from_set.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::client::Bot; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to delete a sticker from a set created by the bot /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeleteStickerFromSet { /// File identifier of the sticker diff --git a/src/methods/delete_sticker_set.rs b/src/methods/delete_sticker_set.rs index 967d4d37..5523fdc8 100644 --- a/src/methods/delete_sticker_set.rs +++ b/src/methods/delete_sticker_set.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::client::Bot; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to delete a sticker set that was created by the bot. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct DeleteStickerSet { /// Sticker set name diff --git a/src/methods/edit_general_forum_topic.rs b/src/methods/edit_general_forum_topic.rs index 767e6acb..62cc76a6 100644 --- a/src/methods/edit_general_forum_topic.rs +++ b/src/methods/edit_general_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to close an open `General` topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct EditGeneralForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/edit_message_text.rs b/src/methods/edit_message_text.rs index 87400cf8..e3d78c1f 100644 --- a/src/methods/edit_message_text.rs +++ b/src/methods/edit_message_text.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InlineKeyboardMarkup, MessageEntity, MessageOrTrue}, + types::{ChatIdKind, InlineKeyboardMarkup, LinkPreviewOptions, MessageEntity, MessageOrTrue}, }; use serde::Serialize; @@ -28,8 +28,8 @@ pub struct EditMessageText { pub parse_mode: Option, /// A JSON-serialized list of special entities that appear in message text, which can be specified instead of `parse_mode` pub entities: Option>, - /// Disables link previews for links in this message - pub disable_web_page_preview: Option, + /// Link preview generation options for the message + pub link_preview_options: Option, /// A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards). pub reply_markup: Option, } @@ -44,7 +44,7 @@ impl EditMessageText { text: text.into(), parse_mode: None, entities: None, - disable_web_page_preview: None, + link_preview_options: None, reply_markup: None, } } @@ -104,9 +104,9 @@ impl EditMessageText { } #[must_use] - pub fn disable_web_page_preview(self, val: bool) -> Self { + pub fn link_preview_options(self, val: LinkPreviewOptions) -> Self { Self { - disable_web_page_preview: Some(val), + link_preview_options: Some(val), ..self } } @@ -168,9 +168,9 @@ impl EditMessageText { } #[must_use] - pub fn disable_web_page_preview_option(self, val: Option) -> Self { + pub fn link_preview_options_option(self, val: Option) -> Self { Self { - disable_web_page_preview: val, + link_preview_options: val, ..self } } diff --git a/src/methods/export_chat_invite_link.rs b/src/methods/export_chat_invite_link.rs index 34c014c0..fa962398 100644 --- a/src/methods/export_chat_invite_link.rs +++ b/src/methods/export_chat_invite_link.rs @@ -3,7 +3,6 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to generate a new primary invite link for a chat; any previously generated primary link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. /// # Documentation @@ -12,7 +11,6 @@ use serde_with::skip_serializing_none; /// Each administrator in a chat generates their own invite links. Bots can't use invite links generated by other administrators. If you want your bot to work with invite links, it will need to generate its own link using [`crate::methods::ExportChatInviteLink`] or by calling the [`crate::methods::GetChat`] method. If your bot needs to generate a new primary invite link replacing its previous one, use [`crate::methods::ExportChatInviteLink`] again. /// # Returns /// Returns the new invite link as `String` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct ExportChatInviteLink { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/forward_message.rs b/src/methods/forward_message.rs index d8072fb7..b9382ac1 100644 --- a/src/methods/forward_message.rs +++ b/src/methods/forward_message.rs @@ -24,7 +24,7 @@ pub struct ForwardMessage { pub from_chat_id: ChatIdKind, /// Message identifier in the chat specified in `from_chat_id` pub message_id: i64, - /// Sends the message [silently](https://telegram.org/blog/channels-2-0#silent-messages). Users will receive a notification with no sound + /// Sends the message [silently](https://telegram.org/blog/channels-2-0#silent-messages). Users will receive a notification with no sound. pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, diff --git a/src/methods/forward_messages.rs b/src/methods/forward_messages.rs new file mode 100644 index 00000000..6575bac7 --- /dev/null +++ b/src/methods/forward_messages.rs @@ -0,0 +1,146 @@ +use super::base::{Request, TelegramMethod}; + +use crate::{ + client::Bot, + types::{ChatIdKind, MessageId}, +}; + +use serde::Serialize; +use serde_with::skip_serializing_none; + +/// Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. +/// # Documentation +/// +/// # Returns +/// On success, an array of [`MessageId`] of the sent messages is returned. +#[skip_serializing_none] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] +pub struct ForwardMessages { + /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) + pub chat_id: ChatIdKind, + /// Unique identifier for the target message thread (topic) of the forum; for forum supergroups only + pub message_thread_id: Option, + /// Unique identifier for the chat where the original messages were sent (or channel username in the format `@channelusername`) + pub from_chat_id: ChatIdKind, + /// Identifiers of 1-100 messages in the chat `from_chat_id` to forward. The identifiers must be specified in a strictly increasing order. + pub message_ids: Vec, + /// Sends the messages [silently](https://telegram.org/blog/channels-2-0#silent-messages). Users will receive a notification with no sound. + pub disable_notification: Option, + /// Protects the contents of the forwarded messages from forwarding and saving + pub protect_content: Option, +} + +impl ForwardMessages { + #[must_use] + pub fn new( + chat_id: impl Into, + from_chat_id: impl Into, + message_ids: impl IntoIterator, + ) -> Self { + Self { + chat_id: chat_id.into(), + message_thread_id: None, + from_chat_id: from_chat_id.into(), + message_ids: message_ids.into_iter().collect(), + disable_notification: None, + protect_content: None, + } + } + + #[must_use] + pub fn chat_id(self, val: impl Into) -> Self { + Self { + chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn message_thread_id(self, val: i64) -> Self { + Self { + message_thread_id: Some(val), + ..self + } + } + + #[must_use] + pub fn from_chat_id(self, val: impl Into) -> Self { + Self { + from_chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn message_id(self, val: i64) -> Self { + Self { + message_ids: self.message_ids.into_iter().chain(Some(val)).collect(), + ..self + } + } + + #[must_use] + pub fn message_ids(self, val: impl IntoIterator) -> Self { + Self { + message_ids: self.message_ids.into_iter().chain(val).collect(), + ..self + } + } + + #[must_use] + pub fn disable_notification(self, val: bool) -> Self { + Self { + disable_notification: Some(val), + ..self + } + } + + #[must_use] + pub fn protect_content(self, val: bool) -> Self { + Self { + protect_content: Some(val), + ..self + } + } +} + +impl ForwardMessages { + #[must_use] + pub fn message_thread_id_option(self, val: Option) -> Self { + Self { + message_thread_id: val, + ..self + } + } + + #[must_use] + pub fn disable_notification_option(self, val: Option) -> Self { + Self { + disable_notification: val, + ..self + } + } + + #[must_use] + pub fn protect_content_option(self, val: Option) -> Self { + Self { + protect_content: val, + ..self + } + } +} + +impl TelegramMethod for ForwardMessages { + type Method = Self; + type Return = Box<[MessageId]>; + + fn build_request(&self, _bot: &Bot) -> Request { + Request::new("forwardMessages", self, None) + } +} + +impl AsRef for ForwardMessages { + fn as_ref(&self) -> &Self { + self + } +} diff --git a/src/methods/get_chat.rs b/src/methods/get_chat.rs index bbba366d..5ea63243 100644 --- a/src/methods/get_chat.rs +++ b/src/methods/get_chat.rs @@ -6,14 +6,12 @@ use crate::{ }; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). /// # Documentation /// /// # Returns /// Returns a [`Chat`] object on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetChat { /// Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`) diff --git a/src/methods/get_chat_administrators.rs b/src/methods/get_chat_administrators.rs index 164f352e..c89c2b51 100644 --- a/src/methods/get_chat_administrators.rs +++ b/src/methods/get_chat_administrators.rs @@ -6,14 +6,12 @@ use crate::{ }; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get a list of administrators in a chat, which aren't bots. /// # Documentation /// /// # Returns /// Returns an Array of [`ChatMember`] objects -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetChatAdministrators { /// Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`) diff --git a/src/methods/get_chat_member.rs b/src/methods/get_chat_member.rs index ff6b4522..78c2b091 100644 --- a/src/methods/get_chat_member.rs +++ b/src/methods/get_chat_member.rs @@ -6,14 +6,12 @@ use crate::{ }; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get information about a member of a chat. The method is only guaranteed to work for other users if the bot is an administrator in the chat /// # Documentation /// /// # Returns /// Returns a [`ChatMember`] object on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetChatMember { /// Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`) diff --git a/src/methods/get_chat_member_count.rs b/src/methods/get_chat_member_count.rs index ac150a47..455fb66d 100644 --- a/src/methods/get_chat_member_count.rs +++ b/src/methods/get_chat_member_count.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get the number of members in a chat. /// # Documentation /// /// # Returns /// Returns `i64` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetChatMemberCount { /// Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`) diff --git a/src/methods/get_custom_emoji_stickers.rs b/src/methods/get_custom_emoji_stickers.rs index 19cfc602..0083448d 100644 --- a/src/methods/get_custom_emoji_stickers.rs +++ b/src/methods/get_custom_emoji_stickers.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::Sticker}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get information about custom emoji stickers by their identifiers. /// # Documentation /// /// # Returns /// Returns an Array of [`Sticker`] objects -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetCustomEmojiStickers { /// List of custom emoji identifiers. At most 200 custom emoji identifiers can be specified. diff --git a/src/methods/get_file.rs b/src/methods/get_file.rs index a8ab8d33..3d13f675 100644 --- a/src/methods/get_file.rs +++ b/src/methods/get_file.rs @@ -3,7 +3,6 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::File}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. The file can then be downloaded via the link `https://api.telegram.org/file/bot/`, where `` is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling [`GetFile`](crate::methods::GetFile) again. /// # Documentation @@ -12,7 +11,6 @@ use serde_with::skip_serializing_none; /// This function may not preserve the original file name and MIME type. You should save the file's MIME type and name (if available) when the File object is received. /// # Returns /// On success, a [`File`] object is returned -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetFile { /// File identifier to get info about diff --git a/src/methods/get_forum_topic_icon_stickers.rs b/src/methods/get_forum_topic_icon_stickers.rs index f640bedd..ccb95dbd 100644 --- a/src/methods/get_forum_topic_icon_stickers.rs +++ b/src/methods/get_forum_topic_icon_stickers.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::Sticker}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. Requires no parameters. /// # Documentation /// /// # Returns /// Returns an Array of [`Sticker`] objects -#[skip_serializing_none] #[derive(Debug, Default, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetForumTopicIconStickers {} diff --git a/src/methods/get_sticker_set.rs b/src/methods/get_sticker_set.rs index 7a4f1318..afeb6519 100644 --- a/src/methods/get_sticker_set.rs +++ b/src/methods/get_sticker_set.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::StickerSet}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to get a sticker set. /// # Documentation /// /// # Returns /// On success, a [`StickerSet`] object is returned -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct GetStickerSet { /// Name of the sticker set diff --git a/src/methods/get_user_chat_boosts.rs b/src/methods/get_user_chat_boosts.rs new file mode 100644 index 00000000..0b0a8f94 --- /dev/null +++ b/src/methods/get_user_chat_boosts.rs @@ -0,0 +1,62 @@ +use super::base::{Request, TelegramMethod}; + +use crate::{ + client::Bot, + types::{ChatIdKind, UserChatBoosts}, +}; + +use serde::Serialize; + +/// Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. +/// # Documentation +/// +/// # Returns +/// Returns a [`UserChatBoosts`] object. +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] +pub struct GetUserChatBoosts { + /// Unique identifier for the chat or username of the channel (in the format `@channelusername`) + pub chat_id: ChatIdKind, + /// Unique identifier of the target user + pub user_id: i64, +} + +impl GetUserChatBoosts { + #[must_use] + pub fn new(chat_id: impl Into, user_id: i64) -> Self { + Self { + chat_id: chat_id.into(), + user_id, + } + } + + #[must_use] + pub fn chat_id(self, val: impl Into) -> Self { + Self { + chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn user_id(self, val: i64) -> Self { + Self { + user_id: val, + ..self + } + } +} + +impl TelegramMethod for GetUserChatBoosts { + type Method = Self; + type Return = UserChatBoosts; + + fn build_request(&self, _bot: &Bot) -> Request { + Request::new("getUserChatBoosts", self, None) + } +} + +impl AsRef for GetUserChatBoosts { + fn as_ref(&self) -> &Self { + self + } +} diff --git a/src/methods/hide_general_forum_topic.rs b/src/methods/hide_general_forum_topic.rs index d90b757c..a3110173 100644 --- a/src/methods/hide_general_forum_topic.rs +++ b/src/methods/hide_general_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to hide the `General` topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights. The topic will be automatically closed if it was open. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct HideGeneralForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/leave_chat.rs b/src/methods/leave_chat.rs index dc47bd92..b622dcd3 100644 --- a/src/methods/leave_chat.rs +++ b/src/methods/leave_chat.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method for your bot to leave a group, supergroup or channel. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct LeaveChat { /// Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`) diff --git a/src/methods/promote_chat_member.rs b/src/methods/promote_chat_member.rs index 8ef161ee..39cc16dd 100644 --- a/src/methods/promote_chat_member.rs +++ b/src/methods/promote_chat_member.rs @@ -5,7 +5,7 @@ use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; use serde_with::skip_serializing_none; -/// Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Pass `False` for all boolean parameters to demote a user. +/// Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Pass `false` for all boolean parameters to demote a user. /// # Documentation /// /// # Returns diff --git a/src/methods/reopen_forum_topic.rs b/src/methods/reopen_forum_topic.rs index cdf56d59..f54bda0d 100644 --- a/src/methods/reopen_forum_topic.rs +++ b/src/methods/reopen_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights, unless it is the creator of the topic. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct ReopenForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/reopen_general_forum_topic.rs b/src/methods/reopen_general_forum_topic.rs index 7613d9f2..65059c2e 100644 --- a/src/methods/reopen_general_forum_topic.rs +++ b/src/methods/reopen_general_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to reopen a closed `General` topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights. The topic will be automatically unhidden if it was hidden. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct ReopenGeneralForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/revoke_chat_invite_link.rs b/src/methods/revoke_chat_invite_link.rs index 52dc25e0..8b7f96f1 100644 --- a/src/methods/revoke_chat_invite_link.rs +++ b/src/methods/revoke_chat_invite_link.rs @@ -6,14 +6,12 @@ use crate::{ }; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to revoke an invite link created by the bot. If the primary link is revoked, a new link is automatically generated. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns the revoked invite link as [`ChatInviteLink`] object -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct RevokeChatInviteLink { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/send_animation.rs b/src/methods/send_animation.rs index 7caa282b..6be3dbae 100644 --- a/src/methods/send_animation.rs +++ b/src/methods/send_animation.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -44,10 +44,8 @@ pub struct SendAnimation<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -70,8 +68,7 @@ impl<'a> SendAnimation<'a> { supports_streaming: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -209,17 +206,9 @@ impl<'a> SendAnimation<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -337,17 +326,9 @@ impl<'a> SendAnimation<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_audio.rs b/src/methods/send_audio.rs index 8f4b2bdb..c19b1e58 100644 --- a/src/methods/send_audio.rs +++ b/src/methods/send_audio.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -40,10 +40,8 @@ pub struct SendAudio<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -64,8 +62,7 @@ impl<'a> SendAudio<'a> { thumbnail: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -187,17 +184,9 @@ impl<'a> SendAudio<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -302,17 +291,9 @@ impl<'a> SendAudio<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_contact.rs b/src/methods/send_contact.rs index da124f7b..aca3b18f 100644 --- a/src/methods/send_contact.rs +++ b/src/methods/send_contact.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, Message, ReplyMarkup}, + types::{ChatIdKind, Message, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -32,10 +32,8 @@ pub struct SendContact { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -56,8 +54,7 @@ impl SendContact { vcard: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -127,17 +124,9 @@ impl SendContact { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -193,17 +182,9 @@ impl SendContact { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_dice.rs b/src/methods/send_dice.rs index 1fddabe3..a25a4007 100644 --- a/src/methods/send_dice.rs +++ b/src/methods/send_dice.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, Message, ReplyMarkup}, + types::{ChatIdKind, Message, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -26,10 +26,8 @@ pub struct SendDice { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -43,8 +41,7 @@ impl SendDice { emoji: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -90,17 +87,9 @@ impl SendDice { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -148,17 +137,9 @@ impl SendDice { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_document.rs b/src/methods/send_document.rs index 9a56c7bb..0b94a800 100644 --- a/src/methods/send_document.rs +++ b/src/methods/send_document.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -36,10 +36,8 @@ pub struct SendDocument<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -58,8 +56,7 @@ impl<'a> SendDocument<'a> { disable_content_type_detection: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -165,17 +162,9 @@ impl<'a> SendDocument<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -256,17 +245,9 @@ impl<'a> SendDocument<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_game.rs b/src/methods/send_game.rs index cc3a8d96..515515ec 100644 --- a/src/methods/send_game.rs +++ b/src/methods/send_game.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{InlineKeyboardMarkup, Message}, + types::{InlineKeyboardMarkup, Message, ReplyParameters}, }; use serde::Serialize; @@ -26,10 +26,8 @@ pub struct SendGame { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards). If empty, one ‘Play game_title’ button will be shown. If not empty, the first button must launch the game. pub reply_markup: Option, } @@ -43,8 +41,7 @@ impl SendGame { game_short_name: game_short_name.into(), disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -90,17 +87,9 @@ impl SendGame { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -140,17 +129,9 @@ impl SendGame { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_invoice.rs b/src/methods/send_invoice.rs index 774371b0..41d41f36 100644 --- a/src/methods/send_invoice.rs +++ b/src/methods/send_invoice.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InlineKeyboardMarkup, LabeledPrice, Message}, + types::{ChatIdKind, InlineKeyboardMarkup, LabeledPrice, Message, ReplyParameters}, }; use serde::Serialize; @@ -66,10 +66,8 @@ pub struct SendInvoice { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards). If empty, one 'Pay `total price`' button will be shown. If not empty, the first button must be a Pay button. pub reply_markup: Option, } @@ -111,8 +109,7 @@ impl SendInvoice { is_flexible: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -338,17 +335,9 @@ impl SendInvoice { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -500,17 +489,9 @@ impl SendInvoice { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_location.rs b/src/methods/send_location.rs index 2e9a0622..a5c17c4a 100644 --- a/src/methods/send_location.rs +++ b/src/methods/send_location.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, Message, ReplyMarkup}, + types::{ChatIdKind, Message, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -36,10 +36,8 @@ pub struct SendLocation { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -58,8 +56,7 @@ impl SendLocation { proximity_alert_radius: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -145,17 +142,9 @@ impl SendLocation { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -227,17 +216,9 @@ impl SendLocation { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_media_group.rs b/src/methods/send_media_group.rs index 3a8462db..b8778acb 100644 --- a/src/methods/send_media_group.rs +++ b/src/methods/send_media_group.rs @@ -2,7 +2,7 @@ use super::base::{prepare_input_media_group, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputMedia, Message}, + types::{ChatIdKind, InputMedia, Message, ReplyParameters}, }; use serde::Serialize; @@ -26,10 +26,8 @@ pub struct SendMediaGroup<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, } impl<'a> SendMediaGroup<'a> { @@ -45,8 +43,7 @@ impl<'a> SendMediaGroup<'a> { media: media.into_iter().map(Into::into).collect(), disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, } } @@ -107,17 +104,9 @@ impl<'a> SendMediaGroup<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -149,17 +138,9 @@ impl<'a> SendMediaGroup<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_message.rs b/src/methods/send_message.rs index 1d3dd849..8b9f660a 100644 --- a/src/methods/send_message.rs +++ b/src/methods/send_message.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, LinkPreviewOptions, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -26,16 +26,14 @@ pub struct SendMessage { pub parse_mode: Option, /// A JSON-serialized list of special entities that appear in message text, which can be specified instead of `parse_mode` pub entities: Option>, - /// Disables link previews for links in this message - pub disable_web_page_preview: Option, + /// Link preview generation options for the message + pub link_preview_options: Option, /// Sends the message [silently](https://telegram.org/blog/channels-2-0#silent-messages). Users will receive a notification with no sound. pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -49,11 +47,10 @@ impl SendMessage { text: text.into(), parse_mode: None, entities: None, - disable_web_page_preview: None, + link_preview_options: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -119,9 +116,9 @@ impl SendMessage { } #[must_use] - pub fn disable_web_page_preview(self, val: bool) -> Self { + pub fn link_preview_options(self, val: LinkPreviewOptions) -> Self { Self { - disable_web_page_preview: Some(val), + link_preview_options: Some(val), ..self } } @@ -143,17 +140,9 @@ impl SendMessage { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -193,9 +182,9 @@ impl SendMessage { } #[must_use] - pub fn disable_web_page_preview_option(self, val: Option) -> Self { + pub fn link_preview_options_option(self, val: Option) -> Self { Self { - disable_web_page_preview: val, + link_preview_options: val, ..self } } @@ -217,17 +206,9 @@ impl SendMessage { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_photo.rs b/src/methods/send_photo.rs index 2511bd0a..cdc0cc61 100644 --- a/src/methods/send_photo.rs +++ b/src/methods/send_photo.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -34,10 +34,8 @@ pub struct SendPhoto<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, *id* of the original message - pub reply_to_message_id: Option, - /// Pass `true` if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -55,8 +53,7 @@ impl<'a> SendPhoto<'a> { has_spoiler: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -154,17 +151,9 @@ impl<'a> SendPhoto<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -245,17 +234,9 @@ impl<'a> SendPhoto<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_poll.rs b/src/methods/send_poll.rs index af0e119f..e42a6280 100644 --- a/src/methods/send_poll.rs +++ b/src/methods/send_poll.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -29,7 +29,7 @@ pub struct SendPoll { /// Poll type, `quiz` or `regular`, defaults to `regular` #[serde(rename = "type")] pub poll_type: Option, - /// `true`, if the poll allows multiple answers, ignored for polls in `quiz` mode, defaults to `False` + /// `true`, if the poll allows multiple answers, ignored for polls in `quiz` mode, defaults to `false` pub allows_multiple_answers: Option, /// 0-based identifier of the correct answer option, required for polls in `quiz` mode pub correct_option_id: Option, @@ -49,10 +49,8 @@ pub struct SendPoll { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -81,8 +79,7 @@ impl SendPoll { is_closed: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -252,17 +249,9 @@ impl SendPoll { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -385,17 +374,9 @@ impl SendPoll { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_sticker.rs b/src/methods/send_sticker.rs index 68137521..ed773d94 100644 --- a/src/methods/send_sticker.rs +++ b/src/methods/send_sticker.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -28,10 +28,8 @@ pub struct SendSticker<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -46,8 +44,7 @@ impl<'a> SendSticker<'a> { emoji: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -101,17 +98,9 @@ impl<'a> SendSticker<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -159,17 +148,9 @@ impl<'a> SendSticker<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_venue.rs b/src/methods/send_venue.rs index a27853dd..cca94123 100644 --- a/src/methods/send_venue.rs +++ b/src/methods/send_venue.rs @@ -2,7 +2,7 @@ use super::base::{Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, Message, ReplyMarkup}, + types::{ChatIdKind, Message, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -40,10 +40,8 @@ pub struct SendVenue { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -70,8 +68,7 @@ impl SendVenue { google_place_type: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -173,17 +170,9 @@ impl SendVenue { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -255,17 +244,9 @@ impl SendVenue { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_video.rs b/src/methods/send_video.rs index 77a48374..6110d222 100644 --- a/src/methods/send_video.rs +++ b/src/methods/send_video.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -44,10 +44,8 @@ pub struct SendVideo<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -70,8 +68,7 @@ impl<'a> SendVideo<'a> { supports_streaming: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -209,17 +206,9 @@ impl<'a> SendVideo<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -337,17 +326,9 @@ impl<'a> SendVideo<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_video_note.rs b/src/methods/send_video_note.rs index 1cbf1af0..62ab53f3 100644 --- a/src/methods/send_video_note.rs +++ b/src/methods/send_video_note.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -32,10 +32,8 @@ pub struct SendVideoNote<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -52,8 +50,7 @@ impl<'a> SendVideoNote<'a> { thumbnail: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -123,17 +120,9 @@ impl<'a> SendVideoNote<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -197,17 +186,9 @@ impl<'a> SendVideoNote<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/send_voice.rs b/src/methods/send_voice.rs index 93428f92..9d13f13a 100644 --- a/src/methods/send_voice.rs +++ b/src/methods/send_voice.rs @@ -2,7 +2,7 @@ use super::base::{prepare_file, Request, TelegramMethod}; use crate::{ client::Bot, - types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup}, + types::{ChatIdKind, InputFile, Message, MessageEntity, ReplyMarkup, ReplyParameters}, }; use serde::Serialize; @@ -34,10 +34,8 @@ pub struct SendVoice<'a> { pub disable_notification: Option, /// Protects the contents of the sent message from forwarding and saving pub protect_content: Option, - /// If the message is a reply, ID of the original message - pub reply_to_message_id: Option, - /// Pass `true`, if the message should be sent even if the specified replied-to message is not found - pub allow_sending_without_reply: Option, + /// Description of the message to reply to + pub reply_parameters: Option, /// Additional interface options. A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards), [custom reply keyboard](https://core.telegram.org/bots/features#keyboards), instructions to remove reply keyboard or to force a reply from the user. pub reply_markup: Option, } @@ -55,8 +53,7 @@ impl<'a> SendVoice<'a> { duration: None, disable_notification: None, protect_content: None, - reply_to_message_id: None, - allow_sending_without_reply: None, + reply_parameters: None, reply_markup: None, } } @@ -154,17 +151,9 @@ impl<'a> SendVoice<'a> { } #[must_use] - pub fn reply_to_message_id(self, val: i64) -> Self { + pub fn reply_parameters(self, val: ReplyParameters) -> Self { Self { - reply_to_message_id: Some(val), - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply(self, val: bool) -> Self { - Self { - allow_sending_without_reply: Some(val), + reply_parameters: Some(val), ..self } } @@ -245,17 +234,9 @@ impl<'a> SendVoice<'a> { } #[must_use] - pub fn reply_to_message_id_option(self, val: Option) -> Self { - Self { - reply_to_message_id: val, - ..self - } - } - - #[must_use] - pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + pub fn reply_parameters_option(self, val: Option) -> Self { Self { - allow_sending_without_reply: val, + reply_parameters: val, ..self } } diff --git a/src/methods/set_chat_administrator_custom_title.rs b/src/methods/set_chat_administrator_custom_title.rs index 7e509f8f..b0bed26d 100644 --- a/src/methods/set_chat_administrator_custom_title.rs +++ b/src/methods/set_chat_administrator_custom_title.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to set a custom title for an administrator in a supergroup promoted by the bot. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetChatAdministratorCustomTitle { /// Unique identifier for the target group or username of the target supergroup (in the format `@channelusername`) diff --git a/src/methods/set_chat_description.rs b/src/methods/set_chat_description.rs index bbf74d73..c9b788cb 100644 --- a/src/methods/set_chat_description.rs +++ b/src/methods/set_chat_description.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to change the description of a group, a supergroup or a channel. descriptions can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetChatDescription { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/set_chat_photo.rs b/src/methods/set_chat_photo.rs index fa8dc197..aa1dd8d1 100644 --- a/src/methods/set_chat_photo.rs +++ b/src/methods/set_chat_photo.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetChatPhoto<'a> { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/set_chat_sticker_set.rs b/src/methods/set_chat_sticker_set.rs index 8e9cec3c..d7e49eed 100644 --- a/src/methods/set_chat_sticker_set.rs +++ b/src/methods/set_chat_sticker_set.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field `can_set_sticker_set` optionally returned in [`GetChat`](crate::methods::GetChat) requests to check if the bot can use this method. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetChatStickerSet { /// Unique identifier for the target chat or username of the target supergroup (in the format `@channelusername`) diff --git a/src/methods/set_chat_title.rs b/src/methods/set_chat_title.rs index 370ad5ab..4f46b398 100644 --- a/src/methods/set_chat_title.rs +++ b/src/methods/set_chat_title.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetChatTitle { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/set_message_reaction.rs b/src/methods/set_message_reaction.rs new file mode 100644 index 00000000..19fc24d2 --- /dev/null +++ b/src/methods/set_message_reaction.rs @@ -0,0 +1,152 @@ +use super::base::{Request, TelegramMethod}; + +use crate::{ + client::Bot, + types::{ChatIdKind, ReactionType}, +}; + +use serde::Serialize; +use serde_with::skip_serializing_none; + +/// Use this method to change the chosen reactions on a message. Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same available reactions as messages in the channel. In albums, bots must react to the first message. +/// # Documentation +/// +/// # Returns +/// Returns `true` on success +#[skip_serializing_none] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] +pub struct SetMessageReaction { + /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) + pub chat_id: ChatIdKind, + /// Identifier of the target message + pub message_id: i64, + /// New list of reaction types to set on the message. Currently, as non-premium users, bots can set up to one reaction per message. A custom emoji reaction can be used if it is either already present on the message or explicitly allowed by chat administrators. + pub reaction: Option>, + /// Pass `true` to set the reaction with a big animation + pub is_big: Option, +} + +impl SetMessageReaction { + #[must_use] + pub fn new(chat_id: impl Into, message_id: i64) -> Self { + Self { + chat_id: chat_id.into(), + message_id, + reaction: None, + is_big: None, + } + } + + #[must_use] + pub fn chat_id(self, val: impl Into) -> Self { + Self { + chat_id: val.into(), + ..self + } + } + + #[must_use] + pub fn message_id(self, val: i64) -> Self { + Self { + message_id: val, + ..self + } + } + + #[must_use] + pub fn reaction(self, val: impl Into) -> Self { + Self { + reaction: Some( + self.reaction + .unwrap_or_default() + .into_iter() + .chain(Some(val.into())) + .collect(), + ), + ..self + } + } + + #[must_use] + pub fn reactions(self, val: I) -> Self + where + T: Into, + I: IntoIterator, + { + Self { + reaction: Some( + self.reaction + .unwrap_or_default() + .into_iter() + .chain(val.into_iter().map(Into::into)) + .collect(), + ), + ..self + } + } + + #[must_use] + pub fn is_big(self, val: bool) -> Self { + Self { + is_big: Some(val), + ..self + } + } +} + +impl SetMessageReaction { + #[must_use] + pub fn reaction_option(self, val: Option>) -> Self { + Self { + reaction: val.map(|val| { + self.reaction + .unwrap_or_default() + .into_iter() + .chain(Some(val.into())) + .collect() + }), + ..self + } + } + + #[must_use] + pub fn reactions_option(self, val: Option) -> Self + where + T: Into, + I: IntoIterator, + { + Self { + reaction: val.map(|val| { + self.reaction + .unwrap_or_default() + .into_iter() + .chain(val.into_iter().map(Into::into)) + .collect() + }), + ..self + } + } + + #[must_use] + pub fn is_big_option(self, val: Option) -> Self { + Self { + is_big: val, + ..self + } + } +} + +impl TelegramMethod for SetMessageReaction { + type Method = Self; + type Return = bool; + + fn build_request(&self, _bot: &Bot) -> Request { + Request::new("setMessageReaction", self, None) + } +} + +impl AsRef for SetMessageReaction { + fn as_ref(&self) -> &Self { + self + } +} diff --git a/src/methods/set_passport_data_errors.rs b/src/methods/set_passport_data_errors.rs index 97e780a1..2e758cc5 100644 --- a/src/methods/set_passport_data_errors.rs +++ b/src/methods/set_passport_data_errors.rs @@ -3,7 +3,6 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::PassportElementError}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for which you returned the error must change). /// # Documentation @@ -12,7 +11,6 @@ use serde_with::skip_serializing_none; /// Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues. /// # Returns /// On success, `true` is returned -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetPassportDataErrors { /// User identifier diff --git a/src/methods/set_sticker_emoji_list.rs b/src/methods/set_sticker_emoji_list.rs index 8ee0d525..32080c54 100644 --- a/src/methods/set_sticker_emoji_list.rs +++ b/src/methods/set_sticker_emoji_list.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::client::Bot; use serde::Serialize; -use serde_with::skip_serializing_none; /// 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. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetStickerEmojiList { /// File identifier of the sticker diff --git a/src/methods/set_sticker_keywords.rs b/src/methods/set_sticker_keywords.rs index d5cbead7..50cc8430 100644 --- a/src/methods/set_sticker_keywords.rs +++ b/src/methods/set_sticker_keywords.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::client::Bot; use serde::Serialize; -use serde_with::skip_serializing_none; /// 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. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetStickerKeywords { /// File identifier of the sticker diff --git a/src/methods/set_sticker_position_in_set.rs b/src/methods/set_sticker_position_in_set.rs index caea429d..c1bd765b 100644 --- a/src/methods/set_sticker_position_in_set.rs +++ b/src/methods/set_sticker_position_in_set.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::client::Bot; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to move a sticker in a set created by the bot to a specific position /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetStickerPositionInSet { /// File identifier of the sticker diff --git a/src/methods/set_sticker_set_title.rs b/src/methods/set_sticker_set_title.rs index 411c6edd..e294ac76 100644 --- a/src/methods/set_sticker_set_title.rs +++ b/src/methods/set_sticker_set_title.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::client::Bot; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to set the title of a created sticker set. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct SetStickerSetTitle { /// Sticker set name diff --git a/src/methods/unban_chat_sender_chat.rs b/src/methods/unban_chat_sender_chat.rs index d0f19743..f825c13e 100644 --- a/src/methods/unban_chat_sender_chat.rs +++ b/src/methods/unban_chat_sender_chat.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to unban a previously banned channel chat in a supergroup or channel. The bot must be an administrator for this to work and must have the appropriate administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct UnbanChatSenderChat { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/unhide_general_forum_topic.rs b/src/methods/unhide_general_forum_topic.rs index e1b165ea..7e15e719 100644 --- a/src/methods/unhide_general_forum_topic.rs +++ b/src/methods/unhide_general_forum_topic.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to unhide the `General` topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the `can_manage_topics` administrator rights. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct UnhideGeneralForumTopic { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/unpin_all_chat_messages.rs b/src/methods/unpin_all_chat_messages.rs index 7cac976c..4304641e 100644 --- a/src/methods/unpin_all_chat_messages.rs +++ b/src/methods/unpin_all_chat_messages.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the `can_pin_messages` administrator right in a supergroup or `can_edit_messages` administrator right in a channel. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct UnpinAllChatMessages { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/methods/unpin_all_forum_topic_messages.rs b/src/methods/unpin_all_forum_topic_messages.rs index 7963f6c7..b3cb5347 100644 --- a/src/methods/unpin_all_forum_topic_messages.rs +++ b/src/methods/unpin_all_forum_topic_messages.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to clear the list of pinned messages in a forum topic. The bot must be an administrator in the chat for this to work and must have the `can_pin_messages` administrator right in the supergroup. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct UnpinAllForumTopicMessages { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/unpin_all_general_forum_topic_messages.rs b/src/methods/unpin_all_general_forum_topic_messages.rs index bd7e8dd5..301fc9c8 100644 --- a/src/methods/unpin_all_general_forum_topic_messages.rs +++ b/src/methods/unpin_all_general_forum_topic_messages.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to clear the list of pinned messages in a General forum topic. The bot must be an administrator in the chat for this to work and must have the `can_pin_messages` administrator right in the supergroup /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct UnpinAllGeneralForumTopicMessages { /// Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) diff --git a/src/methods/unpin_chat_message.rs b/src/methods/unpin_chat_message.rs index b35047fe..5b857187 100644 --- a/src/methods/unpin_chat_message.rs +++ b/src/methods/unpin_chat_message.rs @@ -3,14 +3,12 @@ use super::base::{Request, TelegramMethod}; use crate::{client::Bot, types::ChatIdKind}; use serde::Serialize; -use serde_with::skip_serializing_none; /// Use this method to remove a message from the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the `can_pin_messages` administrator right in a supergroup or `can_edit_messages` administrator right in a channel. /// # Documentation /// /// # Returns /// Returns `true` on success -#[skip_serializing_none] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] pub struct UnpinChatMessage { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) diff --git a/src/router.rs b/src/router.rs index e05350a6..7844e4fc 100644 --- a/src/router.rs +++ b/src/router.rs @@ -371,6 +371,8 @@ pub struct Router { pub edited_message: TelegramObserver, pub channel_post: TelegramObserver, pub edited_channel_post: TelegramObserver, + pub message_reaction: TelegramObserver, + pub message_reaction_count: TelegramObserver, pub inline_query: TelegramObserver, pub chosen_inline_result: TelegramObserver, pub callback_query: TelegramObserver, @@ -381,7 +383,8 @@ pub struct Router { pub my_chat_member: TelegramObserver, pub chat_member: TelegramObserver, pub chat_join_request: TelegramObserver, - + pub chat_boost: TelegramObserver, + pub removed_chat_boost: TelegramObserver, /// This special event observer is used to handle all telegram events. /// It's called for router and its sub routers and before other telegram observers. /// This observer is useful for register important middlewares (often libraries) like `FSMContext` and `UserContext`, @@ -408,6 +411,8 @@ where edited_message: TelegramObserver::new(TelegramObserverName::EditedMessage.as_ref()), channel_post: TelegramObserver::new(TelegramObserverName::ChannelPost.as_ref()), edited_channel_post: TelegramObserver::new(TelegramObserverName::EditedChannelPost.as_ref()), + message_reaction: TelegramObserver::new(TelegramObserverName::MessageReaction.as_ref()), + message_reaction_count: TelegramObserver::new(TelegramObserverName::MessageReactionCount.as_ref()), inline_query: TelegramObserver::new(TelegramObserverName::InlineQuery.as_ref()), chosen_inline_result: TelegramObserver::new(TelegramObserverName::ChosenInlineResult.as_ref()), callback_query: TelegramObserver::new(TelegramObserverName::CallbackQuery.as_ref()), @@ -418,6 +423,8 @@ where my_chat_member: TelegramObserver::new(TelegramObserverName::MyChatMember.as_ref()), chat_member: TelegramObserver::new(TelegramObserverName::ChatMember.as_ref()), chat_join_request: TelegramObserver::new(TelegramObserverName::ChatJoinRequest.as_ref()), + chat_boost: TelegramObserver::new(TelegramObserverName::ChatBoost.as_ref()), + removed_chat_boost: TelegramObserver::new(TelegramObserverName::RemovedChatBoost.as_ref()), update: TelegramObserver::new(TelegramObserverName::Update.as_ref()), startup: SimpleObserver::new(SimpleObserverName::Startup.as_ref()), shutdown: SimpleObserver::new(SimpleObserverName::Shutdown.as_ref()), @@ -449,12 +456,14 @@ where impl Router { /// Get all telegram event observers #[must_use] - pub const fn telegram_observers(&self) -> [&TelegramObserver; 15] { + pub const fn telegram_observers(&self) -> [&TelegramObserver; 19] { [ &self.message, &self.edited_message, &self.channel_post, &self.edited_channel_post, + &self.message_reaction, + &self.message_reaction_count, &self.inline_query, &self.chosen_inline_result, &self.callback_query, @@ -465,6 +474,8 @@ impl Router { &self.my_chat_member, &self.chat_member, &self.chat_join_request, + &self.chat_boost, + &self.removed_chat_boost, &self.update, ] } @@ -474,13 +485,15 @@ impl Router { /// This method is useful for registering middlewares to the many observers without code duplication and macros #[must_use] pub fn telegram_observers_mut(&mut self) -> Vec<&mut TelegramObserver> { - let mut observers = Vec::with_capacity(15); + let mut observers = Vec::with_capacity(19); observers.extend([ &mut self.message, &mut self.edited_message, &mut self.channel_post, &mut self.edited_channel_post, + &mut self.message_reaction, + &mut self.message_reaction_count, &mut self.inline_query, &mut self.chosen_inline_result, &mut self.callback_query, @@ -491,6 +504,8 @@ impl Router { &mut self.my_chat_member, &mut self.chat_member, &mut self.chat_join_request, + &mut self.chat_boost, + &mut self.removed_chat_boost, &mut self.update, ]); @@ -591,6 +606,7 @@ where type ServiceProvider = Service; type InitError = (); + #[allow(clippy::too_many_lines)] fn to_service_provider( mut self, mut config: Self::Config, @@ -614,6 +630,8 @@ where edited_message, channel_post, edited_channel_post, + message_reaction, + message_reaction_count, inline_query, chosen_inline_result, callback_query, @@ -624,6 +642,8 @@ where my_chat_member, chat_member, chat_join_request, + chat_boost, + removed_chat_boost, update ); @@ -652,6 +672,8 @@ where edited_message, channel_post, edited_channel_post, + message_reaction, + message_reaction_count, inline_query, chosen_inline_result, callback_query, @@ -662,6 +684,8 @@ where my_chat_member, chat_member, chat_join_request, + chat_boost, + removed_chat_boost, update ); @@ -679,6 +703,8 @@ where edited_message: self.edited_message.to_service_provider_default()?, channel_post: self.channel_post.to_service_provider_default()?, edited_channel_post: self.edited_channel_post.to_service_provider_default()?, + message_reaction: self.message_reaction.to_service_provider_default()?, + message_reaction_count: self.message_reaction_count.to_service_provider_default()?, inline_query: self.inline_query.to_service_provider_default()?, chosen_inline_result: self.chosen_inline_result.to_service_provider_default()?, callback_query: self.callback_query.to_service_provider_default()?, @@ -689,6 +715,8 @@ where my_chat_member: self.my_chat_member.to_service_provider_default()?, chat_member: self.chat_member.to_service_provider_default()?, chat_join_request: self.chat_join_request.to_service_provider_default()?, + chat_boost: self.chat_boost.to_service_provider_default()?, + removed_chat_boost: self.removed_chat_boost.to_service_provider_default()?, update: self.update.to_service_provider_default()?, startup: self.startup.to_service_provider_default()?, shutdown: self.shutdown.to_service_provider_default()?, @@ -704,6 +732,8 @@ pub struct Service { edited_message: TelegramObserverService, channel_post: TelegramObserverService, edited_channel_post: TelegramObserverService, + message_reaction: TelegramObserverService, + message_reaction_count: TelegramObserverService, inline_query: TelegramObserverService, chosen_inline_result: TelegramObserverService, callback_query: TelegramObserverService, @@ -714,6 +744,9 @@ pub struct Service { my_chat_member: TelegramObserverService, chat_member: TelegramObserverService, chat_join_request: TelegramObserverService, + chat_boost: TelegramObserverService, + removed_chat_boost: TelegramObserverService, + update: TelegramObserverService, startup: SimpleObserverService, @@ -926,12 +959,14 @@ impl PropagateEvent for Service { impl Service { #[must_use] - pub const fn telegram_observers(&self) -> [&TelegramObserverService; 15] { + pub const fn telegram_observers(&self) -> [&TelegramObserverService; 19] { [ &self.message, &self.edited_message, &self.channel_post, &self.edited_channel_post, + &self.message_reaction, + &self.message_reaction_count, &self.inline_query, &self.chosen_inline_result, &self.callback_query, @@ -942,6 +977,8 @@ impl Service { &self.my_chat_member, &self.chat_member, &self.chat_join_request, + &self.chat_boost, + &self.removed_chat_boost, &self.update, ] } @@ -961,6 +998,8 @@ impl Service { UpdateType::EditedMessage => &self.edited_message, UpdateType::ChannelPost => &self.channel_post, UpdateType::EditedChannelPost => &self.edited_channel_post, + UpdateType::MessageReaction => &self.message_reaction, + UpdateType::MessageReactionCount => &self.message_reaction_count, UpdateType::InlineQuery => &self.inline_query, UpdateType::ChosenInlineResult => &self.chosen_inline_result, UpdateType::CallbackQuery => &self.callback_query, @@ -971,6 +1010,8 @@ impl Service { UpdateType::MyChatMember => &self.my_chat_member, UpdateType::ChatMember => &self.chat_member, UpdateType::ChatJoinRequest => &self.chat_join_request, + UpdateType::ChatBoost => &self.chat_boost, + UpdateType::RemovedChatBoost => &self.removed_chat_boost, } } } @@ -1029,6 +1070,8 @@ pub struct OuterMiddlewaresConfig { pub edited_message: Box<[Arc>]>, pub channel_post: Box<[Arc>]>, pub edited_channel_post: Box<[Arc>]>, + pub message_reaction: Box<[Arc>]>, + pub message_reaction_count: Box<[Arc>]>, pub inline_query: Box<[Arc>]>, pub chosen_inline_result: Box<[Arc>]>, pub callback_query: Box<[Arc>]>, @@ -1039,6 +1082,8 @@ pub struct OuterMiddlewaresConfig { pub my_chat_member: Box<[Arc>]>, pub chat_member: Box<[Arc>]>, pub chat_join_request: Box<[Arc>]>, + pub chat_boost: Box<[Arc>]>, + pub removed_chat_boost: Box<[Arc>]>, pub update: Box<[Arc>]>, } @@ -1071,6 +1116,8 @@ impl Clone for OuterMiddlewaresConfig { edited_message: self.edited_message.clone(), channel_post: self.channel_post.clone(), edited_channel_post: self.edited_channel_post.clone(), + message_reaction: self.message_reaction.clone(), + message_reaction_count: self.message_reaction_count.clone(), inline_query: self.inline_query.clone(), chosen_inline_result: self.chosen_inline_result.clone(), callback_query: self.callback_query.clone(), @@ -1081,6 +1128,8 @@ impl Clone for OuterMiddlewaresConfig { my_chat_member: self.my_chat_member.clone(), chat_member: self.chat_member.clone(), chat_join_request: self.chat_join_request.clone(), + chat_boost: self.chat_boost.clone(), + removed_chat_boost: self.removed_chat_boost.clone(), update: self.update.clone(), } } @@ -1091,6 +1140,8 @@ pub struct OuterMiddlewaresConfigBuilder { pub edited_message: Vec>>, pub channel_post: Vec>>, pub edited_channel_post: Vec>>, + pub message_reaction: Vec>>, + pub message_reaction_count: Vec>>, pub inline_query: Vec>>, pub chosen_inline_result: Vec>>, pub callback_query: Vec>>, @@ -1101,6 +1152,8 @@ pub struct OuterMiddlewaresConfigBuilder { pub my_chat_member: Vec>>, pub chat_member: Vec>>, pub chat_join_request: Vec>>, + pub chat_boost: Vec>>, + pub removed_chat_boost: Vec>>, pub update: Vec>>, } @@ -1129,6 +1182,18 @@ impl OuterMiddlewaresConfigBuilder { self } + #[must_use] + pub fn message_reaction(mut self, val: impl OuterMiddleware + 'static) -> Self { + self.message_reaction.push(Arc::new(val)); + self + } + + #[must_use] + pub fn message_reaction_count(mut self, val: impl OuterMiddleware + 'static) -> Self { + self.message_reaction_count.push(Arc::new(val)); + self + } + #[must_use] pub fn inline_query(mut self, val: impl OuterMiddleware + 'static) -> Self { self.inline_query.push(Arc::new(val)); @@ -1189,6 +1254,18 @@ impl OuterMiddlewaresConfigBuilder { self } + #[must_use] + pub fn chat_boost(mut self, val: impl OuterMiddleware + 'static) -> Self { + self.chat_boost.push(Arc::new(val)); + self + } + + #[must_use] + pub fn removed_chat_boost(mut self, val: impl OuterMiddleware + 'static) -> Self { + self.removed_chat_boost.push(Arc::new(val)); + self + } + #[must_use] pub fn update(mut self, val: impl OuterMiddleware + 'static) -> Self { self.update.push(Arc::new(val)); @@ -1202,6 +1279,8 @@ impl OuterMiddlewaresConfigBuilder { edited_message: self.edited_message.into(), channel_post: self.channel_post.into(), edited_channel_post: self.edited_channel_post.into(), + message_reaction: self.message_reaction.into(), + message_reaction_count: self.message_reaction_count.into(), inline_query: self.inline_query.into(), chosen_inline_result: self.chosen_inline_result.into(), callback_query: self.callback_query.into(), @@ -1212,6 +1291,8 @@ impl OuterMiddlewaresConfigBuilder { my_chat_member: self.my_chat_member.into(), chat_member: self.chat_member.into(), chat_join_request: self.chat_join_request.into(), + chat_boost: self.chat_boost.into(), + removed_chat_boost: self.removed_chat_boost.into(), update: self.update.into(), } } @@ -1225,6 +1306,8 @@ impl Default for OuterMiddlewaresConfigBuilder { edited_message: vec![], channel_post: vec![], edited_channel_post: vec![], + message_reaction: vec![], + message_reaction_count: vec![], inline_query: vec![], chosen_inline_result: vec![], callback_query: vec![], @@ -1235,6 +1318,8 @@ impl Default for OuterMiddlewaresConfigBuilder { my_chat_member: vec![], chat_member: vec![], chat_join_request: vec![], + chat_boost: vec![], + removed_chat_boost: vec![], update: vec![], } } @@ -1245,6 +1330,8 @@ pub struct InnerMiddlewaresConfig { pub edited_message: Box<[Arc>]>, pub channel_post: Box<[Arc>]>, pub edited_channel_post: Box<[Arc>]>, + pub message_reaction: Box<[Arc>]>, + pub message_reaction_count: Box<[Arc>]>, pub inline_query: Box<[Arc>]>, pub chosen_inline_result: Box<[Arc>]>, pub callback_query: Box<[Arc>]>, @@ -1255,6 +1342,8 @@ pub struct InnerMiddlewaresConfig { pub my_chat_member: Box<[Arc>]>, pub chat_member: Box<[Arc>]>, pub chat_join_request: Box<[Arc>]>, + pub chat_boost: Box<[Arc>]>, + pub removed_chat_boost: Box<[Arc>]>, pub update: Box<[Arc>]>, } @@ -1283,6 +1372,8 @@ where .edited_message(logging_middleware.clone()) .channel_post(logging_middleware.clone()) .edited_channel_post(logging_middleware.clone()) + .message_reaction(logging_middleware.clone()) + .message_reaction_count(logging_middleware.clone()) .inline_query(logging_middleware.clone()) .chosen_inline_result(logging_middleware.clone()) .callback_query(logging_middleware.clone()) @@ -1293,6 +1384,8 @@ where .my_chat_member(logging_middleware.clone()) .chat_member(logging_middleware.clone()) .chat_join_request(logging_middleware.clone()) + .chat_boost(logging_middleware.clone()) + .removed_chat_boost(logging_middleware.clone()) .update(logging_middleware) .build() } @@ -1305,6 +1398,8 @@ impl Clone for InnerMiddlewaresConfig { edited_message: self.edited_message.clone(), channel_post: self.channel_post.clone(), edited_channel_post: self.edited_channel_post.clone(), + message_reaction: self.message_reaction.clone(), + message_reaction_count: self.message_reaction_count.clone(), inline_query: self.inline_query.clone(), chosen_inline_result: self.chosen_inline_result.clone(), callback_query: self.callback_query.clone(), @@ -1315,6 +1410,8 @@ impl Clone for InnerMiddlewaresConfig { my_chat_member: self.my_chat_member.clone(), chat_member: self.chat_member.clone(), chat_join_request: self.chat_join_request.clone(), + chat_boost: self.chat_boost.clone(), + removed_chat_boost: self.removed_chat_boost.clone(), update: self.update.clone(), } } @@ -1325,6 +1422,8 @@ pub struct InnerMiddlewaresConfigBuilder { pub edited_message: Vec>>, pub channel_post: Vec>>, pub edited_channel_post: Vec>>, + pub message_reaction: Vec>>, + pub message_reaction_count: Vec>>, pub inline_query: Vec>>, pub chosen_inline_result: Vec>>, pub callback_query: Vec>>, @@ -1335,6 +1434,8 @@ pub struct InnerMiddlewaresConfigBuilder { pub my_chat_member: Vec>>, pub chat_member: Vec>>, pub chat_join_request: Vec>>, + pub chat_boost: Vec>>, + pub removed_chat_boost: Vec>>, pub update: Vec>>, } @@ -1363,6 +1464,18 @@ impl InnerMiddlewaresConfigBuilder { self } + #[must_use] + pub fn message_reaction(mut self, val: impl InnerMiddleware + 'static) -> Self { + self.message_reaction.push(Arc::new(val)); + self + } + + #[must_use] + pub fn message_reaction_count(mut self, val: impl InnerMiddleware + 'static) -> Self { + self.message_reaction_count.push(Arc::new(val)); + self + } + #[must_use] pub fn inline_query(mut self, val: impl InnerMiddleware + 'static) -> Self { self.inline_query.push(Arc::new(val)); @@ -1423,6 +1536,18 @@ impl InnerMiddlewaresConfigBuilder { self } + #[must_use] + pub fn chat_boost(mut self, val: impl InnerMiddleware + 'static) -> Self { + self.chat_boost.push(Arc::new(val)); + self + } + + #[must_use] + pub fn removed_chat_boost(mut self, val: impl InnerMiddleware + 'static) -> Self { + self.removed_chat_boost.push(Arc::new(val)); + self + } + #[must_use] pub fn update(mut self, val: impl InnerMiddleware + 'static) -> Self { self.update.push(Arc::new(val)); @@ -1436,6 +1561,8 @@ impl InnerMiddlewaresConfigBuilder { edited_message: self.edited_message.into(), channel_post: self.channel_post.into(), edited_channel_post: self.edited_channel_post.into(), + message_reaction: self.message_reaction.into(), + message_reaction_count: self.message_reaction_count.into(), inline_query: self.inline_query.into(), chosen_inline_result: self.chosen_inline_result.into(), callback_query: self.callback_query.into(), @@ -1446,6 +1573,8 @@ impl InnerMiddlewaresConfigBuilder { my_chat_member: self.my_chat_member.into(), chat_member: self.chat_member.into(), chat_join_request: self.chat_join_request.into(), + chat_boost: self.chat_boost.into(), + removed_chat_boost: self.removed_chat_boost.into(), update: self.update.into(), } } @@ -1459,6 +1588,8 @@ impl Default for InnerMiddlewaresConfigBuilder { edited_message: vec![], channel_post: vec![], edited_channel_post: vec![], + message_reaction: vec![], + message_reaction_count: vec![], inline_query: vec![], chosen_inline_result: vec![], callback_query: vec![], @@ -1469,6 +1600,8 @@ impl Default for InnerMiddlewaresConfigBuilder { my_chat_member: vec![], chat_member: vec![], chat_join_request: vec![], + chat_boost: vec![], + removed_chat_boost: vec![], update: vec![], } } @@ -1588,6 +1721,8 @@ mod tests { router.edited_message.register(telegram_handler); router.channel_post.register(telegram_handler); router.edited_channel_post.register(telegram_handler); + router.message_reaction.register(telegram_handler); + router.message_reaction_count.register(telegram_handler); router.inline_query.register(telegram_handler); router.chosen_inline_result.register(telegram_handler); router.callback_query.register(telegram_handler); @@ -1598,6 +1733,8 @@ mod tests { router.my_chat_member.register(telegram_handler); router.chat_member.register(telegram_handler); router.chat_join_request.register(telegram_handler); + router.chat_boost.register(telegram_handler); + router.removed_chat_boost.register(telegram_handler); router.update.register(telegram_handler); // Event observers router.startup.register(simple_handler, ()); diff --git a/src/types.rs b/src/types.rs index 04e1230e..d168aa08 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,7 +8,7 @@ //! - is a tagged union, it will be wrapped in `enum` with variants named as in the documentation, //! - is a type with optional fields, it can be represented as an enum with variants for specific cases (check [`Message`] for example). //! - is a string, it will be represented as [`String`] or [`str`] wrapped in [`Box`], -//! - is a number, it will be represented as [`i64`] +//! - is a number, it will be represented as [`i64`] or [`u16`] if it's UTF-16 code unit, //! - is a float, it will be represented as [`f64`], //! - is a boolean, it will be represented as [`bool`], //! - is a file, it will be represented as [`InputFile`], @@ -35,8 +35,7 @@ //! - [`MessageText`] //! - [`MessageAnimation`] //! and so on... (see [`Message`] for full list of variants). -//! Each variant has an implementation of [`From`] trait to convert from the variant to the [`Message`] -//! and [`Into`] trait to convert from the [`Message`] to the variant. +//! Each variant has an implementation of [`Into`] trait to convert from the variant to the [`Message`]. pub mod animation; pub mod audio; @@ -56,6 +55,13 @@ pub mod callback_game; pub mod callback_query; pub mod chat; pub mod chat_administrator_rights; +pub mod chat_boost; +pub mod chat_boost_removed; +pub mod chat_boost_source; +pub mod chat_boost_source_gift_code; +pub mod chat_boost_source_giveaway; +pub mod chat_boost_source_premium; +pub mod chat_boost_updated; pub mod chat_id_kind; pub mod chat_invite_link; pub mod chat_join_request; @@ -77,6 +83,7 @@ pub mod dice; pub mod document; pub mod encrypted_credentials; pub mod encrypted_passport_element; +pub mod external_reply_info; pub mod file; pub mod force_reply; pub mod forum_topic; @@ -88,6 +95,11 @@ pub mod game; pub mod game_high_score; pub mod general_forum_topic_hidden; pub mod general_forum_topic_unhidden; +pub mod giveaway; +pub mod giveaway_completed; +pub mod giveaway_created; +pub mod giveaway_winners; +pub mod inaccessible_message; pub mod inline_keyboard_button; pub mod inline_keyboard_markup; pub mod inline_query; @@ -131,11 +143,13 @@ pub mod invoice; pub mod keyboard_button; pub mod keyboard_button_poll_type; pub mod keyboard_button_request_chat; -pub mod keyboard_button_request_user; +pub mod keyboard_button_request_users; pub mod labeled_price; +pub mod link_preview_options; pub mod location; pub mod login_url; pub mod mask_position; +pub mod maybe_inaccessible_message; pub mod menu_button; pub mod menu_button_commands; pub mod menu_button_default; @@ -145,6 +159,13 @@ pub mod message_auto_delete_timer_changed; pub mod message_entity; pub mod message_id; pub mod message_or_true; +pub mod message_origin; +pub mod message_origin_channel; +pub mod message_origin_chat; +pub mod message_origin_hidden_user; +pub mod message_origin_user; +pub mod message_reaction_count_updated; +pub mod message_reaction_updated; pub mod order_info; pub mod passport_data; pub mod passport_element_error; @@ -164,9 +185,14 @@ pub mod poll_answer; pub mod poll_option; pub mod pre_checkout_query; pub mod proximity_alert_triggered; +pub mod reaction_count; +pub mod reaction_type; +pub mod reaction_type_custom_emoji; +pub mod reaction_type_emoji; pub mod reply_keyboard_markup; pub mod reply_keyboard_remove; pub mod reply_markup; +pub mod reply_parameters; pub mod response_parameters; pub mod sent_web_app_message; pub mod shipping_address; @@ -177,10 +203,13 @@ pub mod sticker_set; pub mod story; pub mod successful_payment; pub mod switch_inline_query_chosen_chat; +pub mod text_quote; pub mod update; pub mod user; +pub mod user_chat_boosts; pub mod user_profile_photos; pub mod user_shared; +pub mod users_shared; pub mod venue; pub mod video; pub mod video_chat_ended; @@ -216,6 +245,13 @@ pub use chat::{ Supergroup as ChatSupergroup, }; pub use chat_administrator_rights::ChatAdministratorRights; +pub use chat_boost::ChatBoost; +pub use chat_boost_removed::ChatBoostRemoved; +pub use chat_boost_source::ChatBoostSource; +pub use chat_boost_source_gift_code::ChatBoostSourceGiftCode; +pub use chat_boost_source_giveaway::ChatBoostSourceGiveaway; +pub use chat_boost_source_premium::ChatBoostSourcePremium; +pub use chat_boost_updated::ChatBoostUpdated; pub use chat_id_kind::ChatIdKind; pub use chat_invite_link::ChatInviteLink; pub use chat_join_request::ChatJoinRequest; @@ -250,6 +286,17 @@ pub use encrypted_passport_element::{ TemporaryRegistration as EncryptedPassportElementTemporaryRegistration, UtilityBill as EncryptedPassportElementUtilityBill, }; +pub use external_reply_info::{ + Animation as ExternalReplyInfoAnimation, Audio as ExternalReplyInfoAudio, + Contact as ExternalReplyInfoContact, Dice as ExternalReplyInfoDice, + Document as ExternalReplyInfoDocument, ExternalReplyInfo, Game as ExternalReplyInfoGame, + Giveaway as ExternalReplyInfoGiveaway, GiveawayWinners as ExternalReplyInfoGiveawayWinners, + Invoice as ExternalReplyInfoInvoice, Location as ExternalReplyInfoLocation, + Photo as ExternalReplyInfoPhoto, Poll as ExternalReplyInfoPoll, + Sticker as ExternalReplyInfoSticker, Story as ExternalReplyInfoStory, + Venue as ExternalReplyInfoVenue, Video as ExternalReplyInfoVideo, + VideoNote as ExternalReplyInfoVideoNote, Voice as ExternalReplyInfoVoice, +}; pub use file::File; pub use force_reply::ForceReply; pub use forum_topic::ForumTopic; @@ -261,6 +308,11 @@ pub use game::Game; pub use game_high_score::GameHighScore; pub use general_forum_topic_hidden::GeneralForumTopicHidden; pub use general_forum_topic_unhidden::GeneralForumTopicUnhidden; +pub use giveaway::Giveaway; +pub use giveaway_completed::GiveawayCompleted; +pub use giveaway_created::GiveawayCreated; +pub use giveaway_winners::GiveawayWinners; +pub use inaccessible_message::InaccessibleMessage; pub use inline_keyboard_button::InlineKeyboardButton; pub use inline_keyboard_markup::InlineKeyboardMarkup; pub use inline_query::InlineQuery; @@ -307,11 +359,13 @@ pub use invoice::Invoice; pub use keyboard_button::KeyboardButton; pub use keyboard_button_poll_type::KeyboardButtonPollType; pub use keyboard_button_request_chat::KeyboardButtonRequestChat; -pub use keyboard_button_request_user::KeyboardButtonRequestUser; +pub use keyboard_button_request_users::KeyboardButtonRequestUsers; pub use labeled_price::LabeledPrice; +pub use link_preview_options::LinkPreviewOptions; pub use location::Location; pub use login_url::LoginUrl; pub use mask_position::MaskPosition; +pub use maybe_inaccessible_message::MaybeInaccessibleMessage; pub use menu_button::MenuButton; pub use menu_button_commands::MenuButtonCommands; pub use menu_button_default::MenuButtonDefault; @@ -321,14 +375,14 @@ pub use message::{ ChannelChatCreated as MessageChannelChatCreated, ChatShared as MessageChatShared, ConnectedWebsite as MessageConnectedWebsite, Contact as MessageContact, DeleteChatPhoto as MessageDeleteChatPhoto, Dice as MessageDice, Document as MessageDocument, - Empty as MessageEmpty, ForumTopicClosed as MessageForumTopicClosed, - ForumTopicCreated as MessageForumTopicCreated, ForumTopicEdited as MessageForumTopicEdited, - ForumTopicReopened as MessageForumTopicReopened, Forward as MessageForward, - ForwardedFrom as MessageForwardedFrom, Game as MessageGame, - GeneralForumTopicHidden as MessageGeneralForumTopicHidden, - GeneralForumTopicUnhidden as MessageGeneralForumTopicUnhidden, - GroupChatCreated as MessageGroupChatCreated, Invoice as MessageInvoice, - LeftChatMember as MessageLeftChatMember, Location as MessageLocation, Message, + ForumTopicClosed as MessageForumTopicClosed, ForumTopicCreated as MessageForumTopicCreated, + ForumTopicEdited as MessageForumTopicEdited, ForumTopicReopened as MessageForumTopicReopened, + Game as MessageGame, GeneralForumTopicHidden as MessageGeneralForumTopicHidden, + GeneralForumTopicUnhidden as MessageGeneralForumTopicUnhidden, Giveaway as MessageGiveaway, + GiveawayCompleted as MessageGiveawayCompleted, GiveawayCreated as MessageGiveawayCreated, + GiveawayWinners as MessageGiveawayWinners, GroupChatCreated as MessageGroupChatCreated, + Invoice as MessageInvoice, LeftChatMember as MessageLeftChatMember, + Location as MessageLocation, Message, MessageAutoDeleteTimerChanged as MessageMessageAutoDeleteTimerChanged, MigrateFromChat as MessageMigrateFromChat, MigrateToChat as MessageMigrateToChat, NewChatMembers as MessageNewChatMembers, NewChatPhoto as MessageNewChatPhoto, @@ -337,7 +391,7 @@ pub use message::{ ProximityAlertTriggered as MessageProximityAlertTriggered, Sticker as MessageSticker, Story as MessageStory, SuccessfulPayment as MessageSuccessfulPayment, SupergroupChatCreated as MessageSupergroupChatCreated, Text as MessageText, - UserShared as MessageUserShared, Venue as MessageVenue, Video as MessageVideo, + UsersShared as MessageUsersShared, Venue as MessageVenue, Video as MessageVideo, VideoChatEnded as MessageVideoChatEnded, VideoChatParticipantsInvited as MessageVideoChatParticipantsInvited, VideoChatScheduled as MessageVideoChatScheduled, VideoChatStarted as MessageVideoChatStarted, @@ -352,6 +406,13 @@ pub use message_entity::{ }; pub use message_id::MessageId; pub use message_or_true::MessageOrTrue; +pub use message_origin::MessageOrigin; +pub use message_origin_channel::MessageOriginChannel; +pub use message_origin_chat::MessageOriginChat; +pub use message_origin_hidden_user::MessageOriginHiddenUser; +pub use message_origin_user::MessageOriginUser; +pub use message_reaction_count_updated::MessageReactionCountUpdated; +pub use message_reaction_updated::MessageReactionUpdated; pub use order_info::OrderInfo; pub use passport_data::PassportData; pub use passport_element_error::PassportElementError; @@ -387,9 +448,14 @@ pub use poll_answer::PollAnswer; pub use poll_option::PollOption; pub use pre_checkout_query::PreCheckoutQuery; pub use proximity_alert_triggered::ProximityAlertTriggered; +pub use reaction_count::ReactionCount; +pub use reaction_type::ReactionType; +pub use reaction_type_custom_emoji::ReactionTypeCustomEmoji; +pub use reaction_type_emoji::ReactionTypeEmoji; pub use reply_keyboard_markup::ReplyKeyboardMarkup; pub use reply_keyboard_remove::ReplyKeyboardRemove; pub use reply_markup::ReplyMarkup; +pub use reply_parameters::ReplyParameters; pub use response_parameters::ResponseParameters; pub use sent_web_app_message::SentWebAppMessage; pub use shipping_address::ShippingAddress; @@ -400,10 +466,13 @@ pub use sticker_set::StickerSet; pub use story::Story; pub use successful_payment::SuccessfulPayment; pub use switch_inline_query_chosen_chat::SwitchInlineQueryChosenChat; +pub use text_quote::TextQuote; pub use update::{Kind as UpdateKind, Update}; pub use user::User; +pub use user_chat_boosts::UserChatBoosts; pub use user_profile_photos::UserProfilePhotos; pub use user_shared::UserShared; +pub use users_shared::UsersShared; pub use venue::Venue; pub use video::Video; pub use video_chat_ended::VideoChatEnded; diff --git a/src/types/callback_query.rs b/src/types/callback_query.rs index cf296dc9..df5a7f0e 100644 --- a/src/types/callback_query.rs +++ b/src/types/callback_query.rs @@ -1,4 +1,4 @@ -use super::{Message, Update, UpdateKind, User}; +use super::{InaccessibleMessage, MaybeInaccessibleMessage, Update, UpdateKind, User}; use crate::errors::ConvertToTypeError; @@ -8,8 +8,6 @@ use serde::Deserialize; /// **NOTE:** After the user presses a callback button, Telegram clients will display a progress bar until you call [`AnswerCallbackQuery`](crate::methods::AnswerCallbackQuery). It is, therefore, necessary to react by calling [`AnswerCallbackQuery`](crate::methods::AnswerCallbackQuery) even if no notification to the user is needed (e.g., without specifying any of the optional parameters). /// # Documentation /// -/// # Warnings -/// This structure has so big size, so it's recommended to use it inside [`std::sync::Arc`], [`Box`] and other smart pointers #[derive(Debug, Default, Clone, PartialEq, Deserialize)] pub struct CallbackQuery { /// Unique identifier for this query @@ -18,8 +16,8 @@ pub struct CallbackQuery { pub from: User, /// Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in [`games`](https://core.telegram.org/bots/api#games). pub chat_instance: Box, - /// Message with the callback button that originated the query. Note that message content and message date will not be available if the message is too old - pub message: Option, + /// Message sent by the bot with the callback button that originated the query + pub message: Option, /// Identifier of the message sent via the bot in inline mode, that originated the query. pub inline_message_id: Option>, /// Data associated with the callback button. Be aware that the message originated the query can contain no callback buttons with this data. @@ -32,7 +30,12 @@ impl CallbackQuery { #[must_use] pub const fn chat_id(&self) -> Option { if let Some(message) = &self.message { - Some(message.chat().id()) + match message { + MaybeInaccessibleMessage::Message(message) => Some(message.chat().id()), + MaybeInaccessibleMessage::InaccessibleMessage(InaccessibleMessage { + chat, .. + }) => Some(chat.id()), + } } else { None } @@ -41,7 +44,12 @@ impl CallbackQuery { #[must_use] pub const fn message_id(&self) -> Option { if let Some(message) = &self.message { - Some(message.id()) + match message { + MaybeInaccessibleMessage::Message(message) => Some(message.id()), + MaybeInaccessibleMessage::InaccessibleMessage(InaccessibleMessage { + id, .. + }) => Some(*id), + } } else { None } @@ -50,7 +58,10 @@ impl CallbackQuery { #[must_use] pub fn message_text(&self) -> Option<&str> { if let Some(message) = &self.message { - message.text() + match message { + MaybeInaccessibleMessage::Message(message) => message.text(), + MaybeInaccessibleMessage::InaccessibleMessage(_) => None, + } } else { None } @@ -59,7 +70,10 @@ impl CallbackQuery { #[must_use] pub fn message_caption(&self) -> Option<&str> { if let Some(message) = &self.message { - message.caption() + match message { + MaybeInaccessibleMessage::Message(message) => message.caption(), + MaybeInaccessibleMessage::InaccessibleMessage(_) => None, + } } else { None } @@ -68,7 +82,10 @@ impl CallbackQuery { #[must_use] pub fn message_text_or_caption(&self) -> Option<&str> { if let Some(message) = &self.message { - message.text_or_caption() + match message { + MaybeInaccessibleMessage::Message(message) => message.text_or_caption(), + MaybeInaccessibleMessage::InaccessibleMessage(_) => None, + } } else { None } diff --git a/src/types/chat.rs b/src/types/chat.rs index 69c5e453..74bf6e65 100644 --- a/src/types/chat.rs +++ b/src/types/chat.rs @@ -28,6 +28,14 @@ pub struct Private { pub photo: Option, /// If non-empty, the list of all [active chat usernames](https://telegram.org/blog/topics-in-groups-collectible-usernames/ru?ln=a#collectible-usernames). Returned only in [`GetChat`](crate::methods::GetChat). pub active_usernames: Option]>>, + /// Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See [accent colors](https://core.telegram.org/bots/api#accent-colors) for more details. Returned only in [`GetChat`](crate::methods::GetChat). Always returned in [`GetChat`](crate::methods::GetChat). + pub accent_color_id: Option, + /// Custom emoji identifier of emoji chosen by the chat for the reply header and link preview background. Returned only in [`GetChat`](crate::methods::GetChat). + pub background_custom_emoji_id: Option>, + /// Identifier of the accent color for the chat's profile background. See [profile accent colors](https://core.telegram.org/bots/api#profile-accent-colors) for more details. Returned only in [`GetChat`](crate::methods::GetChat). + pub profile_accent_color_id: Option, + /// Custom emoji identifier of the emoji chosen by the chat for its profile background. Returned only in [`GetChat`](crate::methods::GetChat). + pub profile_background_custom_emoji_id: Option>, /// Custom emoji identifier of emoji status of the other party. Returned only in [`GetChat`](crate::methods::GetChat). pub emoji_status_custom_emoji_id: Option>, /// Expiration date of the emoji status of the other party in Unix time, if any. Returned only in [`GetChat`](crate::methods::GetChat). @@ -66,6 +74,8 @@ pub struct Group { pub has_hidden_members: Option, /// `true`, if messages from the chat can't be forwarded to other chats. Returned only in [`GetChat`](crate::methods::GetChat). pub has_protected_content: Option, + /// `true`, if new chat members will have access to old messages; available only to chat administrators. Returned only in [`GetChat`](crate::methods::GetChat). + pub has_visible_history: Option, } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -82,6 +92,20 @@ pub struct Supergroup { pub photo: Option, /// If non-empty, the list of all [active chat usernames](https://telegram.org/blog/topics-in-groups-collectible-usernames/ru?ln=a#collectible-usernames). Returned only in [`GetChat`](crate::methods::GetChat). pub active_usernames: Option]>>, + /// List of available reactions allowed in the chat. If omitted, then all [emoji reactions](https://core.telegram.org/bots/api#reactiontypeemoji) are allowed. Returned only in [`GetChat`](crate::methods::GetChat). + pub available_reactions: Option]>>, + /// Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See [accent colors](https://core.telegram.org/bots/api#accent-colors) for more details. Returned only in [`GetChat`](crate::methods::GetChat). Always returned in [`GetChat`](crate::methods::GetChat). + pub accent_color_id: Option, + /// Custom emoji identifier of emoji chosen by the chat for the reply header and link preview background. Returned only in [`GetChat`](crate::methods::GetChat). + pub background_custom_emoji_id: Option>, + /// Identifier of the accent color for the chat's profile background. See [profile accent colors](https://core.telegram.org/bots/api#profile-accent-colors) for more details. Returned only in [`GetChat`](crate::methods::GetChat). + pub profile_accent_color_id: Option, + /// Custom emoji identifier of the emoji chosen by the chat for its profile background. Returned only in [`GetChat`](crate::methods::GetChat). + pub profile_background_custom_emoji_id: Option>, + /// Custom emoji identifier of the emoji status. Returned only in [`GetChat`](crate::methods::GetChat). + pub emoji_status_custom_emoji_id: Option>, + /// Expiration date of the emoji status in Unix time, if any. Returned only in [`GetChat`](crate::methods::GetChat). + pub emoji_status_expiration_date: Option, /// `true`, if users need to join the supergroup before they can send messages. Returned only in [`GetChat`](crate::methods::GetChat). pub join_to_send_messages: Option, /// `true`, if all users directly joining the supergroup need to be approved by supergroup administrators. Returned only in [`GetChat`](crate::methods::GetChat). @@ -104,6 +128,8 @@ pub struct Supergroup { pub has_hidden_members: Option, /// `true`, if messages from the chat can't be forwarded to other chats. Returned only in [`GetChat`](crate::methods::GetChat). pub has_protected_content: Option, + /// `true`, if new chat members will have access to old messages; available only to chat administrators. Returned only in [`GetChat`](crate::methods::GetChat). + pub has_visible_history: Option, /// Name of group sticker set. Returned only in [`GetChat`](crate::methods::GetChat). pub sticker_set_name: Option>, /// `true`, if the bot can change the group sticker set. Returned only in [`GetChat`](crate::methods::GetChat). @@ -126,6 +152,20 @@ pub struct Channel { pub photo: Option, /// If non-empty, the list of all [active chat usernames](https://telegram.org/blog/topics-in-groups-collectible-usernames/ru?ln=a#collectible-usernames). Returned only in [`GetChat`](crate::methods::GetChat). pub active_usernames: Option]>>, + /// List of available reactions allowed in the chat. If omitted, then all [emoji reactions](https://core.telegram.org/bots/api#reactiontypeemoji) are allowed. Returned only in [`GetChat`](crate::methods::GetChat). + pub available_reactions: Option]>>, + /// Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See [accent colors](https://core.telegram.org/bots/api#accent-colors) for more details. Returned only in [`GetChat`](crate::methods::GetChat). Always returned in [`GetChat`](crate::methods::GetChat). + pub accent_color_id: Option, + /// Custom emoji identifier of emoji chosen by the chat for the reply header and link preview background. Returned only in [`GetChat`](crate::methods::GetChat). + pub background_custom_emoji_id: Option>, + /// Identifier of the accent color for the chat's profile background. See [profile accent colors](https://core.telegram.org/bots/api#profile-accent-colors) for more details. Returned only in [`GetChat`](crate::methods::GetChat). + pub profile_accent_color_id: Option, + /// Custom emoji identifier of the emoji chosen by the chat for its profile background. Returned only in [`GetChat`](crate::methods::GetChat). + pub profile_background_custom_emoji_id: Option>, + /// Custom emoji identifier of the emoji status. Returned only in [`GetChat`](crate::methods::GetChat). + pub emoji_status_custom_emoji_id: Option>, + /// Expiration date of the emoji status in Unix time, if any. Returned only in [`GetChat`](crate::methods::GetChat). + pub emoji_status_expiration_date: Option, /// Description. Returned only in [`GetChat`](crate::methods::GetChat). pub description: Option>, /// Primary invite link. Returned only in [`GetChat`](crate::methods::GetChat). @@ -205,6 +245,105 @@ impl Chat { } } + #[must_use] + pub const fn available_reactions(&self) -> Option<&[Box]> { + match self { + Self::Group(_) | Self::Private(_) => None, + Self::Supergroup(Supergroup { + available_reactions, + .. + }) + | Self::Channel(Channel { + available_reactions, + .. + }) => match available_reactions { + Some(available_reactions) => Some(available_reactions), + None => None, + }, + } + } + + #[must_use] + pub const fn accent_color_id(&self) -> Option { + match self { + Self::Group(_) | Self::Private(_) => None, + Self::Supergroup(Supergroup { + accent_color_id, .. + }) + | Self::Channel(Channel { + accent_color_id, .. + }) => *accent_color_id, + } + } + + #[must_use] + pub const fn background_custom_emoji_id(&self) -> Option<&str> { + match self { + Self::Group(_) | Self::Private(_) => None, + Self::Supergroup(Supergroup { + background_custom_emoji_id, + .. + }) + | Self::Channel(Channel { + background_custom_emoji_id, + .. + }) => match background_custom_emoji_id { + Some(background_custom_emoji_id) => Some(background_custom_emoji_id), + None => None, + }, + } + } + + #[must_use] + pub const fn profile_accent_color_id(&self) -> Option { + match self { + Self::Group(_) | Self::Private(_) => None, + Self::Supergroup(Supergroup { + profile_accent_color_id, + .. + }) + | Self::Channel(Channel { + profile_accent_color_id, + .. + }) => *profile_accent_color_id, + } + } + + #[must_use] + pub const fn profile_background_custom_emoji_id(&self) -> Option<&str> { + match self { + Self::Group(_) | Self::Private(_) => None, + Self::Supergroup(Supergroup { + profile_background_custom_emoji_id, + .. + }) + | Self::Channel(Channel { + profile_background_custom_emoji_id, + .. + }) => match profile_background_custom_emoji_id { + Some(profile_background_custom_emoji_id) => { + Some(profile_background_custom_emoji_id) + } + None => None, + }, + } + } + + #[must_use] + pub const fn has_visible_history(&self) -> Option { + match self { + Self::Private(_) | Self::Channel(_) => None, + Self::Group(Group { + has_visible_history, + .. + }) + | Self::Supergroup(Supergroup { + has_visible_history, + .. + }) => *has_visible_history, + } + } + #[must_use] pub const fn description(&self) -> Option<&str> { match self { diff --git a/src/types/chat_boost.rs b/src/types/chat_boost.rs new file mode 100644 index 00000000..d853edcb --- /dev/null +++ b/src/types/chat_boost.rs @@ -0,0 +1,19 @@ +use super::ChatBoostSource; + +use serde::Deserialize; + +/// This object contains information about a chat boost. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct ChatBoost { + /// Unique identifier of the boost + #[serde(rename = "boost_id")] + pub id: i64, + /// Point in time (Unix timestamp) when the chat was boosted + pub add_date: i64, + /// Point in time (Unix timestamp) when the boost will automatically expire, unless the booster's Telegram Premium subscription is prolonged + pub expiration_date: i64, + /// Source of the added boost + pub source: ChatBoostSource, +} diff --git a/src/types/chat_boost_removed.rs b/src/types/chat_boost_removed.rs new file mode 100644 index 00000000..32ec57e0 --- /dev/null +++ b/src/types/chat_boost_removed.rs @@ -0,0 +1,32 @@ +use super::{Chat, ChatBoostSource, Update, UpdateKind}; + +use crate::errors::ConvertToTypeError; + +use serde::Deserialize; + +/// This object represents a boost removed from a chat. +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct ChatBoostRemoved { + /// Chat which was boosted + pub chat: Chat, + /// Unique identifier of the boost + #[serde(rename = "boost_id")] + pub id: i64, + /// Point in time (Unix timestamp) when the boost was removed + pub remove_date: i64, + /// Source of the removed boost + pub source: ChatBoostSource, +} + +impl TryFrom for ChatBoostRemoved { + type Error = ConvertToTypeError; + + fn try_from(update: Update) -> Result { + match update.kind { + UpdateKind::RemovedChatBoost(val) => Ok(val), + _ => Err(ConvertToTypeError::new("Update", "ChatBoostRemoved")), + } + } +} diff --git a/src/types/chat_boost_source.rs b/src/types/chat_boost_source.rs new file mode 100644 index 00000000..e307ec35 --- /dev/null +++ b/src/types/chat_boost_source.rs @@ -0,0 +1,38 @@ +use super::{ChatBoostSourceGiftCode, ChatBoostSourceGiveaway, ChatBoostSourcePremium}; + +use serde::Deserialize; + +/// This object describes the source of a chat boost. It can be one of +/// - [`ChatBoostSourcePremium`] +/// - [`ChatBoostSourceGiftCode`] +/// - [`ChatBoostSourceGiveaway`] +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +#[serde(tag = "source", rename_all = "snake_case")] +pub enum ChatBoostSource { + Premium(ChatBoostSourcePremium), + GiftCode(ChatBoostSourceGiftCode), + Giveaway(ChatBoostSourceGiveaway), +} + +impl From for ChatBoostSource { + #[must_use] + fn from(source: ChatBoostSourcePremium) -> Self { + Self::Premium(source) + } +} + +impl From for ChatBoostSource { + #[must_use] + fn from(source: ChatBoostSourceGiftCode) -> Self { + Self::GiftCode(source) + } +} + +impl From for ChatBoostSource { + #[must_use] + fn from(source: ChatBoostSourceGiveaway) -> Self { + Self::Giveaway(source) + } +} diff --git a/src/types/chat_boost_source_gift_code.rs b/src/types/chat_boost_source_gift_code.rs new file mode 100644 index 00000000..a597806b --- /dev/null +++ b/src/types/chat_boost_source_gift_code.rs @@ -0,0 +1,12 @@ +use super::User; + +use serde::Deserialize; + +/// The boost was obtained by the creation of Telegram Premium gift codes to boost a chat. Each such code boosts the chat 4 times for the duration of the corresponding Telegram Premium subscription. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct ChatBoostSourceGiftCode { + /// User for which the gift code was created + pub user: User, +} diff --git a/src/types/chat_boost_source_giveaway.rs b/src/types/chat_boost_source_giveaway.rs new file mode 100644 index 00000000..7011a4ac --- /dev/null +++ b/src/types/chat_boost_source_giveaway.rs @@ -0,0 +1,16 @@ +use super::User; + +use serde::Deserialize; + +/// The boost was obtained by the creation of a Telegram Premium giveaway. This boosts the chat 4 times for the duration of the corresponding Telegram Premium subscription. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct ChatBoostSourceGiveaway { + /// Identifier of a message in the chat with the giveaway; the message could have been deleted already. May be 0 if the message isn't sent yet. + pub giveaway_message_id: i64, + /// User that won the prize in the giveaway if any + pub user: Option, + /// `true`, if the giveaway was completed, but there was no user to win the prize + pub is_unclaimed: Option, +} diff --git a/src/types/chat_boost_source_premium.rs b/src/types/chat_boost_source_premium.rs new file mode 100644 index 00000000..4ffeed42 --- /dev/null +++ b/src/types/chat_boost_source_premium.rs @@ -0,0 +1,12 @@ +use super::User; + +use serde::Deserialize; + +/// The boost was obtained by subscribing to Telegram Premium or by gifting a Telegram Premium subscription to another user. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct ChatBoostSourcePremium { + /// User that boosted the chat + pub user: User, +} diff --git a/src/types/chat_boost_updated.rs b/src/types/chat_boost_updated.rs new file mode 100644 index 00000000..2447aaa1 --- /dev/null +++ b/src/types/chat_boost_updated.rs @@ -0,0 +1,27 @@ +use super::{Chat, ChatBoostSource, Update, UpdateKind}; + +use crate::errors::ConvertToTypeError; + +use serde::Deserialize; + +/// This object represents a boost added to a chat or changed. +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct ChatBoostUpdated { + /// Chat which was boosted + pub chat: Chat, + /// Infomation about the chat boost + pub boost: ChatBoostSource, +} + +impl TryFrom for ChatBoostUpdated { + type Error = ConvertToTypeError; + + fn try_from(update: Update) -> Result { + match update.kind { + UpdateKind::ChatBoost(val) => Ok(val), + _ => Err(ConvertToTypeError::new("Update", "ChatBoostUpdated")), + } + } +} diff --git a/src/types/chat_join_request.rs b/src/types/chat_join_request.rs index 03393c3c..b45afe87 100644 --- a/src/types/chat_join_request.rs +++ b/src/types/chat_join_request.rs @@ -7,8 +7,6 @@ use serde::Deserialize; /// Represents a join request sent to a chat. /// # Documentation /// -/// # Warnings -/// This structure has so big size, so it's recommended to use it inside [`std::sync::Arc`], [`Box`] and other smart pointers #[derive(Debug, Default, Clone, PartialEq, Deserialize)] pub struct ChatJoinRequest { /// Chat to which the request was sent diff --git a/src/types/chat_member_updated.rs b/src/types/chat_member_updated.rs index 4028227d..435f7846 100644 --- a/src/types/chat_member_updated.rs +++ b/src/types/chat_member_updated.rs @@ -7,8 +7,6 @@ use serde::Deserialize; /// This object represents changes in the status of a chat member. /// # Documentation /// -/// # Warnings -/// This structure has so big size, so it's recommended to use it inside [`std::sync::Arc`], [`Box`] and other smart pointers #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct ChatMemberUpdated { /// Chat the user belongs to diff --git a/src/types/external_reply_info.rs b/src/types/external_reply_info.rs new file mode 100644 index 00000000..b09eb803 --- /dev/null +++ b/src/types/external_reply_info.rs @@ -0,0 +1,944 @@ +use super::{Chat, LinkPreviewOptions, MessageEntity, MessageOrigin}; + +use crate::types; + +use serde::Deserialize; + +/// This object contains information about a message that is being replied to, which may come from another chat or forum topic +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +#[serde(untagged)] +pub enum ExternalReplyInfo { + Animation(Animation), + Audio(Audio), + Document(Document), + Photo(Photo), + Sticker(Sticker), + Story(Story), + Video(Video), + VideoNote(VideoNote), + Voice(Voice), + Contact(Contact), + Dice(Dice), + Game(Game), + Giveaway(Giveaway), + GiveawayWinners(GiveawayWinners), + Invoice(Invoice), + Venue(Venue), + Location(Location), + Poll(Poll), + Text(Text), +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Text { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Options used for link preview generation for the original message + pub link_preview_options: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Animation { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set + pub animation: types::Animation, + /// Caption + pub caption: Option>, + /// Special entities like usernames, URLs, bot commands, etc. that appear in the caption + #[serde(rename = "caption_entities")] + pub entities: Option>, + /// `true`, if the message media is covered by a spoiler animation + pub has_media_spoiler: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Audio { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Caption + pub caption: Option>, + /// Special entities like usernames, URLs, bot commands, etc. that appear in the caption + #[serde(rename = "caption_entities")] + pub entities: Option>, + /// Message is an audio file, information about the file + pub audio: types::Audio, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Document { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Caption + pub caption: Option>, + /// Special entities like usernames, URLs, bot commands, etc. that appear in the caption + #[serde(rename = "caption_entities")] + pub entities: Option>, + /// Message is a general file, information about the file + pub document: types::Document, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Photo { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a photo, available sizes of the photo + pub photo: Vec, + /// Caption + pub caption: Option>, + /// Special entities like usernames, URLs, bot commands, etc. that appear in the caption + #[serde(rename = "caption_entities")] + pub entities: Option>, + /// `true`, if the message media is covered by a spoiler animation + pub has_media_spoiler: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Sticker { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a sticker, information about the sticker + pub sticker: types::Sticker, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Story { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a forwarded story + pub story: types::Story, + /// `true`, if the message media is covered by a spoiler animation + pub has_media_spoiler: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Video { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Caption + pub caption: Option>, + /// Special entities like usernames, URLs, bot commands, etc. that appear in the caption + #[serde(rename = "caption_entities")] + pub entities: Option>, + /// Message is a video, information about the video + pub video: types::Video, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct VideoNote { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a [video note](https://telegram.org/blog/video-messages-and-telescope), information about the video message + pub video_note: types::VideoNote, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Voice { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Caption + pub caption: Option>, + /// Special entities like usernames, URLs, bot commands, etc. that appear in the caption + #[serde(rename = "caption_entities")] + pub entities: Option>, + /// Message is a voice message, information about the file + pub voice: types::Voice, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Contact { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a shared contact, information about the contact + pub contact: types::Contact, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Dice { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a dice with random value + pub dice: types::Dice, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Game { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a game, information about the game. [More about games](https://core.telegram.org/bots/api#games) + pub game: types::Game, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Giveaway { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a scheduled giveaway, information about the giveaway + pub giveaway: types::Giveaway, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct GiveawayWinners { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// A giveaway with public winners was completed + pub giveaway_winners: types::GiveawayWinners, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Invoice { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is an invoice for a [payment](https://core.telegram.org/bots/api#payments), information about the invoice. [More about payments](https://core.telegram.org/bots/api#payments) + pub invoice: types::Invoice, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Location { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a shared location, information about the location + pub location: types::Location, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Poll { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a native poll, information about the poll + pub poll: types::Poll, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Venue { + /// Origin of the message replied to by the given message + pub origin: MessageOrigin, + /// Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + pub chat: Option, + /// Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + pub message_id: Option, + /// Message is a venue, information about the venue + pub venue: types::Venue, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn deserialize_text() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + })]; + + for json in jsons { + let external_reply_info_kind: Text = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Text(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_animation() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "animation": { + "file_id": "test", + "file_unique_id": "test", + "width": 1, + "height": 1, + "duration": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Animation(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_audio() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "audio": { + "file_id": "test", + "file_unique_id": "test", + "duration": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Audio(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_document() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "document": { + "file_id": "test", + "file_unique_id": "test", + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Document(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_photo() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "photo": [{ + "file_id": "test", + "file_unique_id": "test", + "width": 1, + "height": 1, + }], + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Photo(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_sticker() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "sticker": { + "file_id": "test", + "file_unique_id": "test", + "type": "regular", + "width": 1, + "height": 1, + "is_animated": false, + "is_video": false, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Sticker(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_story() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "story": {}, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Story(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_video() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "video": { + "file_id": "test", + "file_unique_id": "test", + "width": 1, + "height": 1, + "duration": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Video(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_video_note() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "video_note": { + "file_id": "test", + "file_unique_id": "test", + "length": 1, + "duration": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::VideoNote(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_voice() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "voice": { + "file_id": "test", + "file_unique_id": "test", + "duration": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Voice(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_contact() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "contact": { + "phone_number": "test", + "first_name": "test", + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Contact(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_dice() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "dice": { + "emoji": "🎲", + "value": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Dice(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_game() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "game": { + "title": "test", + "description": "test", + "photo": [{ + "file_id": "test", + "file_unique_id": "test", + "width": 1, + "height": 1, + }], + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Game(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_giveaway() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "giveaway": { + "chats": [{ + "id": -1, + "title": "test", + "type": "channel", + }], + "winners_selection_date": 0, + "winner_count": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Giveaway(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_giveaway_winners() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "giveaway_winners": { + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "giveaway_message_id": 1, + "winners_selection_date": 0, + "winner_count": 1, + "winners": [{ + "id": 1, + "is_bot": false, + "first_name": "test", + }], + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::GiveawayWinners(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_invoice() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "invoice": { + "title": "test", + "description": "test", + "start_parameter": "test", + "currency": "test", + "total_amount": 1, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Invoice(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_venue() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "venue": { + "location": { + "latitude": 1.0, + "longitude": 1.0, + }, + "title": "test", + "address": "test", + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Venue(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_location() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "location": { + "latitude": 1.0, + "longitude": 1.0, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Location(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } + + #[test] + fn deserialize_poll() { + let jsons = [serde_json::json!({ + "origin": { + "type": "user", + "date": 0, + "sender_user": { + "id": 1, + "is_bot": false, + "first_name": "test", + }, + }, + "poll": { + "id": "test", + "question": "test", + "options": [ + { + "text": "test", + "voter_count": 1, + }, + { + "text": "test", + "voter_count": 1, + }, + ], + "total_voter_count": 2, + "is_closed": false, + "is_anonymous": false, + "type": "regular", + "allows_multiple_answers": false, + }, + })]; + + for json in jsons { + let external_reply_info_kind = serde_json::from_value(json.clone()).unwrap(); + let external_reply_info: ExternalReplyInfo = serde_json::from_value(json).unwrap(); + + match external_reply_info { + ExternalReplyInfo::Poll(external_reply_info) => { + assert_eq!(external_reply_info, external_reply_info_kind) + } + _ => panic!("Unexpected external reply info type: {external_reply_info:?}"), + } + } + } +} diff --git a/src/types/giveaway.rs b/src/types/giveaway.rs new file mode 100644 index 00000000..e7dfb5df --- /dev/null +++ b/src/types/giveaway.rs @@ -0,0 +1,26 @@ +use serde::Deserialize; + +use super::Chat; + +/// This object represents a message about a scheduled giveaway. +/// # Documentation +/// +#[derive(Debug, Default, Clone, PartialEq, Deserialize)] +pub struct Giveaway { + /// The list of chats which the user must join to participate in the giveaway + pub chats: Box<[Chat]>, + /// Point in time (Unix timestamp) when winners of the giveaway will be selected + pub winners_selection_date: i64, + /// The number of users which are supposed to be selected as winners of the giveaway + pub winner_count: i64, + /// `true`, if only users who join the chats after the giveaway started should be eligible to win + pub only_new_members: Option, + /// `true`, if the list of giveaway winners will be visible to everyone + pub has_public_winners: Option, + /// Description of additional giveaway prize + pub prize_description: Option>, + /// A list of two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes indicating the countries from which eligible users for the giveaway must come. If empty, then all users can participate in the giveaway. Users with a phone number that was bought on Fragment can always participate in giveaways. + pub country_codes: Option]>>, + /// The number of months the Telegram Premium subscription won from the giveaway will be active for + pub premium_subscription_month_count: Option, +} diff --git a/src/types/giveaway_completed.rs b/src/types/giveaway_completed.rs new file mode 100644 index 00000000..8386e2c2 --- /dev/null +++ b/src/types/giveaway_completed.rs @@ -0,0 +1,16 @@ +use super::Message; + +use serde::Deserialize; + +/// This object represents a service message about the completion of a giveaway without public winners. +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct GiveawayCompleted { + /// Number of winners in the giveaway + pub winner_count: i64, + /// Number of undistributed prizes + pub unclaimed_prize_count: Option, + /// Message with the giveaway that was completed, if it wasn't deleted + pub giveaway_message: Option>, +} diff --git a/src/types/giveaway_created.rs b/src/types/giveaway_created.rs new file mode 100644 index 00000000..27ca93cc --- /dev/null +++ b/src/types/giveaway_created.rs @@ -0,0 +1,7 @@ +use serde::Deserialize; + +/// This object represents a service message about the creation of a scheduled giveaway. Currently holds no information. +/// # Documentation +/// +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize)] +pub struct GiveawayCreated {} diff --git a/src/types/giveaway_winners.rs b/src/types/giveaway_winners.rs new file mode 100644 index 00000000..86d921f8 --- /dev/null +++ b/src/types/giveaway_winners.rs @@ -0,0 +1,32 @@ +use serde::Deserialize; + +use super::{Chat, User}; + +/// This object represents a message about the completion of a giveaway with public winners. +/// # Documentation +/// +#[derive(Debug, Default, Clone, PartialEq, Deserialize)] +pub struct GiveawayWinners { + /// The chat that created the giveaway + pub chat: Chat, + /// Identifier of the messsage with the giveaway in the chat + pub giveaway_message_id: i64, + /// Point in time (Unix timestamp) when winners of the giveaway were selected + pub winners_selection_date: i64, + /// Total number of winners in the giveaway + pub winner_count: i64, + /// List of up to 100 winners of the giveaway + pub winners: Box<[User]>, + /// The number of other chats the user had to join in order to be eligible for the giveaway + pub additional_chat_count: Option, + /// The number of months the Telegram Premium subscription won from the giveaway will be active for + pub premium_subscription_month_count: Option, + /// Number of undistributed prizes + pub unclaimed_prize_count: Option, + /// `true`, if only users who had joined the chats after the giveaway started were eligible to win + pub only_new_members: Option, + /// `true`, if the giveaway was canceled because the payment for it was refunded + pub was_refunded: Option, + /// Description of additional giveaway prize + pub additional_prize_description: Option>, +} diff --git a/src/types/inaccessible_message.rs b/src/types/inaccessible_message.rs new file mode 100644 index 00000000..fe17d9e8 --- /dev/null +++ b/src/types/inaccessible_message.rs @@ -0,0 +1,17 @@ +use super::Chat; + +use serde::Deserialize; + +/// This object describes a message that was deleted or is otherwise inaccessible to the bot. +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct InaccessibleMessage { + /// Chat the message belonged to + pub chat: Chat, + /// Unique message identifier inside the chat + #[serde(rename = "message_id")] + pub id: i64, + /// Always 0. The field can be used to differentiate regular and inaccessible messages. + pub date: i64, +} diff --git a/src/types/input_text_message_content.rs b/src/types/input_text_message_content.rs index f8dc773b..f522c9d6 100644 --- a/src/types/input_text_message_content.rs +++ b/src/types/input_text_message_content.rs @@ -1,4 +1,4 @@ -use super::MessageEntity; +use super::{LinkPreviewOptions, MessageEntity}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -15,8 +15,8 @@ pub struct InputTextMessageContent { pub parse_mode: Option, /// List of special entities that appear in message text, which can be specified instead of *parse_mode* pub entities: Option>, - /// Disables link previews for links in the sent message - pub disable_web_page_preview: Option, + /// Link preview generation options for the message + pub link_preview_options: Option, } impl InputTextMessageContent { @@ -26,7 +26,7 @@ impl InputTextMessageContent { message_text: message_text.into(), parse_mode: None, entities: None, - disable_web_page_preview: None, + link_preview_options: None, } } @@ -75,9 +75,9 @@ impl InputTextMessageContent { } #[must_use] - pub fn disable_web_page_preview(self, val: bool) -> Self { + pub fn link_preview_options(self, val: LinkPreviewOptions) -> Self { Self { - disable_web_page_preview: Some(val), + link_preview_options: Some(val), ..self } } @@ -107,9 +107,9 @@ impl InputTextMessageContent { } #[must_use] - pub fn disable_web_page_preview_option(self, val: Option) -> Self { + pub fn link_preview_options_option(self, val: Option) -> Self { Self { - disable_web_page_preview: val, + link_preview_options: val, ..self } } diff --git a/src/types/keyboard_button.rs b/src/types/keyboard_button.rs index 954f7440..cd4e1f28 100644 --- a/src/types/keyboard_button.rs +++ b/src/types/keyboard_button.rs @@ -1,5 +1,5 @@ use super::{ - KeyboardButtonPollType, KeyboardButtonRequestChat, KeyboardButtonRequestUser, WebAppInfo, + KeyboardButtonPollType, KeyboardButtonRequestChat, KeyboardButtonRequestUsers, WebAppInfo, }; use serde::{Deserialize, Serialize}; @@ -17,8 +17,8 @@ use serde_with::skip_serializing_none; pub struct KeyboardButton { /// Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed pub text: String, - /// If specified, pressing the button will open a list of suitable users. Tapping on any user will send their identifier to the bot in a `user_shared` service message. Available in private chats only. - pub request_user: Option, + /// If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a `users_shared` service message. Available in private chats only. + pub request_users: Option, /// If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a `chat_shared` service message. Available in private chats only. pub request_chat: Option, /// If `true`, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only. @@ -36,7 +36,7 @@ impl KeyboardButton { pub fn new(text: impl Into) -> Self { Self { text: text.into(), - request_user: None, + request_users: None, request_chat: None, request_contact: None, request_location: None, @@ -54,9 +54,9 @@ impl KeyboardButton { } #[must_use] - pub fn request_user(self, val: KeyboardButtonRequestUser) -> Self { + pub fn request_user(self, val: KeyboardButtonRequestUsers) -> Self { Self { - request_user: Some(val), + request_users: Some(val), ..self } } @@ -104,9 +104,9 @@ impl KeyboardButton { impl KeyboardButton { #[must_use] - pub fn request_user_option(self, val: Option) -> Self { + pub fn request_user_option(self, val: Option) -> Self { Self { - request_user: val, + request_users: val, ..self } } diff --git a/src/types/keyboard_button_request_chat.rs b/src/types/keyboard_button_request_chat.rs index cfb3a209..56dbdb47 100644 --- a/src/types/keyboard_button_request_chat.rs +++ b/src/types/keyboard_button_request_chat.rs @@ -9,11 +9,11 @@ use serde::{Deserialize, Serialize}; pub struct KeyboardButtonRequestChat { /// Signed 32-bit identifier of the request, which will be received back in the [`UserShared`](crate::types::UserShared) object. Must be unique within the message pub request_id: i64, - /// Pass `true` to request a channel chat, pass `False` to request a group or a supergroup chat. + /// Pass `true` to request a channel chat, pass `false` to request a group or a supergroup chat. pub chat_is_channel: bool, - /// Pass `true` to request a forum supergroup, pass `False` to request a non-forum chat. If not specified, no additional restrictions are applied. + /// Pass `true` to request a forum supergroup, pass `false` to request a non-forum chat. If not specified, no additional restrictions are applied. pub chat_is_forum: Option, - /// Pass `true` to request a supergroup or a channel with a username, pass `False` to request a chat without a username. If not specified, no additional restrictions are applied. + /// Pass `true` to request a supergroup or a channel with a username, pass `false` to request a chat without a username. If not specified, no additional restrictions are applied. pub chat_has_username: Option, /// Pass `true` to request a chat owned by the user. Otherwise, no additional restrictions are applied. pub chat_is_created: Option, diff --git a/src/types/keyboard_button_request_user.rs b/src/types/keyboard_button_request_user.rs deleted file mode 100644 index f7059ab0..00000000 --- a/src/types/keyboard_button_request_user.rs +++ /dev/null @@ -1,67 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// This object defines the criteria used to request a suitable user. The identifier of the selected user will be shared with the bot when the corresponding button is pressed. [`More about requesting users`](https://core.telegram.org/bots/features#chat-and-user-selection) -/// # Documentation -/// -#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] -pub struct KeyboardButtonRequestUser { - /// Signed 32-bit identifier of the request, which will be received back in the [`UserShared`](crate::types::UserShared) object. Must be unique within the message - pub request_id: i64, - /// Pass `true` to request a bot, pass `False` to request a regular user. If not specified, no additional restrictions are applied. - pub user_is_bot: Option, - /// Pass `true` to request a premium user, pass `False` to request a non-premium user. If not specified, no additional restrictions are applied. - pub user_is_premium: Option, -} - -impl KeyboardButtonRequestUser { - #[must_use] - pub fn new(request_id: i64) -> Self { - Self { - request_id, - user_is_bot: None, - user_is_premium: None, - } - } - - #[must_use] - pub fn request_id(self, val: i64) -> Self { - Self { - request_id: val, - ..self - } - } - - #[must_use] - pub fn user_is_bot(self, val: bool) -> Self { - Self { - user_is_bot: Some(val), - ..self - } - } - - #[must_use] - pub fn user_is_premium(self, val: bool) -> Self { - Self { - user_is_premium: Some(val), - ..self - } - } -} - -impl KeyboardButtonRequestUser { - #[must_use] - pub fn user_is_bot_option(self, val: Option) -> Self { - Self { - user_is_bot: val, - ..self - } - } - - #[must_use] - pub fn user_is_premium_option(self, val: Option) -> Self { - Self { - user_is_premium: val, - ..self - } - } -} diff --git a/src/types/keyboard_button_request_users.rs b/src/types/keyboard_button_request_users.rs new file mode 100644 index 00000000..80471c6f --- /dev/null +++ b/src/types/keyboard_button_request_users.rs @@ -0,0 +1,86 @@ +use serde::{Deserialize, Serialize}; + +/// This object defines the criteria used to request suitable users. The identifiers of the selected users will be shared with the bot when the corresponding button is pressed. [`More about requesting users`](https://core.telegram.org/bots/features#chat-and-user-selection) +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct KeyboardButtonRequestUsers { + /// Signed 32-bit identifier of the request that will be received back in the [`UserShared`](crate::types::UserShared) object. Must be unique within the message + pub request_id: i64, + /// Pass `true` to request bots, pass `false` to request regular users. If not specified, no additional restrictions are applied. + pub user_is_bot: Option, + /// Pass `true` to request premium users, pass `false` to request non-premium users. If not specified, no additional restrictions are applied. + pub user_is_premium: Option, + /// The maximum number of users to be selected; 1-10. Defaults to 1. + pub max_quantity: Option, +} + +impl KeyboardButtonRequestUsers { + #[must_use] + pub const fn new(request_id: i64) -> Self { + Self { + request_id, + user_is_bot: None, + user_is_premium: None, + max_quantity: None, + } + } + + #[must_use] + pub fn request_id(self, val: i64) -> Self { + Self { + request_id: val, + ..self + } + } + + #[must_use] + pub fn user_is_bot(self, val: bool) -> Self { + Self { + user_is_bot: Some(val), + ..self + } + } + + #[must_use] + pub fn user_is_premium(self, val: bool) -> Self { + Self { + user_is_premium: Some(val), + ..self + } + } + + #[must_use] + pub fn max_quantity(self, val: i64) -> Self { + Self { + max_quantity: Some(val), + ..self + } + } +} + +impl KeyboardButtonRequestUsers { + #[must_use] + pub fn user_is_bot_option(self, val: Option) -> Self { + Self { + user_is_bot: val, + ..self + } + } + + #[must_use] + pub fn user_is_premium_option(self, val: Option) -> Self { + Self { + user_is_premium: val, + ..self + } + } + + #[must_use] + pub fn max_quantity_option(self, val: Option) -> Self { + Self { + max_quantity: val, + ..self + } + } +} diff --git a/src/types/link_preview_options.rs b/src/types/link_preview_options.rs new file mode 100644 index 00000000..c48c4688 --- /dev/null +++ b/src/types/link_preview_options.rs @@ -0,0 +1,115 @@ +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; + +/// Describes the options used for link preview generation. +/// # Documentation +/// +#[skip_serializing_none] +#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +pub struct LinkPreviewOptions { + /// `true`, if the link preview is disabled + pub is_disabled: Option, + /// URL to use for the link preview. If empty, then the first URL found in the message text will be used + pub url: Option, + /// `true`, if the media in the link preview is suppposed to be shrunk; ignored if the URL isn't explicitly specified or media size change isn't supported for the preview + pub prefer_small_media: Option, + /// `true`, if the media in the link preview is suppposed to be enlarged; ignored if the URL isn't explicitly specified or media size change isn't supported for the preview + pub prefer_large_media: Option, + /// `true`, if the link preview must be shown above the message text; otherwise, the link preview will be shown below the message text + pub show_above_text: Option, +} + +impl LinkPreviewOptions { + #[must_use] + pub const fn new() -> Self { + Self { + is_disabled: None, + url: None, + prefer_small_media: None, + prefer_large_media: None, + show_above_text: None, + } + } + + #[must_use] + pub fn is_disabled(self, val: bool) -> Self { + Self { + is_disabled: Some(val), + ..self + } + } + + #[must_use] + pub fn url(self, val: impl Into) -> Self { + Self { + url: Some(val.into()), + ..self + } + } + + #[must_use] + pub fn prefer_small_media(self, val: bool) -> Self { + Self { + prefer_small_media: Some(val), + ..self + } + } + + #[must_use] + pub fn prefer_large_media(self, val: bool) -> Self { + Self { + prefer_large_media: Some(val), + ..self + } + } + + #[must_use] + pub fn show_above_text(self, val: bool) -> Self { + Self { + show_above_text: Some(val), + ..self + } + } +} + +impl LinkPreviewOptions { + #[must_use] + pub fn is_disabled_option(self, val: Option) -> Self { + Self { + is_disabled: val, + ..self + } + } + + #[must_use] + pub fn url_option(self, val: Option>) -> Self { + Self { + url: val.map(Into::into), + ..self + } + } + + #[must_use] + pub fn prefer_small_media_option(self, val: Option) -> Self { + Self { + prefer_small_media: val, + ..self + } + } + + #[must_use] + pub fn prefer_large_media_option(self, val: Option) -> Self { + Self { + prefer_large_media: val, + ..self + } + } + + #[must_use] + pub fn show_above_text_option(self, val: Option) -> Self { + Self { + show_above_text: val, + ..self + } + } +} diff --git a/src/types/location.rs b/src/types/location.rs index 51142809..81a10429 100644 --- a/src/types/location.rs +++ b/src/types/location.rs @@ -3,7 +3,6 @@ use serde::Deserialize; /// This object represents a point on the map. /// # Documentation /// - #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct Location { /// Longitude as defined by sender diff --git a/src/types/maybe_inaccessible_message.rs b/src/types/maybe_inaccessible_message.rs new file mode 100644 index 00000000..86425a1e --- /dev/null +++ b/src/types/maybe_inaccessible_message.rs @@ -0,0 +1,27 @@ +use super::{InaccessibleMessage, Message}; + +use serde::Deserialize; + +/// This object describes a message that can be inaccessible to the bot. It can be one of +/// - [`Message`] +/// - [`InaccessibleMessage`] +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +#[serde(untagged)] +pub enum MaybeInaccessibleMessage { + Message(Message), + InaccessibleMessage(InaccessibleMessage), +} + +impl From for MaybeInaccessibleMessage { + fn from(message: Message) -> Self { + Self::Message(message) + } +} + +impl From for MaybeInaccessibleMessage { + fn from(inaccessible_message: InaccessibleMessage) -> Self { + Self::InaccessibleMessage(inaccessible_message) + } +} diff --git a/src/types/message.rs b/src/types/message.rs index c3cd2b34..6c16defc 100644 --- a/src/types/message.rs +++ b/src/types/message.rs @@ -1,4 +1,7 @@ -use super::{Chat, InlineKeyboardMarkup, MessageEntity, PhotoSize, Update, UpdateKind, User}; +use super::{ + Chat, ExternalReplyInfo, InlineKeyboardMarkup, LinkPreviewOptions, MaybeInaccessibleMessage, + MessageEntity, MessageOrigin, PhotoSize, TextQuote, Update, UpdateKind, User, +}; use crate::{errors::ConvertToTypeError, types}; @@ -7,8 +10,6 @@ use serde::Deserialize; /// This object represents a message. /// # Documentation /// -/// # Notes -/// Note that message date will not be available if the message is too old in [callback queries](https://core.telegram.org/bots/api#callbackquery). #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] pub enum Message { @@ -42,7 +43,7 @@ pub enum Message { Pinned(Pinned), Invoice(Invoice), SuccessfulPayment(SuccessfulPayment), - UserShared(UserShared), + UsersShared(UsersShared), ChatShared(ChatShared), ConnectedWebsite(ConnectedWebsite), WriteAccessAllowed(WriteAccessAllowed), @@ -54,13 +55,15 @@ pub enum Message { ForumTopicReopened(ForumTopicReopened), GeneralForumTopicHidden(GeneralForumTopicHidden), GeneralForumTopicUnhidden(GeneralForumTopicUnhidden), + GiveawayCreated(GiveawayCreated), + Giveaway(Giveaway), + GiveawayWinners(GiveawayWinners), + GiveawayCompleted(GiveawayCompleted), VideoChatScheduled(VideoChatScheduled), VideoChatStarted(VideoChatStarted), VideoChatEnded(VideoChatEnded), VideoChatParticipantsInvited(VideoChatParticipantsInvited), WebAppData(WebAppData), - /// Note that message content and message date will not be available if the message is too old in [callback queries](https://core.telegram.org/bots/api#callbackquery). - Empty(Empty), } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -79,14 +82,18 @@ pub struct Animation { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -124,14 +131,18 @@ pub struct Audio { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -169,14 +180,16 @@ pub struct Contact { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Bot through which the message was sent pub via_bot: Option, /// `true`, if the message can't be forwarded @@ -205,14 +218,16 @@ pub struct Dice { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// `true`, if the message can't be forwarded pub has_protected_content: Option, /// Signature of the post author for messages in channels, or the custom title of an anonymous group administrator @@ -239,14 +254,18 @@ pub struct Document { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -284,12 +303,14 @@ pub struct Game { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -320,14 +341,16 @@ pub struct Poll { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Date the message was last edited in Unix time pub edit_date: Option, /// `true`, if the message can't be forwarded @@ -356,14 +379,16 @@ pub struct Venue { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -394,14 +419,16 @@ pub struct Location { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -432,14 +459,18 @@ pub struct Photo { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -505,12 +536,14 @@ pub struct Story { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Signature of the post author for messages in channels, or the custom title of an anonymous group administrator pub author_signature: Option>, /// Forwarded story @@ -533,14 +566,16 @@ pub struct Sticker { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Bot through which the message was sent pub via_bot: Option, /// `true`, if the message can't be forwarded @@ -567,14 +602,18 @@ pub struct Text { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -587,6 +626,8 @@ pub struct Text { pub text: Box, /// Special entities like usernames, URLs, bot commands, etc. that appear in the text pub entities: Option>, + /// Options used for link preview generation for the message, if it is a text message an + pub link_preview_options: Option, /// Inline keyboard attached to the message. `login_url` buttons are represented as ordinary `url` buttons. pub reply_markup: Option, } @@ -607,14 +648,18 @@ pub struct Video { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// Date the message was last edited in Unix time @@ -654,14 +699,16 @@ pub struct VideoNote { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// `true`, if the message can't be forwarded pub has_protected_content: Option, /// Signature of the post author for messages in channels, or the custom title of an anonymous group administrator @@ -688,14 +735,18 @@ pub struct Voice { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group pub is_automatic_forward: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// For replies that quote part of the original message, the quoted part of the message + pub quote: Option, /// Bot through which the message was sent pub via_bot: Option, /// `true`, if the message can't be forwarded @@ -729,8 +780,8 @@ pub struct MigrateToChat { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// The group has been migrated to a supergroup with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. @@ -754,8 +805,8 @@ pub struct MigrateFromChat { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// The supergroup has been migrated from a group with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. @@ -763,71 +814,6 @@ pub struct MigrateFromChat { pub from_chat_id: i64, } -#[derive(Debug, Default, Clone, PartialEq, Deserialize)] -pub struct Empty { - /// Unique message identifier inside this chat - #[serde(rename = "message_id")] - pub id: i64, - /// Unique identifier of a message thread to which the message belongs; for supergroups only - #[serde(rename = "message_thread_id")] - pub thread_id: Option, - /// Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. - pub from: Option, - /// Sender of the message, sent on behalf of a chat. For example, the channel itself for channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for messages automatically forwarded to the discussion group. For backward compatibility, the field *from* contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. - pub sender_chat: Option, - /// Date the message was sent in Unix time - pub date: i64, - /// Conversation the message belongs to - pub chat: Chat, - #[serde(flatten)] - pub forward: Option, - /// `true`, if the message is sent to a forum topic - pub is_topic_message: Option, - /// `true`, if the message is a channel post that was automatically forwarded to the connected discussion group - pub is_automatic_forward: Option, - /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. - pub reply_to_message: Option>, - /// Bot through which the message was sent - pub via_bot: Option, - /// Date the message was last edited in Unix time - pub edit_date: Option, -} - -#[derive(Debug, Clone, PartialEq, Deserialize)] -pub struct Forward { - /// For forwarded messages, date the original message was sent in Unix time - #[serde(rename = "forward_date")] - pub date: i64, - #[serde(flatten)] - pub from: ForwardedFrom, - /// For messages forwarded from channels, identifier of the original message in the channel - #[serde(rename = "forward_from_message_id")] - pub from_message_id: Option, - /// For forwarded messages that were originally sent in channels or by an anonymous chat administrator, signature of the message sender if present - #[serde(rename = "forward_signature")] - pub signature: Option>, -} - -#[derive(Debug, Clone, PartialEq, Deserialize)] -#[serde(untagged)] -pub enum ForwardedFrom { - /// For forwarded messages, sender of the original message - #[serde(rename = "forward_from")] - User(User), - /// For messages forwarded from channels or from anonymous administrators, information about the original sender chat - #[serde(rename = "forward_from_chat")] - Chat(Chat), - /// Sender's name for messages forwarded from users who disallow adding a link to their account in forwarded messages - #[serde(rename = "forward_sender_name")] - SenderName(Box), -} - -impl From for ForwardedFrom { - fn from(forward: Forward) -> Self { - forward.from - } -} - #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct NewChatMembers { /// Unique message identifier inside this chat @@ -1062,7 +1048,7 @@ pub struct Pinned { pub via_bot: Option, /// Specified message was pinned. Note that the Message object in this field will not contain further *reply_to_message* fields even if it is itself a reply. #[serde(rename = "pinned_message")] - pub message: Box, + pub message: Box, } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -1081,12 +1067,14 @@ pub struct Invoice { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. pub reply_to_message: Option>, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, /// Bot through which the message was sent pub via_bot: Option, /// `true`, if the message can't be forwarded @@ -1115,8 +1103,8 @@ pub struct SuccessfulPayment { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// For replies, the original message. Note that the [Message object](https://core.telegram.org/bots/api#message) in this field will not contain further *reply_to_message* fields even if it itself is a reply. @@ -1129,7 +1117,7 @@ pub struct SuccessfulPayment { } #[derive(Debug, Clone, PartialEq, Deserialize)] -pub struct UserShared { +pub struct UsersShared { /// Unique message identifier inside this chat #[serde(rename = "message_id")] pub id: i64, @@ -1146,9 +1134,9 @@ pub struct UserShared { pub chat: Chat, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, - /// Service message: a user was shared with the bot - #[serde(rename = "user_shared")] - pub shared: types::UserShared, + /// Service message: users were shared with the bot + #[serde(rename = "users_shared")] + pub shared: types::UsersShared, } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -1190,8 +1178,8 @@ pub struct ConnectedWebsite { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// Bot through which the message was sent @@ -1242,8 +1230,8 @@ pub struct PassportData { pub date: i64, /// Conversation the message belongs to pub chat: Chat, - #[serde(flatten)] - pub forward: Option, + /// Information about the original message for forwarded messages + pub forward_origin: Option, /// `true`, if the message is sent to a forum topic pub is_topic_message: Option, /// Signature of the post author for messages in channels, or the custom title of an anonymous group administrator @@ -1438,6 +1426,93 @@ pub struct GeneralForumTopicUnhidden { pub unhidden: types::GeneralForumTopicUnhidden, } +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct GiveawayCreated { + /// Unique message identifier inside this chat + #[serde(rename = "message_id")] + pub id: i64, + /// Unique identifier of a message thread to which the message belongs; for supergroups only + #[serde(rename = "message_thread_id")] + pub thread_id: Option, + /// Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub from: Option, + /// Sender of the message, sent on behalf of a chat. For example, the channel itself for channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for messages automatically forwarded to the discussion group. For backward compatibility, the field *from* contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub sender_chat: Option, + /// Date the message was sent in Unix time + pub date: i64, + /// Conversation the message belongs to + pub chat: Chat, + /// Service message: a scheduled giveaway was created + #[serde(rename = "giveaway_created")] + pub created: types::GiveawayCreated, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Giveaway { + /// Unique message identifier inside this chat + #[serde(rename = "message_id")] + pub id: i64, + /// Unique identifier of a message thread to which the message belongs; for supergroups only + #[serde(rename = "message_thread_id")] + pub thread_id: Option, + /// Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub from: Option, + /// Sender of the message, sent on behalf of a chat. For example, the channel itself for channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for messages automatically forwarded to the discussion group. For backward compatibility, the field *from* contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub sender_chat: Option, + /// Date the message was sent in Unix time + pub date: i64, + /// Conversation the message belongs to + pub chat: Chat, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// The message is a scheduled giveaway message + pub giveaway: types::Giveaway, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct GiveawayWinners { + /// Unique message identifier inside this chat + #[serde(rename = "message_id")] + pub id: i64, + /// Unique identifier of a message thread to which the message belongs; for supergroups only + #[serde(rename = "message_thread_id")] + pub thread_id: Option, + /// Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub from: Option, + /// Sender of the message, sent on behalf of a chat. For example, the channel itself for channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for messages automatically forwarded to the discussion group. For backward compatibility, the field *from* contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub sender_chat: Option, + /// Date the message was sent in Unix time + pub date: i64, + /// Conversation the message belongs to + pub chat: Chat, + /// Information about the message that is being replied to, which may come from another chat or forum topic + pub external_reply: Option, + /// A giveaway with public winners was completed + #[serde(rename = "giveaway_winners")] + pub winners: types::GiveawayWinners, +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct GiveawayCompleted { + /// Unique message identifier inside this chat + #[serde(rename = "message_id")] + pub id: i64, + /// Unique identifier of a message thread to which the message belongs; for supergroups only + #[serde(rename = "message_thread_id")] + pub thread_id: Option, + /// Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub from: Option, + /// Sender of the message, sent on behalf of a chat. For example, the channel itself for channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for messages automatically forwarded to the discussion group. For backward compatibility, the field *from* contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + pub sender_chat: Option, + /// Date the message was sent in Unix time + pub date: i64, + /// Conversation the message belongs to + pub chat: Chat, + /// Service message: a giveaway without public winners was completed + #[serde(rename = "giveaway_completed")] + pub completed: types::GiveawayCompleted, +} + #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct VideoChatScheduled { /// Unique message identifier inside this chat @@ -1585,7 +1660,7 @@ impl Message { | Message::Pinned(Pinned { id, .. }) | Message::Invoice(Invoice { id, .. }) | Message::SuccessfulPayment(SuccessfulPayment { id, .. }) - | Message::UserShared(UserShared { id, .. }) + | Message::UsersShared(UsersShared { id, .. }) | Message::ChatShared(ChatShared { id, .. }) | Message::ConnectedWebsite(ConnectedWebsite { id, .. }) | Message::WriteAccessAllowed(WriteAccessAllowed { id, .. }) @@ -1602,7 +1677,10 @@ impl Message { | Message::VideoChatEnded(VideoChatEnded { id, .. }) | Message::VideoChatParticipantsInvited(VideoChatParticipantsInvited { id, .. }) | Message::WebAppData(WebAppData { id, .. }) - | Message::Empty(Empty { id, .. }) => *id, + | Message::GiveawayCreated(GiveawayCreated { id, .. }) + | Message::Giveaway(Giveaway { id, .. }) + | Message::GiveawayWinners(GiveawayWinners { id, .. }) + | Message::GiveawayCompleted(GiveawayCompleted { id, .. }) => *id, } } @@ -1642,7 +1720,7 @@ impl Message { | Message::Pinned(Pinned { thread_id, .. }) | Message::Invoice(Invoice { thread_id, .. }) | Message::SuccessfulPayment(SuccessfulPayment { thread_id, .. }) - | Message::UserShared(UserShared { thread_id, .. }) + | Message::UsersShared(UsersShared { thread_id, .. }) | Message::ChatShared(ChatShared { thread_id, .. }) | Message::ConnectedWebsite(ConnectedWebsite { thread_id, .. }) | Message::WriteAccessAllowed(WriteAccessAllowed { thread_id, .. }) @@ -1662,7 +1740,10 @@ impl Message { .. }) | Message::WebAppData(WebAppData { thread_id, .. }) - | Message::Empty(Empty { thread_id, .. }) => *thread_id, + | Message::GiveawayCreated(GiveawayCreated { thread_id, .. }) + | Message::Giveaway(Giveaway { thread_id, .. }) + | Message::GiveawayWinners(GiveawayWinners { thread_id, .. }) + | Message::GiveawayCompleted(GiveawayCompleted { thread_id, .. }) => *thread_id, } } @@ -1701,7 +1782,7 @@ impl Message { | Message::Pinned(Pinned { date, .. }) | Message::Invoice(Invoice { date, .. }) | Message::SuccessfulPayment(SuccessfulPayment { date, .. }) - | Message::UserShared(UserShared { date, .. }) + | Message::UsersShared(UsersShared { date, .. }) | Message::ChatShared(ChatShared { date, .. }) | Message::ConnectedWebsite(ConnectedWebsite { date, .. }) | Message::WriteAccessAllowed(WriteAccessAllowed { date, .. }) @@ -1720,7 +1801,10 @@ impl Message { date, .. }) | Message::WebAppData(WebAppData { date, .. }) - | Message::Empty(Empty { date, .. }) => *date, + | Message::GiveawayCreated(GiveawayCreated { date, .. }) + | Message::Giveaway(Giveaway { date, .. }) + | Message::GiveawayWinners(GiveawayWinners { date, .. }) + | Message::GiveawayCompleted(GiveawayCompleted { date, .. }) => *date, } } @@ -1759,7 +1843,7 @@ impl Message { | Message::Pinned(Pinned { chat, .. }) | Message::Invoice(Invoice { chat, .. }) | Message::SuccessfulPayment(SuccessfulPayment { chat, .. }) - | Message::UserShared(UserShared { chat, .. }) + | Message::UsersShared(UsersShared { chat, .. }) | Message::ChatShared(ChatShared { chat, .. }) | Message::ConnectedWebsite(ConnectedWebsite { chat, .. }) | Message::WriteAccessAllowed(WriteAccessAllowed { chat, .. }) @@ -1778,7 +1862,10 @@ impl Message { chat, .. }) | Message::WebAppData(WebAppData { chat, .. }) - | Message::Empty(Empty { chat, .. }) => chat, + | Message::GiveawayCreated(GiveawayCreated { chat, .. }) + | Message::Giveaway(Giveaway { chat, .. }) + | Message::GiveawayWinners(GiveawayWinners { chat, .. }) + | Message::GiveawayCompleted(GiveawayCompleted { chat, .. }) => chat, } } @@ -1812,8 +1899,7 @@ impl Message { | Message::ForumTopicReopened(ForumTopicReopened { via_bot, .. }) | Message::GeneralForumTopicHidden(GeneralForumTopicHidden { via_bot, .. }) | Message::GeneralForumTopicUnhidden(GeneralForumTopicUnhidden { via_bot, .. }) - | Message::WebAppData(WebAppData { via_bot, .. }) - | Message::Empty(Empty { via_bot, .. }) => via_bot, + | Message::WebAppData(WebAppData { via_bot, .. }) => via_bot, _ => &None, } .as_ref() @@ -1887,7 +1973,7 @@ impl Message { | Message::Pinned(Pinned { from, .. }) | Message::Invoice(Invoice { from, .. }) | Message::SuccessfulPayment(SuccessfulPayment { from, .. }) - | Message::UserShared(UserShared { from, .. }) + | Message::UsersShared(UsersShared { from, .. }) | Message::ChatShared(ChatShared { from, .. }) | Message::ConnectedWebsite(ConnectedWebsite { from, .. }) | Message::WriteAccessAllowed(WriteAccessAllowed { from, .. }) @@ -1906,7 +1992,10 @@ impl Message { from, .. }) | Message::WebAppData(WebAppData { from, .. }) - | Message::Empty(Empty { from, .. }) => from, + | Message::GiveawayCreated(GiveawayCreated { from, .. }) + | Message::Giveaway(Giveaway { from, .. }) + | Message::GiveawayWinners(GiveawayWinners { from, .. }) + | Message::GiveawayCompleted(GiveawayCompleted { from, .. }) => from, } .as_ref() } @@ -1955,7 +2044,7 @@ impl Message { | Message::Pinned(Pinned { sender_chat, .. }) | Message::Invoice(Invoice { sender_chat, .. }) | Message::SuccessfulPayment(SuccessfulPayment { sender_chat, .. }) - | Message::UserShared(UserShared { sender_chat, .. }) + | Message::UsersShared(UsersShared { sender_chat, .. }) | Message::ChatShared(ChatShared { sender_chat, .. }) | Message::ConnectedWebsite(ConnectedWebsite { sender_chat, .. }) | Message::WriteAccessAllowed(WriteAccessAllowed { sender_chat, .. }) @@ -1977,7 +2066,10 @@ impl Message { .. }) | Message::WebAppData(WebAppData { sender_chat, .. }) - | Message::Empty(Empty { sender_chat, .. }) => sender_chat, + | Message::GiveawayCreated(GiveawayCreated { sender_chat, .. }) + | Message::Giveaway(Giveaway { sender_chat, .. }) + | Message::GiveawayWinners(GiveawayWinners { sender_chat, .. }) + | Message::GiveawayCompleted(GiveawayCompleted { sender_chat, .. }) => sender_chat, } .as_ref() } @@ -2132,9 +2224,6 @@ impl Message { | Message::GeneralForumTopicUnhidden(GeneralForumTopicUnhidden { reply_to_message, .. - }) - | Message::Empty(Empty { - reply_to_message, .. }) => match reply_to_message { Some(reply_to_message) => Some(reply_to_message), None => None, @@ -2143,6 +2232,52 @@ impl Message { } } + #[must_use] + pub const fn external_reply(&self) -> Option<&ExternalReplyInfo> { + match self { + Message::Text(Text { external_reply, .. }) + | Message::Animation(Animation { external_reply, .. }) + | Message::Audio(Audio { external_reply, .. }) + | Message::Document(Document { external_reply, .. }) + | Message::Photo(Photo { external_reply, .. }) + | Message::Sticker(Sticker { external_reply, .. }) + | Message::Story(Story { external_reply, .. }) + | Message::Video(Video { external_reply, .. }) + | Message::VideoNote(VideoNote { external_reply, .. }) + | Message::Voice(Voice { external_reply, .. }) + | Message::Contact(Contact { external_reply, .. }) + | Message::Dice(Dice { external_reply, .. }) + | Message::Game(Game { external_reply, .. }) + | Message::Giveaway(Giveaway { external_reply, .. }) + | Message::GiveawayWinners(GiveawayWinners { external_reply, .. }) + | Message::Poll(Poll { external_reply, .. }) + | Message::Venue(Venue { external_reply, .. }) + | Message::Location(Location { external_reply, .. }) + | Message::Invoice(Invoice { external_reply, .. }) => match external_reply { + Some(external_reply) => Some(external_reply), + None => None, + }, + _ => None, + } + } + + #[must_use] + pub const fn quote(&self) -> Option<&TextQuote> { + match self { + Message::Text(Text { quote, .. }) + | Message::Animation(Animation { quote, .. }) + | Message::Audio(Audio { quote, .. }) + | Message::Document(Document { quote, .. }) + | Message::Video(Video { quote, .. }) + | Message::Voice(Voice { quote, .. }) + | Message::Photo(Photo { quote, .. }) => match quote { + Some(quote) => Some(quote), + None => None, + }, + _ => None, + } + } + #[must_use] pub const fn edit_date(&self) -> Option { *match self { @@ -2155,8 +2290,7 @@ impl Message { | Message::Game(Game { edit_date, .. }) | Message::Poll(Poll { edit_date, .. }) | Message::Venue(Venue { edit_date, .. }) - | Message::Location(Location { edit_date, .. }) - | Message::Empty(Empty { edit_date, .. }) => edit_date, + | Message::Location(Location { edit_date, .. }) => edit_date, _ => &None, } } @@ -2255,28 +2389,27 @@ impl Message { } #[must_use] - pub const fn forward(&self) -> Option<&Forward> { + pub const fn forward_origin(&self) -> Option<&MessageOrigin> { match self { - Message::Text(Text { forward, .. }) - | Message::Animation(Animation { forward, .. }) - | Message::Audio(Audio { forward, .. }) - | Message::Document(Document { forward, .. }) - | Message::Photo(Photo { forward, .. }) - | Message::Sticker(Sticker { forward, .. }) - | Message::Story(Story { forward, .. }) - | Message::Video(Video { forward, .. }) - | Message::VideoNote(VideoNote { forward, .. }) - | Message::Voice(Voice { forward, .. }) - | Message::Contact(Contact { forward, .. }) - | Message::Dice(Dice { forward, .. }) - | Message::Game(Game { forward, .. }) - | Message::Poll(Poll { forward, .. }) - | Message::Venue(Venue { forward, .. }) - | Message::Location(Location { forward, .. }) - | Message::Invoice(Invoice { forward, .. }) - | Message::SuccessfulPayment(SuccessfulPayment { forward, .. }) - | Message::ConnectedWebsite(ConnectedWebsite { forward, .. }) - | Message::Empty(Empty { forward, .. }) => forward, + Message::Text(Text { forward_origin, .. }) + | Message::Animation(Animation { forward_origin, .. }) + | Message::Audio(Audio { forward_origin, .. }) + | Message::Document(Document { forward_origin, .. }) + | Message::Photo(Photo { forward_origin, .. }) + | Message::Sticker(Sticker { forward_origin, .. }) + | Message::Story(Story { forward_origin, .. }) + | Message::Video(Video { forward_origin, .. }) + | Message::VideoNote(VideoNote { forward_origin, .. }) + | Message::Voice(Voice { forward_origin, .. }) + | Message::Contact(Contact { forward_origin, .. }) + | Message::Dice(Dice { forward_origin, .. }) + | Message::Game(Game { forward_origin, .. }) + | Message::Poll(Poll { forward_origin, .. }) + | Message::Venue(Venue { forward_origin, .. }) + | Message::Location(Location { forward_origin, .. }) + | Message::Invoice(Invoice { forward_origin, .. }) + | Message::SuccessfulPayment(SuccessfulPayment { forward_origin, .. }) + | Message::ConnectedWebsite(ConnectedWebsite { forward_origin, .. }) => forward_origin, _ => &None, } .as_ref() @@ -2431,7 +2564,7 @@ impl Message { } #[must_use] - pub const fn pinned(&self) -> Option<&Message> { + pub const fn pinned(&self) -> Option<&MaybeInaccessibleMessage> { match self { Message::Pinned(Pinned { message, .. }) => Some(message), _ => None, @@ -2455,9 +2588,9 @@ impl Message { } #[must_use] - pub const fn user_shared(&self) -> Option<&types::UserShared> { + pub const fn users_shared(&self) -> Option<&types::UsersShared> { match self { - Message::UserShared(UserShared { shared, .. }) => Some(shared), + Message::UsersShared(UsersShared { shared, .. }) => Some(shared), _ => None, } } @@ -2556,6 +2689,38 @@ impl Message { } } + #[must_use] + pub const fn giveaway_created(&self) -> Option<&types::GiveawayCreated> { + match self { + Message::GiveawayCreated(GiveawayCreated { created, .. }) => Some(created), + _ => None, + } + } + + #[must_use] + pub const fn giveaway(&self) -> Option<&types::Giveaway> { + match self { + Message::Giveaway(Giveaway { giveaway, .. }) => Some(giveaway), + _ => None, + } + } + + #[must_use] + pub const fn giveaway_winners(&self) -> Option<&types::GiveawayWinners> { + match self { + Message::GiveawayWinners(GiveawayWinners { winners, .. }) => Some(winners), + _ => None, + } + } + + #[must_use] + pub const fn giveaway_completed(&self) -> Option<&types::GiveawayCompleted> { + match self { + Message::GiveawayCompleted(GiveawayCompleted { completed, .. }) => Some(completed), + _ => None, + } + } + #[must_use] pub const fn video_chat_scheduled(&self) -> Option<&types::VideoChatScheduled> { match self { @@ -2705,44 +2870,12 @@ impl Message { _ => None, } } - - #[must_use] - pub const fn empty(&self) -> Option<&Empty> { - match self { - Message::Empty(empty) => Some(empty), - _ => None, - } - } } impl Default for Message { #[must_use] fn default() -> Self { - Message::Empty(Empty::default()) - } -} - -impl TryFrom for Forward { - type Error = ConvertToTypeError; - - fn try_from(value: Message) -> Result { - if let Some(forward) = value.forward() { - Ok(forward.clone()) - } else { - Err(Self::Error::new("Message", "Forward")) - } - } -} - -impl TryFrom for ForwardedFrom { - type Error = ConvertToTypeError; - - fn try_from(value: Message) -> Result { - if let Some(forward) = value.forward() { - Ok(forward.clone().into()) - } else { - Err(Self::Error::new("Message", "ForwardedFrom")) - } + Message::Text(Text::default()) } } @@ -2792,12 +2925,15 @@ impl_try_from_message!(ForumTopicClosed, ForumTopicClosed); impl_try_from_message!(ForumTopicReopened, ForumTopicReopened); impl_try_from_message!(GeneralForumTopicHidden, GeneralForumTopicHidden); impl_try_from_message!(GeneralForumTopicUnhidden, GeneralForumTopicUnhidden); +impl_try_from_message!(GiveawayCreated, GiveawayCreated); +impl_try_from_message!(Giveaway, Giveaway); +impl_try_from_message!(GiveawayWinners, GiveawayWinners); +impl_try_from_message!(GiveawayCompleted, GiveawayCompleted); impl_try_from_message!(VideoChatScheduled, VideoChatScheduled); impl_try_from_message!(VideoChatStarted, VideoChatStarted); impl_try_from_message!(VideoChatEnded, VideoChatEnded); impl_try_from_message!(VideoChatParticipantsInvited, VideoChatParticipantsInvited); impl_try_from_message!(WebAppData, WebAppData); -impl_try_from_message!(Empty, Empty); impl_try_from_message!(Poll, Poll); impl_try_from_message!(Venue, Venue); impl_try_from_message!(Photo, Photo); @@ -2807,7 +2943,7 @@ impl_try_from_message!(Video, Video); impl_try_from_message!(VideoNote, VideoNote); impl_try_from_message!(Voice, Voice); impl_try_from_message!(WriteAccessAllowed, WriteAccessAllowed); -impl_try_from_message!(UserShared, UserShared); +impl_try_from_message!(UsersShared, UsersShared); impl_try_from_message!(ChatShared, ChatShared); impl_try_from_message!(MessageAutoDeleteTimerChanged, MessageAutoDeleteTimerChanged); @@ -2867,12 +3003,15 @@ impl_try_from_update!(ForumTopicClosed); impl_try_from_update!(ForumTopicReopened); impl_try_from_update!(GeneralForumTopicHidden); impl_try_from_update!(GeneralForumTopicUnhidden); +impl_try_from_update!(GiveawayCreated); +impl_try_from_update!(Giveaway); +impl_try_from_update!(GiveawayWinners); +impl_try_from_update!(GiveawayCompleted); impl_try_from_update!(VideoChatScheduled); impl_try_from_update!(VideoChatStarted); impl_try_from_update!(VideoChatEnded); impl_try_from_update!(VideoChatParticipantsInvited); impl_try_from_update!(WebAppData); -impl_try_from_update!(Empty); impl_try_from_update!(Poll); impl_try_from_update!(Venue); impl_try_from_update!(Photo); @@ -2882,18 +3021,16 @@ impl_try_from_update!(Video); impl_try_from_update!(VideoNote); impl_try_from_update!(Voice); impl_try_from_update!(WriteAccessAllowed); -impl_try_from_update!(UserShared); +impl_try_from_update!(UsersShared); impl_try_from_update!(ChatShared); impl_try_from_update!(MessageAutoDeleteTimerChanged); -impl_try_from_update!(Forward); -impl_try_from_update!(ForwardedFrom); #[cfg(test)] mod tests { use super::*; #[test] - fn parse_text() { + fn deserialize_text() { let jsons = [ serde_json::json!({ "message_id": 1, @@ -2942,13 +3079,13 @@ mod tests { match message { Message::Text(message) => assert_eq!(message, message_text), - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_text_with_forward() { + fn deserialize_text_with_forward() { let jsons = [ serde_json::json!({ "message_id": 1, @@ -3015,48 +3152,70 @@ mod tests { ]; for json in jsons { - let message_text: Text = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Text(message) => assert_eq!(message, message_text), - _ => panic!("Unexpected message type"), + Message::Text(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_animation() { - let jsons = [serde_json::json!({ - "message_id": 1, - "date": 0, - "chat": { - "id": -1, - "title": "test", - "type": "channel", - }, - "animation": { - "file_id": "test", - "file_unique_id": "test", - "width": 1, - "height": 1, - "duration": 1, - }, - })]; + fn deserialize_animation() { + let jsons = [ + serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "animation": { + "file_id": "test", + "file_unique_id": "test", + "width": 1, + "height": 1, + "duration": 1, + }, + }), + serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "animation": { + "file_id": "test", + "file_unique_id": "test", + "width": 1, + "height": 1, + "duration": 1, + }, + "document": { + "file_id": "test", + "file_unique_id": "test", + }, + }), + ]; for json in jsons { - let message_animation: Animation = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Animation(message) => assert_eq!(message, message_animation), - _ => panic!("Unexpected message type"), + Message::Animation(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_audio() { + fn deserialize_audio() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3073,18 +3232,18 @@ mod tests { })]; for json in jsons { - let message_audio: Audio = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Audio(message) => assert_eq!(message, message_audio), - _ => panic!("Unexpected message type"), + Message::Audio(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_document() { + fn deserialize_document() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3100,18 +3259,18 @@ mod tests { })]; for json in jsons { - let message_document: Document = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Document(message) => assert_eq!(message, message_document), - _ => panic!("Unexpected message type"), + Message::Document(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_photo() { + fn deserialize_photo() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3129,18 +3288,18 @@ mod tests { })]; for json in jsons { - let message_photo: Photo = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Photo(message) => assert_eq!(message, message_photo), - _ => panic!("Unexpected message type"), + Message::Photo(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_sticker() { + fn deserialize_sticker() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3161,18 +3320,18 @@ mod tests { })]; for json in jsons { - let message_sticker: Sticker = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Sticker(message) => assert_eq!(message, message_sticker), - _ => panic!("Unexpected message type"), + Message::Sticker(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_story() { + fn deserialize_story() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3186,18 +3345,18 @@ mod tests { })]; for json in jsons { - let message_story: Story = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Story(message) => assert_eq!(message, message_story), - _ => panic!("Unexpected message type"), + Message::Story(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_video() { + fn deserialize_video() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3217,18 +3376,18 @@ mod tests { })]; for json in jsons { - let message_video: Video = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Video(message) => assert_eq!(message, message_video), - _ => panic!("Unexpected message type"), + Message::Video(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_video_note() { + fn deserialize_video_note() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3247,18 +3406,18 @@ mod tests { })]; for json in jsons { - let message_video_note: VideoNote = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::VideoNote(message) => assert_eq!(message, message_video_note), - _ => panic!("Unexpected message type"), + Message::VideoNote(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_voice() { + fn deserialize_voice() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3276,18 +3435,18 @@ mod tests { })]; for json in jsons { - let message_voice: Voice = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Voice(message) => assert_eq!(message, message_voice), - _ => panic!("Unexpected message type"), + Message::Voice(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_contact() { + fn deserialize_contact() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3304,18 +3463,18 @@ mod tests { })]; for json in jsons { - let message_contact: Contact = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Contact(message) => assert_eq!(message, message_contact), - _ => panic!("Unexpected message type"), + Message::Contact(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_dice() { + fn deserialize_dice() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3332,18 +3491,18 @@ mod tests { })]; for json in jsons { - let message_dice: Dice = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Dice(message) => assert_eq!(message, message_dice), - _ => panic!("Unexpected message type"), + Message::Dice(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_game() { + fn deserialize_game() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3366,18 +3525,18 @@ mod tests { })]; for json in jsons { - let message_game: Game = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Game(message) => assert_eq!(message, message_game), - _ => panic!("Unexpected message type"), + Message::Game(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_poll() { + fn deserialize_poll() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3409,76 +3568,99 @@ mod tests { })]; for json in jsons { - let message_poll: Poll = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Poll(message) => assert_eq!(message, message_poll), - _ => panic!("Unexpected message type"), + Message::Poll(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_venue() { - let jsons = [serde_json::json!({ - "message_id": 1, - "date": 0, - "chat": { - "id": -1, - "title": "test", - "type": "channel", - }, - "venue": { + fn deserialize_venue() { + let jsons = [ + serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "venue": { + "location": { + "latitude": 1.0, + "longitude": 1.0, + }, + "title": "test", + "address": "test", + }, + }), + serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "venue": { + "location": { + "latitude": 1.0, + "longitude": 1.0, + }, + "title": "test", + "address": "test", + }, "location": { "latitude": 1.0, "longitude": 1.0, }, - "title": "test", - "address": "test", - }, - })]; + }), + ]; for json in jsons { - let message_venue: Venue = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Venue(message) => assert_eq!(message, message_venue), - _ => panic!("Unexpected message type"), + Message::Venue(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_location() { + fn deserialize_location() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, - "location": { - "latitude": 1.0, - "longitude": 1.0, - }, "chat": { "id": -1, "title": "test", "type": "channel", }, + "location": { + "latitude": 1.0, + "longitude": 1.0, + }, })]; for json in jsons { - let message_location: Location = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Location(message) => assert_eq!(message, message_location), - _ => panic!("Unexpected message type"), + Message::Location(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_new_chat_members() { + fn deserialize_new_chat_members() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3495,19 +3677,18 @@ mod tests { })]; for json in jsons { - let message_new_chat_members: NewChatMembers = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::NewChatMembers(message) => assert_eq!(message, message_new_chat_members), - _ => panic!("Unexpected message type"), + Message::NewChatMembers(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_left_chat_member() { + fn deserialize_left_chat_member() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3524,19 +3705,18 @@ mod tests { })]; for json in jsons { - let message_left_chat_member: LeftChatMember = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::LeftChatMember(message) => assert_eq!(message, message_left_chat_member), - _ => panic!("Unexpected message type"), + Message::LeftChatMember(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_new_chat_title() { + fn deserialize_new_chat_title() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3549,19 +3729,18 @@ mod tests { })]; for json in jsons { - let message_new_chat_title: NewChatTitle = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::NewChatTitle(message) => assert_eq!(message, message_new_chat_title), - _ => panic!("Unexpected message type"), + Message::NewChatTitle(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_new_chat_photo() { + fn deserialize_new_chat_photo() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3579,19 +3758,18 @@ mod tests { })]; for json in jsons { - let message_new_chat_photo: NewChatPhoto = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::NewChatPhoto(message) => assert_eq!(message, message_new_chat_photo), - _ => panic!("Unexpected message type"), + Message::NewChatPhoto(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_delete_chat_photo() { + fn deserialize_delete_chat_photo() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3604,19 +3782,18 @@ mod tests { })]; for json in jsons { - let message_delete_chat_photo: DeleteChatPhoto = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::DeleteChatPhoto(message) => assert_eq!(message, message_delete_chat_photo), - _ => panic!("Unexpected message type"), + Message::DeleteChatPhoto(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_group_chat_created() { + fn deserialize_group_chat_created() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3629,21 +3806,20 @@ mod tests { })]; for json in jsons { - let message_group_chat_created: GroupChatCreated = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::GroupChatCreated(message) => { - assert_eq!(message, message_group_chat_created); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_supergroup_chat_created() { + fn deserialize_supergroup_chat_created() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3656,21 +3832,20 @@ mod tests { })]; for json in jsons { - let message_supergroup_chat_created: SupergroupChatCreated = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::SupergroupChatCreated(message) => { - assert_eq!(message, message_supergroup_chat_created); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_channel_chat_created() { + fn deserialize_channel_chat_created() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3683,21 +3858,20 @@ mod tests { })]; for json in jsons { - let message_channel_chat_created: ChannelChatCreated = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ChannelChatCreated(message) => { - assert_eq!(message, message_channel_chat_created); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_auto_delete_timer_changed() { + fn deserialize_auto_delete_timer_changed() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3712,21 +3886,20 @@ mod tests { })]; for json in jsons { - let message_auto_delete_timer_changed: MessageAutoDeleteTimerChanged = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::MessageAutoDeleteTimerChanged(message) => { - assert_eq!(message, message_auto_delete_timer_changed); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_migrate_to_chat_id() { + fn deserialize_migrate_to_chat_id() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3739,21 +3912,20 @@ mod tests { })]; for json in jsons { - let message_migrate_to_chat_id: MigrateToChat = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::MigrateToChat(message) => { - assert_eq!(message, message_migrate_to_chat_id); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_migrate_from_chat_id() { + fn deserialize_migrate_from_chat_id() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3766,21 +3938,20 @@ mod tests { })]; for json in jsons { - let message_migrate_from_chat_id: MigrateFromChat = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::MigrateFromChat(message) => { - assert_eq!(message, message_migrate_from_chat_id); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_pinned_message() { + fn deserialize_pinned_message() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3802,18 +3973,18 @@ mod tests { })]; for json in jsons { - let message_pinned_message: Pinned = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Pinned(message) => assert_eq!(message, message_pinned_message), - _ => panic!("Unexpected message type"), + Message::Pinned(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_invoice() { + fn deserialize_invoice() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3832,18 +4003,18 @@ mod tests { })]; for json in jsons { - let message_invoice: Invoice = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Invoice(message) => assert_eq!(message, message_invoice), - _ => panic!("Unexpected message type"), + Message::Invoice(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_successful_payment() { + fn deserialize_successful_payment() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3862,21 +4033,20 @@ mod tests { })]; for json in jsons { - let message_successful_payment: SuccessfulPayment = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::SuccessfulPayment(message) => { - assert_eq!(message, message_successful_payment); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_user_shared() { + fn deserialize_users_shared() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3885,25 +4055,25 @@ mod tests { "title": "test", "type": "channel", }, - "user_shared": { + "users_shared": { "request_id": 1, - "user_id": 1, + "user_ids": [1, 2, 3], }, })]; for json in jsons { - let message_user_shared: UserShared = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::UserShared(message) => assert_eq!(message, message_user_shared), - _ => panic!("Unexpected message type"), + Message::UsersShared(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_chat_shared() { + fn deserialize_chat_shared() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3919,18 +4089,18 @@ mod tests { })]; for json in jsons { - let message_chat_shared: ChatShared = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::ChatShared(message) => assert_eq!(message, message_chat_shared), - _ => panic!("Unexpected message type"), + Message::ChatShared(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_connected_website() { + fn deserialize_connected_website() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3943,21 +4113,20 @@ mod tests { })]; for json in jsons { - let message_connected_website: ConnectedWebsite = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ConnectedWebsite(message) => { - assert_eq!(message, message_connected_website); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_write_access_allowed() { + fn deserialize_write_access_allowed() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -3970,21 +4139,20 @@ mod tests { })]; for json in jsons { - let message_write_access_allowed: WriteAccessAllowed = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::WriteAccessAllowed(message) => { - assert_eq!(message, message_write_access_allowed); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_passport_data() { + fn deserialize_passport_data() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4008,18 +4176,18 @@ mod tests { })]; for json in jsons { - let message_passport_data: PassportData = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::PassportData(message) => assert_eq!(message, message_passport_data), - _ => panic!("Unexpected message type"), + Message::PassportData(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_proximity_alert_triggered() { + fn deserialize_proximity_alert_triggered() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4044,21 +4212,20 @@ mod tests { })]; for json in jsons { - let message_proximity_alert_triggered: ProximityAlertTriggered = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ProximityAlertTriggered(message) => { - assert_eq!(message, message_proximity_alert_triggered); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_forum_topic_created() { + fn deserialize_forum_topic_created() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4075,21 +4242,20 @@ mod tests { })]; for json in jsons { - let message_forum_topic_created: ForumTopicCreated = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ForumTopicCreated(message) => { - assert_eq!(message, message_forum_topic_created); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_forum_topic_edited() { + fn deserialize_forum_topic_edited() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4105,21 +4271,20 @@ mod tests { })]; for json in jsons { - let message_forum_topic_edited: ForumTopicEdited = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ForumTopicEdited(message) => { - assert_eq!(message, message_forum_topic_edited); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_forum_topic_closed() { + fn deserialize_forum_topic_closed() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4132,21 +4297,20 @@ mod tests { })]; for json in jsons { - let message_forum_topic_closed: ForumTopicClosed = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ForumTopicClosed(message) => { - assert_eq!(message, message_forum_topic_closed); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_forum_topic_reopened() { + fn deserialize_forum_topic_reopened() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4159,21 +4323,20 @@ mod tests { })]; for json in jsons { - let message_forum_topic_reopened: ForumTopicReopened = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::ForumTopicReopened(message) => { - assert_eq!(message, message_forum_topic_reopened); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_general_forum_topic_hidden() { + fn deserialize_general_forum_topic_hidden() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4186,21 +4349,20 @@ mod tests { })]; for json in jsons { - let message_general_forum_topic_hidden: GeneralForumTopicHidden = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::GeneralForumTopicHidden(message) => { - assert_eq!(message, message_general_forum_topic_hidden); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_general_forum_topic_unhidden() { + fn deserialize_general_forum_topic_unhidden() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4213,21 +4375,146 @@ mod tests { })]; for json in jsons { - let message_general_forum_topic_unhidden: GeneralForumTopicUnhidden = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::GeneralForumTopicUnhidden(message) => { - assert_eq!(message, message_general_forum_topic_unhidden); + assert_eq!(message, message_kind); + } + _ => panic!("Unexpected message type: {message:?}"), + } + } + } + + #[test] + fn deserialize_giveaway_created() { + let jsons = [serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "giveaway_created": {}, + })]; + + for json in jsons { + let message_kind = serde_json::from_value(json.clone()).unwrap(); + let message: Message = serde_json::from_value(json).unwrap(); + + match message { + Message::GiveawayCreated(message) => { + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_video_chat_scheduled() { + fn deserialize_giveaway() { + let jsons = [serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "giveaway": { + "chats": [{ + "id": -1, + "title": "test", + "type": "channel", + }], + "winners_selection_date": 0, + "winner_count": 1, + }, + })]; + + for json in jsons { + let message_kind = serde_json::from_value(json.clone()).unwrap(); + let message: Message = serde_json::from_value(json).unwrap(); + + match message { + Message::Giveaway(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), + } + } + } + + #[test] + fn deserialize_giveaway_winners() { + let jsons = [serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "giveaway_winners": { + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "giveaway_message_id": 1, + "winners_selection_date": 0, + "winner_count": 1, + "winners": [{ + "id": 1, + "is_bot": false, + "first_name": "test", + }], + }, + })]; + + for json in jsons { + let message_kind = serde_json::from_value(json.clone()).unwrap(); + let message: Message = serde_json::from_value(json).unwrap(); + + match message { + Message::GiveawayWinners(message) => { + assert_eq!(message, message_kind); + } + _ => panic!("Unexpected message type: {message:?}"), + } + } + } + + #[test] + fn deserialize_giveaway_completed() { + let jsons = [serde_json::json!({ + "message_id": 1, + "date": 0, + "chat": { + "id": -1, + "title": "test", + "type": "channel", + }, + "giveaway_completed": { + "winner_count": 1, + }, + })]; + + for json in jsons { + let message_kind = serde_json::from_value(json.clone()).unwrap(); + let message: Message = serde_json::from_value(json).unwrap(); + + match message { + Message::GiveawayCompleted(message) => { + assert_eq!(message, message_kind); + } + _ => panic!("Unexpected message type: {message:?}"), + } + } + } + + #[test] + fn deserialize_video_chat_scheduled() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4242,21 +4529,20 @@ mod tests { })]; for json in jsons { - let message_video_chat_scheduled: VideoChatScheduled = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::VideoChatScheduled(message) => { - assert_eq!(message, message_video_chat_scheduled); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_video_chat_started() { + fn deserialize_video_chat_started() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4269,21 +4555,20 @@ mod tests { })]; for json in jsons { - let message_video_chat_started: VideoChatStarted = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::VideoChatStarted(message) => { - assert_eq!(message, message_video_chat_started); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_video_chat_ended() { + fn deserialize_video_chat_ended() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4298,21 +4583,20 @@ mod tests { })]; for json in jsons { - let message_video_chat_ended: VideoChatEnded = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::VideoChatEnded(message) => { - assert_eq!(message, message_video_chat_ended); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_video_chat_participants_invited() { + fn deserialize_video_chat_participants_invited() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4338,21 +4622,20 @@ mod tests { })]; for json in jsons { - let message_video_chat_participants_invited: VideoChatParticipantsInvited = - serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { Message::VideoChatParticipantsInvited(message) => { - assert_eq!(message, message_video_chat_participants_invited); + assert_eq!(message, message_kind); } - _ => panic!("Unexpected message type"), + _ => panic!("Unexpected message type: {message:?}"), } } } #[test] - fn parse_web_app_data() { + fn deserialize_web_app_data() { let jsons = [serde_json::json!({ "message_id": 1, "date": 0, @@ -4368,35 +4651,12 @@ mod tests { })]; for json in jsons { - let message_web_app_data: WebAppData = serde_json::from_value(json.clone()).unwrap(); - let message: Message = serde_json::from_value(json).unwrap(); - - match message { - Message::WebAppData(message) => assert_eq!(message, message_web_app_data), - _ => panic!("Unexpected message type"), - } - } - } - - #[test] - fn parse_empty() { - let jsons = [serde_json::json!({ - "message_id": 1, - "date": 0, - "chat": { - "id": -1, - "title": "test", - "type": "channel", - }, - })]; - - for json in jsons { - let message_empty: Empty = serde_json::from_value(json.clone()).unwrap(); + let message_kind = serde_json::from_value(json.clone()).unwrap(); let message: Message = serde_json::from_value(json).unwrap(); match message { - Message::Empty(message) => assert_eq!(message, message_empty), - _ => panic!("Unexpected message type"), + Message::WebAppData(message) => assert_eq!(message, message_kind), + _ => panic!("Unexpected message type: {message:?}"), } } } diff --git a/src/types/message_entity.rs b/src/types/message_entity.rs index 2c30a9e5..4297cd13 100644 --- a/src/types/message_entity.rs +++ b/src/types/message_entity.rs @@ -33,6 +33,7 @@ pub enum Kind { Underline, Strikethrough, Spoiler, + Blockquote, Code, Pre(Pre), TextLink(TextLink), @@ -168,6 +169,11 @@ impl MessageEntity { Self::new(offset, length, Kind::Spoiler) } + #[must_use] + pub fn new_blockquote(offset: u16, length: u16) -> Self { + Self::new(offset, length, Kind::Blockquote) + } + #[must_use] pub fn new_code(offset: u16, length: u16) -> Self { Self::new(offset, length, Kind::Code) diff --git a/src/types/message_origin.rs b/src/types/message_origin.rs new file mode 100644 index 00000000..4cfba9d5 --- /dev/null +++ b/src/types/message_origin.rs @@ -0,0 +1,54 @@ +use super::{MessageOriginChannel, MessageOriginChat, MessageOriginHiddenUser, MessageOriginUser}; + +use serde::Deserialize; + +/// This object describes the origin of a message. It can be one of +/// - [`MessageOriginUser`] +/// - [`MessageOriginChat`] +/// - [`MessageOriginHiddenUser`] +/// - [`MessageOriginChannel`] +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum MessageOrigin { + /// The message was originally sent by a known user. + User(MessageOriginUser), + /// The message was originally sent by an unknown user. + HiddenUser(MessageOriginHiddenUser), + /// The message was originally sent on behalf of a chat to a group chat. + Chat(MessageOriginChat), + /// The message was originally sent to a channel chat. + Channel(MessageOriginChannel), +} + +impl Default for MessageOrigin { + #[must_use] + fn default() -> Self { + Self::User(MessageOriginUser::default()) + } +} + +impl From for MessageOrigin { + fn from(origin: MessageOriginUser) -> Self { + Self::User(origin) + } +} + +impl From for MessageOrigin { + fn from(origin: MessageOriginHiddenUser) -> Self { + Self::HiddenUser(origin) + } +} + +impl From for MessageOrigin { + fn from(origin: MessageOriginChat) -> Self { + Self::Chat(origin) + } +} + +impl From for MessageOrigin { + fn from(origin: MessageOriginChannel) -> Self { + Self::Channel(origin) + } +} diff --git a/src/types/message_origin_channel.rs b/src/types/message_origin_channel.rs new file mode 100644 index 00000000..b7983226 --- /dev/null +++ b/src/types/message_origin_channel.rs @@ -0,0 +1,19 @@ +use super::Chat; + +use serde::Deserialize; + +/// The message was originally sent to a channel chat. +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct MessageOriginChannel { + /// Date the message was sent originally in Unix time + pub date: i64, + /// Channel chat to which the message was originally sent + pub chat: Chat, + /// Unique message identifier inside the chat + #[serde(rename = "message_id")] + pub id: i64, + /// Signature of the original post author + pub author_signature: Option>, +} diff --git a/src/types/message_origin_chat.rs b/src/types/message_origin_chat.rs new file mode 100644 index 00000000..8c277563 --- /dev/null +++ b/src/types/message_origin_chat.rs @@ -0,0 +1,16 @@ +use super::Chat; + +use serde::Deserialize; + +/// The message was originally sent on behalf of a chat to a group chat. +/// # Documentation +/// +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct MessageOriginChat { + /// Date the message was sent originally in Unix time + pub date: i64, + /// Chat that sent the message originally + pub sender_chat: Chat, + /// For messages originally sent by an anonymous chat administrator, original message author signature + pub author_signature: Option>, +} diff --git a/src/types/message_origin_hidden_user.rs b/src/types/message_origin_hidden_user.rs new file mode 100644 index 00000000..6e891161 --- /dev/null +++ b/src/types/message_origin_hidden_user.rs @@ -0,0 +1,12 @@ +use serde::Deserialize; + +/// The message was originally sent by an unknown user. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct MessageOriginHiddenUser { + /// Date the message was sent originally in Unix time + pub date: i64, + /// Name of the user that sent the message originally + pub sender_user_name: Box, +} diff --git a/src/types/message_origin_user.rs b/src/types/message_origin_user.rs new file mode 100644 index 00000000..fd8cf5b9 --- /dev/null +++ b/src/types/message_origin_user.rs @@ -0,0 +1,14 @@ +use super::User; + +use serde::Deserialize; + +/// The message was originally sent by a known user. +/// # Documentation +/// +#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct MessageOriginUser { + /// Date the message was sent originally in Unix time + pub date: i64, + /// User that sent the message originally + pub sender_user: User, +} diff --git a/src/types/message_reaction_count_updated.rs b/src/types/message_reaction_count_updated.rs new file mode 100644 index 00000000..b2a624f1 --- /dev/null +++ b/src/types/message_reaction_count_updated.rs @@ -0,0 +1,32 @@ +use super::{Chat, ReactionCount, Update, UpdateKind}; + +use crate::errors::ConvertToTypeError; + +use serde::Deserialize; + +/// This object represents reaction changes on a message with anonymous reactions. +/// # Documentation +/// +#[derive(Debug, Default, Clone, PartialEq, Deserialize)] +pub struct MessageReactionCountUpdated { + /// The chat containing the message + pub chat: Chat, + /// Unique message identifier inside the chat + #[serde(rename = "message_id")] + pub id: i64, + /// Date of the change in Unix time + pub date: i64, + /// List of reactions that are present on the message + pub reactions: Box<[ReactionCount]>, +} + +impl TryFrom for MessageReactionCountUpdated { + type Error = ConvertToTypeError; + + fn try_from(update: Update) -> Result { + match update.kind { + UpdateKind::MessageReactionCount(val) => Ok(val), + _ => Err(ConvertToTypeError::new("Update", "MessageReactionCount")), + } + } +} diff --git a/src/types/message_reaction_updated.rs b/src/types/message_reaction_updated.rs new file mode 100644 index 00000000..de479cb9 --- /dev/null +++ b/src/types/message_reaction_updated.rs @@ -0,0 +1,100 @@ +use super::{Chat, ReactionType, Update, UpdateKind, User}; + +use crate::errors::ConvertToTypeError; + +use serde::Deserialize; + +/// This object represents a change of a reaction on a message performed by a user. +/// # Documentation +/// +#[derive(Debug, Default, Clone, PartialEq, Deserialize)] +pub struct MessageReactionUpdated { + /// The chat containing the message the user reacted to + pub chat: Chat, + /// Unique identifier of the message inside the chat + #[serde(rename = "message_id")] + pub id: i64, + /// The user that changed the reaction, if the user isn't anonymous + pub user: Option, + /// The chat on behalf of which the reaction was changed, if the user is anonymous + pub actor_chat: Option, + /// Date of the change in Unix time + pub date: i64, + /// Previous list of reaction types that were set by the user + pub old_reaction: Box<[ReactionType]>, + /// New list of reaction types that have been set by the user + pub new_reaction: Box<[ReactionType]>, +} + +impl TryFrom for MessageReactionUpdated { + type Error = ConvertToTypeError; + + fn try_from(update: Update) -> Result { + match update.kind { + UpdateKind::MessageReaction(val) => Ok(val), + _ => Err(ConvertToTypeError::new("Update", "MessageReaction")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn serialize() { + let jsons = [ + serde_json::json!( + { + "chat": { + "id": 1, + "title": "test", + "type": "supergroup" + }, + "message_id": 1, + "user": { + "id": 1, + "is_bot": false, + "first_name": "first_name" + }, + "date": 1, + "old_reaction": [ + {"type":"emoji","emoji":"👍"}, + {"type":"custom_emoji","custom_emoji":"123"} + ], + "new_reaction": [ + {"type":"custom_emoji","custom_emoji":"123"}, + {"type":"emoji","emoji":"👍"} + ] + } + ), + serde_json::json!( + { + "chat": { + "id": 1, + "title": "test", + "type": "supergroup" + }, + "message_id": 1, + "actor_chat": { + "id": 1, + "title": "test", + "type": "supergroup" + }, + "date": 1, + "old_reaction": [ + {"type":"emoji","emoji":"👍"}, + {"type":"custom_emoji","custom_emoji":"123"} + ], + "new_reaction": [ + {"type":"custom_emoji","custom_emoji":"123"}, + {"type":"emoji","emoji":"👍"} + ] + }), + ]; + + for json in jsons.iter() { + let _: MessageReactionUpdated = serde_json::from_value(json.clone()).unwrap(); + } + } +} diff --git a/src/types/reaction_count.rs b/src/types/reaction_count.rs new file mode 100644 index 00000000..5b4782bc --- /dev/null +++ b/src/types/reaction_count.rs @@ -0,0 +1,15 @@ +use super::ReactionType; + +use serde::Deserialize; + +/// Represents a reaction added to a message along with the number of times it was added. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct ReactionCount { + /// Type of the reaction + #[serde(rename = "type")] + pub reaction_type: ReactionType, + /// Number of times the reaction was added + pub total_count: i64, +} diff --git a/src/types/reaction_type.rs b/src/types/reaction_type.rs new file mode 100644 index 00000000..abf223de --- /dev/null +++ b/src/types/reaction_type.rs @@ -0,0 +1,78 @@ +use super::{ReactionTypeCustomEmoji, ReactionTypeEmoji}; + +use serde::{Deserialize, Serialize}; + +/// This object describes the type of a reaction. Currently, it can be one of +/// - [`ReactionTypeEmoji`] +/// - [`ReactionTypeCustomEmoji`] +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ReactionType { + Emoji(ReactionTypeEmoji), + CustomEmoji(ReactionTypeCustomEmoji), +} + +impl ReactionType { + #[must_use] + pub fn emoji(emoji: impl Into) -> Self { + Self::Emoji(ReactionTypeEmoji::new(emoji)) + } + + #[must_use] + pub fn custom_emoji(custom_emoji: impl Into) -> Self { + Self::CustomEmoji(ReactionTypeCustomEmoji::new(custom_emoji)) + } +} + +impl From for ReactionType { + #[must_use] + fn from(emoji: ReactionTypeEmoji) -> Self { + Self::Emoji(emoji) + } +} + +impl From for ReactionType { + #[must_use] + fn from(custom_emoji: ReactionTypeCustomEmoji) -> Self { + Self::CustomEmoji(custom_emoji) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialize_emoji() { + let data = ReactionType::emoji("👍"); + let json = serde_json::to_string(&data).unwrap(); + + assert_eq!(json, r#"{"type":"emoji","emoji":"👍"}"#); + } + + #[test] + fn serialize_custom_emoji() { + let data = ReactionType::custom_emoji("123"); + let json = serde_json::to_string(&data).unwrap(); + + assert_eq!(json, r#"{"type":"custom_emoji","custom_emoji":"123"}"#); + } + + #[test] + fn deserialize_emoji() { + let data = r#"{"type":"emoji","emoji":"👍"}"#; + let emoji: ReactionType = serde_json::from_str(data).unwrap(); + + assert_eq!(emoji, ReactionType::emoji("👍")); + } + + #[test] + fn deserialize_custom_emoji() { + let data = r#"{"type":"custom_emoji","custom_emoji":"123"}"#; + let custom_emoji: ReactionType = serde_json::from_str(data).unwrap(); + + assert_eq!(custom_emoji, ReactionType::custom_emoji("123")); + } +} diff --git a/src/types/reaction_type_custom_emoji.rs b/src/types/reaction_type_custom_emoji.rs new file mode 100644 index 00000000..a2326499 --- /dev/null +++ b/src/types/reaction_type_custom_emoji.rs @@ -0,0 +1,26 @@ +use serde::{Deserialize, Serialize}; + +/// The reaction is based on a custom emoji. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +pub struct ReactionTypeCustomEmoji { + /// Custom emoji identifier + pub custom_emoji: String, +} + +impl ReactionTypeCustomEmoji { + #[must_use] + pub fn new(custom_emoji: impl Into) -> Self { + Self { + custom_emoji: custom_emoji.into(), + } + } + + #[must_use] + pub fn emoji(self, val: impl Into) -> Self { + Self { + custom_emoji: val.into(), + } + } +} diff --git a/src/types/reaction_type_emoji.rs b/src/types/reaction_type_emoji.rs new file mode 100644 index 00000000..cccd7d96 --- /dev/null +++ b/src/types/reaction_type_emoji.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +/// The reaction is based on an emoji. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +pub struct ReactionTypeEmoji { + /// Emoji on which the reaction is based. + pub emoji: String, +} + +impl ReactionTypeEmoji { + #[must_use] + pub fn new(emoji: impl Into) -> Self { + Self { + emoji: emoji.into(), + } + } + + #[must_use] + pub fn emoji(self, val: impl Into) -> Self { + Self { emoji: val.into() } + } +} diff --git a/src/types/reply_parameters.rs b/src/types/reply_parameters.rs new file mode 100644 index 00000000..cecefe56 --- /dev/null +++ b/src/types/reply_parameters.rs @@ -0,0 +1,176 @@ +use super::{ChatIdKind, MessageEntity}; + +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; + +/// Describes reply parameters for the message that is being sent. +/// # Documentation +/// +#[skip_serializing_none] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +pub struct ReplyParameters { + /// Identifier of the message that will be replied to in the current chat, or in the chat chat_id if it is specified + pub message_id: i64, + /// If the message to be replied to is from a different chat, unique identifier for the chat or username of the channel (in the format `@channelusername`) + pub chat_id: Option, + /// Pass `true` if the message should be sent even if the specified message to be replied to is not found; can be used only for replies in the same chat and forum topic. + pub allow_sending_without_reply: Option, + /// Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including `bold`, `italic, `underline`, `strikethrough`, `spoiler`, and `custom_emoji` entities. The message will fail to send if the quote isn't found in the original message. + pub quote: Option, + /// Mode for parsing entities in the quote. See [formatting options](https://core.telegram.org/bots/api#formatting-options) for more details. + pub quote_parse_mode: Option, + /// A JSON-serialized list of special entities that appear in the quote. It can be specified instead of `quote_parse_mode` + pub quote_entities: Option>, + /// Position of the quote in the original message in UTF-16 code units + pub quote_position: Option, +} + +impl ReplyParameters { + #[must_use] + pub fn new(message_id: i64) -> Self { + Self { + message_id, + chat_id: None, + allow_sending_without_reply: None, + quote: None, + quote_parse_mode: None, + quote_entities: None, + quote_position: None, + } + } + + #[must_use] + pub fn message_id(self, val: i64) -> Self { + Self { + message_id: val, + ..self + } + } + + #[must_use] + pub fn chat_id(self, val: impl Into) -> Self { + Self { + chat_id: Some(val.into()), + ..self + } + } + + #[must_use] + pub fn allow_sending_without_reply(self, val: bool) -> Self { + Self { + allow_sending_without_reply: Some(val), + ..self + } + } + + #[must_use] + pub fn quote(self, val: impl Into) -> Self { + Self { + quote: Some(val.into()), + ..self + } + } + + #[must_use] + pub fn quote_parse_mode(self, val: impl Into) -> Self { + Self { + quote_parse_mode: Some(val.into()), + ..self + } + } + + #[must_use] + pub fn quote_entity(self, val: MessageEntity) -> Self { + Self { + quote_entities: Some( + self.quote_entities + .unwrap_or_default() + .into_iter() + .chain(Some(val)) + .collect(), + ), + ..self + } + } + + #[must_use] + pub fn quote_entities(self, val: impl IntoIterator) -> Self { + Self { + quote_entities: Some( + self.quote_entities + .unwrap_or_default() + .into_iter() + .chain(val) + .collect(), + ), + ..self + } + } + + #[must_use] + pub fn quote_position(self, val: u16) -> Self { + Self { + quote_position: Some(val), + ..self + } + } +} + +impl ReplyParameters { + #[must_use] + pub fn chat_id_option(self, val: Option>) -> Self { + Self { + chat_id: val.map(Into::into), + ..self + } + } + + #[must_use] + pub fn allow_sending_without_reply_option(self, val: Option) -> Self { + Self { + allow_sending_without_reply: val, + ..self + } + } + + #[must_use] + pub fn quote_option(self, val: Option>) -> Self { + Self { + quote: val.map(Into::into), + ..self + } + } + + #[must_use] + pub fn quote_parse_mode_option(self, val: Option>) -> Self { + Self { + quote_parse_mode: val.map(Into::into), + ..self + } + } + + #[must_use] + pub fn quote_entities_option( + self, + val: Option>, + ) -> Self { + Self { + quote_entities: val.map(|val| { + self.quote_entities + .unwrap_or_default() + .into_iter() + .chain(val) + .collect() + }), + ..self + } + } + + #[must_use] + pub fn quote_position_option(self, val: Option) -> Self { + Self { + quote_position: val, + ..self + } + } +} diff --git a/src/types/text_quote.rs b/src/types/text_quote.rs new file mode 100644 index 00000000..c5de0998 --- /dev/null +++ b/src/types/text_quote.rs @@ -0,0 +1,18 @@ +use super::MessageEntity; + +use serde::Deserialize; + +/// This object contains information about the quoted part of a message that is replied to by the given message. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct TextQuote { + /// Text of the quoted part of a message that is replied to by the given message + pub text: Box, + /// Special entities that appear in the quote. Currently, only `bold`, `italic`, `underline`, `strikethrough`, `spoiler`, and `custom_emoji` entities are kept in quotes. + pub entities: Option>, + /// Approximate quote position in the original message in UTF-16 code units as specified by the sender + pub position: u16, + /// `true`, if the quote was chosen manually by the message sender. Otherwise, the quote was added automatically by the server. + pub is_manual: bool, +} diff --git a/src/types/update.rs b/src/types/update.rs index 81a6954d..b65e01ce 100644 --- a/src/types/update.rs +++ b/src/types/update.rs @@ -1,6 +1,8 @@ use super::{ - CallbackQuery, Chat, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InlineQuery, - Message, Poll, PollAnswer, PreCheckoutQuery, ShippingQuery, User, + CallbackQuery, Chat, ChatBoostRemoved, ChatBoostSource, ChatBoostSourcePremium, + ChatBoostUpdated, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InaccessibleMessage, + InlineQuery, MaybeInaccessibleMessage, Message, MessageReactionCountUpdated, + MessageReactionUpdated, Poll, PollAnswer, PreCheckoutQuery, ShippingQuery, User, }; use crate::enums::UpdateType; @@ -34,6 +36,10 @@ pub enum Kind { ChannelPost(Message), /// New version of a channel post that is known to the bot and was edited EditedChannelPost(Message), + /// A reaction to a message was changed by a user. The bot must be an administrator in the chat and must explicitly specify `message_reaction` in the list of `allowed_updates`` to receive these updates. The update isn't received for reactions set by bots. + MessageReaction(MessageReactionUpdated), + /// Reactions to a message with anonymous reactions were changed. The bot must be an administrator in the chat and must explicitly specify `message_reaction_count` in the list of `allowed_updates`` to receive these updates. + MessageReactionCount(MessageReactionCountUpdated), /// New incoming inline query InlineQuery(InlineQuery), /// The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the [`feedback collecting`](https://core.telegram.org/bots/inline#collecting-feedback) for details on how to enable these updates for your bot. @@ -54,6 +60,10 @@ pub enum Kind { ChatMember(ChatMemberUpdated), /// A request to join the chat has been sent. The bot must have the *can_invite_users* administrator right in the chat to receive these updates. ChatJoinRequest(ChatJoinRequest), + /// A chat boost was added or changed. The bot must be an administrator in the chat to receive these updates. + ChatBoost(ChatBoostUpdated), + /// A boost was removed from a chat. The bot must be an administrator in the chat to receive these updates. + RemovedChatBoost(ChatBoostRemoved), } impl Kind { @@ -80,7 +90,11 @@ impl Kind { | Kind::MyChatMember(_) | Kind::ChatMember(_) | Kind::ChatJoinRequest(_) - | Kind::Poll(_) => None, + | Kind::Poll(_) + | Kind::MessageReaction(_) + | Kind::MessageReactionCount(_) + | Kind::ChatBoost(_) + | Kind::RemovedChatBoost(_) => None, } } @@ -96,7 +110,10 @@ impl Kind { return None; }; - message.caption() + match message { + MaybeInaccessibleMessage::Message(message) => message.caption(), + MaybeInaccessibleMessage::InaccessibleMessage(_) => None, + } } Kind::InlineQuery(_) | Kind::ChosenInlineResult(_) @@ -106,7 +123,11 @@ impl Kind { | Kind::MyChatMember(_) | Kind::ChatMember(_) | Kind::ChatJoinRequest(_) - | Kind::Poll(_) => None, + | Kind::Poll(_) + | Kind::MessageReaction(_) + | Kind::MessageReactionCount(_) + | Kind::ChatBoost(_) + | Kind::RemovedChatBoost(_) => None, } } @@ -133,8 +154,13 @@ impl Kind { | Kind::MyChatMember(ChatMemberUpdated { from, .. }) | Kind::ChatMember(ChatMemberUpdated { from, .. }) | Kind::ChatJoinRequest(ChatJoinRequest { from, .. }) => Some(from), - Kind::PollAnswer(PollAnswer { user, .. }) => user.as_ref(), - Kind::Poll(_) => None, + Kind::PollAnswer(PollAnswer { user, .. }) + | Kind::MessageReaction(MessageReactionUpdated { user, .. }) => user.as_ref(), + Kind::ChatBoost(ChatBoostUpdated { boost, .. }) => match boost { + ChatBoostSource::Premium(ChatBoostSourcePremium { user }) => Some(user), + ChatBoostSource::GiftCode(_) | ChatBoostSource::Giveaway(_) => None, + }, + Kind::Poll(_) | Kind::MessageReactionCount(_) | Kind::RemovedChatBoost(_) => None, } } @@ -158,11 +184,21 @@ impl Kind { return None; }; - Some(message.chat()) + match message { + MaybeInaccessibleMessage::Message(message) => Some(message.chat()), + MaybeInaccessibleMessage::InaccessibleMessage(InaccessibleMessage { + chat, + .. + }) => Some(chat), + } } Kind::MyChatMember(ChatMemberUpdated { chat, .. }) | Kind::ChatMember(ChatMemberUpdated { chat, .. }) - | Kind::ChatJoinRequest(ChatJoinRequest { chat, .. }) => Some(chat), + | Kind::ChatJoinRequest(ChatJoinRequest { chat, .. }) + | Kind::MessageReactionCount(MessageReactionCountUpdated { chat, .. }) + | Kind::ChatBoost(ChatBoostUpdated { chat, .. }) + | Kind::RemovedChatBoost(ChatBoostRemoved { chat, .. }) => Some(chat), + Kind::MessageReaction(MessageReactionUpdated { actor_chat, .. }) => actor_chat.as_ref(), Kind::InlineQuery(_) | Kind::ChosenInlineResult(_) | Kind::ShippingQuery(_) @@ -193,7 +229,10 @@ impl Kind { return None; }; - message.sender_chat() + match message { + MaybeInaccessibleMessage::Message(message) => message.sender_chat(), + MaybeInaccessibleMessage::InaccessibleMessage(_) => None, + } } Kind::InlineQuery(_) | Kind::ChosenInlineResult(_) @@ -203,7 +242,11 @@ impl Kind { | Kind::MyChatMember(_) | Kind::ChatMember(_) | Kind::ChatJoinRequest(_) - | Kind::Poll(_) => None, + | Kind::Poll(_) + | Kind::MessageReaction(_) + | Kind::MessageReactionCount(_) + | Kind::ChatBoost(_) + | Kind::RemovedChatBoost(_) => None, } } @@ -228,7 +271,10 @@ impl Kind { return None; }; - message.thread_id() + match message { + MaybeInaccessibleMessage::Message(message) => message.thread_id(), + MaybeInaccessibleMessage::InaccessibleMessage(_) => None, + } } Kind::InlineQuery(_) | Kind::ChosenInlineResult(_) @@ -238,7 +284,11 @@ impl Kind { | Kind::MyChatMember(_) | Kind::ChatMember(_) | Kind::ChatJoinRequest(_) - | Kind::Poll(_) => None, + | Kind::Poll(_) + | Kind::MessageReaction(_) + | Kind::MessageReactionCount(_) + | Kind::ChatBoost(_) + | Kind::RemovedChatBoost(_) => None, } } } @@ -330,6 +380,18 @@ impl<'de> Deserialize<'de> for Kind { UpdateType::ChatJoinRequest => map .next_value::() .map(Kind::ChatJoinRequest), + UpdateType::MessageReaction => map + .next_value::() + .map(Kind::MessageReaction), + UpdateType::MessageReactionCount => map + .next_value::() + .map(Kind::MessageReactionCount), + UpdateType::ChatBoost => { + map.next_value::().map(Kind::ChatBoost) + } + UpdateType::RemovedChatBoost => map + .next_value::() + .map(Kind::RemovedChatBoost), }; match update_kind { @@ -347,17 +409,17 @@ impl<'de> Deserialize<'de> for Kind { impl Update { #[must_use] - pub fn text(&self) -> Option<&str> { + pub const fn text(&self) -> Option<&str> { self.kind().text() } #[must_use] - pub fn caption(&self) -> Option<&str> { + pub const fn caption(&self) -> Option<&str> { self.kind().caption() } #[must_use] - pub fn text_or_caption(&self) -> Option<&str> { + pub const fn text_or_caption(&self) -> Option<&str> { self.kind().text_or_caption() } diff --git a/src/types/user_chat_boosts.rs b/src/types/user_chat_boosts.rs new file mode 100644 index 00000000..8e68cc6f --- /dev/null +++ b/src/types/user_chat_boosts.rs @@ -0,0 +1,12 @@ +use super::ChatBoost; + +use serde::Deserialize; + +/// This object represents a list of boosts added to a chat by a user. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct UserChatBoosts { + /// The list of boosts added to the chat by the user + pub boosts: Box<[ChatBoost]>, +} diff --git a/src/types/users_shared.rs b/src/types/users_shared.rs new file mode 100644 index 00000000..a67219e9 --- /dev/null +++ b/src/types/users_shared.rs @@ -0,0 +1,12 @@ +use serde::Deserialize; + +/// This object contains information about the users whose identifiers were shared with the bot using a [`KeyboardButtonRequestUsers`](crate::types::KeyboardButtonRequestUsers) button. +/// # Documentation +/// +#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] +pub struct UsersShared { + /// Identifier of the request + pub request_id: i64, + /// Identifiers of the shared users. These numbers may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting them. But they have at most 52 significant bits, so 64-bit integers or double-precision float types are safe for storing these identifiers. The bot may not have access to the users and could be unable to use these identifiers, unless the users are already known to the bot by some other means. + pub user_ids: Box<[i64]>, +} diff --git a/src/utils/text/formatter.rs b/src/utils/text/formatter.rs index 141a4407..f2eb0046 100644 --- a/src/utils/text/formatter.rs +++ b/src/utils/text/formatter.rs @@ -51,6 +51,11 @@ pub trait Formatter { where T: AsRef; + #[must_use] + fn blockquote(&self, text: T) -> String + where + T: AsRef; + #[must_use] fn text_link(&self, text: T, url: U) -> String where @@ -211,6 +216,13 @@ mod tests { todo!() } + fn blockquote(&self, _text: T) -> String + where + T: AsRef, + { + todo!() + } + fn text_link(&self, _text: T, _url: U) -> String where T: AsRef, diff --git a/src/utils/text/html_formatter.rs b/src/utils/text/html_formatter.rs index bde71287..7e6eb0ba 100644 --- a/src/utils/text/html_formatter.rs +++ b/src/utils/text/html_formatter.rs @@ -121,6 +121,13 @@ impl TextFormatter for Formatter { ) } + fn blockquote(&self, text: T) -> String + where + T: AsRef, + { + format!("
{text}
", text = text.as_ref()) + } + fn text_link(&self, text: T, url: U) -> String where T: AsRef, @@ -227,6 +234,7 @@ impl TextFormatter for Formatter { MessageEntityKind::Underline => self.underline(editable_text), MessageEntityKind::Strikethrough => self.strikethrough(editable_text), MessageEntityKind::Spoiler => self.spoiler(editable_text), + MessageEntityKind::Blockquote => self.blockquote(editable_text), MessageEntityKind::Code => self.code(editable_text), MessageEntityKind::Pre(PreMessageEntity { language }) => match language { Some(language) => self.pre_language(editable_text, language), @@ -269,6 +277,10 @@ pub fn spoiler(text: impl AsRef) -> String { FORMATTER.spoiler(text) } +pub fn blockquote(text: impl AsRef) -> String { + FORMATTER.blockquote(text) +} + pub fn text_link(text: impl AsRef, url: impl AsRef) -> String { FORMATTER.text_link(text, url) } @@ -331,6 +343,15 @@ mod tests { assert_eq!(formatter.spoiler("text"), "text"); } + #[test] + fn test_blockquote() { + let formatter = Formatter::default(); + assert_eq!( + formatter.blockquote("text"), + "
text
" + ); + } + #[test] fn test_text_link() { let formatter = Formatter::default(); diff --git a/src/utils/text/markdown_formatter.rs b/src/utils/text/markdown_formatter.rs index 82ce8233..2902c4cd 100644 --- a/src/utils/text/markdown_formatter.rs +++ b/src/utils/text/markdown_formatter.rs @@ -74,6 +74,17 @@ impl TextFormatter for Formatter { format!("|{text}|", text = text.as_ref()) } + fn blockquote(&self, text: T) -> String + where + T: AsRef, + { + text.as_ref() + .lines() + .map(|line| format!(">{line}")) + .collect::>() + .join("\n") + } + fn text_link(&self, text: T, url: U) -> String where T: AsRef, @@ -168,6 +179,7 @@ impl TextFormatter for Formatter { MessageEntityKind::Underline => self.underline(editable_text), MessageEntityKind::Strikethrough => self.strikethrough(editable_text), MessageEntityKind::Spoiler => self.spoiler(editable_text), + MessageEntityKind::Blockquote => self.blockquote(editable_text), MessageEntityKind::Code => self.code(editable_text), MessageEntityKind::Pre(PreMessageEntity { language }) => match language { Some(language) => self.pre_language(editable_text, language), @@ -210,6 +222,10 @@ pub fn spoiler(text: impl AsRef) -> String { FORMATTER.spoiler(text) } +pub fn blockquote(text: impl AsRef) -> String { + FORMATTER.blockquote(text) +} + pub fn text_link(text: impl AsRef, url: &str) -> String { FORMATTER.text_link(text, url) } @@ -272,6 +288,13 @@ mod tests { assert_eq!(formatter.spoiler("text"), "|text|"); } + #[test] + fn test_blockquote() { + let formatter = Formatter::default(); + assert_eq!(formatter.blockquote("text"), ">text"); + assert_eq!(formatter.blockquote("text\ntext"), ">text\n>text"); + } + #[test] fn test_text_link() { let formatter = Formatter::default();