2020-03-29 09:52:30 +02:00
|
|
|
#!/usr/bin/env python
|
2022-05-15 14:08:40 +02:00
|
|
|
# pylint: disable=unused-argument, wrong-import-position
|
2020-03-29 09:52:30 +02:00
|
|
|
# This program is dedicated to the public domain under the CC0 license.
|
|
|
|
|
|
|
|
"""
|
|
|
|
Basic example for a bot that works with polls. Only 3 people are allowed to interact with each
|
2021-03-28 18:53:44 +02:00
|
|
|
poll/quiz the bot generates. The preview command generates a closed poll/quiz, exactly like the
|
2020-03-29 09:52:30 +02:00
|
|
|
one the user sends the bot
|
|
|
|
"""
|
|
|
|
import logging
|
|
|
|
|
2022-05-15 14:08:40 +02:00
|
|
|
from telegram import __version__ as TG_VER
|
|
|
|
|
|
|
|
try:
|
|
|
|
from telegram import __version_info__
|
|
|
|
except ImportError:
|
|
|
|
__version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment]
|
|
|
|
|
|
|
|
if __version_info__ < (20, 0, 0, "alpha", 1):
|
|
|
|
raise RuntimeError(
|
|
|
|
f"This example is not compatible with your current PTB version {TG_VER}. To view the "
|
|
|
|
f"{TG_VER} version of this example, "
|
2022-06-09 17:22:32 +02:00
|
|
|
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
|
2022-05-15 14:08:40 +02:00
|
|
|
)
|
2020-03-29 09:52:30 +02:00
|
|
|
from telegram import (
|
|
|
|
KeyboardButton,
|
|
|
|
KeyboardButtonPollType,
|
2022-05-05 09:27:54 +02:00
|
|
|
Poll,
|
2020-03-29 09:52:30 +02:00
|
|
|
ReplyKeyboardMarkup,
|
|
|
|
ReplyKeyboardRemove,
|
2020-10-31 16:33:34 +01:00
|
|
|
Update,
|
2020-03-29 09:52:30 +02:00
|
|
|
)
|
2021-10-19 18:28:19 +02:00
|
|
|
from telegram.constants import ParseMode
|
2020-03-29 09:52:30 +02:00
|
|
|
from telegram.ext import (
|
2022-05-05 09:27:54 +02:00
|
|
|
Application,
|
2020-03-29 09:52:30 +02:00
|
|
|
CommandHandler,
|
2022-05-12 19:36:25 +02:00
|
|
|
ContextTypes,
|
2022-05-05 09:27:54 +02:00
|
|
|
MessageHandler,
|
2020-03-29 09:52:30 +02:00
|
|
|
PollAnswerHandler,
|
|
|
|
PollHandler,
|
2021-11-20 11:36:18 +01:00
|
|
|
filters,
|
2020-03-29 09:52:30 +02:00
|
|
|
)
|
2020-10-09 17:22:07 +02:00
|
|
|
|
2021-10-09 13:56:50 +02:00
|
|
|
# Enable logging
|
2020-03-29 09:52:30 +02:00
|
|
|
logging.basicConfig(
|
|
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
|
|
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2023-03-25 19:18:04 +01:00
|
|
|
TOTAL_VOTER_COUNT = 3
|
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Inform user about what this bot can do"""
|
2022-04-24 12:38:09 +02:00
|
|
|
await update.message.reply_text(
|
2020-03-29 09:52:30 +02:00
|
|
|
"Please select /poll to get a Poll, /quiz to get a Quiz or /preview"
|
|
|
|
" to generate a preview for your poll"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def poll(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Sends a predefined poll"""
|
|
|
|
questions = ["Good", "Really good", "Fantastic", "Great"]
|
2022-04-24 12:38:09 +02:00
|
|
|
message = await context.bot.send_poll(
|
2020-08-13 13:38:23 +02:00
|
|
|
update.effective_chat.id,
|
|
|
|
"How are you?",
|
|
|
|
questions,
|
2020-03-29 09:52:30 +02:00
|
|
|
is_anonymous=False,
|
|
|
|
allows_multiple_answers=True,
|
|
|
|
)
|
|
|
|
# Save some info about the poll the bot_data for later use in receive_poll_answer
|
|
|
|
payload = {
|
|
|
|
message.poll.id: {
|
|
|
|
"questions": questions,
|
|
|
|
"message_id": message.message_id,
|
|
|
|
"chat_id": update.effective_chat.id,
|
|
|
|
"answers": 0,
|
|
|
|
}
|
2020-10-09 17:22:07 +02:00
|
|
|
}
|
2020-03-29 09:52:30 +02:00
|
|
|
context.bot_data.update(payload)
|
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def receive_poll_answer(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Summarize a users poll vote"""
|
|
|
|
answer = update.poll_answer
|
2022-04-24 12:38:09 +02:00
|
|
|
answered_poll = context.bot_data[answer.poll_id]
|
2020-03-29 09:52:30 +02:00
|
|
|
try:
|
2022-04-24 12:38:09 +02:00
|
|
|
questions = answered_poll["questions"]
|
2020-03-29 09:52:30 +02:00
|
|
|
# this means this poll answer update is from an old poll, we can't do our answering then
|
|
|
|
except KeyError:
|
|
|
|
return
|
|
|
|
selected_options = answer.option_ids
|
|
|
|
answer_string = ""
|
|
|
|
for question_id in selected_options:
|
|
|
|
if question_id != selected_options[-1]:
|
|
|
|
answer_string += questions[question_id] + " and "
|
|
|
|
else:
|
|
|
|
answer_string += questions[question_id]
|
2022-04-24 12:38:09 +02:00
|
|
|
await context.bot.send_message(
|
|
|
|
answered_poll["chat_id"],
|
2020-11-23 22:09:29 +01:00
|
|
|
f"{update.effective_user.mention_html()} feels {answer_string}!",
|
2020-03-29 09:52:30 +02:00
|
|
|
parse_mode=ParseMode.HTML,
|
|
|
|
)
|
2022-04-24 12:38:09 +02:00
|
|
|
answered_poll["answers"] += 1
|
2020-03-29 09:52:30 +02:00
|
|
|
# Close poll after three participants voted
|
2023-03-25 19:18:04 +01:00
|
|
|
if answered_poll["answers"] == TOTAL_VOTER_COUNT:
|
2022-04-24 12:38:09 +02:00
|
|
|
await context.bot.stop_poll(answered_poll["chat_id"], answered_poll["message_id"])
|
2020-03-29 09:52:30 +02:00
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def quiz(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Send a predefined poll"""
|
|
|
|
questions = ["1", "2", "4", "20"]
|
2022-04-24 12:38:09 +02:00
|
|
|
message = await update.effective_message.reply_poll(
|
2020-03-29 09:52:30 +02:00
|
|
|
"How many eggs do you need for a cake?", questions, type=Poll.QUIZ, correct_option_id=2
|
|
|
|
)
|
|
|
|
# Save some info about the poll the bot_data for later use in receive_quiz_answer
|
|
|
|
payload = {
|
|
|
|
message.poll.id: {"chat_id": update.effective_chat.id, "message_id": message.message_id}
|
|
|
|
}
|
|
|
|
context.bot_data.update(payload)
|
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def receive_quiz_answer(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Close quiz after three participants took it"""
|
|
|
|
# the bot can receive closed poll updates we don't care about
|
|
|
|
if update.poll.is_closed:
|
|
|
|
return
|
2023-03-25 19:18:04 +01:00
|
|
|
if update.poll.total_voter_count == TOTAL_VOTER_COUNT:
|
2020-03-29 09:52:30 +02:00
|
|
|
try:
|
|
|
|
quiz_data = context.bot_data[update.poll.id]
|
|
|
|
# this means this poll answer update is from an old poll, we can't stop it then
|
|
|
|
except KeyError:
|
|
|
|
return
|
2022-04-24 12:38:09 +02:00
|
|
|
await context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"])
|
2020-03-29 09:52:30 +02:00
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def preview(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Ask user to create a poll and display a preview of it"""
|
|
|
|
# using this without a type lets the user chooses what he wants (quiz or poll)
|
|
|
|
button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]]
|
|
|
|
message = "Press the button to let the bot generate a preview for your poll"
|
|
|
|
# using one_time_keyboard to hide the keyboard
|
2022-04-24 12:38:09 +02:00
|
|
|
await update.effective_message.reply_text(
|
2020-03-29 09:52:30 +02:00
|
|
|
message, reply_markup=ReplyKeyboardMarkup(button, one_time_keyboard=True)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def receive_poll(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""On receiving polls, reply to it by a closed poll copying the received poll"""
|
|
|
|
actual_poll = update.effective_message.poll
|
|
|
|
# Only need to set the question and options, since all other parameters don't matter for
|
|
|
|
# a closed poll
|
2022-04-24 12:38:09 +02:00
|
|
|
await update.effective_message.reply_poll(
|
2020-03-29 09:52:30 +02:00
|
|
|
question=actual_poll.question,
|
|
|
|
options=[o.text for o in actual_poll.options],
|
|
|
|
# with is_closed true, the poll/quiz is immediately closed
|
|
|
|
is_closed=True,
|
|
|
|
reply_markup=ReplyKeyboardRemove(),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-05-12 19:36:25 +02:00
|
|
|
async def help_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
2020-03-29 09:52:30 +02:00
|
|
|
"""Display a help message"""
|
2022-04-24 12:38:09 +02:00
|
|
|
await update.message.reply_text("Use /quiz, /poll or /preview to test this bot.")
|
2020-03-29 09:52:30 +02:00
|
|
|
|
|
|
|
|
2020-10-31 16:33:34 +01:00
|
|
|
def main() -> None:
|
2021-05-27 20:34:58 +02:00
|
|
|
"""Run bot."""
|
2022-04-24 12:38:09 +02:00
|
|
|
# Create the Application and pass it your bot's token.
|
|
|
|
application = Application.builder().token("TOKEN").build()
|
|
|
|
application.add_handler(CommandHandler("start", start))
|
|
|
|
application.add_handler(CommandHandler("poll", poll))
|
|
|
|
application.add_handler(CommandHandler("quiz", quiz))
|
|
|
|
application.add_handler(CommandHandler("preview", preview))
|
|
|
|
application.add_handler(CommandHandler("help", help_handler))
|
|
|
|
application.add_handler(MessageHandler(filters.POLL, receive_poll))
|
|
|
|
application.add_handler(PollAnswerHandler(receive_poll_answer))
|
|
|
|
application.add_handler(PollHandler(receive_quiz_answer))
|
|
|
|
|
|
|
|
# Run the bot until the user presses Ctrl-C
|
|
|
|
application.run_polling()
|
2020-03-29 09:52:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|