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, 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 sticker: Option<Sticker>,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub story: Option<serde_json::Value>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub video: Option<Video>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub video_note: Option<VideoNote>,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub voice: Option<Voice>,
189
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub caption: Option<String>,
193
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub caption_entities: Option<Vec<MessageEntity>>,
197
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub show_caption_above_media: Option<bool>,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub has_media_spoiler: Option<bool>,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
208 pub checklist: Option<Checklist>,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
213 pub contact: Option<Contact>,
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub dice: Option<Dice>,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub game: Option<serde_json::Value>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub poll: Option<Poll>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub venue: Option<Venue>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub location: Option<Location>,
229
230 #[serde(skip_serializing_if = "Option::is_none")]
233 pub new_chat_members: Option<Vec<User>>,
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub left_chat_member: Option<User>,
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub chat_owner_left: Option<ChatOwnerLeft>,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub chat_owner_changed: Option<ChatOwnerChanged>,
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub new_chat_title: Option<String>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub new_chat_photo: Option<Vec<PhotoSize>>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub delete_chat_photo: Option<bool>,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub group_chat_created: Option<bool>,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub supergroup_chat_created: Option<bool>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub channel_chat_created: Option<bool>,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub message_auto_delete_timer_changed: Option<serde_json::Value>,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub migrate_to_chat_id: Option<i64>,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub migrate_from_chat_id: Option<i64>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub pinned_message: Option<Box<Message>>,
273
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub reply_markup: Option<InlineKeyboardMarkup>,
277
278 #[serde(skip_serializing_if = "Option::is_none")]
281 pub invoice: Option<serde_json::Value>,
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub successful_payment: Option<serde_json::Value>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub refunded_payment: Option<serde_json::Value>,
288
289 #[serde(skip_serializing_if = "Option::is_none")]
292 pub web_app_data: Option<WebAppData>,
293
294 #[serde(skip_serializing_if = "Option::is_none")]
297 pub forum_topic_created: Option<serde_json::Value>,
298 #[serde(skip_serializing_if = "Option::is_none")]
300 pub forum_topic_edited: Option<serde_json::Value>,
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub forum_topic_closed: Option<serde_json::Value>,
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub forum_topic_reopened: Option<serde_json::Value>,
307 #[serde(skip_serializing_if = "Option::is_none")]
309 pub general_forum_topic_hidden: Option<serde_json::Value>,
310 #[serde(skip_serializing_if = "Option::is_none")]
312 pub general_forum_topic_unhidden: Option<serde_json::Value>,
313
314 #[serde(skip_serializing_if = "Option::is_none")]
317 pub managed_bot_created: Option<ManagedBotCreated>,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
322 pub poll_option_added: Option<PollOptionAdded>,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub poll_option_deleted: Option<PollOptionDeleted>,
326
327 #[serde(skip_serializing_if = "Option::is_none")]
330 pub checklist_tasks_done: Option<ChecklistTasksDone>,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub checklist_tasks_added: Option<ChecklistTasksAdded>,
334
335 #[serde(skip_serializing_if = "Option::is_none")]
338 pub direct_message_price_changed: Option<DirectMessagePriceChanged>,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub paid_message_price_changed: Option<PaidMessagePriceChanged>,
342
343 #[serde(skip_serializing_if = "Option::is_none")]
346 pub suggested_post_approved: Option<SuggestedPostApproved>,
347 #[serde(skip_serializing_if = "Option::is_none")]
349 pub suggested_post_approval_failed: Option<SuggestedPostApprovalFailed>,
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub suggested_post_declined: Option<SuggestedPostDeclined>,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub suggested_post_paid: Option<SuggestedPostPaid>,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub suggested_post_refunded: Option<SuggestedPostRefunded>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
363 pub guest_bot_caller_user: Option<User>,
364
365 #[serde(skip_serializing_if = "Option::is_none")]
367 pub guest_bot_caller_chat: Option<crate::chat::Chat>,
368
369 #[serde(skip_serializing_if = "Option::is_none")]
375 pub guest_query_id: Option<String>,
376}
377
378impl Message {
379 #[must_use]
381 pub fn effective_text(&self) -> Option<&str> {
382 self.text.as_deref().or(self.caption.as_deref())
383 }
384
385 #[must_use]
387 pub fn is_command(&self) -> bool {
388 self.entities
389 .as_deref()
390 .unwrap_or_default()
391 .iter()
392 .any(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)
393 }
394
395 #[must_use]
397 pub fn command(&self) -> Option<&str> {
398 let text = self.text.as_deref()?;
399 let entity = self
400 .entities
401 .as_deref()?
402 .iter()
403 .find(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)?;
404 let raw = &text[..entity.length as usize];
405 Some(
407 raw.find('@')
408 .map_or(raw, |i| &raw[..i])
409 .trim_start_matches('/'),
410 )
411 }
412}
413
414#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
416#[serde(rename_all = "snake_case")]
417pub enum MessageEntityKind {
418 Mention,
420 Hashtag,
422 Cashtag,
424 BotCommand,
426 Url,
428 Email,
430 PhoneNumber,
432 Bold,
434 Italic,
436 Underline,
438 Strikethrough,
440 Spoiler,
442 Blockquote,
444 ExpandableBlockquote,
446 Code,
448 Pre,
450 TextLink,
452 TextMention,
454 CustomEmoji,
456 DateTime,
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct MessageEntity {
463 #[serde(rename = "type")]
465 pub kind: MessageEntityKind,
466 pub offset: u32,
468 pub length: u32,
470 #[serde(skip_serializing_if = "Option::is_none")]
472 pub url: Option<String>,
473 #[serde(skip_serializing_if = "Option::is_none")]
475 pub user: Option<User>,
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub language: Option<String>,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub custom_emoji_id: Option<String>,
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub unix_time: Option<i64>,
485 #[serde(skip_serializing_if = "Option::is_none")]
487 pub date_time_format: Option<String>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492#[serde(tag = "type", rename_all = "snake_case")]
493pub enum MessageOrigin {
494 User {
496 date: i64,
498 sender_user: User,
500 },
501 HiddenUser {
503 date: i64,
505 sender_user_name: String,
507 },
508 Chat {
510 date: i64,
512 sender_chat: Chat,
514 #[serde(skip_serializing_if = "Option::is_none")]
516 author_signature: Option<String>,
517 },
518 Channel {
520 date: i64,
522 chat: Chat,
524 message_id: i64,
526 #[serde(skip_serializing_if = "Option::is_none")]
528 author_signature: Option<String>,
529 },
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
534pub struct ReplyParameters {
535 pub message_id: i64,
537 #[serde(skip_serializing_if = "Option::is_none")]
539 pub chat_id: Option<crate::user::ChatId>,
540 #[serde(skip_serializing_if = "Option::is_none")]
542 pub allow_sending_without_reply: Option<bool>,
543 #[serde(skip_serializing_if = "Option::is_none")]
545 pub quote: Option<String>,
546 #[serde(skip_serializing_if = "Option::is_none")]
548 pub quote_parse_mode: Option<ParseMode>,
549 #[serde(skip_serializing_if = "Option::is_none")]
551 pub quote_entities: Option<Vec<MessageEntity>>,
552 #[serde(skip_serializing_if = "Option::is_none")]
554 pub quote_position: Option<u32>,
555 #[serde(skip_serializing_if = "Option::is_none")]
557 pub poll_option_id: Option<String>,
558 #[serde(skip_serializing_if = "Option::is_none")]
560 pub checklist_task_id: Option<i64>,
561}
562
563#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
565pub enum ParseMode {
566 MarkdownV2,
568 HTML,
570 Markdown,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
576#[serde(tag = "type", rename_all = "snake_case")]
577pub enum ReactionType {
578 Emoji {
580 emoji: String,
582 },
583 CustomEmoji {
585 custom_emoji_id: String,
587 },
588 Paid,
590}
591
592#[derive(Debug, Clone, Default, Serialize, Deserialize)]
594pub struct LinkPreviewOptions {
595 #[serde(skip_serializing_if = "Option::is_none")]
597 pub is_disabled: Option<bool>,
598 #[serde(skip_serializing_if = "Option::is_none")]
600 pub url: Option<String>,
601 #[serde(skip_serializing_if = "Option::is_none")]
603 pub prefer_small_media: Option<bool>,
604 #[serde(skip_serializing_if = "Option::is_none")]
606 pub prefer_large_media: Option<bool>,
607 #[serde(skip_serializing_if = "Option::is_none")]
609 pub show_above_text: Option<bool>,
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize)]
614pub struct TextQuote {
615 pub text: String,
617 #[serde(skip_serializing_if = "Option::is_none")]
619 pub entities: Option<Vec<MessageEntity>>,
620 pub position: u32,
622 #[serde(skip_serializing_if = "Option::is_none")]
624 pub is_manual: Option<bool>,
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct ExternalReplyInfo {
630 pub origin: MessageOrigin,
632 #[serde(skip_serializing_if = "Option::is_none")]
634 pub chat: Option<Chat>,
635 #[serde(skip_serializing_if = "Option::is_none")]
637 pub message_id: Option<i64>,
638 #[serde(skip_serializing_if = "Option::is_none")]
640 pub link_preview_options: Option<LinkPreviewOptions>,
641 #[serde(skip_serializing_if = "Option::is_none")]
643 pub animation: Option<Animation>,
644 #[serde(skip_serializing_if = "Option::is_none")]
646 pub audio: Option<Audio>,
647 #[serde(skip_serializing_if = "Option::is_none")]
649 pub document: Option<Document>,
650 #[serde(skip_serializing_if = "Option::is_none")]
652 pub photo: Option<Vec<PhotoSize>>,
653 #[serde(skip_serializing_if = "Option::is_none")]
655 pub sticker: Option<Sticker>,
656 #[serde(skip_serializing_if = "Option::is_none")]
658 pub video: Option<Video>,
659 #[serde(skip_serializing_if = "Option::is_none")]
661 pub video_note: Option<VideoNote>,
662 #[serde(skip_serializing_if = "Option::is_none")]
664 pub voice: Option<Voice>,
665 #[serde(skip_serializing_if = "Option::is_none")]
667 pub has_media_spoiler: Option<bool>,
668 #[serde(skip_serializing_if = "Option::is_none")]
670 pub checklist: Option<Checklist>,
671 #[serde(skip_serializing_if = "Option::is_none")]
673 pub contact: Option<Contact>,
674 #[serde(skip_serializing_if = "Option::is_none")]
676 pub dice: Option<Dice>,
677 #[serde(skip_serializing_if = "Option::is_none")]
679 pub location: Option<Location>,
680 #[serde(skip_serializing_if = "Option::is_none")]
682 pub venue: Option<Venue>,
683 #[serde(skip_serializing_if = "Option::is_none")]
685 pub poll: Option<Poll>,
686}
687
688#[derive(Debug, Clone, Serialize, Deserialize)]
690pub struct Contact {
691 pub phone_number: String,
693 pub first_name: String,
695 #[serde(skip_serializing_if = "Option::is_none")]
697 pub last_name: Option<String>,
698 #[serde(skip_serializing_if = "Option::is_none")]
700 pub user_id: Option<i64>,
701 #[serde(skip_serializing_if = "Option::is_none")]
703 pub vcard: Option<String>,
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
708pub struct Dice {
709 pub emoji: String,
711 pub value: u8,
713}
714
715#[derive(Debug, Clone, Serialize, Deserialize)]
717pub struct WebAppData {
718 pub data: String,
720 pub button_text: String,
722}
723
724#[derive(Debug, Clone, Serialize, Deserialize)]
726pub struct WebAppInfo {
727 pub url: String,
729}
730
731#[derive(Debug, Clone, Serialize, Deserialize)]
733pub struct MessageId {
734 pub message_id: i64,
736}
737
738#[derive(Debug, Clone, Serialize, Deserialize)]
740pub struct ChatOwnerLeft {
741 #[serde(skip_serializing_if = "Option::is_none")]
743 pub new_owner: Option<User>,
744}
745
746#[derive(Debug, Clone, Serialize, Deserialize)]
748pub struct ChatOwnerChanged {
749 pub new_owner: User,
751}