diff --git a/CHANGELOG.md b/CHANGELOG.md index f3ef54d8..5fc64a20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Deserialization of chat migrations, see issue [#427][issue427] ([#143][pr143]) - Type of `BanChatMember::until_date`: `u64` -> `chrono::DateTime<Utc>` ([#116][pr116]) - Type of `Poll::correct_option_id`: `i32` -> `u8` ([#119][pr119]) - Type of `Poll::open_period`: `i32` -> `u16` ([#119][pr119]) @@ -54,7 +55,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [pr133]: https://github.com/teloxide/teloxide-core/pull/133 [pr141]: https://github.com/teloxide/teloxide-core/pull/141 [pr142]: https://github.com/teloxide/teloxide-core/pull/142 +[pr143]: https://github.com/teloxide/teloxide-core/pull/143 [issue473]: https://github.com/teloxide/teloxide/issues/473 +[issue427]: https://github.com/teloxide/teloxide/issues/427 ## 0.3.3 - 2021-08-03 diff --git a/src/types/encrypted_credentials.rs b/src/types/encrypted_credentials.rs index 583821e7..50d87c73 100644 --- a/src/types/encrypted_credentials.rs +++ b/src/types/encrypted_credentials.rs @@ -43,8 +43,8 @@ mod tests { "hash":"1122", "secret":"secret" }"# - .replace("\n", "") - .replace(" ", ""); + .replace('\n', "") + .replace(' ', ""); let encrypted_credentials = EncryptedCredentials { data: "someData".to_string(), hash: "1122".to_string(), diff --git a/src/types/message.rs b/src/types/message.rs index 0afd73f4..c9a4ed48 100644 --- a/src/types/message.rs +++ b/src/types/message.rs @@ -46,7 +46,6 @@ pub enum MessageKind { SupergroupChatCreated(MessageSupergroupChatCreated), ChannelChatCreated(MessageChannelChatCreated), MessageAutoDeleteTimerChanged(MessageMessageAutoDeleteTimerChanged), - Migrate(MessageMigrate), Pinned(MessagePinned), Invoice(MessageInvoice), SuccessfulPayment(MessageSuccessfulPayment), @@ -157,23 +156,32 @@ pub struct MessageMessageAutoDeleteTimerChanged { pub message_auto_delete_timer_changed: MessageAutoDeleteTimerChanged, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct MessageMigrate { +/// Represents group migration to a supergroup or a supergroup migration from a +/// group. +/// +/// Note that bot receives **both** updates. For example: a group with id `0` +/// migrates to a supergroup with id `1` bots in that group will receive 2 +/// updates: +/// - `message.chat.id = 0`, `message.chat_migration() = ChatMigration::To { +/// chat_id: 1 }` +/// - `message.chat.id = 1`, `message.chat_migration() = ChatMigration::From { +/// chat_id: 0 }` +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ChatMigration { /// The group has been migrated to a supergroup with the specified - /// identifier. This number may be greater than 32 bits and some - /// programming languages may have difficulty/silent defects in - /// interpreting it. But it is smaller than 52 bits, so a signed 64 bit - /// integer or double-precision float type are safe for storing this - /// identifier. - pub migrate_to_chat_id: i64, + /// identifier `chat_id`. + To { + #[serde(rename = "migrate_to_chat_id")] + chat_id: i64, + }, /// The supergroup has been migrated from a group with the specified - /// identifier. This number may be greater than 32 bits and some - /// programming languages may have difficulty/silent defects in - /// interpreting it. But it is smaller than 52 bits, so a signed 64 bit - /// integer or double-precision float type are safe for storing this - /// identifier. - pub migrate_from_chat_id: i64, + /// identifier `chat_id`. + From { + #[serde(rename = "migrate_from_chat_id")] + chat_id: i64, + }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -283,6 +291,7 @@ pub enum MediaKind { VideoNote(MediaVideoNote), Voice(MediaVoice), Venue(MediaVenue), + Migration(ChatMigration), } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -500,15 +509,15 @@ mod getters { use crate::types::{ self, message::{ForwardKind::NonChannel, MessageKind::*}, - Chat, ForwardChannel, ForwardKind, ForwardNonChannel, ForwardOrigin, ForwardedFrom, - MediaAnimation, MediaAudio, MediaContact, MediaDocument, MediaGame, MediaKind, - MediaLocation, MediaPhoto, MediaPoll, MediaSticker, MediaText, MediaVenue, MediaVideo, - MediaVideoNote, MediaVoice, Message, MessageChannelChatCreated, MessageCommon, + Chat, ChatMigration, ForwardChannel, ForwardKind, ForwardNonChannel, ForwardOrigin, + ForwardedFrom, MediaAnimation, MediaAudio, MediaContact, MediaDocument, MediaGame, + MediaKind, MediaLocation, MediaPhoto, MediaPoll, MediaSticker, MediaText, MediaVenue, + MediaVideo, MediaVideoNote, MediaVoice, Message, MessageChannelChatCreated, MessageCommon, MessageConnectedWebsite, MessageDeleteChatPhoto, MessageDice, MessageEntity, - MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageMigrate, - MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, - MessagePinned, MessageProximityAlertTriggered, MessageSuccessfulPayment, - MessageSupergroupChatCreated, PhotoSize, True, User, + MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageNewChatMembers, + MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, MessagePinned, + MessageProximityAlertTriggered, MessageSuccessfulPayment, MessageSupergroupChatCreated, + PhotoSize, True, User, }; /// Getters for [Message] fields from [telegram docs]. @@ -926,21 +935,32 @@ mod getters { } } + pub fn chat_migration(&self) -> Option<ChatMigration> { + match &self.kind { + Common(MessageCommon { + media_kind: MediaKind::Migration(chat_migration), + .. + }) => Some(*chat_migration), + _ => None, + } + } + pub fn migrate_to_chat_id(&self) -> Option<i64> { match &self.kind { - Migrate(MessageMigrate { - migrate_to_chat_id, .. - }) => Some(*migrate_to_chat_id), + Common(MessageCommon { + media_kind: MediaKind::Migration(ChatMigration::To { chat_id }), + .. + }) => Some(*chat_id), _ => None, } } pub fn migrate_from_chat_id(&self) -> Option<i64> { match &self.kind { - Migrate(MessageMigrate { - migrate_from_chat_id, + Common(MessageCommon { + media_kind: MediaKind::Migration(ChatMigration::From { chat_id }), .. - }) => Some(*migrate_from_chat_id), + }) => Some(*chat_id), _ => None, } } @@ -1252,4 +1272,42 @@ mod tests { let message = from_str::<Message>(json); assert!(message.is_ok()); } + + /// Regression test for <https://github.com/teloxide/teloxide/issues/427> + #[test] + fn issue_427() { + let old = -599075523; + let new = -1001555296434; + + // Migration to a supergroup + let json = r#"{"chat":{"all_members_are_administrators":false,"id":-599075523,"title":"test","type":"group"},"date":1629404938,"from":{"first_name":"nullptr","id":729497414,"is_bot":false,"language_code":"en","username":"hex0x0000"},"message_id":16,"migrate_to_chat_id":-1001555296434}"#; + let message: Message = from_str(json).unwrap(); + + assert_eq!(message.chat.id, old); + assert_eq!( + message.chat_migration(), + Some(ChatMigration::To { chat_id: new }) + ); + assert_eq!(message.migrate_to_chat_id(), Some(new)); + + // The user who initialized the migration + assert!(message.from().is_some()); + + // Migration from a common group + let json = r#"{"chat":{"id":-1001555296434,"title":"test","type":"supergroup"},"date":1629404938,"from":{"first_name":"Group","id":1087968824,"is_bot":true,"username":"GroupAnonymousBot"},"message_id":1,"migrate_from_chat_id":-599075523,"sender_chat":{"id":-1001555296434,"title":"test","type":"supergroup"}}"#; + let message: Message = from_str(json).unwrap(); + + assert_eq!(message.chat.id, new); + assert_eq!( + message.chat_migration(), + Some(ChatMigration::From { chat_id: old }) + ); + assert_eq!(message.migrate_from_chat_id(), Some(old)); + + // Anonymous bot + assert!(message.from().is_some()); + + // The chat to which the group migrated + assert!(message.sender_chat().is_some()); + } }