Skip to main content

rustigram_types/
message.rs

1use serde::{Deserialize, Serialize};
2
3use crate::background::ChatBackground;
4use crate::chat::{Chat, Location, Venue};
5use crate::checklist::{Checklist, ChecklistTasksAdded, ChecklistTasksDone};
6use crate::community::{CommunityChatAdded, CommunityChatRemoved};
7use crate::direct_messages::{
8    DirectMessagePriceChanged, DirectMessagesTopic, PaidMessagePriceChanged,
9};
10use crate::file::{Animation, Audio, Document, LivePhoto, PhotoSize, Video, VideoNote, Voice};
11use crate::forum::{
12    ForumTopicClosed, ForumTopicCreated, ForumTopicEdited, ForumTopicReopened,
13    GeneralForumTopicHidden, GeneralForumTopicUnhidden,
14};
15use crate::games::Game;
16use crate::gifts::{GiftInfo, UniqueGiftInfo};
17use crate::giveaway::{Giveaway, GiveawayCompleted, GiveawayCreated, GiveawayWinners};
18use crate::keyboard::InlineKeyboardMarkup;
19use crate::managed_bot::ManagedBotCreated;
20use crate::passport::PassportData;
21use crate::payments::{Invoice, PaidMediaInfo, RefundedPayment, SuccessfulPayment};
22use crate::poll::{Poll, PollOptionAdded, PollOptionDeleted};
23use crate::rich_message::RichMessage;
24use crate::shared::{ChatShared, UsersShared};
25use crate::sticker::Sticker;
26use crate::story::Story;
27use crate::suggested_post::{
28    SuggestedPostApprovalFailed, SuggestedPostApproved, SuggestedPostDeclined, SuggestedPostInfo,
29    SuggestedPostPaid, SuggestedPostRefunded,
30};
31use crate::update::ChatBoostAdded;
32use crate::user::User;
33use crate::video_chat::{
34    VideoChatEnded, VideoChatParticipantsInvited, VideoChatScheduled, VideoChatStarted,
35};
36
37/// A Telegram message.
38///
39/// Only fields that were actually sent will be `Some`. Consult the
40/// official Bot API documentation for field availability rules.
41///
42/// This is the largest and most frequently extended object in the Bot API, so
43/// it is `#[non_exhaustive]`. Construct one with [`Default::default`] and
44/// assign the fields you need; every future field Telegram adds then arrives
45/// as a non-breaking change rather than a major version bump.
46///
47/// ```rust,ignore
48/// let mut msg = Message::default();
49/// msg.message_id = 1;
50/// msg.text = Some("hello".to_owned());
51/// ```
52#[derive(Debug, Clone, Default, Serialize, Deserialize)]
53#[non_exhaustive]
54pub struct Message {
55    /// Unique message identifier inside this chat; `0` for ephemeral messages.
56    /// In specific instances (e.g. a video sent to a large chat), the server
57    /// might schedule the message instead of sending it immediately — in that
58    /// case this field is also `0` until the message is actually sent.
59    pub message_id: i64,
60
61    /// For ephemeral messages — the user who received the message.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub receiver_user: Option<User>,
64
65    /// For ephemeral messages — identifier of the message inside this chat.
66    /// The identifier may be reused for another ephemeral message once this
67    /// one is deleted or expires.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub ephemeral_message_id: Option<i64>,
70
71    /// Optional — unique identifier of a message thread to which the message belongs.
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub message_thread_id: Option<i64>,
74
75    /// Information about the direct messages chat topic that contains the message.
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub direct_messages_topic: Option<DirectMessagesTopic>,
78
79    /// Sender of the message; empty for messages sent to channels.
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub from: Option<User>,
82
83    /// Sender of the message; empty for messages sent to channels or on behalf of a chat.
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub sender_chat: Option<Chat>,
86
87    /// For supergroup messages — boost count of the sender.
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub sender_boost_count: Option<u32>,
90
91    /// The bot that actually sent the message on behalf of the business account.
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub sender_business_bot: Option<User>,
94
95    /// Tag or custom title of the sender; for supergroups only (Bot API 9.5).
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub sender_tag: Option<String>,
98
99    /// Date the message was sent, as a Unix timestamp.
100    pub date: i64,
101
102    /// Unique identifier of the business connection from which the message was received.
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub business_connection_id: Option<String>,
105
106    /// Conversation the message belongs to.
107    pub chat: Chat,
108
109    /// Information about the original message for forwarded messages.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub forward_origin: Option<MessageOrigin>,
112
113    /// `true` if the message is sent to a forum topic.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub is_topic_message: Option<bool>,
116
117    /// `true` if the message is a channel post automatically forwarded to the
118    /// connected discussion group.
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub is_automatic_forward: Option<bool>,
121
122    /// For replies in the same chat and message thread, the original message.
123    /// The nested `Message` never carries its own `reply_to_message`, even if
124    /// it is itself a reply. May be omitted for replies to an ephemeral message.
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub reply_to_message: Option<Box<Message>>,
127
128    /// Information about the message that is being replied to from another chat or topic.
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub external_reply: Option<ExternalReplyInfo>,
131
132    /// For replies that quote part of the original message, the quoted part.
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub quote: Option<TextQuote>,
135
136    /// For replies to a story, the original story.
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub reply_to_story: Option<Story>,
139
140    /// Identifier of the checklist task being replied to.
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub reply_to_checklist_task_id: Option<i64>,
143
144    /// Persistent identifier of the poll option being replied to.
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub reply_to_poll_option_id: Option<String>,
147
148    /// Bot through which the message was sent.
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub via_bot: Option<User>,
151
152    /// Date the message was last edited, as a Unix timestamp.
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub edit_date: Option<i64>,
155
156    /// `true` if the message can't be forwarded.
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub has_protected_content: Option<bool>,
159
160    /// `true` if the message was sent by an implicit action.
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub is_from_offline: Option<bool>,
163
164    /// `true` if the message is a paid post.
165    ///
166    /// Paid posts must not be deleted for 24 hours after sending and cannot be edited.
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub is_paid_post: Option<bool>,
169
170    /// The unique identifier of a media message group this message belongs to.
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub media_group_id: Option<String>,
173
174    /// Signature of the post author for messages in channels.
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub author_signature: Option<String>,
177
178    /// Number of Telegram Stars paid by the sender to send this message.
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub paid_star_count: Option<i64>,
181
182    /// Actual UTF-8 text of the message (0–4096 characters).
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub text: Option<String>,
185
186    /// Rich formatted message content.
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub rich_message: Option<RichMessage>,
189
190    /// Special entities like usernames, URLs, bot commands, etc.
191    #[serde(skip_serializing_if = "Option::is_none")]
192    pub entities: Option<Vec<MessageEntity>>,
193
194    /// Options used for link preview generation.
195    #[serde(skip_serializing_if = "Option::is_none")]
196    pub link_preview_options: Option<LinkPreviewOptions>,
197
198    /// Information about a suggested post; present when the message is a suggested
199    /// post in a channel direct messages chat.
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub suggested_post_info: Option<SuggestedPostInfo>,
202
203    /// Unique identifier of the message effect added to the message.
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub effect_id: Option<String>,
206
207    // Media fields
208    /// Animation attached to the message.
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub animation: Option<Animation>,
211    /// Audio file attached to the message.
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub audio: Option<Audio>,
214    /// Document attached to the message.
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub document: Option<Document>,
217    /// Paid media attached to the message.
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub paid_media: Option<PaidMediaInfo>,
220    /// Photo attached to the message (array of sizes).
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub photo: Option<Vec<PhotoSize>>,
223    /// Live photo attached to the message.
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub live_photo: Option<LivePhoto>,
226    /// Sticker attached to the message.
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub sticker: Option<Sticker>,
229    /// Story attached to the message.
230    #[serde(skip_serializing_if = "Option::is_none")]
231    pub story: Option<Story>,
232    /// Video attached to the message.
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub video: Option<Video>,
235    /// Video note attached to the message.
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub video_note: Option<VideoNote>,
238    /// Voice note attached to the message.
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub voice: Option<Voice>,
241
242    /// Caption for the animation, audio, document, paid media, photo, video or voice.
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub caption: Option<String>,
245
246    /// Special entities in the caption.
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub caption_entities: Option<Vec<MessageEntity>>,
249
250    /// `true` if the caption must be shown above the message media.
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub show_caption_above_media: Option<bool>,
253
254    /// `true` if the message media is covered by a spoiler animation.
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub has_media_spoiler: Option<bool>,
257
258    /// Checklist attached to the message.
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub checklist: Option<Checklist>,
261
262    // Service message types
263    /// Contact shared in the message.
264    #[serde(skip_serializing_if = "Option::is_none")]
265    pub contact: Option<Contact>,
266    /// Dice result in the message.
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub dice: Option<Dice>,
269    /// Game in the message.
270    #[serde(skip_serializing_if = "Option::is_none")]
271    pub game: Option<Game>,
272    /// Poll in the message.
273    #[serde(skip_serializing_if = "Option::is_none")]
274    pub poll: Option<Poll>,
275    /// Venue in the message.
276    #[serde(skip_serializing_if = "Option::is_none")]
277    pub venue: Option<Venue>,
278    /// Location in the message.
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub location: Option<Location>,
281
282    // Group events
283    /// New members that joined the group.
284    #[serde(skip_serializing_if = "Option::is_none")]
285    pub new_chat_members: Option<Vec<User>>,
286    /// A member that left the group.
287    #[serde(skip_serializing_if = "Option::is_none")]
288    pub left_chat_member: Option<User>,
289    /// Service message: chat owner has left.
290    #[serde(skip_serializing_if = "Option::is_none")]
291    pub chat_owner_left: Option<ChatOwnerLeft>,
292    /// Service message: chat owner has changed.
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub chat_owner_changed: Option<ChatOwnerChanged>,
295    /// New chat title (service message).
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub new_chat_title: Option<String>,
298    /// New chat photo (service message).
299    #[serde(skip_serializing_if = "Option::is_none")]
300    pub new_chat_photo: Option<Vec<PhotoSize>>,
301    /// `true` if the chat photo was deleted (service message).
302    #[serde(skip_serializing_if = "Option::is_none")]
303    pub delete_chat_photo: Option<bool>,
304    /// `true` if the group was created (service message).
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub group_chat_created: Option<bool>,
307    /// `true` if the supergroup was created (service message).
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub supergroup_chat_created: Option<bool>,
310    /// `true` if the channel was created (service message).
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub channel_chat_created: Option<bool>,
313    /// Service message: chat added to a Community.
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub community_chat_added: Option<CommunityChatAdded>,
316    /// Service message: chat removed from a Community.
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub community_chat_removed: Option<CommunityChatRemoved>,
319    /// Auto-delete timer changed (service message).
320    #[serde(skip_serializing_if = "Option::is_none")]
321    pub message_auto_delete_timer_changed: Option<MessageAutoDeleteTimerChanged>,
322    /// The group has been migrated to a supergroup with this ID.
323    #[serde(skip_serializing_if = "Option::is_none")]
324    pub migrate_to_chat_id: Option<i64>,
325    /// The supergroup has been migrated from a group with this ID.
326    #[serde(skip_serializing_if = "Option::is_none")]
327    pub migrate_from_chat_id: Option<i64>,
328    /// The pinned message (service message).
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub pinned_message: Option<Box<Message>>,
331
332    /// Inline keyboard attached to the message.
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub reply_markup: Option<InlineKeyboardMarkup>,
335
336    // Payment fields
337    /// Invoice for a payment (service message).
338    #[serde(skip_serializing_if = "Option::is_none")]
339    pub invoice: Option<Invoice>,
340    /// Successful payment information (service message).
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub successful_payment: Option<SuccessfulPayment>,
343    /// Refunded payment information (service message).
344    #[serde(skip_serializing_if = "Option::is_none")]
345    pub refunded_payment: Option<RefundedPayment>,
346
347    // Web app
348    /// Data from the Web App.
349    #[serde(skip_serializing_if = "Option::is_none")]
350    pub web_app_data: Option<WebAppData>,
351
352    // Forum topic events
353    /// Forum topic created (service message).
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub forum_topic_created: Option<ForumTopicCreated>,
356    /// Forum topic edited (service message).
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub forum_topic_edited: Option<ForumTopicEdited>,
359    /// Forum topic closed (service message).
360    #[serde(skip_serializing_if = "Option::is_none")]
361    pub forum_topic_closed: Option<ForumTopicClosed>,
362    /// Forum topic reopened (service message).
363    #[serde(skip_serializing_if = "Option::is_none")]
364    pub forum_topic_reopened: Option<ForumTopicReopened>,
365    /// General forum topic hidden (service message).
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub general_forum_topic_hidden: Option<GeneralForumTopicHidden>,
368    /// General forum topic unhidden (service message).
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub general_forum_topic_unhidden: Option<GeneralForumTopicUnhidden>,
371
372    // Sharing, access, and boosts
373    /// Users were shared with the bot (service message).
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub users_shared: Option<UsersShared>,
376    /// A chat was shared with the bot (service message).
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub chat_shared: Option<ChatShared>,
379    /// Domain of the website the user logged in on via Telegram Login.
380    #[serde(skip_serializing_if = "Option::is_none")]
381    pub connected_website: Option<String>,
382    /// The user allowed the bot to write to them (service message).
383    #[serde(skip_serializing_if = "Option::is_none")]
384    pub write_access_allowed: Option<WriteAccessAllowed>,
385    /// Telegram Passport data shared with the bot.
386    #[serde(skip_serializing_if = "Option::is_none")]
387    pub passport_data: Option<PassportData>,
388    /// A user came within another user's proximity radius (service message).
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub proximity_alert_triggered: Option<ProximityAlertTriggered>,
391    /// The chat was boosted (service message).
392    #[serde(skip_serializing_if = "Option::is_none")]
393    pub boost_added: Option<ChatBoostAdded>,
394    /// The chat background was changed (service message).
395    #[serde(skip_serializing_if = "Option::is_none")]
396    pub chat_background_set: Option<ChatBackground>,
397
398    // Gifts and giveaways
399    /// A regular gift was sent or received (service message).
400    #[serde(skip_serializing_if = "Option::is_none")]
401    pub gift: Option<GiftInfo>,
402    /// A unique gift was sent or received (service message).
403    #[serde(skip_serializing_if = "Option::is_none")]
404    pub unique_gift: Option<UniqueGiftInfo>,
405    /// A gift upgrade was sent as a separate service message.
406    #[serde(skip_serializing_if = "Option::is_none")]
407    pub gift_upgrade_sent: Option<GiftInfo>,
408    /// A scheduled giveaway was created (service message).
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub giveaway_created: Option<GiveawayCreated>,
411    /// The message is a scheduled giveaway.
412    #[serde(skip_serializing_if = "Option::is_none")]
413    pub giveaway: Option<Giveaway>,
414    /// Giveaway winners were selected (service message).
415    #[serde(skip_serializing_if = "Option::is_none")]
416    pub giveaway_winners: Option<GiveawayWinners>,
417    /// A giveaway without public winners completed (service message).
418    #[serde(skip_serializing_if = "Option::is_none")]
419    pub giveaway_completed: Option<GiveawayCompleted>,
420
421    // Video chat events
422    /// A video chat was scheduled (service message).
423    #[serde(skip_serializing_if = "Option::is_none")]
424    pub video_chat_scheduled: Option<VideoChatScheduled>,
425    /// A video chat started (service message).
426    #[serde(skip_serializing_if = "Option::is_none")]
427    pub video_chat_started: Option<VideoChatStarted>,
428    /// A video chat ended (service message).
429    #[serde(skip_serializing_if = "Option::is_none")]
430    pub video_chat_ended: Option<VideoChatEnded>,
431    /// New participants were invited to a video chat (service message).
432    #[serde(skip_serializing_if = "Option::is_none")]
433    pub video_chat_participants_invited: Option<VideoChatParticipantsInvited>,
434
435    // Managed bot events
436    /// Service message: a new managed bot was created (Bot API 9.6).
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub managed_bot_created: Option<ManagedBotCreated>,
439
440    // Poll events
441    /// Service message: an option was added to a poll (Bot API 9.6).
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub poll_option_added: Option<PollOptionAdded>,
444    /// Service message: an option was deleted from a poll (Bot API 9.6).
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub poll_option_deleted: Option<PollOptionDeleted>,
447
448    // Checklist events
449    /// Service message: tasks in a checklist were marked done or not done.
450    #[serde(skip_serializing_if = "Option::is_none")]
451    pub checklist_tasks_done: Option<ChecklistTasksDone>,
452    /// Service message: tasks were added to a checklist.
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub checklist_tasks_added: Option<ChecklistTasksAdded>,
455
456    // Direct messages events
457    /// Service message: the price for paid messages in the direct messages chat changed.
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub direct_message_price_changed: Option<DirectMessagePriceChanged>,
460    /// Service message: the price for paid messages in the chat changed.
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub paid_message_price_changed: Option<PaidMessagePriceChanged>,
463
464    // Suggested post events
465    /// Service message: a suggested post was approved.
466    #[serde(skip_serializing_if = "Option::is_none")]
467    pub suggested_post_approved: Option<SuggestedPostApproved>,
468    /// Service message: approval of a suggested post has failed.
469    #[serde(skip_serializing_if = "Option::is_none")]
470    pub suggested_post_approval_failed: Option<SuggestedPostApprovalFailed>,
471    /// Service message: a suggested post was declined.
472    #[serde(skip_serializing_if = "Option::is_none")]
473    pub suggested_post_declined: Option<SuggestedPostDeclined>,
474    /// Service message: payment for a suggested post was received.
475    #[serde(skip_serializing_if = "Option::is_none")]
476    pub suggested_post_paid: Option<SuggestedPostPaid>,
477    /// Service message: payment for a suggested post was refunded.
478    #[serde(skip_serializing_if = "Option::is_none")]
479    pub suggested_post_refunded: Option<SuggestedPostRefunded>,
480
481    // Guest mode fields
482    /// For a message sent by a guest bot, the user whose original message triggered the bot's response.
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub guest_bot_caller_user: Option<User>,
485
486    /// For a message sent by a guest bot, the chat whose original message triggered the bot's response.
487    #[serde(skip_serializing_if = "Option::is_none")]
488    pub guest_bot_caller_chat: Option<crate::chat::Chat>,
489
490    /// The unique identifier for the guest query.
491    ///
492    /// Use with [`answerGuestQuery`](https://core.telegram.org/bots/api#answerguestquery) to send a
493    /// response. If non-empty, the message belongs to a chat of the corresponding business account
494    /// independent from any potential bot chat sharing the same identifier.
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub guest_query_id: Option<String>,
497}
498
499impl Message {
500    /// Returns the effective text of the message — `text` or `caption`.
501    #[must_use]
502    pub fn effective_text(&self) -> Option<&str> {
503        self.text.as_deref().or(self.caption.as_deref())
504    }
505
506    /// Returns `true` if the message is a command (text starts with `/`).
507    #[must_use]
508    pub fn is_command(&self) -> bool {
509        self.entities
510            .as_deref()
511            .unwrap_or_default()
512            .iter()
513            .any(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)
514    }
515
516    /// Extracts the command string (e.g. `"start"` from `/start@bot`), if present.
517    #[must_use]
518    pub fn command(&self) -> Option<&str> {
519        let text = self.text.as_deref()?;
520        let entity = self
521            .entities
522            .as_deref()?
523            .iter()
524            .find(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)?;
525        let raw = &text[..entity.length as usize];
526        // Strip the `@BotUsername` suffix if present.
527        Some(
528            raw.find('@')
529                .map_or(raw, |i| &raw[..i])
530                .trim_start_matches('/'),
531        )
532    }
533}
534
535/// Type of a message entity.
536#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
537#[serde(rename_all = "snake_case")]
538pub enum MessageEntityKind {
539    /// @username mention.
540    Mention,
541    /// #hashtag.
542    Hashtag,
543    /// $cashtag.
544    Cashtag,
545    /// /bot_command.
546    BotCommand,
547    /// Plain URL.
548    Url,
549    /// Email address.
550    Email,
551    /// Phone number.
552    PhoneNumber,
553    /// **Bold** text.
554    Bold,
555    /// _Italic_ text.
556    Italic,
557    /// Underlined text.
558    Underline,
559    /// ~~Strikethrough~~ text.
560    Strikethrough,
561    /// ||Spoiler|| text.
562    Spoiler,
563    /// Block quotation.
564    Blockquote,
565    /// An "expandable" block quotation that can be expanded to show the full text.
566    ExpandableBlockquote,
567    /// Monospaced text.
568    Code,
569    /// Monospaced block.
570    Pre,
571    /// A text link. The `url` field will contain the destination URL.
572    TextLink,
573    /// A text mention of a user. The `user` field will contain the mentioned user.
574    TextMention,
575    /// Custom emoji. The `custom_emoji_id` field will contain the identifier of the custom emoji.
576    CustomEmoji,
577    /// Date/time entity.
578    DateTime,
579}
580
581/// One special entity in a message text.
582#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct MessageEntity {
584    /// Type of the entity.
585    #[serde(rename = "type")]
586    pub kind: MessageEntityKind,
587    /// Offset in UTF-16 code units to the start of the entity.
588    pub offset: u32,
589    /// Length of the entity in UTF-16 code units.
590    pub length: u32,
591    /// For `TextLink` — URL.
592    #[serde(skip_serializing_if = "Option::is_none")]
593    pub url: Option<String>,
594    /// For `TextMention` — the mentioned user.
595    #[serde(skip_serializing_if = "Option::is_none")]
596    pub user: Option<User>,
597    /// For `Pre` — the programming language.
598    #[serde(skip_serializing_if = "Option::is_none")]
599    pub language: Option<String>,
600    /// For `CustomEmoji` — identifier of the custom emoji.
601    #[serde(skip_serializing_if = "Option::is_none")]
602    pub custom_emoji_id: Option<String>,
603    /// For `DateTime` — Unix timestamp associated with the entity.
604    #[serde(skip_serializing_if = "Option::is_none")]
605    pub unix_time: Option<i64>,
606    /// For `DateTime` — format string (`r|w?[dD]?[tT]?`).
607    #[serde(skip_serializing_if = "Option::is_none")]
608    pub date_time_format: Option<String>,
609}
610
611/// Origin of a forwarded message.
612#[derive(Debug, Clone, Serialize, Deserialize)]
613#[serde(tag = "type", rename_all = "snake_case")]
614pub enum MessageOrigin {
615    /// Forwarded from a user with a visible profile.
616    User {
617        /// Date the original message was sent, as a Unix timestamp.
618        date: i64,
619        /// The user who sent the original message.
620        sender_user: User,
621    },
622    /// Forwarded from a user with a hidden profile.
623    HiddenUser {
624        /// Date the original message was sent, as a Unix timestamp.
625        date: i64,
626        /// Name of the user who sent the original message.
627        sender_user_name: String,
628    },
629    /// Forwarded from a chat on behalf of the chat itself.
630    Chat {
631        /// Date the original message was sent, as a Unix timestamp.
632        date: i64,
633        /// The chat that sent the original message.
634        sender_chat: Chat,
635        /// Signature of the original post author.
636        #[serde(skip_serializing_if = "Option::is_none")]
637        author_signature: Option<String>,
638    },
639    /// Forwarded from a channel.
640    Channel {
641        /// Date the original message was sent, as a Unix timestamp.
642        date: i64,
643        /// The channel that sent the original message.
644        chat: Chat,
645        /// Identifier of the original message in the channel.
646        message_id: i64,
647        /// Signature of the original post author.
648        #[serde(skip_serializing_if = "Option::is_none")]
649        author_signature: Option<String>,
650    },
651}
652
653/// Parameters for replying to a message.
654#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct ReplyParameters {
656    /// Identifier of the message that will be replied to in the current
657    /// chat, or in the chat `chat_id` if specified. Required if
658    /// `ephemeral_message_id` isn't specified.
659    #[serde(skip_serializing_if = "Option::is_none")]
660    pub message_id: Option<i64>,
661    /// Identifier of the ephemeral message that will be replied to in the
662    /// current chat. A reply to an ephemeral message must itself be an
663    /// ephemeral message. Required if `message_id` isn't specified.
664    #[serde(skip_serializing_if = "Option::is_none")]
665    pub ephemeral_message_id: Option<i64>,
666    /// If the message to be replied to is from a different chat, the chat
667    /// containing it. Not supported for messages sent on behalf of a business
668    /// account, direct-messages-chat messages, or ephemeral messages.
669    #[serde(skip_serializing_if = "Option::is_none")]
670    pub chat_id: Option<crate::user::ChatId>,
671    /// `true` if the message should be sent even if the specified message is
672    /// not found. Always `false` for replies in another chat or forum topic
673    /// and for sent ephemeral messages. Always `true` for messages sent on
674    /// behalf of a business account.
675    #[serde(skip_serializing_if = "Option::is_none")]
676    pub allow_sending_without_reply: Option<bool>,
677    /// Quoted part of the message to be replied to; 0–1024 characters after
678    /// entities parsing. Must be an exact substring of the message being
679    /// replied to, including any bold/italic/underline/strikethrough/spoiler/
680    /// custom_emoji/date_time entities. Ignored for ephemeral messages.
681    #[serde(skip_serializing_if = "Option::is_none")]
682    pub quote: Option<String>,
683    /// Parse mode for the quote.
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub quote_parse_mode: Option<ParseMode>,
686    /// Special entities in the quote.
687    #[serde(skip_serializing_if = "Option::is_none")]
688    pub quote_entities: Option<Vec<MessageEntity>>,
689    /// Position of the quote in the original message (UTF-16 offset).
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub quote_position: Option<u32>,
692    /// Identifier of the poll option being replied to.
693    #[serde(skip_serializing_if = "Option::is_none")]
694    pub poll_option_id: Option<String>,
695    /// Identifier of the checklist task being replied to.
696    #[serde(skip_serializing_if = "Option::is_none")]
697    pub checklist_task_id: Option<i64>,
698}
699
700/// Text parse mode for message formatting.
701#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
702pub enum ParseMode {
703    /// Markdown v2 — recommended.
704    MarkdownV2,
705    /// HTML formatting.
706    HTML,
707    /// Legacy Markdown — limited.
708    Markdown,
709}
710
711/// Reaction type on a message.
712#[derive(Debug, Clone, Serialize, Deserialize)]
713#[serde(tag = "type", rename_all = "snake_case")]
714pub enum ReactionType {
715    /// A standard emoji reaction.
716    Emoji {
717        /// The emoji character.
718        emoji: String,
719    },
720    /// A custom emoji reaction.
721    CustomEmoji {
722        /// Identifier of the custom emoji.
723        custom_emoji_id: String,
724    },
725    /// A paid star reaction.
726    Paid,
727}
728
729/// Options for controlling how links are previewed in messages.
730#[derive(Debug, Clone, Default, Serialize, Deserialize)]
731pub struct LinkPreviewOptions {
732    /// `true` if link preview is disabled.
733    #[serde(skip_serializing_if = "Option::is_none")]
734    pub is_disabled: Option<bool>,
735    /// URL to use for the link preview.
736    #[serde(skip_serializing_if = "Option::is_none")]
737    pub url: Option<String>,
738    /// `true` if the media in the link preview should be shrunk.
739    #[serde(skip_serializing_if = "Option::is_none")]
740    pub prefer_small_media: Option<bool>,
741    /// `true` if the media in the link preview should be enlarged.
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub prefer_large_media: Option<bool>,
744    /// `true` if the link preview should be shown above the message text.
745    #[serde(skip_serializing_if = "Option::is_none")]
746    pub show_above_text: Option<bool>,
747}
748
749/// Information about the quoted part of a message that is replied to.
750#[derive(Debug, Clone, Serialize, Deserialize)]
751pub struct TextQuote {
752    /// Text of the quoted part of the message.
753    pub text: String,
754    /// Special entities in the quote.
755    #[serde(skip_serializing_if = "Option::is_none")]
756    pub entities: Option<Vec<MessageEntity>>,
757    /// Approximate quote position in the original message (UTF-16 offset).
758    pub position: u32,
759    /// `true` if the quote was chosen manually by the message sender.
760    #[serde(skip_serializing_if = "Option::is_none")]
761    pub is_manual: Option<bool>,
762}
763
764/// Information about a message that is being replied to from outside the thread.
765///
766/// `#[non_exhaustive]` but deliberately without [`Default`]: its required
767/// `origin` field is a [`MessageOrigin`], and every variant of that enum
768/// describes a distinct real forwarding situation. Nominating one as the
769/// default would manufacture a message origin that never occurs. This type is
770/// only ever received from the API, never built by callers, so sealing alone
771/// gives the non-breaking guarantee without inventing that value.
772#[derive(Debug, Clone, Serialize, Deserialize)]
773#[non_exhaustive]
774pub struct ExternalReplyInfo {
775    /// Origin of the message being replied to.
776    pub origin: MessageOrigin,
777    /// Chat the original message belongs to (if not the current chat).
778    #[serde(skip_serializing_if = "Option::is_none")]
779    pub chat: Option<Chat>,
780    /// Identifier of the original message in the original chat.
781    #[serde(skip_serializing_if = "Option::is_none")]
782    pub message_id: Option<i64>,
783    /// Options for link preview.
784    #[serde(skip_serializing_if = "Option::is_none")]
785    pub link_preview_options: Option<LinkPreviewOptions>,
786    /// Animation in the original message.
787    #[serde(skip_serializing_if = "Option::is_none")]
788    pub animation: Option<Animation>,
789    /// Audio in the original message.
790    #[serde(skip_serializing_if = "Option::is_none")]
791    pub audio: Option<Audio>,
792    /// Document in the original message.
793    #[serde(skip_serializing_if = "Option::is_none")]
794    pub document: Option<Document>,
795    /// Photo in the original message.
796    #[serde(skip_serializing_if = "Option::is_none")]
797    pub photo: Option<Vec<PhotoSize>>,
798    /// Live photo in the original message.
799    #[serde(skip_serializing_if = "Option::is_none")]
800    pub live_photo: Option<LivePhoto>,
801    /// Sticker in the original message.
802    #[serde(skip_serializing_if = "Option::is_none")]
803    pub sticker: Option<Sticker>,
804    /// Video in the original message.
805    #[serde(skip_serializing_if = "Option::is_none")]
806    pub video: Option<Video>,
807    /// Video note in the original message.
808    #[serde(skip_serializing_if = "Option::is_none")]
809    pub video_note: Option<VideoNote>,
810    /// Voice note in the original message.
811    #[serde(skip_serializing_if = "Option::is_none")]
812    pub voice: Option<Voice>,
813    /// `true` if the media is covered by a spoiler animation.
814    #[serde(skip_serializing_if = "Option::is_none")]
815    pub has_media_spoiler: Option<bool>,
816    /// Checklist in the original message.
817    #[serde(skip_serializing_if = "Option::is_none")]
818    pub checklist: Option<Checklist>,
819    /// Paid media in the original message.
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub paid_media: Option<PaidMediaInfo>,
822    /// Story in the original message.
823    #[serde(skip_serializing_if = "Option::is_none")]
824    pub story: Option<Story>,
825    /// Game in the original message.
826    #[serde(skip_serializing_if = "Option::is_none")]
827    pub game: Option<Game>,
828    /// Scheduled giveaway in the original message.
829    #[serde(skip_serializing_if = "Option::is_none")]
830    pub giveaway: Option<Giveaway>,
831    /// Completed giveaway with public winners in the original message.
832    #[serde(skip_serializing_if = "Option::is_none")]
833    pub giveaway_winners: Option<GiveawayWinners>,
834    /// Invoice in the original message.
835    #[serde(skip_serializing_if = "Option::is_none")]
836    pub invoice: Option<Invoice>,
837    /// Contact in the original message.
838    #[serde(skip_serializing_if = "Option::is_none")]
839    pub contact: Option<Contact>,
840    /// Dice in the original message.
841    #[serde(skip_serializing_if = "Option::is_none")]
842    pub dice: Option<Dice>,
843    /// Location in the original message.
844    #[serde(skip_serializing_if = "Option::is_none")]
845    pub location: Option<Location>,
846    /// Venue in the original message.
847    #[serde(skip_serializing_if = "Option::is_none")]
848    pub venue: Option<Venue>,
849    /// Poll in the original message.
850    #[serde(skip_serializing_if = "Option::is_none")]
851    pub poll: Option<Poll>,
852}
853
854/// Phone contact.
855#[derive(Debug, Clone, Serialize, Deserialize)]
856pub struct Contact {
857    /// Contact's phone number.
858    pub phone_number: String,
859    /// Contact's first name.
860    pub first_name: String,
861    /// Contact's last name.
862    #[serde(skip_serializing_if = "Option::is_none")]
863    pub last_name: Option<String>,
864    /// Contact's Telegram user ID.
865    #[serde(skip_serializing_if = "Option::is_none")]
866    pub user_id: Option<i64>,
867    /// Contact's vCard.
868    #[serde(skip_serializing_if = "Option::is_none")]
869    pub vcard: Option<String>,
870}
871
872/// Animated dice with a random value.
873#[derive(Debug, Clone, Serialize, Deserialize)]
874pub struct Dice {
875    /// Emoji on which the dice throw animation is based.
876    pub emoji: String,
877    /// Value of the dice (1–6 for 🎲🎯🎳; 1–5 for 🏀⚽; 1–64 for 🎰).
878    pub value: u8,
879}
880
881/// Data sent from a Web App.
882#[derive(Debug, Clone, Serialize, Deserialize)]
883pub struct WebAppData {
884    /// Data sent by the Web App.
885    pub data: String,
886    /// Text of the button that opened the Web App.
887    pub button_text: String,
888}
889
890/// Information about a Web App.
891#[derive(Debug, Clone, Serialize, Deserialize)]
892pub struct WebAppInfo {
893    /// HTTPS URL of the Web App.
894    pub url: String,
895}
896
897/// Service message: a user came within the proximity radius set by another
898/// user's live location.
899#[derive(Debug, Clone, Default, Serialize, Deserialize)]
900#[non_exhaustive]
901pub struct ProximityAlertTriggered {
902    /// The user who triggered the alert.
903    pub traveler: User,
904    /// The user who set the alert.
905    pub watcher: User,
906    /// Distance between the users, in metres.
907    pub distance: u32,
908}
909
910/// Service message: the chat's auto-delete timer was changed.
911#[derive(Debug, Clone, Default, Serialize, Deserialize)]
912#[non_exhaustive]
913pub struct MessageAutoDeleteTimerChanged {
914    /// New auto-delete time for messages in the chat, in seconds.
915    pub message_auto_delete_time: u32,
916}
917
918/// Service message: the user allowed the bot to write to them.
919///
920/// Every field is optional, and which one is set says how access was granted:
921/// from a `requestWriteAccess` prompt, by launching a named Web App, or by
922/// adding the bot to the attachment menu.
923#[derive(Debug, Clone, Default, Serialize, Deserialize)]
924#[non_exhaustive]
925pub struct WriteAccessAllowed {
926    /// `true` if access was granted after the user accepted an explicit request
927    /// from a Web App sent by the method `requestWriteAccess`.
928    #[serde(skip_serializing_if = "Option::is_none")]
929    pub from_request: Option<bool>,
930    /// Name of the Web App, if access was granted by launching one.
931    #[serde(skip_serializing_if = "Option::is_none")]
932    pub web_app_name: Option<String>,
933    /// `true` if access was granted when the bot was added to the attachment
934    /// or side menu.
935    #[serde(skip_serializing_if = "Option::is_none")]
936    pub from_attachment_menu: Option<bool>,
937}
938
939/// A message that is no longer accessible to the bot.
940#[derive(Debug, Clone, Default, Serialize, Deserialize)]
941#[non_exhaustive]
942pub struct InaccessibleMessage {
943    /// Chat the message belonged to.
944    pub chat: Chat,
945    /// Unique message identifier inside the chat.
946    pub message_id: i64,
947    /// Always `0`. This is the field that marks the message inaccessible.
948    pub date: i64,
949}
950
951/// A message that may or may not still be accessible to the bot.
952///
953/// Telegram distinguishes the two cases by `date`: an inaccessible message
954/// always has `date == 0`. The variants are otherwise structurally
955/// indistinguishable — an [`InaccessibleMessage`] is a strict subset of
956/// [`Message`] — so `#[serde(untagged)]` would always pick whichever variant
957/// came first. The manual [`Deserialize`] below dispatches on `date` instead.
958#[derive(Debug, Clone, Serialize)]
959#[serde(untagged)]
960pub enum MaybeInaccessibleMessage {
961    /// A regular, still-accessible message.
962    Message(Box<Message>),
963    /// A message the bot can no longer access.
964    Inaccessible(InaccessibleMessage),
965}
966
967impl<'de> Deserialize<'de> for MaybeInaccessibleMessage {
968    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
969    where
970        D: serde::Deserializer<'de>,
971    {
972        let value = serde_json::Value::deserialize(deserializer)?;
973        let is_inaccessible = value.get("date").and_then(serde_json::Value::as_i64) == Some(0);
974        if is_inaccessible {
975            InaccessibleMessage::deserialize(value)
976                .map(Self::Inaccessible)
977                .map_err(serde::de::Error::custom)
978        } else {
979            Message::deserialize(value)
980                .map(|m| Self::Message(Box::new(m)))
981                .map_err(serde::de::Error::custom)
982        }
983    }
984}
985
986/// Lightweight message identifier returned by `copyMessage`.
987#[derive(Debug, Clone, Serialize, Deserialize)]
988pub struct MessageId {
989    /// Identifier of the message.
990    pub message_id: i64,
991}
992
993/// Service message: the chat owner left the chat (Bot API 9.4).
994#[derive(Debug, Clone, Serialize, Deserialize)]
995pub struct ChatOwnerLeft {
996    /// The user who will become the new owner if the previous owner does not return.
997    #[serde(skip_serializing_if = "Option::is_none")]
998    pub new_owner: Option<User>,
999}
1000
1001/// Service message: ownership of the chat changed (Bot API 9.4).
1002#[derive(Debug, Clone, Serialize, Deserialize)]
1003pub struct ChatOwnerChanged {
1004    /// The new owner of the chat.
1005    pub new_owner: User,
1006}