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#[derive(Clone, Debug, derive_more::From, Deserialize, PartialEq, PartialOrd, Serialize)]
23#[serde(tag = "type")]
24#[serde(rename_all = "snake_case")]
25pub enum Poll {
26 Regular(RegularPoll),
28 Quiz(Quiz),
30}
31
32#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
34#[serde(rename_all = "lowercase")]
35pub enum PollType {
36 Quiz,
38 Regular,
40}
41
42#[serde_with::skip_serializing_none]
43#[derive(Deserialize, Serialize)]
44struct RawQuestion {
45 question: String,
46 question_entities: Option<TextEntities>,
47}
48
49impl RawQuestion {
50 fn deserialize_value<'de, D>(deserializer: D) -> Result<Text, D::Error>
51 where
52 D: Deserializer<'de>,
53 {
54 let value = Self::deserialize(deserializer)?;
55 Ok(Text {
56 data: value.question,
57 entities: value.question_entities,
58 })
59 }
60
61 fn serialize_value<S>(value: &Text, serializer: S) -> Result<S::Ok, S::Error>
62 where
63 S: Serializer,
64 {
65 Self {
66 question: value.data.clone(),
67 question_entities: value.entities.clone(),
68 }
69 .serialize(serializer)
70 }
71}
72
73#[serde_with::skip_serializing_none]
75#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
76pub struct RegularPoll {
77 pub allows_multiple_answers: bool,
79 pub id: String,
81 pub is_anonymous: bool,
83 pub is_closed: bool,
85 pub options: Vec<PollOption>,
87 #[serde(
89 flatten,
90 deserialize_with = "RawQuestion::deserialize_value",
91 serialize_with = "RawQuestion::serialize_value"
92 )]
93 pub question: Text,
94 pub total_voter_count: Integer,
96 pub close_date: Option<Integer>,
98 pub open_period: Option<Integer>,
100}
101
102impl RegularPoll {
103 pub fn new<A, B>(id: A, question: B) -> Self
110 where
111 A: Into<String>,
112 B: Into<Text>,
113 {
114 Self {
115 allows_multiple_answers: false,
116 id: id.into(),
117 is_anonymous: false,
118 is_closed: false,
119 options: vec![],
120 question: question.into(),
121 total_voter_count: 0,
122 close_date: None,
123 open_period: None,
124 }
125 }
126
127 pub fn with_allows_multiple_answers(mut self, value: bool) -> Self {
133 self.allows_multiple_answers = value;
134 self
135 }
136
137 pub fn with_close_date(mut self, value: Integer) -> Self {
143 self.close_date = Some(value);
144 self
145 }
146
147 pub fn with_is_anonymous(mut self, value: bool) -> Self {
153 self.is_anonymous = value;
154 self
155 }
156
157 pub fn with_is_closed(mut self, value: bool) -> Self {
163 self.is_closed = value;
164 self
165 }
166
167 pub fn with_open_period(mut self, value: Integer) -> Self {
173 self.open_period = Some(value);
174 self
175 }
176
177 pub fn with_options<T>(mut self, value: T) -> Self
183 where
184 T: IntoIterator<Item = PollOption>,
185 {
186 self.options = value.into_iter().collect();
187 self
188 }
189
190 pub fn with_total_voter_count(mut self, value: Integer) -> Self {
196 self.total_voter_count = value;
197 self
198 }
199}
200
201#[serde_with::skip_serializing_none]
203#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
204pub struct Quiz {
205 pub correct_option_id: Integer,
211 pub id: String,
213 pub is_anonymous: bool,
215 pub is_closed: bool,
217 pub options: Vec<PollOption>,
219 #[serde(
221 flatten,
222 deserialize_with = "RawQuestion::deserialize_value",
223 serialize_with = "RawQuestion::serialize_value"
224 )]
225 pub question: Text,
226 pub total_voter_count: Integer,
228 pub close_date: Option<Integer>,
230 #[serde(
233 flatten,
234 deserialize_with = "QuizExplanation::deserialize_value",
235 serialize_with = "QuizExplanation::serialize_value"
236 )]
237 pub explanation: Option<Text>,
238 pub open_period: Option<Integer>,
240}
241
242impl Quiz {
243 pub fn new<A, B>(id: A, question: B) -> Self
250 where
251 A: Into<String>,
252 B: Into<Text>,
253 {
254 Self {
255 correct_option_id: 0,
256 id: id.into(),
257 is_anonymous: false,
258 is_closed: false,
259 options: vec![],
260 question: question.into(),
261 total_voter_count: 0,
262 close_date: None,
263 explanation: None,
264 open_period: None,
265 }
266 }
267
268 pub fn with_close_date(mut self, value: Integer) -> Self {
274 self.close_date = Some(value);
275 self
276 }
277
278 pub fn with_correct_option_id(mut self, value: Integer) -> Self {
284 self.correct_option_id = value;
285 self
286 }
287
288 pub fn with_explanation<T>(mut self, value: T) -> Self
296 where
297 T: Into<Text>,
298 {
299 self.explanation = Some(value.into());
300 self
301 }
302
303 pub fn with_is_anonymous(mut self, value: bool) -> Self {
309 self.is_anonymous = value;
310 self
311 }
312
313 pub fn with_is_closed(mut self, value: bool) -> Self {
319 self.is_closed = value;
320 self
321 }
322
323 pub fn with_open_period(mut self, value: Integer) -> Self {
329 self.open_period = Some(value);
330 self
331 }
332
333 pub fn with_options<T>(mut self, value: T) -> Self
339 where
340 T: IntoIterator<Item = PollOption>,
341 {
342 self.options = value.into_iter().collect();
343 self
344 }
345
346 pub fn with_total_voter_count(mut self, value: Integer) -> Self {
352 self.total_voter_count = value;
353 self
354 }
355}
356
357#[serde_with::skip_serializing_none]
358#[derive(Deserialize, Serialize)]
359struct QuizExplanation {
360 explanation: String,
361 explanation_entities: Option<TextEntities>,
362}
363
364impl QuizExplanation {
365 fn deserialize_value<'de, D>(deserializer: D) -> Result<Option<Text>, D::Error>
366 where
367 D: Deserializer<'de>,
368 {
369 Option::<QuizExplanation>::deserialize(deserializer).map(|x| {
370 x.map(|value| Text {
371 data: value.explanation,
372 entities: value.explanation_entities,
373 })
374 })
375 }
376
377 fn serialize_value<S>(value: &Option<Text>, serializer: S) -> Result<S::Ok, S::Error>
378 where
379 S: Serializer,
380 {
381 let value = value.clone().map(|value| QuizExplanation {
382 explanation: value.data,
383 explanation_entities: value.entities,
384 });
385 value.serialize(serializer)
386 }
387}
388
389#[serde_with::skip_serializing_none]
390#[derive(Deserialize, Serialize)]
391struct RawPollOptionText {
392 text: String,
393 text_entities: Option<TextEntities>,
394}
395
396impl RawPollOptionText {
397 fn deserialize_value<'de, D>(deserializer: D) -> Result<Text, D::Error>
398 where
399 D: Deserializer<'de>,
400 {
401 let value = Self::deserialize(deserializer)?;
402 Ok(Text {
403 data: value.text,
404 entities: value.text_entities,
405 })
406 }
407
408 fn serialize_value<S>(value: &Text, serializer: S) -> Result<S::Ok, S::Error>
409 where
410 S: Serializer,
411 {
412 Self {
413 text: value.data.clone(),
414 text_entities: value.entities.clone(),
415 }
416 .serialize(serializer)
417 }
418}
419
420#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
422pub struct PollOption {
423 #[serde(
425 flatten,
426 deserialize_with = "RawPollOptionText::deserialize_value",
427 serialize_with = "RawPollOptionText::serialize_value"
428 )]
429 pub text: Text,
430 pub voter_count: Integer,
432}
433
434impl PollOption {
435 pub fn new<T>(text: T, voter_count: Integer) -> Self
442 where
443 T: Into<Text>,
444 {
445 Self {
446 text: text.into(),
447 voter_count,
448 }
449 }
450}
451
452#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
454pub struct PollAnswer {
455 pub option_ids: Vec<Integer>,
459 pub poll_id: String,
461 #[serde(flatten)]
463 pub voter: PollAnswerVoter,
464}
465
466impl PollAnswer {
467 pub fn new<A, B, C>(option_ids: A, poll_id: B, voter: C) -> Self
475 where
476 A: IntoIterator<Item = Integer>,
477 B: Into<String>,
478 C: Into<PollAnswerVoter>,
479 {
480 Self {
481 option_ids: option_ids.into_iter().collect(),
482 poll_id: poll_id.into(),
483 voter: voter.into(),
484 }
485 }
486}
487
488#[derive(Clone, Debug, derive_more::From, Deserialize, PartialEq, Serialize)]
490#[serde(rename_all = "snake_case")]
491pub enum PollAnswerVoter {
492 Chat(Chat),
494 User(User),
496}
497
498#[serde_with::skip_serializing_none]
500#[derive(Clone, Debug, Serialize)]
501pub struct InputPollOption {
502 text: String,
503 text_parse_mode: Option<ParseMode>,
504 text_entities: Option<TextEntities>,
505}
506
507impl InputPollOption {
508 pub fn new<T>(text: T) -> Self
514 where
515 T: Into<String>,
516 {
517 Self {
518 text: text.into(),
519 text_parse_mode: None,
520 text_entities: None,
521 }
522 }
523
524 pub fn with_entities<T>(mut self, value: T) -> Self
532 where
533 T: IntoIterator<Item = TextEntity>,
534 {
535 self.text_entities = Some(value.into_iter().collect());
536 self.text_parse_mode = None;
537 self
538 }
539
540 pub fn with_parse_mode(mut self, value: ParseMode) -> Self {
549 self.text_parse_mode = Some(value);
550 self.text_entities = None;
551 self
552 }
553}
554
555impl<T> From<T> for InputPollOption
556where
557 T: Into<Text>,
558{
559 fn from(value: T) -> Self {
560 let value = value.into();
561 Self {
562 text: value.data,
563 text_entities: value.entities,
564 text_parse_mode: None,
565 }
566 }
567}
568
569#[serde_with::skip_serializing_none]
570#[derive(Clone, Debug, Serialize)]
571struct PollParameters {
572 chat_id: ChatId,
573 options: Vec<InputPollOption>,
574 question: String,
575 allow_paid_broadcast: Option<bool>,
576 allows_multiple_answers: Option<bool>,
577 business_connection_id: Option<String>,
578 close_date: Option<Integer>,
579 correct_option_id: Option<Integer>,
580 disable_notification: Option<bool>,
581 explanation: Option<String>,
582 explanation_entities: Option<TextEntities>,
583 explanation_parse_mode: Option<ParseMode>,
584 is_anonymous: Option<bool>,
585 is_closed: Option<bool>,
586 message_effect_id: Option<String>,
587 message_thread_id: Option<Integer>,
588 open_period: Option<Integer>,
589 #[serde(rename = "type")]
590 poll_type: Option<PollType>,
591 protect_content: Option<bool>,
592 question_entities: Option<TextEntities>,
593 question_parse_mode: Option<ParseMode>,
594 reply_markup: Option<ReplyMarkup>,
595 reply_parameters: Option<ReplyParameters>,
596}
597
598impl PollParameters {
599 fn new<A, B>(chat_id: ChatId, question: String, poll_type: PollType, options: A) -> Self
600 where
601 A: IntoIterator<Item = B>,
602 B: Into<InputPollOption>,
603 {
604 Self {
605 chat_id,
606 options: options.into_iter().map(Into::into).collect(),
607 question,
608 allow_paid_broadcast: None,
609 allows_multiple_answers: None,
610 business_connection_id: None,
611 close_date: None,
612 correct_option_id: None,
613 disable_notification: None,
614 explanation: None,
615 explanation_entities: None,
616 explanation_parse_mode: None,
617 is_anonymous: None,
618 is_closed: None,
619 message_effect_id: None,
620 message_thread_id: None,
621 open_period: None,
622 poll_type: Some(poll_type),
623 question_entities: None,
624 question_parse_mode: None,
625 protect_content: None,
626 reply_markup: None,
627 reply_parameters: None,
628 }
629 }
630}
631
632#[derive(Clone, Debug, Serialize)]
636pub struct SendQuiz {
637 #[serde(flatten)]
638 inner: PollParameters,
639}
640
641impl SendQuiz {
642 pub fn new<A, B, C, D>(chat_id: A, question: B, correct_option_id: Integer, options: C) -> Self
651 where
652 A: Into<ChatId>,
653 B: Into<String>,
654 C: IntoIterator<Item = D>,
655 D: Into<InputPollOption>,
656 {
657 let mut parameters = PollParameters::new(chat_id.into(), question.into(), PollType::Quiz, options);
658 parameters.correct_option_id = Some(correct_option_id);
659 Self { inner: parameters }
660 }
661
662 pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
670 self.inner.allow_paid_broadcast = Some(value);
671 self
672 }
673
674 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
680 where
681 T: Into<String>,
682 {
683 self.inner.business_connection_id = Some(value.into());
684 self
685 }
686
687 pub fn with_close_date(mut self, value: Integer) -> Self {
696 self.inner.close_date = Some(value);
697 self.inner.open_period = None;
698 self
699 }
700
701 pub fn with_disable_notification(mut self, value: bool) -> Self {
708 self.inner.disable_notification = Some(value);
709 self
710 }
711
712 pub fn with_explanation<T>(mut self, value: T) -> Self
720 where
721 T: Into<String>,
722 {
723 self.inner.explanation = Some(value.into());
724 self
725 }
726
727 pub fn with_explanation_entities<T>(mut self, value: T) -> Self
735 where
736 T: IntoIterator<Item = TextEntity>,
737 {
738 self.inner.explanation_entities = Some(value.into_iter().collect());
739 self.inner.explanation_parse_mode = None;
740 self
741 }
742
743 pub fn with_explanation_parse_mode(mut self, value: ParseMode) -> Self {
751 self.inner.explanation_parse_mode = Some(value);
752 self.inner.explanation_entities = None;
753 self
754 }
755
756 pub fn with_is_anonymous(mut self, value: bool) -> Self {
762 self.inner.is_anonymous = Some(value);
763 self
764 }
765
766 pub fn with_is_closed(mut self, value: bool) -> Self {
772 self.inner.is_closed = Some(value);
773 self
774 }
775
776 pub fn with_message_effect_id<T>(mut self, value: T) -> Self
782 where
783 T: Into<String>,
784 {
785 self.inner.message_effect_id = Some(value.into());
786 self
787 }
788
789 pub fn with_message_thread_id(mut self, value: Integer) -> Self {
796 self.inner.message_thread_id = Some(value);
797 self
798 }
799
800 pub fn with_open_period(mut self, value: Integer) -> Self {
808 self.inner.open_period = Some(value);
809 self.inner.close_date = None;
810 self
811 }
812
813 pub fn with_protect_content(mut self, value: bool) -> Self {
820 self.inner.protect_content = Some(value);
821 self
822 }
823
824 pub fn with_question_entities<T>(mut self, value: T) -> Self
832 where
833 T: IntoIterator<Item = TextEntity>,
834 {
835 self.inner.question_entities = Some(value.into_iter().collect());
836 self.inner.question_parse_mode = None;
837 self
838 }
839
840 pub fn with_question_parse_mode(mut self, value: ParseMode) -> Self {
848 self.inner.question_parse_mode = Some(value);
849 self.inner.question_entities = None;
850 self
851 }
852
853 pub fn with_reply_markup<T>(mut self, value: T) -> Self
859 where
860 T: Into<ReplyMarkup>,
861 {
862 self.inner.reply_markup = Some(value.into());
863 self
864 }
865
866 pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Self {
872 self.inner.reply_parameters = Some(value);
873 self
874 }
875}
876
877impl Method for SendQuiz {
878 type Response = Message;
879
880 fn into_payload(self) -> Payload {
881 Payload::json("sendPoll", self)
882 }
883}
884
885#[derive(Clone, Debug, Serialize)]
889pub struct SendPoll {
890 #[serde(flatten)]
891 inner: PollParameters,
892}
893
894impl SendPoll {
895 pub fn new<A, B, C, D>(chat_id: A, question: B, options: C) -> Self
903 where
904 A: Into<ChatId>,
905 B: Into<String>,
906 C: IntoIterator<Item = D>,
907 D: Into<InputPollOption>,
908 {
909 Self {
910 inner: PollParameters::new(chat_id.into(), question.into(), PollType::Regular, options),
911 }
912 }
913
914 pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
922 self.inner.allow_paid_broadcast = Some(value);
923 self
924 }
925
926 pub fn with_allows_multiple_answers(mut self, value: bool) -> Self {
932 self.inner.allows_multiple_answers = Some(value);
933 self
934 }
935
936 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
942 where
943 T: Into<String>,
944 {
945 self.inner.business_connection_id = Some(value.into());
946 self
947 }
948
949 pub fn with_close_date(mut self, value: Integer) -> Self {
958 self.inner.close_date = Some(value);
959 self.inner.open_period = None;
960 self
961 }
962
963 pub fn with_disable_notification(mut self, value: bool) -> Self {
970 self.inner.disable_notification = Some(value);
971 self
972 }
973
974 pub fn with_is_anonymous(mut self, value: bool) -> Self {
980 self.inner.is_anonymous = Some(value);
981 self
982 }
983
984 pub fn with_is_closed(mut self, value: bool) -> Self {
990 self.inner.is_closed = Some(value);
991 self
992 }
993
994 pub fn with_message_effect_id<T>(mut self, value: T) -> Self
1000 where
1001 T: Into<String>,
1002 {
1003 self.inner.message_effect_id = Some(value.into());
1004 self
1005 }
1006
1007 pub fn with_message_thread_id(mut self, value: Integer) -> Self {
1014 self.inner.message_thread_id = Some(value);
1015 self
1016 }
1017
1018 pub fn with_open_period(mut self, value: Integer) -> Self {
1026 self.inner.open_period = Some(value);
1027 self.inner.close_date = None;
1028 self
1029 }
1030
1031 pub fn with_protect_content(mut self, value: bool) -> Self {
1038 self.inner.protect_content = Some(value);
1039 self
1040 }
1041
1042 pub fn with_question_entities<T>(mut self, value: T) -> Self
1050 where
1051 T: IntoIterator<Item = TextEntity>,
1052 {
1053 self.inner.question_entities = Some(value.into_iter().collect());
1054 self.inner.question_parse_mode = None;
1055 self
1056 }
1057
1058 pub fn with_question_parse_mode(mut self, value: ParseMode) -> Self {
1066 self.inner.question_parse_mode = Some(value);
1067 self.inner.question_entities = None;
1068 self
1069 }
1070
1071 pub fn with_reply_markup<T>(mut self, value: T) -> Self
1077 where
1078 T: Into<ReplyMarkup>,
1079 {
1080 self.inner.reply_markup = Some(value.into());
1081 self
1082 }
1083
1084 pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Self {
1090 self.inner.reply_parameters = Some(value);
1091 self
1092 }
1093}
1094
1095impl Method for SendPoll {
1096 type Response = Message;
1097
1098 fn into_payload(self) -> Payload {
1099 Payload::json("sendPoll", self)
1100 }
1101}
1102
1103#[serde_with::skip_serializing_none]
1107#[derive(Clone, Debug, Serialize)]
1108pub struct StopPoll {
1109 chat_id: ChatId,
1110 message_id: Integer,
1111 business_connection_id: Option<String>,
1112 reply_markup: Option<InlineKeyboardMarkup>,
1113}
1114
1115pub type StopQuiz = StopPoll;
1119
1120impl StopPoll {
1121 pub fn new<T>(chat_id: T, message_id: Integer) -> Self
1128 where
1129 T: Into<ChatId>,
1130 {
1131 Self {
1132 chat_id: chat_id.into(),
1133 message_id,
1134 business_connection_id: None,
1135 reply_markup: None,
1136 }
1137 }
1138
1139 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
1145 where
1146 T: Into<String>,
1147 {
1148 self.business_connection_id = Some(value.into());
1149 self
1150 }
1151
1152 pub fn with_reply_markup<T>(mut self, value: T) -> Self
1158 where
1159 T: Into<InlineKeyboardMarkup>,
1160 {
1161 self.reply_markup = Some(value.into());
1162 self
1163 }
1164}
1165
1166impl Method for StopPoll {
1167 type Response = Poll;
1168
1169 fn into_payload(self) -> Payload {
1170 Payload::json("stopPoll", self)
1171 }
1172}