skip to Main Content

I have a question that has been bothering me for a long time. I would greatly appreciate your help!

Let’s imagine a situation like this:

You entered a function by a command. In this particular function, there is a loop that takes a long time to finish, or worse, it is actually an infinite loop. The question is, how can you abort this loop or function so that the bot can handle other requests?

An example would be like this:

def function(update, context):
  counter = 0
  message = update.message.reply_text(str(counter))

  while True:
    counter += 1
    message = message.edit_text(text = str(counter))
    time.sleep(1)

dispatcher.add_handler(CommandHandler("main", function))

After user enters this function by "/main", he or she gets a message that includes a number, which the number increases every second and this will take forever to finish.

Something that I have tried:

  • Design another MessageHandler, if user sends some keywords like "stop", it changes the value of a global variable. In the loop, it checks if this variable is True every time before it adds the number, if it was False, it breaks.

    However, the MessageHandler won’t detect this message before the loop has actually ended, so this is meaningless.

  • Include a InlineKeyboard in the message, with a button called "Stop". Design another CallbackQueryHandler to handle this button.

    Similarly, CallbackQueryHandler will only receive the callback after the loop was finished. This can be verified if I set a manual break when the counter hits a number like 5 or something.

I was thinking if I could design some kind of abort function that runs asynchronously, but I am personally not very familiar with async in Python. After some trials like setting async def or CallbackQueryHandler(query_handle, run_async = True), I still failed to produce this effect.

I noticed there is a decorator called from telegram.ext.dispatcher import run_async, does that help? Or, is there any other way to realize this result?

Thanks!

2

Answers


  1. By default Dispatcher handles the incoming updates one by one. That’s why your first two approaches can’t work. What you can do is run the loops in a standalone thread. This is in fact, what the run_async parameter is there for – it has nothing to do with asyncio programming. See this wiki page for detailed information about run_async. Also note that the next major version of PTB will introduce asyncio to PTB – see this announcement.

    I’d like to point out that a while loop + time.sleep() might not be the best idea anyway, especially if the sleep-time is significant. An alternative would be to schedule a repeating job. PTB has the built-in JobQueue for that.


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

    Login or Signup to reply.
  2. Examples how to configure handlers to be run in anync mode for python-telegram-bot==13.13 (run_async=True is passed to handlers):

    dispatcher.add_handler(CommandHandler(["zip_and_send_all_logs", "zsal"],
                                          zip_and_send_all_logs_cmd, run_async=True))
    dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command,
                                          run_sh_cmd_for_text_msg, run_async=True))
    

    Also there is @run_async decorator function in

    from telegram.ext.dispatcher import run_async
    

    which can be used to mark handler to be run in async mode. But this decorator was deprecated. Recommended way is to pass run_async parameter to handlers. Its default value is False.

    Related question: Is python-telegram-bot suitable for async job? #877

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search