1use std::{collections::BTreeMap, fmt};
2
3use serde::{
4 Deserialize, Deserializer, Serialize, Serializer,
5 de::{DeserializeOwned, Error as DeError},
6 ser::SerializeStruct,
7};
8
9fn deserialize_string_enum<'de, D, T, F>(deserializer: D, from_str: F) -> Result<T, D::Error>
14where
15 D: Deserializer<'de>,
16 F: FnOnce(String) -> T,
17{
18 let value = String::deserialize(deserializer)?;
19 Ok(from_str(value))
20}
21
22fn serialize_string_enum<S>(serializer: S, value: &str) -> Result<S::Ok, S::Error>
23where
24 S: Serializer,
25{
26 serializer.serialize_str(value)
27}
28
29#[derive(Debug, Clone, Serialize)]
35pub struct User {
36 pub user_id: i64,
41 pub first_name: String,
42 pub last_name: Option<String>,
43 pub username: Option<String>,
44 pub is_bot: Option<bool>,
45 pub last_activity_time: Option<i64>,
46 pub description: Option<String>,
47 pub avatar_url: Option<String>,
48 pub full_avatar_url: Option<String>,
49 pub commands: Option<Vec<BotCommand>>,
50}
51
52impl User {
53 pub fn display_name(&self) -> String {
55 match self.last_name.as_deref() {
56 Some(last_name) if !last_name.is_empty() => {
57 format!("{} {}", self.first_name, last_name)
58 }
59 _ => self.first_name.clone(),
60 }
61 }
62}
63
64impl<'de> Deserialize<'de> for User {
65 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66 where
67 D: Deserializer<'de>,
68 {
69 #[derive(Deserialize)]
70 struct WireUser {
71 user_id: i64,
72 #[serde(default)]
73 first_name: Option<String>,
74 #[serde(default)]
75 last_name: Option<String>,
76 #[serde(default)]
77 name: Option<String>,
78 #[serde(default)]
79 username: Option<String>,
80 #[serde(default)]
81 is_bot: Option<bool>,
82 #[serde(default)]
83 last_activity_time: Option<i64>,
84 #[serde(default)]
85 description: Option<String>,
86 #[serde(default)]
87 avatar_url: Option<String>,
88 #[serde(default)]
89 full_avatar_url: Option<String>,
90 #[serde(default)]
91 commands: Option<Vec<BotCommand>>,
92 }
93
94 let wire = WireUser::deserialize(deserializer)?;
95 let first_name = wire
96 .first_name
97 .or(wire.name)
98 .ok_or_else(|| D::Error::missing_field("first_name"))?;
99
100 Ok(Self {
101 user_id: wire.user_id,
102 first_name,
103 last_name: wire.last_name,
104 username: wire.username,
105 is_bot: wire.is_bot,
106 last_activity_time: wire.last_activity_time,
107 description: wire.description,
108 avatar_url: wire.avatar_url,
109 full_avatar_url: wire.full_avatar_url,
110 commands: wire.commands,
111 })
112 }
113}
114
115#[non_exhaustive]
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub enum ChatType {
122 Dialog,
123 Chat,
124 Channel,
125 Unknown(String),
126}
127
128impl ChatType {
129 pub fn as_str(&self) -> &str {
130 match self {
131 Self::Dialog => "dialog",
132 Self::Chat => "chat",
133 Self::Channel => "channel",
134 Self::Unknown(value) => value.as_str(),
135 }
136 }
137}
138
139impl Serialize for ChatType {
140 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141 where
142 S: Serializer,
143 {
144 serialize_string_enum(serializer, self.as_str())
145 }
146}
147
148impl<'de> Deserialize<'de> for ChatType {
149 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150 where
151 D: Deserializer<'de>,
152 {
153 deserialize_string_enum(deserializer, |value| match value.as_str() {
154 "dialog" => Self::Dialog,
155 "chat" => Self::Chat,
156 "channel" => Self::Channel,
157 _ => Self::Unknown(value),
158 })
159 }
160}
161
162#[non_exhaustive]
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub enum ChatStatus {
165 Active,
166 Removed,
167 Left,
168 Closed,
169 Unknown(String),
170}
171
172impl ChatStatus {
173 pub fn as_str(&self) -> &str {
174 match self {
175 Self::Active => "active",
176 Self::Removed => "removed",
177 Self::Left => "left",
178 Self::Closed => "closed",
179 Self::Unknown(value) => value.as_str(),
180 }
181 }
182}
183
184impl Serialize for ChatStatus {
185 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186 where
187 S: Serializer,
188 {
189 serialize_string_enum(serializer, self.as_str())
190 }
191}
192
193impl<'de> Deserialize<'de> for ChatStatus {
194 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
195 where
196 D: Deserializer<'de>,
197 {
198 deserialize_string_enum(deserializer, |value| match value.as_str() {
199 "active" => Self::Active,
200 "removed" => Self::Removed,
201 "left" => Self::Left,
202 "closed" => Self::Closed,
203 _ => Self::Unknown(value),
204 })
205 }
206}
207
208#[derive(Debug, Clone, Deserialize, Serialize)]
210pub struct Chat {
211 pub chat_id: i64,
215 pub r#type: ChatType,
216 pub status: Option<ChatStatus>,
217 pub title: Option<String>,
218 pub icon: Option<Image>,
219 pub last_event_time: Option<i64>,
220 pub participants_count: Option<i32>,
221 pub owner_id: Option<i64>,
222 pub is_public: Option<bool>,
223 pub link: Option<String>,
224 pub description: Option<String>,
225 pub dialog_with_user: Option<User>,
226 pub chat_message_id: Option<String>,
227 pub pinned_message: Option<Box<Message>>,
228}
229
230#[derive(Debug, Clone, Deserialize, Serialize)]
231pub struct Image {
232 pub url: String,
233}
234
235#[derive(Debug, Clone, Deserialize)]
237pub struct ChatList {
238 pub chats: Vec<Chat>,
239 pub marker: Option<i64>,
240}
241
242#[derive(Debug, Clone, Serialize, Default)]
244pub struct EditChatBody {
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub icon: Option<PhotoAttachmentPayload>,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub title: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub description: Option<String>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub pin: Option<String>,
253 #[serde(skip_serializing_if = "Option::is_none")]
254 pub notify: Option<bool>,
255}
256
257#[non_exhaustive]
265#[derive(Debug, Clone, PartialEq, Eq, Default)]
266pub enum MessageFormat {
267 #[default]
268 Markdown,
269 Html,
270 Unknown(String),
271}
272
273impl MessageFormat {
274 pub fn as_str(&self) -> &str {
275 match self {
276 Self::Markdown => "markdown",
277 Self::Html => "html",
278 Self::Unknown(value) => value.as_str(),
279 }
280 }
281}
282
283impl Serialize for MessageFormat {
284 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
285 where
286 S: Serializer,
287 {
288 serialize_string_enum(serializer, self.as_str())
289 }
290}
291
292impl<'de> Deserialize<'de> for MessageFormat {
293 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
294 where
295 D: Deserializer<'de>,
296 {
297 deserialize_string_enum(deserializer, |value| match value.as_str() {
298 "markdown" => Self::Markdown,
299 "html" => Self::Html,
300 _ => Self::Unknown(value),
301 })
302 }
303}
304
305#[derive(Debug, Clone, Deserialize, Serialize)]
307pub struct Message {
308 pub sender: Option<User>,
309 pub recipient: Recipient,
310 pub timestamp: i64,
311 pub link: Option<LinkedMessage>,
312 pub body: MessageBody,
313 pub stat: Option<MessageStat>,
314 pub url: Option<String>,
315 pub constructor: Option<serde_json::Value>,
316}
317
318impl Message {
319 pub fn chat_id(&self) -> i64 {
324 self.recipient.chat_id
325 }
326
327 pub fn message_id(&self) -> &str {
329 &self.body.mid
330 }
331
332 pub fn text(&self) -> Option<&str> {
334 self.body.text.as_deref()
335 }
336
337 pub fn sender_user_id(&self) -> Option<i64> {
339 self.sender.as_ref().map(|sender| sender.user_id)
340 }
341
342 pub fn has_attachments(&self) -> bool {
344 self.body
345 .attachments
346 .as_ref()
347 .map(|attachments| !attachments.is_empty())
348 .unwrap_or(false)
349 }
350}
351
352#[derive(Debug, Clone, Deserialize, Serialize)]
357pub struct Recipient {
358 pub chat_id: i64,
360 pub chat_type: ChatType,
361 pub user_id: Option<i64>,
363}
364
365#[derive(Debug, Clone, Deserialize, Serialize)]
366pub struct MessageBody {
367 pub mid: String,
368 pub seq: i64,
369 pub text: Option<String>,
370 #[serde(default, deserialize_with = "deserialize_attachments_lossy")]
371 pub attachments: Option<Vec<Attachment>>,
372}
373
374#[derive(Debug, Clone, Deserialize, Serialize)]
375pub struct MessageStat {
376 pub views: Option<i32>,
377}
378
379fn deserialize_attachments_lossy<'de, D>(
380 deserializer: D,
381) -> std::result::Result<Option<Vec<Attachment>>, D::Error>
382where
383 D: Deserializer<'de>,
384{
385 let raw = Option::<Vec<serde_json::Value>>::deserialize(deserializer)?;
386
387 Ok(raw.map(|items| {
388 items
389 .into_iter()
390 .map(|value| {
391 serde_json::from_value::<Attachment>(value.clone()).unwrap_or_else(|_| {
392 Attachment::Unknown {
393 r#type: value
394 .get("type")
395 .and_then(|value| value.as_str())
396 .unwrap_or("unknown")
397 .to_string(),
398 payload: value.get("payload").cloned(),
399 raw: value,
400 }
401 })
402 })
403 .collect()
404 }))
405}
406
407#[derive(Debug, Clone, Deserialize, Serialize)]
408pub struct LinkedMessage {
409 pub r#type: String,
410 pub sender: Option<User>,
411 pub chat_id: Option<i64>,
412 pub message: Option<MessageBody>,
413}
414
415#[derive(Debug, Clone, Deserialize)]
417pub struct MessageList {
418 pub messages: Vec<Message>,
419}
420
421#[non_exhaustive]
426#[derive(Debug, Clone)]
427pub enum Attachment {
428 Image {
429 payload: MediaPayload,
430 },
431 Video {
432 payload: MediaPayload,
433 },
434 Audio {
435 payload: MediaPayload,
436 },
437 File {
438 payload: FilePayload,
439 },
440 Sticker {
441 payload: StickerPayload,
442 },
443 InlineKeyboard {
444 payload: KeyboardPayload,
445 },
446 Location {
447 payload: LocationPayload,
448 },
449 Contact {
450 payload: ContactPayload,
451 },
452 Unknown {
453 r#type: String,
454 payload: Option<serde_json::Value>,
455 raw: serde_json::Value,
456 },
457}
458
459impl Attachment {
460 pub fn kind(&self) -> AttachmentKind {
461 match self {
462 Self::Image { .. } => AttachmentKind::Image,
463 Self::Video { .. } => AttachmentKind::Video,
464 Self::Audio { .. } => AttachmentKind::Audio,
465 Self::File { .. } => AttachmentKind::File,
466 Self::Sticker { .. } => AttachmentKind::Sticker,
467 Self::InlineKeyboard { .. } => AttachmentKind::InlineKeyboard,
468 Self::Location { .. } => AttachmentKind::Location,
469 Self::Contact { .. } => AttachmentKind::Contact,
470 Self::Unknown { .. } => AttachmentKind::Unknown,
471 }
472 }
473}
474
475impl Serialize for Attachment {
476 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
477 where
478 S: Serializer,
479 {
480 match self {
481 Self::Image { payload } => serialize_attachment(serializer, "image", payload),
482 Self::Video { payload } => serialize_attachment(serializer, "video", payload),
483 Self::Audio { payload } => serialize_attachment(serializer, "audio", payload),
484 Self::File { payload } => serialize_attachment(serializer, "file", payload),
485 Self::Sticker { payload } => serialize_attachment(serializer, "sticker", payload),
486 Self::InlineKeyboard { payload } => {
487 serialize_attachment(serializer, "inline_keyboard", payload)
488 }
489 Self::Location { payload } => serialize_attachment(serializer, "location", payload),
490 Self::Contact { payload } => serialize_attachment(serializer, "contact", payload),
491 Self::Unknown { raw, .. } => raw.serialize(serializer),
492 }
493 }
494}
495
496fn serialize_attachment<S, T>(
497 serializer: S,
498 attachment_type: &str,
499 payload: &T,
500) -> Result<S::Ok, S::Error>
501where
502 S: Serializer,
503 T: Serialize,
504{
505 let mut state = serializer.serialize_struct("Attachment", 2)?;
506 state.serialize_field("type", attachment_type)?;
507 state.serialize_field("payload", payload)?;
508 state.end()
509}
510
511impl<'de> Deserialize<'de> for Attachment {
512 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
513 where
514 D: Deserializer<'de>,
515 {
516 let raw = serde_json::Value::deserialize(deserializer)?;
517 let attachment_type = raw
518 .get("type")
519 .and_then(|value| value.as_str())
520 .ok_or_else(|| D::Error::missing_field("type"))?;
521
522 match attachment_type {
523 "image" => Ok(Self::Image {
524 payload: deserialize_attachment_payload(&raw)?,
525 }),
526 "video" => Ok(Self::Video {
527 payload: deserialize_attachment_payload(&raw)?,
528 }),
529 "audio" => Ok(Self::Audio {
530 payload: deserialize_attachment_payload(&raw)?,
531 }),
532 "file" => Ok(Self::File {
533 payload: deserialize_attachment_payload(&raw)?,
534 }),
535 "sticker" => Ok(Self::Sticker {
536 payload: deserialize_attachment_payload(&raw)?,
537 }),
538 "inline_keyboard" => Ok(Self::InlineKeyboard {
539 payload: deserialize_attachment_payload(&raw)?,
540 }),
541 "location" => Ok(Self::Location {
542 payload: deserialize_attachment_payload(&raw)?,
543 }),
544 "contact" => Ok(Self::Contact {
545 payload: deserialize_attachment_payload(&raw)?,
546 }),
547 _ => Ok(Self::Unknown {
548 r#type: attachment_type.to_string(),
549 payload: raw.get("payload").cloned(),
550 raw,
551 }),
552 }
553 }
554}
555
556fn deserialize_attachment_payload<T, E>(raw: &serde_json::Value) -> Result<T, E>
557where
558 T: DeserializeOwned,
559 E: DeError,
560{
561 let value = raw.get("payload").cloned().unwrap_or_else(|| raw.clone());
562
563 serde_json::from_value(value).map_err(E::custom)
564}
565
566#[non_exhaustive]
567#[derive(Debug, Clone, Copy, PartialEq, Eq)]
568pub enum AttachmentKind {
569 Image,
570 Video,
571 Audio,
572 File,
573 Sticker,
574 InlineKeyboard,
575 Location,
576 Contact,
577 Unknown,
578}
579
580#[derive(Debug, Clone, Deserialize, Serialize)]
581pub struct MediaPayload {
582 pub url: Option<String>,
583 pub token: Option<String>,
584 pub photo_id: Option<i64>,
585}
586
587#[derive(Debug, Clone, Deserialize, Serialize)]
588pub struct FilePayload {
589 pub url: Option<String>,
590 pub token: Option<String>,
591 pub filename: Option<String>,
592 pub size: Option<i64>,
593}
594
595#[derive(Debug, Clone, Deserialize, Serialize)]
596pub struct StickerPayload {
597 pub code: String,
598 pub url: Option<String>,
599 pub width: Option<i32>,
600 pub height: Option<i32>,
601}
602
603#[derive(Debug, Clone, Deserialize, Serialize)]
604pub struct LocationPayload {
605 pub latitude: f64,
606 pub longitude: f64,
607}
608
609#[derive(Debug, Clone, Deserialize, Serialize)]
610pub struct ContactPayload {
611 pub name: Option<String>,
612 pub contact_id: Option<i64>,
613 pub vcf_info: Option<String>,
614 pub vcf_phone: Option<String>,
615}
616
617#[derive(Debug, Clone, Deserialize, Serialize, Default)]
622pub struct KeyboardPayload {
623 pub buttons: Vec<Vec<Button>>,
625}
626
627#[non_exhaustive]
628#[derive(Debug, Clone, Deserialize, Serialize)]
629#[serde(tag = "type", rename_all = "snake_case")]
630pub enum Button {
631 Callback {
633 text: String,
634 payload: String,
635 #[serde(skip_serializing_if = "Option::is_none")]
636 intent: Option<ButtonIntent>,
637 },
638 Link {
640 text: String,
641 url: String,
642 #[serde(skip_serializing_if = "Option::is_none")]
643 intent: Option<ButtonIntent>,
644 },
645 Message {
647 text: String,
648 #[serde(skip_serializing_if = "Option::is_none")]
649 intent: Option<ButtonIntent>,
650 },
651 OpenApp {
653 text: String,
654 #[serde(default, skip_serializing_if = "String::is_empty")]
655 web_app: String,
656 #[serde(skip_serializing_if = "Option::is_none")]
657 payload: Option<String>,
658 #[serde(skip_serializing_if = "Option::is_none")]
659 contact_id: Option<i64>,
660 },
661 Clipboard { text: String, payload: String },
666 RequestContact { text: String },
672 RequestGeoLocation {
677 text: String,
678 #[serde(skip_serializing_if = "Option::is_none")]
679 quick: Option<bool>,
680 },
681}
682
683#[non_exhaustive]
685#[derive(Debug, Clone, PartialEq, Eq, Default)]
686pub enum ButtonIntent {
687 #[default]
688 Default,
689 Positive,
690 Negative,
691 Unknown(String),
692}
693
694impl ButtonIntent {
695 pub fn as_str(&self) -> &str {
696 match self {
697 Self::Default => "default",
698 Self::Positive => "positive",
699 Self::Negative => "negative",
700 Self::Unknown(value) => value.as_str(),
701 }
702 }
703}
704
705impl Serialize for ButtonIntent {
706 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
707 where
708 S: Serializer,
709 {
710 serialize_string_enum(serializer, self.as_str())
711 }
712}
713
714impl<'de> Deserialize<'de> for ButtonIntent {
715 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
716 where
717 D: Deserializer<'de>,
718 {
719 deserialize_string_enum(deserializer, |value| match value.as_str() {
720 "default" => Self::Default,
721 "positive" => Self::Positive,
722 "negative" => Self::Negative,
723 _ => Self::Unknown(value),
724 })
725 }
726}
727
728impl Button {
729 pub fn callback(text: impl Into<String>, payload: impl Into<String>) -> Self {
730 Self::Callback {
731 text: text.into(),
732 payload: payload.into(),
733 intent: None,
734 }
735 }
736
737 pub fn link(text: impl Into<String>, url: impl Into<String>) -> Self {
738 Self::Link {
739 text: text.into(),
740 url: url.into(),
741 intent: None,
742 }
743 }
744
745 pub fn message(text: impl Into<String>) -> Self {
746 Self::Message {
747 text: text.into(),
748 intent: None,
749 }
750 }
751
752 pub fn open_app(text: impl Into<String>, web_app: impl Into<String>) -> Self {
753 Self::OpenApp {
754 text: text.into(),
755 web_app: web_app.into(),
756 payload: None,
757 contact_id: None,
758 }
759 }
760
761 pub fn open_app_with_payload(
762 text: impl Into<String>,
763 web_app: impl Into<String>,
764 payload: impl Into<String>,
765 ) -> Self {
766 Self::OpenApp {
767 text: text.into(),
768 web_app: web_app.into(),
769 payload: Some(payload.into()),
770 contact_id: None,
771 }
772 }
773
774 pub fn open_app_full(
775 text: impl Into<String>,
776 web_app: impl Into<String>,
777 payload: Option<String>,
778 contact_id: Option<i64>,
779 ) -> Self {
780 Self::OpenApp {
781 text: text.into(),
782 web_app: web_app.into(),
783 payload,
784 contact_id,
785 }
786 }
787
788 pub fn clipboard(text: impl Into<String>, payload: impl Into<String>) -> Self {
789 Self::Clipboard {
790 text: text.into(),
791 payload: payload.into(),
792 }
793 }
794
795 pub fn request_contact(text: impl Into<String>) -> Self {
796 Self::RequestContact { text: text.into() }
797 }
798
799 pub fn request_geo_location(text: impl Into<String>) -> Self {
800 Self::RequestGeoLocation {
801 text: text.into(),
802 quick: None,
803 }
804 }
805}
806
807#[derive(Debug, Clone, Serialize, Default)]
813pub struct NewMessageBody {
814 #[serde(skip_serializing_if = "Option::is_none")]
815 pub text: Option<String>,
816 #[serde(skip_serializing_if = "Option::is_none")]
817 pub attachments: Option<Vec<NewAttachment>>,
818 #[serde(skip_serializing_if = "Option::is_none")]
819 pub link: Option<NewMessageLink>,
820 #[serde(skip_serializing_if = "Option::is_none")]
821 pub notify: Option<bool>,
822 #[serde(skip_serializing_if = "Option::is_none")]
823 pub format: Option<MessageFormat>,
824}
825
826impl NewMessageBody {
827 pub fn empty() -> Self {
828 Self::default()
829 }
830
831 pub fn text(text: impl Into<String>) -> Self {
832 Self {
833 text: Some(text.into()),
834 ..Default::default()
835 }
836 }
837
838 pub fn text_opt(text: Option<impl Into<String>>) -> Self {
839 match text {
840 Some(text) => Self::text(text),
841 None => Self::empty(),
842 }
843 }
844
845 pub fn with_attachment(mut self, attachment: NewAttachment) -> Self {
846 self.attachments
847 .get_or_insert_with(Vec::new)
848 .push(attachment);
849 self
850 }
851
852 pub fn with_attachments(
853 mut self,
854 attachments: impl IntoIterator<Item = NewAttachment>,
855 ) -> Self {
856 self.attachments
857 .get_or_insert_with(Vec::new)
858 .extend(attachments);
859 self
860 }
861
862 pub fn with_keyboard(self, keyboard: KeyboardPayload) -> Self {
863 self.with_attachment(NewAttachment::inline_keyboard(keyboard))
864 }
865
866 pub fn with_format(mut self, format: MessageFormat) -> Self {
867 self.format = Some(format);
868 self
869 }
870
871 pub fn with_notify(mut self, notify: bool) -> Self {
872 self.notify = Some(notify);
873 self
874 }
875
876 pub fn with_reply_to(mut self, message_id: impl Into<String>) -> Self {
877 self.link = Some(NewMessageLink {
878 r#type: LinkType::Reply,
879 mid: message_id.into(),
880 });
881 self
882 }
883
884 pub fn with_forward_from(mut self, message_id: impl Into<String>) -> Self {
885 self.link = Some(NewMessageLink {
886 r#type: LinkType::Forward,
887 mid: message_id.into(),
888 });
889 self
890 }
891}
892
893#[non_exhaustive]
894#[derive(Debug, Clone, Serialize)]
895#[serde(tag = "type", rename_all = "snake_case")]
896pub enum NewAttachment {
897 InlineKeyboard { payload: KeyboardPayload },
898 Image { payload: ImageAttachmentPayload },
899 Video { payload: UploadedToken },
900 Audio { payload: UploadedToken },
901 File { payload: UploadedToken },
902}
903
904impl NewAttachment {
905 pub fn inline_keyboard(keyboard: KeyboardPayload) -> Self {
906 Self::InlineKeyboard { payload: keyboard }
907 }
908
909 pub fn image(token: impl Into<String>) -> Self {
910 Self::Image {
911 payload: ImageAttachmentPayload::token(token),
912 }
913 }
914
915 pub fn image_url(url: impl Into<String>) -> Self {
916 Self::Image {
917 payload: ImageAttachmentPayload::url(url),
918 }
919 }
920
921 pub fn image_photos(photos: PhotoTokens) -> Self {
922 Self::Image {
923 payload: ImageAttachmentPayload::photos(photos),
924 }
925 }
926
927 pub fn video(token: impl Into<String>) -> Self {
928 Self::Video {
929 payload: UploadedToken::new(token),
930 }
931 }
932
933 pub fn audio(token: impl Into<String>) -> Self {
934 Self::Audio {
935 payload: UploadedToken::new(token),
936 }
937 }
938
939 pub fn file(token: impl Into<String>) -> Self {
940 Self::File {
941 payload: UploadedToken::new(token),
942 }
943 }
944}
945
946#[derive(Debug, Clone, Deserialize, Serialize)]
947pub struct PhotoToken {
948 pub token: String,
949 #[serde(flatten)]
950 pub extra: BTreeMap<String, serde_json::Value>,
951}
952
953impl PhotoToken {
954 pub fn new(token: impl Into<String>) -> Self {
955 Self {
956 token: token.into(),
957 extra: BTreeMap::new(),
958 }
959 }
960}
961
962pub type PhotoTokens = BTreeMap<String, PhotoToken>;
963
964#[derive(Debug, Clone, Default, Deserialize, Serialize)]
965pub struct ImageAttachmentPayload {
966 #[serde(skip_serializing_if = "Option::is_none")]
967 pub url: Option<String>,
968 #[serde(skip_serializing_if = "Option::is_none")]
969 pub token: Option<String>,
970 #[serde(skip_serializing_if = "Option::is_none")]
971 pub photos: Option<PhotoTokens>,
972}
973
974impl ImageAttachmentPayload {
975 pub fn token(token: impl Into<String>) -> Self {
976 Self {
977 token: Some(token.into()),
978 ..Default::default()
979 }
980 }
981
982 pub fn url(url: impl Into<String>) -> Self {
983 Self {
984 url: Some(url.into()),
985 ..Default::default()
986 }
987 }
988
989 pub fn photos(photos: PhotoTokens) -> Self {
990 Self {
991 photos: Some(photos),
992 ..Default::default()
993 }
994 }
995}
996
997#[derive(Debug, Clone, Serialize)]
998pub struct UploadedToken {
999 pub token: String,
1000}
1001
1002impl UploadedToken {
1003 pub fn new(token: impl Into<String>) -> Self {
1004 Self {
1005 token: token.into(),
1006 }
1007 }
1008}
1009
1010#[derive(Debug, Clone, Serialize)]
1011pub struct NewMessageLink {
1012 pub r#type: LinkType,
1013 pub mid: String,
1014}
1015
1016#[non_exhaustive]
1017#[derive(Debug, Clone, PartialEq, Eq)]
1018pub enum LinkType {
1019 Forward,
1020 Reply,
1021 Unknown(String),
1022}
1023
1024impl LinkType {
1025 pub fn as_str(&self) -> &str {
1026 match self {
1027 Self::Forward => "forward",
1028 Self::Reply => "reply",
1029 Self::Unknown(value) => value.as_str(),
1030 }
1031 }
1032}
1033
1034impl Serialize for LinkType {
1035 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1036 where
1037 S: Serializer,
1038 {
1039 serialize_string_enum(serializer, self.as_str())
1040 }
1041}
1042
1043impl<'de> Deserialize<'de> for LinkType {
1044 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1045 where
1046 D: Deserializer<'de>,
1047 {
1048 deserialize_string_enum(deserializer, |value| match value.as_str() {
1049 "forward" => Self::Forward,
1050 "reply" => Self::Reply,
1051 _ => Self::Unknown(value),
1052 })
1053 }
1054}
1055
1056#[derive(Debug, Clone, Copy, Default, Serialize)]
1058pub struct SendMessageOptions {
1059 #[serde(skip_serializing_if = "Option::is_none")]
1060 pub disable_link_preview: Option<bool>,
1061}
1062
1063impl SendMessageOptions {
1064 pub fn disable_link_preview(disable: bool) -> Self {
1065 Self {
1066 disable_link_preview: Some(disable),
1067 }
1068 }
1069}
1070
1071#[derive(Debug, Clone, Deserialize)]
1077pub struct UpdatesResponse {
1078 pub updates: Vec<Update>,
1079 pub marker: Option<i64>,
1080}
1081
1082#[derive(Debug, Clone, Deserialize)]
1084pub struct RawUpdatesResponse {
1085 pub updates: Vec<serde_json::Value>,
1086 pub marker: Option<i64>,
1087}
1088
1089#[allow(clippy::large_enum_variant)]
1094#[non_exhaustive]
1095#[derive(Debug, Clone)]
1096pub enum Update {
1097 MessageCreated { timestamp: i64, message: Message },
1099 MessageEdited { timestamp: i64, message: Message },
1101 MessageRemoved {
1103 timestamp: i64,
1104 message_id: String,
1105 chat_id: i64,
1106 user_id: i64,
1107 },
1108 MessageCallback {
1110 timestamp: i64,
1111 callback: Callback,
1112 message: Option<Message>,
1113 user_locale: Option<String>,
1114 },
1115 BotStarted {
1117 timestamp: i64,
1118 chat_id: i64,
1119 user: User,
1120 payload: Option<String>,
1121 user_locale: Option<String>,
1122 },
1123 BotAdded {
1125 timestamp: i64,
1126 chat_id: i64,
1127 user: User,
1128 is_channel: Option<bool>,
1129 },
1130 BotRemoved {
1132 timestamp: i64,
1133 chat_id: i64,
1134 user: User,
1135 is_channel: Option<bool>,
1136 },
1137 UserAdded {
1139 timestamp: i64,
1140 chat_id: i64,
1141 user: User,
1142 inviter_id: Option<i64>,
1143 is_channel: Option<bool>,
1144 },
1145 UserRemoved {
1147 timestamp: i64,
1148 chat_id: i64,
1149 user: User,
1150 admin_id: Option<i64>,
1151 is_channel: Option<bool>,
1152 },
1153 ChatTitleChanged {
1155 timestamp: i64,
1156 chat_id: i64,
1157 user: User,
1158 title: String,
1159 },
1160 Unknown {
1162 update_type: Option<String>,
1163 timestamp: Option<i64>,
1164 raw: serde_json::Value,
1165 },
1166}
1167
1168impl Update {
1169 pub fn timestamp(&self) -> Option<i64> {
1171 match self {
1172 Self::MessageCreated { timestamp, .. }
1173 | Self::MessageEdited { timestamp, .. }
1174 | Self::MessageRemoved { timestamp, .. }
1175 | Self::MessageCallback { timestamp, .. }
1176 | Self::BotStarted { timestamp, .. }
1177 | Self::BotAdded { timestamp, .. }
1178 | Self::BotRemoved { timestamp, .. }
1179 | Self::UserAdded { timestamp, .. }
1180 | Self::UserRemoved { timestamp, .. }
1181 | Self::ChatTitleChanged { timestamp, .. } => Some(*timestamp),
1182 Self::Unknown { timestamp, .. } => *timestamp,
1183 }
1184 }
1185
1186 pub fn timestamp_or_default(&self) -> i64 {
1188 self.timestamp().unwrap_or_default()
1189 }
1190
1191 pub fn update_type(&self) -> Option<&str> {
1192 match self {
1193 Self::MessageCreated { .. } => Some("message_created"),
1194 Self::MessageEdited { .. } => Some("message_edited"),
1195 Self::MessageRemoved { .. } => Some("message_removed"),
1196 Self::MessageCallback { .. } => Some("message_callback"),
1197 Self::BotStarted { .. } => Some("bot_started"),
1198 Self::BotAdded { .. } => Some("bot_added"),
1199 Self::BotRemoved { .. } => Some("bot_removed"),
1200 Self::UserAdded { .. } => Some("user_added"),
1201 Self::UserRemoved { .. } => Some("user_removed"),
1202 Self::ChatTitleChanged { .. } => Some("chat_title_changed"),
1203 Self::Unknown { update_type, .. } => update_type.as_deref(),
1204 }
1205 }
1206
1207 pub fn raw(&self) -> Option<&serde_json::Value> {
1208 match self {
1209 Self::Unknown { raw, .. } => Some(raw),
1210 _ => None,
1211 }
1212 }
1213}
1214
1215impl<'de> Deserialize<'de> for Update {
1216 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1217 where
1218 D: Deserializer<'de>,
1219 {
1220 let raw = serde_json::Value::deserialize(deserializer)?;
1221 let update_type = raw
1222 .get("update_type")
1223 .and_then(|value| value.as_str())
1224 .map(String::from);
1225 let timestamp = raw.get("timestamp").and_then(|value| value.as_i64());
1226
1227 let Some(kind) = update_type.as_deref() else {
1228 return Ok(Self::Unknown {
1229 update_type,
1230 timestamp,
1231 raw,
1232 });
1233 };
1234
1235 macro_rules! parse_update {
1236 ($wire:ty, $map:expr) => {
1237 match serde_json::from_value::<$wire>(raw.clone()) {
1238 Ok(wire) => $map(wire),
1239 Err(_) => Self::Unknown {
1240 update_type,
1241 timestamp,
1242 raw,
1243 },
1244 }
1245 };
1246 }
1247
1248 #[derive(Deserialize)]
1249 struct MessageUpdate {
1250 timestamp: i64,
1251 message: Message,
1252 }
1253
1254 #[derive(Deserialize)]
1255 struct MessageRemovedUpdate {
1256 timestamp: i64,
1257 message_id: String,
1258 chat_id: i64,
1259 user_id: i64,
1260 }
1261
1262 #[derive(Deserialize)]
1263 struct MessageCallbackUpdate {
1264 timestamp: i64,
1265 callback: Callback,
1266 #[serde(default)]
1267 message: Option<Message>,
1268 #[serde(default)]
1269 user_locale: Option<String>,
1270 }
1271
1272 #[derive(Deserialize)]
1273 struct BotStartedUpdate {
1274 timestamp: i64,
1275 chat_id: i64,
1276 user: User,
1277 #[serde(default)]
1278 payload: Option<String>,
1279 #[serde(default)]
1280 user_locale: Option<String>,
1281 }
1282
1283 #[derive(Deserialize)]
1284 struct BotChatUpdate {
1285 timestamp: i64,
1286 chat_id: i64,
1287 user: User,
1288 #[serde(default)]
1289 is_channel: Option<bool>,
1290 }
1291
1292 #[derive(Deserialize)]
1293 struct UserAddedUpdate {
1294 timestamp: i64,
1295 chat_id: i64,
1296 user: User,
1297 #[serde(default)]
1298 inviter_id: Option<i64>,
1299 #[serde(default)]
1300 is_channel: Option<bool>,
1301 }
1302
1303 #[derive(Deserialize)]
1304 struct UserRemovedUpdate {
1305 timestamp: i64,
1306 chat_id: i64,
1307 user: User,
1308 #[serde(default)]
1309 admin_id: Option<i64>,
1310 #[serde(default)]
1311 is_channel: Option<bool>,
1312 }
1313
1314 #[derive(Deserialize)]
1315 struct ChatTitleChangedUpdate {
1316 timestamp: i64,
1317 chat_id: i64,
1318 user: User,
1319 title: String,
1320 }
1321
1322 Ok(match kind {
1323 "message_created" => parse_update!(MessageUpdate, |wire: MessageUpdate| {
1324 Self::MessageCreated {
1325 timestamp: wire.timestamp,
1326 message: wire.message,
1327 }
1328 }),
1329 "message_edited" => parse_update!(MessageUpdate, |wire: MessageUpdate| {
1330 Self::MessageEdited {
1331 timestamp: wire.timestamp,
1332 message: wire.message,
1333 }
1334 }),
1335 "message_removed" => {
1336 parse_update!(MessageRemovedUpdate, |wire: MessageRemovedUpdate| {
1337 Self::MessageRemoved {
1338 timestamp: wire.timestamp,
1339 message_id: wire.message_id,
1340 chat_id: wire.chat_id,
1341 user_id: wire.user_id,
1342 }
1343 })
1344 }
1345 "message_callback" => {
1346 parse_update!(MessageCallbackUpdate, |wire: MessageCallbackUpdate| {
1347 Self::MessageCallback {
1348 timestamp: wire.timestamp,
1349 callback: wire.callback,
1350 message: wire.message,
1351 user_locale: wire.user_locale,
1352 }
1353 })
1354 }
1355 "bot_started" => parse_update!(BotStartedUpdate, |wire: BotStartedUpdate| {
1356 Self::BotStarted {
1357 timestamp: wire.timestamp,
1358 chat_id: wire.chat_id,
1359 user: wire.user,
1360 payload: wire.payload,
1361 user_locale: wire.user_locale,
1362 }
1363 }),
1364 "bot_added" => parse_update!(BotChatUpdate, |wire: BotChatUpdate| {
1365 Self::BotAdded {
1366 timestamp: wire.timestamp,
1367 chat_id: wire.chat_id,
1368 user: wire.user,
1369 is_channel: wire.is_channel,
1370 }
1371 }),
1372 "bot_removed" => parse_update!(BotChatUpdate, |wire: BotChatUpdate| {
1373 Self::BotRemoved {
1374 timestamp: wire.timestamp,
1375 chat_id: wire.chat_id,
1376 user: wire.user,
1377 is_channel: wire.is_channel,
1378 }
1379 }),
1380 "user_added" => parse_update!(UserAddedUpdate, |wire: UserAddedUpdate| {
1381 Self::UserAdded {
1382 timestamp: wire.timestamp,
1383 chat_id: wire.chat_id,
1384 user: wire.user,
1385 inviter_id: wire.inviter_id,
1386 is_channel: wire.is_channel,
1387 }
1388 }),
1389 "user_removed" => parse_update!(UserRemovedUpdate, |wire: UserRemovedUpdate| {
1390 Self::UserRemoved {
1391 timestamp: wire.timestamp,
1392 chat_id: wire.chat_id,
1393 user: wire.user,
1394 admin_id: wire.admin_id,
1395 is_channel: wire.is_channel,
1396 }
1397 }),
1398 "chat_title_changed" => {
1399 parse_update!(ChatTitleChangedUpdate, |wire: ChatTitleChangedUpdate| {
1400 Self::ChatTitleChanged {
1401 timestamp: wire.timestamp,
1402 chat_id: wire.chat_id,
1403 user: wire.user,
1404 title: wire.title,
1405 }
1406 })
1407 }
1408 _ => Self::Unknown {
1409 update_type,
1410 timestamp,
1411 raw,
1412 },
1413 })
1414 }
1415}
1416
1417#[derive(Debug, Clone, Deserialize, Serialize)]
1419pub struct Callback {
1420 pub callback_id: String,
1421 pub user: User,
1422 pub payload: Option<String>,
1423 pub timestamp: i64,
1424}
1425
1426#[derive(Debug, Clone, Deserialize, Serialize)]
1431pub struct Subscription {
1432 pub url: String,
1433 pub time: i64,
1434 pub update_types: Option<Vec<String>>,
1435 pub version: Option<String>,
1436}
1437
1438#[derive(Debug, Clone, Deserialize)]
1439pub struct SubscriptionList {
1440 pub subscriptions: Vec<Subscription>,
1441}
1442
1443#[derive(Debug, Clone, Serialize)]
1444pub struct SubscribeBody {
1445 pub url: String,
1447 #[serde(skip_serializing_if = "Option::is_none")]
1449 pub update_types: Option<Vec<String>>,
1450 #[serde(skip_serializing_if = "Option::is_none")]
1451 pub version: Option<String>,
1452 #[serde(skip_serializing_if = "Option::is_none")]
1455 pub secret: Option<String>,
1456}
1457
1458#[derive(Debug, Clone, Deserialize)]
1465pub struct UploadResponse {
1466 pub token: Option<String>,
1468 pub photos: Option<PhotoTokens>,
1470}
1471
1472#[non_exhaustive]
1473#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1474#[serde(rename_all = "lowercase")]
1475pub enum UploadType {
1476 Image,
1479 Video,
1481 Audio,
1483 File,
1485}
1486
1487impl UploadType {
1488 pub fn as_str(&self) -> &'static str {
1489 match self {
1490 Self::Image => "image",
1491 Self::Video => "video",
1492 Self::Audio => "audio",
1493 Self::File => "file",
1494 }
1495 }
1496}
1497
1498#[derive(Debug, Clone, Deserialize)]
1499pub struct UploadEndpoint {
1500 pub url: String,
1501 pub token: Option<String>,
1502}
1503
1504#[derive(Debug, Clone, Serialize, Default)]
1510pub struct AnswerCallbackBody {
1511 pub callback_id: String,
1512 #[serde(skip_serializing_if = "Option::is_none")]
1513 pub message: Option<NewMessageBody>,
1514 #[serde(skip_serializing_if = "Option::is_none")]
1515 pub notification: Option<String>,
1516}
1517
1518#[derive(Debug, Clone, Deserialize)]
1524pub struct SimpleResult {
1525 pub success: bool,
1526 pub message: Option<String>,
1527 pub failed_user_ids: Option<Vec<i64>>,
1528 pub failed_user_details: Option<Vec<serde_json::Value>>,
1529}
1530
1531#[derive(Debug, Clone, Deserialize, Serialize)]
1532pub struct VideoInfo {
1533 pub token: String,
1534 pub urls: Option<VideoUrls>,
1535 pub thumbnail: Option<PhotoAttachmentPayload>,
1536 pub width: Option<i32>,
1537 pub height: Option<i32>,
1538 pub duration: Option<i32>,
1539}
1540
1541#[derive(Debug, Clone, Default, Deserialize, Serialize)]
1542pub struct VideoUrls {
1543 #[serde(flatten)]
1544 pub values: BTreeMap<String, serde_json::Value>,
1545}
1546
1547#[derive(Debug, Clone, Default, Deserialize, Serialize)]
1548pub struct PhotoAttachmentPayload {
1549 pub url: Option<String>,
1550 pub token: Option<String>,
1551 pub photo_id: Option<i64>,
1552 pub width: Option<i32>,
1553 pub height: Option<i32>,
1554 #[serde(flatten)]
1555 pub extra: BTreeMap<String, serde_json::Value>,
1556}
1557
1558#[derive(Debug, Clone, Serialize)]
1563pub struct ChatMember {
1564 pub user_id: i64,
1565 pub first_name: String,
1566 pub last_name: Option<String>,
1567 pub username: Option<String>,
1568 pub avatar_url: Option<String>,
1569 pub full_avatar_url: Option<String>,
1570 pub description: Option<String>,
1571 pub is_owner: Option<bool>,
1572 pub is_admin: Option<bool>,
1573 pub join_time: Option<i64>,
1574 pub permissions: Option<Vec<ChatAdminPermission>>,
1575 pub last_activity_time: Option<i64>,
1576 pub last_access_time: Option<i64>,
1577 pub is_bot: Option<bool>,
1578 pub alias: Option<String>,
1579}
1580
1581impl<'de> Deserialize<'de> for ChatMember {
1582 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1583 where
1584 D: Deserializer<'de>,
1585 {
1586 #[derive(Deserialize)]
1587 struct WireChatMember {
1588 user_id: i64,
1589 #[serde(default)]
1590 first_name: Option<String>,
1591 #[serde(default)]
1592 name: Option<String>,
1593 #[serde(default)]
1594 last_name: Option<String>,
1595 #[serde(default)]
1596 username: Option<String>,
1597 #[serde(default)]
1598 avatar_url: Option<String>,
1599 #[serde(default)]
1600 full_avatar_url: Option<String>,
1601 #[serde(default)]
1602 description: Option<String>,
1603 #[serde(default)]
1604 is_owner: Option<bool>,
1605 #[serde(default)]
1606 is_admin: Option<bool>,
1607 #[serde(default)]
1608 join_time: Option<i64>,
1609 #[serde(default)]
1610 permissions: Option<Vec<ChatAdminPermission>>,
1611 #[serde(default)]
1612 last_activity_time: Option<i64>,
1613 #[serde(default)]
1614 last_access_time: Option<i64>,
1615 #[serde(default)]
1616 is_bot: Option<bool>,
1617 #[serde(default)]
1618 alias: Option<String>,
1619 }
1620
1621 let wire = WireChatMember::deserialize(deserializer)?;
1622 let first_name = wire
1623 .first_name
1624 .or(wire.name)
1625 .ok_or_else(|| D::Error::missing_field("first_name"))?;
1626
1627 Ok(Self {
1628 user_id: wire.user_id,
1629 first_name,
1630 last_name: wire.last_name,
1631 username: wire.username,
1632 avatar_url: wire.avatar_url,
1633 full_avatar_url: wire.full_avatar_url,
1634 description: wire.description,
1635 is_owner: wire.is_owner,
1636 is_admin: wire.is_admin,
1637 join_time: wire.join_time,
1638 permissions: wire.permissions,
1639 last_activity_time: wire.last_activity_time,
1640 last_access_time: wire.last_access_time,
1641 is_bot: wire.is_bot,
1642 alias: wire.alias,
1643 })
1644 }
1645}
1646
1647#[derive(Debug, Clone, Deserialize)]
1648pub struct ChatMembersList {
1649 pub members: Vec<ChatMember>,
1650 pub marker: Option<i64>,
1651}
1652
1653#[non_exhaustive]
1654#[derive(Debug, Clone, PartialEq, Eq)]
1655pub enum ChatAdminPermission {
1656 ReadAllMessages,
1657 AddRemoveMembers,
1658 AddAdmins,
1659 ChangeChatInfo,
1660 PinMessage,
1661 Write,
1662 CanCall,
1663 EditLink,
1664 PostEditDeleteMessage,
1665 EditMessage,
1666 DeleteMessage,
1667 Unknown(String),
1668}
1669
1670impl ChatAdminPermission {
1671 pub fn as_str(&self) -> &str {
1672 match self {
1673 Self::ReadAllMessages => "read_all_messages",
1674 Self::AddRemoveMembers => "add_remove_members",
1675 Self::AddAdmins => "add_admins",
1676 Self::ChangeChatInfo => "change_chat_info",
1677 Self::PinMessage => "pin_message",
1678 Self::Write => "write",
1679 Self::CanCall => "can_call",
1680 Self::EditLink => "edit_link",
1681 Self::PostEditDeleteMessage => "post_edit_delete_message",
1682 Self::EditMessage => "edit_message",
1683 Self::DeleteMessage => "delete_message",
1684 Self::Unknown(value) => value.as_str(),
1685 }
1686 }
1687}
1688
1689impl Serialize for ChatAdminPermission {
1690 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1691 where
1692 S: Serializer,
1693 {
1694 serialize_string_enum(serializer, self.as_str())
1695 }
1696}
1697
1698impl<'de> Deserialize<'de> for ChatAdminPermission {
1699 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1700 where
1701 D: Deserializer<'de>,
1702 {
1703 deserialize_string_enum(deserializer, |value| match value.as_str() {
1704 "read_all_messages" => Self::ReadAllMessages,
1705 "add_remove_members" => Self::AddRemoveMembers,
1706 "add_admins" => Self::AddAdmins,
1707 "change_chat_info" => Self::ChangeChatInfo,
1708 "pin_message" => Self::PinMessage,
1709 "write" => Self::Write,
1710 "can_call" => Self::CanCall,
1711 "edit_link" => Self::EditLink,
1712 "post_edit_delete_message" => Self::PostEditDeleteMessage,
1713 "edit_message" => Self::EditMessage,
1714 "delete_message" => Self::DeleteMessage,
1715 _ => Self::Unknown(value),
1716 })
1717 }
1718}
1719
1720#[derive(Debug, Clone, Serialize)]
1721pub struct ChatAdmin {
1722 pub user_id: i64,
1723 pub permissions: Vec<ChatAdminPermission>,
1724 #[serde(skip_serializing_if = "Option::is_none")]
1725 pub alias: Option<String>,
1726}
1727
1728#[derive(Debug, Clone, Serialize)]
1729pub struct SetChatAdminsBody {
1730 pub admins: Vec<ChatAdmin>,
1731 #[serde(skip_serializing_if = "Option::is_none")]
1732 pub marker: Option<i64>,
1733}
1734
1735#[derive(Debug, Clone, Serialize)]
1737pub struct AddMembersBody {
1738 pub user_ids: Vec<i64>,
1739}
1740
1741#[derive(Debug, Clone, Serialize)]
1743pub struct RemoveMemberQuery {
1744 pub user_id: i64,
1745}
1746
1747#[derive(Debug, Clone, Deserialize)]
1749pub struct PinnedMessage {
1750 pub message: Message,
1751}
1752
1753#[derive(Debug, Clone, Serialize)]
1755pub struct PinMessageBody {
1756 pub message_id: String,
1757 #[serde(skip_serializing_if = "Option::is_none")]
1758 pub notify: Option<bool>,
1759}
1760
1761#[derive(Debug, Clone, Serialize, Deserialize)]
1763pub struct BotCommand {
1764 pub name: String,
1765 pub description: String,
1766}
1767
1768#[non_exhaustive]
1773#[derive(Debug, Clone, PartialEq, Eq)]
1774pub enum SenderAction {
1775 TypingOn,
1776 SendingImage,
1777 SendingVideo,
1778 SendingAudio,
1779 SendingFile,
1780 MarkSeen,
1781 Unknown(String),
1782}
1783
1784impl SenderAction {
1785 pub fn as_str(&self) -> &str {
1786 match self {
1787 Self::TypingOn => "typing_on",
1788 Self::SendingImage => "sending_photo",
1789 Self::SendingVideo => "sending_video",
1790 Self::SendingAudio => "sending_audio",
1791 Self::SendingFile => "sending_file",
1792 Self::MarkSeen => "mark_seen",
1793 Self::Unknown(value) => value.as_str(),
1794 }
1795 }
1796}
1797
1798impl Serialize for SenderAction {
1799 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1800 where
1801 S: Serializer,
1802 {
1803 serialize_string_enum(serializer, self.as_str())
1804 }
1805}
1806
1807impl<'de> Deserialize<'de> for SenderAction {
1808 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1809 where
1810 D: Deserializer<'de>,
1811 {
1812 deserialize_string_enum(deserializer, |value| match value.as_str() {
1813 "typing_on" => Self::TypingOn,
1814 "sending_photo" => Self::SendingImage,
1815 "sending_video" => Self::SendingVideo,
1816 "sending_audio" => Self::SendingAudio,
1817 "sending_file" => Self::SendingFile,
1818 "mark_seen" => Self::MarkSeen,
1819 _ => Self::Unknown(value),
1820 })
1821 }
1822}
1823
1824impl fmt::Display for SenderAction {
1825 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1826 f.write_str(self.as_str())
1827 }
1828}