pollbot.pyΒΆ

  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()