grammers_client/types/peer/
user.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use grammers_session::defs::PeerAuth;
10use grammers_tl_types as tl;
11use std::fmt;
12
13/// Platform Identifier.
14#[non_exhaustive]
15pub enum Platform {
16    All,
17    Android,
18    Ios,
19    WindowsPhone,
20    Other(String),
21}
22
23/// Contains the reason why a certain user is restricted.
24pub struct RestrictionReason {
25    pub platforms: Vec<Platform>,
26    pub reason: String,
27    pub text: String,
28}
29
30impl RestrictionReason {
31    pub fn from_raw(reason: &tl::enums::RestrictionReason) -> Self {
32        let tl::enums::RestrictionReason::Reason(reason) = reason;
33        Self {
34            platforms: reason
35                .platform
36                .split('-')
37                .map(|p| match p {
38                    // Taken from https://core.telegram.org/constructor/restrictionReason
39                    "all" => Platform::All,
40                    "android" => Platform::Android,
41                    "ios" => Platform::Ios,
42                    "wp" => Platform::WindowsPhone,
43                    o => Platform::Other(o.to_string()),
44                })
45                .collect(),
46            reason: reason.reason.to_string(),
47            text: reason.text.to_string(),
48        }
49    }
50}
51
52/// A user.
53///
54/// Users include your contacts, members of a group, bot accounts created by [@BotFather], or
55/// anyone with a Telegram account.
56///
57/// A "normal" (non-bot) user may also behave like a "bot" without actually being one, for
58/// example, when controlled with a program as opposed to being controlled by a human through
59/// a Telegram application. These are commonly known as "userbots", and some people use them
60/// to enhance their Telegram experience (for example, creating "commands" so that the program
61/// automatically reacts to them, like translating messages).
62///
63/// [@BotFather]: https://t.me/BotFather
64#[derive(Clone)]
65pub struct User {
66    pub raw: tl::enums::User,
67}
68
69impl fmt::Debug for User {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        self.raw.fmt(f)
72    }
73}
74
75// TODO: photo
76impl User {
77    pub fn from_raw(user: tl::enums::User) -> Self {
78        Self { raw: user }
79    }
80
81    pub(crate) fn user(&self) -> Option<&tl::types::User> {
82        match &self.raw {
83            tl::enums::User::User(u) => Some(u),
84            tl::enums::User::Empty(_) => None,
85        }
86    }
87
88    /// Return the user presence status (also known as "last seen").
89    pub fn status(&self) -> &grammers_tl_types::enums::UserStatus {
90        self.user()
91            .and_then(|u| u.status.as_ref())
92            .unwrap_or(&grammers_tl_types::enums::UserStatus::Empty)
93    }
94
95    /// Return the unique identifier for this user.
96    pub fn bare_id(&self) -> i64 {
97        self.raw.id()
98    }
99
100    pub(crate) fn auth(&self) -> PeerAuth {
101        self.user()
102            .and_then(|u| u.access_hash)
103            .map(PeerAuth::from_hash)
104            .unwrap_or_default()
105    }
106
107    /// Return the first name of this user.
108    ///
109    /// The name will be `None` if the account was deleted. It may also be `None` if you received
110    /// it previously.
111    pub fn first_name(&self) -> Option<&str> {
112        self.user().and_then(|u| u.first_name.as_deref())
113    }
114
115    /// Return the last name of this user, if any.
116    pub fn last_name(&self) -> Option<&str> {
117        self.user().and_then(|u| {
118            u.last_name
119                .as_deref()
120                .and_then(|name| if name.is_empty() { None } else { Some(name) })
121        })
122    }
123
124    /// Return the full name of this user.
125    ///
126    /// This is equal to the user's first name concatenated with the user's last name, if this
127    /// is not empty. Otherwise, it equals the user's first name.
128    pub fn full_name(&self) -> String {
129        let first_name = self.first_name().unwrap_or_default();
130        if let Some(last_name) = self.last_name() {
131            let mut name = String::with_capacity(first_name.len() + 1 + last_name.len());
132            name.push_str(first_name);
133            name.push(' ');
134            name.push_str(last_name);
135            name
136        } else {
137            first_name.to_string()
138        }
139    }
140
141    /// Return the public @username of this user, if any.
142    ///
143    /// The returned username does not contain the "@" prefix.
144    ///
145    /// Outside of the application, people may link to this user with one of Telegram's URLs, such
146    /// as https://t.me/username.
147    pub fn username(&self) -> Option<&str> {
148        self.user().and_then(|u| u.username.as_deref())
149    }
150
151    /// Return collectible usernames of this user, if any.
152    ///
153    /// The returned usernames do not contain the "@" prefix.
154    ///
155    /// Outside of the application, people may link to this user with one of its username, such
156    /// as https://t.me/username.
157    pub fn usernames(&self) -> Vec<&str> {
158        self.user()
159            .and_then(|u| u.usernames.as_deref())
160            .map_or(Vec::new(), |usernames| {
161                usernames
162                    .iter()
163                    .map(|username| match username {
164                        tl::enums::Username::Username(username) => username.username.as_ref(),
165                    })
166                    .collect()
167            })
168    }
169
170    /// Return the phone number of this user, if they are not a bot and their privacy settings
171    /// allow you to see it.
172    pub fn phone(&self) -> Option<&str> {
173        self.user().and_then(|u| u.phone.as_deref())
174    }
175
176    /// Return the photo of this user, if any.
177    pub fn photo(&self) -> Option<&tl::types::UserProfilePhoto> {
178        match self.user().and_then(|u| u.photo.as_ref()) {
179            Some(maybe_photo) => match maybe_photo {
180                tl::enums::UserProfilePhoto::Empty => None,
181                tl::enums::UserProfilePhoto::Photo(photo) => Some(photo),
182            },
183            None => None,
184        }
185    }
186
187    /// Does this user represent the account that's currently logged in?
188    pub fn is_self(&self) -> bool {
189        // TODO if is_self is false, check in peer cache if id == ourself
190        self.user().map(|u| u.is_self).unwrap_or(false)
191    }
192
193    /// Is this user in your account's contact list?
194    pub fn contact(&self) -> bool {
195        self.user().map(|u| u.contact).unwrap_or(false)
196    }
197
198    /// Is this user a mutual contact?
199    ///
200    /// Contacts are mutual if both the user of the current account and this user have eachother
201    /// in their respective contact list.
202    pub fn mutual_contact(&self) -> bool {
203        self.user().map(|u| u.mutual_contact).unwrap_or(false)
204    }
205
206    /// Has the account of this user been deleted?
207    pub fn deleted(&self) -> bool {
208        self.user().map(|u| u.deleted).unwrap_or(false)
209    }
210
211    /// Is the current account a bot?
212    ///
213    /// Bot accounts are those created by [@BotFather](https://t.me/BotFather).
214    pub fn is_bot(&self) -> bool {
215        self.user().map(|u| u.bot).unwrap_or(false)
216    }
217
218    /// If the current user is a bot, does it have [privacy mode] enabled?
219    ///
220    /// * Bots with privacy enabled won't see messages in groups unless they are replied or the
221    ///   command includes their name (`/command@bot`).
222    /// * Bots with privacy disabled will be able to see all messages in a group.
223    ///
224    /// [privacy mode]: https://core.telegram.org/bots#privacy-mode
225    pub fn bot_privacy(&self) -> bool {
226        self.user().map(|u| !u.bot_chat_history).unwrap_or(false)
227    }
228
229    /// If the current user is a bot, can it be added to groups?
230    pub fn bot_supports_chats(self) -> bool {
231        self.user().map(|u| u.bot_nochats).unwrap_or(false)
232    }
233
234    /// Has the account of this user been verified?
235    ///
236    /// Verified accounts, such as [@BotFather](https://t.me/BotFather), have a special icon next
237    /// to their names in official applications (commonly a blue starred checkmark).
238    pub fn verified(&self) -> bool {
239        self.user().map(|u| u.verified).unwrap_or(false)
240    }
241
242    /// Does this user have restrictions applied to their account?
243    pub fn restricted(&self) -> bool {
244        self.user().map(|u| u.restricted).unwrap_or(false)
245    }
246
247    /// If the current user is a bot, does it want geolocation information on inline queries?
248    pub fn bot_inline_geo(&self) -> bool {
249        self.user().map(|u| u.bot_inline_geo).unwrap_or(false)
250    }
251
252    /// Is this user an official member of the support team?
253    pub fn support(&self) -> bool {
254        self.user().map(|u| u.support).unwrap_or(false)
255    }
256
257    /// Has this user been flagged for trying to scam other people?
258    pub fn scam(&self) -> bool {
259        self.user().map(|u| u.scam).unwrap_or(false)
260    }
261
262    /// The reason(s) why this user is restricted, could be empty.
263    pub fn restriction_reason(&self) -> Vec<RestrictionReason> {
264        if let Some(reasons) = self.user().and_then(|u| u.restriction_reason.as_ref()) {
265            reasons.iter().map(RestrictionReason::from_raw).collect()
266        } else {
267            Vec::new()
268        }
269    }
270
271    /// Return the placeholder for inline queries if the current user is a bot and has said
272    /// placeholder configured.
273    pub fn bot_inline_placeholder(&self) -> Option<&str> {
274        self.user()
275            .and_then(|u| u.bot_inline_placeholder.as_deref())
276    }
277
278    /// Language code of the user, if any.
279    pub fn lang_code(&self) -> Option<&str> {
280        self.user().and_then(|u| u.lang_code.as_deref())
281    }
282}