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