tgbot/types/definitions/
user.rs

1use std::{error::Error, fmt};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    api::{Method, Payload},
7    types::{Integer, ParseMode, PhotoSize},
8};
9
10/// Represents the date of birth of a user.
11#[serde_with::skip_serializing_none]
12#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
13pub struct Birthdate {
14    /// Day of the user's birth; 1-31
15    pub day: Integer,
16    /// Month of the user's birth; 1-12
17    pub month: Integer,
18    /// Year of the user's birth
19    pub year: Option<Integer>,
20}
21
22impl Birthdate {
23    /// Creates a new `Birthdate`.
24    ///
25    /// # Arguments
26    ///
27    /// * `day` - Day.
28    /// * `month` - Month.
29    pub fn new(day: Integer, month: Integer) -> Self {
30        Self { day, month, year: None }
31    }
32
33    /// Sets a new year.
34    ///
35    /// # Arguments
36    ///
37    /// * `year` - Year.
38    pub fn with_year(mut self, value: Integer) -> Self {
39        self.year = Some(value);
40        self
41    }
42}
43
44/// Contains information about a user that
45/// was shared with the bot using a [`crate::types::KeyboardButtonRequestUsers`] button.
46#[serde_with::skip_serializing_none]
47#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
48pub struct SharedUser {
49    /// Identifier of the shared user.
50    ///
51    /// The bot may not have access to the user and could be unable to use this identifier,
52    /// unless the user is already known to the bot by some other means.
53    pub user_id: Integer,
54    /// First name of the user, if the name was requested by the bot.
55    pub first_name: Option<String>,
56    /// Last name of the user, if the name was requested by the bot.
57    pub last_name: Option<String>,
58    /// Available sizes of the user photo, if the photo was requested by the bot
59    pub photo: Option<Vec<PhotoSize>>,
60    /// Username of the user, if the username was requested by the bot.
61    pub username: Option<String>,
62}
63
64impl SharedUser {
65    /// Creates a new `SharedUser`.
66    ///
67    /// # Arguments
68    ///
69    /// * `user_id` - Identifier of the shared user.
70    pub fn new(user_id: Integer) -> Self {
71        Self {
72            user_id,
73            first_name: None,
74            last_name: None,
75            photo: None,
76            username: None,
77        }
78    }
79
80    /// Sets a new first name.
81    ///
82    /// # Arguments
83    ///
84    /// * `value` - First name.
85    pub fn with_first_name<T>(mut self, value: T) -> Self
86    where
87        T: Into<String>,
88    {
89        self.first_name = Some(value.into());
90        self
91    }
92
93    /// Sets a new last name.
94    ///
95    /// # Arguments
96    ///
97    /// * `value` - Last name.
98    pub fn with_last_name<T>(mut self, value: T) -> Self
99    where
100        T: Into<String>,
101    {
102        self.last_name = Some(value.into());
103        self
104    }
105
106    /// Sets new photo sizes.
107    ///
108    /// # Arguments
109    ///
110    /// * `value` - Available sizes of photo.
111    pub fn with_photo<T>(mut self, value: T) -> Self
112    where
113        T: IntoIterator<Item = PhotoSize>,
114    {
115        self.photo = Some(value.into_iter().collect());
116        self
117    }
118
119    /// Sets a new username.
120    ///
121    /// # Arguments
122    ///
123    /// * `value` - Username.
124    pub fn with_username<T>(mut self, value: T) -> Self
125    where
126        T: Into<String>,
127    {
128        self.username = Some(value.into());
129        self
130    }
131}
132
133/// Represents a user.
134#[serde_with::skip_serializing_none]
135#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
136pub struct User {
137    /// First name of the user.
138    pub first_name: String,
139    /// Unique identifier of the user.
140    pub id: UserPeerId,
141    /// Indicates whether the user is a bot.
142    pub is_bot: bool,
143    /// Indicates whether the user added the bot to the attachment menu.
144    pub added_to_attachment_menu: Option<bool>,
145    /// Indicates whether the user is a Telegram Premium user.
146    pub is_premium: Option<bool>,
147    /// [IETF language tag][1] of the user's language.
148    ///
149    /// [1]: https://en.wikipedia.org/wiki/IETF_language_tag
150    pub language_code: Option<String>,
151    /// Last name of the user.
152    pub last_name: Option<String>,
153    /// Username of the user.
154    pub username: Option<UserUsername>,
155}
156
157impl User {
158    /// Creates a new `User`.
159    ///
160    /// # Arguments
161    ///
162    /// * `id` - Unique identifier of the user.
163    /// * `first_name` - First name of the user.
164    /// * `is_bot` - Indicates whether the user is a bot.
165    pub fn new<A, B>(id: A, first_name: B, is_bot: bool) -> Self
166    where
167        A: Into<UserPeerId>,
168        B: Into<String>,
169    {
170        Self {
171            first_name: first_name.into(),
172            id: id.into(),
173            is_bot,
174            added_to_attachment_menu: None,
175            is_premium: None,
176            language_code: None,
177            last_name: None,
178            username: None,
179        }
180    }
181
182    /// Returns the full name of the user (first name + last name).
183    pub fn get_full_name(&self) -> String {
184        let mut full_name = self.first_name.clone();
185        if let Some(ref last_name) = self.last_name {
186            full_name.push(' ');
187            full_name += last_name;
188        }
189        full_name
190    }
191
192    /// Returns the link to the user (`tg://user?id=xxx`).
193    ///
194    /// These links will work only if they are used inside an inline link.
195    /// For example, they will not work,
196    /// when used in an inline keyboard button or in a message text.
197    pub fn get_link(&self) -> String {
198        format!("tg://user?id={}", self.id.0)
199    }
200
201    /// Returns the mention for the user.
202    ///
203    /// # Arguments
204    ///
205    /// * `parse_mode` - A parse mode for formatting the mention.
206    ///
207    /// These mentions are only guaranteed to work if the user has contacted the bot in the past,
208    /// has sent a callback query to the bot via inline button or is a member
209    /// in the group where he was mentioned.
210    pub fn get_link_mention(&self, parse_mode: ParseMode) -> Result<String, MentionError> {
211        let full_name = parse_mode.escape(self.get_full_name());
212        let user_link = self.get_link();
213        Ok(match parse_mode {
214            ParseMode::Markdown => return Err(MentionError::UnsupportedParseMode(parse_mode)),
215            ParseMode::MarkdownV2 => format!(r#"[{full_name}]({user_link})"#),
216            ParseMode::Html => format!(r#"<a href="{user_link}">{full_name}</a>"#),
217        })
218    }
219
220    /// Sets a new value for the `added_to_attachment_menu` flag.
221    ///
222    /// # Arguments
223    ///
224    /// * `value` - Indicates whether the user added the bot to the attachment menu.
225    pub fn with_added_to_attachment_menu(mut self, value: bool) -> Self {
226        self.added_to_attachment_menu = Some(value);
227        self
228    }
229
230    /// Sets a new value for the `is_premium` flag.
231    ///
232    /// # Arguments
233    ///
234    /// * `value` - Indicates whether the user is a Telegram Premium user.
235    pub fn with_is_premium(mut self, value: bool) -> Self {
236        self.is_premium = Some(value);
237        self
238    }
239
240    /// Sets a new language code.
241    ///
242    /// # Arguments
243    ///
244    /// * `value` - Language code.
245    pub fn with_language_code<T>(mut self, value: T) -> Self
246    where
247        T: Into<String>,
248    {
249        self.language_code = Some(value.into());
250        self
251    }
252
253    /// Sets a new last name.
254    ///
255    /// # Arguments
256    ///
257    /// * `value` - Last name.
258    pub fn with_last_name<T>(mut self, value: T) -> Self
259    where
260        T: Into<String>,
261    {
262        self.last_name = Some(value.into());
263        self
264    }
265
266    /// Sets a new username.
267    ///
268    /// # Arguments
269    ///
270    /// * `value` - Username.
271    pub fn with_username<T>(mut self, value: T) -> Self
272    where
273        T: Into<UserUsername>,
274    {
275        self.username = Some(value.into());
276        self
277    }
278}
279
280/// Represents an error occurred when getting user mention.
281#[derive(Debug)]
282pub enum MentionError {
283    /// Parse mode is not supported
284    UnsupportedParseMode(ParseMode),
285}
286
287impl Error for MentionError {}
288
289impl fmt::Display for MentionError {
290    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
291        match self {
292            MentionError::UnsupportedParseMode(parse_mode) => {
293                write!(out, "can not mention with {parse_mode} parse mode")
294            }
295        }
296    }
297}
298
299/// Represents a list of profile pictures of a user.
300#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
301pub struct UserProfilePhotos {
302    /// Requested profile pictures (in up to 4 sizes each).
303    pub photos: Vec<Vec<PhotoSize>>,
304    /// Total number of profile pictures the target user has.
305    pub total_count: Integer,
306}
307
308impl UserProfilePhotos {
309    /// Creates a new `UserProfilePhotos`.
310    ///
311    /// # Arguments
312    ///
313    /// * `photos` - A list of photos.
314    /// * `total_count` - Total number of photos.
315    pub fn new<A, B>(photos: A, total_count: Integer) -> Self
316    where
317        A: IntoIterator<Item = B>,
318        B: IntoIterator<Item = PhotoSize>,
319    {
320        Self {
321            photos: photos.into_iter().map(|x| x.into_iter().collect()).collect(),
322            total_count,
323        }
324    }
325}
326
327/// ID of a user.
328#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
329#[serde(from = "Integer", into = "Integer")]
330pub struct UserPeerId(Integer);
331
332impl From<Integer> for UserPeerId {
333    fn from(value: Integer) -> Self {
334        Self(value)
335    }
336}
337
338impl From<UserPeerId> for Integer {
339    fn from(value: UserPeerId) -> Self {
340        value.0
341    }
342}
343
344impl fmt::Display for UserPeerId {
345    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346        self.0.fmt(f)
347    }
348}
349
350impl PartialEq<Integer> for UserPeerId {
351    fn eq(&self, other: &Integer) -> bool {
352        self.0.eq(other)
353    }
354}
355
356/// Username of a user in the format `@username`.
357#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
358#[serde(from = "String", into = "String")]
359pub struct UserUsername(String);
360
361impl From<&str> for UserUsername {
362    fn from(value: &str) -> Self {
363        Self(String::from(value))
364    }
365}
366
367impl From<String> for UserUsername {
368    fn from(value: String) -> Self {
369        Self(value)
370    }
371}
372
373impl From<UserUsername> for String {
374    fn from(value: UserUsername) -> Self {
375        value.0
376    }
377}
378
379impl fmt::Display for UserUsername {
380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381        self.0.fmt(f)
382    }
383}
384
385impl PartialEq<String> for UserUsername {
386    fn eq(&self, other: &String) -> bool {
387        self.0.eq(other)
388    }
389}
390
391impl PartialEq<str> for UserUsername {
392    fn eq(&self, other: &str) -> bool {
393        self.0.eq(other)
394    }
395}
396
397/// Represents a user ID.
398#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Serialize)]
399#[serde(untagged)]
400pub enum UserId {
401    /// ID of a user.
402    Id(UserPeerId),
403    /// @username of a user.
404    Username(UserUsername),
405}
406
407impl From<&str> for UserId {
408    fn from(username: &str) -> UserId {
409        UserId::Username(String::from(username).into())
410    }
411}
412
413impl From<String> for UserId {
414    fn from(username: String) -> UserId {
415        UserId::Username(username.into())
416    }
417}
418
419impl From<Integer> for UserId {
420    fn from(id: Integer) -> UserId {
421        UserId::Id(id.into())
422    }
423}
424
425impl fmt::Display for UserId {
426    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
427        match self {
428            UserId::Id(chat_id) => write!(out, "{}", chat_id.0),
429            UserId::Username(username) => write!(out, "{}", username.0),
430        }
431    }
432}
433
434impl From<UserPeerId> for UserId {
435    fn from(value: UserPeerId) -> Self {
436        UserId::Id(value)
437    }
438}
439
440impl From<UserUsername> for UserId {
441    fn from(value: UserUsername) -> Self {
442        UserId::Username(value)
443    }
444}
445
446/// Returns a list of profile pictures for a user.
447#[serde_with::skip_serializing_none]
448#[derive(Clone, Debug, Serialize)]
449pub struct GetUserProfilePhotos {
450    user_id: Integer,
451    limit: Option<Integer>,
452    offset: Option<Integer>,
453}
454
455impl GetUserProfilePhotos {
456    /// Creates a new `GetUserProfilePhotos`.
457    ///
458    /// # Arguments
459    ///
460    /// * `user_id` - Unique identifier of the target user.
461    pub fn new(user_id: Integer) -> Self {
462        GetUserProfilePhotos {
463            user_id,
464            offset: None,
465            limit: None,
466        }
467    }
468
469    /// Sets a new limit.
470    ///
471    /// # Arguments
472    ///
473    /// * `value` - Limit of the number of photos to be retrieved; 1—100; default - 100.
474    pub fn with_limit(mut self, limit: Integer) -> Self {
475        self.limit = Some(limit);
476        self
477    }
478
479    /// Sets a new offset.
480    ///
481    /// # Arguments
482    ///
483    /// * `value` - Sequential number of the first photo to be returned.
484    ///
485    /// By default, all photos are returned.
486    pub fn with_offset(mut self, offset: Integer) -> Self {
487        self.offset = Some(offset);
488        self
489    }
490}
491
492impl Method for GetUserProfilePhotos {
493    type Response = UserProfilePhotos;
494
495    fn into_payload(self) -> Payload {
496        Payload::json("getUserProfilePhotos", self)
497    }
498}
499
500/// Changes the emoji status for a given user
501/// that previously allowed the bot to manage their emoji status
502/// via the Mini App method `requestEmojiStatusAccess`.
503#[serde_with::skip_serializing_none]
504#[derive(Clone, Debug, Serialize)]
505pub struct SetUserEmojiStatus {
506    user_id: Integer,
507    emoji_status_custom_emoji_id: Option<String>,
508    emoji_status_expiration_date: Option<Integer>,
509}
510
511impl SetUserEmojiStatus {
512    /// Creates a new `SetUserEmojiStatus`.
513    ///
514    /// # Arguments
515    ///
516    /// * `user_id` - Unique identifier of the target user
517    pub fn new(user_id: Integer) -> Self {
518        Self {
519            user_id,
520            emoji_status_custom_emoji_id: None,
521            emoji_status_expiration_date: None,
522        }
523    }
524
525    /// Sets a new emoji ID.
526    ///
527    /// # Arguments
528    ///
529    /// * `value` - Custom emoji identifier of the emoji status to set.
530    ///   Pass an empty string to remove the status.
531    pub fn with_emoji_id<T>(mut self, value: T) -> Self
532    where
533        T: Into<String>,
534    {
535        self.emoji_status_custom_emoji_id = Some(value.into());
536        self
537    }
538
539    /// Sets a new expiration date.
540    ///
541    /// # Arguments
542    ///
543    /// * `value` - Expiration date of the emoji status, if any.
544    pub fn with_expiration_date(mut self, value: Integer) -> Self {
545        self.emoji_status_expiration_date = Some(value);
546        self
547    }
548}
549
550impl Method for SetUserEmojiStatus {
551    type Response = bool;
552
553    fn into_payload(self) -> Payload {
554        Payload::json("setUserEmojiStatus", self)
555    }
556}