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