tg_flows/types/
chat.rs

1use serde::{Deserialize, Serialize};
2
3use crate::types::{ChatId, ChatLocation, ChatPermissions, ChatPhoto, Message, True, User};
4
5/// This object represents a chat.
6///
7/// [The official docs](https://core.telegram.org/bots/api#chat).
8#[serde_with_macros::skip_serializing_none]
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
10pub struct Chat {
11    /// A unique identifier for this chat.
12    pub id: ChatId,
13
14    #[serde(flatten)]
15    pub kind: ChatKind,
16
17    /// A chat photo. Returned only in [`GetChat`].
18    ///
19    /// [`GetChat`]: crate::payloads::GetChat
20    pub photo: Option<ChatPhoto>,
21
22    /// The most recent pinned message (by sending date). Returned only in
23    /// [`GetChat`].
24    ///
25    /// [`GetChat`]: crate::payloads::GetChat
26    pub pinned_message: Option<Box<Message>>,
27
28    /// The time after which all messages sent to the chat will be automatically
29    /// deleted; in seconds. Returned only in [`GetChat`].
30    ///
31    /// [`GetChat`]: crate::payloads::GetChat
32    pub message_auto_delete_time: Option<u32>,
33
34    /// `true`, if non-administrators can only get the list of bots and
35    /// administrators in the chat. Returned only in [`GetChat`].
36    ///
37    /// [`GetChat`]: crate::payloads::GetChat
38    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
39    pub has_hidden_members: bool,
40
41    /// `true`, if aggressive anti-spam checks are enabled in the supergroup.
42    /// The field is only available to chat administrators. Returned only in
43    /// [`GetChat`].
44    ///
45    /// [`GetChat`]: crate::payloads::GetChat
46    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
47    pub has_aggressive_anti_spam_enabled: bool,
48}
49
50#[serde_with_macros::skip_serializing_none]
51#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
52#[serde(untagged)]
53pub enum ChatKind {
54    Public(ChatPublic),
55    Private(ChatPrivate),
56}
57
58#[serde_with_macros::skip_serializing_none]
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60pub struct ChatPublic {
61    /// A title, for supergroups, channels and group chats.
62    pub title: Option<String>,
63
64    #[serde(flatten)]
65    pub kind: PublicChatKind,
66
67    /// A description, for groups, supergroups and channel chats. Returned
68    /// only in [`GetChat`].
69    ///
70    /// [`GetChat`]: crate::payloads::GetChat
71    pub description: Option<String>,
72
73    /// A chat invite link, for groups, supergroups and channel chats. Each
74    /// administrator in a chat generates their own invite links, so the
75    /// bot must first generate the link using
76    /// [`ExportChatInviteLink`]. Returned only in
77    /// [`GetChat`].
78    ///
79    /// [`ExportChatInviteLink`]:
80    /// crate::payloads::ExportChatInviteLink
81    ///
82    /// [`GetChat`]: crate::payloads::GetChat
83    pub invite_link: Option<String>,
84
85    /// `True`, if messages from the chat can't be forwarded to other chats.
86    /// Returned only in [`GetChat`].
87    ///
88    /// [`GetChat`]: crate::payloads::GetChat
89    pub has_protected_content: Option<True>,
90}
91
92#[serde_with_macros::skip_serializing_none]
93#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
94#[serde(from = "serde_helper::ChatPrivate", into = "serde_helper::ChatPrivate")]
95pub struct ChatPrivate {
96    /// A username, for private chats, supergroups and channels if
97    /// available.
98    pub username: Option<String>,
99
100    /// A first name of the other party in a private chat.
101    pub first_name: Option<String>,
102
103    /// A last name of the other party in a private chat.
104    pub last_name: Option<String>,
105
106    /// Custom emoji identifier of emoji status of the other party in a private
107    /// chat. Returned only in [`GetChat`].
108    ///
109    /// [`GetChat`]: crate::payloads::GetChat
110    // FIXME: CustomEmojiId
111    pub emoji_status_custom_emoji_id: Option<String>,
112
113    /// Bio of the other party in a private chat. Returned only in [`GetChat`].
114    ///
115    /// [`GetChat`]: crate::payloads::GetChat
116    pub bio: Option<String>,
117
118    /// `True`, if privacy settings of the other party in the private chat
119    /// allows to use `tg://user?id=<user_id>` links only in chats with the
120    /// user. Returned only in [`GetChat`].
121    ///
122    /// [`GetChat`]: crate::payloads::GetChat
123    pub has_private_forwards: Option<True>,
124
125    /// `True`, if the privacy settings of the other party restrict sending
126    /// voice and video note messages in the private chat. Returned only in
127    /// [`GetChat`].
128    ///
129    /// [`GetChat`]: crate::payloads::GetChat
130    pub has_restricted_voice_and_video_messages: Option<True>,
131}
132
133#[serde_with_macros::skip_serializing_none]
134#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
135#[serde(rename_all = "snake_case")]
136#[serde(tag = "type")]
137pub enum PublicChatKind {
138    Channel(PublicChatChannel),
139    Group(PublicChatGroup),
140    Supergroup(PublicChatSupergroup),
141}
142
143#[serde_with_macros::skip_serializing_none]
144#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
145pub struct PublicChatChannel {
146    /// A username, for private chats, supergroups and channels if available.
147    pub username: Option<String>,
148
149    /// Unique identifier for the linked chat, i.e. the discussion group
150    /// identifier for a channel and vice versa. Returned only in [`GetChat`].
151    ///
152    /// [`GetChat`]: crate::payloads::GetChat
153    pub linked_chat_id: Option<i64>,
154}
155
156#[serde_with_macros::skip_serializing_none]
157#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
158pub struct PublicChatGroup {
159    /// A default chat member permissions, for groups and supergroups. Returned
160    /// only from [`GetChat`].
161    ///
162    /// [`GetChat`]: crate::payloads::GetChat
163    pub permissions: Option<ChatPermissions>,
164}
165
166#[serde_with_macros::skip_serializing_none]
167#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
168pub struct PublicChatSupergroup {
169    /// A username, for private chats, supergroups and channels if
170    /// available.
171    pub username: Option<String>,
172
173    /// If non-empty, the list of all active chat usernames; for private chats,
174    /// supergroups and channels. Returned only from [`GetChat`].
175    ///
176    /// [`GetChat`]: crate::payloads::GetChat
177    pub active_usernames: Option<Vec<String>>,
178
179    /// `true`, if the supergroup chat is a forum (has topics enabled).
180    #[serde(default)]
181    pub is_forum: bool,
182
183    /// For supergroups, name of group sticker set. Returned only from
184    /// [`GetChat`].
185    ///
186    /// [`GetChat`]: crate::payloads::GetChat
187    pub sticker_set_name: Option<String>,
188
189    /// `true`, if the bot can change the group sticker set. Returned only
190    /// from [`GetChat`].
191    ///
192    /// [`GetChat`]: crate::payloads::GetChat
193    pub can_set_sticker_set: Option<bool>,
194
195    /// A default chat member permissions, for groups and supergroups.
196    /// Returned only from [`GetChat`].
197    ///
198    /// [`GetChat`]: crate::payloads::GetChat
199    pub permissions: Option<ChatPermissions>,
200
201    /// The minimum allowed delay between consecutive messages sent by each
202    /// unpriviledged user. Returned only from [`GetChat`].
203    ///
204    /// [`GetChat`]: crate::payloads::GetChat
205    pub slow_mode_delay: Option<u32>,
206
207    /// Unique identifier for the linked chat, i.e. the discussion group
208    /// identifier for a channel and vice versa. Returned only in [`GetChat`].
209    ///
210    /// [`GetChat`]: crate::payloads::GetChat
211    pub linked_chat_id: Option<i64>,
212
213    /// The location to which the supergroup is connected. Returned only in
214    /// [`GetChat`].
215    ///
216    /// [`GetChat`]: crate::payloads::GetChat
217    pub location: Option<ChatLocation>,
218
219    /// True, if users need to join the supergroup before they can send
220    /// messages. Returned only in [`GetChat`].
221    ///
222    /// [`GetChat`]: crate::payloads::GetChat
223    pub join_to_send_messages: Option<True>,
224
225    /// True, if all users directly joining the supergroup need to be approved
226    /// by supergroup administrators. Returned only in [`GetChat`].
227    ///
228    /// [`GetChat`]: crate::payloads::GetChat
229    pub join_by_request: Option<True>,
230}
231
232impl Chat {
233    #[must_use]
234    pub fn is_private(&self) -> bool {
235        matches!(self.kind, ChatKind::Private(_))
236    }
237
238    #[must_use]
239    pub fn is_group(&self) -> bool {
240        matches!(
241            self.kind,
242            ChatKind::Public(ChatPublic {
243                kind: PublicChatKind::Group(_),
244                ..
245            })
246        )
247    }
248
249    #[must_use]
250    pub fn is_supergroup(&self) -> bool {
251        matches!(
252            self.kind,
253            ChatKind::Public(ChatPublic {
254                kind: PublicChatKind::Supergroup(_),
255                ..
256            })
257        )
258    }
259
260    #[must_use]
261    pub fn is_channel(&self) -> bool {
262        matches!(
263            self.kind,
264            ChatKind::Public(ChatPublic {
265                kind: PublicChatKind::Channel(_),
266                ..
267            })
268        )
269    }
270
271    #[must_use]
272    pub fn is_chat(&self) -> bool {
273        self.is_private() || self.is_group() || self.is_supergroup()
274    }
275}
276
277/// Getters
278impl Chat {
279    /// A title, for supergroups, channels and group chats.
280    #[must_use]
281    pub fn title(&self) -> Option<&str> {
282        match &self.kind {
283            ChatKind::Public(this) => this.title.as_deref(),
284            _ => None,
285        }
286    }
287
288    /// A username, for private chats, supergroups and channels if available.
289    #[must_use]
290    pub fn username(&self) -> Option<&str> {
291        match &self.kind {
292            ChatKind::Public(this) => match &this.kind {
293                PublicChatKind::Channel(PublicChatChannel { username, .. })
294                | PublicChatKind::Supergroup(PublicChatSupergroup { username, .. }) => {
295                    username.as_deref()
296                }
297                PublicChatKind::Group(_) => None,
298            },
299            ChatKind::Private(this) => this.username.as_deref(),
300        }
301    }
302
303    /// Unique identifier for the linked chat, i.e. the discussion group
304    /// identifier for a channel and vice versa. Returned only in [`GetChat`].
305    ///
306    /// [`GetChat`]: crate::payloads::GetChat
307    #[must_use]
308    pub fn linked_chat_id(&self) -> Option<i64> {
309        match &self.kind {
310            ChatKind::Public(this) => match &this.kind {
311                PublicChatKind::Channel(PublicChatChannel { linked_chat_id, .. })
312                | PublicChatKind::Supergroup(PublicChatSupergroup { linked_chat_id, .. }) => {
313                    *linked_chat_id
314                }
315                PublicChatKind::Group(_) => None,
316            },
317            _ => None,
318        }
319    }
320
321    /// A default chat member permissions, for groups and supergroups. Returned
322    /// only from [`GetChat`].
323    ///
324    /// [`GetChat`]: crate::payloads::GetChat
325    #[must_use]
326    pub fn permissions(&self) -> Option<ChatPermissions> {
327        if let ChatKind::Public(this) = &self.kind {
328            if let PublicChatKind::Group(PublicChatGroup { permissions })
329            | PublicChatKind::Supergroup(PublicChatSupergroup { permissions, .. }) = &this.kind
330            {
331                return *permissions;
332            }
333        }
334
335        None
336    }
337
338    /// For supergroups, name of group sticker set. Returned only from
339    /// [`GetChat`].
340    ///
341    /// [`GetChat`]: crate::payloads::GetChat
342    #[must_use]
343    pub fn sticker_set_name(&self) -> Option<&str> {
344        if let ChatKind::Public(this) = &self.kind {
345            if let PublicChatKind::Supergroup(this) = &this.kind {
346                return this.sticker_set_name.as_deref();
347            }
348        }
349
350        None
351    }
352
353    /// `true`, if the bot can change the group sticker set. Returned only
354    /// from [`GetChat`].
355    ///
356    /// [`GetChat`]: crate::payloads::GetChat
357    #[must_use]
358    pub fn can_set_sticker_set(&self) -> Option<bool> {
359        if let ChatKind::Public(this) = &self.kind {
360            if let PublicChatKind::Supergroup(this) = &this.kind {
361                return this.can_set_sticker_set;
362            }
363        }
364
365        None
366    }
367
368    /// The minimum allowed delay between consecutive messages sent by each
369    /// unpriviledged user. Returned only from [`GetChat`].
370    ///
371    /// [`GetChat`]: crate::payloads::GetChat
372    #[must_use]
373    pub fn slow_mode_delay(&self) -> Option<u32> {
374        if let ChatKind::Public(this) = &self.kind {
375            if let PublicChatKind::Supergroup(this) = &this.kind {
376                return this.slow_mode_delay;
377            }
378        }
379
380        None
381    }
382
383    /// The location to which the supergroup is connected. Returned only in
384    /// [`GetChat`].
385    ///
386    /// [`GetChat`]: crate::payloads::GetChat
387    #[must_use]
388    pub fn location(&self) -> Option<&ChatLocation> {
389        if let ChatKind::Public(this) = &self.kind {
390            if let PublicChatKind::Supergroup(this) = &this.kind {
391                return this.location.as_ref();
392            }
393        }
394
395        None
396    }
397
398    /// True, if users need to join the supergroup before they can send
399    /// messages. Returned only in [`GetChat`].
400    ///
401    /// [`GetChat`]: crate::payloads::GetChat
402    #[must_use]
403    pub fn join_to_send_messages(&self) -> Option<True> {
404        if let ChatKind::Public(this) = &self.kind {
405            if let PublicChatKind::Supergroup(this) = &this.kind {
406                return this.join_to_send_messages;
407            }
408        }
409
410        None
411    }
412
413    /// True, if all users directly joining the supergroup need to be approved
414    /// by supergroup administrators. Returned only in [`GetChat`].
415    ///
416    /// [`GetChat`]: crate::payloads::GetChat
417    #[must_use]
418    pub fn join_by_request(&self) -> Option<True> {
419        if let ChatKind::Public(this) = &self.kind {
420            if let PublicChatKind::Supergroup(this) = &this.kind {
421                return this.join_by_request;
422            }
423        }
424
425        None
426    }
427
428    /// A description, for groups, supergroups and channel chats. Returned
429    /// only in [`GetChat`].
430    ///
431    /// [`GetChat`]: crate::payloads::GetChat
432    #[must_use]
433    pub fn description(&self) -> Option<&str> {
434        match &self.kind {
435            ChatKind::Public(this) => this.description.as_deref(),
436            _ => None,
437        }
438    }
439
440    /// A chat invite link, for groups, supergroups and channel chats. Each
441    /// administrator in a chat generates their own invite links, so the
442    /// bot must first generate the link using
443    /// [`ExportChatInviteLink`]. Returned only in
444    /// [`GetChat`].
445    ///
446    /// [`ExportChatInviteLink`]:
447    /// crate::payloads::ExportChatInviteLink
448    ///
449    /// [`GetChat`]: crate::payloads::GetChat
450    #[must_use]
451    pub fn invite_link(&self) -> Option<&str> {
452        match &self.kind {
453            ChatKind::Public(this) => this.invite_link.as_deref(),
454            _ => None,
455        }
456    }
457
458    /// `True`, if messages from the chat can't be forwarded to other chats.
459    /// Returned only in [`GetChat`].
460    ///
461    /// [`GetChat`]: crate::payloads::GetChat
462    #[must_use]
463    pub fn has_protected_content(&self) -> Option<True> {
464        match &self.kind {
465            ChatKind::Public(this) => this.has_protected_content,
466            _ => None,
467        }
468    }
469
470    /// A first name of the other party in a private chat.
471    #[must_use]
472    pub fn first_name(&self) -> Option<&str> {
473        match &self.kind {
474            ChatKind::Private(this) => this.first_name.as_deref(),
475            _ => None,
476        }
477    }
478
479    /// A last name of the other party in a private chat.
480    #[must_use]
481    pub fn last_name(&self) -> Option<&str> {
482        match &self.kind {
483            ChatKind::Private(this) => this.last_name.as_deref(),
484            _ => None,
485        }
486    }
487
488    /// Bio of the other party in a private chat. Returned only in [`GetChat`].
489    ///
490    /// [`GetChat`]: crate::payloads::GetChat
491    #[must_use]
492    pub fn bio(&self) -> Option<&str> {
493        match &self.kind {
494            ChatKind::Private(this) => this.bio.as_deref(),
495            _ => None,
496        }
497    }
498
499    /// `True`, if privacy settings of the other party in the private chat
500    /// allows to use tg://user?id=<user_id> links only in chats with the
501    /// user. Returned only in [`GetChat`].
502    ///
503    /// [`GetChat`]: crate::payloads::GetChat
504    #[must_use]
505    pub fn has_private_forwards(&self) -> Option<True> {
506        match &self.kind {
507            ChatKind::Private(this) => this.has_private_forwards,
508            _ => None,
509        }
510    }
511
512    /// Returns all users that are "contained" in this `Chat`
513    /// structure.
514    ///
515    /// This might be useful to track information about users.
516    ///
517    /// Note that this function can return duplicate users.
518    pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
519        crate::util::flatten(self.pinned_message.as_ref().map(|m| m.mentioned_users()))
520    }
521
522    /// `{Message, Chat}::mentioned_users` are mutually recursive, as such we
523    /// can't use `->impl Iterator` everywhere, as it would make an infinite
524    /// type. So we need to box somewhere.
525    pub(crate) fn mentioned_users_rec(&self) -> impl Iterator<Item = &User> {
526        crate::util::flatten(
527            self.pinned_message
528                .as_ref()
529                .map(|m| m.mentioned_users_rec()),
530        )
531    }
532}
533
534mod serde_helper {
535    use crate::types::True;
536    use serde::{Deserialize, Serialize};
537
538    #[derive(Serialize, Deserialize)]
539    enum Type {
540        #[allow(non_camel_case_types)]
541        private,
542    }
543
544    #[derive(Serialize, Deserialize)]
545    pub(super) struct ChatPrivate {
546        /// A dummy field. Used to ensure that the `type` field is equal to
547        /// `private`.
548        r#type: Type,
549
550        username: Option<String>,
551        first_name: Option<String>,
552        last_name: Option<String>,
553        bio: Option<String>,
554        has_private_forwards: Option<True>,
555        has_restricted_voice_and_video_messages: Option<True>,
556        emoji_status_custom_emoji_id: Option<String>,
557    }
558
559    impl From<ChatPrivate> for super::ChatPrivate {
560        fn from(
561            ChatPrivate {
562                r#type: _,
563                username,
564                first_name,
565                last_name,
566                bio,
567                has_private_forwards,
568                has_restricted_voice_and_video_messages,
569                emoji_status_custom_emoji_id,
570            }: ChatPrivate,
571        ) -> Self {
572            Self {
573                username,
574                first_name,
575                last_name,
576                bio,
577                has_private_forwards,
578                has_restricted_voice_and_video_messages,
579                emoji_status_custom_emoji_id,
580            }
581        }
582    }
583
584    impl From<super::ChatPrivate> for ChatPrivate {
585        fn from(
586            super::ChatPrivate {
587                username,
588                first_name,
589                last_name,
590                bio,
591                has_private_forwards,
592                has_restricted_voice_and_video_messages,
593                emoji_status_custom_emoji_id,
594            }: super::ChatPrivate,
595        ) -> Self {
596            Self {
597                r#type: Type::private,
598                username,
599                first_name,
600                last_name,
601                bio,
602                has_private_forwards,
603                has_restricted_voice_and_video_messages,
604                emoji_status_custom_emoji_id,
605            }
606        }
607    }
608}
609
610#[cfg(test)]
611mod tests {
612    use serde_json::{from_str, to_string};
613
614    use crate::types::*;
615
616    #[test]
617    fn channel_de() {
618        let expected = Chat {
619            id: ChatId(-1),
620            kind: ChatKind::Public(ChatPublic {
621                title: None,
622                kind: PublicChatKind::Channel(PublicChatChannel {
623                    username: Some("channel_name".into()),
624                    linked_chat_id: None,
625                }),
626                description: None,
627                invite_link: None,
628                has_protected_content: None,
629            }),
630            photo: None,
631            pinned_message: None,
632            message_auto_delete_time: None,
633            has_hidden_members: false,
634            has_aggressive_anti_spam_enabled: false,
635        };
636        let actual = from_str(r#"{"id":-1,"type":"channel","username":"channel_name"}"#).unwrap();
637        assert_eq!(expected, actual);
638    }
639
640    #[test]
641    fn private_chat_de() {
642        assert_eq!(
643            Chat {
644                id: ChatId(0),
645                kind: ChatKind::Private(ChatPrivate {
646                    username: Some("username".into()),
647                    first_name: Some("Anon".into()),
648                    last_name: None,
649                    bio: None,
650                    has_private_forwards: None,
651                    has_restricted_voice_and_video_messages: None,
652                    emoji_status_custom_emoji_id: None
653                }),
654                photo: None,
655                pinned_message: None,
656                message_auto_delete_time: None,
657                has_hidden_members: false,
658                has_aggressive_anti_spam_enabled: false,
659            },
660            from_str(r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#)
661                .unwrap()
662        );
663    }
664
665    #[test]
666    fn private_roundtrip() {
667        let chat = Chat {
668            id: ChatId(0),
669            kind: ChatKind::Private(ChatPrivate {
670                username: Some("username".into()),
671                first_name: Some("Anon".into()),
672                last_name: None,
673                bio: None,
674                has_private_forwards: None,
675                has_restricted_voice_and_video_messages: None,
676                emoji_status_custom_emoji_id: None,
677            }),
678            photo: None,
679            pinned_message: None,
680            message_auto_delete_time: None,
681            has_hidden_members: false,
682            has_aggressive_anti_spam_enabled: false,
683        };
684
685        let json = to_string(&chat).unwrap();
686        let chat2 = from_str::<Chat>(&json).unwrap();
687
688        assert_eq!(chat, chat2);
689    }
690
691    #[test]
692    fn private_chat_de_wrong_type_field() {
693        assert!(from_str::<Chat>(r#"{"id":0,"type":"WRONG"}"#).is_err());
694    }
695}