1use serde::{Deserialize, Serialize};
2
3use crate::types::{ChatId, ChatLocation, ChatPermissions, ChatPhoto, Message, True, User};
4
5#[serde_with_macros::skip_serializing_none]
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
10pub struct Chat {
11 pub id: ChatId,
13
14 #[serde(flatten)]
15 pub kind: ChatKind,
16
17 pub photo: Option<ChatPhoto>,
21
22 pub pinned_message: Option<Box<Message>>,
27
28 pub message_auto_delete_time: Option<u32>,
33
34 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
39 pub has_hidden_members: bool,
40
41 #[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 pub title: Option<String>,
63
64 #[serde(flatten)]
65 pub kind: PublicChatKind,
66
67 pub description: Option<String>,
72
73 pub invite_link: Option<String>,
84
85 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 pub username: Option<String>,
99
100 pub first_name: Option<String>,
102
103 pub last_name: Option<String>,
105
106 pub emoji_status_custom_emoji_id: Option<String>,
112
113 pub bio: Option<String>,
117
118 pub has_private_forwards: Option<True>,
124
125 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 pub username: Option<String>,
148
149 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 pub permissions: Option<ChatPermissions>,
164}
165
166#[serde_with_macros::skip_serializing_none]
167#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
168pub struct PublicChatSupergroup {
169 pub username: Option<String>,
172
173 pub active_usernames: Option<Vec<String>>,
178
179 #[serde(default)]
181 pub is_forum: bool,
182
183 pub sticker_set_name: Option<String>,
188
189 pub can_set_sticker_set: Option<bool>,
194
195 pub permissions: Option<ChatPermissions>,
200
201 pub slow_mode_delay: Option<u32>,
206
207 pub linked_chat_id: Option<i64>,
212
213 pub location: Option<ChatLocation>,
218
219 pub join_to_send_messages: Option<True>,
224
225 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
277impl Chat {
279 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 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}