diff --git a/src/adaptors/erased.rs b/src/adaptors/erased.rs index a951f037..6dd77a1b 100644 --- a/src/adaptors/erased.rs +++ b/src/adaptors/erased.rs @@ -386,13 +386,13 @@ trait ErasableRequester<'a> { fn ban_chat_sender_chat( &self, chat_id: Recipient, - sender_chat_id: i64, + sender_chat_id: ChatId, ) -> ErasedRequest<'a, BanChatSenderChat, Self::Err>; fn unban_chat_sender_chat( &self, chat_id: Recipient, - sender_chat_id: i64, + sender_chat_id: ChatId, ) -> ErasedRequest<'a, UnbanChatSenderChat, Self::Err>; fn set_chat_permissions( @@ -978,7 +978,7 @@ where fn ban_chat_sender_chat( &self, chat_id: Recipient, - sender_chat_id: i64, + sender_chat_id: ChatId, ) -> ErasedRequest<'a, BanChatSenderChat, Self::Err> { Requester::ban_chat_sender_chat(self, chat_id, sender_chat_id).erase() } @@ -986,7 +986,7 @@ where fn unban_chat_sender_chat( &self, chat_id: Recipient, - sender_chat_id: i64, + sender_chat_id: ChatId, ) -> ErasedRequest<'a, UnbanChatSenderChat, Self::Err> { Requester::unban_chat_sender_chat(self, chat_id, sender_chat_id).erase() } diff --git a/src/adaptors/throttle.rs b/src/adaptors/throttle.rs index 8c03535f..908697a0 100644 --- a/src/adaptors/throttle.rs +++ b/src/adaptors/throttle.rs @@ -638,14 +638,14 @@ download_forward! { /// usernames. (It is just a hashed username.) #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] enum ChatIdHash { - Id(i64), + Id(ChatId), ChannelUsernameHash(u64), } impl ChatIdHash { fn is_channel(&self) -> bool { match self { - &Self::Id(id) => Recipient::Id(id).is_channel(), + &Self::Id(id) => id.is_channel(), Self::ChannelUsernameHash(_) => true, } } diff --git a/src/bot/api.rs b/src/bot/api.rs index 4383e536..0fdb8a51 100644 --- a/src/bot/api.rs +++ b/src/bot/api.rs @@ -5,8 +5,8 @@ use crate::{ prelude::Requester, requests::{JsonRequest, MultipartRequest}, types::{ - BotCommand, ChatPermissions, InlineQueryResult, InputFile, InputMedia, InputSticker, - LabeledPrice, Recipient, UserId, + BotCommand, ChatId, ChatPermissions, InlineQueryResult, InputFile, InputMedia, + InputSticker, LabeledPrice, Recipient, UserId, }, Bot, }; @@ -401,7 +401,7 @@ impl Requester for Bot { type BanChatSenderChat = JsonRequest; - fn ban_chat_sender_chat(&self, chat_id: C, sender_chat_id: i64) -> Self::BanChatSenderChat + fn ban_chat_sender_chat(&self, chat_id: C, sender_chat_id: ChatId) -> Self::BanChatSenderChat where C: Into, { @@ -416,7 +416,7 @@ impl Requester for Bot { fn unban_chat_sender_chat( &self, chat_id: C, - sender_chat_id: i64, + sender_chat_id: ChatId, ) -> Self::UnbanChatSenderChat where C: Into, diff --git a/src/lib.rs b/src/lib.rs index 1f222b6b..71e89323 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ //! ``` //! # #[cfg(feature = "auto_send")] //! # async { -//! # let chat_id = 0; +//! # let chat_id = teloxide_core::types::ChatId(-1); //! use teloxide_core::{ //! prelude::*, //! types::{DiceEmoji, ParseMode}, diff --git a/src/local_macros.rs b/src/local_macros.rs index e31876b8..43f26711 100644 --- a/src/local_macros.rs +++ b/src/local_macros.rs @@ -724,17 +724,17 @@ macro_rules! requester_forward { (@method ban_chat_sender_chat $body:ident $ty:ident) => { type BanChatSenderChat = $ty![BanChatSenderChat]; - fn ban_chat_sender_chat(&self, chat_id: C, sender_chat_id: i64) -> Self::BanChatSenderChat where C: Into { + fn ban_chat_sender_chat(&self, chat_id: C, sender_chat_id: ChatId) -> Self::BanChatSenderChat where C: Into { let this = self; - $body!(ban_chat_sender_chat this (chat_id: C, sender_chat_id: i64)) + $body!(ban_chat_sender_chat this (chat_id: C, sender_chat_id: ChatId)) } }; (@method unban_chat_sender_chat $body:ident $ty:ident) => { type UnbanChatSenderChat = $ty![UnbanChatSenderChat]; - fn unban_chat_sender_chat(&self, chat_id: C, sender_chat_id: i64) -> Self::UnbanChatSenderChat where C: Into { + fn unban_chat_sender_chat(&self, chat_id: C, sender_chat_id: ChatId) -> Self::UnbanChatSenderChat where C: Into { let this = self; - $body!(unban_chat_sender_chat this (chat_id: C, sender_chat_id: i64)) + $body!(unban_chat_sender_chat this (chat_id: C, sender_chat_id: ChatId)) } }; (@method set_chat_permissions $body:ident $ty:ident) => { diff --git a/src/payloads/ban_chat_sender_chat.rs b/src/payloads/ban_chat_sender_chat.rs index 3aaf7863..dd1d43ef 100644 --- a/src/payloads/ban_chat_sender_chat.rs +++ b/src/payloads/ban_chat_sender_chat.rs @@ -8,7 +8,7 @@ // [`schema`]: https://github.com/WaffleLapkin/tg-methods-schema use serde::Serialize; -use crate::types::{Recipient, True}; +use crate::types::{ChatId, Recipient, True}; impl_payload! { /// Use this method to ban a channel chat in a supergroup or a channel. The owner of the chat will not be able to send messages and join live streams on behalf of the chat, unless it is unbanned first. The bot must be an administrator in the supergroup or channel for this to work and must have the appropriate administrator rights. @@ -18,7 +18,7 @@ impl_payload! { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) pub chat_id: Recipient [into], /// Unique identifier of the target sender chat - pub sender_chat_id: i64, + pub sender_chat_id: ChatId, } } } diff --git a/src/payloads/unban_chat_sender_chat.rs b/src/payloads/unban_chat_sender_chat.rs index fb884acc..b4ec78f2 100644 --- a/src/payloads/unban_chat_sender_chat.rs +++ b/src/payloads/unban_chat_sender_chat.rs @@ -8,7 +8,7 @@ // [`schema`]: https://github.com/WaffleLapkin/tg-methods-schema use serde::Serialize; -use crate::types::{Recipient, True}; +use crate::types::{ChatId, Recipient, True}; impl_payload! { /// Use this method to unban a previously banned channel chat in a supergroup or channel. The bot must be an administrator for this to work and must have the appropriate administrator rights. @@ -18,7 +18,7 @@ impl_payload! { /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) pub chat_id: Recipient [into], /// Unique identifier of the target sender chat - pub sender_chat_id: i64, + pub sender_chat_id: ChatId, } } } diff --git a/src/requests/request.rs b/src/requests/request.rs index 0dd99e86..ee91232b 100644 --- a/src/requests/request.rs +++ b/src/requests/request.rs @@ -75,12 +75,12 @@ pub trait Request: HasPayload { /// ## Examples /// ``` /// # async { - /// use teloxide_core::{prelude::*, requests::Request, Bot}; + /// use teloxide_core::{prelude::*, requests::Request, types::ChatId, Bot}; /// /// let bot = Bot::new("TOKEN"); - /// # let chat_ids = vec![1i64, 2, 3, 4].into_iter().map(Into::into); + /// # let chat_ids = vec![1i64, 2, 3, 4].into_iter().map(ChatId).map(Into::into).collect::>(); /// - /// let mut req = bot.send_message(0, "Hi there!"); + /// let mut req = bot.send_message(ChatId(0xAAAAAAAA), "Hi there!"); /// for chat_id in chat_ids { /// req.chat_id = chat_id; /// req.send_ref().await.unwrap(); diff --git a/src/requests/requester.rs b/src/requests/requester.rs index 7df85bee..1cde022e 100644 --- a/src/requests/requester.rs +++ b/src/requests/requester.rs @@ -6,10 +6,7 @@ use url::Url; use crate::{ payloads::{GetMe, SendMessage, *}, requests::Request, - types::{ - BotCommand, ChatAction, ChatPermissions, InlineQueryResult, InputFile, InputMedia, - InputSticker, LabeledPrice, PassportElementError, Recipient, TargetMessage, UserId, - }, + types::*, }; /// Methods for building requests. @@ -22,27 +19,35 @@ use crate::{ /// /// ``` /// # async { -/// use teloxide_core::{prelude::*, types::ParseMode}; +/// # let chat_id = ChatId(-1); +/// use teloxide_core::{ +/// prelude::*, +/// types::{ChatId, ParseMode}, +/// }; /// /// // Bot implements `Requester` /// let bot = Bot::new("TOKEN"); /// /// // Required parameters are supplied to the `Requester` methods: -/// bot.send_message(0, "Text") +/// bot.send_message(chat_id, "Text") /// // Optional parameters can be supplied by calling setters /// .parse_mode(ParseMode::Html) /// // To send request to telegram you need to call `.send()` and await the resulting future /// .send() /// .await?; -/// # Ok::<_, teloxide_core::RequestError>(()) }; +/// # Ok::<_, teloxide_core::RequestError>(()) +/// # }; /// ``` /// /// Using `Requester` in a generic context: /// /// ``` -/// use teloxide_core::{prelude::*, types::Message}; +/// use teloxide_core::{ +/// prelude::*, +/// types::{ChatId, Message}, +/// }; /// -/// async fn send_hi(bot: R, chat: i64) -> Message +/// async fn send_hi(bot: R, chat: ChatId) -> Message /// where /// R: Requester, /// { @@ -371,7 +376,11 @@ pub trait Requester { type BanChatSenderChat: Request; /// For Telegram documentation see [`BanChatSenderChat`]. - fn ban_chat_sender_chat(&self, chat_id: C, sender_chat_id: i64) -> Self::BanChatSenderChat + fn ban_chat_sender_chat( + &self, + chat_id: C, + sender_chat_id: ChatId, + ) -> Self::BanChatSenderChat where C: Into; @@ -381,7 +390,7 @@ pub trait Requester { fn unban_chat_sender_chat( &self, chat_id: C, - sender_chat_id: i64, + sender_chat_id: ChatId, ) -> Self::UnbanChatSenderChat where C: Into; diff --git a/src/serde_multipart/mod.rs b/src/serde_multipart/mod.rs index bb279722..5d04902a 100644 --- a/src/serde_multipart/mod.rs +++ b/src/serde_multipart/mod.rs @@ -88,9 +88,9 @@ mod tests { use crate::{ payloads::{self, setters::*}, types::{ - InputFile, InputMedia, InputMediaAnimation, InputMediaAudio, InputMediaDocument, - InputMediaPhoto, InputMediaVideo, InputSticker, MessageEntity, MessageEntityKind, - ParseMode, UserId, + ChatId, InputFile, InputMedia, InputMediaAnimation, InputMediaAudio, + InputMediaDocument, InputMediaPhoto, InputMediaVideo, InputSticker, MessageEntity, + MessageEntityKind, ParseMode, UserId, }, }; @@ -98,7 +98,7 @@ mod tests { #[tokio::test] async fn issue_473() { to_form_ref( - &payloads::SendPhoto::new(0, InputFile::file_id("0")).caption_entities([ + &payloads::SendPhoto::new(ChatId(0), InputFile::file_id("0")).caption_entities([ MessageEntity { kind: MessageEntityKind::Url, offset: 0, @@ -115,7 +115,7 @@ mod tests { const CAPTION: &str = "caption"; to_form_ref(&payloads::SendMediaGroup::new( - 0, + ChatId(0), [ InputMedia::Photo( InputMediaPhoto::new(InputFile::file("./media/logo.png")) @@ -163,7 +163,7 @@ mod tests { #[tokio::test] async fn test_send_animation() { to_form_ref( - &payloads::SendAnimation::new(0, InputFile::file("./media/logo.png")) + &payloads::SendAnimation::new(ChatId(0), InputFile::file("./media/logo.png")) .caption_entities(entities()) .thumb(InputFile::read( File::open("./media/logo.png").await.unwrap(), diff --git a/src/types.rs b/src/types.rs index 230fdcaf..100b1279 100644 --- a/src/types.rs +++ b/src/types.rs @@ -220,9 +220,11 @@ mod non_telegram_types { pub(super) mod until_date; } +mod chat_id; mod recipient; mod user_id; +pub use chat_id::*; pub use recipient::*; pub use user_id::*; diff --git a/src/types/bot_command_scope.rs b/src/types/bot_command_scope.rs index 8a7042c9..e005aabb 100644 --- a/src/types/bot_command_scope.rs +++ b/src/types/bot_command_scope.rs @@ -56,13 +56,15 @@ pub enum BotCommandScope { #[test] fn issue_486() { + use crate::types::ChatId; + serde_json::to_string(&BotCommandScope::Chat { - chat_id: Recipient::Id(0), + chat_id: Recipient::Id(ChatId(0)), }) .unwrap(); serde_json::to_string(&BotCommandScope::ChatAdministrators { - chat_id: Recipient::Id(0), + chat_id: Recipient::Id(ChatId(0)), }) .unwrap(); } diff --git a/src/types/chat.rs b/src/types/chat.rs index a3e6f3fa..626b4d95 100644 --- a/src/types/chat.rs +++ b/src/types/chat.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::types::{ChatLocation, ChatPermissions, ChatPhoto, Message, True}; +use crate::types::{ChatId, ChatLocation, ChatPermissions, ChatPhoto, Message, True}; /// This object represents a chat. /// @@ -8,12 +8,8 @@ use crate::types::{ChatLocation, ChatPermissions, ChatPhoto, Message, True}; #[serde_with_macros::skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Chat { - /// A unique identifier for this chat. 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 id: i64, + /// A unique identifier for this chat. + pub id: ChatId, #[serde(flatten)] pub kind: ChatKind, @@ -454,11 +450,11 @@ mod tests { #[test] fn channel_de() { let expected = Chat { - id: -1, + id: ChatId(-1), kind: ChatKind::Public(ChatPublic { title: None, kind: PublicChatKind::Channel(PublicChatChannel { - username: Some("channelname".into()), + username: Some("channel_name".into()), linked_chat_id: None, }), description: None, @@ -469,7 +465,7 @@ mod tests { pinned_message: None, message_auto_delete_time: None, }; - let actual = from_str(r#"{"id":-1,"type":"channel","username":"channelname"}"#).unwrap(); + let actual = from_str(r#"{"id":-1,"type":"channel","username":"channel_name"}"#).unwrap(); assert_eq!(expected, actual); } @@ -477,7 +473,7 @@ mod tests { fn private_chat_de() { assert_eq!( Chat { - id: 0, + id: ChatId(0), kind: ChatKind::Private(ChatPrivate { type_: (), username: Some("username".into()), diff --git a/src/types/chat_id.rs b/src/types/chat_id.rs index 8b137891..d66c84f3 100644 --- a/src/types/chat_id.rs +++ b/src/types/chat_id.rs @@ -1 +1,78 @@ +use derive_more::Display; +use serde::{Deserialize, Serialize}; +use crate::types::UserId; + +/// Identifier of a chat. +/// +/// Note that "a chat" here means any of group, supergroup, channel or user PM. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display, Serialize, Deserialize, +)] +#[serde(transparent)] +pub struct ChatId(pub i64); + +impl ChatId { + pub(crate) fn is_channel(self) -> bool { + matches!(self.unmark(), UnmarkedChatId::Channel(_)) + } + + pub(crate) fn unmark(self) -> UnmarkedChatId { + use UnmarkedChatId::*; + + // https://github.com/mtcute/mtcute/blob/6933ecc3f82dd2e9100f52b0afec128af564713b/packages/core/src/utils/peer-utils.ts#L4 + const MIN_MARKED_CHANNEL_ID: i64 = -1997852516352; + const MAX_MARKED_CHANNEL_ID: i64 = -1000000000000; + const MIN_MARKED_CHAT_ID: i64 = MAX_MARKED_CHANNEL_ID + 1; + const MAX_MARKED_CHAT_ID: i64 = MIN_USER_ID - 1; + const MIN_USER_ID: i64 = 0; + const MAX_USER_ID: i64 = (1 << 40) - 1; + + match self.0 { + id @ MIN_MARKED_CHAT_ID..=MAX_MARKED_CHAT_ID => Group(-id as _), + id @ MIN_MARKED_CHANNEL_ID..=MAX_MARKED_CHANNEL_ID => { + Channel((MAX_MARKED_CHANNEL_ID - id) as _) + } + id @ MIN_USER_ID..=MAX_USER_ID => User(UserId(id as _)), + id => panic!("malformed chat id: {}", id), + } + } +} + +pub(crate) enum UnmarkedChatId { + User(UserId), + Group(u64), + Channel(u64), +} + +#[cfg(test)] +mod tests { + use serde::{Deserialize, Serialize}; + + use crate::types::{ChatId, UnmarkedChatId, UserId}; + + /// Test that `ChatId` is serialized as the underlying integer + #[test] + fn deser() { + let chat_id = S { + chat_id: ChatId(0xAA), + }; + let json = r#"{"chat_id":170}"#; + + #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] + struct S { + chat_id: ChatId, + } + + assert_eq!(serde_json::to_string(&chat_id).unwrap(), json); + assert_eq!(chat_id, serde_json::from_str(json).unwrap()); + } + + #[test] + fn user_id_unmark() { + assert!(matches!( + ChatId(5298363099).unmark(), + UnmarkedChatId::User(UserId(5298363099)) + )); + } +} diff --git a/src/types/message.rs b/src/types/message.rs index b7d58e88..bc680e5f 100644 --- a/src/types/message.rs +++ b/src/types/message.rs @@ -4,8 +4,8 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::types::{ - Animation, Audio, Chat, Contact, Dice, Document, Game, InlineKeyboardMarkup, Invoice, Location, - MessageAutoDeleteTimerChanged, MessageEntity, PassportData, PhotoSize, Poll, + Animation, Audio, Chat, ChatId, Contact, Dice, Document, Game, InlineKeyboardMarkup, Invoice, + Location, MessageAutoDeleteTimerChanged, MessageEntity, PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker, SuccessfulPayment, True, User, Venue, Video, VideoNote, Voice, VoiceChatEnded, VoiceChatParticipantsInvited, VoiceChatScheduled, VoiceChatStarted, }; @@ -187,14 +187,14 @@ pub enum ChatMigration { /// identifier `chat_id`. To { #[serde(rename = "migrate_to_chat_id")] - chat_id: i64, + chat_id: ChatId, }, /// The supergroup has been migrated from a group with the specified /// identifier `chat_id`. From { #[serde(rename = "migrate_from_chat_id")] - chat_id: i64, + chat_id: ChatId, }, } @@ -519,14 +519,15 @@ mod getters { use std::ops::Deref; use crate::types::{ - self, message::MessageKind::*, Chat, 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, MessageGroupChatCreated, - MessageInvoice, MessageLeftChatMember, MessageNewChatMembers, MessageNewChatPhoto, - MessageNewChatTitle, MessagePassportData, MessagePinned, MessageProximityAlertTriggered, - MessageSuccessfulPayment, MessageSupergroupChatCreated, PhotoSize, True, User, + 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, + MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageNewChatMembers, + MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, MessagePinned, + MessageProximityAlertTriggered, MessageSuccessfulPayment, MessageSupergroupChatCreated, + PhotoSize, True, User, }; /// Getters for [Message] fields from [telegram docs]. @@ -558,7 +559,7 @@ mod getters { } #[deprecated(since = "0.4.2", note = "use `.chat.id` field instead")] - pub fn chat_id(&self) -> i64 { + pub fn chat_id(&self) -> ChatId { self.chat.id } @@ -931,7 +932,7 @@ mod getters { } } - pub fn migrate_to_chat_id(&self) -> Option { + pub fn migrate_to_chat_id(&self) -> Option { match &self.kind { Common(MessageCommon { media_kind: MediaKind::Migration(ChatMigration::To { chat_id }), @@ -941,7 +942,7 @@ mod getters { } } - pub fn migrate_from_chat_id(&self) -> Option { + pub fn migrate_from_chat_id(&self) -> Option { match &self.kind { Common(MessageCommon { media_kind: MediaKind::Migration(ChatMigration::From { chat_id }), @@ -1069,7 +1070,8 @@ impl Message { // accessible to the group members. None => format!( "https://t.me/c/{0}/{1}/", - (-self.chat.id) - 1000000000000, + // FIXME: this may be wrong for private channels + (-self.chat.id.0) - 1000000000000, self.id ), }; @@ -1326,7 +1328,7 @@ mod tests { let message: Message = serde_json::from_str(json).unwrap(); let group = Chat { - id: -1001160242915, + id: ChatId(-1001160242915), kind: ChatKind::Public(ChatPublic { title: Some("a".to_owned()), kind: PublicChatKind::Supergroup(PublicChatSupergroup { @@ -1360,8 +1362,8 @@ mod tests { /// Regression test for #[test] fn issue_427() { - let old = -599075523; - let new = -1001555296434; + let old = ChatId(-599075523); + let new = ChatId(-1001555296434); // Migration to a supergroup let json = r#"{"chat":{"all_members_are_administrators":false,"id":-599075523,"title":"test","type":"group"},"date":1629404938,"from":{"first_name":"nullptr","id":729497414,"is_bot":false,"language_code":"en","username":"hex0x0000"},"message_id":16,"migrate_to_chat_id":-1001555296434}"#; diff --git a/src/types/recipient.rs b/src/types/recipient.rs index e1664448..dc719f62 100644 --- a/src/types/recipient.rs +++ b/src/types/recipient.rs @@ -1,6 +1,8 @@ use derive_more::{Display, From}; use serde::{Deserialize, Serialize}; +use crate::types::ChatId; + /// A unique identifier for the target chat or username of the target channel /// (in the format `@channelusername`). #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Display, From)] @@ -8,7 +10,7 @@ use serde::{Deserialize, Serialize}; pub enum Recipient { /// A chat identifier. #[display(fmt = "{}", _0)] - Id(i64), + Id(ChatId), /// A channel username (in the format @channelusername). #[display(fmt = "{}", _0)] @@ -16,39 +18,13 @@ pub enum Recipient { } impl Recipient { + #[allow(unused)] pub(crate) fn is_channel(&self) -> bool { - matches!(self.unmark(), None | Some(UnmarkedChatId::Channel(_))) + match self { + Recipient::Id(id) => id.is_channel(), + Recipient::ChannelUsername(_) => true, + } } - - pub(crate) fn unmark(&self) -> Option { - use UnmarkedChatId::*; - - // https://github.com/mtcute/mtcute/blob/6933ecc3f82dd2e9100f52b0afec128af564713b/packages/core/src/utils/peer-utils.ts#L4 - const MIN_MARKED_CHANNEL_ID: i64 = -1997852516352; - const MAX_MARKED_CHANNEL_ID: i64 = -1000000000000; - const MIN_MARKED_CHAT_ID: i64 = MAX_MARKED_CHANNEL_ID + 1; - const MAX_MARKED_CHAT_ID: i64 = MIN_USER_ID - 1; - const MIN_USER_ID: i64 = 0; - const MAX_USER_ID: i64 = (1 << 40) - 1; - - let res = match self { - &Self::Id(id @ MIN_MARKED_CHAT_ID..=MAX_MARKED_CHAT_ID) => Chat(-id as _), - &Self::Id(id @ MIN_MARKED_CHANNEL_ID..=MAX_MARKED_CHANNEL_ID) => { - Channel((MAX_MARKED_CHANNEL_ID - id) as _) - } - &Self::Id(id @ MIN_USER_ID..=MAX_USER_ID) => User(id as _), - &Self::Id(id) => panic!("malformed chat id: {}", id), - Self::ChannelUsername(_) => return None, - }; - - Some(res) - } -} - -pub(crate) enum UnmarkedChatId { - User(u64), - Chat(u64), - Channel(u64), } #[cfg(test)] @@ -58,7 +34,7 @@ mod tests { #[test] fn chat_id_id_serialization() { let expected_json = String::from(r#"123456"#); - let actual_json = serde_json::to_string(&Recipient::Id(123_456)).unwrap(); + let actual_json = serde_json::to_string(&Recipient::Id(ChatId(123_456))).unwrap(); assert_eq!(expected_json, actual_json) } @@ -71,12 +47,4 @@ mod tests { assert_eq!(expected_json, actual_json) } - - #[test] - fn user_id_unmark() { - assert!(matches!( - Recipient::Id(5298363099).unmark(), - Some(UnmarkedChatId::User(5298363099)) - )); - } } diff --git a/src/types/update.rs b/src/types/update.rs index 1b97031b..05a316e7 100644 --- a/src/types/update.rs +++ b/src/types/update.rs @@ -294,8 +294,8 @@ impl Serialize for UpdateKind { #[cfg(test)] mod test { use crate::types::{ - Chat, ChatKind, ChatPrivate, MediaKind, MediaText, Message, MessageCommon, MessageKind, - Update, UpdateKind, User, UserId, + Chat, ChatId, ChatKind, ChatPrivate, MediaKind, MediaText, Message, MessageCommon, + MessageKind, Update, UpdateKind, User, UserId, }; use chrono::{DateTime, NaiveDateTime, Utc}; @@ -335,7 +335,7 @@ mod test { id: 6557, date, chat: Chat { - id: 218_485_655, + id: ChatId(218_485_655), kind: ChatKind::Private(ChatPrivate { type_: (), username: Some(String::from("WaffleLapkin")),