Search code examples
pythontelegramtelegram-botpython-telegram-bot

How to execute poll function right away in telegram bot python code?


I am trying to run a code that will automatically send out a telegram poll during certain events, and analyze the answers immediately.

I found an example code that can achieve what I want but I would like to execute self.poll() immediately upon running this piece of code instead of waiting for a user to input '/poll'. I am not yet familiar with this style of coding and can't seem to find a way to do so... could someone help me out?

The issue is mainly in line 83 of the code. (see comment on that part)

from telegram import (
    Poll,
    ParseMode,
    KeyboardButton,
    KeyboardButtonPollType,
    ReplyKeyboardMarkup,
    ReplyKeyboardRemove,
    Update,
)
from telegram.ext import (
    Updater,
    CommandHandler,
    PollAnswerHandler,
    PollHandler,
    MessageHandler,
    Filters,
    CallbackContext,
)
from telegram import Bot
import time

class TelegramBot:
    def __init__(self, api_key, chat_id):
        self.api_key = api_key
        self.chat_id = chat_id
        self.options = ['0', '1', '2']

    def send_message(self, message):
        self.tel = Bot(token=self.api_key)
        self.tel.sendMessage(self.chat_id, message)

    def poll(self, update: Update , context: CallbackContext) -> None:
        """Sends a predefined poll"""
        options = self.options
        message = context.bot.send_poll(
            update.effective_chat.id,
            "What is the number?",
            options,
            is_anonymous=False,
            allows_multiple_answers=False
        )

        # Save some info about the poll the bot_data for later use in receive_poll_answer
        payload = {
            message.poll.id: {
                "options": options,
                "message_id": message.message_id,
                "chat_id": update.effective_chat.id,
                "answers": 0,
            }
        }
        context.bot_data.update(payload)

    def receive_poll_answer(self, update: Update, context: CallbackContext) -> None:
        """Summarize a users poll vote"""
        answer = update.poll_answer
        poll_id = answer.poll_id
        try:
            options = context.bot_data[poll_id]["options"]
        # 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 option_id in selected_options:
            if option_id != selected_options[-1]:
                answer_string += options[option_id] + " and "
            else:
                answer_string += options[option_id]

        context.bot_data[poll_id]["answers"] += 1
        # Close poll after 50 participants voted
        if context.bot_data[poll_id]["answers"] == 50:
            context.bot.stop_poll(
                context.bot_data[poll_id]["chat_id"], context.bot_data[poll_id]["message_id"]
            )

    def run(self) -> None:
        """Run bot."""
        # Create the Updater and pass it your bot's token.
        updater = Updater(self.api_key)
        dispatcher = updater.dispatcher
        dispatcher.add_handler(CommandHandler('poll', self.poll)) # <---- I want to run self.poll right here when I execute this whole function run(). Currently it's only possible to trigger it by user input command '/poll'
        dispatcher.add_handler(PollAnswerHandler(self.receive_poll_answer))
        # Start the Bot
        updater.start_polling()
        updater.idle()

telegram_bot = TelegramBot('api_key', 'chat_id')
telegram_bot.send_message('/poll') # I tried sending /poll using the bot itself before executing run(), but it does not seem to register bot messages
telegram_bot.run()

Solution

  • poll is a handler callback, i.e. to work it needs an incoming update and the context associated with that update. More precisely, your poll function uses the chat_id contained in the update to send the message to, context.bot_data to store data in and context.bot to make the request to Telegram. What you can do is factor out the update- and context-related logic into another function, e.g.

    
    def poll(self, chat_id, bot, bot_data):
        # what `poll` currently does but replace `update.effective_chat.id` with `chat_id`
       # and `context.{bot_data, bot}` with `bot_data`/`bot`
    
    def poll_callback(self, update, context):
        self.poll(update.effective_chat.id, context.bot, context.bot_data)
    

    (Note that it would be even easier if you e.g. stored self.bot = updater.bot in run - then you don't need to pass bot to poll but can just use self.bot.)

    Then you can call poll in your run function as

    self.poll(some_chat_id, dispatcher.bot_data)
    

    Note that context.bot_data is always the same objects as dispatcher.bot_data, that's why this works.


    Disclaimer: I'm currently the maintainer of python-telegram-bot