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 isTrue
every time before it adds the number, if it wasFalse
, 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 anotherCallbackQueryHandler
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
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 therun_async
parameter is there for – it has nothing to do withasyncio
programming. See this wiki page for detailed information aboutrun_async
. Also note that the next major version of PTB will introduceasyncio
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-inJobQueue
for that.Disclaimer: I’m currently the maintainer of
python-telegram-bot
.Examples how to configure handlers to be run in anync mode for
python-telegram-bot==13.13
(run_async=True
is passed to handlers):Also there is
@run_async
decorator function inwhich 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 isFalse
.Related question: Is python-telegram-bot suitable for async job? #877