Skip to main content

rustigram_api/methods/
editing.rs

1use std::future::{Future, IntoFuture};
2use std::pin::Pin;
3
4use serde::Serialize;
5
6use rustigram_types::checklist::InputChecklist;
7use rustigram_types::keyboard::InlineKeyboardMarkup;
8use rustigram_types::message::{LinkPreviewOptions, Message, MessageEntity, ParseMode};
9use rustigram_types::user::ChatId;
10
11use crate::client::BotClient;
12use crate::error::Result;
13
14// ─── Helper macro ─────────────────────────────────────────────────────────────
15
16/// Generates an `IntoFuture` impl that calls `BotClient::post_json`.
17macro_rules! impl_into_future {
18    ($builder:ident, $return_ty:ty, $method:literal) => {
19        impl IntoFuture for $builder {
20            type Output = Result<$return_ty>;
21            type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
22
23            fn into_future(self) -> Self::IntoFuture {
24                Box::pin(async move { self.client.post_json($method, &self.params).await })
25            }
26        }
27    };
28}
29
30/// Target identifier for inline message edits.
31#[derive(Serialize)]
32#[serde(untagged)]
33/// Identifies the target message — either a chat message or an inline message.
34pub enum EditTarget {
35    /// Targets a regular chat message.
36    Chat {
37        /// The chat containing the message.
38        chat_id: ChatId,
39        /// Identifier of the message to edit.
40        message_id: i64,
41    },
42    /// Targets an inline message sent via inline mode.
43    Inline {
44        /// Identifier of the inline message.
45        inline_message_id: String,
46    },
47}
48
49// ─── editMessageText ──────────────────────────────────────────────────────────
50
51#[derive(Serialize)]
52struct EditMessageTextParams {
53    #[serde(flatten)]
54    target: EditTarget,
55    text: String,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    business_connection_id: Option<String>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    parse_mode: Option<ParseMode>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    entities: Option<Vec<MessageEntity>>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    link_preview_options: Option<LinkPreviewOptions>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    reply_markup: Option<InlineKeyboardMarkup>,
66}
67
68/// Builder for the [`editMessageText`](https://core.telegram.org/bots/api#editmessagetext) method.
69pub struct EditMessageText {
70    client: BotClient,
71    params: EditMessageTextParams,
72}
73
74impl EditMessageText {
75    pub(crate) fn in_chat(
76        client: BotClient,
77        chat_id: impl Into<ChatId>,
78        message_id: i64,
79        text: impl Into<String>,
80    ) -> Self {
81        Self {
82            client,
83            params: EditMessageTextParams {
84                target: EditTarget::Chat {
85                    chat_id: chat_id.into(),
86                    message_id,
87                },
88                text: text.into(),
89                business_connection_id: None,
90                parse_mode: None,
91                entities: None,
92                link_preview_options: None,
93                reply_markup: None,
94            },
95        }
96    }
97    pub(crate) fn inline(
98        client: BotClient,
99        inline_message_id: impl Into<String>,
100        text: impl Into<String>,
101    ) -> Self {
102        Self {
103            client,
104            params: EditMessageTextParams {
105                target: EditTarget::Inline {
106                    inline_message_id: inline_message_id.into(),
107                },
108                text: text.into(),
109                business_connection_id: None,
110                parse_mode: None,
111                entities: None,
112                link_preview_options: None,
113                reply_markup: None,
114            },
115        }
116    }
117    /// Sets the text parse mode (`MarkdownV2`, `HTML`, or `Markdown`).
118    pub fn parse_mode(mut self, m: ParseMode) -> Self {
119        self.params.parse_mode = Some(m);
120        self
121    }
122    /// Sets custom message entities instead of using a parse mode.
123    pub fn entities(mut self, e: Vec<MessageEntity>) -> Self {
124        self.params.entities = Some(e);
125        self
126    }
127    /// Configures link preview options for the edited message.
128    pub fn link_preview_options(mut self, o: LinkPreviewOptions) -> Self {
129        self.params.link_preview_options = Some(o);
130        self
131    }
132    /// Attaches a reply markup (inline keyboard, reply keyboard, etc.).
133    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
134        self.params.reply_markup = Some(m);
135        self
136    }
137}
138
139impl_into_future!(EditMessageText, Message, "editMessageText");
140
141// ─── editMessageCaption ───────────────────────────────────────────────────────
142
143#[derive(Serialize)]
144struct EditMessageCaptionParams {
145    #[serde(flatten)]
146    target: EditTarget,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    business_connection_id: Option<String>,
149    #[serde(skip_serializing_if = "Option::is_none")]
150    caption: Option<String>,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    parse_mode: Option<ParseMode>,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    caption_entities: Option<Vec<MessageEntity>>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    show_caption_above_media: Option<bool>,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    reply_markup: Option<InlineKeyboardMarkup>,
159}
160
161/// Builder for the [`editMessageCaption`](https://core.telegram.org/bots/api#editmessagecaption) method.
162pub struct EditMessageCaption {
163    client: BotClient,
164    params: EditMessageCaptionParams,
165}
166
167impl EditMessageCaption {
168    pub(crate) fn in_chat(client: BotClient, chat_id: impl Into<ChatId>, message_id: i64) -> Self {
169        Self {
170            client,
171            params: EditMessageCaptionParams {
172                target: EditTarget::Chat {
173                    chat_id: chat_id.into(),
174                    message_id,
175                },
176                business_connection_id: None,
177                caption: None,
178                parse_mode: None,
179                caption_entities: None,
180                show_caption_above_media: None,
181                reply_markup: None,
182            },
183        }
184    }
185    pub(crate) fn inline(client: BotClient, inline_message_id: impl Into<String>) -> Self {
186        Self {
187            client,
188            params: EditMessageCaptionParams {
189                target: EditTarget::Inline {
190                    inline_message_id: inline_message_id.into(),
191                },
192                business_connection_id: None,
193                caption: None,
194                parse_mode: None,
195                caption_entities: None,
196                show_caption_above_media: None,
197                reply_markup: None,
198            },
199        }
200    }
201    /// Sets the new caption text (0–1024 characters).
202    pub fn caption(mut self, c: impl Into<String>) -> Self {
203        self.params.caption = Some(c.into());
204        self
205    }
206    /// Sets the caption parse mode (`MarkdownV2`, `HTML`, or `Markdown`).
207    pub fn parse_mode(mut self, m: ParseMode) -> Self {
208        self.params.parse_mode = Some(m);
209        self
210    }
211    /// Shows the caption above the media instead of below it.
212    pub fn show_caption_above_media(mut self, v: bool) -> Self {
213        self.params.show_caption_above_media = Some(v);
214        self
215    }
216    /// Attaches a reply markup (inline keyboard, reply keyboard, etc.).
217    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
218        self.params.reply_markup = Some(m);
219        self
220    }
221}
222
223impl_into_future!(EditMessageCaption, Message, "editMessageCaption");
224
225// ─── editMessageMedia ─────────────────────────────────────────────────────────
226
227#[derive(Serialize)]
228struct EditMessageMediaParams {
229    #[serde(flatten)]
230    target: EditTarget,
231    /// The new media content.
232    ///
233    /// Uses `serde_json::Value` until the `InputMedia` enum is defined in
234    /// Priority 4. Pass the result of `serde_json::to_value(&your_input_media)`.
235    media: serde_json::Value,
236    #[serde(skip_serializing_if = "Option::is_none")]
237    business_connection_id: Option<String>,
238    #[serde(skip_serializing_if = "Option::is_none")]
239    reply_markup: Option<InlineKeyboardMarkup>,
240}
241
242/// Builder for the [`editMessageMedia`](https://core.telegram.org/bots/api#editmessagemedia) method.
243///
244/// Edits the media content of a message (animation, audio, document, photo, or video).
245///
246/// The `media` parameter accepts `serde_json::Value` until the `InputMedia` enum is
247/// defined in Priority 4. Construct it with `serde_json::json!({...})` or
248/// `serde_json::to_value(&input_media)`.
249pub struct EditMessageMedia {
250    client: BotClient,
251    params: EditMessageMediaParams,
252}
253
254impl EditMessageMedia {
255    pub(crate) fn in_chat(
256        client: BotClient,
257        chat_id: impl Into<ChatId>,
258        message_id: i64,
259        media: serde_json::Value,
260    ) -> Self {
261        Self {
262            client,
263            params: EditMessageMediaParams {
264                target: EditTarget::Chat {
265                    chat_id: chat_id.into(),
266                    message_id,
267                },
268                media,
269                business_connection_id: None,
270                reply_markup: None,
271            },
272        }
273    }
274    pub(crate) fn inline(
275        client: BotClient,
276        inline_message_id: impl Into<String>,
277        media: serde_json::Value,
278    ) -> Self {
279        Self {
280            client,
281            params: EditMessageMediaParams {
282                target: EditTarget::Inline {
283                    inline_message_id: inline_message_id.into(),
284                },
285                media,
286                business_connection_id: None,
287                reply_markup: None,
288            },
289        }
290    }
291    /// Business connection ID for editing a message sent on behalf of a business account.
292    pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
293        self.params.business_connection_id = Some(id.into());
294        self
295    }
296    /// Attaches a new inline keyboard to the message.
297    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
298        self.params.reply_markup = Some(m);
299        self
300    }
301}
302
303impl_into_future!(EditMessageMedia, Message, "editMessageMedia");
304
305// ─── editMessageReplyMarkup ───────────────────────────────────────────────────
306
307#[derive(Serialize)]
308struct EditMessageReplyMarkupParams {
309    #[serde(flatten)]
310    target: EditTarget,
311    #[serde(skip_serializing_if = "Option::is_none")]
312    business_connection_id: Option<String>,
313    #[serde(skip_serializing_if = "Option::is_none")]
314    reply_markup: Option<InlineKeyboardMarkup>,
315}
316
317/// Builder for the [`editMessageReplyMarkup`](https://core.telegram.org/bots/api#editmessagereplymarkup) method.
318pub struct EditMessageReplyMarkup {
319    client: BotClient,
320    params: EditMessageReplyMarkupParams,
321}
322
323impl EditMessageReplyMarkup {
324    pub(crate) fn in_chat(client: BotClient, chat_id: impl Into<ChatId>, message_id: i64) -> Self {
325        Self {
326            client,
327            params: EditMessageReplyMarkupParams {
328                target: EditTarget::Chat {
329                    chat_id: chat_id.into(),
330                    message_id,
331                },
332                business_connection_id: None,
333                reply_markup: None,
334            },
335        }
336    }
337    pub(crate) fn inline(client: BotClient, inline_message_id: impl Into<String>) -> Self {
338        Self {
339            client,
340            params: EditMessageReplyMarkupParams {
341                target: EditTarget::Inline {
342                    inline_message_id: inline_message_id.into(),
343                },
344                business_connection_id: None,
345                reply_markup: None,
346            },
347        }
348    }
349    /// Attaches a reply markup (inline keyboard, reply keyboard, etc.).
350    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
351        self.params.reply_markup = Some(m);
352        self
353    }
354    /// Removes the inline keyboard from the message.
355    pub fn remove_markup(mut self) -> Self {
356        self.params.reply_markup = None;
357        self
358    }
359}
360
361impl_into_future!(EditMessageReplyMarkup, Message, "editMessageReplyMarkup");
362
363// ─── editMessageChecklist ─────────────────────────────────────────────────────
364
365#[derive(Serialize)]
366struct EditMessageChecklistParams {
367    business_connection_id: String,
368    chat_id: i64,
369    message_id: i64,
370    checklist: InputChecklist,
371    #[serde(skip_serializing_if = "Option::is_none")]
372    reply_markup: Option<InlineKeyboardMarkup>,
373}
374
375/// Builder for the [`editMessageChecklist`](https://core.telegram.org/bots/api#editmessagechecklist) method.
376///
377/// Business bots only — edits a checklist message sent on behalf of a connected
378/// business account. Requires the `can_reply` business bot right.
379pub struct EditMessageChecklist {
380    client: BotClient,
381    params: EditMessageChecklistParams,
382}
383
384impl EditMessageChecklist {
385    pub(crate) fn new(
386        client: BotClient,
387        business_connection_id: impl Into<String>,
388        chat_id: i64,
389        message_id: i64,
390        checklist: InputChecklist,
391    ) -> Self {
392        Self {
393            client,
394            params: EditMessageChecklistParams {
395                business_connection_id: business_connection_id.into(),
396                chat_id,
397                message_id,
398                checklist,
399                reply_markup: None,
400            },
401        }
402    }
403    /// Attaches a new inline keyboard to the message.
404    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
405        self.params.reply_markup = Some(m);
406        self
407    }
408}
409
410impl_into_future!(EditMessageChecklist, Message, "editMessageChecklist");
411
412// ─── approveSuggestedPost ─────────────────────────────────────────────────────
413
414#[derive(Serialize)]
415struct ApproveSuggestedPostParams {
416    chat_id: i64,
417    message_id: i64,
418    #[serde(skip_serializing_if = "Option::is_none")]
419    send_date: Option<i64>,
420}
421
422/// Builder for the [`approveSuggestedPost`](https://core.telegram.org/bots/api#approvesuggestedpost) method.
423///
424/// Approves a suggested post in a direct messages chat.
425/// Requires the `can_post_messages` administrator right in the corresponding channel.
426pub struct ApproveSuggestedPost {
427    client: BotClient,
428    params: ApproveSuggestedPostParams,
429}
430
431impl ApproveSuggestedPost {
432    pub(crate) fn new(client: BotClient, chat_id: i64, message_id: i64) -> Self {
433        Self {
434            client,
435            params: ApproveSuggestedPostParams {
436                chat_id,
437                message_id,
438                send_date: None,
439            },
440        }
441    }
442    /// Unix timestamp when the post will be published (not more than 30 days in the future).
443    ///
444    /// Omit if the send date was already specified when the post was suggested.
445    pub fn send_date(mut self, ts: i64) -> Self {
446        self.params.send_date = Some(ts);
447        self
448    }
449}
450
451impl_into_future!(ApproveSuggestedPost, bool, "approveSuggestedPost");
452
453// ─── declineSuggestedPost ─────────────────────────────────────────────────────
454
455#[derive(Serialize)]
456struct DeclineSuggestedPostParams {
457    chat_id: i64,
458    message_id: i64,
459    #[serde(skip_serializing_if = "Option::is_none")]
460    comment: Option<String>,
461}
462
463/// Builder for the [`declineSuggestedPost`](https://core.telegram.org/bots/api#declinesuggestedpost) method.
464///
465/// Declines a suggested post in a direct messages chat.
466/// Requires the `can_manage_direct_messages` administrator right in the corresponding channel.
467pub struct DeclineSuggestedPost {
468    client: BotClient,
469    params: DeclineSuggestedPostParams,
470}
471
472impl DeclineSuggestedPost {
473    pub(crate) fn new(client: BotClient, chat_id: i64, message_id: i64) -> Self {
474        Self {
475            client,
476            params: DeclineSuggestedPostParams {
477                chat_id,
478                message_id,
479                comment: None,
480            },
481        }
482    }
483    /// Optional comment for the creator of the suggested post (0–128 characters).
484    pub fn comment(mut self, c: impl Into<String>) -> Self {
485        self.params.comment = Some(c.into());
486        self
487    }
488}
489
490impl_into_future!(DeclineSuggestedPost, bool, "declineSuggestedPost");
491
492// ─── editMessageLiveLocation ──────────────────────────────────────────────────
493
494#[derive(Serialize)]
495struct EditMessageLiveLocationParams {
496    #[serde(flatten)]
497    target: EditTarget,
498    latitude: f64,
499    longitude: f64,
500    #[serde(skip_serializing_if = "Option::is_none")]
501    live_period: Option<u32>,
502    #[serde(skip_serializing_if = "Option::is_none")]
503    horizontal_accuracy: Option<f64>,
504    #[serde(skip_serializing_if = "Option::is_none")]
505    heading: Option<u16>,
506    #[serde(skip_serializing_if = "Option::is_none")]
507    proximity_alert_radius: Option<u32>,
508    #[serde(skip_serializing_if = "Option::is_none")]
509    reply_markup: Option<InlineKeyboardMarkup>,
510}
511
512/// Builder for the [`editMessageLiveLocation`](https://core.telegram.org/bots/api#editmessagelivelocation) method.
513pub struct EditMessageLiveLocation {
514    client: BotClient,
515    params: EditMessageLiveLocationParams,
516}
517
518impl EditMessageLiveLocation {
519    pub(crate) fn in_chat(
520        client: BotClient,
521        chat_id: impl Into<ChatId>,
522        message_id: i64,
523        latitude: f64,
524        longitude: f64,
525    ) -> Self {
526        Self {
527            client,
528            params: EditMessageLiveLocationParams {
529                target: EditTarget::Chat {
530                    chat_id: chat_id.into(),
531                    message_id,
532                },
533                latitude,
534                longitude,
535                live_period: None,
536                horizontal_accuracy: None,
537                heading: None,
538                proximity_alert_radius: None,
539                reply_markup: None,
540            },
541        }
542    }
543    pub(crate) fn inline(
544        client: BotClient,
545        inline_message_id: impl Into<String>,
546        latitude: f64,
547        longitude: f64,
548    ) -> Self {
549        Self {
550            client,
551            params: EditMessageLiveLocationParams {
552                target: EditTarget::Inline {
553                    inline_message_id: inline_message_id.into(),
554                },
555                latitude,
556                longitude,
557                live_period: None,
558                horizontal_accuracy: None,
559                heading: None,
560                proximity_alert_radius: None,
561                reply_markup: None,
562            },
563        }
564    }
565    /// Sets how long the location stays live, in seconds (60–86400).
566    pub fn live_period(mut self, v: u32) -> Self {
567        self.params.live_period = Some(v);
568        self
569    }
570    /// Sets the direction of movement in degrees (1–360).
571    pub fn heading(mut self, v: u16) -> Self {
572        self.params.heading = Some(v);
573        self
574    }
575    /// Attaches a reply markup (inline keyboard, reply keyboard, etc.).
576    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
577        self.params.reply_markup = Some(m);
578        self
579    }
580}
581
582impl_into_future!(EditMessageLiveLocation, Message, "editMessageLiveLocation");
583
584// ─── stopMessageLiveLocation ──────────────────────────────────────────────────
585
586#[derive(Serialize)]
587struct StopMessageLiveLocationParams {
588    #[serde(flatten)]
589    target: EditTarget,
590    #[serde(skip_serializing_if = "Option::is_none")]
591    reply_markup: Option<InlineKeyboardMarkup>,
592}
593
594/// Builder for the [`stopMessageLiveLocation`](https://core.telegram.org/bots/api#stopmessagelivelocation) method.
595pub struct StopMessageLiveLocation {
596    client: BotClient,
597    params: StopMessageLiveLocationParams,
598}
599
600impl StopMessageLiveLocation {
601    pub(crate) fn in_chat(client: BotClient, chat_id: impl Into<ChatId>, message_id: i64) -> Self {
602        Self {
603            client,
604            params: StopMessageLiveLocationParams {
605                target: EditTarget::Chat {
606                    chat_id: chat_id.into(),
607                    message_id,
608                },
609                reply_markup: None,
610            },
611        }
612    }
613    pub(crate) fn inline(client: BotClient, inline_message_id: impl Into<String>) -> Self {
614        Self {
615            client,
616            params: StopMessageLiveLocationParams {
617                target: EditTarget::Inline {
618                    inline_message_id: inline_message_id.into(),
619                },
620                reply_markup: None,
621            },
622        }
623    }
624    /// Attaches a reply markup (inline keyboard, reply keyboard, etc.).
625    pub fn reply_markup(mut self, m: InlineKeyboardMarkup) -> Self {
626        self.params.reply_markup = Some(m);
627        self
628    }
629}
630
631impl_into_future!(StopMessageLiveLocation, Message, "stopMessageLiveLocation");