Merge pull request #80 from teloxide/private_url

Add `Chat` getters, misssing field and improve `Message::url`
This commit is contained in:
Hirrolot 2021-05-04 10:34:53 -07:00 committed by GitHub
commit 893bd50870
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 219 additions and 28 deletions

View file

@ -10,13 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- `impl Clone` for {`CacheMe`, `DefaultParseMode`, `Throttle`} ([#75][pr75])
- Getters for fields nested in `Chat` ([#80][pr80])
- API errors: `ApiError::NotEnoughRightsToManagePins`, `ApiError::BotKickedFromSupergroup` ([#84][pr84])
[pr75]: https://github.com/teloxide/teloxide-core/pull/75
[pr80]: https://github.com/teloxide/teloxide-core/pull/80
[pr84]: https://github.com/teloxide/teloxide-core/pull/84
### Changed
- `Message::url` now returns links to messages in private groups too ([#80][pr80])
- Refactor `ChatMember` methods ([#74][pr74])
- impl `Deref<Target = ChatMemberKind>` to make `ChatMemberKind`'s methods callible directly on `ChatMember`
- Add `ChatMemberKind::is_{creator,administrator,member,restricted,left,kicked}` which check `kind` along with `is_privileged` and `is_in_chat` which combine some of the above.
@ -26,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Type of `PublicChatSupergroup::slow_mode_delay` field: `Option<i32>`=> `Option<u32>` ([#80][pr80])
- Add missing `Chat::message_auto_delete_time` field ([#80][pr80])
- Output types of `LeaveChat` `PinChatMessage`, `SetChatDescription`, `SetChatPhoto` `SetChatTitle`, `UnpinAllChatMessages` and `UnpinChatMessage`: `String` => `True` ([#79][pr79])
- `SendChatAction` output type `Message` => `True` ([#75][pr75])
- `GetChatAdministrators` output type `ChatMember` => `Vec<ChatMember>` ([#73][pr73])

View file

@ -28,6 +28,12 @@ pub struct Chat {
///
/// [`GetChat`]: crate::payloads::GetChat
pub pinned_message: Option<Box<Message>>,
/// The time after which all messages sent to the chat will be automatically
/// deleted; in seconds. Returned only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub message_auto_delete_time: Option<u32>,
}
#[serde_with_macros::skip_serializing_none]
@ -153,7 +159,7 @@ pub struct PublicChatSupergroup {
/// unpriviledged user. Returned only from [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub slow_mode_delay: Option<i32>,
pub slow_mode_delay: Option<u32>,
/// Unique identifier for the linked chat, i.e. the discussion group
/// identifier for a channel and vice versa. Returned only in [`GetChat`].
@ -199,6 +205,7 @@ impl Chat {
pub fn is_private(&self) -> bool {
matches!(self.kind, ChatKind::Private(_))
}
pub fn is_group(&self) -> bool {
matches!(
self.kind,
@ -208,6 +215,7 @@ impl Chat {
})
)
}
pub fn is_supergroup(&self) -> bool {
matches!(
self.kind,
@ -217,6 +225,7 @@ impl Chat {
})
)
}
pub fn is_channel(&self) -> bool {
matches!(
self.kind,
@ -232,6 +241,174 @@ impl Chat {
}
}
/// Getters
impl Chat {
/// A title, for supergroups, channels and group chats.
pub fn title(&self) -> Option<&str> {
match &self.kind {
ChatKind::Public(this) => this.title.as_deref(),
_ => None,
}
}
/// A username, for private chats, supergroups and channels if available.
pub fn username(&self) -> Option<&str> {
match &self.kind {
ChatKind::Public(this) => match &this.kind {
PublicChatKind::Channel(PublicChatChannel { username, .. })
| PublicChatKind::Supergroup(PublicChatSupergroup { username, .. }) => {
username.as_deref()
}
PublicChatKind::Group(_) => None,
},
ChatKind::Private(this) => this.username.as_deref(),
}
}
/// Unique identifier for the linked chat, i.e. the discussion group
/// identifier for a channel and vice versa. Returned only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn linked_chat_id(&self) -> Option<i64> {
match &self.kind {
ChatKind::Public(this) => match &this.kind {
PublicChatKind::Channel(PublicChatChannel { linked_chat_id, .. })
| PublicChatKind::Supergroup(PublicChatSupergroup { linked_chat_id, .. }) => {
*linked_chat_id
}
PublicChatKind::Group(_) => None,
},
_ => None,
}
}
/// A default chat member permissions, for groups and supergroups. Returned
/// only from [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn permissions(&self) -> Option<ChatPermissions> {
if let ChatKind::Public(this) = &self.kind {
if let PublicChatKind::Group(PublicChatGroup { permissions })
| PublicChatKind::Supergroup(PublicChatSupergroup { permissions, .. }) = &this.kind
{
return *permissions;
}
}
None
}
/// For supergroups, name of group sticker set. Returned only from
/// [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn sticker_set_name(&self) -> Option<&str> {
if let ChatKind::Public(this) = &self.kind {
if let PublicChatKind::Supergroup(this) = &this.kind {
return this.sticker_set_name.as_deref();
}
}
None
}
/// `true`, if the bot can change the group sticker set. Returned only
/// from [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn can_set_sticker_set(&self) -> Option<bool> {
if let ChatKind::Public(this) = &self.kind {
if let PublicChatKind::Supergroup(this) = &this.kind {
return this.can_set_sticker_set;
}
}
None
}
/// The minimum allowed delay between consecutive messages sent by each
/// unpriviledged user. Returned only from [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn slow_mode_delay(&self) -> Option<u32> {
if let ChatKind::Public(this) = &self.kind {
if let PublicChatKind::Supergroup(this) = &this.kind {
return this.slow_mode_delay;
}
}
None
}
/// The location to which the supergroup is connected. Returned only in
/// [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn location(&self) -> Option<&ChatLocation> {
if let ChatKind::Public(this) = &self.kind {
if let PublicChatKind::Supergroup(this) = &this.kind {
return this.location.as_ref();
}
}
None
}
/// A description, for groups, supergroups and channel chats. Returned
/// only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn description(&self) -> Option<&str> {
match &self.kind {
ChatKind::Public(this) => this.description.as_deref(),
_ => None,
}
}
/// 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
/// [`ExportChatInviteLink`]. Returned only in
/// [`GetChat`].
///
/// [`ExportChatInviteLink`]:
/// crate::payloads::ExportChatInviteLink
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn invite_link(&self) -> Option<&str> {
match &self.kind {
ChatKind::Public(this) => this.invite_link.as_deref(),
_ => None,
}
}
/// A first name of the other party in a private chat.
pub fn first_name(&self) -> Option<&str> {
match &self.kind {
ChatKind::Private(this) => this.first_name.as_deref(),
_ => None,
}
}
/// A last name of the other party in a private chat.
pub fn last_name(&self) -> Option<&str> {
match &self.kind {
ChatKind::Private(this) => this.last_name.as_deref(),
_ => None,
}
}
/// Bio of the other party in a private chat. Returned only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn bio(&self) -> Option<&str> {
match &self.kind {
ChatKind::Private(this) => this.bio.as_deref(),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use serde_json::from_str;
@ -253,6 +430,7 @@ mod tests {
}),
photo: None,
pinned_message: None,
message_auto_delete_time: None,
};
let actual = from_str(r#"{"id":-1,"type":"channel","username":"channelname"}"#).unwrap();
assert_eq!(expected, actual);
@ -272,6 +450,7 @@ mod tests {
}),
photo: None,
pinned_message: None,
message_auto_delete_time: None,
},
from_str(r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#)
.unwrap()

View file

@ -3,12 +3,10 @@
use serde::{Deserialize, Serialize};
use crate::types::{
chat::{ChatKind, PublicChatKind},
Animation, Audio, Chat, ChatPublic, Contact, Dice, Document, Game, InlineKeyboardMarkup,
Invoice, Location, MessageAutoDeleteTimerChanged, MessageEntity, PassportData, PhotoSize, Poll,
ProximityAlertTriggered, PublicChatChannel, PublicChatSupergroup, Sticker, SuccessfulPayment,
True, User, Venue, Video, VideoNote, Voice, VoiceChatEnded, VoiceChatParticipantsInvited,
VoiceChatStarted,
Animation, Audio, Chat, Contact, Dice, Document, Game, InlineKeyboardMarkup, Invoice, Location,
MessageAutoDeleteTimerChanged, MessageEntity, PassportData, PhotoSize, Poll,
ProximityAlertTriggered, Sticker, SuccessfulPayment, True, User, Venue, Video, VideoNote,
Voice, VoiceChatEnded, VoiceChatParticipantsInvited, VoiceChatStarted,
};
/// This object represents a message.
@ -982,29 +980,37 @@ mod getters {
}
impl Message {
/// Produces a direct link to the message.
///
/// Note that for private groups the link will only be accesible for group
/// members.
///
/// Returns `None` for private chats (i.e.: DMs).
pub fn url(&self) -> Option<reqwest::Url> {
match &self.chat.kind {
ChatKind::Public(ChatPublic {
kind:
PublicChatKind::Channel(PublicChatChannel {
username: Some(username),
..
}),
..
})
| ChatKind::Public(ChatPublic {
kind:
PublicChatKind::Supergroup(PublicChatSupergroup {
username: Some(username),
..
}),
..
}) => Some(
reqwest::Url::parse(format!("https://t.me/{0}/{1}/", username, self.id).as_str())
.unwrap(),
),
_ => None,
if self.chat.is_private() {
// For private chats (i.e.: DMs) we can't produce "normal" t.me link.
//
// There are "tg://openmessage?user_id={0}&message_id={1}" links, which are
// supposed to open any chat, including private messages, but they
// are only supported by some telegram clients (e.g. Plus Messenger,
// Telegram for Androud 4.9+).
return None;
}
let url = match self.chat.username() {
// If it's public group (i.e. not DM, not private group), we can produce
// "normal" t.me link (accesible to everyone).
Some(username) => format!("https://t.me/{0}/{1}/", username, self.id),
// For private groups we produce "private" t.me/c links. These are only
// accesible to the group members.
None => format!("https://t.me/c/{0}/{1}/", self.chat.id, self.id),
};
// UNWRAP:
//
// The `url` produced by formatting is correct since username is
// /[a-zA-Z0-9_]{5,32}/ and chat/message ids are integers.
Some(reqwest::Url::parse(&url).unwrap())
}
}

View file

@ -186,6 +186,7 @@ mod test {
}),
photo: None,
pinned_message: None,
message_auto_delete_time: None,
},
kind: MessageKind::Common(MessageCommon {
from: Some(User {