Skip to main content

rustigram_types/
message.rs

1use serde::{Deserialize, Serialize};
2
3use crate::chat::{Chat, Location, Venue};
4use crate::checklist::{Checklist, ChecklistTasksAdded, ChecklistTasksDone};
5use crate::community::{CommunityChatAdded, CommunityChatRemoved};
6use crate::direct_messages::{
7    DirectMessagePriceChanged, DirectMessagesTopic, PaidMessagePriceChanged,
8};
9use crate::file::{Animation, Audio, Document, LivePhoto, PhotoSize, Video, VideoNote, Voice};
10use crate::keyboard::InlineKeyboardMarkup;
11use crate::managed_bot::ManagedBotCreated;
12use crate::poll::{Poll, PollOptionAdded, PollOptionDeleted};
13use crate::rich_message::RichMessage;
14use crate::sticker::Sticker;
15use crate::suggested_post::{
16    SuggestedPostApprovalFailed, SuggestedPostApproved, SuggestedPostDeclined, SuggestedPostInfo,
17    SuggestedPostPaid, SuggestedPostRefunded,
18};
19use crate::user::User;
20
21/// A Telegram message.
22///
23/// Only fields that were actually sent will be `Some`. Consult the
24/// official Bot API documentation for field availability rules.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Message {
27    /// Unique message identifier inside this chat; `0` for ephemeral messages.
28    /// In specific instances (e.g. a video sent to a large chat), the server
29    /// might schedule the message instead of sending it immediately — in that
30    /// case this field is also `0` until the message is actually sent.
31    pub message_id: i64,
32
33    /// For ephemeral messages — the user who received the message.
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub receiver_user: Option<User>,
36
37    /// For ephemeral messages — identifier of the message inside this chat.
38    /// The identifier may be reused for another ephemeral message once this
39    /// one is deleted or expires.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub ephemeral_message_id: Option<i64>,
42
43    /// Optional — unique identifier of a message thread to which the message belongs.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub message_thread_id: Option<i64>,
46
47    /// Information about the direct messages chat topic that contains the message.
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub direct_messages_topic: Option<DirectMessagesTopic>,
50
51    /// Sender of the message; empty for messages sent to channels.
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub from: Option<User>,
54
55    /// Sender of the message; empty for messages sent to channels or on behalf of a chat.
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub sender_chat: Option<Chat>,
58
59    /// For supergroup messages — boost count of the sender.
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub sender_boost_count: Option<u32>,
62
63    /// The bot that actually sent the message on behalf of the business account.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub sender_business_bot: Option<User>,
66
67    /// Tag or custom title of the sender; for supergroups only (Bot API 9.5).
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub sender_tag: Option<String>,
70
71    /// Date the message was sent, as a Unix timestamp.
72    pub date: i64,
73
74    /// Unique identifier of the business connection from which the message was received.
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub business_connection_id: Option<String>,
77
78    /// Conversation the message belongs to.
79    pub chat: Chat,
80
81    /// Information about the original message for forwarded messages.
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub forward_origin: Option<MessageOrigin>,
84
85    /// `true` if the message is sent to a forum topic.
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub is_topic_message: Option<bool>,
88
89    /// `true` if the message is a channel post automatically forwarded to the
90    /// connected discussion group.
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub is_automatic_forward: Option<bool>,
93
94    /// For replies in the same chat and message thread, the original message.
95    /// The nested `Message` never carries its own `reply_to_message`, even if
96    /// it is itself a reply. May be omitted for replies to an ephemeral message.
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub reply_to_message: Option<Box<Message>>,
99
100    /// Information about the message that is being replied to from another chat or topic.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub external_reply: Option<ExternalReplyInfo>,
103
104    /// For replies that quote part of the original message, the quoted part.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub quote: Option<TextQuote>,
107
108    /// For replies to a story, the original story.
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub reply_to_story: Option<serde_json::Value>,
111
112    /// Identifier of the checklist task being replied to.
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub reply_to_checklist_task_id: Option<i64>,
115
116    /// Persistent identifier of the poll option being replied to.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub reply_to_poll_option_id: Option<String>,
119
120    /// Bot through which the message was sent.
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub via_bot: Option<User>,
123
124    /// Date the message was last edited, as a Unix timestamp.
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub edit_date: Option<i64>,
127
128    /// `true` if the message can't be forwarded.
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub has_protected_content: Option<bool>,
131
132    /// `true` if the message was sent by an implicit action.
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub is_from_offline: Option<bool>,
135
136    /// `true` if the message is a paid post.
137    ///
138    /// Paid posts must not be deleted for 24 hours after sending and cannot be edited.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub is_paid_post: Option<bool>,
141
142    /// The unique identifier of a media message group this message belongs to.
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub media_group_id: Option<String>,
145
146    /// Signature of the post author for messages in channels.
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub author_signature: Option<String>,
149
150    /// Number of Telegram Stars paid by the sender to send this message.
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub paid_star_count: Option<i64>,
153
154    /// Actual UTF-8 text of the message (0–4096 characters).
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub text: Option<String>,
157
158    /// Rich formatted message content.
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub rich_message: Option<RichMessage>,
161
162    /// Special entities like usernames, URLs, bot commands, etc.
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub entities: Option<Vec<MessageEntity>>,
165
166    /// Options used for link preview generation.
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub link_preview_options: Option<LinkPreviewOptions>,
169
170    /// Information about a suggested post; present when the message is a suggested
171    /// post in a channel direct messages chat.
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub suggested_post_info: Option<SuggestedPostInfo>,
174
175    /// Unique identifier of the message effect added to the message.
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub effect_id: Option<String>,
178
179    // Media fields
180    /// Animation attached to the message.
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub animation: Option<Animation>,
183    /// Audio file attached to the message.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub audio: Option<Audio>,
186    /// Document attached to the message.
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub document: Option<Document>,
189    /// Paid media attached to the message.
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub paid_media: Option<serde_json::Value>,
192    /// Photo attached to the message (array of sizes).
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub photo: Option<Vec<PhotoSize>>,
195    /// Live photo attached to the message.
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub live_photo: Option<LivePhoto>,
198    /// Sticker attached to the message.
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub sticker: Option<Sticker>,
201    /// Story attached to the message.
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub story: Option<serde_json::Value>,
204    /// Video attached to the message.
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub video: Option<Video>,
207    /// Video note attached to the message.
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub video_note: Option<VideoNote>,
210    /// Voice note attached to the message.
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub voice: Option<Voice>,
213
214    /// Caption for the animation, audio, document, paid media, photo, video or voice.
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub caption: Option<String>,
217
218    /// Special entities in the caption.
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub caption_entities: Option<Vec<MessageEntity>>,
221
222    /// `true` if the caption must be shown above the message media.
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub show_caption_above_media: Option<bool>,
225
226    /// `true` if the message media is covered by a spoiler animation.
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub has_media_spoiler: Option<bool>,
229
230    /// Checklist attached to the message.
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub checklist: Option<Checklist>,
233
234    // Service message types
235    /// Contact shared in the message.
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub contact: Option<Contact>,
238    /// Dice result in the message.
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub dice: Option<Dice>,
241    /// Game in the message.
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub game: Option<serde_json::Value>,
244    /// Poll in the message.
245    #[serde(skip_serializing_if = "Option::is_none")]
246    pub poll: Option<Poll>,
247    /// Venue in the message.
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub venue: Option<Venue>,
250    /// Location in the message.
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub location: Option<Location>,
253
254    // Group events
255    /// New members that joined the group.
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub new_chat_members: Option<Vec<User>>,
258    /// A member that left the group.
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub left_chat_member: Option<User>,
261    /// Service message: chat owner has left.
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub chat_owner_left: Option<ChatOwnerLeft>,
264    /// Service message: chat owner has changed.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub chat_owner_changed: Option<ChatOwnerChanged>,
267    /// New chat title (service message).
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub new_chat_title: Option<String>,
270    /// New chat photo (service message).
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub new_chat_photo: Option<Vec<PhotoSize>>,
273    /// `true` if the chat photo was deleted (service message).
274    #[serde(skip_serializing_if = "Option::is_none")]
275    pub delete_chat_photo: Option<bool>,
276    /// `true` if the group was created (service message).
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub group_chat_created: Option<bool>,
279    /// `true` if the supergroup was created (service message).
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub supergroup_chat_created: Option<bool>,
282    /// `true` if the channel was created (service message).
283    #[serde(skip_serializing_if = "Option::is_none")]
284    pub channel_chat_created: Option<bool>,
285    /// Service message: chat added to a Community.
286    #[serde(skip_serializing_if = "Option::is_none")]
287    pub community_chat_added: Option<CommunityChatAdded>,
288    /// Service message: chat removed from a Community.
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub community_chat_removed: Option<CommunityChatRemoved>,
291    /// Auto-delete timer changed (service message).
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub message_auto_delete_timer_changed: Option<serde_json::Value>,
294    /// The group has been migrated to a supergroup with this ID.
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub migrate_to_chat_id: Option<i64>,
297    /// The supergroup has been migrated from a group with this ID.
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub migrate_from_chat_id: Option<i64>,
300    /// The pinned message (service message).
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub pinned_message: Option<Box<Message>>,
303
304    /// Inline keyboard attached to the message.
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub reply_markup: Option<InlineKeyboardMarkup>,
307
308    // Payment fields
309    /// Invoice for a payment (service message).
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub invoice: Option<serde_json::Value>,
312    /// Successful payment information (service message).
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub successful_payment: Option<serde_json::Value>,
315    /// Refunded payment information (service message).
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub refunded_payment: Option<serde_json::Value>,
318
319    // Web app
320    /// Data from the Web App.
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub web_app_data: Option<WebAppData>,
323
324    // Forum topic events
325    /// Forum topic created (service message).
326    #[serde(skip_serializing_if = "Option::is_none")]
327    pub forum_topic_created: Option<serde_json::Value>,
328    /// Forum topic edited (service message).
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub forum_topic_edited: Option<serde_json::Value>,
331    /// Forum topic closed (service message).
332    #[serde(skip_serializing_if = "Option::is_none")]
333    pub forum_topic_closed: Option<serde_json::Value>,
334    /// Forum topic reopened (service message).
335    #[serde(skip_serializing_if = "Option::is_none")]
336    pub forum_topic_reopened: Option<serde_json::Value>,
337    /// General forum topic hidden (service message).
338    #[serde(skip_serializing_if = "Option::is_none")]
339    pub general_forum_topic_hidden: Option<serde_json::Value>,
340    /// General forum topic unhidden (service message).
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub general_forum_topic_unhidden: Option<serde_json::Value>,
343
344    // Managed bot events
345    /// Service message: a new managed bot was created (Bot API 9.6).
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub managed_bot_created: Option<ManagedBotCreated>,
348
349    // Poll events
350    /// Service message: an option was added to a poll (Bot API 9.6).
351    #[serde(skip_serializing_if = "Option::is_none")]
352    pub poll_option_added: Option<PollOptionAdded>,
353    /// Service message: an option was deleted from a poll (Bot API 9.6).
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub poll_option_deleted: Option<PollOptionDeleted>,
356
357    // Checklist events
358    /// Service message: tasks in a checklist were marked done or not done.
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub checklist_tasks_done: Option<ChecklistTasksDone>,
361    /// Service message: tasks were added to a checklist.
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub checklist_tasks_added: Option<ChecklistTasksAdded>,
364
365    // Direct messages events
366    /// Service message: the price for paid messages in the direct messages chat changed.
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub direct_message_price_changed: Option<DirectMessagePriceChanged>,
369    /// Service message: the price for paid messages in the chat changed.
370    #[serde(skip_serializing_if = "Option::is_none")]
371    pub paid_message_price_changed: Option<PaidMessagePriceChanged>,
372
373    // Suggested post events
374    /// Service message: a suggested post was approved.
375    #[serde(skip_serializing_if = "Option::is_none")]
376    pub suggested_post_approved: Option<SuggestedPostApproved>,
377    /// Service message: approval of a suggested post has failed.
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub suggested_post_approval_failed: Option<SuggestedPostApprovalFailed>,
380    /// Service message: a suggested post was declined.
381    #[serde(skip_serializing_if = "Option::is_none")]
382    pub suggested_post_declined: Option<SuggestedPostDeclined>,
383    /// Service message: payment for a suggested post was received.
384    #[serde(skip_serializing_if = "Option::is_none")]
385    pub suggested_post_paid: Option<SuggestedPostPaid>,
386    /// Service message: payment for a suggested post was refunded.
387    #[serde(skip_serializing_if = "Option::is_none")]
388    pub suggested_post_refunded: Option<SuggestedPostRefunded>,
389
390    // Guest mode fields
391    /// For a message sent by a guest bot, the user whose original message triggered the bot's response.
392    #[serde(skip_serializing_if = "Option::is_none")]
393    pub guest_bot_caller_user: Option<User>,
394
395    /// For a message sent by a guest bot, the chat whose original message triggered the bot's response.
396    #[serde(skip_serializing_if = "Option::is_none")]
397    pub guest_bot_caller_chat: Option<crate::chat::Chat>,
398
399    /// The unique identifier for the guest query.
400    ///
401    /// Use with [`answerGuestQuery`](https://core.telegram.org/bots/api#answerguestquery) to send a
402    /// response. If non-empty, the message belongs to a chat of the corresponding business account
403    /// independent from any potential bot chat sharing the same identifier.
404    #[serde(skip_serializing_if = "Option::is_none")]
405    pub guest_query_id: Option<String>,
406}
407
408impl Message {
409    /// Returns the effective text of the message — `text` or `caption`.
410    #[must_use]
411    pub fn effective_text(&self) -> Option<&str> {
412        self.text.as_deref().or(self.caption.as_deref())
413    }
414
415    /// Returns `true` if the message is a command (text starts with `/`).
416    #[must_use]
417    pub fn is_command(&self) -> bool {
418        self.entities
419            .as_deref()
420            .unwrap_or_default()
421            .iter()
422            .any(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)
423    }
424
425    /// Extracts the command string (e.g. `"start"` from `/start@bot`), if present.
426    #[must_use]
427    pub fn command(&self) -> Option<&str> {
428        let text = self.text.as_deref()?;
429        let entity = self
430            .entities
431            .as_deref()?
432            .iter()
433            .find(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)?;
434        let raw = &text[..entity.length as usize];
435        // Strip the `@BotUsername` suffix if present.
436        Some(
437            raw.find('@')
438                .map_or(raw, |i| &raw[..i])
439                .trim_start_matches('/'),
440        )
441    }
442}
443
444/// Type of a message entity.
445#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
446#[serde(rename_all = "snake_case")]
447pub enum MessageEntityKind {
448    /// @username mention.
449    Mention,
450    /// #hashtag.
451    Hashtag,
452    /// $cashtag.
453    Cashtag,
454    /// /bot_command.
455    BotCommand,
456    /// Plain URL.
457    Url,
458    /// Email address.
459    Email,
460    /// Phone number.
461    PhoneNumber,
462    /// **Bold** text.
463    Bold,
464    /// _Italic_ text.
465    Italic,
466    /// Underlined text.
467    Underline,
468    /// ~~Strikethrough~~ text.
469    Strikethrough,
470    /// ||Spoiler|| text.
471    Spoiler,
472    /// Block quotation.
473    Blockquote,
474    /// An "expandable" block quotation that can be expanded to show the full text.
475    ExpandableBlockquote,
476    /// Monospaced text.
477    Code,
478    /// Monospaced block.
479    Pre,
480    /// A text link. The `url` field will contain the destination URL.
481    TextLink,
482    /// A text mention of a user. The `user` field will contain the mentioned user.
483    TextMention,
484    /// Custom emoji. The `custom_emoji_id` field will contain the identifier of the custom emoji.
485    CustomEmoji,
486    /// Date/time entity.
487    DateTime,
488}
489
490/// One special entity in a message text.
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct MessageEntity {
493    /// Type of the entity.
494    #[serde(rename = "type")]
495    pub kind: MessageEntityKind,
496    /// Offset in UTF-16 code units to the start of the entity.
497    pub offset: u32,
498    /// Length of the entity in UTF-16 code units.
499    pub length: u32,
500    /// For `TextLink` — URL.
501    #[serde(skip_serializing_if = "Option::is_none")]
502    pub url: Option<String>,
503    /// For `TextMention` — the mentioned user.
504    #[serde(skip_serializing_if = "Option::is_none")]
505    pub user: Option<User>,
506    /// For `Pre` — the programming language.
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub language: Option<String>,
509    /// For `CustomEmoji` — identifier of the custom emoji.
510    #[serde(skip_serializing_if = "Option::is_none")]
511    pub custom_emoji_id: Option<String>,
512    /// For `DateTime` — Unix timestamp associated with the entity.
513    #[serde(skip_serializing_if = "Option::is_none")]
514    pub unix_time: Option<i64>,
515    /// For `DateTime` — format string (`r|w?[dD]?[tT]?`).
516    #[serde(skip_serializing_if = "Option::is_none")]
517    pub date_time_format: Option<String>,
518}
519
520/// Origin of a forwarded message.
521#[derive(Debug, Clone, Serialize, Deserialize)]
522#[serde(tag = "type", rename_all = "snake_case")]
523pub enum MessageOrigin {
524    /// Forwarded from a user with a visible profile.
525    User {
526        /// Date the original message was sent, as a Unix timestamp.
527        date: i64,
528        /// The user who sent the original message.
529        sender_user: User,
530    },
531    /// Forwarded from a user with a hidden profile.
532    HiddenUser {
533        /// Date the original message was sent, as a Unix timestamp.
534        date: i64,
535        /// Name of the user who sent the original message.
536        sender_user_name: String,
537    },
538    /// Forwarded from a chat on behalf of the chat itself.
539    Chat {
540        /// Date the original message was sent, as a Unix timestamp.
541        date: i64,
542        /// The chat that sent the original message.
543        sender_chat: Chat,
544        /// Signature of the original post author.
545        #[serde(skip_serializing_if = "Option::is_none")]
546        author_signature: Option<String>,
547    },
548    /// Forwarded from a channel.
549    Channel {
550        /// Date the original message was sent, as a Unix timestamp.
551        date: i64,
552        /// The channel that sent the original message.
553        chat: Chat,
554        /// Identifier of the original message in the channel.
555        message_id: i64,
556        /// Signature of the original post author.
557        #[serde(skip_serializing_if = "Option::is_none")]
558        author_signature: Option<String>,
559    },
560}
561
562/// Parameters for replying to a message.
563#[derive(Debug, Clone, Serialize, Deserialize)]
564pub struct ReplyParameters {
565    /// Identifier of the message that will be replied to in the current
566    /// chat, or in the chat `chat_id` if specified. Required if
567    /// `ephemeral_message_id` isn't specified.
568    #[serde(skip_serializing_if = "Option::is_none")]
569    pub message_id: Option<i64>,
570    /// Identifier of the ephemeral message that will be replied to in the
571    /// current chat. A reply to an ephemeral message must itself be an
572    /// ephemeral message. Required if `message_id` isn't specified.
573    #[serde(skip_serializing_if = "Option::is_none")]
574    pub ephemeral_message_id: Option<i64>,
575    /// If the message to be replied to is from a different chat, the chat
576    /// containing it. Not supported for messages sent on behalf of a business
577    /// account, direct-messages-chat messages, or ephemeral messages.
578    #[serde(skip_serializing_if = "Option::is_none")]
579    pub chat_id: Option<crate::user::ChatId>,
580    /// `true` if the message should be sent even if the specified message is
581    /// not found. Always `false` for replies in another chat or forum topic
582    /// and for sent ephemeral messages. Always `true` for messages sent on
583    /// behalf of a business account.
584    #[serde(skip_serializing_if = "Option::is_none")]
585    pub allow_sending_without_reply: Option<bool>,
586    /// Quoted part of the message to be replied to; 0–1024 characters after
587    /// entities parsing. Must be an exact substring of the message being
588    /// replied to, including any bold/italic/underline/strikethrough/spoiler/
589    /// custom_emoji/date_time entities. Ignored for ephemeral messages.
590    #[serde(skip_serializing_if = "Option::is_none")]
591    pub quote: Option<String>,
592    /// Parse mode for the quote.
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub quote_parse_mode: Option<ParseMode>,
595    /// Special entities in the quote.
596    #[serde(skip_serializing_if = "Option::is_none")]
597    pub quote_entities: Option<Vec<MessageEntity>>,
598    /// Position of the quote in the original message (UTF-16 offset).
599    #[serde(skip_serializing_if = "Option::is_none")]
600    pub quote_position: Option<u32>,
601    /// Identifier of the poll option being replied to.
602    #[serde(skip_serializing_if = "Option::is_none")]
603    pub poll_option_id: Option<String>,
604    /// Identifier of the checklist task being replied to.
605    #[serde(skip_serializing_if = "Option::is_none")]
606    pub checklist_task_id: Option<i64>,
607}
608
609/// Text parse mode for message formatting.
610#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
611pub enum ParseMode {
612    /// Markdown v2 — recommended.
613    MarkdownV2,
614    /// HTML formatting.
615    HTML,
616    /// Legacy Markdown — limited.
617    Markdown,
618}
619
620/// Reaction type on a message.
621#[derive(Debug, Clone, Serialize, Deserialize)]
622#[serde(tag = "type", rename_all = "snake_case")]
623pub enum ReactionType {
624    /// A standard emoji reaction.
625    Emoji {
626        /// The emoji character.
627        emoji: String,
628    },
629    /// A custom emoji reaction.
630    CustomEmoji {
631        /// Identifier of the custom emoji.
632        custom_emoji_id: String,
633    },
634    /// A paid star reaction.
635    Paid,
636}
637
638/// Options for controlling how links are previewed in messages.
639#[derive(Debug, Clone, Default, Serialize, Deserialize)]
640pub struct LinkPreviewOptions {
641    /// `true` if link preview is disabled.
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub is_disabled: Option<bool>,
644    /// URL to use for the link preview.
645    #[serde(skip_serializing_if = "Option::is_none")]
646    pub url: Option<String>,
647    /// `true` if the media in the link preview should be shrunk.
648    #[serde(skip_serializing_if = "Option::is_none")]
649    pub prefer_small_media: Option<bool>,
650    /// `true` if the media in the link preview should be enlarged.
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub prefer_large_media: Option<bool>,
653    /// `true` if the link preview should be shown above the message text.
654    #[serde(skip_serializing_if = "Option::is_none")]
655    pub show_above_text: Option<bool>,
656}
657
658/// Information about the quoted part of a message that is replied to.
659#[derive(Debug, Clone, Serialize, Deserialize)]
660pub struct TextQuote {
661    /// Text of the quoted part of the message.
662    pub text: String,
663    /// Special entities in the quote.
664    #[serde(skip_serializing_if = "Option::is_none")]
665    pub entities: Option<Vec<MessageEntity>>,
666    /// Approximate quote position in the original message (UTF-16 offset).
667    pub position: u32,
668    /// `true` if the quote was chosen manually by the message sender.
669    #[serde(skip_serializing_if = "Option::is_none")]
670    pub is_manual: Option<bool>,
671}
672
673/// Information about a message that is being replied to from outside the thread.
674#[derive(Debug, Clone, Serialize, Deserialize)]
675pub struct ExternalReplyInfo {
676    /// Origin of the message being replied to.
677    pub origin: MessageOrigin,
678    /// Chat the original message belongs to (if not the current chat).
679    #[serde(skip_serializing_if = "Option::is_none")]
680    pub chat: Option<Chat>,
681    /// Identifier of the original message in the original chat.
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub message_id: Option<i64>,
684    /// Options for link preview.
685    #[serde(skip_serializing_if = "Option::is_none")]
686    pub link_preview_options: Option<LinkPreviewOptions>,
687    /// Animation in the original message.
688    #[serde(skip_serializing_if = "Option::is_none")]
689    pub animation: Option<Animation>,
690    /// Audio in the original message.
691    #[serde(skip_serializing_if = "Option::is_none")]
692    pub audio: Option<Audio>,
693    /// Document in the original message.
694    #[serde(skip_serializing_if = "Option::is_none")]
695    pub document: Option<Document>,
696    /// Photo in the original message.
697    #[serde(skip_serializing_if = "Option::is_none")]
698    pub photo: Option<Vec<PhotoSize>>,
699    /// Live photo in the original message.
700    #[serde(skip_serializing_if = "Option::is_none")]
701    pub live_photo: Option<LivePhoto>,
702    /// Sticker in the original message.
703    #[serde(skip_serializing_if = "Option::is_none")]
704    pub sticker: Option<Sticker>,
705    /// Video in the original message.
706    #[serde(skip_serializing_if = "Option::is_none")]
707    pub video: Option<Video>,
708    /// Video note in the original message.
709    #[serde(skip_serializing_if = "Option::is_none")]
710    pub video_note: Option<VideoNote>,
711    /// Voice note in the original message.
712    #[serde(skip_serializing_if = "Option::is_none")]
713    pub voice: Option<Voice>,
714    /// `true` if the media is covered by a spoiler animation.
715    #[serde(skip_serializing_if = "Option::is_none")]
716    pub has_media_spoiler: Option<bool>,
717    /// Checklist in the original message.
718    #[serde(skip_serializing_if = "Option::is_none")]
719    pub checklist: Option<Checklist>,
720    /// Contact in the original message.
721    #[serde(skip_serializing_if = "Option::is_none")]
722    pub contact: Option<Contact>,
723    /// Dice in the original message.
724    #[serde(skip_serializing_if = "Option::is_none")]
725    pub dice: Option<Dice>,
726    /// Location in the original message.
727    #[serde(skip_serializing_if = "Option::is_none")]
728    pub location: Option<Location>,
729    /// Venue in the original message.
730    #[serde(skip_serializing_if = "Option::is_none")]
731    pub venue: Option<Venue>,
732    /// Poll in the original message.
733    #[serde(skip_serializing_if = "Option::is_none")]
734    pub poll: Option<Poll>,
735}
736
737/// Phone contact.
738#[derive(Debug, Clone, Serialize, Deserialize)]
739pub struct Contact {
740    /// Contact's phone number.
741    pub phone_number: String,
742    /// Contact's first name.
743    pub first_name: String,
744    /// Contact's last name.
745    #[serde(skip_serializing_if = "Option::is_none")]
746    pub last_name: Option<String>,
747    /// Contact's Telegram user ID.
748    #[serde(skip_serializing_if = "Option::is_none")]
749    pub user_id: Option<i64>,
750    /// Contact's vCard.
751    #[serde(skip_serializing_if = "Option::is_none")]
752    pub vcard: Option<String>,
753}
754
755/// Animated dice with a random value.
756#[derive(Debug, Clone, Serialize, Deserialize)]
757pub struct Dice {
758    /// Emoji on which the dice throw animation is based.
759    pub emoji: String,
760    /// Value of the dice (1–6 for 🎲🎯🎳; 1–5 for 🏀⚽; 1–64 for 🎰).
761    pub value: u8,
762}
763
764/// Data sent from a Web App.
765#[derive(Debug, Clone, Serialize, Deserialize)]
766pub struct WebAppData {
767    /// Data sent by the Web App.
768    pub data: String,
769    /// Text of the button that opened the Web App.
770    pub button_text: String,
771}
772
773/// Information about a Web App.
774#[derive(Debug, Clone, Serialize, Deserialize)]
775pub struct WebAppInfo {
776    /// HTTPS URL of the Web App.
777    pub url: String,
778}
779
780/// Lightweight message identifier returned by `copyMessage`.
781#[derive(Debug, Clone, Serialize, Deserialize)]
782pub struct MessageId {
783    /// Identifier of the message.
784    pub message_id: i64,
785}
786
787/// Service message: the chat owner left the chat (Bot API 9.4).
788#[derive(Debug, Clone, Serialize, Deserialize)]
789pub struct ChatOwnerLeft {
790    /// The user who will become the new owner if the previous owner does not return.
791    #[serde(skip_serializing_if = "Option::is_none")]
792    pub new_owner: Option<User>,
793}
794
795/// Service message: ownership of the chat changed (Bot API 9.4).
796#[derive(Debug, Clone, Serialize, Deserialize)]
797pub struct ChatOwnerChanged {
798    /// The new owner of the chat.
799    pub new_owner: User,
800}