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}