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#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Message {
26 pub message_id: i64,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub message_thread_id: Option<i64>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub direct_messages_topic: Option<DirectMessagesTopic>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub from: Option<User>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub sender_chat: Option<Chat>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub sender_boost_count: Option<u32>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub sender_business_bot: Option<User>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub sender_tag: Option<String>,
56
57 pub date: i64,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub business_connection_id: Option<String>,
63
64 pub chat: Chat,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub forward_origin: Option<MessageOrigin>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub is_topic_message: Option<bool>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
78 pub is_automatic_forward: Option<bool>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub reply_to_message: Option<Box<Message>>,
83
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub external_reply: Option<ExternalReplyInfo>,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub quote: Option<TextQuote>,
91
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub reply_to_story: Option<serde_json::Value>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub reply_to_checklist_task_id: Option<i64>,
99
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub reply_to_poll_option_id: Option<String>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub via_bot: Option<User>,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub edit_date: Option<i64>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub has_protected_content: Option<bool>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub is_from_offline: Option<bool>,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
124 pub is_paid_post: Option<bool>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub media_group_id: Option<String>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub author_signature: Option<String>,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub paid_star_count: Option<i64>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub text: Option<String>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub rich_message: Option<RichMessage>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub entities: Option<Vec<MessageEntity>>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub link_preview_options: Option<LinkPreviewOptions>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
157 pub suggested_post_info: Option<SuggestedPostInfo>,
158
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub effect_id: Option<String>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
166 pub animation: Option<Animation>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub audio: Option<Audio>,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub document: Option<Document>,
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub paid_media: Option<serde_json::Value>,
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub photo: Option<Vec<PhotoSize>>,
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub live_photo: Option<LivePhoto>,
182 #[serde(skip_serializing_if = "Option::is_none")]
184 pub sticker: Option<Sticker>,
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub story: Option<serde_json::Value>,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub video: Option<Video>,
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub video_note: Option<VideoNote>,
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub voice: Option<Voice>,
197
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub caption: Option<String>,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub caption_entities: Option<Vec<MessageEntity>>,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
208 pub show_caption_above_media: Option<bool>,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub has_media_spoiler: Option<bool>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub checklist: Option<Checklist>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
221 pub contact: Option<Contact>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub dice: Option<Dice>,
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub game: Option<serde_json::Value>,
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub poll: Option<Poll>,
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub venue: Option<Venue>,
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub location: Option<Location>,
237
238 #[serde(skip_serializing_if = "Option::is_none")]
241 pub new_chat_members: Option<Vec<User>>,
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub left_chat_member: Option<User>,
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub chat_owner_left: Option<ChatOwnerLeft>,
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub chat_owner_changed: Option<ChatOwnerChanged>,
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub new_chat_title: Option<String>,
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub new_chat_photo: Option<Vec<PhotoSize>>,
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub delete_chat_photo: Option<bool>,
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub group_chat_created: Option<bool>,
263 #[serde(skip_serializing_if = "Option::is_none")]
265 pub supergroup_chat_created: Option<bool>,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub channel_chat_created: Option<bool>,
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub message_auto_delete_timer_changed: Option<serde_json::Value>,
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub migrate_to_chat_id: Option<i64>,
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub migrate_from_chat_id: Option<i64>,
278 #[serde(skip_serializing_if = "Option::is_none")]
280 pub pinned_message: Option<Box<Message>>,
281
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub reply_markup: Option<InlineKeyboardMarkup>,
285
286 #[serde(skip_serializing_if = "Option::is_none")]
289 pub invoice: Option<serde_json::Value>,
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub successful_payment: Option<serde_json::Value>,
293 #[serde(skip_serializing_if = "Option::is_none")]
295 pub refunded_payment: Option<serde_json::Value>,
296
297 #[serde(skip_serializing_if = "Option::is_none")]
300 pub web_app_data: Option<WebAppData>,
301
302 #[serde(skip_serializing_if = "Option::is_none")]
305 pub forum_topic_created: Option<serde_json::Value>,
306 #[serde(skip_serializing_if = "Option::is_none")]
308 pub forum_topic_edited: Option<serde_json::Value>,
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub forum_topic_closed: Option<serde_json::Value>,
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub forum_topic_reopened: Option<serde_json::Value>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub general_forum_topic_hidden: Option<serde_json::Value>,
318 #[serde(skip_serializing_if = "Option::is_none")]
320 pub general_forum_topic_unhidden: Option<serde_json::Value>,
321
322 #[serde(skip_serializing_if = "Option::is_none")]
325 pub managed_bot_created: Option<ManagedBotCreated>,
326
327 #[serde(skip_serializing_if = "Option::is_none")]
330 pub poll_option_added: Option<PollOptionAdded>,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub poll_option_deleted: Option<PollOptionDeleted>,
334
335 #[serde(skip_serializing_if = "Option::is_none")]
338 pub checklist_tasks_done: Option<ChecklistTasksDone>,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub checklist_tasks_added: Option<ChecklistTasksAdded>,
342
343 #[serde(skip_serializing_if = "Option::is_none")]
346 pub direct_message_price_changed: Option<DirectMessagePriceChanged>,
347 #[serde(skip_serializing_if = "Option::is_none")]
349 pub paid_message_price_changed: Option<PaidMessagePriceChanged>,
350
351 #[serde(skip_serializing_if = "Option::is_none")]
354 pub suggested_post_approved: Option<SuggestedPostApproved>,
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub suggested_post_approval_failed: Option<SuggestedPostApprovalFailed>,
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub suggested_post_declined: Option<SuggestedPostDeclined>,
361 #[serde(skip_serializing_if = "Option::is_none")]
363 pub suggested_post_paid: Option<SuggestedPostPaid>,
364 #[serde(skip_serializing_if = "Option::is_none")]
366 pub suggested_post_refunded: Option<SuggestedPostRefunded>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
371 pub guest_bot_caller_user: Option<User>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub guest_bot_caller_chat: Option<crate::chat::Chat>,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
383 pub guest_query_id: Option<String>,
384}
385
386impl Message {
387 #[must_use]
389 pub fn effective_text(&self) -> Option<&str> {
390 self.text.as_deref().or(self.caption.as_deref())
391 }
392
393 #[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 #[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 Some(
415 raw.find('@')
416 .map_or(raw, |i| &raw[..i])
417 .trim_start_matches('/'),
418 )
419 }
420}
421
422#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
424#[serde(rename_all = "snake_case")]
425pub enum MessageEntityKind {
426 Mention,
428 Hashtag,
430 Cashtag,
432 BotCommand,
434 Url,
436 Email,
438 PhoneNumber,
440 Bold,
442 Italic,
444 Underline,
446 Strikethrough,
448 Spoiler,
450 Blockquote,
452 ExpandableBlockquote,
454 Code,
456 Pre,
458 TextLink,
460 TextMention,
462 CustomEmoji,
464 DateTime,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct MessageEntity {
471 #[serde(rename = "type")]
473 pub kind: MessageEntityKind,
474 pub offset: u32,
476 pub length: u32,
478 #[serde(skip_serializing_if = "Option::is_none")]
480 pub url: Option<String>,
481 #[serde(skip_serializing_if = "Option::is_none")]
483 pub user: Option<User>,
484 #[serde(skip_serializing_if = "Option::is_none")]
486 pub language: Option<String>,
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub custom_emoji_id: Option<String>,
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub unix_time: Option<i64>,
493 #[serde(skip_serializing_if = "Option::is_none")]
495 pub date_time_format: Option<String>,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500#[serde(tag = "type", rename_all = "snake_case")]
501pub enum MessageOrigin {
502 User {
504 date: i64,
506 sender_user: User,
508 },
509 HiddenUser {
511 date: i64,
513 sender_user_name: String,
515 },
516 Chat {
518 date: i64,
520 sender_chat: Chat,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 author_signature: Option<String>,
525 },
526 Channel {
528 date: i64,
530 chat: Chat,
532 message_id: i64,
534 #[serde(skip_serializing_if = "Option::is_none")]
536 author_signature: Option<String>,
537 },
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct ReplyParameters {
543 pub message_id: i64,
545 #[serde(skip_serializing_if = "Option::is_none")]
547 pub chat_id: Option<crate::user::ChatId>,
548 #[serde(skip_serializing_if = "Option::is_none")]
550 pub allow_sending_without_reply: Option<bool>,
551 #[serde(skip_serializing_if = "Option::is_none")]
553 pub quote: Option<String>,
554 #[serde(skip_serializing_if = "Option::is_none")]
556 pub quote_parse_mode: Option<ParseMode>,
557 #[serde(skip_serializing_if = "Option::is_none")]
559 pub quote_entities: Option<Vec<MessageEntity>>,
560 #[serde(skip_serializing_if = "Option::is_none")]
562 pub quote_position: Option<u32>,
563 #[serde(skip_serializing_if = "Option::is_none")]
565 pub poll_option_id: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
568 pub checklist_task_id: Option<i64>,
569}
570
571#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
573pub enum ParseMode {
574 MarkdownV2,
576 HTML,
578 Markdown,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
584#[serde(tag = "type", rename_all = "snake_case")]
585pub enum ReactionType {
586 Emoji {
588 emoji: String,
590 },
591 CustomEmoji {
593 custom_emoji_id: String,
595 },
596 Paid,
598}
599
600#[derive(Debug, Clone, Default, Serialize, Deserialize)]
602pub struct LinkPreviewOptions {
603 #[serde(skip_serializing_if = "Option::is_none")]
605 pub is_disabled: Option<bool>,
606 #[serde(skip_serializing_if = "Option::is_none")]
608 pub url: Option<String>,
609 #[serde(skip_serializing_if = "Option::is_none")]
611 pub prefer_small_media: Option<bool>,
612 #[serde(skip_serializing_if = "Option::is_none")]
614 pub prefer_large_media: Option<bool>,
615 #[serde(skip_serializing_if = "Option::is_none")]
617 pub show_above_text: Option<bool>,
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
622pub struct TextQuote {
623 pub text: String,
625 #[serde(skip_serializing_if = "Option::is_none")]
627 pub entities: Option<Vec<MessageEntity>>,
628 pub position: u32,
630 #[serde(skip_serializing_if = "Option::is_none")]
632 pub is_manual: Option<bool>,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize)]
637pub struct ExternalReplyInfo {
638 pub origin: MessageOrigin,
640 #[serde(skip_serializing_if = "Option::is_none")]
642 pub chat: Option<Chat>,
643 #[serde(skip_serializing_if = "Option::is_none")]
645 pub message_id: Option<i64>,
646 #[serde(skip_serializing_if = "Option::is_none")]
648 pub link_preview_options: Option<LinkPreviewOptions>,
649 #[serde(skip_serializing_if = "Option::is_none")]
651 pub animation: Option<Animation>,
652 #[serde(skip_serializing_if = "Option::is_none")]
654 pub audio: Option<Audio>,
655 #[serde(skip_serializing_if = "Option::is_none")]
657 pub document: Option<Document>,
658 #[serde(skip_serializing_if = "Option::is_none")]
660 pub photo: Option<Vec<PhotoSize>>,
661 #[serde(skip_serializing_if = "Option::is_none")]
663 pub live_photo: Option<LivePhoto>,
664 #[serde(skip_serializing_if = "Option::is_none")]
666 pub sticker: Option<Sticker>,
667 #[serde(skip_serializing_if = "Option::is_none")]
669 pub video: Option<Video>,
670 #[serde(skip_serializing_if = "Option::is_none")]
672 pub video_note: Option<VideoNote>,
673 #[serde(skip_serializing_if = "Option::is_none")]
675 pub voice: Option<Voice>,
676 #[serde(skip_serializing_if = "Option::is_none")]
678 pub has_media_spoiler: Option<bool>,
679 #[serde(skip_serializing_if = "Option::is_none")]
681 pub checklist: Option<Checklist>,
682 #[serde(skip_serializing_if = "Option::is_none")]
684 pub contact: Option<Contact>,
685 #[serde(skip_serializing_if = "Option::is_none")]
687 pub dice: Option<Dice>,
688 #[serde(skip_serializing_if = "Option::is_none")]
690 pub location: Option<Location>,
691 #[serde(skip_serializing_if = "Option::is_none")]
693 pub venue: Option<Venue>,
694 #[serde(skip_serializing_if = "Option::is_none")]
696 pub poll: Option<Poll>,
697}
698
699#[derive(Debug, Clone, Serialize, Deserialize)]
701pub struct Contact {
702 pub phone_number: String,
704 pub first_name: String,
706 #[serde(skip_serializing_if = "Option::is_none")]
708 pub last_name: Option<String>,
709 #[serde(skip_serializing_if = "Option::is_none")]
711 pub user_id: Option<i64>,
712 #[serde(skip_serializing_if = "Option::is_none")]
714 pub vcard: Option<String>,
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize)]
719pub struct Dice {
720 pub emoji: String,
722 pub value: u8,
724}
725
726#[derive(Debug, Clone, Serialize, Deserialize)]
728pub struct WebAppData {
729 pub data: String,
731 pub button_text: String,
733}
734
735#[derive(Debug, Clone, Serialize, Deserialize)]
737pub struct WebAppInfo {
738 pub url: String,
740}
741
742#[derive(Debug, Clone, Serialize, Deserialize)]
744pub struct MessageId {
745 pub message_id: i64,
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize)]
751pub struct ChatOwnerLeft {
752 #[serde(skip_serializing_if = "Option::is_none")]
754 pub new_owner: Option<User>,
755}
756
757#[derive(Debug, Clone, Serialize, Deserialize)]
759pub struct ChatOwnerChanged {
760 pub new_owner: User,
762}