From dbf138a5b30b712883484f12ae079ee255fa5870 Mon Sep 17 00:00:00 2001 From: Andrey Brusnik Date: Fri, 19 Jul 2024 17:42:26 +0400 Subject: [PATCH] Add `ChatBoostUpdated` and `ChatBoostRemoved` updates --- crates/teloxide-core/src/types.rs | 4 + .../teloxide-core/src/types/allowed_update.rs | 2 + .../src/types/chat_boost_removed.rs | 54 +++++ .../src/types/chat_boost_updated.rs | 49 +++++ crates/teloxide-core/src/types/update.rs | 208 +++++++++++++++++- crates/teloxide/src/dispatching/filter_ext.rs | 2 + .../src/dispatching/handler_description.rs | 2 + 7 files changed, 313 insertions(+), 8 deletions(-) create mode 100644 crates/teloxide-core/src/types/chat_boost_removed.rs create mode 100644 crates/teloxide-core/src/types/chat_boost_updated.rs diff --git a/crates/teloxide-core/src/types.rs b/crates/teloxide-core/src/types.rs index eea23ca5..ae8375d8 100644 --- a/crates/teloxide-core/src/types.rs +++ b/crates/teloxide-core/src/types.rs @@ -14,7 +14,9 @@ pub use chat::*; pub use chat_action::*; pub use chat_administrator_rights::*; pub use chat_boost::*; +pub use chat_boost_removed::*; pub use chat_boost_source::*; +pub use chat_boost_updated::*; pub use chat_full_info::*; pub use chat_invite_link::*; pub use chat_join_request::*; @@ -159,7 +161,9 @@ mod chat; mod chat_action; mod chat_administrator_rights; mod chat_boost; +mod chat_boost_removed; mod chat_boost_source; +mod chat_boost_updated; mod chat_full_info; mod chat_invite_link; mod chat_join_request; diff --git a/crates/teloxide-core/src/types/allowed_update.rs b/crates/teloxide-core/src/types/allowed_update.rs index a77040b3..12949ec5 100644 --- a/crates/teloxide-core/src/types/allowed_update.rs +++ b/crates/teloxide-core/src/types/allowed_update.rs @@ -19,4 +19,6 @@ pub enum AllowedUpdate { MyChatMember, ChatMember, ChatJoinRequest, + ChatBoost, + RemovedChatBoost, } diff --git a/crates/teloxide-core/src/types/chat_boost_removed.rs b/crates/teloxide-core/src/types/chat_boost_removed.rs new file mode 100644 index 00000000..094a0bde --- /dev/null +++ b/crates/teloxide-core/src/types/chat_boost_removed.rs @@ -0,0 +1,54 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +use crate::types::{Chat, ChatBoostSource}; + +/// This object represents a boost removed from a chat. +#[serde_with::skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ChatBoostRemoved { + /// Chat which was boosted + pub chat: Chat, + + /// Unique identifier of the boost + pub boost_id: String, + + /// Point in time (Unix timestamp) when the boost was removed + #[serde(with = "crate::types::serde_date_from_unix_timestamp")] + pub remove_date: DateTime, + + /// Source of the removed boost + pub source: ChatBoostSource, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn deserialize() { + let data = r#" + { + "chat": { + "id": -1002236736395, + "title": "Test", + "type": "channel" + }, + "boost_id": "4506e1b7e866e33fcbde78fe1746ec3a", + "remove_date": 1745089963, + "source": { + "source": "premium", + "user": { + "id": 1459074222, + "is_bot": false, + "first_name": "shadowchain", + "username": "shdwchn10", + "language_code": "en", + "is_premium": true + } + } + } + "#; + serde_json::from_str::(data).unwrap(); + } +} diff --git a/crates/teloxide-core/src/types/chat_boost_updated.rs b/crates/teloxide-core/src/types/chat_boost_updated.rs new file mode 100644 index 00000000..b4646b5f --- /dev/null +++ b/crates/teloxide-core/src/types/chat_boost_updated.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; + +use crate::types::{Chat, ChatBoost}; + +/// This object represents a boost added to a chat or changed. +#[serde_with::skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ChatBoostUpdated { + /// Chat which was boosted + pub chat: Chat, + + /// Infomation about the chat boost + pub boost: ChatBoost, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn deserialize() { + let data = r#" + { + "chat": { + "id": -1002236736395, + "title": "Test", + "type": "channel" + }, + "boost": { + "boost_id": "4506e1b7e866e33fcbde78fe1746ec3a", + "add_date": 1721399621, + "expiration_date": 1745088963, + "source": { + "source": "premium", + "user": { + "id": 1459074222, + "is_bot": false, + "first_name": "shadowchain", + "username": "shdwchn10", + "language_code": "en", + "is_premium": true + } + } + } + } + "#; + serde_json::from_str::(data).unwrap(); + } +} diff --git a/crates/teloxide-core/src/types/update.rs b/crates/teloxide-core/src/types/update.rs index 2d7fa7b6..5a1e8804 100644 --- a/crates/teloxide-core/src/types/update.rs +++ b/crates/teloxide-core/src/types/update.rs @@ -3,9 +3,9 @@ use serde::{de::MapAccess, Deserialize, Serialize, Serializer}; use serde_json::Value; use crate::types::{ - CallbackQuery, Chat, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InlineQuery, - Message, MessageReactionCountUpdated, MessageReactionUpdated, Poll, PollAnswer, - PreCheckoutQuery, ShippingQuery, User, + CallbackQuery, Chat, ChatBoostRemoved, ChatBoostUpdated, ChatJoinRequest, ChatMemberUpdated, + ChosenInlineResult, InlineQuery, Message, MessageReactionCountUpdated, MessageReactionUpdated, + Poll, PollAnswer, PreCheckoutQuery, ShippingQuery, User, }; /// This [object] represents an incoming update. @@ -127,6 +127,14 @@ pub enum UpdateKind { /// 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), + /// An error that happened during deserialization. /// /// This allows `teloxide` to continue working even if telegram adds a new @@ -160,6 +168,8 @@ impl Update { MyChatMember(m) | ChatMember(m) => &m.from, ChatJoinRequest(r) => &r.from, + ChatBoost(b) => return b.boost.source.user(), + RemovedChatBoost(b) => return b.source.user(), MessageReactionCount(_) | Poll(_) | Error(_) => return None, }; @@ -236,6 +246,20 @@ impl Update { i4(member.mentioned_users()) } UpdateKind::ChatJoinRequest(request) => i5(request.mentioned_users()), + + UpdateKind::ChatBoost(b) => { + if let Some(user) = b.boost.source.user() { + return i1(once(user)); + } + i6(empty()) + } + UpdateKind::RemovedChatBoost(b) => { + if let Some(user) = b.source.user() { + return i1(once(user)); + } + i6(empty()) + } + UpdateKind::MessageReactionCount(_) | UpdateKind::Error(_) => i6(empty()), } } @@ -253,6 +277,8 @@ impl Update { ChatJoinRequest(c) => &c.chat, MessageReaction(r) => &r.chat, MessageReactionCount(r) => &r.chat, + ChatBoost(b) => &b.chat, + RemovedChatBoost(b) => &b.chat, InlineQuery(_) | ChosenInlineResult(_) @@ -361,6 +387,13 @@ impl<'de> Deserialize<'de> for UpdateKind { .next_value::() .ok() .map(UpdateKind::ChatJoinRequest), + "chat_boost" => { + map.next_value::().ok().map(UpdateKind::ChatBoost) + } + "removed_chat_boost" => map + .next_value::() + .ok() + .map(UpdateKind::RemovedChatBoost), _ => Some(empty_error()), }) .unwrap_or_else(empty_error); @@ -416,6 +449,10 @@ impl Serialize for UpdateKind { UpdateKind::ChatJoinRequest(v) => { s.serialize_newtype_variant(name, 15, "chat_join_request", v) } + UpdateKind::ChatBoost(v) => s.serialize_newtype_variant(name, 16, "chat_boost", v), + UpdateKind::RemovedChatBoost(v) => { + s.serialize_newtype_variant(name, 17, "removed_chat_boost", v) + } UpdateKind::Error(v) => v.serialize(s), } } @@ -428,11 +465,12 @@ fn empty_error() -> UpdateKind { #[cfg(test)] mod test { use crate::types::{ - Chat, ChatFullInfo, ChatId, ChatKind, ChatPrivate, ChatPublic, LinkPreviewOptions, - MediaKind, MediaText, Message, MessageCommon, MessageId, MessageKind, - MessageReactionCountUpdated, MessageReactionUpdated, PublicChatChannel, PublicChatKind, - PublicChatSupergroup, ReactionCount, ReactionType, ReactionTypeKind, Update, UpdateId, - UpdateKind, User, UserId, + Chat, ChatBoost, ChatBoostRemoved, ChatBoostSource, ChatBoostSourceKind, + ChatBoostSourcePremium, ChatBoostUpdated, ChatFullInfo, ChatId, ChatKind, ChatPrivate, + ChatPublic, LinkPreviewOptions, MediaKind, MediaText, Message, MessageCommon, MessageId, + MessageKind, MessageReactionCountUpdated, MessageReactionUpdated, PublicChatChannel, + PublicChatKind, PublicChatSupergroup, ReactionCount, ReactionType, ReactionTypeKind, + Update, UpdateId, UpdateKind, User, UserId, }; use chrono::DateTime; @@ -948,4 +986,158 @@ mod test { let actual = serde_json::from_str::(json).unwrap(); assert_eq!(expected, actual); } + + #[test] + fn chat_boost_updated() { + let json = r#" + { + "update_id": 71651297, + "chat_boost": { + "chat": { + "id": -1002236736395, + "title": "Test", + "type": "channel" + }, + "boost": { + "boost_id": "4506e1b7e866e33fcbde78fe1746ec3a", + "add_date": 1721399621, + "expiration_date": 1745088963, + "source": { + "source": "premium", + "user": { + "id": 1459074222, + "is_bot": false, + "first_name": "shadowchain", + "username": "shdwchn10", + "language_code": "en", + "is_premium": true + } + } + } + } + } + "#; + + let expected = Update { + id: UpdateId(71651297), + kind: UpdateKind::ChatBoost(ChatBoostUpdated { + chat: Chat { + id: ChatId(-1002236736395), + kind: ChatKind::Public(ChatPublic { + title: Some("Test".to_owned()), + kind: PublicChatKind::Channel(PublicChatChannel { + username: None, + linked_chat_id: None, + }), + description: None, + invite_link: None, + has_protected_content: None, + }), + photo: None, + available_reactions: None, + pinned_message: None, + message_auto_delete_time: None, + has_hidden_members: false, + has_aggressive_anti_spam_enabled: false, + chat_full_info: ChatFullInfo { emoji_status_expiration_date: None }, + }, + boost: ChatBoost { + boost_id: "4506e1b7e866e33fcbde78fe1746ec3a".to_owned(), + add_date: DateTime::from_timestamp(1721399621, 0).unwrap(), + expiration_date: DateTime::from_timestamp(1745088963, 0).unwrap(), + source: ChatBoostSource { + kind: ChatBoostSourceKind::Premium(ChatBoostSourcePremium { + user: User { + id: UserId(1459074222), + is_bot: false, + first_name: "shadowchain".to_owned(), + last_name: None, + username: Some("shdwchn10".to_owned()), + language_code: Some("en".to_owned()), + is_premium: true, + added_to_attachment_menu: false, + }, + }), + }, + }, + }), + }; + + let actual = serde_json::from_str::(json).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn chat_boost_removed() { + let json = r#" + { + "update_id": 71651297, + "removed_chat_boost": { + "chat": { + "id": -1002236736395, + "title": "Test", + "type": "channel" + }, + "boost_id": "4506e1b7e866e33fcbde78fe1746ec3a", + "remove_date": 1721999621, + "source": { + "source": "premium", + "user": { + "id": 1459074222, + "is_bot": false, + "first_name": "shadowchain", + "username": "shdwchn10", + "language_code": "en", + "is_premium": true + } + } + } + } + "#; + + let expected = Update { + id: UpdateId(71651297), + kind: UpdateKind::RemovedChatBoost(ChatBoostRemoved { + chat: Chat { + id: ChatId(-1002236736395), + kind: ChatKind::Public(ChatPublic { + title: Some("Test".to_owned()), + kind: PublicChatKind::Channel(PublicChatChannel { + username: None, + linked_chat_id: None, + }), + description: None, + invite_link: None, + has_protected_content: None, + }), + photo: None, + available_reactions: None, + pinned_message: None, + message_auto_delete_time: None, + has_hidden_members: false, + has_aggressive_anti_spam_enabled: false, + chat_full_info: ChatFullInfo { emoji_status_expiration_date: None }, + }, + boost_id: "4506e1b7e866e33fcbde78fe1746ec3a".to_owned(), + remove_date: DateTime::from_timestamp(1721999621, 0).unwrap(), + source: ChatBoostSource { + kind: ChatBoostSourceKind::Premium(ChatBoostSourcePremium { + user: User { + id: UserId(1459074222), + is_bot: false, + first_name: "shadowchain".to_owned(), + last_name: None, + username: Some("shdwchn10".to_owned()), + language_code: Some("en".to_owned()), + is_premium: true, + added_to_attachment_menu: false, + }, + }), + }, + }), + }; + + let actual = serde_json::from_str::(json).unwrap(); + assert_eq!(expected, actual); + } } diff --git a/crates/teloxide/src/dispatching/filter_ext.rs b/crates/teloxide/src/dispatching/filter_ext.rs index d0f1d744..a2dbc7c6 100644 --- a/crates/teloxide/src/dispatching/filter_ext.rs +++ b/crates/teloxide/src/dispatching/filter_ext.rs @@ -161,4 +161,6 @@ define_update_ext! { (filter_my_chat_member, UpdateKind::MyChatMember, MyChatMember), (filter_chat_member, UpdateKind::ChatMember, ChatMember), (filter_chat_join_request, UpdateKind::ChatJoinRequest, ChatJoinRequest), + (filter_chat_boost, UpdateKind::ChatBoost, ChatBoost), + (filter_removed_chat_boost, UpdateKind::RemovedChatBoost, RemovedChatBoost), } diff --git a/crates/teloxide/src/dispatching/handler_description.rs b/crates/teloxide/src/dispatching/handler_description.rs index 2c1aefab..b18ebee8 100644 --- a/crates/teloxide/src/dispatching/handler_description.rs +++ b/crates/teloxide/src/dispatching/handler_description.rs @@ -77,6 +77,8 @@ impl EventKind for Kind { MyChatMember, ChatMember, ChatJoinRequest, + ChatBoost, + RemovedChatBoost, ] .into_iter() .map(Kind)