diff --git a/src/types/chat.rs b/src/types/chat.rs
index 8f8a8cc3..0fccb5e7 100644
--- a/src/types/chat.rs
+++ b/src/types/chat.rs
@@ -28,54 +28,62 @@ pub struct Chat {
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(untagged)]
 pub enum ChatKind {
-    NonPrivate {
-        /// A title, for supergroups, channels and group chats.
-        title: Option<String>,
+    NonPrivate(ChatNonPrivate),
+    Private(ChatPrivate),
+}
 
-        #[serde(flatten)]
-        kind: NonPrivateChatKind,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct ChatNonPrivate {
+    /// A title, for supergroups, channels and group chats.
+    pub title: Option<String>,
 
-        /// A description, for groups, supergroups and channel chats. Returned
-        /// only in [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        description: Option<String>,
+    #[serde(flatten)]
+    pub kind: NonPrivateChatKind,
 
-        /// A chat invite link, for groups, supergroups and channel chats. Each
-        /// administrator in a chat generates their own invite links, so the
-        /// bot must first generate the link using
-        /// [`Bot::export_chat_invite_link`]. Returned only in
-        /// [`Bot::get_chat`].
-        ///
-        /// [`Bot::export_chat_invite_link`]:
-        /// crate::Bot::export_chat_invite_link
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        invite_link: Option<String>,
+    /// A description, for groups, supergroups and channel chats. Returned
+    /// only in [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub description: Option<String>,
 
-        /// Pinned message, for groups, supergroups and channels. Returned only
-        /// in [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        pinned_message: Option<Box<Message>>,
-    },
-    Private {
-        /// A dummy field. Used to ensure that the `type` field is equal to
-        /// `private`.
-        #[serde(rename = "type")]
-        #[serde(deserialize_with = "assert_private_field")]
-        type_: (),
+    /// A chat invite link, for groups, supergroups and channel chats. Each
+    /// administrator in a chat generates their own invite links, so the
+    /// bot must first generate the link using
+    /// [`Bot::export_chat_invite_link`]. Returned only in
+    /// [`Bot::get_chat`].
+    ///
+    /// [`Bot::export_chat_invite_link`]:
+    /// crate::Bot::export_chat_invite_link
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub invite_link: Option<String>,
 
-        /// A username, for private chats, supergroups and channels if
-        /// available.
-        username: Option<String>,
+    /// Pinned message, for groups, supergroups and channels. Returned only
+    /// in [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub pinned_message: Option<Box<Message>>,
+}
 
-        /// A first name of the other party in a private chat.
-        first_name: Option<String>,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct ChatPrivate {
+    /// A dummy field. Used to ensure that the `type` field is equal to
+    /// `private`.
+    #[serde(rename = "type")]
+    #[serde(deserialize_with = "assert_private_field")]
+    pub type_: (),
 
-        /// A last name of the other party in a private chat.
-        last_name: Option<String>,
-    },
+    /// A username, for private chats, supergroups and channels if
+    /// available.
+    pub username: Option<String>,
+
+    /// A first name of the other party in a private chat.
+    pub first_name: Option<String>,
+
+    /// A last name of the other party in a private chat.
+    pub last_name: Option<String>,
 }
 
 #[serde_with_macros::skip_serializing_none]
@@ -83,47 +91,58 @@ pub enum ChatKind {
 #[serde(rename_all = "snake_case")]
 #[serde(tag = "type")]
 pub enum NonPrivateChatKind {
-    Channel {
-        /// A username, for private chats, supergroups and channels if
-        /// available.
-        username: Option<String>,
-    },
-    Group {
-        /// A default chat member permissions, for groups and supergroups.
-        /// Returned only in [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        permissions: Option<ChatPermissions>,
-    },
-    Supergroup {
-        /// A username, for private chats, supergroups and channels if
-        /// available.
-        username: Option<String>,
+    Channel(NonPrivateChatChannel),
+    Group(NonPrivateChatGroup),
+    Supergroup(NonPrivateChatSupergroup),
+}
 
-        /// For supergroups, name of group sticker set. Returned only in
-        /// [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        sticker_set_name: Option<String>,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct NonPrivateChatChannel {
+    /// A username, for private chats, supergroups and channels if available.
+    pub username: Option<String>,
+}
 
-        /// `true`, if the bot can change the group sticker set. Returned only
-        /// in [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        can_set_sticker_set: Option<bool>,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct NonPrivateChatGroup {
+    /// A default chat member permissions, for groups and supergroups. Returned
+    /// only from [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub permissions: Option<ChatPermissions>,
+}
 
-        /// A default chat member permissions, for groups and supergroups.
-        /// Returned only in [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        permissions: Option<ChatPermissions>,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct NonPrivateChatSupergroup {
+    /// A username, for private chats, supergroups and channels if
+    /// available.
+    pub username: Option<String>,
 
-        /// The minimum allowed delay between consecutive messages sent by each
-        /// unpriviledged user. Returned only in [`Bot::get_chat`].
-        ///
-        /// [`Bot::get_chat`]: crate::Bot::get_chat
-        slow_mode_delay: Option<i32>,
-    },
+    /// For supergroups, name of group sticker set. Returned only from
+    /// [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub sticker_set_name: Option<String>,
+
+    /// `true`, if the bot can change the group sticker set. Returned only
+    /// from [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub can_set_sticker_set: Option<bool>,
+
+    /// A default chat member permissions, for groups and supergroups.
+    /// Returned only from [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub permissions: Option<ChatPermissions>,
+
+    /// The minimum allowed delay between consecutive messages sent by each
+    /// unpriviledged user. Returned only from [`Bot::get_chat`].
+    ///
+    /// [`Bot::get_chat`]: crate::Bot::get_chat
+    pub slow_mode_delay: Option<i32>,
 }
 
 struct PrivateChatKindVisitor;
