mobot/api/
message.rs

1use chrono::Utc;
2use mobot_derive::BotRequest;
3use serde::{Deserialize, Serialize};
4
5use super::{chat::Chat, sticker::Sticker, user::User, Document, PhotoSize, ReplyMarkup, API};
6
7/// `Message` represents a message sent in a chat. It can be a text message, a sticker, a photo, etc.
8/// <https://core.telegram.org/bots/api#message>
9#[derive(Default, Debug, Clone, Deserialize, Serialize)]
10pub struct Message {
11    /// Unique message identifier inside this chat
12    pub message_id: i64,
13
14    /// Sender, empty for messages sent to channels
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub from: Option<User>,
17
18    /// Date the message was sent in Unix time
19    pub date: i64,
20
21    /// Message text
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub text: Option<String>,
24
25    /// Message is a photo, available sizes of the photo
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub photo: Option<Vec<PhotoSize>>,
28
29    /// Message is a general file, information about the file
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub document: Option<Document>,
32
33    /// Conversation the message belongs to
34    /// - For sent messages, the first available identifier of the chat
35    /// - For messages forwarded to the chat, the identifier of the original chat
36    /// - For messages in channels, the identifier of the channel is contained in the `chat_id` field
37    pub chat: Chat,
38
39    /// For forwarded messages, sender of the original message
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub forward_from: Option<User>,
42
43    /// For messages forwarded from channels, information about the original channel
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub forward_from_chat: Option<Chat>,
46
47    /// For messages forwarded from channels, identifier of the original message in the channel
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub forward_from_message_id: Option<i64>,
50
51    /// For messages forwarded from channels, signature of the post author if present
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub forward_signature: Option<String>,
54
55    /// Sender's name for messages forwarded from users who disallow adding a link to their account in forwarded messages
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub forward_sender_name: Option<String>,
58
59    /// For forwarded messages, date the original message was sent in Unix time
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub forward_date: Option<i64>,
62
63    /// For replies, the original message. Note that the Message object in this field will not contain further `reply_to_message` fields even if it itself is a reply.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub reply_to_message: Option<i64>,
66
67    /// Sticker for messages with a sticker
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub sticker: Option<Sticker>,
70
71    /// Inline keyboard attached to the message.
72    #[serde(flatten, skip_serializing_if = "Option::is_none")]
73    pub reply_markup: Option<ReplyMarkup>,
74}
75
76impl Message {
77    /// Creates a new `Message` with the given `text` and `from` fields.
78    pub fn new(from: impl Into<String>, text: impl Into<String>) -> Self {
79        let mut message = Message::fake(from.into());
80        message.text = Some(text.into());
81        message
82    }
83
84    pub fn fake(from: impl AsRef<str>) -> Self {
85        Message {
86            message_id: rand::random(),
87            from: Some(from.as_ref().into()),
88            date: Utc::now().timestamp(),
89            chat: from.as_ref().into(),
90            ..Default::default()
91        }
92    }
93}
94
95#[derive(Debug, Deserialize, Serialize, Clone)]
96pub enum ParseMode {
97    #[serde(rename = "MarkdownV2")]
98    MarkdownV2,
99    #[serde(rename = "Markdown")]
100    Markdown,
101    #[serde(rename = "HTML")]
102    HTML,
103    #[serde(rename = "")]
104    Text,
105}
106
107#[derive(Default, Debug, Serialize, Deserialize, Clone, BotRequest)]
108pub struct SendMessageRequest {
109    /// Unique identifier for the target chat or username of the target
110    pub chat_id: i64,
111
112    /// Text of the message to be sent
113    pub text: String,
114
115    /// If the message is a reply, ID of the original message
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub reply_to_message_id: Option<i64>,
118
119    /// Parse mode for the message
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub parse_mode: Option<ParseMode>,
122
123    /// Reply markup for the message
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub reply_markup: Option<ReplyMarkup>,
126}
127
128impl SendMessageRequest {
129    pub fn new(chat_id: i64, text: impl Into<String>) -> Self {
130        Self {
131            chat_id,
132            text: text.into(),
133            ..Default::default()
134        }
135    }
136
137    pub fn with_reply_markup(mut self, reply_markup: ReplyMarkup) -> Self {
138        self.reply_markup = Some(reply_markup);
139        self
140    }
141
142    pub fn with_parse_mode(mut self, parse_mode: ParseMode) -> Self {
143        self.parse_mode = Some(parse_mode);
144        self
145    }
146}
147
148#[derive(Default, Debug, Serialize, Deserialize, Clone)]
149pub struct EditMessageBase {
150    /// Required if `inline_message_id` is not specified. Unique identifier for the
151    /// target chat or username of the target channel (in the format @channelusername)
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub chat_id: Option<i64>,
154
155    /// Required if `inline_message_id` is not specified. Identifier of the message
156    /// to edit
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub message_id: Option<i64>,
159
160    /// Inline message identifier
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub inline_message_id: Option<String>,
163
164    /// Mode for parsing entities in the message text. See formatting options for
165    /// more details.
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub parse_mode: Option<ParseMode>,
168
169    /// Reply markup for the message
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub reply_markup: Option<String>,
172}
173
174impl EditMessageBase {
175    pub fn new() -> Self {
176        Self::default()
177    }
178
179    pub fn with_chat_id(mut self, chat_id: i64) -> Self {
180        self.chat_id = Some(chat_id);
181        self
182    }
183
184    pub fn with_message_id(mut self, message_id: i64) -> Self {
185        self.message_id = Some(message_id);
186        self
187    }
188
189    pub fn with_parse_mode(mut self, parse_mode: ParseMode) -> Self {
190        self.parse_mode = Some(parse_mode);
191        self
192    }
193
194    pub fn with_reply_markup(mut self, reply_markup: ReplyMarkup) -> Self {
195        self.reply_markup = Some(serde_json::to_string(&reply_markup).unwrap());
196        self
197    }
198}
199
200#[derive(Default, Debug, Serialize, Deserialize, Clone, BotRequest)]
201pub struct EditMessageTextRequest {
202    /// Base fields for edit requests
203    #[serde(flatten)]
204    pub base: EditMessageBase,
205
206    /// The new text of the message, 1-4096 characters after entities parsing
207    /// (Markdown or HTML)
208    pub text: String,
209}
210
211impl EditMessageTextRequest {
212    pub fn new(text: String) -> Self {
213        Self {
214            base: EditMessageBase::new(),
215            text,
216        }
217    }
218
219    pub fn with_chat_id(mut self, chat_id: i64) -> Self {
220        self.base.chat_id = Some(chat_id);
221        self
222    }
223
224    pub fn with_message_id(mut self, message_id: i64) -> Self {
225        self.base.message_id = Some(message_id);
226        self
227    }
228}
229
230#[derive(Default, Debug, Serialize, Deserialize, Clone, BotRequest)]
231pub struct EditMessageCaptionRequest {
232    /// Base fields for edit requests
233    #[serde(flatten)]
234    pub base: EditMessageBase,
235
236    /// New caption of the message, 0-1024 characters after entities parsing
237    /// (Markdown or HTML)
238    pub caption: String,
239}
240
241impl EditMessageCaptionRequest {
242    pub fn new(caption: String) -> Self {
243        Self {
244            base: EditMessageBase::new(),
245            caption,
246        }
247    }
248
249    pub fn with_chat_id(mut self, chat_id: i64) -> Self {
250        self.base.chat_id = Some(chat_id);
251        self
252    }
253}
254
255#[derive(Default, Debug, Serialize, Deserialize, Clone, BotRequest)]
256pub struct EditMessageReplyMarkupRequest {
257    /// Base fields for edit requests
258    #[serde(flatten)]
259    pub base: EditMessageBase,
260}
261
262impl EditMessageReplyMarkupRequest {
263    pub fn new(reply_markup: ReplyMarkup) -> Self {
264        Self {
265            base: EditMessageBase::new().with_reply_markup(reply_markup),
266        }
267    }
268
269    pub fn with_chat_id(mut self, chat_id: i64) -> Self {
270        self.base.chat_id = Some(chat_id);
271        self
272    }
273
274    pub fn with_message_id(mut self, message_id: i64) -> Self {
275        self.base.message_id = Some(message_id);
276        self
277    }
278}
279
280#[derive(Default, Debug, Serialize, Deserialize, Clone, BotRequest)]
281pub struct DeleteMessageRequest {
282    /// Unique identifier for the target chat or username of the target channel
283    /// (in the format @channelusername)
284    pub chat_id: i64,
285
286    /// Identifier of the message to delete
287    pub message_id: i64,
288}
289
290impl DeleteMessageRequest {
291    pub fn new(chat_id: i64, message_id: i64) -> Self {
292        Self {
293            chat_id,
294            message_id,
295        }
296    }
297}
298
299/// API methods for sending, editing, and deleting messages.
300impl API {
301    /// Send a message to a chat or channel.
302    ///
303    /// # Example
304    ///
305    /// ```no_run
306    /// # use mobot::*;
307    /// # #[tokio::main]
308    /// # async fn main() {
309    /// #    let api = API::new(Client::new(String::from("boo")));
310    /// #    let chat_id = 123456789;
311    ///    api.send_message(&api::SendMessageRequest::new(chat_id, "Hello!")).await;
312    /// # }
313    /// ```
314    pub async fn send_message(&self, req: &SendMessageRequest) -> anyhow::Result<Message> {
315        self.client.post("sendMessage", req).await
316    }
317
318    /// Edit the text of a previously sent message.
319    ///
320    /// # Example
321    ///
322    /// ```no_run
323    /// # use mobot::*;
324    /// # #[tokio::main]
325    /// # async fn main() {
326    /// #    let api = API::new(Client::new(String::from("boo")));
327    /// #    let chat_id = 123456789;
328    /// #    let message_id = 0;
329    /// api.edit_message_text(
330    ///   &api::EditMessageTextRequest::new(String::from("Changed my mind: Goodbye world!"))
331    ///      .with_chat_id(chat_id)
332    ///      .with_message_id(message_id)
333    /// ).await;
334    /// # }
335    /// ```
336    pub async fn edit_message_text(&self, req: &EditMessageTextRequest) -> anyhow::Result<Message> {
337        self.client.post("editMessageText", req).await
338    }
339
340    /// Edit the caption of a message.
341    pub async fn edit_message_caption(
342        &self,
343        req: &EditMessageCaptionRequest,
344    ) -> anyhow::Result<Message> {
345        self.client.post("editMessageCaption", req).await
346    }
347
348    /// Edit the reply markup of a message.
349    pub async fn edit_message_reply_markup(
350        &self,
351        req: &EditMessageReplyMarkupRequest,
352    ) -> anyhow::Result<Message> {
353        self.client.post("editMessageReplyMarkup", req).await
354    }
355
356    /// Delete a message.
357    pub async fn delete_message(&self, req: &DeleteMessageRequest) -> anyhow::Result<bool> {
358        self.client.post("deleteMessage", req).await
359    }
360
361    pub async fn remove_reply_keyboard(
362        &self,
363        chat_id: i64,
364        text: String,
365    ) -> anyhow::Result<Message> {
366        self.send_message(
367            &SendMessageRequest::new(chat_id, text)
368                .with_reply_markup(ReplyMarkup::reply_keyboard_remove()),
369        )
370        .await
371    }
372}