Skip to main content

rustigram_api/methods/
stories.rs

1use rustigram_types::story::{InputStoryContent, Story, StoryArea};
2
3use crate::client::BotClient;
4use crate::error::Result;
5use rustigram_types::message::{MessageEntity, ParseMode};
6use serde::Serialize;
7use std::future::{Future, IntoFuture};
8use std::pin::Pin;
9
10// ─── Helper macro ─────────────────────────────────────────────────────────────
11
12/// Generates an `IntoFuture` impl that calls `BotClient::post_json`.
13macro_rules! impl_into_future {
14    ($builder:ident, $return_ty:ty, $method:literal) => {
15        impl IntoFuture for $builder {
16            type Output = Result<$return_ty>;
17            type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
18
19            fn into_future(self) -> Self::IntoFuture {
20                Box::pin(async move { self.client.post_json($method, &self.params).await })
21            }
22        }
23    };
24}
25
26// ─── postStory ────────────────────────────────────────────────────────────────
27
28#[derive(Serialize)]
29struct PostStoryParams {
30    business_connection_id: String,
31    /// Content of the story.
32    ///
33    /// Construct with `serde_json::json!({"type":"photo","photo":"<file_id>"})` or
34    /// `serde_json::to_value(&your_input_story_content)`.
35    content: InputStoryContent,
36    /// Period in seconds after which the story moves to the archive.
37    ///
38    /// Must be one of `21600` (6h), `43200` (12h), `86400` (24h), or `172800` (48h).
39    active_period: u32,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    caption: Option<String>,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    parse_mode: Option<ParseMode>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    caption_entities: Option<Vec<MessageEntity>>,
46    /// Clickable areas to show on the story.
47    ///
48    #[serde(skip_serializing_if = "Option::is_none")]
49    areas: Option<Vec<StoryArea>>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    post_to_chat_page: Option<bool>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    protect_content: Option<bool>,
54}
55
56/// Builder for the [`postStory`](https://core.telegram.org/bots/api#poststory) method.
57///
58/// Posts a story on behalf of a managed business account.
59/// Requires the `can_manage_stories` business bot right.
60///
61/// `active_period` must be one of `21600`, `43200`, `86400`, or `172800` seconds.
62pub struct PostStory {
63    client: BotClient,
64    params: PostStoryParams,
65}
66
67impl PostStory {
68    pub(crate) fn new(
69        client: BotClient,
70        business_connection_id: impl Into<String>,
71        content: InputStoryContent,
72        active_period: u32,
73    ) -> Self {
74        Self {
75            client,
76            params: PostStoryParams {
77                business_connection_id: business_connection_id.into(),
78                content,
79                active_period,
80                caption: None,
81                parse_mode: None,
82                caption_entities: None,
83                areas: None,
84                post_to_chat_page: None,
85                protect_content: None,
86            },
87        }
88    }
89    /// Sets the story caption (0–2048 characters after entities parsing).
90    pub fn caption(mut self, c: impl Into<String>) -> Self {
91        self.params.caption = Some(c.into());
92        self
93    }
94    /// Sets the caption parse mode (`MarkdownV2`, `HTML`, or `Markdown`).
95    pub fn parse_mode(mut self, m: ParseMode) -> Self {
96        self.params.parse_mode = Some(m);
97        self
98    }
99    /// Sets special entities in the caption; alternative to `parse_mode`.
100    pub fn caption_entities(mut self, e: Vec<MessageEntity>) -> Self {
101        self.params.caption_entities = Some(e);
102        self
103    }
104    /// Sets clickable areas on the story.
105    ///
106    pub fn areas(mut self, a: Vec<StoryArea>) -> Self {
107        self.params.areas = Some(a);
108        self
109    }
110    /// Pass `true` to keep the story accessible after it expires.
111    pub fn post_to_chat_page(mut self, v: bool) -> Self {
112        self.params.post_to_chat_page = Some(v);
113        self
114    }
115    /// Pass `true` to protect the story content from forwarding and screenshotting.
116    pub fn protect_content(mut self, v: bool) -> Self {
117        self.params.protect_content = Some(v);
118        self
119    }
120}
121
122impl_into_future!(PostStory, Story, "postStory");
123
124// ─── repostStory ──────────────────────────────────────────────────────────────
125
126#[derive(Serialize)]
127struct RepostStoryParams {
128    business_connection_id: String,
129    from_chat_id: i64,
130    from_story_id: i64,
131    /// Period in seconds after which the story moves to the archive.
132    ///
133    /// Must be one of `21600` (6h), `43200` (12h), `86400` (24h), or `172800` (48h).
134    active_period: u32,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    post_to_chat_page: Option<bool>,
137    #[serde(skip_serializing_if = "Option::is_none")]
138    protect_content: Option<bool>,
139}
140
141/// Builder for the [`repostStory`](https://core.telegram.org/bots/api#repoststory) method.
142///
143/// Reposts a story from one managed business account to another.
144/// Both accounts must be managed by the same bot, and the story must have been
145/// posted by the bot. Requires the `can_manage_stories` business bot right on both accounts.
146///
147/// `active_period` must be one of `21600`, `43200`, `86400`, or `172800` seconds.
148pub struct RepostStory {
149    client: BotClient,
150    params: RepostStoryParams,
151}
152
153impl RepostStory {
154    pub(crate) fn new(
155        client: BotClient,
156        business_connection_id: impl Into<String>,
157        from_chat_id: i64,
158        from_story_id: i64,
159        active_period: u32,
160    ) -> Self {
161        Self {
162            client,
163            params: RepostStoryParams {
164                business_connection_id: business_connection_id.into(),
165                from_chat_id,
166                from_story_id,
167                active_period,
168                post_to_chat_page: None,
169                protect_content: None,
170            },
171        }
172    }
173    /// Pass `true` to keep the story accessible after it expires.
174    pub fn post_to_chat_page(mut self, v: bool) -> Self {
175        self.params.post_to_chat_page = Some(v);
176        self
177    }
178    /// Pass `true` to protect the story content from forwarding and screenshotting.
179    pub fn protect_content(mut self, v: bool) -> Self {
180        self.params.protect_content = Some(v);
181        self
182    }
183}
184
185impl_into_future!(RepostStory, Story, "repostStory");
186
187// ─── editStory ────────────────────────────────────────────────────────────────
188
189#[derive(Serialize)]
190struct EditStoryParams {
191    business_connection_id: String,
192    story_id: i64,
193    /// New content of the story.
194    ///
195    content: InputStoryContent,
196    #[serde(skip_serializing_if = "Option::is_none")]
197    caption: Option<String>,
198    #[serde(skip_serializing_if = "Option::is_none")]
199    parse_mode: Option<ParseMode>,
200    #[serde(skip_serializing_if = "Option::is_none")]
201    caption_entities: Option<Vec<MessageEntity>>,
202    /// Clickable areas to show on the story.
203    ///
204    #[serde(skip_serializing_if = "Option::is_none")]
205    areas: Option<Vec<StoryArea>>,
206}
207
208/// Builder for the [`editStory`](https://core.telegram.org/bots/api#editstory) method.
209///
210/// Edits a story previously posted by the bot on behalf of a managed business account.
211/// Requires the `can_manage_stories` business bot right.
212///
213pub struct EditStory {
214    client: BotClient,
215    params: EditStoryParams,
216}
217
218impl EditStory {
219    pub(crate) fn new(
220        client: BotClient,
221        business_connection_id: impl Into<String>,
222        story_id: i64,
223        content: InputStoryContent,
224    ) -> Self {
225        Self {
226            client,
227            params: EditStoryParams {
228                business_connection_id: business_connection_id.into(),
229                story_id,
230                content,
231                caption: None,
232                parse_mode: None,
233                caption_entities: None,
234                areas: None,
235            },
236        }
237    }
238    /// Sets the story caption (0–2048 characters after entities parsing).
239    pub fn caption(mut self, c: impl Into<String>) -> Self {
240        self.params.caption = Some(c.into());
241        self
242    }
243    /// Sets the caption parse mode (`MarkdownV2`, `HTML`, or `Markdown`).
244    pub fn parse_mode(mut self, m: ParseMode) -> Self {
245        self.params.parse_mode = Some(m);
246        self
247    }
248    /// Sets special entities in the caption; alternative to `parse_mode`.
249    pub fn caption_entities(mut self, e: Vec<MessageEntity>) -> Self {
250        self.params.caption_entities = Some(e);
251        self
252    }
253    /// Sets clickable areas on the story.
254    ///
255    pub fn areas(mut self, a: Vec<StoryArea>) -> Self {
256        self.params.areas = Some(a);
257        self
258    }
259}
260
261impl_into_future!(EditStory, Story, "editStory");
262
263// ─── deleteStory ──────────────────────────────────────────────────────────────
264
265#[derive(Serialize)]
266struct DeleteStoryParams {
267    business_connection_id: String,
268    story_id: i64,
269}
270
271/// Builder for the [`deleteStory`](https://core.telegram.org/bots/api#deletestory) method.
272///
273/// Deletes a story previously posted by the bot on behalf of a managed business account.
274/// Requires the `can_manage_stories` business bot right.
275pub struct DeleteStory {
276    client: BotClient,
277    params: DeleteStoryParams,
278}
279
280impl DeleteStory {
281    pub(crate) fn new(
282        client: BotClient,
283        business_connection_id: impl Into<String>,
284        story_id: i64,
285    ) -> Self {
286        Self {
287            client,
288            params: DeleteStoryParams {
289                business_connection_id: business_connection_id.into(),
290                story_id,
291            },
292        }
293    }
294}
295
296impl_into_future!(DeleteStory, bool, "deleteStory");