1#!/usr/bin/env python
2# pylint: disable=unused-argument
3# This program is dedicated to the public domain under the CC0 license.
4
5"""
6Basic example for a bot that works with polls. Only 3 people are allowed to interact with each
7poll/quiz the bot generates. The preview command generates a closed poll/quiz, exactly like the
8one the user sends the bot
9"""
10import logging
11
12from telegram import (
13 KeyboardButton,
14 KeyboardButtonPollType,
15 Poll,
16 ReplyKeyboardMarkup,
17 ReplyKeyboardRemove,
18 Update,
19)
20from telegram.constants import ParseMode
21from telegram.ext import (
22 Application,
23 CommandHandler,
24 ContextTypes,
25 MessageHandler,
26 PollAnswerHandler,
27 PollHandler,
28 filters,
29)
30
31# Enable logging
32logging.basicConfig(
33 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
34)
35# set higher logging level for httpx to avoid all GET and POST requests being logged
36logging.getLogger("httpx").setLevel(logging.WARNING)
37
38logger = logging.getLogger(__name__)
39
40
41TOTAL_VOTER_COUNT = 3
42
43
44async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
45 """Inform user about what this bot can do"""
46 await update.message.reply_text(
47 "Please select /poll to get a Poll, /quiz to get a Quiz or /preview"
48 " to generate a preview for your poll"
49 )
50
51
52async def poll(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
53 """Sends a predefined poll"""
54 questions = ["Good", "Really good", "Fantastic", "Great"]
55 message = await context.bot.send_poll(
56 update.effective_chat.id,
57 "How are you?",
58 questions,
59 is_anonymous=False,
60 allows_multiple_answers=True,
61 )
62 # Save some info about the poll the bot_data for later use in receive_poll_answer
63 payload = {
64 message.poll.id: {
65 "questions": questions,
66 "message_id": message.message_id,
67 "chat_id": update.effective_chat.id,
68 "answers": 0,
69 }
70 }
71 context.bot_data.update(payload)
72
73
74async def receive_poll_answer(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
75 """Summarize a users poll vote"""
76 answer = update.poll_answer
77 answered_poll = context.bot_data[answer.poll_id]
78 try:
79 questions = answered_poll["questions"]
80 # this means this poll answer update is from an old poll, we can't do our answering then
81 except KeyError:
82 return
83 selected_options = answer.option_ids
84 answer_string = ""
85 for question_id in selected_options:
86 if question_id != selected_options[-1]:
87 answer_string += questions[question_id] + " and "
88 else:
89 answer_string += questions[question_id]
90 await context.bot.send_message(
91 answered_poll["chat_id"],
92 f"{update.effective_user.mention_html()} feels {answer_string}!",
93 parse_mode=ParseMode.HTML,
94 )
95 answered_poll["answers"] += 1
96 # Close poll after three participants voted
97 if answered_poll["answers"] == TOTAL_VOTER_COUNT:
98 await context.bot.stop_poll(answered_poll["chat_id"], answered_poll["message_id"])
99
100
101async def quiz(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
102 """Send a predefined poll"""
103 questions = ["1", "2", "4", "20"]
104 message = await update.effective_message.reply_poll(
105 "How many eggs do you need for a cake?", questions, type=Poll.QUIZ, correct_option_id=2
106 )
107 # Save some info about the poll the bot_data for later use in receive_quiz_answer
108 payload = {
109 message.poll.id: {"chat_id": update.effective_chat.id, "message_id": message.message_id}
110 }
111 context.bot_data.update(payload)
112
113
114async def receive_quiz_answer(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
115 """Close quiz after three participants took it"""
116 # the bot can receive closed poll updates we don't care about
117 if update.poll.is_closed:
118 return
119 if update.poll.total_voter_count == TOTAL_VOTER_COUNT:
120 try:
121 quiz_data = context.bot_data[update.poll.id]
122 # this means this poll answer update is from an old poll, we can't stop it then
123 except KeyError:
124 return
125 await context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"])
126
127
128async def preview(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
129 """Ask user to create a poll and display a preview of it"""
130 # using this without a type lets the user chooses what he wants (quiz or poll)
131 button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]]
132 message = "Press the button to let the bot generate a preview for your poll"
133 # using one_time_keyboard to hide the keyboard
134 await update.effective_message.reply_text(
135 message, reply_markup=ReplyKeyboardMarkup(button, one_time_keyboard=True)
136 )
137
138
139async def receive_poll(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
140 """On receiving polls, reply to it by a closed poll copying the received poll"""
141 actual_poll = update.effective_message.poll
142 # Only need to set the question and options, since all other parameters don't matter for
143 # a closed poll
144 await update.effective_message.reply_poll(
145 question=actual_poll.question,
146 options=[o.text for o in actual_poll.options],
147 # with is_closed true, the poll/quiz is immediately closed
148 is_closed=True,
149 reply_markup=ReplyKeyboardRemove(),
150 )
151
152
153async def help_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
154 """Display a help message"""
155 await update.message.reply_text("Use /quiz, /poll or /preview to test this bot.")
156
157
158def main() -> None:
159 """Run bot."""
160 # Create the Application and pass it your bot's token.
161 application = Application.builder().token("TOKEN").build()
162 application.add_handler(CommandHandler("start", start))
163 application.add_handler(CommandHandler("poll", poll))
164 application.add_handler(CommandHandler("quiz", quiz))
165 application.add_handler(CommandHandler("preview", preview))
166 application.add_handler(CommandHandler("help", help_handler))
167 application.add_handler(MessageHandler(filters.POLL, receive_poll))
168 application.add_handler(PollAnswerHandler(receive_poll_answer))
169 application.add_handler(PollHandler(receive_quiz_answer))
170
171 # Run the bot until the user presses Ctrl-C
172 application.run_polling(allowed_updates=Update.ALL_TYPES)
173
174
175if __name__ == "__main__":
176 main()