diff --git a/crates/teloxide-core/src/types.rs b/crates/teloxide-core/src/types.rs index 0a94ce54..89897656 100644 --- a/crates/teloxide-core/src/types.rs +++ b/crates/teloxide-core/src/types.rs @@ -17,6 +17,7 @@ pub use chat_member::*; pub use chat_member_updated::*; pub use chat_permissions::*; pub use chat_photo::*; +pub use chat_shared::*; pub use chat_type::*; pub use chosen_inline_result::*; pub use contact::*; @@ -67,6 +68,8 @@ pub use input_sticker::*; pub use invoice::*; pub use keyboard_button::*; pub use keyboard_button_poll_type::*; +pub use keyboard_button_request_chat::*; +pub use keyboard_button_request_user::*; pub use label_price::*; pub use location::*; pub use login_url::*; @@ -106,6 +109,7 @@ pub use unit_true::*; pub use update::*; pub use user::*; pub use user_profile_photos::*; +pub use user_shared::*; pub use venue::*; pub use video::*; pub use video_chat_ended::*; @@ -136,6 +140,7 @@ mod chat_member; mod chat_member_updated; mod chat_permissions; mod chat_photo; +mod chat_shared; mod chat_type; mod chosen_inline_result; mod contact; @@ -162,6 +167,8 @@ mod input_sticker; mod invoice; mod keyboard_button; mod keyboard_button_poll_type; +mod keyboard_button_request_chat; +mod keyboard_button_request_user; mod label_price; mod location; mod login_url; @@ -198,6 +205,7 @@ mod unit_true; mod update; mod user; mod user_profile_photos; +mod user_shared; mod venue; mod video; mod video_chat_ended; diff --git a/crates/teloxide-core/src/types/chat_member.rs b/crates/teloxide-core/src/types/chat_member.rs index 3f712af8..221bc4a6 100644 --- a/crates/teloxide-core/src/types/chat_member.rs +++ b/crates/teloxide-core/src/types/chat_member.rs @@ -632,15 +632,46 @@ impl ChatMemberKind { } } + /// Returns `true` if the user is allowed to send audios, documents, photos, + /// videos, video notes and voice notes. + /// + /// I.e. returns **`false`** if the user + /// - has left or has been banned in the chat + /// - is restricted and doesn't have all send media right + /// Returns `true` otherwise. + #[must_use] + pub fn can_send_media_messages(&self) -> bool { + match &self { + Self::Restricted(Restricted { + can_send_audios, + can_send_documents, + can_send_photos, + can_send_videos, + can_send_video_notes, + can_send_voice_notes, + .. + }) => { + *can_send_audios + && *can_send_documents + && *can_send_photos + && *can_send_videos + && *can_send_video_notes + && *can_send_voice_notes + } + Self::Owner(_) | Self::Administrator(_) | Self::Member => true, + Self::Left | Self::Banned(_) => false, + } + } + /// Returns `true` if the user is allowed to send animations, games, /// stickers and use inline bots. /// /// I.e. returns **`false`** if the user /// - has left or has been banned from the chat - /// - is restricted and doesn't have the [`can_send_media_messages`] right + /// - is restricted and has no [`can_send_other_messages`] right /// Returns `true` otherwise. /// - /// [`can_send_media_messages`]: Restricted::can_send_media_messages + /// [`can_send_other_messages`]: Restricted::can_send_other_messages #[deprecated( since = "0.9.0", note = "Match manually and use `can_send_other_messages` field directly. Details: https://github.com/teloxide/teloxide/issues/781" @@ -661,10 +692,10 @@ impl ChatMemberKind { /// /// I.e. returns **`false`** if the user /// - has left or has been banned from the chat - /// - is restricted and doesn't have the [`can_send_media_messages`] right + /// - is restricted and has no [`can_add_web_page_previews`] right /// Returns `true` otherwise. /// - /// [`can_send_media_messages`]: Restricted::can_send_media_messages + /// [`can_add_web_page_previews`]: Restricted::can_add_web_page_previews #[deprecated( since = "0.9.0", note = "Match manually and use `can_add_web_page_previews` field directly. Details: https://github.com/teloxide/teloxide/issues/781" @@ -884,6 +915,7 @@ mod tests { "status":"restricted", "is_member": true, "can_send_messages": true, + "can_send_media_messages": true, "can_send_audios": false, "can_send_documents": false, "can_send_photos": true, diff --git a/crates/teloxide-core/src/types/chat_permissions.rs b/crates/teloxide-core/src/types/chat_permissions.rs index 67b6cddc..31b73e15 100644 --- a/crates/teloxide-core/src/types/chat_permissions.rs +++ b/crates/teloxide-core/src/types/chat_permissions.rs @@ -51,59 +51,69 @@ bitflags::bitflags! { /// locations and venues. const SEND_MESSAGES = 1; - /// Set if the user is allowed to send audios. - const SEND_AUDIOS = (1 << 1) | Self::SEND_MESSAGES.bits; - - /// Set if the user is allowed to send documents. - const SEND_DOCUMENTS = (1 << 2) | Self::SEND_MESSAGES.bits; - - /// Set if the user is allowed to send photos. - const SEND_PHOTOS = (1 << 3) | Self::SEND_MESSAGES.bits; - - /// Set if the user is allowed to send videos. - const SEND_VIDEOS = (1 << 4) | Self::SEND_MESSAGES.bits; - - /// Set if the user is allowed to send video notes. - const SEND_VIDEO_NOTES = (1 << 5) | Self::SEND_MESSAGES.bits; - - /// Set if the user is allowed to send voice notes. - const SEND_VOICE_NOTES = (1 << 6) | Self::SEND_MESSAGES.bits; - /// Set if the user is allowed to send polls, implies /// `SEND_MESSAGES`. - const SEND_POLLS = (1 << 7) | Self::SEND_MESSAGES.bits; + const SEND_POLLS = (1 << 2) | Self::SEND_MESSAGES.bits; /// Set if the user is allowed to send animations, games, stickers and - /// use inline bots, implies midia messages permissions. - const SEND_OTHER_MESSAGES = (1 << 8) | Self::SEND_AUDIOS.bits - | Self::SEND_DOCUMENTS.bits - | Self::SEND_PHOTOS.bits - | Self::SEND_VIDEOS.bits - | Self::SEND_VIDEO_NOTES.bits - | Self::SEND_VOICE_NOTES.bits; + /// use inline bots, implies `SEND_MEDIA_MESSAGES`. + const SEND_OTHER_MESSAGES = (1 << 3); /// Set if the user is allowed to add web page previews to - /// their messages, implies midia messages permissions. - const ADD_WEB_PAGE_PREVIEWS = (1 << 9) | Self::SEND_AUDIOS.bits - | Self::SEND_DOCUMENTS.bits - | Self::SEND_PHOTOS.bits - | Self::SEND_VIDEOS.bits - | Self::SEND_VIDEO_NOTES.bits - | Self::SEND_VOICE_NOTES.bits; + /// their messages, implies `SEND_MEDIA_MESSAGES`. + const ADD_WEB_PAGE_PREVIEWS = (1 << 4); /// Set if the user is allowed to change the chat title, photo and /// other settings. Ignored in public supergroups. - const CHANGE_INFO = (1 << 10); + const CHANGE_INFO = (1 << 5); /// Set if the user is allowed to invite new users to the chat. - const INVITE_USERS = (1 << 11); + const INVITE_USERS = (1 << 6); /// Set if the user is allowed to pin messages. Ignored in public /// supergroups. - const PIN_MESSAGES = (1 << 12); + const PIN_MESSAGES = (1 << 7); /// Set if the user is allowed to create, rename, close, and reopen forum topics. - const MANAGE_TOPICS = (1 << 13); + const MANAGE_TOPICS = (1 << 8); + + /// Set if the user is allowed to send audios. implies + /// `SEND_MESSAGES`. + const SEND_AUDIOS = (1 << 9) | Self::SEND_MESSAGES.bits; + + /// Set if the user is allowed to send documents. implies + /// `SEND_MESSAGES`. + const SEND_DOCUMENTS = (1 << 10) | Self::SEND_MESSAGES.bits; + + /// Set if the user is allowed to send photos. implies + /// `SEND_MESSAGES`. + const SEND_PHOTOS = (1 << 11) | Self::SEND_MESSAGES.bits; + + /// Set if the user is allowed to send videos. implies + /// `SEND_MESSAGES`. + const SEND_VIDEOS = (1 << 12) | Self::SEND_MESSAGES.bits; + + /// Set if the user is allowed to send video notes. implies + /// `SEND_MESSAGES`. + const SEND_VIDEO_NOTES = (1 << 13) | Self::SEND_MESSAGES.bits; + + /// Set if the user is allowed to send voice notes. implies + /// `SEND_MESSAGES`. + const SEND_VOICE_NOTES = (1 << 14) | Self::SEND_MESSAGES.bits; + + /// Set if the user is allowed to send audios, documents, + /// photos, videos, video notes and voice notes, implies + /// `SEND_MESSAGES`, `SEND_AUDIOS`, `SEND_DOCUMENTS`, + /// `SEND_PHOTOS`, `SEND_VIDEOS`, `SEND_VIDEO_NOTES` and `SEND_VOICE_NOTES`. + /// Note: this is not a separate permission on it's own, this is just a alias for all the permissions mentioned. + const SEND_MEDIA_MESSAGES = Self::SEND_MESSAGES.bits + | Self::SEND_AUDIOS.bits + | Self::SEND_DOCUMENTS.bits + | Self::SEND_PHOTOS.bits + | Self::SEND_VIDEOS.bits + | Self::SEND_VIDEO_NOTES.bits + | Self::SEND_VOICE_NOTES.bits; + } } @@ -157,6 +167,13 @@ impl ChatPermissions { self.contains(ChatPermissions::SEND_VOICE_NOTES) } + /// Checks for [`SEND_MEDIA_MESSAGES`] permission. + /// + /// [`SEND_MEDIA_MESSAGES`]: ChatPermissions::SEND_MEDIA_MESSAGES + pub fn can_send_media_messages(&self) -> bool { + self.contains(ChatPermissions::SEND_MEDIA_MESSAGES) + } + /// Checks for [`SEND_POLLS`] permission. /// /// [`SEND_POLLS`]: ChatPermissions::SEND_POLLS diff --git a/crates/teloxide-core/src/types/chat_shared.rs b/crates/teloxide-core/src/types/chat_shared.rs new file mode 100644 index 00000000..4c75b94e --- /dev/null +++ b/crates/teloxide-core/src/types/chat_shared.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; + +use crate::types::ChatId; + +/// Information about the chat whose identifier was shared with the bot using a +/// [`KeyboardButtonRequestChat`] button. +/// +/// [`KeyboardButtonRequestChat`]: crate::types::KeyboardButtonRequestChat +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct ChatShared { + /// Identifier of the request. + pub request_id: i32, + /// Identifier of the shared chat. + pub chat_id: ChatId, +} diff --git a/crates/teloxide-core/src/types/keyboard_button.rs b/crates/teloxide-core/src/types/keyboard_button.rs index c6c3390d..42aab9bd 100644 --- a/crates/teloxide-core/src/types/keyboard_button.rs +++ b/crates/teloxide-core/src/types/keyboard_button.rs @@ -1,6 +1,8 @@ use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; -use crate::types::{KeyboardButtonPollType, True, WebAppInfo}; +use crate::types::{ + KeyboardButtonPollType, KeyboardButtonRequestChat, KeyboardButtonRequestUser, True, WebAppInfo, +}; /// This object represents one button of the reply keyboard. /// @@ -59,6 +61,18 @@ pub enum ButtonRequest { /// 9 April, 2016. Older clients will display unsupported message. Contact, + /// If this variant is used, pressing the button will open a list of + /// suitable chats. Tapping on a chat will send its identifier to the bot in + /// a [`chat_shared`] service message. + /// + /// [`chat_shared`]: crate::types::MessageKind::ChatShared + RequestChat(KeyboardButtonRequestChat), + + /// If this variant is used, pressing the button will open a list of + /// suitable users. Tapping on any user will send their identifier to the + /// bot in a “user_shared” service message. + RequestUser(KeyboardButtonRequestUser), + /// If this variant is used, the user will be asked to create a poll and /// send it to the bot when the button is pressed. /// @@ -89,6 +103,18 @@ struct RawRequest { #[serde(rename = "request_location")] location: Option, + /// If specified, pressing the button will open a list of suitable chats. + /// Tapping on a chat will send its identifier to the bot in a “chat_shared” + /// service message. Available in private chats only. + #[serde(rename = "request_chat")] + chat: Option, + + /// If specified, pressing the button will open a list of suitable users. + /// Tapping on any user will send their identifier to the bot in a + /// “user_shared” service message. Available in private chats only. + #[serde(rename = "request_user")] + user: Option, + /// If specified, the user will be asked to create a poll and /// send it to the bot when the button is pressed. Available in private /// chats only. @@ -108,25 +134,36 @@ impl<'de> Deserialize<'de> for ButtonRequest { { let raw = RawRequest::deserialize(deserializer)?; match raw { - RawRequest { contact, location, poll, web_app } + RawRequest { contact, location, chat, user, poll, web_app } if 1 < (contact.is_some() as u8 + location.is_some() as u8 + + chat.is_some() as u8 + + user.is_some() as u8 + poll.is_some() as u8 + web_app.is_some() as u8) => { Err(D::Error::custom( - "`request_contact`, `request_location`, `request_poll` and `web_app` fields \ - are mutually exclusive", + "`request_contact`, `request_location`, `request_chat`, `request_user`, \ + `request_poll` and `web_app` fields are mutually exclusive", )) } - RawRequest { contact: Some(_), .. } => Ok(Self::Contact), - RawRequest { location: Some(_), .. } => Ok(Self::Location), + RawRequest { contact: Some(True), .. } => Ok(Self::Contact), + RawRequest { location: Some(True), .. } => Ok(Self::Location), + RawRequest { chat: Some(request_chat), .. } => Ok(Self::RequestChat(request_chat)), + RawRequest { user: Some(request_user), .. } => Ok(Self::RequestUser(request_user)), RawRequest { poll: Some(poll_type), .. } => Ok(Self::Poll(poll_type)), RawRequest { web_app: Some(web_app), .. } => Ok(Self::WebApp(web_app)), - _ => Err(D::Error::custom( - "Either one of `request_contact`, `request_location`, `request_poll` and \ - `web_app` fields is required", + RawRequest { + contact: None, + location: None, + chat: None, + user: None, + poll: None, + web_app: None, + } => Err(D::Error::custom( + "Either one of `request_contact`, `request_chat`, `request_user`, \ + `request_location`, `request_poll` and `web_app` fields is required", )), } } @@ -137,11 +174,20 @@ impl Serialize for ButtonRequest { where S: Serializer, { - let mut raw = RawRequest { contact: None, location: None, poll: None, web_app: None }; + let mut raw = RawRequest { + contact: None, + location: None, + chat: None, + user: None, + poll: None, + web_app: None, + }; match self { Self::Contact => raw.contact = Some(True), Self::Location => raw.location = Some(True), + Self::RequestChat(request_chat) => raw.chat = Some(request_chat.clone()), + Self::RequestUser(request_user) => raw.user = Some(request_user.clone()), Self::Poll(poll_type) => raw.poll = Some(poll_type.clone()), Self::WebApp(web_app) => raw.web_app = Some(web_app.clone()), }; @@ -171,6 +217,17 @@ mod tests { assert_eq!(expected, actual); } + #[test] + fn serialize_chat_request() { + let button = KeyboardButton { + text: String::from(""), + request: Some(ButtonRequest::RequestChat(KeyboardButtonRequestChat::new(0, false))), + }; + let expected = r#"{"text":"","request_chat":{"request_id":0,"chat_is_channel":false}}"#; + let actual = serde_json::to_string(&button).unwrap(); + assert_eq!(expected, actual); + } + #[test] fn deserialize_no_request() { let json = r#"{"text":""}"#; diff --git a/crates/teloxide-core/src/types/keyboard_button_request_chat.rs b/crates/teloxide-core/src/types/keyboard_button_request_chat.rs new file mode 100644 index 00000000..fef4222b --- /dev/null +++ b/crates/teloxide-core/src/types/keyboard_button_request_chat.rs @@ -0,0 +1,114 @@ +use serde::{Deserialize, Serialize}; + +use crate::types::ChatAdministratorRights; + +/// This object defines the criteria used to request a suitable chat. The +/// identifier of the selected chat will be shared with the bot when the +/// corresponding button is pressed. [More about requesting chats »] +/// +/// [More about requesting chats »]: https://core.telegram.org/bots/features#chat-and-user-selection +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct KeyboardButtonRequestChat { + /// identifier of the request, which will be received back in the + /// [`ChatShared`] object. Must be unique within the message. + /// + /// [`ChatShared`]: crate::types::ChatShared + pub request_id: i32, + + /// Pass `true` to request a channel chat, pass `false` to request a group + /// or a supergroup chat. + pub chat_is_channel: bool, + + /// Pass `true` to request a forum supergroup, pass `false` to request a + /// non-forum chat. If not specified, no additional restrictions are + /// applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub chat_is_forum: Option, + + /// Pass `true` to request a supergroup or a channel with a username, pass + /// `false` to request a chat without a username. If not specified, no + /// additional restrictions are applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub chat_has_username: Option, + + /// Pass `true` to request a chat owned by the user. Otherwise, no + /// additional restrictions are applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub chat_is_created: Option, + + /// Listing the required administrator rights of the user in the chat. The + /// rights must be a superset of bot_administrator_rights. If not specified, + /// no additional restrictions are applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_administrator_rights: Option, + + /// Listing the required administrator rights of the bot in the chat. The + /// rights must be a subset of user_administrator_rights. If not specified, + /// no additional restrictions are applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bot_administrator_rights: Option, + + /// Pass `true` to request a chat with the bot as a member. Otherwise, no + /// additional restrictions are applied. + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + pub bot_is_member: bool, +} + +impl KeyboardButtonRequestChat { + /// Creates a new [`KeyboardButtonRequestChat`]. + pub fn new(request_id: i32, chat_is_channel: bool) -> Self { + Self { + request_id, + chat_is_channel, + chat_is_forum: None, + chat_has_username: None, + chat_is_created: None, + user_administrator_rights: None, + bot_administrator_rights: None, + bot_is_member: false, + } + } + + /// Setter for `chat_is_forum` field. + #[must_use] + pub fn chat_is_forum(mut self, value: bool) -> Self { + self.chat_is_forum = Some(value); + self + } + + /// Setter for `chat_has_username` field. + #[must_use] + pub fn chat_has_username(mut self, value: bool) -> Self { + self.chat_has_username = Some(value); + self + } + + /// Setter for `chat_is_created` field. + #[must_use] + pub fn chat_is_created(mut self, value: bool) -> Self { + self.chat_is_created = Some(value); + self + } + + /// Request a chat where the user has the specified administrator rights. + #[must_use] + pub fn user_administrator_rights(mut self, rights: ChatAdministratorRights) -> Self { + self.user_administrator_rights = Some(rights); + self + } + + /// Request a chat where the bot has the specified administrator rights. + #[must_use] + pub fn bot_administrator_rights(mut self, rights: ChatAdministratorRights) -> Self { + self.bot_administrator_rights = Some(rights); + self + } + + /// Setter for `bot_is_member` field. + #[must_use] + pub fn bot_is_member(mut self, value: bool) -> Self { + self.bot_is_member = value; + self + } +} diff --git a/crates/teloxide-core/src/types/keyboard_button_request_user.rs b/crates/teloxide-core/src/types/keyboard_button_request_user.rs new file mode 100644 index 00000000..843b86d1 --- /dev/null +++ b/crates/teloxide-core/src/types/keyboard_button_request_user.rs @@ -0,0 +1,44 @@ +use serde::{Deserialize, Serialize}; + +/// This object defines the criteria used to request a suitable user. The +/// identifier of the selected user will be shared with the bot when the +/// corresponding button is pressed. More about requesting users » +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct KeyboardButtonRequestUser { + /// identifier of the request, which will be received back in the + /// [`UserShared`] object. Must be unique within the message. + /// + /// [`UserShared`]: crate::types::UserShared + pub request_id: i32, + + /// Pass `true` to request a bot, pass `false` to request a regular user. If + /// not specified, no additional restrictions are applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_is_bot: Option, + + /// Pass `true` to request a premium user, pass `false` to request a + /// non-premium user. If not specified, no additional restrictions are + /// applied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_is_premium: Option, +} + +impl KeyboardButtonRequestUser { + /// Creates a new [`KeyboardButtonRequestUser`]. + pub fn new(request_id: i32) -> Self { + Self { request_id, user_is_bot: None, user_is_premium: None } + } + + /// Setter for `user_is_bot` field + pub fn user_is_bot(mut self, value: bool) -> Self { + self.user_is_bot = Some(value); + self + } + + /// Setter for `user_is_premium` field + pub fn user_is_premium(mut self, value: bool) -> Self { + self.user_is_premium = Some(value); + self + } +} diff --git a/crates/teloxide-core/src/types/message.rs b/crates/teloxide-core/src/types/message.rs index 781aab72..d988fad5 100644 --- a/crates/teloxide-core/src/types/message.rs +++ b/crates/teloxide-core/src/types/message.rs @@ -5,12 +5,12 @@ use serde::{Deserialize, Serialize}; use url::Url; use crate::types::{ - Animation, Audio, BareChatId, Chat, ChatId, Contact, Dice, Document, ForumTopicClosed, - ForumTopicCreated, ForumTopicEdited, ForumTopicReopened, Game, GeneralForumTopicHidden, - GeneralForumTopicUnhidden, InlineKeyboardMarkup, Invoice, Location, + Animation, Audio, BareChatId, Chat, ChatId, ChatShared, Contact, Dice, Document, + ForumTopicClosed, ForumTopicCreated, ForumTopicEdited, ForumTopicReopened, Game, + GeneralForumTopicHidden, GeneralForumTopicUnhidden, InlineKeyboardMarkup, Invoice, Location, MessageAutoDeleteTimerChanged, MessageEntity, MessageEntityRef, MessageId, PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker, SuccessfulPayment, ThreadId, True, User, - Venue, Video, VideoChatEnded, VideoChatParticipantsInvited, VideoChatScheduled, + UserShared, Venue, Video, VideoChatEnded, VideoChatParticipantsInvited, VideoChatScheduled, VideoChatStarted, VideoNote, Voice, WebAppData, WriteAccessAllowed, }; @@ -60,6 +60,8 @@ pub enum MessageKind { ChannelChatCreated(MessageChannelChatCreated), MessageAutoDeleteTimerChanged(MessageMessageAutoDeleteTimerChanged), Pinned(MessagePinned), + ChatShared(MessageChatShared), + UserShared(MessageUserShared), Invoice(MessageInvoice), SuccessfulPayment(MessageSuccessfulPayment), ConnectedWebsite(MessageConnectedWebsite), @@ -247,6 +249,18 @@ pub struct MessagePinned { pub pinned: Box, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct MessageChatShared { + /// A chat was shared with the bot. + pub chat_shared: ChatShared, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct MessageUserShared { + /// A chat was shared with the bot. + pub user_shared: UserShared, +} + #[serde_with_macros::skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct MessageInvoice { @@ -655,12 +669,13 @@ mod getters { self, message::MessageKind::*, Chat, ChatId, ChatMigration, Forward, ForwardedFrom, MediaAnimation, MediaAudio, MediaContact, MediaDocument, MediaGame, MediaKind, MediaLocation, MediaPhoto, MediaPoll, MediaSticker, MediaText, MediaVenue, MediaVideo, - MediaVideoNote, MediaVoice, Message, MessageChannelChatCreated, MessageCommon, - MessageConnectedWebsite, MessageDeleteChatPhoto, MessageDice, MessageEntity, + MediaVideoNote, MediaVoice, Message, MessageChannelChatCreated, MessageChatShared, + MessageCommon, MessageConnectedWebsite, MessageDeleteChatPhoto, MessageDice, MessageEntity, MessageGroupChatCreated, MessageId, MessageInvoice, MessageLeftChatMember, MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, MessagePinned, MessageProximityAlertTriggered, MessageSuccessfulPayment, - MessageSupergroupChatCreated, MessageVideoChatParticipantsInvited, PhotoSize, User, + MessageSupergroupChatCreated, MessageUserShared, MessageVideoChatParticipantsInvited, + PhotoSize, User, }; use super::{ @@ -1269,6 +1284,22 @@ mod getters { } } + #[must_use] + pub fn shared_chat(&self) -> Option<&types::ChatShared> { + match &self.kind { + ChatShared(MessageChatShared { chat_shared }) => Some(chat_shared), + _ => None, + } + } + + #[must_use] + pub fn shared_user(&self) -> Option<&types::UserShared> { + match &self.kind { + UserShared(MessageUserShared { user_shared }) => Some(user_shared), + _ => None, + } + } + #[must_use] pub fn dice(&self) -> Option<&types::Dice> { match &self.kind { @@ -1699,6 +1730,56 @@ mod tests { assert!(message.is_ok()); } + #[test] + fn de_shared_chat() { + let json = r#"{ + "message_id": 198283, + "chat": { + "id": 250918540, + "first_name": "Андрей", + "last_name": "Власов", + "username": "aka_dude", + "type": "private" + }, + "date": 1567927221, + "chat_shared": { + "request_id": 348349, + "chat_id": 384939 + } + }"#; + let message = from_str::(json); + assert!(message.is_ok()); + assert_eq!( + message.unwrap(), + Message { + id: MessageId(198283), + thread_id: None, + date: chrono::NaiveDateTime::from_timestamp_opt(1567927221, 0).unwrap().and_utc(), + chat: Chat { + id: ChatId(250918540), + kind: ChatKind::Private(ChatPrivate { + first_name: Some("Андрей".to_string()), + last_name: Some("Власов".to_string()), + username: Some("aka_dude".to_string()), + bio: None, + emoji_status_custom_emoji_id: None, + has_private_forwards: None, + has_restricted_voice_and_video_messages: None + }), + photo: None, + has_aggressive_anti_spam_enabled: false, + pinned_message: None, + message_auto_delete_time: None, + has_hidden_members: false + }, + kind: MessageKind::ChatShared(MessageChatShared { + chat_shared: ChatShared { request_id: 348349, chat_id: ChatId(384939) } + }), + via_bot: None + } + ); + } + #[test] fn de_media_group_forwarded() { let json = r#"{ diff --git a/crates/teloxide-core/src/types/user_shared.rs b/crates/teloxide-core/src/types/user_shared.rs new file mode 100644 index 00000000..1fd0cefe --- /dev/null +++ b/crates/teloxide-core/src/types/user_shared.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; + +use crate::types::UserId; + +/// Information about the chat whose identifier was shared with the bot using a +/// [`KeyboardButtonRequestUser`] button. +/// +/// [`KeyboardButtonRequestUser`]: crate::types::KeyboardButtonRequestUser +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct UserShared { + /// Identifier of the request. + pub request_id: i32, + /// Identifier of the shared user. + pub user_id: UserId, +}