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::sticker::Sticker;
13use crate::suggested_post::{
14 SuggestedPostApprovalFailed, SuggestedPostApproved, SuggestedPostDeclined, SuggestedPostInfo,
15 SuggestedPostPaid, SuggestedPostRefunded,
16};
17use crate::user::User;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Message {
25 pub message_id: i64,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub message_thread_id: Option<i64>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub direct_messages_topic: Option<DirectMessagesTopic>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub from: Option<User>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub sender_chat: Option<Chat>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub sender_boost_count: Option<u32>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub sender_business_bot: Option<User>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub sender_tag: Option<String>,
55
56 pub date: i64,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub business_connection_id: Option<String>,
62
63 pub chat: Chat,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub forward_origin: Option<MessageOrigin>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub is_topic_message: Option<bool>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
77 pub is_automatic_forward: Option<bool>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub reply_to_message: Option<Box<Message>>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub external_reply: Option<ExternalReplyInfo>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub quote: Option<TextQuote>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub reply_to_story: Option<serde_json::Value>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub reply_to_checklist_task_id: Option<i64>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub reply_to_poll_option_id: Option<String>,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub via_bot: Option<User>,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub edit_date: Option<i64>,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub has_protected_content: Option<bool>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub is_from_offline: Option<bool>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
123 pub is_paid_post: Option<bool>,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub media_group_id: Option<String>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub author_signature: Option<String>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub paid_star_count: Option<i64>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub text: Option<String>,
140
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub entities: Option<Vec<MessageEntity>>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub link_preview_options: Option<LinkPreviewOptions>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
152 pub suggested_post_info: Option<SuggestedPostInfo>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub effect_id: Option<String>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
161 pub animation: Option<Animation>,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub audio: Option<Audio>,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub document: Option<Document>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub paid_media: Option<serde_json::Value>,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub photo: Option<Vec<PhotoSize>>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub live_photo: Option<LivePhoto>,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub sticker: Option<Sticker>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub story: Option<serde_json::Value>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub video: Option<Video>,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub video_note: Option<VideoNote>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub voice: Option<Voice>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub caption: Option<String>,
196
197 #[serde(skip_serializing_if = "Option::is_none")]
199 pub caption_entities: Option<Vec<MessageEntity>>,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub show_caption_above_media: Option<bool>,
204
205 #[serde(skip_serializing_if = "Option::is_none")]
207 pub has_media_spoiler: Option<bool>,
208
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub checklist: Option<Checklist>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
216 pub contact: Option<Contact>,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub dice: Option<Dice>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub game: Option<serde_json::Value>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub poll: Option<Poll>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub venue: Option<Venue>,
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub location: Option<Location>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
236 pub new_chat_members: Option<Vec<User>>,
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub left_chat_member: Option<User>,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub chat_owner_left: Option<ChatOwnerLeft>,
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub chat_owner_changed: Option<ChatOwnerChanged>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub new_chat_title: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub new_chat_photo: Option<Vec<PhotoSize>>,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub delete_chat_photo: Option<bool>,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub group_chat_created: Option<bool>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub supergroup_chat_created: Option<bool>,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub channel_chat_created: Option<bool>,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub message_auto_delete_timer_changed: Option<serde_json::Value>,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub migrate_to_chat_id: Option<i64>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub migrate_from_chat_id: Option<i64>,
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub pinned_message: Option<Box<Message>>,
276
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub reply_markup: Option<InlineKeyboardMarkup>,
280
281 #[serde(skip_serializing_if = "Option::is_none")]
284 pub invoice: Option<serde_json::Value>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub successful_payment: Option<serde_json::Value>,
288 #[serde(skip_serializing_if = "Option::is_none")]
290 pub refunded_payment: Option<serde_json::Value>,
291
292 #[serde(skip_serializing_if = "Option::is_none")]
295 pub web_app_data: Option<WebAppData>,
296
297 #[serde(skip_serializing_if = "Option::is_none")]
300 pub forum_topic_created: Option<serde_json::Value>,
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub forum_topic_edited: Option<serde_json::Value>,
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub forum_topic_closed: Option<serde_json::Value>,
307 #[serde(skip_serializing_if = "Option::is_none")]
309 pub forum_topic_reopened: Option<serde_json::Value>,
310 #[serde(skip_serializing_if = "Option::is_none")]
312 pub general_forum_topic_hidden: Option<serde_json::Value>,
313 #[serde(skip_serializing_if = "Option::is_none")]
315 pub general_forum_topic_unhidden: Option<serde_json::Value>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
320 pub managed_bot_created: Option<ManagedBotCreated>,
321
322 #[serde(skip_serializing_if = "Option::is_none")]
325 pub poll_option_added: Option<PollOptionAdded>,
326 #[serde(skip_serializing_if = "Option::is_none")]
328 pub poll_option_deleted: Option<PollOptionDeleted>,
329
330 #[serde(skip_serializing_if = "Option::is_none")]
333 pub checklist_tasks_done: Option<ChecklistTasksDone>,
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub checklist_tasks_added: Option<ChecklistTasksAdded>,
337
338 #[serde(skip_serializing_if = "Option::is_none")]
341 pub direct_message_price_changed: Option<DirectMessagePriceChanged>,
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub paid_message_price_changed: Option<PaidMessagePriceChanged>,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
349 pub suggested_post_approved: Option<SuggestedPostApproved>,
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub suggested_post_approval_failed: Option<SuggestedPostApprovalFailed>,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub suggested_post_declined: Option<SuggestedPostDeclined>,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub suggested_post_paid: Option<SuggestedPostPaid>,
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub suggested_post_refunded: Option<SuggestedPostRefunded>,
362
363 #[serde(skip_serializing_if = "Option::is_none")]
366 pub guest_bot_caller_user: Option<User>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub guest_bot_caller_chat: Option<crate::chat::Chat>,
371
372 #[serde(skip_serializing_if = "Option::is_none")]
378 pub guest_query_id: Option<String>,
379}
380
381impl Message {
382 #[must_use]
384 pub fn effective_text(&self) -> Option<&str> {
385 self.text.as_deref().or(self.caption.as_deref())
386 }
387
388 #[must_use]
390 pub fn is_command(&self) -> bool {
391 self.entities
392 .as_deref()
393 .unwrap_or_default()
394 .iter()
395 .any(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)
396 }
397
398 #[must_use]
400 pub fn command(&self) -> Option<&str> {
401 let text = self.text.as_deref()?;
402 let entity = self
403 .entities
404 .as_deref()?
405 .iter()
406 .find(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)?;
407 let raw = &text[..entity.length as usize];
408 Some(
410 raw.find('@')
411 .map_or(raw, |i| &raw[..i])
412 .trim_start_matches('/'),
413 )
414 }
415}
416
417#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
419#[serde(rename_all = "snake_case")]
420pub enum MessageEntityKind {
421 Mention,
423 Hashtag,
425 Cashtag,
427 BotCommand,
429 Url,
431 Email,
433 PhoneNumber,
435 Bold,
437 Italic,
439 Underline,
441 Strikethrough,
443 Spoiler,
445 Blockquote,
447 ExpandableBlockquote,
449 Code,
451 Pre,
453 TextLink,
455 TextMention,
457 CustomEmoji,
459 DateTime,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct MessageEntity {
466 #[serde(rename = "type")]
468 pub kind: MessageEntityKind,
469 pub offset: u32,
471 pub length: u32,
473 #[serde(skip_serializing_if = "Option::is_none")]
475 pub url: Option<String>,
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub user: Option<User>,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub language: Option<String>,
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub custom_emoji_id: Option<String>,
485 #[serde(skip_serializing_if = "Option::is_none")]
487 pub unix_time: Option<i64>,
488 #[serde(skip_serializing_if = "Option::is_none")]
490 pub date_time_format: Option<String>,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495#[serde(tag = "type", rename_all = "snake_case")]
496pub enum MessageOrigin {
497 User {
499 date: i64,
501 sender_user: User,
503 },
504 HiddenUser {
506 date: i64,
508 sender_user_name: String,
510 },
511 Chat {
513 date: i64,
515 sender_chat: Chat,
517 #[serde(skip_serializing_if = "Option::is_none")]
519 author_signature: Option<String>,
520 },
521 Channel {
523 date: i64,
525 chat: Chat,
527 message_id: i64,
529 #[serde(skip_serializing_if = "Option::is_none")]
531 author_signature: Option<String>,
532 },
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct ReplyParameters {
538 pub message_id: i64,
540 #[serde(skip_serializing_if = "Option::is_none")]
542 pub chat_id: Option<crate::user::ChatId>,
543 #[serde(skip_serializing_if = "Option::is_none")]
545 pub allow_sending_without_reply: Option<bool>,
546 #[serde(skip_serializing_if = "Option::is_none")]
548 pub quote: Option<String>,
549 #[serde(skip_serializing_if = "Option::is_none")]
551 pub quote_parse_mode: Option<ParseMode>,
552 #[serde(skip_serializing_if = "Option::is_none")]
554 pub quote_entities: Option<Vec<MessageEntity>>,
555 #[serde(skip_serializing_if = "Option::is_none")]
557 pub quote_position: Option<u32>,
558 #[serde(skip_serializing_if = "Option::is_none")]
560 pub poll_option_id: Option<String>,
561 #[serde(skip_serializing_if = "Option::is_none")]
563 pub checklist_task_id: Option<i64>,
564}
565
566#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
568pub enum ParseMode {
569 MarkdownV2,
571 HTML,
573 Markdown,
575}
576
577#[derive(Debug, Clone, Serialize, Deserialize)]
579#[serde(tag = "type", rename_all = "snake_case")]
580pub enum ReactionType {
581 Emoji {
583 emoji: String,
585 },
586 CustomEmoji {
588 custom_emoji_id: String,
590 },
591 Paid,
593}
594
595#[derive(Debug, Clone, Default, Serialize, Deserialize)]
597pub struct LinkPreviewOptions {
598 #[serde(skip_serializing_if = "Option::is_none")]
600 pub is_disabled: Option<bool>,
601 #[serde(skip_serializing_if = "Option::is_none")]
603 pub url: Option<String>,
604 #[serde(skip_serializing_if = "Option::is_none")]
606 pub prefer_small_media: Option<bool>,
607 #[serde(skip_serializing_if = "Option::is_none")]
609 pub prefer_large_media: Option<bool>,
610 #[serde(skip_serializing_if = "Option::is_none")]
612 pub show_above_text: Option<bool>,
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct TextQuote {
618 pub text: String,
620 #[serde(skip_serializing_if = "Option::is_none")]
622 pub entities: Option<Vec<MessageEntity>>,
623 pub position: u32,
625 #[serde(skip_serializing_if = "Option::is_none")]
627 pub is_manual: Option<bool>,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize)]
632pub struct ExternalReplyInfo {
633 pub origin: MessageOrigin,
635 #[serde(skip_serializing_if = "Option::is_none")]
637 pub chat: Option<Chat>,
638 #[serde(skip_serializing_if = "Option::is_none")]
640 pub message_id: Option<i64>,
641 #[serde(skip_serializing_if = "Option::is_none")]
643 pub link_preview_options: Option<LinkPreviewOptions>,
644 #[serde(skip_serializing_if = "Option::is_none")]
646 pub animation: Option<Animation>,
647 #[serde(skip_serializing_if = "Option::is_none")]
649 pub audio: Option<Audio>,
650 #[serde(skip_serializing_if = "Option::is_none")]
652 pub document: Option<Document>,
653 #[serde(skip_serializing_if = "Option::is_none")]
655 pub photo: Option<Vec<PhotoSize>>,
656 #[serde(skip_serializing_if = "Option::is_none")]
658 pub live_photo: Option<LivePhoto>,
659 #[serde(skip_serializing_if = "Option::is_none")]
661 pub sticker: Option<Sticker>,
662 #[serde(skip_serializing_if = "Option::is_none")]
664 pub video: Option<Video>,
665 #[serde(skip_serializing_if = "Option::is_none")]
667 pub video_note: Option<VideoNote>,
668 #[serde(skip_serializing_if = "Option::is_none")]
670 pub voice: Option<Voice>,
671 #[serde(skip_serializing_if = "Option::is_none")]
673 pub has_media_spoiler: Option<bool>,
674 #[serde(skip_serializing_if = "Option::is_none")]
676 pub checklist: Option<Checklist>,
677 #[serde(skip_serializing_if = "Option::is_none")]
679 pub contact: Option<Contact>,
680 #[serde(skip_serializing_if = "Option::is_none")]
682 pub dice: Option<Dice>,
683 #[serde(skip_serializing_if = "Option::is_none")]
685 pub location: Option<Location>,
686 #[serde(skip_serializing_if = "Option::is_none")]
688 pub venue: Option<Venue>,
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub poll: Option<Poll>,
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize)]
696pub struct Contact {
697 pub phone_number: String,
699 pub first_name: String,
701 #[serde(skip_serializing_if = "Option::is_none")]
703 pub last_name: Option<String>,
704 #[serde(skip_serializing_if = "Option::is_none")]
706 pub user_id: Option<i64>,
707 #[serde(skip_serializing_if = "Option::is_none")]
709 pub vcard: Option<String>,
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize)]
714pub struct Dice {
715 pub emoji: String,
717 pub value: u8,
719}
720
721#[derive(Debug, Clone, Serialize, Deserialize)]
723pub struct WebAppData {
724 pub data: String,
726 pub button_text: String,
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize)]
732pub struct WebAppInfo {
733 pub url: String,
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize)]
739pub struct MessageId {
740 pub message_id: i64,
742}
743
744#[derive(Debug, Clone, Serialize, Deserialize)]
746pub struct ChatOwnerLeft {
747 #[serde(skip_serializing_if = "Option::is_none")]
749 pub new_owner: Option<User>,
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct ChatOwnerChanged {
755 pub new_owner: User,
757}