Внимание! Пакеты
python-telegram-bot
версии 13.x будут придерживаться многопоточной парадигмы программирования (*на данный момент актуальна версия 13.15). Пакеты версий 20.x и новее предоставляют чистый асинхронный Python интерфейс для Telegram Bot API. Дополнительно смотрите основные изменения в пакетеpython-telegram-bot
версии 20.x.
Базовый пример бота, который работает с опросами и построен на библиотеке python-telegram-bot
. В примере задается лимит на количество участников/ответов (принимается только 3 ответа от 3-х пользователей), которые могут взаимодействовать с каждым опросом/викториной.
Команда /preview
генерирует пользовательский закрытый опрос/викторину, очень похожую на ту, которую пользователь отправляет боту.
Хранение промежуточных данных опроса происходит в bot_data
для последующего использования в функции ответа на опрос/викторину.
Пример бота, работающего с опросами, снабжен подробными комментариями.
import logging # импорт API Telegramm from telegram import ( Poll, ParseMode, KeyboardButton, KeyboardButtonPollType, ReplyKeyboardMarkup, ReplyKeyboardRemove, Update, ) # импорт расширений библиотеки from telegram.ext import ( Updater, CommandHandler, PollAnswerHandler, PollHandler, MessageHandler, Filters, ) # подключение логирования logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) ### ОПРЕДЕЛЕНИЕ ФУНКЦИЙ ОБРАТНОГО ВЫЗОВА ### def start(update, _): """Информация о том, что может сделать этот бот""" update.message.reply_text( 'Введите `/poll` для участия в опросе, `/quiz` для участия в викторине или `/preview`' ' чтобы создать собственный опрос/викторину' ) def poll(update, context): """Отправка заранее подготовленного опроса""" # Вопрос опроса и его ответы. questions = "Как дела?" answer = ["Нормально", "Хорошо", "Отлично", "Супер!"] # Отправляем опрос в чат message = context.bot.send_poll( update.effective_chat.id, questions, answer, is_anonymous=False, allows_multiple_answers=True, ) # Сохраним информацию опроса в `bot_data` для последующего # использования в функции `receive_poll_answer` payload = { # ключом словаря с данными будет `id` опроса message.poll.id: { "questions": questions, "message_id": message.message_id, "chat_id": update.effective_chat.id, "answers": 0, } } # сохранение промежуточных результатов в `bot_data` context.bot_data.update(payload) def receive_poll_answer(update, context): """Итоги опроса пользователей""" answer = update.poll_answer poll_id = answer.poll_id try: questions = context.bot_data[poll_id]["questions"] 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] + " и " else: answer_string += questions[question_id] context.bot.send_message( context.bot_data[poll_id]["chat_id"], f"{update.effective_user.mention_html()} => {answer_string}!", parse_mode=ParseMode.HTML, ) # изменение промежуточных результатов в `bot_data` context.bot_data[poll_id]["answers"] += 1 # Закрываем опрос после того, как проголосовали три участника if context.bot_data[poll_id]["answers"] == 3: context.bot.stop_poll( context.bot_data[poll_id]["chat_id"], context.bot_data[poll_id]["message_id"] ) def quiz(update, context): """Отправка заранее определенную викторину""" # Вопрос викторины и ответы questions = 'Сколько яиц нужно для торта?' answer = ['1', '2', '4', '20'] # посылаем сообщение с викториной, правильный ответ указывается # в `correct_option_id`, представляет собой индекс `answer` message = update.effective_message.reply_poll( questions, answer, type=Poll.QUIZ, correct_option_id=2 ) # Сохраним промежуточные данные викторины в `bot_data` для использования в `receive_quiz_answer` payload = { # ключом словаря с данными будет `id` викторины message.poll.id: {"chat_id": update.effective_chat.id, "message_id": message.message_id} } context.bot_data.update(payload) def receive_quiz_answer(update, context): """Закрываем викторину после того, как ее прошли три участника""" # бот может получать обновления уже закрытого опроса, которые уже не волнуют if update.poll.is_closed: return if update.poll.total_voter_count == 3: try: quiz_data = context.bot_data[update.poll.id] except KeyError: # Это означает, что это ответ из старой викторины return context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"]) def preview(update, _): """Позволяет создать викторину или опрос пользователям чата""" # При использовании, без указания типа, позволяет пользователю # выбрать то, что он хочет создать - викторину или опрос button = [[KeyboardButton("Нажми меня!", request_poll=KeyboardButtonPollType())]] message = "Нажмите кнопку, для предварительного просмотра вашего опроса" # использование `one_time_keyboard=True` скрывает клавиатуру update.effective_message.reply_text( message, reply_markup=ReplyKeyboardMarkup(button, one_time_keyboard=True) ) def receive_poll(update, _): """ При получении ответа на пользовательский опрос/викторину, отвечаем на него закрытым опросом, копируя полученный опрос """ actual_poll = update.effective_message.poll # Нужно только `question` и `options`, все остальные # параметры не имеют значения для закрытого опроса update.effective_message.reply_poll( question=actual_poll.question, options=[o.text for o in actual_poll.options], # с `is_closed=True` опрос/викторина немедленно закрывается is_closed=True, reply_markup=ReplyKeyboardRemove(), ) def help_handler(update, _): """Отображение справочного сообщения""" update.message.reply_text("Используйте /quiz, /poll или /preview для тестирования этого бота.") if __name__ == '__main__': updater = Updater("TOKEN") dispatcher = updater.dispatcher # определяем соответствующие обработчики dispatcher.add_handler(CommandHandler('start', start)) # команда `/pool` dispatcher.add_handler(CommandHandler('poll', poll)) # обработчик ответа на опрос dispatcher.add_handler(PollAnswerHandler(receive_poll_answer)) # команда `/quiz` dispatcher.add_handler(CommandHandler('quiz', quiz)) # обработчик ответа на викторину dispatcher.add_handler(PollHandler(receive_quiz_answer)) # команда `/preview` dispatcher.add_handler(CommandHandler('preview', preview)) # обработчик создания пользовательского опроса/викторины dispatcher.add_handler(MessageHandler(Filters.poll, receive_poll)) dispatcher.add_handler(CommandHandler('help', help_handler)) # Запуск бота updater.start_polling() updater.idle()