@@ -158,38 +177,36 @@ where
 
 impl Chat {
     pub fn is_private(&self) -> bool {
-        match self.kind {
-            ChatKind::Private { .. } => true,
-            _ => false,
-        }
+        matches!(self.kind, ChatKind::Private(_))
     }
     pub fn is_group(&self) -> bool {
-        match self.kind {
-            ChatKind::NonPrivate {
-                kind: NonPrivateChatKind::Group { .. },
+        matches!(
+            self.kind,
+            ChatKind::NonPrivate(ChatNonPrivate {
+                kind: NonPrivateChatKind::Group(_),
                 ..
-            } => true,
-            _ => false,
-        }
+            })
+        )
     }
     pub fn is_supergroup(&self) -> bool {
-        match self.kind {
-            ChatKind::NonPrivate {
-                kind: NonPrivateChatKind::Supergroup { .. },
+        matches!(
+            self.kind,
+            ChatKind::NonPrivate(ChatNonPrivate {
+                kind: NonPrivateChatKind::Supergroup(_),
                 ..
-            } => true,
-            _ => false,
-        }
+            })
+        )
     }
     pub fn is_channel(&self) -> bool {
-        match self.kind {
-            ChatKind::NonPrivate {
-                kind: NonPrivateChatKind::Channel { .. },
+        matches!(
+            self.kind,
+            ChatKind::NonPrivate(ChatNonPrivate {
+                kind: NonPrivateChatKind::Channel(_),
                 ..
-            } => true,
-            _ => false,
-        }
+            })
+        )
     }
+
     pub fn is_chat(&self) -> bool {
         self.is_private() || self.is_group() || self.is_supergroup()
     }
@@ -205,15 +222,15 @@ mod tests {
     fn channel_de() {
         let expected = Chat {
             id: -1,
-            kind: ChatKind::NonPrivate {
+            kind: ChatKind::NonPrivate(ChatNonPrivate {
                 title: None,
-                kind: NonPrivateChatKind::Channel {
+                kind: NonPrivateChatKind::Channel(NonPrivateChatChannel {
                     username: Some("channelname".into()),
-                },
+                }),
                 description: None,
                 invite_link: None,
                 pinned_message: None,
-            },
+            }),
             photo: None,
         };
         let actual =
@@ -227,12 +244,12 @@ mod tests {
         assert_eq!(
             Chat {
                 id: 0,
-                kind: ChatKind::Private {
+                kind: ChatKind::Private(ChatPrivate {
                     type_: (),
                     username: Some("username".into()),
                     first_name: Some("Anon".into()),
                     last_name: None,
-                },
+                }),
                 photo: None,
             },
             from_str(
diff --git a/src/types/encrypted_passport_element.rs b/src/types/encrypted_passport_element.rs
index 6b52f67b..d03eca1b 100644
--- a/src/types/encrypted_passport_element.rs
+++ b/src/types/encrypted_passport_element.rs
@@ -19,337 +19,392 @@ pub struct EncryptedPassportElement {
     pub kind: EncryptedPassportElementKind,
 }
 
-#[serde_with_macros::skip_serializing_none]
 #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 #[allow(clippy::large_enum_variant)]
 pub enum EncryptedPassportElementKind {
-    PersonalDetails {
-        ///  Base64-encoded encrypted Telegram Passport element data provided
-        /// by the user, available for `personal_details`, `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport` and
-        /// `address` types. Can be decrypted and verified using the
-        /// accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        data: String,
-    },
-    Passport {
-        ///  Base64-encoded encrypted Telegram Passport element data provided
-        /// by the user, available for `personal_details`, `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport` and
-        /// `address` types. Can be decrypted and verified using the
-        /// accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        data: String,
-
-        /// Encrypted file with the front side of the document, provided by the
-        /// user. Available for `passport`, `driver_license`, `identity_card`
-        /// and `internal_passport`. The file can be decrypted and verified
-        /// using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        front_side: PassportFile,
-
-        /// Encrypted file with the selfie of the user holding a document,
-        /// provided by the user; available for `passport`, `driver_license`,
-        /// `identity_card` and `internal_passport`. The file can be decrypted
-        /// and verified using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        selfie: PassportFile,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    DriverLicense {
-        ///  Base64-encoded encrypted Telegram Passport element data provided
-        /// by the user, available for `personal_details`, `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport` and
-        /// `address` types. Can be decrypted and verified using the
-        /// accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        data: String,
-
-        /// Encrypted file with the front side of the document, provided by the
-        /// user. Available for `passport`, `driver_license`, `identity_card`
-        /// and `internal_passport`. The file can be decrypted and verified
-        /// using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        front_side: PassportFile,
-
-        /// Encrypted file with the reverse side of the document, provided by
-        /// the user. Available for `driver_license` and `identity_card`. The
-        /// file can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        reverse_side: PassportFile,
-
-        /// Encrypted file with the selfie of the user holding a document,
-        /// provided by the user; available for `passport`, `driver_license`,
-        /// `identity_card` and `internal_passport`. The file can be decrypted
-        /// and verified using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        selfie: PassportFile,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    IdentityCard {
-        ///  Base64-encoded encrypted Telegram Passport element data provided
-        /// by the user, available for `personal_details`, `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport` and
-        /// `address` types. Can be decrypted and verified using the
-        /// accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        data: String,
-
-        /// Encrypted file with the front side of the document, provided by the
-        /// user. Available for `passport`, `driver_license`, `identity_card`
-        /// and `internal_passport`. The file can be decrypted and verified
-        /// using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        front_side: PassportFile,
-
-        /// Encrypted file with the reverse side of the document, provided by
-        /// the user. Available for `driver_license` and `identity_card`. The
-        /// file can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        reverse_side: PassportFile,
-
-        /// Encrypted file with the selfie of the user holding a document,
-        /// provided by the user; available for `passport`, `driver_license`,
-        /// `identity_card` and `internal_passport`. The file can be decrypted
-        /// and verified using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        selfie: PassportFile,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    InternalPassport {
-        ///  Base64-encoded encrypted Telegram Passport element data provided
-        /// by the user, available for `personal_details`, `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport` and
-        /// `address` types. Can be decrypted and verified using the
-        /// accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        data: String,
-
-        /// Encrypted file with the front side of the document, provided by the
-        /// user. Available for `passport`, `driver_license`, `identity_card`
-        /// and `internal_passport`. The file can be decrypted and verified
-        /// using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        front_side: PassportFile,
-
-        /// Encrypted file with the selfie of the user holding a document,
-        /// provided by the user; available for `passport`, `driver_license`,
-        /// `identity_card` and `internal_passport`. The file can be decrypted
-        /// and verified using the accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        selfie: PassportFile,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    Address {
-        ///  Base64-encoded encrypted Telegram Passport element data provided
-        /// by the user, available for `personal_details`, `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport` and
-        /// `address` types. Can be decrypted and verified using the
-        /// accompanying [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        data: String,
-    },
-    UtilityBill {
-        /// Array of encrypted files with documents provided by the user,
-        /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        files: Vec<PassportFile>,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    BankStatement {
-        /// Array of encrypted files with documents provided by the user,
-        /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        files: Vec<PassportFile>,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    RentalAgreement {
-        /// Array of encrypted files with documents provided by the user,
-        /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        files: Vec<PassportFile>,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    PassportRegistration {
-        /// Array of encrypted files with documents provided by the user,
-        /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        files: Vec<PassportFile>,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    TemporaryRegistration {
-        /// Array of encrypted files with documents provided by the user,
-        /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        files: Vec<PassportFile>,
-
-        /// Array of encrypted files with translated versions of documents
-        /// provided by the user. Available if requested for `passport`,
-        /// `driver_license`, `identity_card`, `internal_passport`,
-        /// `utility_bill`, `bank_statement`, `rental_agreement`,
-        /// `passport_registration` and `temporary_registration` types. Files
-        /// can be decrypted and verified using the accompanying
-        /// [`EncryptedCredentials`].
-        ///
-        /// [`EncryptedCredentials`]:
-        /// crate::types::EncryptedCredentials
-        translation: Option<Vec<PassportFile>>,
-    },
-    PhoneNumber {
-        /// User's verified phone number, available only for `phone_number`
-        /// type.
-        phone_number: String,
-    },
-    Email {
-        /// User's verified email address, available only for `email` type.
-        email: String,
-    },
+    PersonalDetails(EncryptedPassportElementPersonalDetails),
+    Passport(EncryptedPassportElementPassport),
+    DriverLicense(EncryptedPassportElementDriverLicense),
+    IdentityCard(EncryptedPassportElementIdentityCard),
+    InternalPassport(EncryptedPassportElementInternalPassport),
+    Address(EncryptedPassportElementAddress),
+    UtilityBill(EncryptedPassportElementUtilityBill),
+    BankStatement(EncryptedPassportElementBankStatement),
+    RentalAgreement(EncryptedPassportElementRentalAgreement),
+    PassportRegistration(EncryptedPassportElementPassportRegistration),
+    EncryptedPassportElement(EncryptedPassportElementTemporaryRegistration),
+    PhoneNumber(EncryptedPassportElementPhoneNumber),
+    Email(EncryptedPassportElementEmail),
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementPersonalDetails {
+    ///  Base64-encoded encrypted Telegram Passport element data provided
+    /// by the user, available for `personal_details`, `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport` and
+    /// `address` types. Can be decrypted and verified using the
+    /// accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub data: String,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementPassport {
+    ///  Base64-encoded encrypted Telegram Passport element data provided
+    /// by the user, available for `personal_details`, `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport` and
+    /// `address` types. Can be decrypted and verified using the
+    /// accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub data: String,
+
+    /// Encrypted file with the front side of the document, provided by the
+    /// user. Available for `passport`, `driver_license`, `identity_card`
+    /// and `internal_passport`. The file can be decrypted and verified
+    /// using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub front_side: PassportFile,
+
+    /// Encrypted file with the selfie of the user holding a document,
+    /// provided by the user; available for `passport`, `driver_license`,
+    /// `identity_card` and `internal_passport`. The file can be decrypted
+    /// and verified using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub selfie: PassportFile,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementDriverLicense {
+    ///  Base64-encoded encrypted Telegram Passport element data provided
+    /// by the user, available for `personal_details`, `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport` and
+    /// `address` types. Can be decrypted and verified using the
+    /// accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub data: String,
+
+    /// Encrypted file with the front side of the document, provided by the
+    /// user. Available for `passport`, `driver_license`, `identity_card`
+    /// and `internal_passport`. The file can be decrypted and verified
+    /// using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub front_side: PassportFile,
+
+    /// Encrypted file with the reverse side of the document, provided by
+    /// the user. Available for `driver_license` and `identity_card`. The
+    /// file can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub reverse_side: PassportFile,
+
+    /// Encrypted file with the selfie of the user holding a document,
+    /// provided by the user; available for `passport`, `driver_license`,
+    /// `identity_card` and `internal_passport`. The file can be decrypted
+    /// and verified using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub selfie: PassportFile,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementIdentityCard {
+    ///  Base64-encoded encrypted Telegram Passport element data provided
+    /// by the user, available for `personal_details`, `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport` and
+    /// `address` types. Can be decrypted and verified using the
+    /// accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub data: String,
+
+    /// Encrypted file with the front side of the document, provided by the
+    /// user. Available for `passport`, `driver_license`, `identity_card`
+    /// and `internal_passport`. The file can be decrypted and verified
+    /// using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub front_side: PassportFile,
+
+    /// Encrypted file with the reverse side of the document, provided by
+    /// the user. Available for `driver_license` and `identity_card`. The
+    /// file can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub reverse_side: PassportFile,
+
+    /// Encrypted file with the selfie of the user holding a document,
+    /// provided by the user; available for `passport`, `driver_license`,
+    /// `identity_card` and `internal_passport`. The file can be decrypted
+    /// and verified using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub selfie: PassportFile,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+
+pub struct EncryptedPassportElementInternalPassport {
+    ///  Base64-encoded encrypted Telegram Passport element data provided
+    /// by the user, available for `personal_details`, `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport` and
+    /// `address` types. Can be decrypted and verified using the
+    /// accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub data: String,
+
+    /// Encrypted file with the front side of the document, provided by the
+    /// user. Available for `passport`, `driver_license`, `identity_card`
+    /// and `internal_passport`. The file can be decrypted and verified
+    /// using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub front_side: PassportFile,
+
+    /// Encrypted file with the selfie of the user holding a document,
+    /// provided by the user; available for `passport`, `driver_license`,
+    /// `identity_card` and `internal_passport`. The file can be decrypted
+    /// and verified using the accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub selfie: PassportFile,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementAddress {
+    ///  Base64-encoded encrypted Telegram Passport element data provided
+    /// by the user, available for `personal_details`, `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport` and
+    /// `address` types. Can be decrypted and verified using the
+    /// accompanying [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub data: String,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementUtilityBill {
+    /// Array of encrypted files with documents provided by the user,
+    /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub files: Vec<PassportFile>,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+
+pub struct EncryptedPassportElementBankStatement {
+    /// Array of encrypted files with documents provided by the user,
+    /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub files: Vec<PassportFile>,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementRentalAgreement {
+    /// Array of encrypted files with documents provided by the user,
+    /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub files: Vec<PassportFile>,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+
+pub struct EncryptedPassportElementPassportRegistration {
+    /// Array of encrypted files with documents provided by the user,
+    /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub files: Vec<PassportFile>,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementTemporaryRegistration {
+    /// Array of encrypted files with documents provided by the user,
+    /// available for `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub files: Vec<PassportFile>,
+
+    /// Array of encrypted files with translated versions of documents
+    /// provided by the user. Available if requested for `passport`,
+    /// `driver_license`, `identity_card`, `internal_passport`,
+    /// `utility_bill`, `bank_statement`, `rental_agreement`,
+    /// `passport_registration` and `temporary_registration` types. Files
+    /// can be decrypted and verified using the accompanying
+    /// [`EncryptedCredentials`].
+    ///
+    /// [`EncryptedCredentials`]:
+    /// crate::types::EncryptedCredentials
+    pub translation: Option<Vec<PassportFile>>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct EncryptedPassportElementPhoneNumber {
+    /// User's verified phone number, available only for `phone_number`
+    /// type.
+    pub phone_number: String,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+
+pub struct EncryptedPassportElementEmail {
+    /// User's verified email address, available only for `email` type.
+    pub email: String,
 }
diff --git a/src/types/inline_query_result.rs b/src/types/inline_query_result.rs
index b4273dce..de3b28e8 100644
--- a/src/types/inline_query_result.rs
+++ b/src/types/inline_query_result.rs
@@ -59,6 +59,7 @@ mod tests {
     use crate::types::{
         inline_keyboard_markup::InlineKeyboardMarkup, parse_mode::ParseMode,
         InlineQueryResult, InlineQueryResultCachedAudio, InputMessageContent,
+        InputMessageContentText,
     };
 
     #[test]
@@ -89,11 +90,13 @@ mod tests {
                 caption: Some(String::from("caption")),
                 parse_mode: Some(ParseMode::HTML),
                 reply_markup: Some(InlineKeyboardMarkup::default()),
-                input_message_content: Some(InputMessageContent::Text {
-                    message_text: String::from("message_text"),
-                    parse_mode: Some(ParseMode::MarkdownV2),
-                    disable_web_page_preview: Some(true),
-                }),
+                input_message_content: Some(InputMessageContent::Text(
+                    InputMessageContentText {
+                        message_text: String::from("message_text"),
+                        parse_mode: Some(ParseMode::MarkdownV2),
+                        disable_web_page_preview: Some(true),
+                    },
+                )),
             });
 
         let expected_json = r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id","caption":"caption","parse_mode":"HTML","reply_markup":{"inline_keyboard":[]},"input_message_content":{"message_text":"message_text","parse_mode":"MarkdownV2","disable_web_page_preview":true}}"#;
diff --git a/src/types/input_media.rs b/src/types/input_media.rs
index 365597d7..a7b8daa8 100644
--- a/src/types/input_media.rs
+++ b/src/types/input_media.rs
@@ -3,7 +3,6 @@ use serde::{Deserialize, Serialize};
 use crate::types::{InputFile, ParseMode};
 
 // TODO: should variants use new-type?
-#[serde_with_macros::skip_serializing_none]
 #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
 #[serde(tag = "type")]
 #[serde(rename_all = "snake_case")]
@@ -11,169 +10,185 @@ use crate::types::{InputFile, ParseMode};
 ///
 /// [The official docs](https://core.telegram.org/bots/api#inputmedia).
 pub enum InputMedia {
-    /// Represents a photo to be sent.
+    Photo(InputMediaPhoto),
+    Video(InputMediaVideo),
+    Animation(InputMediaAnimation),
+    Audio(InputMediaAudio),
+    Document(InputMediaDocument),
+}
+
+/// Represents a photo to be sent.
+///
+/// [The official docs](https://core.telegram.org/bots/api#inputmediaphoto).
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct InputMediaPhoto {
+    /// File to send.
+    pub media: InputFile,
+
+    /// Caption of the photo to be sent, 0-1024 characters.
+    pub caption: Option<String>,
+
+    /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
+    /// italic, fixed-width text or inline URLs] in the media caption.
     ///
-    /// [The official docs](https://core.telegram.org/bots/api#inputmediaphoto).
-    Photo {
-        /// File to send.
-        media: InputFile,
+    /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
+    /// [HTML]: https://core.telegram.org/bots/api#html-style
+    /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
+    pub parse_mode: Option<ParseMode>,
+}
 
-        /// Caption of the photo to be sent, 0-1024 characters.
-        caption: Option<String>,
+/// Represents a video to be sent.
+///
+/// [The official docs](https://core.telegram.org/bots/api#inputmediavideo).
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct InputMediaVideo {
+    // File to send.
+    pub media: InputFile,
 
-        /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
-        /// italic, fixed-width text or inline URLs] in the media caption.
-        ///
-        /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
-        /// [HTML]: https://core.telegram.org/bots/api#html-style
-        /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
-        parse_mode: Option<ParseMode>,
-    },
+    /// Thumbnail of the file sent; can be ignored if thumbnail generation
+    /// for the file is supported server-side. The thumbnail should be in
+    /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
+    /// height should not exceed 320. Ignored if the file is not uploaded
+    /// using multipart/form-data.
+    pub thumb: Option<InputFile>,
 
-    /// Represents a video to be sent.
+    /// Caption of the video to be sent, 0-1024 characters.
+    pub caption: Option<String>,
+
+    /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
+    /// italic, fixed-width text or inline URLs] in the media caption.
     ///
-    /// [The official docs](https://core.telegram.org/bots/api#inputmediavideo).
-    Video {
-        // File to send.
-        media: InputFile,
+    /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
+    /// [HTML]: https://core.telegram.org/bots/api#html-style
+    /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
+    pub parse_mode: Option<ParseMode>,
 
-        /// Thumbnail of the file sent; can be ignored if thumbnail generation
-        /// for the file is supported server-side. The thumbnail should be in
-        /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
-        /// height should not exceed 320. Ignored if the file is not uploaded
-        /// using multipart/form-data.
-        thumb: Option<InputFile>,
+    /// Video width.
+    pub width: Option<u16>,
 
-        /// Caption of the video to be sent, 0-1024 characters.
-        caption: Option<String>,
+    /// Video height.
+    pub height: Option<u16>,
 
-        /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
-        /// italic, fixed-width text or inline URLs] in the media caption.
-        ///
-        /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
-        /// [HTML]: https://core.telegram.org/bots/api#html-style
-        /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
-        parse_mode: Option<ParseMode>,
+    /// Video duration.
+    pub duration: Option<u16>,
 
-        /// Video width.
-        width: Option<u16>,
+    /// Pass `true`, if the uploaded video is suitable for streaming.
+    pub supports_streaming: Option<bool>,
+}
 
-        /// Video height.
-        height: Option<u16>,
+/// Represents an animation file (GIF or H.264/MPEG-4 AVC video without
+/// sound) to be sent.
+///
+/// [The official docs](https://core.telegram.org/bots/api#inputmediaanimation).
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct InputMediaAnimation {
+    /// File to send.
+    pub media: InputFile,
 
-        /// Video duration.
-        duration: Option<u16>,
+    /// Thumbnail of the file sent; can be ignored if thumbnail generation
+    /// for the file is supported server-side. The thumbnail should be in
+    /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
+    /// height should not exceed 320. Ignored if the file is not uploaded
+    /// using multipart/form-data.
+    pub thumb: Option<InputFile>,
 
-        /// Pass `true`, if the uploaded video is suitable for streaming.
-        supports_streaming: Option<bool>,
-    },
+    /// Caption of the animation to be sent, 0-1024 characters.
+    pub caption: Option<String>,
 
-    /// Represents an animation file (GIF or H.264/MPEG-4 AVC video without
-    /// sound) to be sent.
+    /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
+    /// italic, fixed-width text or inline URLs] in the media caption.
     ///
-    /// [The official docs](https://core.telegram.org/bots/api#inputmediaanimation).
-    Animation {
-        /// File to send.
-        media: InputFile,
+    /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
+    /// [HTML]: https://core.telegram.org/bots/api#html-style
+    /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
+    pub parse_mode: Option<ParseMode>,
 
-        /// Thumbnail of the file sent; can be ignored if thumbnail generation
-        /// for the file is supported server-side. The thumbnail should be in
-        /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
-        /// height should not exceed 320. Ignored if the file is not uploaded
-        /// using multipart/form-data.
-        thumb: Option<InputFile>,
+    /// Animation width.
+    pub width: Option<u16>,
 
-        /// Caption of the animation to be sent, 0-1024 characters.
-        caption: Option<String>,
+    /// Animation height.
+    pub height: Option<u16>,
 
-        /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
-        /// italic, fixed-width text or inline URLs] in the media caption.
-        ///
-        /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
-        /// [HTML]: https://core.telegram.org/bots/api#html-style
-        /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
-        parse_mode: Option<ParseMode>,
+    /// Animation duration.
+    pub duration: Option<u16>,
+}
 
-        /// Animation width.
-        width: Option<u16>,
+/// Represents an audio file to be treated as music to be sent.
+///
+/// [The official docs](https://core.telegram.org/bots/api#inputmediaaudio).
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct InputMediaAudio {
+    /// File to send.
+    pub media: InputFile,
 
-        /// Animation height.
-        height: Option<u16>,
+    /// Thumbnail of the file sent; can be ignored if thumbnail generation
+    /// for the file is supported server-side. The thumbnail should be in
+    /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
+    /// height should not exceed 320. Ignored if the file is not uploaded
+    /// using multipart/form-data.
+    pub thumb: Option<InputFile>,
 
-        /// Animation duration.
-        duration: Option<u16>,
-    },
+    /// Caption of the audio to be sent, 0-1024 characters.
+    pub caption: Option<String>,
 
-    /// Represents an audio file to be treated as music to be sent.
+    /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
+    /// italic, fixed-width text or inline URLs] in the media caption.
     ///
-    /// [The official docs](https://core.telegram.org/bots/api#inputmediaaudio).
-    Audio {
-        /// File to send.
-        media: InputFile,
+    /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
+    /// [HTML]: https://core.telegram.org/bots/api#html-style
+    /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
+    pub parse_mode: Option<String>,
 
-        /// Thumbnail of the file sent; can be ignored if thumbnail generation
-        /// for the file is supported server-side. The thumbnail should be in
-        /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
-        /// height should not exceed 320. Ignored if the file is not uploaded
-        /// using multipart/form-data.
-        thumb: Option<InputFile>,
+    /// Duration of the audio in seconds.
+    pub duration: Option<u16>,
 
-        /// Caption of the audio to be sent, 0-1024 characters.
-        caption: Option<String>,
+    /// Performer of the audio.
+    pub performer: Option<String>,
 
-        /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
-        /// italic, fixed-width text or inline URLs] in the media caption.
-        ///
-        /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
-        /// [HTML]: https://core.telegram.org/bots/api#html-style
-        /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
-        parse_mode: Option<String>,
+    /// Title of the audio.
+    pub title: Option<String>,
+}
 
-        /// Duration of the audio in seconds.
-        duration: Option<u16>,
+/// Represents a general file to be sent.
+///
+/// [The official docs](https://core.telegram.org/bots/api#inputmediadocument).
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct InputMediaDocument {
+    /// File to send.
+    pub media: InputFile,
 
-        /// Performer of the audio.
-        performer: Option<String>,
+    /// Thumbnail of the file sent; can be ignored if thumbnail generation
+    /// for the file is supported server-side. The thumbnail should be in
+    /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
+    /// height should not exceed 320. Ignored if the file is not uploaded
+    /// using multipart/form-data.
+    pub thumb: Option<InputFile>,
 
-        /// Title of the audio.
-        title: Option<String>,
-    },
+    /// Caption of the document to be sent, 0-1024 charactersю
+    pub caption: Option<String>,
 
-    /// Represents a general file to be sent.
+    /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
+    /// italic, fixed-width text or inline URLs] in the media caption.
     ///
-    /// [The official docs](https://core.telegram.org/bots/api#inputmediadocument).
-    Document {
-        /// File to send.
-        media: InputFile,
-
-        /// Thumbnail of the file sent; can be ignored if thumbnail generation
-        /// for the file is supported server-side. The thumbnail should be in
-        /// JPEG format and less than 200 kB in size. A thumbnail‘s width and
-        /// height should not exceed 320. Ignored if the file is not uploaded
-        /// using multipart/form-data.
-        thumb: Option<InputFile>,
-
-        /// Caption of the document to be sent, 0-1024 charactersю
-        caption: Option<String>,
-
-        /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
-        /// italic, fixed-width text or inline URLs] in the media caption.
-        ///
-        /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
-        /// [HTML]: https://core.telegram.org/bots/api#html-style
-        /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
-        parse_mode: Option<ParseMode>,
-    },
+    /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
+    /// [HTML]: https://core.telegram.org/bots/api#html-style
+    /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
+    pub parse_mode: Option<ParseMode>,
 }
 
 impl InputMedia {
     pub fn media(&self) -> &InputFile {
         match self {
-            InputMedia::Photo { media, .. }
-            | InputMedia::Document { media, .. }
-            | InputMedia::Audio { media, .. }
-            | InputMedia::Animation { media, .. }
-            | InputMedia::Video { media, .. } => media,
+            InputMedia::Photo(InputMediaPhoto { media, .. })
+            | InputMedia::Document(InputMediaDocument { media, .. })
+            | InputMedia::Audio(InputMediaAudio { media, .. })
+            | InputMedia::Animation(InputMediaAnimation { media, .. })
+            | InputMedia::Video(InputMediaVideo { media, .. }) => media,
         }
     }
 }
@@ -181,11 +196,11 @@ impl InputMedia {
 impl From<InputMedia> for InputFile {
     fn from(media: InputMedia) -> InputFile {
         match media {
-            InputMedia::Photo { media, .. }
-            | InputMedia::Document { media, .. }
-            | InputMedia::Audio { media, .. }
-            | InputMedia::Animation { media, .. }
-            | InputMedia::Video { media, .. } => media,
+            InputMedia::Photo(InputMediaPhoto { media, .. })
+            | InputMedia::Document(InputMediaDocument { media, .. })
+            | InputMedia::Audio(InputMediaAudio { media, .. })
+            | InputMedia::Animation(InputMediaAnimation { media, .. })
+            | InputMedia::Video(InputMediaVideo { media, .. }) => media,
         }
     }
 }
@@ -197,11 +212,11 @@ mod tests {
     #[test]
     fn photo_serialize() {
         let expected_json = r#"{"type":"photo","media":"123456"}"#;
-        let photo = InputMedia::Photo {
+        let photo = InputMedia::Photo(InputMediaPhoto {
             media: InputFile::FileId(String::from("123456")),
             caption: None,
             parse_mode: None,
-        };
+        });
 
         let actual_json = serde_json::to_string(&photo).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -210,7 +225,7 @@ mod tests {
     #[test]
     fn video_serialize() {
         let expected_json = r#"{"type":"video","media":"123456"}"#;
-        let video = InputMedia::Video {
+        let video = InputMedia::Video(InputMediaVideo {
             media: InputFile::FileId(String::from("123456")),
             thumb: None,
             caption: None,
@@ -219,7 +234,7 @@ mod tests {
             height: None,
             duration: None,
             supports_streaming: None,
-        };
+        });
 
         let actual_json = serde_json::to_string(&video).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -228,7 +243,7 @@ mod tests {
     #[test]
     fn animation_serialize() {
         let expected_json = r#"{"type":"animation","media":"123456"}"#;
-        let video = InputMedia::Animation {
+        let video = InputMedia::Animation(InputMediaAnimation {
             media: InputFile::FileId(String::from("123456")),
             thumb: None,
             caption: None,
@@ -236,7 +251,7 @@ mod tests {
             width: None,
             height: None,
             duration: None,
-        };
+        });
 
         let actual_json = serde_json::to_string(&video).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -245,7 +260,7 @@ mod tests {
     #[test]
     fn audio_serialize() {
         let expected_json = r#"{"type":"audio","media":"123456"}"#;
-        let video = InputMedia::Audio {
+        let video = InputMedia::Audio(InputMediaAudio {
             media: InputFile::FileId(String::from("123456")),
             thumb: None,
             caption: None,
@@ -253,7 +268,7 @@ mod tests {
             duration: None,
             performer: None,
             title: None,
-        };
+        });
 
         let actual_json = serde_json::to_string(&video).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -262,12 +277,12 @@ mod tests {
     #[test]
     fn document_serialize() {
         let expected_json = r#"{"type":"document","media":"123456"}"#;
-        let video = InputMedia::Document {
+        let video = InputMedia::Document(InputMediaDocument {
             media: InputFile::FileId(String::from("123456")),
             thumb: None,
             caption: None,
             parse_mode: None,
-        };
+        });
 
         let actual_json = serde_json::to_string(&video).unwrap();
         assert_eq!(expected_json, actual_json);
diff --git a/src/types/input_message_content.rs b/src/types/input_message_content.rs
index a9563211..79444de4 100644
--- a/src/types/input_message_content.rs
+++ b/src/types/input_message_content.rs
@@ -2,88 +2,100 @@ use serde::{Deserialize, Serialize};
 
 use crate::types::ParseMode;
 
-#[serde_with_macros::skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
-#[serde(untagged)]
 /// This object represents the content of a message to be sent as a result of an
 /// inline query.
 ///
 /// [The official docs](https://core.telegram.org/bots/api#inputmessagecontent).
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
 pub enum InputMessageContent {
-    /// Represents the content of a text message to be sent as the result of an
-    /// inline query.
-    Text {
-        /// Text of the message to be sent, 1-4096 characters.
-        message_text: String,
+    Text(InputMessageContentText),
+    Location(InputMessageContentLocation),
+    Venue(InputMessageContentVenue),
+    Contact(InputMessageContentContact),
+}
+/// Represents the content of a text message to be sent as the result of an
+/// inline query.
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct InputMessageContentText {
+    /// Text of the message to be sent, 1-4096 characters.
+    pub message_text: String,
 
-        /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
-        /// italic, fixed-width text or inline URLs] in the media caption.
-        ///
-        /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
-        /// [HTML]: https://core.telegram.org/bots/api#html-style
-        /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
-        parse_mode: Option<ParseMode>,
+    /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold,
+    /// italic, fixed-width text or inline URLs] in the media caption.
+    ///
+    /// [Markdown]: https://core.telegram.org/bots/api#markdown-style
+    /// [HTML]: https://core.telegram.org/bots/api#html-style
+    /// [bold, italic, fixed-width text or inline URLs]: https://core.telegram.org/bots/api#formatting-options
+    pub parse_mode: Option<ParseMode>,
 
-        /// Disables link previews for links in the sent message.
-        disable_web_page_preview: Option<bool>,
-    },
+    /// Disables link previews for links in the sent message.
+    pub disable_web_page_preview: Option<bool>,
+}
 
-    /// Represents the content of a location message to be sent as the result
-    /// of an inline query.
-    Location {
-        /// Latitude of the location in degrees.
-        latitude: f64,
+/// Represents the content of a location message to be sent as the result of an
+/// inline query.
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct InputMessageContentLocation {
+    /// Latitude of the location in degrees.
+    pub latitude: f64,
 
-        /// Longitude of the location in degrees.
-        longitude: f64,
+    /// Longitude of the location in degrees.
+    pub longitude: f64,
 
-        /// Period in seconds for which the location can be updated, should be
-        /// between 60 and 86400.
-        live_period: Option<u32>,
-    },
+    /// Period in seconds for which the location can be updated, should be
+    /// between 60 and 86400.
+    pub live_period: Option<u32>,
+}
 
-    /// Represents the content of a venue message to be sent as the result of
-    /// an inline query.
-    Venue {
-        /// Latitude of the venue in degrees.
-        latitude: f64,
+/// Represents the content of a venue message to be sent as the result of
+/// an inline query.
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 
-        /// Longitude of the venue in degrees.
-        longitude: f64,
+pub struct InputMessageContentVenue {
+    /// Latitude of the venue in degrees.
+    pub latitude: f64,
 
-        /// Name of the venue.
-        title: String,
+    /// Longitude of the venue in degrees.
+    pub longitude: f64,
 
-        /// Address of the venue.
-        address: String,
+    /// Name of the venue.
+    pub title: String,
 
-        /// Foursquare identifier of the venue, if known.
-        foursquare_id: Option<String>,
+    /// Address of the venue.
+    pub address: String,
 
-        /// Foursquare type of the venue, if known. (For example,
-        /// `arts_entertainment/default`, `arts_entertainment/aquarium`
-        /// or `food/icecream`.)
-        foursquare_type: Option<String>,
-    },
+    /// Foursquare identifier of the venue, if known.
+    pub foursquare_id: Option<String>,
 
-    /// Represents the content of a contact message to be sent as the result of
-    /// an inline query.
-    Contact {
-        /// Contact's phone number.
-        phone_number: String,
+    /// Foursquare type of the venue, if known. (For example,
+    /// `arts_entertainment/default`, `arts_entertainment/aquarium`
+    /// or `food/icecream`.)
+    pub foursquare_type: Option<String>,
+}
 
-        /// Contact's first name.
-        first_name: String,
+/// Represents the content of a contact message to be sent as the result of
+/// an inline query.
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct InputMessageContentContact {
+    /// Contact's phone number.
+    pub phone_number: String,
 
-        /// Contact's last name.
-        last_name: Option<String>,
+    /// Contact's first name.
+    pub first_name: String,
 
-        /// Additional data about the contact in the form of a [vCard], 0-2048
-        /// bytes.
-        ///
-        /// [vCard]: https://en.wikipedia.org/wiki/VCard
-        vcard: Option<String>,
-    },
+    /// Contact's last name.
+    pub last_name: Option<String>,
+
+    /// Additional data about the contact in the form of a [vCard], 0-2048
+    /// bytes.
+    ///
+    /// [vCard]: https://en.wikipedia.org/wiki/VCard
+    pub vcard: Option<String>,
 }
 
 #[cfg(test)]
@@ -93,11 +105,11 @@ mod tests {
     #[test]
     fn text_serialize() {
         let expected_json = r#"{"message_text":"text"}"#;
-        let text_content = InputMessageContent::Text {
+        let text_content = InputMessageContent::Text(InputMessageContentText {
             message_text: String::from("text"),
             parse_mode: None,
             disable_web_page_preview: None,
-        };
+        });
 
         let actual_json = serde_json::to_string(&text_content).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -106,11 +118,12 @@ mod tests {
     #[test]
     fn location_serialize() {
         let expected_json = r#"{"latitude":59.08,"longitude":38.4326}"#;
-        let location_content = InputMessageContent::Location {
-            latitude: 59.08,
-            longitude: 38.4326,
-            live_period: None,
-        };
+        let location_content =
+            InputMessageContent::Location(InputMessageContentLocation {
+                latitude: 59.08,
+                longitude: 38.4326,
+                live_period: None,
+            });
 
         let actual_json = serde_json::to_string(&location_content).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -119,14 +132,15 @@ mod tests {
     #[test]
     fn venue_serialize() {
         let expected_json = r#"{"latitude":59.08,"longitude":38.4326,"title":"some title","address":"some address"}"#;
-        let venue_content = InputMessageContent::Venue {
-            latitude: 59.08,
-            longitude: 38.4326,
-            title: String::from("some title"),
-            address: String::from("some address"),
-            foursquare_id: None,
-            foursquare_type: None,
-        };
+        let venue_content =
+            InputMessageContent::Venue(InputMessageContentVenue {
+                latitude: 59.08,
+                longitude: 38.4326,
+                title: String::from("some title"),
+                address: String::from("some address"),
+                foursquare_id: None,
+                foursquare_type: None,
+            });
 
         let actual_json = serde_json::to_string(&venue_content).unwrap();
         assert_eq!(expected_json, actual_json);
@@ -136,12 +150,13 @@ mod tests {
     fn contact_serialize() {
         let expected_json =
             r#"{"phone_number":"+3800000000","first_name":"jhon"}"#;
-        let contact_content = InputMessageContent::Contact {
-            phone_number: String::from("+3800000000"),
-            first_name: String::from("jhon"),
-            last_name: None,
-            vcard: None,
-        };
+        let contact_content =
+            InputMessageContent::Contact(InputMessageContentContact {
+                phone_number: String::from("+3800000000"),
+                first_name: String::from("jhon"),
+                last_name: None,
+                vcard: None,
+            });
 
         let actual_json = serde_json::to_string(&contact_content).unwrap();
         assert_eq!(expected_json, actual_json);
diff --git a/src/types/message.rs b/src/types/message.rs
index 2e5cffe7..4e7722d9 100644
--- a/src/types/message.rs
+++ b/src/types/message.rs
@@ -4,9 +4,11 @@ use serde::{Deserialize, Serialize};
 
 use crate::types::{
     chat::{ChatKind, NonPrivateChatKind},
-    Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup,
-    Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker,
-    SuccessfulPayment, True, User, Venue, Video, VideoNote, Voice,
+    Animation, Audio, Chat, ChatNonPrivate, Contact, Document, Game,
+    InlineKeyboardMarkup, Invoice, Location, MessageEntity,
+    NonPrivateChatChannel, NonPrivateChatSupergroup, PassportData, PhotoSize,
+    Poll, Sticker, SuccessfulPayment, True, User, Venue, Video, VideoNote,
+    Voice,
 };
 
 /// This object represents a message.
@@ -28,120 +30,165 @@ pub struct Message {
     pub kind: MessageKind,
 }
 
-#[serde_with_macros::skip_serializing_none]
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(untagged)]
 pub enum MessageKind {
-    Common {
-        /// Sender, empty for messages sent to channels.
-        from: Option<User>,
+    Common(MessageCommon),
+    NewChatMembers(MessageNewChatMembers),
+    LeftChatMember(MessageLeftChatMember),
+    NewChatTitle(MessageNewChatTitle),
+    NewChatPhoto(MessageNewChatPhoto),
+    DeleteChatPhoto(MessageDeleteChatPhoto),
+    GroupChatCreated(MessageGroupChatCreated),
+    SupergroupChatCreated(MessageSupergroupChatCreated),
+    ChannelChatCreated(MessageChannelChatCreated),
+    Migrate(MessageMigrate),
+    Pinned(MessagePinned),
+    Invoice(MessageInvoice),
+    SuccessfulPayment(MessageSuccessfulPayment),
+    ConnectedWebsite(MessageConnectedWebsite),
+    PassportData(MessagePassportData),
+}
 
-        #[serde(flatten)]
-        forward_kind: ForwardKind,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageCommon {
+    /// Sender, empty for messages sent to channels.
+    pub from: Option<User>,
 
-        /// Date the message was last edited in Unix time.
-        edit_date: Option<i32>,
+    #[serde(flatten)]
+    pub forward_kind: ForwardKind,
 
-        #[serde(flatten)]
-        media_kind: MediaKind,
+    /// Date the message was last edited in Unix time.
+    pub edit_date: Option<i32>,
 
-        /// Inline keyboard attached to the message. `login_url` buttons are
-        /// represented as ordinary `url` buttons.
-        reply_markup: Option<InlineKeyboardMarkup>,
-    },
-    NewChatMembers {
-        /// New members that were added to the group or supergroup and
-        /// information about them (the bot itself may be one of these
-        /// members).
-        new_chat_members: Vec<User>,
-    },
-    LeftChatMember {
-        /// A member was removed from the group, information about them (this
-        /// member may be the bot itself).
-        left_chat_member: User,
-    },
-    NewChatTitle {
-        /// A chat title was changed to this value.
-        new_chat_title: String,
-    },
-    NewChatPhoto {
-        /// A chat photo was change to this value.
-        new_chat_photo: Vec<PhotoSize>,
-    },
-    DeleteChatPhoto {
-        /// Service message: the chat photo was deleted.
-        delete_chat_photo: True,
-    },
-    GroupChatCreated {
-        /// Service message: the group has been created.
-        group_chat_created: True,
-    },
-    SupergroupChatCreated {
-        /// Service message: the supergroup has been created. This field can‘t
-        /// be received in a message coming through updates, because bot can’t
-        /// be a member of a supergroup when it is created. It can only be
-        /// found in `reply_to_message` if someone replies to a very first
-        /// message in a directly created supergroup.
-        supergroup_chat_created: True,
-    },
-    ChannelChatCreated {
-        /// Service message: the channel has been created. This field can‘t be
-        /// received in a message coming through updates, because bot can’t be
-        /// a member of a channel when it is created. It can only be found in
-        /// `reply_to_message` if someone replies to a very first message in a
-        /// channel.
-        channel_chat_created: True,
-    },
-    Migrate {
-        /// 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.
-        migrate_to_chat_id: i64,
+    #[serde(flatten)]
+    pub media_kind: MediaKind,
 
-        /// 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.
-        migrate_from_chat_id: i64,
-    },
-    Pinned {
-        /// 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")]
-        pinned: Box<Message>,
-    },
-    Invoice {
-        /// Message is an invoice for a [payment], information about the
-        /// invoice. [More about payments »].
-        ///
-        /// [payment]: https://core.telegram.org/bots/api#payments
-        /// [More about payments »]: https://core.telegram.org/bots/api#payments
-        invoice: Invoice,
-    },
-    SuccessfulPayment {
-        /// Message is a service message about a successful payment,
-        /// information about the payment. [More about payments »].
-        ///
-        /// [More about payments »]: https://core.telegram.org/bots/api#payments
-        successful_payment: SuccessfulPayment,
-    },
-    ConnectedWebsite {
-        /// The domain name of the website on which the user has logged in.
-        /// [More about Telegram Login »].
-        ///
-        /// [More about Telegram Login »]: https://core.telegram.org/widgets/login
-        connected_website: String,
-    },
-    PassportData {
-        /// Telegram Passport data.
-        passport_data: PassportData,
-    },
+    /// Inline keyboard attached to the message. `login_url` buttons are
+    /// represented as ordinary `url` buttons.
+    pub reply_markup: Option<InlineKeyboardMarkup>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageNewChatMembers {
+    /// New members that were added to the group or supergroup and
+    /// information about them (the bot itself may be one of these
+    /// members).
+    pub new_chat_members: Vec<User>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageLeftChatMember {
+    /// A member was removed from the group, information about them (this
+    /// member may be the bot itself).
+    pub left_chat_member: User,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageNewChatTitle {
+    /// A chat title was changed to this value.
+    pub new_chat_title: String,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageNewChatPhoto {
+    /// A chat photo was change to this value.
+    pub new_chat_photo: Vec<PhotoSize>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageDeleteChatPhoto {
+    /// Service message: the chat photo was deleted.
+    pub delete_chat_photo: True,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageGroupChatCreated {
+    /// Service message: the group has been created.
+    pub group_chat_created: True,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageSupergroupChatCreated {
+    /// Service message: the supergroup has been created. This field can‘t
+    /// be received in a message coming through updates, because bot can’t
+    /// be a member of a supergroup when it is created. It can only be
+    /// found in `reply_to_message` if someone replies to a very first
+    /// message in a directly created supergroup.
+    pub supergroup_chat_created: True,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageChannelChatCreated {
+    /// Service message: the channel has been created. This field can‘t be
+    /// received in a message coming through updates, because bot can’t be
+    /// a member of a channel when it is created. It can only be found in
+    /// `reply_to_message` if someone replies to a very first message in a
+    /// channel.
+    pub channel_chat_created: True,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageMigrate {
+    /// 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,
+
+    /// 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,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessagePinned {
+    /// 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 pinned: Box<Message>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageInvoice {
+    /// Message is an invoice for a [payment], information about the
+    /// invoice. [More about payments »].
+    ///
+    /// [payment]: https://core.telegram.org/bots/api#payments
+    /// [More about payments »]: https://core.telegram.org/bots/api#payments
+    pub invoice: Invoice,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageSuccessfulPayment {
+    /// Message is a service message about a successful payment,
+    /// information about the payment. [More about payments »].
+    ///
+    /// [More about payments »]: https://core.telegram.org/bots/api#payments
+    pub successful_payment: SuccessfulPayment,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessageConnectedWebsite {
+    /// The domain name of the website on which the user has logged in.
+    /// [More about Telegram Login »].
+    ///
+    /// [More about Telegram Login »]: https://core.telegram.org/widgets/login
+    pub connected_website: String,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MessagePassportData {
+    /// Telegram Passport data.
+    pub passport_data: PassportData,
 }
 
 #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
@@ -155,160 +202,220 @@ pub enum ForwardedFrom {
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(untagged)]
 pub enum ForwardKind {
-    ChannelForward {
-        #[serde(rename = "forward_date")]
-        date: i32,
-        #[serde(rename = "forward_from_chat")]
-        chat: Chat,
-        #[serde(rename = "forward_from_message_id")]
-        message_id: i32,
-        #[serde(rename = "forward_signature")]
-        signature: Option<String>,
-    },
-    NonChannelForward {
-        #[serde(rename = "forward_date")]
-        date: i32,
-        #[serde(flatten)]
-        from: ForwardedFrom,
-    },
-    Origin {
-        reply_to_message: Option<Box<Message>>,
-    },
+    Channel(ForwardChannel),
+    NonChannel(ForwardNonChannel),
+    Origin(ForwardOrigin),
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct ForwardChannel {
+    #[serde(rename = "forward_date")]
+    pub date: i32,
+
+    #[serde(rename = "forward_from_chat")]
+    pub chat: Chat,
+
+    #[serde(rename = "forward_from_message_id")]
+    pub message_id: i32,
+
+    #[serde(rename = "forward_signature")]
+    pub signature: Option<String>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct ForwardNonChannel {
+    #[serde(rename = "forward_date")]
+    pub date: i32,
+
+    #[serde(flatten)]
+    pub from: ForwardedFrom,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct ForwardOrigin {
+    pub reply_to_message: Option<Box<Message>>,
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(untagged)]
 pub enum MediaKind {
-    Animation {
-        /// Message is an animation, information about the animation. For
-        /// backward compatibility, when this field is set, the document field
-        /// will also be set.
-        animation: Animation,
+    Animation(MediaAnimation),
+    Audio(MediaAudio),
+    Contact(MediaContact),
+    Document(MediaDocument),
+    Game(MediaGame),
+    Location(MediaLocation),
+    Photo(MediaPhoto),
+    Poll(MediaPoll),
+    Sticker(MediaSticker),
+    Text(MediaText),
+    Video(MediaVideo),
+    VideoNote(MediaVideoNote),
+    Voice(MediaVoice),
+    Venue(MediaVenue),
+}
 
-        #[doc(hidden)]
-        /// "For backward compatibility" (c) Telegram Docs.
-        #[serde(skip)]
-        document: (),
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaAnimation {
+    /// 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: Animation,
 
-        /// Caption for the animation, 0-1024 characters.
-        caption: Option<String>,
+    #[doc(hidden)]
+    /// "For backward compatibility" (c) Telegram Docs.
+    #[serde(skip)]
+    pub document: (),
 
-        /// For messages with a caption, special entities like usernames, URLs,
-        /// bot commands, etc. that appear in the caption.
-        #[serde(default = "Vec::new")]
-        caption_entities: Vec<MessageEntity>,
-    },
-    Audio {
-        /// Message is an audio file, information about the file.
-        audio: Audio,
+    /// Caption for the animation, 0-1024 characters.
+    pub caption: Option<String>,
 
-        /// Caption for the audio, 0-1024 characters.
-        caption: Option<String>,
+    /// For messages with a caption, special entities like usernames, URLs,
+    /// bot commands, etc. that appear in the caption.
+    #[serde(default = "Vec::new")]
+    pub caption_entities: Vec<MessageEntity>,
+}
 
-        /// For messages with a caption, special entities like usernames, URLs,
-        /// bot commands, etc. that appear in the caption.
-        #[serde(default = "Vec::new")]
-        caption_entities: Vec<MessageEntity>,
-    },
-    Contact {
-        /// Message is a shared contact, information about the contact.
-        contact: Contact,
-    },
-    Document {
-        /// Message is a general file, information about the file.
-        document: Document,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaAudio {
+    /// Message is an audio file, information about the file.
+    pub audio: Audio,
 
-        /// Caption for the document, 0-1024 characters.
-        caption: Option<String>,
+    /// Caption for the audio, 0-1024 characters.
+    pub caption: Option<String>,
 
-        /// For messages with a caption, special entities like usernames, URLs,
-        /// bot commands, etc. that appear in the caption.
-        #[serde(default = "Vec::new")]
-        caption_entities: Vec<MessageEntity>,
-    },
-    Game {
-        /// Message is a game, information about the game. [More
-        /// about games »].
-        ///
-        /// [More about games »]: https://core.telegram.org/bots/api#games
-        game: Game,
-    },
-    Location {
-        /// Message is a shared location, information about the location.
-        location: Location,
-    },
-    Photo {
-        /// Message is a photo, available sizes of the photo.
-        photo: Vec<PhotoSize>,
+    /// For messages with a caption, special entities like usernames, URLs,
+    /// bot commands, etc. that appear in the caption.
+    #[serde(default = "Vec::new")]
+    pub caption_entities: Vec<MessageEntity>,
+}
 
-        /// Caption for the photo, 0-1024 characters.
-        caption: Option<String>,
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaContact {
+    /// Message is a shared contact, information about the contact.
+    contact: Contact,
+}
 
-        /// For messages with a caption, special entities like usernames, URLs,
-        /// bot commands, etc. that appear in the caption.
-        #[serde(default = "Vec::new")]
-        caption_entities: Vec<MessageEntity>,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaDocument {
+    /// Message is a general file, information about the file.
+    pub document: Document,
 
-        /// The unique identifier of a media message group this message belongs
-        /// to.
-        media_group_id: Option<String>,
-    },
-    Poll {
-        /// Message is a native poll, information about the poll.
-        poll: Poll,
-    },
-    Sticker {
-        /// Message is a sticker, information about the sticker.
-        sticker: Sticker,
-    },
-    Text {
-        /// For text messages, the actual UTF-8 text of the message, 0-4096
-        /// characters.
-        text: String,
+    /// Caption for the document, 0-1024 characters.
+    pub caption: Option<String>,
 
-        /// For text messages, special entities like usernames, URLs, bot
-        /// commands, etc. that appear in the text.
-        #[serde(default = "Vec::new")]
-        entities: Vec<MessageEntity>,
-    },
-    Video {
-        /// Message is a video, information about the video.
-        video: Video,
+    /// For messages with a caption, special entities like usernames, URLs,
+    /// bot commands, etc. that appear in the caption.
+    #[serde(default = "Vec::new")]
+    pub caption_entities: Vec<MessageEntity>,
+}
 
-        /// Caption for the video, 0-1024 characters.
-        caption: Option<String>,
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaGame {
+    /// Message is a game, information about the game. [More
+    /// about games »].
+    ///
+    /// [More about games »]: https://core.telegram.org/bots/api#games
+    pub game: Game,
+}
 
-        /// For messages with a caption, special entities like usernames, URLs,
-        /// bot commands, etc. that appear in the caption.
-        #[serde(default = "Vec::new")]
-        caption_entities: Vec<MessageEntity>,
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaLocation {
+    /// Message is a shared location, information about the location.
+    pub location: Location,
+}
 
-        /// The unique identifier of a media message group this message belongs
-        /// to.
-        media_group_id: Option<String>,
-    },
-    VideoNote {
-        /// Message is a [video note], information about the video message.
-        ///
-        /// [video note]: https://telegram.org/blog/video-messages-and-telescope
-        video_note: VideoNote,
-    },
-    Voice {
-        /// Message is a voice message, information about the file.
-        voice: Voice,
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaPhoto {
+    /// Message is a photo, available sizes of the photo.
+    pub photo: Vec<PhotoSize>,
 
-        /// Caption for the voice, 0-1024 characters.
-        caption: Option<String>,
+    /// Caption for the photo, 0-1024 characters.
+    pub caption: Option<String>,
 
-        /// For messages with a caption, special entities like usernames, URLs,
-        /// bot commands, etc. that appear in the caption.
-        #[serde(default = "Vec::new")]
-        caption_entities: Vec<MessageEntity>,
-    },
-    Venue {
-        /// Message is a venue, information about the venue.
-        venue: Venue,
-    },
+    /// For messages with a caption, special entities like usernames, URLs,
+    /// bot commands, etc. that appear in the caption.
+    #[serde(default = "Vec::new")]
+    pub caption_entities: Vec<MessageEntity>,
+
+    /// The unique identifier of a media message group this message belongs
+    /// to.
+    pub media_group_id: Option<String>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaPoll {
+    /// Message is a native poll, information about the poll.
+    pub poll: Poll,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaSticker {
+    /// Message is a sticker, information about the sticker.
+    pub sticker: Sticker,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaText {
+    /// For text messages, the actual UTF-8 text of the message, 0-4096
+    /// characters.
+    pub text: String,
+
+    /// For text messages, special entities like usernames, URLs, bot
+    /// commands, etc. that appear in the text.
+    #[serde(default = "Vec::new")]
+    pub entities: Vec<MessageEntity>,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaVideo {
+    /// Message is a video, information about the video.
+    pub video: Video,
+
+    /// Caption for the video, 0-1024 characters.
+    pub caption: Option<String>,
+
+    /// For messages with a caption, special entities like usernames, URLs,
+    /// bot commands, etc. that appear in the caption.
+    #[serde(default = "Vec::new")]
+    pub caption_entities: Vec<MessageEntity>,
+
+    /// The unique identifier of a media message group this message belongs
+    /// to.
+    pub media_group_id: Option<String>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaVideoNote {
+    /// Message is a [video note], information about the video message.
+    ///
+    /// [video note]: https://telegram.org/blog/video-messages-and-telescope
+    pub video_note: VideoNote,
+}
+
+#[serde_with_macros::skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaVoice {
+    /// Message is a voice message, information about the file.
+    pub voice: Voice,
+
+    /// Caption for the voice, 0-1024 characters.
+    pub caption: Option<String>,
+
+    /// For messages with a caption, special entities like usernames, URLs,
+    /// bot commands, etc. that appear in the caption.
+    #[serde(default = "Vec::new")]
+    pub caption_entities: Vec<MessageEntity>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct MediaVenue {
+    /// Message is a venue, information about the venue.
+    pub venue: Venue,
 }
 
 mod getters {
@@ -317,11 +424,7 @@ mod getters {
     use crate::types::{
         self,
         message::{
-            ForwardKind::{ChannelForward, NonChannelForward, Origin},
-            MediaKind::{
-                Animation, Audio, Contact, Document, Game, Location, Photo,
-                Poll, Sticker, Text, Venue, Video, VideoNote, Voice,
-            },
+            ForwardKind::NonChannel,
             MessageKind::{
                 ChannelChatCreated, Common, ConnectedWebsite, DeleteChatPhoto,
                 GroupChatCreated, Invoice, LeftChatMember, Migrate,
@@ -329,7 +432,17 @@ mod getters {
                 Pinned, SuccessfulPayment, SupergroupChatCreated,
             },
         },
-        Chat, ForwardedFrom, Message, MessageEntity, PhotoSize, True, User,
+        Chat, 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, MessageEntity,
+        MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember,
+        MessageMigrate, MessageNewChatMembers, MessageNewChatPhoto,
+        MessageNewChatTitle, MessagePassportData, MessagePinned,
+        MessageSuccessfulPayment, MessageSupergroupChatCreated, PhotoSize,
+        True, User,
     };
 
     /// Getters for [Message] fields from [telegram docs].
@@ -340,7 +453,7 @@ mod getters {
         /// NOTE: this is getter for both `from` and `author_signature`
         pub fn from(&self) -> Option<&User> {
             match &self.kind {
-                Common { from, .. } => from.as_ref(),
+                Common(MessageCommon { from, .. }) => from.as_ref(),
                 _ => None,
             }
         }
@@ -353,84 +466,105 @@ mod getters {
         /// `forward_sender_name`
         pub fn forward_from(&self) -> Option<&ForwardedFrom> {
             match &self.kind {
-                Common {
-                    forward_kind: NonChannelForward { from, .. }, ..
-                } => Some(from),
+                Common(MessageCommon {
+                    forward_kind: NonChannel(ForwardNonChannel { from, .. }),
+                    ..
+                }) => Some(from),
                 _ => None,
             }
         }
 
         pub fn forward_from_chat(&self) -> Option<&Chat> {
             match &self.kind {
-                Common {
-                    forward_kind: ChannelForward { chat, .. }, ..
-                } => Some(chat),
+                Common(MessageCommon {
+                    forward_kind:
+                        ForwardKind::Channel(ForwardChannel { chat, .. }),
+                    ..
+                }) => Some(chat),
                 _ => None,
             }
         }
 
         pub fn forward_from_message_id(&self) -> Option<&i32> {
             match &self.kind {
-                Common {
-                    forward_kind: ChannelForward { message_id, .. },
+                Common(MessageCommon {
+                    forward_kind:
+                        ForwardKind::Channel(ForwardChannel { message_id, .. }),
                     ..
-                } => Some(message_id),
+                }) => Some(message_id),
                 _ => None,
             }
         }
 
         pub fn forward_signature(&self) -> Option<&str> {
             match &self.kind {
-                Common {
-                    forward_kind: ChannelForward { signature, .. },
+                Common(MessageCommon {
+                    forward_kind:
+                        ForwardKind::Channel(ForwardChannel { signature, .. }),
                     ..
-                } => signature.as_ref().map(Deref::deref),
+                }) => signature.as_ref().map(Deref::deref),
                 _ => None,
             }
         }
 
         pub fn forward_date(&self) -> Option<&i32> {
             match &self.kind {
-                Common {
-                    forward_kind: ChannelForward { date, .. }, ..
-                }
-                | Common {
-                    forward_kind: NonChannelForward { date, .. }, ..
-                } => Some(date),
+                Common(MessageCommon {
+                    forward_kind:
+                        ForwardKind::Channel(ForwardChannel { date, .. }),
+                    ..
+                })
+                | Common(MessageCommon {
+                    forward_kind:
+                        ForwardKind::NonChannel(ForwardNonChannel { date, .. }),
+                    ..
+                }) => Some(date),
                 _ => None,
             }
         }
 
         pub fn reply_to_message(&self) -> Option<&Message> {
             match &self.kind {
-                Common {
-                    forward_kind: Origin { reply_to_message, .. },
+                Common(MessageCommon {
+                    forward_kind:
+                        ForwardKind::Origin(ForwardOrigin {
+                            reply_to_message, ..
+                        }),
                     ..
-                } => reply_to_message.as_ref().map(Deref::deref),
+                }) => reply_to_message.as_ref().map(Deref::deref),
                 _ => None,
             }
         }
 
         pub fn edit_date(&self) -> Option<&i32> {
             match &self.kind {
-                Common { edit_date, .. } => edit_date.as_ref(),
+                Common(MessageCommon { edit_date, .. }) => edit_date.as_ref(),
                 _ => None,
             }
         }
 
         pub fn media_group_id(&self) -> Option<&str> {
             match &self.kind {
-                Common { media_kind: Video { media_group_id, .. }, .. }
-                | Common { media_kind: Photo { media_group_id, .. }, .. } => {
-                    media_group_id.as_ref().map(Deref::deref)
-                }
+                Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Video(MediaVideo { media_group_id, .. }),
+                    ..
+                })
+                | Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Photo(MediaPhoto { media_group_id, .. }),
+                    ..
+                }) => media_group_id.as_ref().map(Deref::deref),
                 _ => None,
             }
         }
 
         pub fn text(&self) -> Option<&str> {
             match &self.kind {
-                Common { media_kind: Text { text, .. }, .. } => Some(text),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Text(MediaText { text, .. }),
+                    ..
+                }) => Some(text),
                 _ => None,
             }
         }
@@ -441,119 +575,159 @@ mod getters {
 
         pub fn entities(&self) -> Option<&[MessageEntity]> {
             match &self.kind {
-                Common { media_kind: Text { entities, .. }, .. } => {
-                    Some(entities)
-                }
+                Common(MessageCommon {
+                    media_kind: MediaKind::Text(MediaText { entities, .. }),
+                    ..
+                }) => Some(entities),
                 _ => None,
             }
         }
 
         pub fn caption_entities(&self) -> Option<&[MessageEntity]> {
             match &self.kind {
-                Common {
-                    media_kind: Animation { caption_entities, .. },
+                Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Animation(MediaAnimation {
+                            caption_entities,
+                            ..
+                        }),
                     ..
-                }
-                | Common {
-                    media_kind: Audio { caption_entities, .. }, ..
-                }
-                | Common {
-                    media_kind: Document { caption_entities, .. },
+                })
+                | Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Audio(MediaAudio { caption_entities, .. }),
                     ..
-                }
-                | Common {
-                    media_kind: Photo { caption_entities, .. }, ..
-                }
-                | Common {
-                    media_kind: Video { caption_entities, .. }, ..
-                }
-                | Common {
-                    media_kind: Voice { caption_entities, .. }, ..
-                } => Some(caption_entities),
+                })
+                | Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Document(MediaDocument {
+                            caption_entities, ..
+                        }),
+                    ..
+                })
+                | Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Photo(MediaPhoto { caption_entities, .. }),
+                    ..
+                })
+                | Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Video(MediaVideo { caption_entities, .. }),
+                    ..
+                })
+                | Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Voice(MediaVoice { caption_entities, .. }),
+                    ..
+                }) => Some(caption_entities),
                 _ => None,
             }
         }
 
         pub fn audio(&self) -> Option<&types::Audio> {
             match &self.kind {
-                Common { media_kind: Audio { audio, .. }, .. } => Some(audio),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Audio(MediaAudio { audio, .. }),
+                    ..
+                }) => Some(audio),
                 _ => None,
             }
         }
 
         pub fn document(&self) -> Option<&types::Document> {
             match &self.kind {
-                Common { media_kind: Document { document, .. }, .. } => {
-                    Some(document)
-                }
+                Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Document(MediaDocument { document, .. }),
+                    ..
+                }) => Some(document),
                 _ => None,
             }
         }
 
         pub fn animation(&self) -> Option<&types::Animation> {
             match &self.kind {
-                Common { media_kind: Animation { animation, .. }, .. } => {
-                    Some(animation)
-                }
+                Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Animation(MediaAnimation { animation, .. }),
+                    ..
+                }) => Some(animation),
                 _ => None,
             }
         }
 
         pub fn game(&self) -> Option<&types::Game> {
             match &self.kind {
-                Common { media_kind: Game { game, .. }, .. } => Some(game),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Game(MediaGame { game, .. }),
+                    ..
+                }) => Some(game),
                 _ => None,
             }
         }
 
         pub fn photo(&self) -> Option<&[PhotoSize]> {
             match &self.kind {
-                Common { media_kind: Photo { photo, .. }, .. } => Some(photo),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Photo(MediaPhoto { photo, .. }),
+                    ..
+                }) => Some(photo),
                 _ => None,
             }
         }
 
         pub fn sticker(&self) -> Option<&types::Sticker> {
             match &self.kind {
-                Common { media_kind: Sticker { sticker, .. }, .. } => {
-                    Some(sticker)
-                }
+                Common(MessageCommon {
+                    media_kind: MediaKind::Sticker(MediaSticker { sticker, .. }),
+                    ..
+                }) => Some(sticker),
                 _ => None,
             }
         }
 
         pub fn video(&self) -> Option<&types::Video> {
             match &self.kind {
-                Common { media_kind: Video { video, .. }, .. } => Some(video),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Video(MediaVideo { video, .. }),
+                    ..
+                }) => Some(video),
                 _ => None,
             }
         }
 
         pub fn voice(&self) -> Option<&types::Voice> {
             match &self.kind {
-                Common { media_kind: Voice { voice, .. }, .. } => Some(voice),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Voice(MediaVoice { voice, .. }),
+                    ..
+                }) => Some(voice),
                 _ => None,
             }
         }
 
         pub fn video_note(&self) -> Option<&types::VideoNote> {
             match &self.kind {
-                Common { media_kind: VideoNote { video_note, .. }, .. } => {
-                    Some(video_note)
-                }
+                Common(MessageCommon {
+                    media_kind:
+                        MediaKind::VideoNote(MediaVideoNote { video_note, .. }),
+                    ..
+                }) => Some(video_note),
                 _ => None,
             }
         }
 
         pub fn caption(&self) -> Option<&str> {
             match &self.kind {
-                Common { media_kind, .. } => match media_kind {
-                    Animation { caption, .. }
-                    | Audio { caption, .. }
-                    | Document { caption, .. }
-                    | Photo { caption, .. }
-                    | Video { caption, .. }
-                    | Voice { caption, .. } => {
+                Common(MessageCommon { media_kind, .. }) => match media_kind {
+                    MediaKind::Animation(MediaAnimation {
+                        caption, ..
+                    })
+                    | MediaKind::Audio(MediaAudio { caption, .. })
+                    | MediaKind::Document(MediaDocument { caption, .. })
+                    | MediaKind::Photo(MediaPhoto { caption, .. })
+                    | MediaKind::Video(MediaVideo { caption, .. })
+                    | MediaKind::Voice(MediaVoice { caption, .. }) => {
                         caption.as_ref().map(Deref::deref)
                     }
                     _ => None,
@@ -564,58 +738,77 @@ mod getters {
 
         pub fn contact(&self) -> Option<&types::Contact> {
             match &self.kind {
-                Common { media_kind: Contact { contact }, .. } => Some(contact),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Contact(MediaContact { contact, .. }),
+                    ..
+                }) => Some(contact),
                 _ => None,
             }
         }
 
         pub fn location(&self) -> Option<&types::Location> {
             match &self.kind {
-                Common { media_kind: Location { location, .. }, .. } => {
-                    Some(location)
-                }
+                Common(MessageCommon {
+                    media_kind:
+                        MediaKind::Location(MediaLocation { location, .. }),
+                    ..
+                }) => Some(location),
                 _ => None,
             }
         }
 
         pub fn venue(&self) -> Option<&types::Venue> {
             match &self.kind {
-                Common { media_kind: Venue { venue, .. }, .. } => Some(venue),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Venue(MediaVenue { venue, .. }),
+                    ..
+                }) => Some(venue),
                 _ => None,
             }
         }
 
         pub fn poll(&self) -> Option<&types::Poll> {
             match &self.kind {
-                Common { media_kind: Poll { poll, .. }, .. } => Some(poll),
+                Common(MessageCommon {
+                    media_kind: MediaKind::Poll(MediaPoll { poll, .. }),
+                    ..
+                }) => Some(poll),
                 _ => None,
             }
         }
 
         pub fn new_chat_members(&self) -> Option<&[User]> {
             match &self.kind {
-                NewChatMembers { new_chat_members } => Some(new_chat_members),
+                NewChatMembers(MessageNewChatMembers { new_chat_members }) => {
+                    Some(new_chat_members.as_ref())
+                }
                 _ => None,
             }
         }
 
         pub fn left_chat_member(&self) -> Option<&User> {
             match &self.kind {
-                LeftChatMember { left_chat_member } => Some(left_chat_member),
+                LeftChatMember(MessageLeftChatMember { left_chat_member }) => {
+                    Some(left_chat_member)
+                }
                 _ => None,
             }
         }
 
         pub fn new_chat_title(&self) -> Option<&str> {
             match &self.kind {
-                NewChatTitle { new_chat_title } => Some(new_chat_title),
+                NewChatTitle(MessageNewChatTitle { new_chat_title }) => {
+                    Some(new_chat_title)
+                }
                 _ => None,
             }
         }
 
         pub fn new_chat_photo(&self) -> Option<&[PhotoSize]> {
             match &self.kind {
-                NewChatPhoto { new_chat_photo } => Some(new_chat_photo),
+                NewChatPhoto(MessageNewChatPhoto { new_chat_photo }) => {
+                    Some(new_chat_photo)
+                }
                 _ => None,
             }
         }
@@ -624,50 +817,52 @@ mod getters {
         //       mb smt like `is_delete_chat_photo(&self) -> bool`?
         pub fn delete_chat_photo(&self) -> Option<True> {
             match &self.kind {
-                DeleteChatPhoto { delete_chat_photo } => {
-                    Some(*delete_chat_photo)
-                }
+                DeleteChatPhoto(MessageDeleteChatPhoto {
+                    delete_chat_photo,
+                }) => Some(*delete_chat_photo),
                 _ => None,
             }
         }
 
         pub fn group_chat_created(&self) -> Option<True> {
             match &self.kind {
-                GroupChatCreated { group_chat_created } => {
-                    Some(*group_chat_created)
-                }
+                GroupChatCreated(MessageGroupChatCreated {
+                    group_chat_created,
+                }) => Some(*group_chat_created),
                 _ => None,
             }
         }
 
         pub fn super_group_chat_created(&self) -> Option<True> {
             match &self.kind {
-                SupergroupChatCreated { supergroup_chat_created } => {
-                    Some(*supergroup_chat_created)
-                }
+                SupergroupChatCreated(MessageSupergroupChatCreated {
+                    supergroup_chat_created,
+                }) => Some(*supergroup_chat_created),
                 _ => None,
             }
         }
 
         pub fn channel_chat_created(&self) -> Option<True> {
             match &self.kind {
-                ChannelChatCreated { channel_chat_created } => {
-                    Some(*channel_chat_created)
-                }
+                ChannelChatCreated(MessageChannelChatCreated {
+                    channel_chat_created,
+                }) => Some(*channel_chat_created),
                 _ => None,
             }
         }
 
         pub fn migrate_to_chat_id(&self) -> Option<i64> {
             match &self.kind {
-                Migrate { migrate_to_chat_id, .. } => Some(*migrate_to_chat_id),
+                Migrate(MessageMigrate { migrate_to_chat_id, .. }) => {
+                    Some(*migrate_to_chat_id)
+                }
                 _ => None,
             }
         }
 
         pub fn migrate_from_chat_id(&self) -> Option<i64> {
             match &self.kind {
-                Migrate { migrate_from_chat_id, .. } => {
+                Migrate(MessageMigrate { migrate_from_chat_id, .. }) => {
                     Some(*migrate_from_chat_id)
                 }
                 _ => None,
@@ -676,46 +871,50 @@ mod getters {
 
         pub fn pinned_message(&self) -> Option<&Message> {
             match &self.kind {
-                Pinned { pinned } => Some(pinned),
+                Pinned(MessagePinned { pinned }) => Some(pinned),
                 _ => None,
             }
         }
 
         pub fn invoice(&self) -> Option<&types::Invoice> {
             match &self.kind {
-                Invoice { invoice } => Some(invoice),
+                Invoice(MessageInvoice { invoice }) => Some(invoice),
                 _ => None,
             }
         }
 
         pub fn successful_payment(&self) -> Option<&types::SuccessfulPayment> {
             match &self.kind {
-                SuccessfulPayment { successful_payment } => {
-                    Some(successful_payment)
-                }
+                SuccessfulPayment(MessageSuccessfulPayment {
+                    successful_payment,
+                }) => Some(successful_payment),
                 _ => None,
             }
         }
 
         pub fn connected_website(&self) -> Option<&str> {
             match &self.kind {
-                ConnectedWebsite { connected_website } => {
-                    Some(connected_website)
-                }
+                ConnectedWebsite(MessageConnectedWebsite {
+                    connected_website,
+                }) => Some(connected_website),
                 _ => None,
             }
         }
 
         pub fn passport_data(&self) -> Option<&types::PassportData> {
             match &self.kind {
-                PassportData { passport_data } => Some(passport_data),
+                PassportData(MessagePassportData { passport_data }) => {
+                    Some(passport_data)
+                }
                 _ => None,
             }
         }
 
         pub fn reply_markup(&self) -> Option<&types::InlineKeyboardMarkup> {
             match &self.kind {
-                Common { reply_markup, .. } => reply_markup.as_ref(),
+                Common(MessageCommon { reply_markup, .. }) => {
+                    reply_markup.as_ref()
+                }
                 _ => None,
             }
         }
@@ -725,17 +924,21 @@ mod getters {
 impl Message {
     pub fn url(&self) -> Option<reqwest::Url> {
         match &self.chat.kind {
-            ChatKind::NonPrivate {
-                kind: NonPrivateChatKind::Channel { username: Some(username) },
-                ..
-            }
-            | ChatKind::NonPrivate {
+            ChatKind::NonPrivate(ChatNonPrivate {
                 kind:
-                    NonPrivateChatKind::Supergroup {
-                        username: Some(username), ..
-                    },
+                    NonPrivateChatKind::Channel(NonPrivateChatChannel {
+                        username: Some(username),
+                    }),
                 ..
-            } => Some(
+            })
+            | ChatKind::NonPrivate(ChatNonPrivate {
+                kind:
+                    NonPrivateChatKind::Supergroup(NonPrivateChatSupergroup {
+                        username: Some(username),
+                        ..
+                    }),
+                ..
+            }) => Some(
                 reqwest::Url::parse(
                     format!("https://t.me/{0}/{1}/", username, self.id)
                         .as_str(),
diff --git a/src/types/message_entity.rs b/src/types/message_entity.rs
index b5f45327..39cc32b9 100644
--- a/src/types/message_entity.rs
+++ b/src/types/message_entity.rs
@@ -50,7 +50,10 @@ impl MessageEntity {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::types::{Chat, ChatKind, ForwardKind, MediaKind, MessageKind};
+    use crate::types::{
+        Chat, ChatKind, ChatPrivate, ForwardKind, ForwardOrigin, MediaKind,
+        MediaText, MessageCommon, MessageKind,
+    };
 
     #[test]
     fn recursive_kind() {
@@ -103,15 +106,15 @@ mod tests {
             date: 0,
             chat: Chat {
                 id: 0,
-                kind: ChatKind::Private {
+                kind: ChatKind::Private(ChatPrivate {
                     type_: (),
                     username: None,
                     first_name: None,
                     last_name: None,
-                },
+                }),
                 photo: None,
             },
-            kind: MessageKind::Common {
+            kind: MessageKind::Common(MessageCommon {
                 from: Some(User {
                     id: 0,
                     is_bot: false,
@@ -120,18 +123,20 @@ mod tests {
                     username: None,
                     language_code: None,
                 }),
-                forward_kind: ForwardKind::Origin { reply_to_message: None },
+                forward_kind: ForwardKind::Origin(ForwardOrigin {
+                    reply_to_message: None,
+                }),
                 edit_date: None,
-                media_kind: MediaKind::Text {
+                media_kind: MediaKind::Text(MediaText {
                     text: "no yes no".to_string(),
                     entities: vec![MessageEntity {
                         kind: MessageEntityKind::Mention,
                         offset: 3,
                         length: 3,
                     }],
-                },
+                }),
                 reply_markup: None,
-            },
+            }),
         }
     }
 }
diff --git a/src/types/passport_element_error.rs b/src/types/passport_element_error.rs
index 9657969f..21e47ee7 100644
--- a/src/types/passport_element_error.rs
+++ b/src/types/passport_element_error.rs
@@ -17,133 +17,160 @@ pub struct PassportElementError {
 #[serde(tag = "source")]
 #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
 pub enum PassportElementErrorKind {
-    /// Represents an issue in one of the data fields that was provided by the
-    /// user. The error is considered resolved when the field's value changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrordatafield).
     #[serde(rename = "data")]
-    DataField {
-        /// The section of the user's Telegram Passport which has the error.
-        r#type: PassportElementErrorDataFieldType,
+    DataField(PassportElementErrorDataField),
 
-        /// Name of the data field which has the error.
-        field_name: String,
-
-        /// Base64-encoded data hash.
-        data_hash: String,
-    },
-
-    /// Represents an issue with the front side of a document. The error is
-    /// considered resolved when the file with the front side of the document
-    /// changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorfrontside).
     #[serde(rename = "snake_case")]
-    FrontSide {
-        /// The section of the user's Telegram Passport which has the issue.
-        r#type: PassportElementErrorFrontSideType,
+    FrontSide(PassportElementErrorFrontSide),
 
-        /// Base64-encoded hash of the file with the front side of the
-        /// document.
-        file_hash: String,
-    },
-
-    /// Represents an issue with the reverse side of a document. The error is
-    /// considered resolved when the file with reverse side of the document
-    /// changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorreverseside).
     #[serde(rename = "snake_case")]
-    ReverseSide {
-        /// The section of the user's Telegram Passport which has the issue.
-        r#type: PassportElementErrorReverseSideType,
+    ReverseSide(PassportElementErrorReverseSide),
 
-        //// Base64-encoded hash of the file with the reverse side of the
-        //// document.
-        file_hash: String,
-    },
-
-    //// Represents an issue with the selfie with a document. The error is
-    //// considered resolved when the file with the selfie changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorselfie).
     #[serde(rename = "snake_case")]
-    Selfie {
-        /// The section of the user's Telegram Passport which has the issue.
-        r#type: PassportElementErrorSelfieType,
+    Selfie(PassportElementErrorSelfie),
 
-        /// Base64-encoded hash of the file with the selfie.
-        file_hash: String,
-    },
-
-    /// Represents an issue with a document scan. The error is considered
-    /// resolved when the file with the document scan changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorfile).
     #[serde(rename = "snake_case")]
-    File {
-        /// The section of the user's Telegram Passport which has the issue.
-        r#type: PassportElementErrorFileType,
+    File(PassportElementErrorFile),
 
-        /// Base64-encoded file hash.
-        file_hash: String,
-    },
-
-    /// Represents an issue with a list of scans. The error is considered
-    /// resolved when the list of files containing the scans changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorfiles).
     #[serde(rename = "snake_case")]
-    Files {
-        /// The section of the user's Telegram Passport which has the issue.
-        r#type: PassportElementErrorFilesType,
+    Files(PassportElementErrorFiles),
 
-        /// List of base64-encoded file hashes.
-        file_hashes: Vec<String>,
-    },
-
-    /// Represents an issue with one of the files that constitute the
-    /// translation of a document. The error is considered resolved when the
-    /// file changes.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrortranslationfile).
     #[serde(rename = "snake_case")]
-    TranslationFile {
-        /// Type of element of the user's Telegram Passport which has the
-        /// issue.
-        r#type: PassportElementErrorTranslationFileType,
+    TranslationFile(PassportElementErrorTranslationFile),
 
-        /// Base64-encoded file hash.
-        file_hash: String,
-    },
-
-    /// Represents an issue with the translated version of a document. The
-    /// error is considered resolved when a file with the document translation
-    /// change.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrortranslationfiles).
     #[serde(rename = "snake_case")]
-    TranslationFiles {
-        /// Type of element of the user's Telegram Passport which has the issue
-        r#type: PassportElementErrorTranslationFilesType,
+    TranslationFiles(PassportElementErrorTranslationFiles),
 
-        /// List of base64-encoded file hashes
-        file_hashes: Vec<String>,
-    },
-
-    /// Represents an issue in an unspecified place. The error is considered
-    /// resolved when new data is added.
-    ///
-    /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorunspecified).
     #[serde(rename = "snake_case")]
-    Unspecified {
-        /// Type of element of the user's Telegram Passport which has the
-        /// issue.
-        r#type: PassportElementErrorUnspecifiedType,
+    Unspecified(PassportElementErrorUnspecified),
+}
 
-        /// Base64-encoded element hash.
-        element_hash: String,
-    },
+/// Represents an issue in one of the data fields that was provided by the
+/// user. The error is considered resolved when the field's value changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrordatafield).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorDataField {
+    /// The section of the user's Telegram Passport which has the error.
+    pub r#type: PassportElementErrorDataFieldType,
+
+    /// Name of the data field which has the error.
+    pub field_name: String,
+
+    /// Base64-encoded data hash.
+    pub data_hash: String,
+}
+
+/// Represents an issue with the front side of a document. The error is
+/// considered resolved when the file with the front side of the document
+/// changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrorfrontside).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorFrontSide {
+    /// The section of the user's Telegram Passport which has the issue.
+    pub r#type: PassportElementErrorFrontSideType,
+
+    /// Base64-encoded hash of the file with the front side of the
+    /// document.
+    pub file_hash: String,
+}
+
+/// Represents an issue with the reverse side of a document. The error is
+/// considered resolved when the file with reverse side of the document
+/// changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrorreverseside).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorReverseSide {
+    /// The section of the user's Telegram Passport which has the issue.
+    pub r#type: PassportElementErrorReverseSideType,
+
+    //// Base64-encoded hash of the file with the reverse side of the
+    //// document.
+    pub file_hash: String,
+}
+
+//// Represents an issue with the selfie with a document. The error is
+//// considered resolved when the file with the selfie changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrorselfie).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorSelfie {
+    /// The section of the user's Telegram Passport which has the issue.
+    pub r#type: PassportElementErrorSelfieType,
+
+    /// Base64-encoded hash of the file with the selfie.
+    pub file_hash: String,
+}
+
+/// Represents an issue with a document scan. The error is considered
+/// resolved when the file with the document scan changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrorfile).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorFile {
+    /// The section of the user's Telegram Passport which has the issue.
+    pub r#type: PassportElementErrorFileType,
+
+    /// Base64-encoded file hash.
+    pub file_hash: String,
+}
+
+/// Represents an issue with a list of scans. The error is considered
+/// resolved when the list of files containing the scans changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrorfiles).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorFiles {
+    /// The section of the user's Telegram Passport which has the issue.
+    pub r#type: PassportElementErrorFilesType,
+
+    /// List of base64-encoded file hashes.
+    pub file_hashes: Vec<String>,
+}
+
+/// Represents an issue with one of the files that constitute the
+/// translation of a document. The error is considered resolved when the
+/// file changes.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrortranslationfile).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorTranslationFile {
+    /// Type of element of the user's Telegram Passport which has the
+    /// issue.
+    pub r#type: PassportElementErrorTranslationFileType,
+
+    /// Base64-encoded file hash.
+    pub file_hash: String,
+}
+
+/// Represents an issue with the translated version of a document. The
+/// error is considered resolved when a file with the document translation
+/// change.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrortranslationfiles).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorTranslationFiles {
+    /// Type of element of the user's Telegram Passport which has the issue
+    pub r#type: PassportElementErrorTranslationFilesType,
+
+    /// List of base64-encoded file hashes
+    pub file_hashes: Vec<String>,
+}
+
+/// Represents an issue in an unspecified place. The error is considered
+/// resolved when new data is added.
+///
+/// [The official docs](https://core.telegram.org/bots/api#passportelementerrorunspecified).
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PassportElementErrorUnspecified {
+    /// Type of element of the user's Telegram Passport which has the
+    /// issue.
+    pub r#type: PassportElementErrorUnspecifiedType,
+
+    /// Base64-encoded element hash.
+    pub element_hash: String,
 }
 
 #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
@@ -252,11 +279,13 @@ mod tests {
     fn serialize_data_field() {
         let data = PassportElementError {
             message: "This is an error message!".to_owned(),
-            kind: PassportElementErrorKind::DataField {
-                r#type: PassportElementErrorDataFieldType::InternalPassport,
-                field_name: "The field name".to_owned(),
-                data_hash: "This is a data hash".to_owned(),
-            },
+            kind: PassportElementErrorKind::DataField(
+                PassportElementErrorDataField {
+                    r#type: PassportElementErrorDataFieldType::InternalPassport,
+                    field_name: "The field name".to_owned(),
+                    data_hash: "This is a data hash".to_owned(),
+                },
+            ),
         };
 
         assert_eq!(
diff --git a/src/types/poll.rs b/src/types/poll.rs
index 11248714..085a4c19 100644
--- a/src/types/poll.rs
+++ b/src/types/poll.rs
@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
 /// This object contains information about a poll.
 ///
 /// [The official docs](https://core.telegram.org/bots/api#poll).
+#[serde_with_macros::skip_serializing_none]
 #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
 pub struct Poll {
     /// Unique poll identifier.
diff --git a/src/types/update.rs b/src/types/update.rs
index 2b3a3e44..31aa9a8c 100644
--- a/src/types/update.rs
+++ b/src/types/update.rs
@@ -125,8 +125,9 @@ impl Update {
 #[cfg(test)]
 mod test {
     use crate::types::{
-        Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind, Update,
-        UpdateKind, User,
+        Chat, ChatKind, ChatPrivate, ForwardKind, ForwardOrigin, MediaKind,
+        MediaText, Message, MessageCommon, MessageKind, Update, UpdateKind,
+        User,
     };
 
     // TODO: more tests for deserialization
@@ -161,15 +162,15 @@ mod test {
                 date: 1_569_518_342,
                 chat: Chat {
                     id: 218_485_655,
-                    kind: ChatKind::Private {
+                    kind: ChatKind::Private(ChatPrivate {
                         type_: (),
                         username: Some(String::from("WaffleLapkin")),
                         first_name: Some(String::from("Waffle")),
                         last_name: None,
-                    },
+                    }),
                     photo: None,
                 },
-                kind: MessageKind::Common {
+                kind: MessageKind::Common(MessageCommon {
                     from: Some(User {
                         id: 218_485_655,
                         is_bot: false,
@@ -178,16 +179,16 @@ mod test {
                         username: Some(String::from("WaffleLapkin")),
                         language_code: Some(String::from("en")),
                     }),
-                    forward_kind: ForwardKind::Origin {
+                    forward_kind: ForwardKind::Origin(ForwardOrigin {
                         reply_to_message: None,
-                    },
+                    }),
                     edit_date: None,
-                    media_kind: MediaKind::Text {
+                    media_kind: MediaKind::Text(MediaText {
                         text: String::from("hello there"),
                         entities: vec![],
-                    },
+                    }),
                     reply_markup: None,
-                },
+                }),
             }),
         };