tg_flows/types/
update.rs

1#![allow(clippy::large_enum_variant)]
2use serde::{de::MapAccess, Deserialize, Serialize, Serializer};
3use serde_json::Value;
4
5use crate::types::{
6    CallbackQuery, Chat, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InlineQuery,
7    Message, Poll, PollAnswer, PreCheckoutQuery, ShippingQuery, User,
8};
9
10/// This [object] represents an incoming update.
11///
12/// [The official docs](https://core.telegram.org/bots/api#update).
13///
14/// [object]: https://core.telegram.org/bots/api#available-types
15#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
16pub struct Update {
17    /// The update‘s unique identifier. Update identifiers start from a certain
18    /// positive number and increase sequentially. This ID becomes especially
19    /// handy if you’re using webhooks, since it allows you to ignore
20    /// repeated updates or to restore the correct update sequence, should
21    /// they get out of order. If there are no new updates for at least a
22    /// week, then identifier of the next update will be chosen randomly
23    /// instead of sequentially.
24    #[serde(rename = "update_id")]
25    pub id: i32,
26
27    #[serde(flatten)]
28    pub kind: UpdateKind,
29}
30
31impl Update {
32    // FIXME: add mentioned_users -> impl Iterator<&User>
33
34    /// Returns the user that performed the action that caused this update, if
35    /// known.
36    ///
37    /// This is generally the `from` field (except for `PollAnswer` where it's
38    /// `user` and `Poll` with `Error` which don't have such field at all).
39    #[must_use]
40    pub fn from(&self) -> Option<&User> {
41        use UpdateKind::*;
42
43        let from = match &self.kind {
44            Message(m) | EditedMessage(m) | ChannelPost(m) | EditedChannelPost(m) => m.from()?,
45
46            CallbackQuery(query) => &query.from,
47            ChosenInlineResult(chosen) => &chosen.from,
48            InlineQuery(query) => &query.from,
49            ShippingQuery(query) => &query.from,
50            PreCheckoutQuery(query) => &query.from,
51            PollAnswer(answer) => &answer.user,
52
53            MyChatMember(m) | ChatMember(m) => &m.from,
54            ChatJoinRequest(r) => &r.from,
55
56            Poll(_) | Error(_) => return None,
57        };
58
59        Some(from)
60    }
61
62    /// Returns all users that are "contained" in this `Update` structure.
63    ///
64    /// This might be useful to track information about users.
65    ///
66    /// Note that this function may return quite a few users as it scans
67    /// replies, pinned messages, message entities, "via bot" fields and more.
68    /// Also note that this function can return duplicate users.
69    pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
70        use either::Either::{Left, Right};
71        use std::iter::{empty, once};
72
73        let i0 = Left;
74        let i1 = |x| Right(Left(x));
75        let i2 = |x| Right(Right(Left(x)));
76        let i3 = |x| Right(Right(Right(Left(x))));
77        let i4 = |x| Right(Right(Right(Right(Left(x)))));
78        let i5 = |x| Right(Right(Right(Right(Right(Left(x))))));
79        let i6 = |x| Right(Right(Right(Right(Right(Right(x))))));
80
81        match &self.kind {
82            UpdateKind::Message(message)
83            | UpdateKind::EditedMessage(message)
84            | UpdateKind::ChannelPost(message)
85            | UpdateKind::EditedChannelPost(message) => i0(message.mentioned_users()),
86
87            UpdateKind::InlineQuery(query) => i1(once(&query.from)),
88            UpdateKind::ChosenInlineResult(query) => i1(once(&query.from)),
89            UpdateKind::CallbackQuery(query) => i2(query.mentioned_users()),
90            UpdateKind::ShippingQuery(query) => i1(once(&query.from)),
91            UpdateKind::PreCheckoutQuery(query) => i1(once(&query.from)),
92            UpdateKind::Poll(poll) => i3(poll.mentioned_users()),
93
94            UpdateKind::PollAnswer(answer) => i1(once(&answer.user)),
95
96            UpdateKind::MyChatMember(member) | UpdateKind::ChatMember(member) => {
97                i4(member.mentioned_users())
98            }
99            UpdateKind::ChatJoinRequest(request) => i5(request.mentioned_users()),
100            UpdateKind::Error(_) => i6(empty()),
101        }
102    }
103
104    /// Returns the chat in which is update has happened, if any.
105    #[must_use]
106    pub fn chat(&self) -> Option<&Chat> {
107        use UpdateKind::*;
108
109        let chat = match &self.kind {
110            Message(m) | EditedMessage(m) | ChannelPost(m) | EditedChannelPost(m) => &m.chat,
111            CallbackQuery(q) => &q.message.as_ref()?.chat,
112            ChatMember(m) => &m.chat,
113            MyChatMember(m) => &m.chat,
114            ChatJoinRequest(c) => &c.chat,
115
116            InlineQuery(_)
117            | ChosenInlineResult(_)
118            | ShippingQuery(_)
119            | PreCheckoutQuery(_)
120            | Poll(_)
121            | PollAnswer(_)
122            | Error(_) => return None,
123        };
124
125        Some(chat)
126    }
127
128    #[deprecated(note = "renamed to `from`", since = "0.10.0")]
129    pub fn user(&self) -> Option<&User> {
130        self.from()
131    }
132}
133
134#[derive(Clone, Debug, PartialEq)]
135pub enum UpdateKind {
136    // NB: When adding new variants, don't forget to update
137    //     - `AllowedUpdate`
138    //     - `Update::user`
139    //     - `Update::chat`
140    //     - `DpHandlerDescription::full_set`
141    //     - `dispatching/filter_ext.rs`
142    /// New incoming message of any kind — text, photo, sticker, etc.
143    Message(Message),
144
145    /// New version of a message that is known to the bot and was edited.
146    EditedMessage(Message),
147
148    /// New incoming channel post of any kind — text, photo, sticker, etc.
149    ChannelPost(Message),
150
151    /// New version of a channel post that is known to the bot and was edited.
152    EditedChannelPost(Message),
153
154    /// New incoming [inline] query.
155    ///
156    /// [inline]: https://core.telegram.org/bots/api#inline-mode
157    InlineQuery(InlineQuery),
158
159    /// The result of an [inline] query that was chosen by a user and sent to
160    /// their chat partner. Please see our documentation on the [feedback
161    /// collecting] for details on how to enable these updates for your bot.
162    ///
163    /// [inline]: https://core.telegram.org/bots/api#inline-mode
164    /// [feedback collecting]: https://core.telegram.org/bots/inline#collecting-feedback
165    ChosenInlineResult(ChosenInlineResult),
166
167    /// New incoming callback query.
168    CallbackQuery(CallbackQuery),
169
170    /// New incoming shipping query. Only for invoices with flexible price.
171    ShippingQuery(ShippingQuery),
172
173    /// New incoming pre-checkout query. Contains full information about
174    /// checkout.
175    PreCheckoutQuery(PreCheckoutQuery),
176
177    /// New poll state. Bots receive only updates about stopped polls and
178    /// polls, which are sent by the bot.
179    Poll(Poll),
180
181    /// A user changed their answer in a non-anonymous poll. Bots receive new
182    /// votes only in polls that were sent by the bot itself.
183    PollAnswer(PollAnswer),
184
185    /// The bot's chat member status was updated in a chat. For private chats,
186    /// this update is received only when the bot is blocked or unblocked by the
187    /// user.
188    MyChatMember(ChatMemberUpdated),
189
190    /// A chat member's status was updated in a chat. The bot must be an
191    /// administrator in the chat and must explicitly specify
192    /// [`AllowedUpdate::ChatMember`] in the list of `allowed_updates` to
193    /// receive these updates.
194    ///
195    /// [`AllowedUpdate::ChatMember`]: crate::types::AllowedUpdate::ChatMember
196    ChatMember(ChatMemberUpdated),
197
198    /// A request to join the chat has been sent. The bot must have the
199    /// can_invite_users administrator right in the chat to receive these
200    /// updates.
201    ChatJoinRequest(ChatJoinRequest),
202
203    /// An error that happened during deserialization.
204    ///
205    /// This allows `teloxide` to continue working even if telegram adds a new
206    /// kinds of updates.
207    ///
208    /// **Note that deserialize implementation always returns an empty value**,
209    /// teloxide fills in the data when doing deserialization.
210    Error(Value),
211}
212
213impl<'de> Deserialize<'de> for UpdateKind {
214    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
215    where
216        D: serde::Deserializer<'de>,
217    {
218        struct Visitor;
219
220        impl<'de> serde::de::Visitor<'de> for Visitor {
221            type Value = UpdateKind;
222
223            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
224                formatter.write_str("a map")
225            }
226
227            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
228            where
229                A: MapAccess<'de>,
230            {
231                let mut tmp = None;
232
233                // Try to deserialize a borrowed-str key, or else try deserializing an owned
234                // string key
235                let key = map.next_key::<&str>().or_else(|_| {
236                    map.next_key::<String>().map(|k| {
237                        tmp = k;
238                        tmp.as_deref()
239                    })
240                });
241
242                let this = key
243                    .ok()
244                    .flatten()
245                    .and_then(|key| match key {
246                        "message" => map.next_value::<Message>().ok().map(UpdateKind::Message),
247                        "edited_message" => map
248                            .next_value::<Message>()
249                            .ok()
250                            .map(UpdateKind::EditedMessage),
251                        "channel_post" => map
252                            .next_value::<Message>()
253                            .ok()
254                            .map(UpdateKind::ChannelPost),
255                        "edited_channel_post" => map
256                            .next_value::<Message>()
257                            .ok()
258                            .map(UpdateKind::EditedChannelPost),
259                        "inline_query" => map
260                            .next_value::<InlineQuery>()
261                            .ok()
262                            .map(UpdateKind::InlineQuery),
263                        "chosen_inline_result" => map
264                            .next_value::<ChosenInlineResult>()
265                            .ok()
266                            .map(UpdateKind::ChosenInlineResult),
267                        "callback_query" => map
268                            .next_value::<CallbackQuery>()
269                            .ok()
270                            .map(UpdateKind::CallbackQuery),
271                        "shipping_query" => map
272                            .next_value::<ShippingQuery>()
273                            .ok()
274                            .map(UpdateKind::ShippingQuery),
275                        "pre_checkout_query" => map
276                            .next_value::<PreCheckoutQuery>()
277                            .ok()
278                            .map(UpdateKind::PreCheckoutQuery),
279                        "poll" => map.next_value::<Poll>().ok().map(UpdateKind::Poll),
280                        "poll_answer" => map
281                            .next_value::<PollAnswer>()
282                            .ok()
283                            .map(UpdateKind::PollAnswer),
284                        "my_chat_member" => map
285                            .next_value::<ChatMemberUpdated>()
286                            .ok()
287                            .map(UpdateKind::MyChatMember),
288                        "chat_member" => map
289                            .next_value::<ChatMemberUpdated>()
290                            .ok()
291                            .map(UpdateKind::ChatMember),
292                        "chat_join_request" => map
293                            .next_value::<ChatJoinRequest>()
294                            .ok()
295                            .map(UpdateKind::ChatJoinRequest),
296                        _ => Some(empty_error()),
297                    })
298                    .unwrap_or_else(empty_error);
299
300                Ok(this)
301            }
302        }
303
304        deserializer.deserialize_any(Visitor)
305    }
306}
307
308impl Serialize for UpdateKind {
309    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
310    where
311        S: Serializer,
312    {
313        let name = "UpdateKind";
314        match self {
315            UpdateKind::Message(v) => s.serialize_newtype_variant(name, 0, "message", v),
316            UpdateKind::EditedMessage(v) => {
317                s.serialize_newtype_variant(name, 1, "edited_message", v)
318            }
319            UpdateKind::ChannelPost(v) => s.serialize_newtype_variant(name, 2, "channel_post", v),
320            UpdateKind::EditedChannelPost(v) => {
321                s.serialize_newtype_variant(name, 3, "edited_channel_post", v)
322            }
323            UpdateKind::InlineQuery(v) => s.serialize_newtype_variant(name, 4, "inline_query", v),
324            UpdateKind::ChosenInlineResult(v) => {
325                s.serialize_newtype_variant(name, 5, "chosen_inline_result", v)
326            }
327            UpdateKind::CallbackQuery(v) => {
328                s.serialize_newtype_variant(name, 6, "callback_query", v)
329            }
330            UpdateKind::ShippingQuery(v) => {
331                s.serialize_newtype_variant(name, 7, "shipping_query", v)
332            }
333            UpdateKind::PreCheckoutQuery(v) => {
334                s.serialize_newtype_variant(name, 8, "pre_checkout_query", v)
335            }
336            UpdateKind::Poll(v) => s.serialize_newtype_variant(name, 9, "poll", v),
337            UpdateKind::PollAnswer(v) => s.serialize_newtype_variant(name, 10, "poll_answer", v),
338            UpdateKind::MyChatMember(v) => {
339                s.serialize_newtype_variant(name, 11, "my_chat_member", v)
340            }
341            UpdateKind::ChatMember(v) => s.serialize_newtype_variant(name, 12, "chat_member", v),
342            UpdateKind::ChatJoinRequest(v) => {
343                s.serialize_newtype_variant(name, 13, "chat_join_request", v)
344            }
345            UpdateKind::Error(v) => v.serialize(s),
346        }
347    }
348}
349
350fn empty_error() -> UpdateKind {
351    UpdateKind::Error(Value::Object(<_>::default()))
352}
353
354#[cfg(test)]
355mod test {
356    use crate::types::{
357        Chat, ChatId, ChatKind, ChatPrivate, MediaKind, MediaText, Message, MessageCommon,
358        MessageId, MessageKind, Update, UpdateKind, User, UserId,
359    };
360
361    use chrono::{DateTime, NaiveDateTime, Utc};
362
363    // TODO: more tests for deserialization
364    #[test]
365    fn message() {
366        let timestamp = 1_569_518_342;
367        let date = DateTime::from_utc(
368            NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap(),
369            Utc,
370        );
371
372        let json = r#"{
373            "update_id":892252934,
374            "message":{
375                "message_id":6557,
376                "from":{
377                    "id":218485655,
378                    "is_bot": false,
379                    "first_name":"Waffle",
380                    "username":"WaffleLapkin",
381                    "language_code":"en"
382                },
383                "chat":{
384                    "id":218485655,
385                    "first_name":"Waffle",
386                    "username":"WaffleLapkin",
387                    "type":"private"
388                },
389               "date":1569518342,
390               "text":"hello there"
391            }
392        }"#;
393
394        let expected = Update {
395            id: 892_252_934,
396            kind: UpdateKind::Message(Message {
397                via_bot: None,
398                id: MessageId(6557),
399                thread_id: None,
400                date,
401                chat: Chat {
402                    id: ChatId(218_485_655),
403                    kind: ChatKind::Private(ChatPrivate {
404                        username: Some(String::from("WaffleLapkin")),
405                        first_name: Some(String::from("Waffle")),
406                        last_name: None,
407                        bio: None,
408                        has_private_forwards: None,
409                        has_restricted_voice_and_video_messages: None,
410                        emoji_status_custom_emoji_id: None,
411                    }),
412                    photo: None,
413                    pinned_message: None,
414                    message_auto_delete_time: None,
415                    has_hidden_members: false,
416                    has_aggressive_anti_spam_enabled: false,
417                },
418                kind: MessageKind::Common(MessageCommon {
419                    from: Some(User {
420                        id: UserId(218_485_655),
421                        is_bot: false,
422                        first_name: String::from("Waffle"),
423                        last_name: None,
424                        username: Some(String::from("WaffleLapkin")),
425                        language_code: Some(String::from("en")),
426                        is_premium: false,
427                        added_to_attachment_menu: false,
428                    }),
429                    reply_to_message: None,
430                    forward: None,
431                    edit_date: None,
432                    media_kind: MediaKind::Text(MediaText {
433                        text: String::from("hello there"),
434                        entities: vec![],
435                    }),
436                    reply_markup: None,
437                    sender_chat: None,
438                    author_signature: None,
439                    is_topic_message: false,
440                    is_automatic_forward: false,
441                    has_protected_content: false,
442                }),
443            }),
444        };
445
446        let actual = serde_json::from_str::<Update>(json).unwrap();
447        assert_eq!(expected, actual);
448    }
449
450    #[test]
451    fn de_private_chat_text_message() {
452        let text = r#"
453  {
454    "message": {
455      "chat": {
456        "first_name": "Hirrolot",
457        "id": 408258968,
458        "type": "private",
459        "username": "hirrolot"
460      },
461      "date": 1581448857,
462      "from": {
463        "first_name": "Hirrolot",
464        "id": 408258968,
465        "is_bot": false,
466        "language_code": "en",
467        "username": "hirrolot"
468      },
469      "message_id": 154,
470      "text": "4"
471    },
472    "update_id": 306197398
473  }
474"#;
475
476        let Update { kind, .. } = serde_json::from_str::<Update>(text).unwrap();
477        match kind {
478            UpdateKind::Message(_) => {}
479            _ => panic!("Expected `Message`"),
480        }
481    }
482
483    #[test]
484    fn pinned_message_works() {
485        let json = r#"{
486    "message": {
487        "chat": {
488            "id": -1001276785818,
489            "title": "teloxide dev",
490            "type": "supergroup",
491            "username": "teloxide_dev"
492        },
493        "date": 1582134655,
494        "from": {
495            "first_name": "Hirrolot",
496            "id": 408258968,
497            "is_bot": false,
498            "username": "hirrolot"
499        },
500        "message_id": 20225,
501        "pinned_message": {
502            "chat": {
503                "id": -1001276785818,
504                "title": "teloxide dev",
505                "type": "supergroup",
506                "username": "teloxide_dev"
507            },
508            "date": 1582134643,
509            "from": {
510                "first_name": "Hirrolot",
511                "id": 408258968,
512                "is_bot": false,
513                "username": "hirrolot"
514            },
515            "message_id": 20224,
516            "text": "Faster than a bullet"
517        }
518    },
519    "update_id": 845402291
520}"#;
521
522        let Update { kind, .. } = serde_json::from_str(json).unwrap();
523        match kind {
524            UpdateKind::Message(_) => {}
525            _ => panic!("Expected `Message`"),
526        }
527    }
528
529    #[test]
530    fn dice_works() {
531        let json = r#"
532        {
533    "message": {
534        "chat": {
535            "id": -1001276785818,
536            "title": "bla bla bla chat",
537            "type": "supergroup",
538            "username": "teloxide_dev"
539        },
540        "date": 1596014550,
541        "dice": {
542            "emoji": "🎲",
543            "value": 2
544        },
545        "from": {
546            "first_name": "Hirrolot",
547            "id": 408258968,
548            "is_bot": false,
549            "language_code": "en",
550            "username": "hirrolot"
551        },
552        "message_id": 35410
553    },
554    "update_id": 573255266
555}
556        "#;
557
558        let Update { kind, .. } = serde_json::from_str(json).unwrap();
559        match kind {
560            UpdateKind::Message(_) => {}
561            _ => panic!("Expected `Message`"),
562        }
563    }
564
565    #[test]
566    fn new_update_kind_error() {
567        let json = r#"{
568            "new_update_kind": {"some_field_idk": 1},
569            "update_id": 1
570        }"#;
571
572        let Update { kind, .. } = serde_json::from_str(json).unwrap();
573
574        match kind {
575            // Deserialization failed successfully
576            UpdateKind::Error(_) => {}
577            _ => panic!("Expected error"),
578        }
579    }
580
581    #[test]
582    fn issue_523() {
583        let json = r#"{
584            "update_id":0,
585            "my_chat_member": {
586                "chat":{"id":0,"first_name":"FN","last_name":"LN","username":"UN","type":"private"},
587                "from":{"id":0,"is_bot":false,"first_name":"FN","last_name":"LN","username":"UN"},
588                "date":1644677726,
589                "old_chat_member":{"user":{"id":1,"is_bot":true,"first_name":"bot","username":"unBot"},"status":"member"},
590                "new_chat_member":{"user":{"id":1,"is_bot":true,"first_name":"bot","username":"unBot"},"status":"kicked","until_date":0}
591            }
592        }"#;
593
594        let Update { kind, .. } = serde_json::from_str(json).unwrap();
595
596        match kind {
597            UpdateKind::MyChatMember(_) => {}
598            _ => panic!("Expected `MyChatMember`"),
599        }
600    }
601}