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/// Describes the rating of a user based on their Telegram Star spendings.
357#[derive(Copy, Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
358pub struct UserRating {
359    /// The rating value required to get the current level.
360    pub current_level_rating: Integer,
361    /// Current level of the user, indicating their reliability when purchasing digital goods and services.
362    ///
363    /// A higher level suggests a more trustworthy customer; a negative level is likely reason for concern.
364    pub level: Integer,
365    /// Numerical value of the user's rating; the higher the rating, the better.
366    pub rating: Integer,
367    /// The rating value required to get to the next level; omitted if the maximum level was reached.
368    pub next_level_rating: Option<Integer>,
369}
370
371/// Username of a user in the format `@username`.
372#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
373#[serde(from = "String", into = "String")]
374pub struct UserUsername(String);
375
376impl From<&str> for UserUsername {
377    fn from(value: &str) -> Self {
378        Self(String::from(value))
379    }
380}
381
382impl From<String> for UserUsername {
383    fn from(value: String) -> Self {
384        Self(value)
385    }
386}
387
388impl From<UserUsername> for String {
389    fn from(value: UserUsername) -> Self {
390        value.0
391    }
392}
393
394impl fmt::Display for UserUsername {
395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        self.0.fmt(f)
397    }
398}
399
400impl PartialEq<String> for UserUsername {
401    fn eq(&self, other: &String) -> bool {
402        self.0.eq(other)
403    }
404}
405
406impl PartialEq<str> for UserUsername {
407    fn eq(&self, other: &str) -> bool {
408        self.0.eq(other)
409    }
410}
411
412/// Represents a user ID.
413#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Serialize)]
414#[serde(untagged)]
415pub enum UserId {
416    /// ID of a user.
417    Id(UserPeerId),
418    /// @username of a user.
419    Username(UserUsername),
420}
421
422impl From<&str> for UserId {
423    fn from(username: &str) -> UserId {
424        UserId::Username(String::from(username).into())
425    }
426}
427
428impl From<String> for UserId {
429    fn from(username: String) -> UserId {
430        UserId::Username(username.into())
431    }
432}
433
434impl From<Integer> for UserId {
435    fn from(id: Integer) -> UserId {
436        UserId::Id(id.into())
437    }
438}
439
440impl fmt::Display for UserId {
441    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
442        match self {
443            UserId::Id(chat_id) => write!(out, "{}", chat_id.0),
444            UserId::Username(username) => write!(out, "{}", username.0),
445        }
446    }
447}
448
449impl From<UserPeerId> for UserId {
450    fn from(value: UserPeerId) -> Self {
451        UserId::Id(value)
452    }
453}
454
455impl From<UserUsername> for UserId {
456    fn from(value: UserUsername) -> Self {
457        UserId::Username(value)
458    }
459}
460
461/// Returns a list of profile pictures for a user.
462#[serde_with::skip_serializing_none]
463#[derive(Clone, Debug, Serialize)]
464pub struct GetUserProfilePhotos {
465    user_id: Integer,
466    limit: Option<Integer>,
467    offset: Option<Integer>,
468}
469
470impl GetUserProfilePhotos {
471    /// Creates a new `GetUserProfilePhotos`.
472    ///
473    /// # Arguments
474    ///
475    /// * `user_id` - Unique identifier of the target user.
476    pub fn new(user_id: Integer) -> Self {
477        GetUserProfilePhotos {
478            user_id,
479            offset: None,
480            limit: None,
481        }
482    }
483
484    /// Sets a new limit.
485    ///
486    /// # Arguments
487    ///
488    /// * `value` - Limit of the number of photos to be retrieved; 1—100; default - 100.
489    pub fn with_limit(mut self, limit: Integer) -> Self {
490        self.limit = Some(limit);
491        self
492    }
493
494    /// Sets a new offset.
495    ///
496    /// # Arguments
497    ///
498    /// * `value` - Sequential number of the first photo to be returned.
499    ///
500    /// By default, all photos are returned.
501    pub fn with_offset(mut self, offset: Integer) -> Self {
502        self.offset = Some(offset);
503        self
504    }
505}
506
507impl Method for GetUserProfilePhotos {
508    type Response = UserProfilePhotos;
509
510    fn into_payload(self) -> Payload {
511        Payload::json("getUserProfilePhotos", self)
512    }
513}
514
515/// Changes the emoji status for a given user
516/// that previously allowed the bot to manage their emoji status
517/// via the Mini App method `requestEmojiStatusAccess`.
518#[serde_with::skip_serializing_none]
519#[derive(Clone, Debug, Serialize)]
520pub struct SetUserEmojiStatus {
521    user_id: Integer,
522    emoji_status_custom_emoji_id: Option<String>,
523    emoji_status_expiration_date: Option<Integer>,
524}
525
526impl SetUserEmojiStatus {
527    /// Creates a new `SetUserEmojiStatus`.
528    ///
529    /// # Arguments
530    ///
531    /// * `user_id` - Unique identifier of the target user
532    pub fn new(user_id: Integer) -> Self {
533        Self {
534            user_id,
535            emoji_status_custom_emoji_id: None,
536            emoji_status_expiration_date: None,
537        }
538    }
539
540    /// Sets a new emoji ID.
541    ///
542    /// # Arguments
543    ///
544    /// * `value` - Custom emoji identifier of the emoji status to set.
545    ///   Pass an empty string to remove the status.
546    pub fn with_emoji_id<T>(mut self, value: T) -> Self
547    where
548        T: Into<String>,
549    {
550        self.emoji_status_custom_emoji_id = Some(value.into());
551        self
552    }
553
554    /// Sets a new expiration date.
555    ///
556    /// # Arguments
557    ///
558    /// * `value` - Expiration date of the emoji status, if any.
559    pub fn with_expiration_date(mut self, value: Integer) -> Self {
560        self.emoji_status_expiration_date = Some(value);
561        self
562    }
563}
564
565impl Method for SetUserEmojiStatus {
566    type Response = bool;
567
568    fn into_payload(self) -> Payload {
569        Payload::json("setUserEmojiStatus", self)
570    }
571}