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