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
361impl Message {
362 #[must_use]
364 pub fn effective_text(&self) -> Option<&str> {
365 self.text.as_deref().or(self.caption.as_deref())
366 }
367
368 #[must_use]
370 pub fn is_command(&self) -> bool {
371 self.entities
372 .as_deref()
373 .unwrap_or_default()
374 .iter()
375 .any(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)
376 }
377
378 #[must_use]
380 pub fn command(&self) -> Option<&str> {
381 let text = self.text.as_deref()?;
382 let entity = self
383 .entities
384 .as_deref()?
385 .iter()
386 .find(|e| e.kind == MessageEntityKind::BotCommand && e.offset == 0)?;
387 let raw = &text[..entity.length as usize];
388 Some(
390 raw.find('@')
391 .map_or(raw, |i| &raw[..i])
392 .trim_start_matches('/'),
393 )
394 }
395}
396
397#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
399#[serde(rename_all = "snake_case")]
400pub enum MessageEntityKind {
401 Mention,
403 Hashtag,
405 Cashtag,
407 BotCommand,
409 Url,
411 Email,
413 PhoneNumber,
415 Bold,
417 Italic,
419 Underline,
421 Strikethrough,
423 Spoiler,
425 Blockquote,
427 ExpandableBlockquote,
429 Code,
431 Pre,
433 TextLink,
435 TextMention,
437 CustomEmoji,
439 DateTime,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct MessageEntity {
446 #[serde(rename = "type")]
448 pub kind: MessageEntityKind,
449 pub offset: u32,
451 pub length: u32,
453 #[serde(skip_serializing_if = "Option::is_none")]
455 pub url: Option<String>,
456 #[serde(skip_serializing_if = "Option::is_none")]
458 pub user: Option<User>,
459 #[serde(skip_serializing_if = "Option::is_none")]
461 pub language: Option<String>,
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub custom_emoji_id: Option<String>,
465 #[serde(skip_serializing_if = "Option::is_none")]
467 pub unix_time: Option<i64>,
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub date_time_format: Option<String>,
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize)]
475#[serde(tag = "type", rename_all = "snake_case")]
476pub enum MessageOrigin {
477 User {
479 date: i64,
481 sender_user: User,
483 },
484 HiddenUser {
486 date: i64,
488 sender_user_name: String,
490 },
491 Chat {
493 date: i64,
495 sender_chat: Chat,
497 #[serde(skip_serializing_if = "Option::is_none")]
499 author_signature: Option<String>,
500 },
501 Channel {
503 date: i64,
505 chat: Chat,
507 message_id: i64,
509 #[serde(skip_serializing_if = "Option::is_none")]
511 author_signature: Option<String>,
512 },
513}
514
515#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct ReplyParameters {
518 pub message_id: i64,
520 #[serde(skip_serializing_if = "Option::is_none")]
522 pub chat_id: Option<crate::user::ChatId>,
523 #[serde(skip_serializing_if = "Option::is_none")]
525 pub allow_sending_without_reply: Option<bool>,
526 #[serde(skip_serializing_if = "Option::is_none")]
528 pub quote: Option<String>,
529 #[serde(skip_serializing_if = "Option::is_none")]
531 pub quote_parse_mode: Option<ParseMode>,
532 #[serde(skip_serializing_if = "Option::is_none")]
534 pub quote_entities: Option<Vec<MessageEntity>>,
535 #[serde(skip_serializing_if = "Option::is_none")]
537 pub quote_position: Option<u32>,
538 #[serde(skip_serializing_if = "Option::is_none")]
540 pub poll_option_id: Option<String>,
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub checklist_task_id: Option<i64>,
544}
545
546#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
548pub enum ParseMode {
549 MarkdownV2,
551 HTML,
553 Markdown,
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize)]
559#[serde(tag = "type", rename_all = "snake_case")]
560pub enum ReactionType {
561 Emoji {
563 emoji: String,
565 },
566 CustomEmoji {
568 custom_emoji_id: String,
570 },
571 Paid,
573}
574
575#[derive(Debug, Clone, Default, Serialize, Deserialize)]
577pub struct LinkPreviewOptions {
578 #[serde(skip_serializing_if = "Option::is_none")]
580 pub is_disabled: Option<bool>,
581 #[serde(skip_serializing_if = "Option::is_none")]
583 pub url: Option<String>,
584 #[serde(skip_serializing_if = "Option::is_none")]
586 pub prefer_small_media: Option<bool>,
587 #[serde(skip_serializing_if = "Option::is_none")]
589 pub prefer_large_media: Option<bool>,
590 #[serde(skip_serializing_if = "Option::is_none")]
592 pub show_above_text: Option<bool>,
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize)]
597pub struct TextQuote {
598 pub text: String,
600 #[serde(skip_serializing_if = "Option::is_none")]
602 pub entities: Option<Vec<MessageEntity>>,
603 pub position: u32,
605 #[serde(skip_serializing_if = "Option::is_none")]
607 pub is_manual: Option<bool>,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct ExternalReplyInfo {
613 pub origin: MessageOrigin,
615 #[serde(skip_serializing_if = "Option::is_none")]
617 pub chat: Option<Chat>,
618 #[serde(skip_serializing_if = "Option::is_none")]
620 pub message_id: Option<i64>,
621 #[serde(skip_serializing_if = "Option::is_none")]
623 pub link_preview_options: Option<LinkPreviewOptions>,
624 #[serde(skip_serializing_if = "Option::is_none")]
626 pub animation: Option<Animation>,
627 #[serde(skip_serializing_if = "Option::is_none")]
629 pub audio: Option<Audio>,
630 #[serde(skip_serializing_if = "Option::is_none")]
632 pub document: Option<Document>,
633 #[serde(skip_serializing_if = "Option::is_none")]
635 pub photo: Option<Vec<PhotoSize>>,
636 #[serde(skip_serializing_if = "Option::is_none")]
638 pub sticker: Option<Sticker>,
639 #[serde(skip_serializing_if = "Option::is_none")]
641 pub video: Option<Video>,
642 #[serde(skip_serializing_if = "Option::is_none")]
644 pub video_note: Option<VideoNote>,
645 #[serde(skip_serializing_if = "Option::is_none")]
647 pub voice: Option<Voice>,
648 #[serde(skip_serializing_if = "Option::is_none")]
650 pub has_media_spoiler: Option<bool>,
651 #[serde(skip_serializing_if = "Option::is_none")]
653 pub checklist: Option<Checklist>,
654 #[serde(skip_serializing_if = "Option::is_none")]
656 pub contact: Option<Contact>,
657 #[serde(skip_serializing_if = "Option::is_none")]
659 pub dice: Option<Dice>,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub location: Option<Location>,
663 #[serde(skip_serializing_if = "Option::is_none")]
665 pub venue: Option<Venue>,
666 #[serde(skip_serializing_if = "Option::is_none")]
668 pub poll: Option<Poll>,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct Contact {
674 pub phone_number: String,
676 pub first_name: String,
678 #[serde(skip_serializing_if = "Option::is_none")]
680 pub last_name: Option<String>,
681 #[serde(skip_serializing_if = "Option::is_none")]
683 pub user_id: Option<i64>,
684 #[serde(skip_serializing_if = "Option::is_none")]
686 pub vcard: Option<String>,
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize)]
691pub struct Dice {
692 pub emoji: String,
694 pub value: u8,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize)]
700pub struct WebAppData {
701 pub data: String,
703 pub button_text: String,
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct WebAppInfo {
710 pub url: String,
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize)]
716pub struct MessageId {
717 pub message_id: i64,
719}
720
721#[derive(Debug, Clone, Serialize, Deserialize)]
723pub struct ChatOwnerLeft {
724 #[serde(skip_serializing_if = "Option::is_none")]
726 pub new_owner: Option<User>,
727}
728
729#[derive(Debug, Clone, Serialize, Deserialize)]
731pub struct ChatOwnerChanged {
732 pub new_owner: User,
734}