Skip to main content

rustigram_types/
poll.rs

1use serde::{Deserialize, Serialize};
2
3use crate::chat::{Chat, Location, Venue};
4use crate::file::{Animation, Audio, Document, LivePhoto, PhotoSize, Video};
5use crate::message::MessageEntity;
6use crate::sticker::Sticker;
7use crate::user::User;
8
9/// Media attached to a poll description, quiz explanation, or poll option.
10///
11/// At most one of the optional fields will be `Some`.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct PollMedia {
14    /// Animation media.
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub animation: Option<Animation>,
17    /// Audio file; currently not receivable in poll options.
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub audio: Option<Audio>,
20    /// General file; currently not receivable in poll options.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub document: Option<Document>,
23    /// Live photo media.
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub live_photo: Option<LivePhoto>,
26    /// Location media.
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub location: Option<Location>,
29    /// Photo media (available sizes).
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub photo: Option<Vec<PhotoSize>>,
32    /// Sticker media; only for poll options.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub sticker: Option<Sticker>,
35    /// Venue media.
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub venue: Option<Venue>,
38    /// Video media.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub video: Option<Video>,
41    /// HTTP link attached to the poll option.
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub link: Option<crate::chat::Link>,
44}
45
46/// Media content for a poll description or quiz explanation to be sent.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(tag = "type", rename_all = "snake_case")]
49pub enum InputPollMedia {
50    /// An animation.
51    Animation(crate::file::InputMediaAnimation),
52    /// An audio file.
53    Audio(crate::file::InputMediaAudio),
54    /// A document.
55    Document(crate::file::InputMediaDocument),
56    /// A live photo.
57    LivePhoto(crate::file::InputMediaLivePhoto),
58    /// A location.
59    Location(crate::file::InputMediaLocation),
60    /// A photo.
61    Photo(crate::file::InputMediaPhoto),
62    /// A venue.
63    Venue(crate::file::InputMediaVenue),
64    /// A video.
65    Video(crate::file::InputMediaVideo),
66}
67
68/// Media content for a poll option to be sent.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(tag = "type", rename_all = "snake_case")]
71pub enum InputPollOptionMedia {
72    /// An animation.
73    Animation(crate::file::InputMediaAnimation),
74    /// A live photo.
75    LivePhoto(crate::file::InputMediaLivePhoto),
76    /// An HTTP link.
77    Link(crate::chat::InputMediaLink),
78    /// A location.
79    Location(crate::file::InputMediaLocation),
80    /// A photo.
81    Photo(crate::file::InputMediaPhoto),
82    /// A sticker.
83    Sticker(crate::file::InputMediaSticker),
84    /// A venue.
85    Venue(crate::file::InputMediaVenue),
86    /// A video.
87    Video(crate::file::InputMediaVideo),
88}
89
90/// A native Telegram poll or quiz.
91///
92/// Polls are either `regular` (users vote freely) or `quiz` (one or more correct
93/// answers). Returned inside [`Message`](crate::message::Message) when a poll is
94/// sent or forwarded.
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct Poll {
97    /// Unique poll identifier.
98    pub id: String,
99    /// Poll question (1–300 characters).
100    pub question: String,
101    /// Special entities in the question.
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub question_entities: Option<Vec<MessageEntity>>,
104    /// List of answer options.
105    pub options: Vec<PollOption>,
106    /// Total number of users who voted.
107    pub total_voter_count: u32,
108    /// `true` if the poll is closed.
109    pub is_closed: bool,
110    /// `true` if the poll is anonymous.
111    pub is_anonymous: bool,
112    /// Poll type.
113    #[serde(rename = "type")]
114    pub kind: PollType,
115    /// `true` if the poll allows multiple answers.
116    pub allows_multiple_answers: bool,
117    /// `true` if voters can change their chosen answer options.
118    pub allows_revoting: bool,
119    /// Zero-based indices of the correct answers (quiz polls only).
120    ///
121    /// Bot API 9.6 changed this from a single `u8` to an array to support
122    /// quizzes with multiple correct answers.
123    #[serde(skip_serializing_if = "Option::is_none")]
124    pub correct_option_ids: Option<Vec<u8>>,
125    /// Explanation shown after a quiz is answered.
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub explanation: Option<String>,
128    /// Special entities in the explanation.
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub explanation_entities: Option<Vec<MessageEntity>>,
131    /// Duration in seconds the poll stays active after creation.
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub open_period: Option<u32>,
134    /// Unix timestamp when the poll closes automatically.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub close_date: Option<i64>,
137    /// Description of the poll; present only inside `Message` objects.
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub description: Option<String>,
140    /// Special entities in the description.
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub description_entities: Option<Vec<MessageEntity>>,
143    /// Media added to the poll description; present only inside `Message` objects.
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub media: Option<PollMedia>,
146    /// Media added to the quiz explanation.
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub explanation_media: Option<PollMedia>,
149    /// `true` if voting is limited to users who have been members of the chat for more than 24 hours.
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub members_only: Option<bool>,
152    /// Two-letter ISO 3166-1 alpha-2 country codes indicating the countries from which users can vote.
153    /// If absent, users from any country can participate.
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub country_codes: Option<Vec<String>>,
156}
157
158/// One option in a [`Poll`].
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct PollOption {
161    /// Persistent identifier of this option, stable across poll edits.
162    pub persistent_id: String,
163    /// Option text (1–100 characters).
164    pub text: String,
165    /// Special entities in the option text.
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub text_entities: Option<Vec<MessageEntity>>,
168    /// Number of users who voted for this option.
169    pub voter_count: u32,
170    /// The user who added this option, if applicable.
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub added_by_user: Option<User>,
173    /// The chat that added this option, if applicable.
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub added_by_chat: Option<Chat>,
176    /// Unix timestamp when this option was added; absent if it existed at poll creation.
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub addition_date: Option<i64>,
179    /// Media added to this poll option.
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub media: Option<PollMedia>,
182}
183
184/// An option to include when creating a poll with `sendPoll`.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct InputPollOption {
187    /// Option text (1–100 characters).
188    pub text: String,
189    /// Parse mode for the option text.
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub text_parse_mode: Option<crate::message::ParseMode>,
192    /// Special entities in the option text; alternative to `text_parse_mode`.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub text_entities: Option<Vec<MessageEntity>>,
195    /// Media added to this poll option.
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub media: Option<InputPollOptionMedia>,
198}
199
200impl InputPollOption {
201    /// Creates a new `InputPollOption` with the given text.
202    pub fn new(text: impl Into<String>) -> Self {
203        Self {
204            text: text.into(),
205            text_parse_mode: None,
206            text_entities: None,
207            media: None,
208        }
209    }
210}
211
212/// The type of a poll.
213#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
214#[serde(rename_all = "snake_case")]
215pub enum PollType {
216    /// A regular poll where users can vote freely.
217    Regular,
218    /// A quiz with one or more correct answers.
219    Quiz,
220}
221
222/// An answer submitted by a user in a non-anonymous poll.
223#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct PollAnswer {
225    /// Unique poll identifier.
226    pub poll_id: String,
227    /// The chat that changed the answer (for anonymous polls in groups).
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub voter_chat: Option<Chat>,
230    /// The user who changed the answer (for non-anonymous polls).
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub user: Option<User>,
233    /// Zero-based indices of the chosen options; empty if the vote was retracted.
234    pub option_ids: Vec<u8>,
235    /// Persistent identifiers of the chosen options; empty if the vote was retracted.
236    pub option_persistent_ids: Vec<String>,
237}
238
239/// Service message: a new option was added to a poll.
240#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct PollOptionAdded {
242    /// Message containing the poll to which the option was added.
243    ///
244    /// Uses `serde_json::Value` because the API returns a `MaybeInaccessibleMessage`
245    /// union type here. Will not contain `reply_to_message`.
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub poll_message: Option<serde_json::Value>,
248    /// Persistent identifier of the added option.
249    pub option_persistent_id: String,
250    /// Text of the added option.
251    pub option_text: String,
252    /// Special entities in the option text.
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub option_text_entities: Option<Vec<MessageEntity>>,
255}
256
257/// Service message: an option was deleted from a poll.
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct PollOptionDeleted {
260    /// Message containing the poll from which the option was deleted.
261    ///
262    /// Uses `serde_json::Value` because the API returns a `MaybeInaccessibleMessage`
263    /// union type here. Will not contain `reply_to_message`.
264    #[serde(skip_serializing_if = "Option::is_none")]
265    pub poll_message: Option<serde_json::Value>,
266    /// Persistent identifier of the deleted option.
267    pub option_persistent_id: String,
268    /// Text of the deleted option.
269    pub option_text: String,
270    /// Special entities in the option text.
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub option_text_entities: Option<Vec<MessageEntity>>,
273}