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#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Message {
27 pub message_id: i64,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub receiver_user: Option<User>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
41 pub ephemeral_message_id: Option<i64>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub message_thread_id: Option<i64>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub direct_messages_topic: Option<DirectMessagesTopic>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub from: Option<User>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub sender_chat: Option<Chat>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub sender_boost_count: Option<u32>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub sender_business_bot: Option<User>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub sender_tag: Option<String>,
70
71 pub date: i64,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub business_connection_id: Option<String>,
77
78 pub chat: Chat,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub forward_origin: Option<MessageOrigin>,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub is_topic_message: Option<bool>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
92 pub is_automatic_forward: Option<bool>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
98 pub reply_to_message: Option<Box<Message>>,
99
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub external_reply: Option<ExternalReplyInfo>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub quote: Option<TextQuote>,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub reply_to_story: Option<serde_json::Value>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub reply_to_checklist_task_id: Option<i64>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub reply_to_poll_option_id: Option<String>,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub via_bot: Option<User>,
123
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub edit_date: Option<i64>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub has_protected_content: Option<bool>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub is_from_offline: Option<bool>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
140 pub is_paid_post: Option<bool>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub media_group_id: Option<String>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub author_signature: Option<String>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub paid_star_count: Option<i64>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub text: Option<String>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub rich_message: Option<RichMessage>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub entities: Option<Vec<MessageEntity>>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub link_preview_options: Option<LinkPreviewOptions>,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
173 pub suggested_post_info: Option<SuggestedPostInfo>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub effect_id: Option<String>,
178
179 #[serde(skip_serializing_if = "Option::is_none")]
182 pub animation: Option<Animation>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub audio: Option<Audio>,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub document: Option<Document>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub paid_media: Option<serde_json::Value>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub photo: Option<Vec<PhotoSize>>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub live_photo: Option<LivePhoto>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub sticker: Option<Sticker>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub story: Option<serde_json::Value>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub video: Option<Video>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub video_note: Option<VideoNote>,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub voice: Option<Voice>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub caption: Option<String>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub caption_entities: Option<Vec<MessageEntity>>,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub show_caption_above_media: Option<bool>,
225
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub has_media_spoiler: Option<bool>,
229
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub checklist: Option<Checklist>,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
237 pub contact: Option<Contact>,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub dice: Option<Dice>,
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub game: Option<serde_json::Value>,
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub poll: Option<Poll>,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub venue: Option<Venue>,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub location: Option<Location>,
253
254 #[serde(skip_serializing_if = "Option::is_none")]
257 pub new_chat_members: Option<Vec<User>>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub left_chat_member: Option<User>,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub chat_owner_left: Option<ChatOwnerLeft>,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub chat_owner_changed: Option<ChatOwnerChanged>,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub new_chat_title: Option<String>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub new_chat_photo: Option<Vec<PhotoSize>>,
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub delete_chat_photo: Option<bool>,
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub group_chat_created: Option<bool>,
279 #[serde(skip_serializing_if = "Option::is_none")]
281 pub supergroup_chat_created: Option<bool>,
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub channel_chat_created: Option<bool>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub community_chat_added: Option<CommunityChatAdded>,
288 #[serde(skip_serializing_if = "Option::is_none")]
290 pub community_chat_removed: Option<CommunityChatRemoved>,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub message_auto_delete_timer_changed: Option<serde_json::Value>,
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub migrate_to_chat_id: Option<i64>,
297 #[serde(skip_serializing_if = "Option::is_none")]
299 pub migrate_from_chat_id: Option<i64>,
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub pinned_message: Option<Box<Message>>,
303
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub reply_markup: Option<InlineKeyboardMarkup>,
307
308 #[serde(skip_serializing_if = "Option::is_none")]
311 pub invoice: Option<serde_json::Value>,
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub successful_payment: Option<serde_json::Value>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub refunded_payment: Option<serde_json::Value>,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
322 pub web_app_data: Option<WebAppData>,
323
324 #[serde(skip_serializing_if = "Option::is_none")]
327 pub forum_topic_created: Option<serde_json::Value>,
328 #[serde(skip_serializing_if = "Option::is_none")]
330 pub forum_topic_edited: Option<serde_json::Value>,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub forum_topic_closed: Option<serde_json::Value>,
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub forum_topic_reopened: Option<serde_json::Value>,
337 #[serde(skip_serializing_if = "Option::is_none")]
339 pub general_forum_topic_hidden: Option<serde_json::Value>,
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub general_forum_topic_unhidden: Option<serde_json::Value>,
343
344 #[serde(skip_serializing_if = "Option::is_none")]
347 pub managed_bot_created: Option<ManagedBotCreated>,
348
349 #[serde(skip_serializing_if = "Option::is_none")]
352 pub poll_option_added: Option<PollOptionAdded>,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub poll_option_deleted: Option<PollOptionDeleted>,
356
357 #[serde(skip_serializing_if = "Option::is_none")]
360 pub checklist_tasks_done: Option<ChecklistTasksDone>,
361 #[serde(skip_serializing_if = "Option::is_none")]
363 pub checklist_tasks_added: Option<ChecklistTasksAdded>,
364
365 #[serde(skip_serializing_if = "Option::is_none")]
368 pub direct_message_price_changed: Option<DirectMessagePriceChanged>,
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub paid_message_price_changed: Option<PaidMessagePriceChanged>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
376 pub suggested_post_approved: Option<SuggestedPostApproved>,
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub suggested_post_approval_failed: Option<SuggestedPostApprovalFailed>,
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub suggested_post_declined: Option<SuggestedPostDeclined>,
383 #[serde(skip_serializing_if = "Option::is_none")]
385 pub suggested_post_paid: Option<SuggestedPostPaid>,
386 #[serde(skip_serializing_if = "Option::is_none")]
388 pub suggested_post_refunded: Option<SuggestedPostRefunded>,
389
390 #[serde(skip_serializing_if = "Option::is_none")]
393 pub guest_bot_caller_user: Option<User>,
394
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub guest_bot_caller_chat: Option<crate::chat::Chat>,
398
399 #[serde(skip_serializing_if = "Option::is_none")]
405 pub guest_query_id: Option<String>,
406}
407
408impl Message {
409 #[must_use]
411 pub fn effective_text(&self) -> Option<&str> {
412 self.text.as_deref().or(self.caption.as_deref())
413 }
414
415 #[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 #[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 Some(
437 raw.find('@')
438 .map_or(raw, |i| &raw[..i])
439 .trim_start_matches('/'),
440 )
441 }
442}
443
444#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
446#[serde(rename_all = "snake_case")]
447pub enum MessageEntityKind {
448 Mention,
450 Hashtag,
452 Cashtag,
454 BotCommand,
456 Url,
458 Email,
460 PhoneNumber,
462 Bold,
464 Italic,
466 Underline,
468 Strikethrough,
470 Spoiler,
472 Blockquote,
474 ExpandableBlockquote,
476 Code,
478 Pre,
480 TextLink,
482 TextMention,
484 CustomEmoji,
486 DateTime,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct MessageEntity {
493 #[serde(rename = "type")]
495 pub kind: MessageEntityKind,
496 pub offset: u32,
498 pub length: u32,
500 #[serde(skip_serializing_if = "Option::is_none")]
502 pub url: Option<String>,
503 #[serde(skip_serializing_if = "Option::is_none")]
505 pub user: Option<User>,
506 #[serde(skip_serializing_if = "Option::is_none")]
508 pub language: Option<String>,
509 #[serde(skip_serializing_if = "Option::is_none")]
511 pub custom_emoji_id: Option<String>,
512 #[serde(skip_serializing_if = "Option::is_none")]
514 pub unix_time: Option<i64>,
515 #[serde(skip_serializing_if = "Option::is_none")]
517 pub date_time_format: Option<String>,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522#[serde(tag = "type", rename_all = "snake_case")]
523pub enum MessageOrigin {
524 User {
526 date: i64,
528 sender_user: User,
530 },
531 HiddenUser {
533 date: i64,
535 sender_user_name: String,
537 },
538 Chat {
540 date: i64,
542 sender_chat: Chat,
544 #[serde(skip_serializing_if = "Option::is_none")]
546 author_signature: Option<String>,
547 },
548 Channel {
550 date: i64,
552 chat: Chat,
554 message_id: i64,
556 #[serde(skip_serializing_if = "Option::is_none")]
558 author_signature: Option<String>,
559 },
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
564pub struct ReplyParameters {
565 #[serde(skip_serializing_if = "Option::is_none")]
569 pub message_id: Option<i64>,
570 #[serde(skip_serializing_if = "Option::is_none")]
574 pub ephemeral_message_id: Option<i64>,
575 #[serde(skip_serializing_if = "Option::is_none")]
579 pub chat_id: Option<crate::user::ChatId>,
580 #[serde(skip_serializing_if = "Option::is_none")]
585 pub allow_sending_without_reply: Option<bool>,
586 #[serde(skip_serializing_if = "Option::is_none")]
591 pub quote: Option<String>,
592 #[serde(skip_serializing_if = "Option::is_none")]
594 pub quote_parse_mode: Option<ParseMode>,
595 #[serde(skip_serializing_if = "Option::is_none")]
597 pub quote_entities: Option<Vec<MessageEntity>>,
598 #[serde(skip_serializing_if = "Option::is_none")]
600 pub quote_position: Option<u32>,
601 #[serde(skip_serializing_if = "Option::is_none")]
603 pub poll_option_id: Option<String>,
604 #[serde(skip_serializing_if = "Option::is_none")]
606 pub checklist_task_id: Option<i64>,
607}
608
609#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
611pub enum ParseMode {
612 MarkdownV2,
614 HTML,
616 Markdown,
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
622#[serde(tag = "type", rename_all = "snake_case")]
623pub enum ReactionType {
624 Emoji {
626 emoji: String,
628 },
629 CustomEmoji {
631 custom_emoji_id: String,
633 },
634 Paid,
636}
637
638#[derive(Debug, Clone, Default, Serialize, Deserialize)]
640pub struct LinkPreviewOptions {
641 #[serde(skip_serializing_if = "Option::is_none")]
643 pub is_disabled: Option<bool>,
644 #[serde(skip_serializing_if = "Option::is_none")]
646 pub url: Option<String>,
647 #[serde(skip_serializing_if = "Option::is_none")]
649 pub prefer_small_media: Option<bool>,
650 #[serde(skip_serializing_if = "Option::is_none")]
652 pub prefer_large_media: Option<bool>,
653 #[serde(skip_serializing_if = "Option::is_none")]
655 pub show_above_text: Option<bool>,
656}
657
658#[derive(Debug, Clone, Serialize, Deserialize)]
660pub struct TextQuote {
661 pub text: String,
663 #[serde(skip_serializing_if = "Option::is_none")]
665 pub entities: Option<Vec<MessageEntity>>,
666 pub position: u32,
668 #[serde(skip_serializing_if = "Option::is_none")]
670 pub is_manual: Option<bool>,
671}
672
673#[derive(Debug, Clone, Serialize, Deserialize)]
675pub struct ExternalReplyInfo {
676 pub origin: MessageOrigin,
678 #[serde(skip_serializing_if = "Option::is_none")]
680 pub chat: Option<Chat>,
681 #[serde(skip_serializing_if = "Option::is_none")]
683 pub message_id: Option<i64>,
684 #[serde(skip_serializing_if = "Option::is_none")]
686 pub link_preview_options: Option<LinkPreviewOptions>,
687 #[serde(skip_serializing_if = "Option::is_none")]
689 pub animation: Option<Animation>,
690 #[serde(skip_serializing_if = "Option::is_none")]
692 pub audio: Option<Audio>,
693 #[serde(skip_serializing_if = "Option::is_none")]
695 pub document: Option<Document>,
696 #[serde(skip_serializing_if = "Option::is_none")]
698 pub photo: Option<Vec<PhotoSize>>,
699 #[serde(skip_serializing_if = "Option::is_none")]
701 pub live_photo: Option<LivePhoto>,
702 #[serde(skip_serializing_if = "Option::is_none")]
704 pub sticker: Option<Sticker>,
705 #[serde(skip_serializing_if = "Option::is_none")]
707 pub video: Option<Video>,
708 #[serde(skip_serializing_if = "Option::is_none")]
710 pub video_note: Option<VideoNote>,
711 #[serde(skip_serializing_if = "Option::is_none")]
713 pub voice: Option<Voice>,
714 #[serde(skip_serializing_if = "Option::is_none")]
716 pub has_media_spoiler: Option<bool>,
717 #[serde(skip_serializing_if = "Option::is_none")]
719 pub checklist: Option<Checklist>,
720 #[serde(skip_serializing_if = "Option::is_none")]
722 pub contact: Option<Contact>,
723 #[serde(skip_serializing_if = "Option::is_none")]
725 pub dice: Option<Dice>,
726 #[serde(skip_serializing_if = "Option::is_none")]
728 pub location: Option<Location>,
729 #[serde(skip_serializing_if = "Option::is_none")]
731 pub venue: Option<Venue>,
732 #[serde(skip_serializing_if = "Option::is_none")]
734 pub poll: Option<Poll>,
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize)]
739pub struct Contact {
740 pub phone_number: String,
742 pub first_name: String,
744 #[serde(skip_serializing_if = "Option::is_none")]
746 pub last_name: Option<String>,
747 #[serde(skip_serializing_if = "Option::is_none")]
749 pub user_id: Option<i64>,
750 #[serde(skip_serializing_if = "Option::is_none")]
752 pub vcard: Option<String>,
753}
754
755#[derive(Debug, Clone, Serialize, Deserialize)]
757pub struct Dice {
758 pub emoji: String,
760 pub value: u8,
762}
763
764#[derive(Debug, Clone, Serialize, Deserialize)]
766pub struct WebAppData {
767 pub data: String,
769 pub button_text: String,
771}
772
773#[derive(Debug, Clone, Serialize, Deserialize)]
775pub struct WebAppInfo {
776 pub url: String,
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize)]
782pub struct MessageId {
783 pub message_id: i64,
785}
786
787#[derive(Debug, Clone, Serialize, Deserialize)]
789pub struct ChatOwnerLeft {
790 #[serde(skip_serializing_if = "Option::is_none")]
792 pub new_owner: Option<User>,
793}
794
795#[derive(Debug, Clone, Serialize, Deserialize)]
797pub struct ChatOwnerChanged {
798 pub new_owner: User,
800}