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