1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2
3use crate::{
4 api::{Method, Payload},
5 types::{
6 Chat,
7 ChatId,
8 InlineKeyboardMarkup,
9 Integer,
10 Message,
11 ParseMode,
12 ReplyMarkup,
13 ReplyParameters,
14 Text,
15 TextEntities,
16 TextEntity,
17 User,
18 },
19};
20
21#[cfg(test)]
22mod tests;
23
24#[derive(Clone, Debug, derive_more::From, Deserialize, PartialEq, PartialOrd, Serialize)]
26#[serde(tag = "type")]
27#[serde(rename_all = "snake_case")]
28pub enum Poll {
29 Regular(RegularPoll),
31 Quiz(Quiz),
33}
34
35#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
37#[serde(rename_all = "lowercase")]
38pub enum PollType {
39 Quiz,
41 Regular,
43}
44
45#[serde_with::skip_serializing_none]
46#[derive(Deserialize, Serialize)]
47struct RawQuestion {
48 question: String,
49 question_entities: Option<TextEntities>,
50}
51
52impl RawQuestion {
53 fn deserialize_value<'de, D>(deserializer: D) -> Result<Text, D::Error>
54 where
55 D: Deserializer<'de>,
56 {
57 let value = Self::deserialize(deserializer)?;
58 Ok(Text {
59 data: value.question,
60 entities: value.question_entities,
61 })
62 }
63
64 fn serialize_value<S>(value: &Text, serializer: S) -> Result<S::Ok, S::Error>
65 where
66 S: Serializer,
67 {
68 Self {
69 question: value.data.clone(),
70 question_entities: value.entities.clone(),
71 }
72 .serialize(serializer)
73 }
74}
75
76#[serde_with::skip_serializing_none]
78#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
79pub struct RegularPoll {
80 pub allows_multiple_answers: bool,
82 pub id: String,
84 pub is_anonymous: bool,
86 pub is_closed: bool,
88 pub options: Vec<PollOption>,
90 #[serde(
92 flatten,
93 deserialize_with = "RawQuestion::deserialize_value",
94 serialize_with = "RawQuestion::serialize_value"
95 )]
96 pub question: Text,
97 pub total_voter_count: Integer,
99 pub close_date: Option<Integer>,
101 pub open_period: Option<Integer>,
103}
104
105impl RegularPoll {
106 pub fn new<A, B>(id: A, question: B) -> Self
113 where
114 A: Into<String>,
115 B: Into<Text>,
116 {
117 Self {
118 allows_multiple_answers: false,
119 id: id.into(),
120 is_anonymous: false,
121 is_closed: false,
122 options: vec![],
123 question: question.into(),
124 total_voter_count: 0,
125 close_date: None,
126 open_period: None,
127 }
128 }
129
130 pub fn with_allows_multiple_answers(mut self, value: bool) -> Self {
136 self.allows_multiple_answers = value;
137 self
138 }
139
140 pub fn with_close_date(mut self, value: Integer) -> Self {
146 self.close_date = Some(value);
147 self
148 }
149
150 pub fn with_is_anonymous(mut self, value: bool) -> Self {
156 self.is_anonymous = value;
157 self
158 }
159
160 pub fn with_is_closed(mut self, value: bool) -> Self {
166 self.is_closed = value;
167 self
168 }
169
170 pub fn with_open_period(mut self, value: Integer) -> Self {
176 self.open_period = Some(value);
177 self
178 }
179
180 pub fn with_options<T>(mut self, value: T) -> Self
186 where
187 T: IntoIterator<Item = PollOption>,
188 {
189 self.options = value.into_iter().collect();
190 self
191 }
192
193 pub fn with_total_voter_count(mut self, value: Integer) -> Self {
199 self.total_voter_count = value;
200 self
201 }
202}
203
204#[serde_with::skip_serializing_none]
206#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
207pub struct Quiz {
208 pub correct_option_id: Integer,
214 pub id: String,
216 pub is_anonymous: bool,
218 pub is_closed: bool,
220 pub options: Vec<PollOption>,
222 #[serde(
224 flatten,
225 deserialize_with = "RawQuestion::deserialize_value",
226 serialize_with = "RawQuestion::serialize_value"
227 )]
228 pub question: Text,
229 pub total_voter_count: Integer,
231 pub close_date: Option<Integer>,
233 #[serde(
236 flatten,
237 deserialize_with = "QuizExplanation::deserialize_value",
238 serialize_with = "QuizExplanation::serialize_value"
239 )]
240 pub explanation: Option<Text>,
241 pub open_period: Option<Integer>,
243}
244
245impl Quiz {
246 pub fn new<A, B>(id: A, question: B) -> Self
253 where
254 A: Into<String>,
255 B: Into<Text>,
256 {
257 Self {
258 correct_option_id: 0,
259 id: id.into(),
260 is_anonymous: false,
261 is_closed: false,
262 options: vec![],
263 question: question.into(),
264 total_voter_count: 0,
265 close_date: None,
266 explanation: None,
267 open_period: None,
268 }
269 }
270
271 pub fn with_close_date(mut self, value: Integer) -> Self {
277 self.close_date = Some(value);
278 self
279 }
280
281 pub fn with_correct_option_id(mut self, value: Integer) -> Self {
287 self.correct_option_id = value;
288 self
289 }
290
291 pub fn with_explanation<T>(mut self, value: T) -> Self
299 where
300 T: Into<Text>,
301 {
302 self.explanation = Some(value.into());
303 self
304 }
305
306 pub fn with_is_anonymous(mut self, value: bool) -> Self {
312 self.is_anonymous = value;
313 self
314 }
315
316 pub fn with_is_closed(mut self, value: bool) -> Self {
322 self.is_closed = value;
323 self
324 }
325
326 pub fn with_open_period(mut self, value: Integer) -> Self {
332 self.open_period = Some(value);
333 self
334 }
335
336 pub fn with_options<T>(mut self, value: T) -> Self
342 where
343 T: IntoIterator<Item = PollOption>,
344 {
345 self.options = value.into_iter().collect();
346 self
347 }
348
349 pub fn with_total_voter_count(mut self, value: Integer) -> Self {
355 self.total_voter_count = value;
356 self
357 }
358}
359
360#[serde_with::skip_serializing_none]
361#[derive(Deserialize, Serialize)]
362struct QuizExplanation {
363 explanation: String,
364 explanation_entities: Option<TextEntities>,
365}
366
367impl QuizExplanation {
368 fn deserialize_value<'de, D>(deserializer: D) -> Result<Option<Text>, D::Error>
369 where
370 D: Deserializer<'de>,
371 {
372 Option::<QuizExplanation>::deserialize(deserializer).map(|x| {
373 x.map(|value| Text {
374 data: value.explanation,
375 entities: value.explanation_entities,
376 })
377 })
378 }
379
380 fn serialize_value<S>(value: &Option<Text>, serializer: S) -> Result<S::Ok, S::Error>
381 where
382 S: Serializer,
383 {
384 let value = value.clone().map(|value| QuizExplanation {
385 explanation: value.data,
386 explanation_entities: value.entities,
387 });
388 value.serialize(serializer)
389 }
390}
391
392#[serde_with::skip_serializing_none]
393#[derive(Deserialize, Serialize)]
394struct RawPollOptionText {
395 text: String,
396 text_entities: Option<TextEntities>,
397}
398
399impl RawPollOptionText {
400 fn deserialize_value<'de, D>(deserializer: D) -> Result<Text, D::Error>
401 where
402 D: Deserializer<'de>,
403 {
404 let value = Self::deserialize(deserializer)?;
405 Ok(Text {
406 data: value.text,
407 entities: value.text_entities,
408 })
409 }
410
411 fn serialize_value<S>(value: &Text, serializer: S) -> Result<S::Ok, S::Error>
412 where
413 S: Serializer,
414 {
415 Self {
416 text: value.data.clone(),
417 text_entities: value.entities.clone(),
418 }
419 .serialize(serializer)
420 }
421}
422
423#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
425pub struct PollOption {
426 #[serde(
428 flatten,
429 deserialize_with = "RawPollOptionText::deserialize_value",
430 serialize_with = "RawPollOptionText::serialize_value"
431 )]
432 pub text: Text,
433 pub voter_count: Integer,
435}
436
437impl PollOption {
438 pub fn new<T>(text: T, voter_count: Integer) -> Self
445 where
446 T: Into<Text>,
447 {
448 Self {
449 text: text.into(),
450 voter_count,
451 }
452 }
453}
454
455#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
457pub struct PollAnswer {
458 pub option_ids: Vec<Integer>,
462 pub poll_id: String,
464 #[serde(flatten)]
466 pub voter: PollAnswerVoter,
467}
468
469impl PollAnswer {
470 pub fn new<A, B, C>(option_ids: A, poll_id: B, voter: C) -> Self
478 where
479 A: IntoIterator<Item = Integer>,
480 B: Into<String>,
481 C: Into<PollAnswerVoter>,
482 {
483 Self {
484 option_ids: option_ids.into_iter().collect(),
485 poll_id: poll_id.into(),
486 voter: voter.into(),
487 }
488 }
489}
490
491#[derive(Clone, Debug, derive_more::From, Deserialize, PartialEq, Serialize)]
493#[serde(rename_all = "snake_case")]
494pub enum PollAnswerVoter {
495 Chat(Chat),
497 User(User),
499}
500
501#[serde_with::skip_serializing_none]
503#[derive(Clone, Debug, Serialize)]
504pub struct InputPollOption {
505 text: String,
506 text_parse_mode: Option<ParseMode>,
507 text_entities: Option<TextEntities>,
508}
509
510impl InputPollOption {
511 pub fn new<T>(text: T) -> Self
517 where
518 T: Into<String>,
519 {
520 Self {
521 text: text.into(),
522 text_parse_mode: None,
523 text_entities: None,
524 }
525 }
526
527 pub fn with_entities<T>(mut self, value: T) -> Self
535 where
536 T: IntoIterator<Item = TextEntity>,
537 {
538 self.text_entities = Some(value.into_iter().collect());
539 self.text_parse_mode = None;
540 self
541 }
542
543 pub fn with_parse_mode(mut self, value: ParseMode) -> Self {
552 self.text_parse_mode = Some(value);
553 self.text_entities = None;
554 self
555 }
556}
557
558impl<T> From<T> for InputPollOption
559where
560 T: Into<Text>,
561{
562 fn from(value: T) -> Self {
563 let value = value.into();
564 Self {
565 text: value.data,
566 text_entities: value.entities,
567 text_parse_mode: None,
568 }
569 }
570}
571
572#[serde_with::skip_serializing_none]
573#[derive(Clone, Debug, Serialize)]
574struct PollParameters {
575 chat_id: ChatId,
576 options: Vec<InputPollOption>,
577 question: String,
578 allow_paid_broadcast: Option<bool>,
579 allows_multiple_answers: Option<bool>,
580 business_connection_id: Option<String>,
581 close_date: Option<Integer>,
582 correct_option_id: Option<Integer>,
583 disable_notification: Option<bool>,
584 explanation: Option<String>,
585 explanation_entities: Option<TextEntities>,
586 explanation_parse_mode: Option<ParseMode>,
587 is_anonymous: Option<bool>,
588 is_closed: Option<bool>,
589 message_effect_id: Option<String>,
590 message_thread_id: Option<Integer>,
591 open_period: Option<Integer>,
592 #[serde(rename = "type")]
593 poll_type: Option<PollType>,
594 protect_content: Option<bool>,
595 question_entities: Option<TextEntities>,
596 question_parse_mode: Option<ParseMode>,
597 reply_markup: Option<ReplyMarkup>,
598 reply_parameters: Option<ReplyParameters>,
599}
600
601impl PollParameters {
602 fn new<A, B>(chat_id: ChatId, question: String, poll_type: PollType, options: A) -> Self
603 where
604 A: IntoIterator<Item = B>,
605 B: Into<InputPollOption>,
606 {
607 Self {
608 chat_id,
609 options: options.into_iter().map(Into::into).collect(),
610 question,
611 allow_paid_broadcast: None,
612 allows_multiple_answers: None,
613 business_connection_id: None,
614 close_date: None,
615 correct_option_id: None,
616 disable_notification: None,
617 explanation: None,
618 explanation_entities: None,
619 explanation_parse_mode: None,
620 is_anonymous: None,
621 is_closed: None,
622 message_effect_id: None,
623 message_thread_id: None,
624 open_period: None,
625 poll_type: Some(poll_type),
626 question_entities: None,
627 question_parse_mode: None,
628 protect_content: None,
629 reply_markup: None,
630 reply_parameters: None,
631 }
632 }
633}
634
635#[derive(Clone, Debug, Serialize)]
639pub struct SendQuiz {
640 #[serde(flatten)]
641 inner: PollParameters,
642}
643
644impl SendQuiz {
645 pub fn new<A, B, C, D>(chat_id: A, question: B, correct_option_id: Integer, options: C) -> Self
654 where
655 A: Into<ChatId>,
656 B: Into<String>,
657 C: IntoIterator<Item = D>,
658 D: Into<InputPollOption>,
659 {
660 let mut parameters = PollParameters::new(chat_id.into(), question.into(), PollType::Quiz, options);
661 parameters.correct_option_id = Some(correct_option_id);
662 Self { inner: parameters }
663 }
664
665 pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
673 self.inner.allow_paid_broadcast = Some(value);
674 self
675 }
676
677 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
683 where
684 T: Into<String>,
685 {
686 self.inner.business_connection_id = Some(value.into());
687 self
688 }
689
690 pub fn with_close_date(mut self, value: Integer) -> Self {
699 self.inner.close_date = Some(value);
700 self.inner.open_period = None;
701 self
702 }
703
704 pub fn with_disable_notification(mut self, value: bool) -> Self {
711 self.inner.disable_notification = Some(value);
712 self
713 }
714
715 pub fn with_explanation<T>(mut self, value: T) -> Self
723 where
724 T: Into<String>,
725 {
726 self.inner.explanation = Some(value.into());
727 self
728 }
729
730 pub fn with_explanation_entities<T>(mut self, value: T) -> Self
738 where
739 T: IntoIterator<Item = TextEntity>,
740 {
741 self.inner.explanation_entities = Some(value.into_iter().collect());
742 self.inner.explanation_parse_mode = None;
743 self
744 }
745
746 pub fn with_explanation_parse_mode(mut self, value: ParseMode) -> Self {
754 self.inner.explanation_parse_mode = Some(value);
755 self.inner.explanation_entities = None;
756 self
757 }
758
759 pub fn with_is_anonymous(mut self, value: bool) -> Self {
765 self.inner.is_anonymous = Some(value);
766 self
767 }
768
769 pub fn with_is_closed(mut self, value: bool) -> Self {
775 self.inner.is_closed = Some(value);
776 self
777 }
778
779 pub fn with_message_effect_id<T>(mut self, value: T) -> Self
785 where
786 T: Into<String>,
787 {
788 self.inner.message_effect_id = Some(value.into());
789 self
790 }
791
792 pub fn with_message_thread_id(mut self, value: Integer) -> Self {
799 self.inner.message_thread_id = Some(value);
800 self
801 }
802
803 pub fn with_open_period(mut self, value: Integer) -> Self {
811 self.inner.open_period = Some(value);
812 self.inner.close_date = None;
813 self
814 }
815
816 pub fn with_protect_content(mut self, value: bool) -> Self {
823 self.inner.protect_content = Some(value);
824 self
825 }
826
827 pub fn with_question_entities<T>(mut self, value: T) -> Self
835 where
836 T: IntoIterator<Item = TextEntity>,
837 {
838 self.inner.question_entities = Some(value.into_iter().collect());
839 self.inner.question_parse_mode = None;
840 self
841 }
842
843 pub fn with_question_parse_mode(mut self, value: ParseMode) -> Self {
851 self.inner.question_parse_mode = Some(value);
852 self.inner.question_entities = None;
853 self
854 }
855
856 pub fn with_reply_markup<T>(mut self, value: T) -> Self
862 where
863 T: Into<ReplyMarkup>,
864 {
865 self.inner.reply_markup = Some(value.into());
866 self
867 }
868
869 pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Self {
875 self.inner.reply_parameters = Some(value);
876 self
877 }
878}
879
880impl Method for SendQuiz {
881 type Response = Message;
882
883 fn into_payload(self) -> Payload {
884 Payload::json("sendPoll", self)
885 }
886}
887
888#[derive(Clone, Debug, Serialize)]
892pub struct SendPoll {
893 #[serde(flatten)]
894 inner: PollParameters,
895}
896
897impl SendPoll {
898 pub fn new<A, B, C, D>(chat_id: A, question: B, options: C) -> Self
906 where
907 A: Into<ChatId>,
908 B: Into<String>,
909 C: IntoIterator<Item = D>,
910 D: Into<InputPollOption>,
911 {
912 Self {
913 inner: PollParameters::new(chat_id.into(), question.into(), PollType::Regular, options),
914 }
915 }
916
917 pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
925 self.inner.allow_paid_broadcast = Some(value);
926 self
927 }
928
929 pub fn with_allows_multiple_answers(mut self, value: bool) -> Self {
935 self.inner.allows_multiple_answers = Some(value);
936 self
937 }
938
939 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
945 where
946 T: Into<String>,
947 {
948 self.inner.business_connection_id = Some(value.into());
949 self
950 }
951
952 pub fn with_close_date(mut self, value: Integer) -> Self {
961 self.inner.close_date = Some(value);
962 self.inner.open_period = None;
963 self
964 }
965
966 pub fn with_disable_notification(mut self, value: bool) -> Self {
973 self.inner.disable_notification = Some(value);
974 self
975 }
976
977 pub fn with_is_anonymous(mut self, value: bool) -> Self {
983 self.inner.is_anonymous = Some(value);
984 self
985 }
986
987 pub fn with_is_closed(mut self, value: bool) -> Self {
993 self.inner.is_closed = Some(value);
994 self
995 }
996
997 pub fn with_message_effect_id<T>(mut self, value: T) -> Self
1003 where
1004 T: Into<String>,
1005 {
1006 self.inner.message_effect_id = Some(value.into());
1007 self
1008 }
1009
1010 pub fn with_message_thread_id(mut self, value: Integer) -> Self {
1017 self.inner.message_thread_id = Some(value);
1018 self
1019 }
1020
1021 pub fn with_open_period(mut self, value: Integer) -> Self {
1029 self.inner.open_period = Some(value);
1030 self.inner.close_date = None;
1031 self
1032 }
1033
1034 pub fn with_protect_content(mut self, value: bool) -> Self {
1041 self.inner.protect_content = Some(value);
1042 self
1043 }
1044
1045 pub fn with_question_entities<T>(mut self, value: T) -> Self
1053 where
1054 T: IntoIterator<Item = TextEntity>,
1055 {
1056 self.inner.question_entities = Some(value.into_iter().collect());
1057 self.inner.question_parse_mode = None;
1058 self
1059 }
1060
1061 pub fn with_question_parse_mode(mut self, value: ParseMode) -> Self {
1069 self.inner.question_parse_mode = Some(value);
1070 self.inner.question_entities = None;
1071 self
1072 }
1073
1074 pub fn with_reply_markup<T>(mut self, value: T) -> Self
1080 where
1081 T: Into<ReplyMarkup>,
1082 {
1083 self.inner.reply_markup = Some(value.into());
1084 self
1085 }
1086
1087 pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Self {
1093 self.inner.reply_parameters = Some(value);
1094 self
1095 }
1096}
1097
1098impl Method for SendPoll {
1099 type Response = Message;
1100
1101 fn into_payload(self) -> Payload {
1102 Payload::json("sendPoll", self)
1103 }
1104}
1105
1106#[serde_with::skip_serializing_none]
1110#[derive(Clone, Debug, Serialize)]
1111pub struct StopPoll {
1112 chat_id: ChatId,
1113 message_id: Integer,
1114 business_connection_id: Option<String>,
1115 reply_markup: Option<InlineKeyboardMarkup>,
1116}
1117
1118pub type StopQuiz = StopPoll;
1122
1123impl StopPoll {
1124 pub fn new<T>(chat_id: T, message_id: Integer) -> Self
1131 where
1132 T: Into<ChatId>,
1133 {
1134 Self {
1135 chat_id: chat_id.into(),
1136 message_id,
1137 business_connection_id: None,
1138 reply_markup: None,
1139 }
1140 }
1141
1142 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
1148 where
1149 T: Into<String>,
1150 {
1151 self.business_connection_id = Some(value.into());
1152 self
1153 }
1154
1155 pub fn with_reply_markup<T>(mut self, value: T) -> Self
1161 where
1162 T: Into<InlineKeyboardMarkup>,
1163 {
1164 self.reply_markup = Some(value.into());
1165 self
1166 }
1167}
1168
1169impl Method for StopPoll {
1170 type Response = Poll;
1171
1172 fn into_payload(self) -> Payload {
1173 Payload::json("stopPoll", self)
1174 }
1175}