tgbot/types/definitions/media/
paid.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    api::{Form, Method, Payload},
5    types::{
6        ChatId,
7        InputPaidMediaGroup,
8        Integer,
9        Message,
10        ParseMode,
11        PhotoSize,
12        ReplyMarkup,
13        ReplyMarkupError,
14        ReplyParameters,
15        ReplyParametersError,
16        SuggestedPostParameters,
17        SuggestedPostParametersError,
18        TextEntities,
19        TextEntity,
20        TextEntityError,
21        User,
22        Video,
23    },
24};
25
26/// Contains information about a paid media purchase.
27#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
28pub struct PaidMediaPurchased {
29    /// User who purchased the media.
30    pub from: User,
31    /// Bot-specified paid media payload.
32    #[serde(rename = "paid_media_payload")]
33    pub payload: String,
34}
35
36impl PaidMediaPurchased {
37    /// Creates a new `PaidMediaPurchased`.
38    ///
39    /// # Arguments
40    ///
41    /// * `from` - User who purchased the media.
42    /// * `payload` - Bot-specified paid media payload.
43    pub fn new<T>(from: User, payload: T) -> Self
44    where
45        T: Into<String>,
46    {
47        Self {
48            from,
49            payload: payload.into(),
50        }
51    }
52}
53
54/// Describes the paid media added to a message.
55#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
56pub struct PaidMediaInfo {
57    /// The number of Telegram Stars that must be paid to buy access to the media.
58    pub star_count: Integer,
59    /// Information about the paid media.
60    pub paid_media: Vec<PaidMedia>,
61}
62
63impl PaidMediaInfo {
64    /// Creates a new `PaidMediaInfo`.
65    ///
66    /// # Arguments
67    ///
68    /// * `star_count` - The number of Telegram Stars that must be paid to buy access to the media.
69    /// * `paid_media` - Information about the paid media.
70    pub fn new<A, B>(star_count: Integer, paid_media: A) -> Self
71    where
72        A: IntoIterator<Item = B>,
73        B: Into<PaidMedia>,
74    {
75        Self {
76            star_count,
77            paid_media: paid_media.into_iter().map(Into::into).collect(),
78        }
79    }
80}
81
82/// Describes paid media.
83#[derive(Clone, Debug, derive_more::From, Deserialize, PartialEq, PartialOrd, Serialize)]
84#[serde(from = "RawPaidMedia", into = "RawPaidMedia")]
85#[allow(clippy::large_enum_variant)]
86pub enum PaidMedia {
87    /// The paid media is a photo.
88    Photo(Vec<PhotoSize>),
89    /// The paid media isn't available before the payment.
90    Preview(PaidMediaPreview),
91    /// The paid media is a video.
92    Video(Video),
93}
94
95/// The paid media isn't available before the payment.
96#[serde_with::skip_serializing_none]
97#[derive(Clone, Debug, Default, Deserialize, PartialEq, PartialOrd, Serialize)]
98pub struct PaidMediaPreview {
99    /// Duration of the media in seconds as defined by the sender.
100    pub duration: Option<Integer>,
101    /// Media height as defined by the sender.
102    pub height: Option<Integer>,
103    /// Media width as defined by the sender.
104    pub width: Option<Integer>,
105}
106
107impl PaidMediaPreview {
108    /// Sets a new duration.
109    ///
110    /// # Arguments
111    ///
112    /// * `value` - Duration of the media in seconds as defined by the sender.
113    pub fn with_duration(mut self, value: Integer) -> Self {
114        self.duration = Some(value);
115        self
116    }
117
118    /// Sets a new height.
119    ///
120    /// # Arguments
121    ///
122    /// * `value` - Height of the media in seconds as defined by the sender.
123    pub fn with_height(mut self, value: Integer) -> Self {
124        self.height = Some(value);
125        self
126    }
127
128    /// Sets a new width.
129    ///
130    /// # Arguments
131    ///
132    /// * `value` - Width of the media in seconds as defined by the sender.
133    pub fn with_width(mut self, value: Integer) -> Self {
134        self.width = Some(value);
135        self
136    }
137}
138
139#[serde_with::skip_serializing_none]
140#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
141#[serde(rename_all = "snake_case", tag = "type")]
142#[allow(clippy::large_enum_variant)]
143enum RawPaidMedia {
144    Photo {
145        photo: Vec<PhotoSize>,
146    },
147    Preview {
148        duration: Option<Integer>,
149        height: Option<Integer>,
150        width: Option<Integer>,
151    },
152    Video {
153        video: Video,
154    },
155}
156
157#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
158struct RawPaidMediaPhoto {
159    photo: Vec<PhotoSize>,
160}
161
162#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
163struct RawPaidMediaVideo {
164    video: Video,
165}
166
167impl From<RawPaidMedia> for PaidMedia {
168    fn from(value: RawPaidMedia) -> Self {
169        match value {
170            RawPaidMedia::Photo { photo } => Self::Photo(photo),
171            RawPaidMedia::Preview {
172                duration,
173                height,
174                width,
175            } => Self::Preview(PaidMediaPreview {
176                duration,
177                height,
178                width,
179            }),
180            RawPaidMedia::Video { video } => Self::Video(video),
181        }
182    }
183}
184
185impl From<PaidMedia> for RawPaidMedia {
186    fn from(value: PaidMedia) -> Self {
187        match value {
188            PaidMedia::Photo(photo) => Self::Photo { photo },
189            PaidMedia::Preview(PaidMediaPreview {
190                duration,
191                height,
192                width,
193            }) => Self::Preview {
194                duration,
195                height,
196                width,
197            },
198            PaidMedia::Video(video) => Self::Video { video },
199        }
200    }
201}
202
203/// Send paid media to channel chats.
204#[derive(Debug)]
205pub struct SendPaidMedia {
206    form: Form,
207}
208
209impl SendPaidMedia {
210    /// Creates a new `SendPaidMedia`.
211    ///
212    /// # Arguments
213    ///
214    /// * `chat_id` - Unique identifier for the target chat.
215    /// * `media` - An array describing the media to be sent
216    /// * `star_count` - The number of Telegram Stars that must be paid to buy access to the media; 1-10000.
217    pub fn new<T>(chat_id: T, media: InputPaidMediaGroup, star_count: Integer) -> Self
218    where
219        T: Into<ChatId>,
220    {
221        let mut form: Form = media.into();
222        form.insert_field("chat_id", chat_id.into());
223        form.insert_field("star_count", star_count);
224        Self { form }
225    }
226
227    /// Sets a new value for the `allow_paid_broadcast` flag.
228    ///
229    /// # Arguments
230    ///
231    /// * `value` - Whether to allow up to 1000 messages per second, ignoring broadcasting limits
232    ///   for a fee of 0.1 Telegram Stars per message.
233    ///   The relevant Stars will be withdrawn from the bot's balance.
234    pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
235        self.form.insert_field("allow_paid_broadcast", value);
236        self
237    }
238
239    /// Sets a new business connection ID.
240    ///
241    /// # Arguments
242    ///
243    /// * `value` - Unique identifier of the business connection
244    ///   on behalf of which the message will be sent.
245    pub fn with_business_connection_id<T>(mut self, value: T) -> Self
246    where
247        T: Into<String>,
248    {
249        self.form.insert_field("business_connection_id", value.into());
250        self
251    }
252
253    /// Sets a new caption.
254    ///
255    /// # Arguments
256    ///
257    /// `value` - Media caption, 0-1024 characters after entities parsing.
258    pub fn with_caption<T>(mut self, value: T) -> Self
259    where
260        T: Into<String>,
261    {
262        self.form.insert_field("caption", value.into());
263        self
264    }
265
266    /// Sets a new list of caption entities.
267    ///
268    /// # Arguments
269    ///
270    /// `value` - A list of special entities that appear in the caption, which can be specified instead of parse_mode.
271    pub fn with_caption_entities<T>(mut self, value: T) -> Result<Self, TextEntityError>
272    where
273        T: IntoIterator<Item = TextEntity>,
274    {
275        let value = value.into_iter().collect::<TextEntities>().serialize()?;
276        self.form.insert_field("caption_entities", value);
277        self.form.remove_field("parse_mode");
278        Ok(self)
279    }
280
281    /// Sets a new direct messages topic ID
282    ///
283    /// * `value` - Identifier of the direct messages topic to which the message will be sent.
284    ///
285    /// Required if the message is sent to a direct messages chat.
286    pub fn with_direct_messages_topic_id(mut self, value: Integer) -> Self {
287        self.form.insert_field("direct_messages_topic_id", value);
288        self
289    }
290
291    /// Sets a new value for the `disable_notification` flag.
292    ///
293    /// # Arguments
294    ///
295    /// `value` - Whether to send the message silently.
296    ///
297    /// Users will receive a notification with no sound.
298    pub fn with_disable_notification(mut self, value: bool) -> Self {
299        self.form.insert_field("disable_notification", value);
300        self
301    }
302
303    /// Sets a new parse mode.
304    ///
305    /// # Arguments
306    ///
307    /// `value` - Mode for parsing entities in the media caption.
308    pub fn with_parse_mode(mut self, value: ParseMode) -> Self {
309        self.form.insert_field("parse_mode", value);
310        self.form.remove_field("caption_entities");
311        self
312    }
313
314    /// Sets a new payload.
315    ///
316    /// # Arguments
317    ///
318    /// * `value` - Bot-defined paid media payload;
319    ///   0-128 bytes;
320    ///   This will not be displayed to the user, use it for your internal processes.
321    pub fn with_payload<T>(mut self, value: T) -> Self
322    where
323        T: Into<String>,
324    {
325        self.form.insert_field("payload", value.into());
326        self
327    }
328
329    /// Sets a new value for the `protect_content` flag.
330    ///
331    /// # Arguments
332    ///
333    /// `value` - Whether to protect the contents of the sent message from forwarding and saving.
334    pub fn with_protect_content(mut self, value: bool) -> Self {
335        self.form.insert_field("protect_content", value);
336        self
337    }
338
339    /// Sets new reply parameters.
340    ///
341    /// # Arguments
342    ///
343    /// `value` - Description of the message to reply to.
344    pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Result<Self, ReplyParametersError> {
345        let value = value.serialize()?;
346        self.form.insert_field("reply_parameters", value);
347        Ok(self)
348    }
349
350    /// Sets a new reply markup.
351    ///
352    /// # Arguments
353    ///
354    /// `value` - Additional interface options.
355    pub fn with_reply_markup<T>(mut self, value: T) -> Result<Self, ReplyMarkupError>
356    where
357        T: Into<ReplyMarkup>,
358    {
359        let value = value.into().serialize()?;
360        self.form.insert_field("reply_markup", value);
361        Ok(self)
362    }
363
364    /// Sets a new value for the `show_caption_above_media` flag.
365    ///
366    /// # Arguments
367    ///
368    /// `value` - Whether the caption must be shown above the message media.
369    pub fn with_show_caption_above_media(mut self, value: bool) -> Self {
370        self.form.insert_field("show_caption_above_media", value);
371        self
372    }
373
374    /// Sets a new suggested post parameters.
375    ///
376    /// # Arguments
377    ///
378    /// * `value` - An object containing the parameters of the suggested post to send.
379    ///
380    /// For direct messages chats only.
381    ///
382    /// If the message is sent as a reply to another suggested post, then that suggested post is automatically declined.
383    pub fn with_suggested_post_parameters(
384        mut self,
385        value: &SuggestedPostParameters,
386    ) -> Result<Self, SuggestedPostParametersError> {
387        let value = serde_json::to_string(value).map_err(SuggestedPostParametersError::Serialize)?;
388        self.form.insert_field("suggested_post_parameters", value);
389        Ok(self)
390    }
391}
392
393impl Method for SendPaidMedia {
394    type Response = Message;
395
396    fn into_payload(self) -> Payload {
397        Payload::form("sendPaidMedia", self.form)
398    }
399}