Skip to main content

rustigram_api/methods/
stories.rs

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