from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
import time
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text("start")
time.sleep(10) # a process that's going to take some time
await update.message.reply_text("finish")
app = ApplicationBuilder().token("TOKEN HERE").build()
app.add_handler(CommandHandler("start", start))
app.run_polling()
This is the simplest example of the problem i'm currently facing in a project
The bot has to do a process that takes some time to finish
But the bot stops responding to other users during that process
I tried everything
I tried older versions of python telegram bot
I tried using threads (which won't work on async functions) and asyncio (i'm not so familiar with this sorta stuffs but for some reasons still did not respond to other users)
I even tried creating two functions inside the "start" function (one async and one not async) and then running the async function through a thread of the normal function.
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
import time
import threading
import asyncio
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
async def thread1(upd: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('start')
time.sleep(10)
await update.message.reply_text('finish')
def thread_the_thread(upd: Update, ctx: ContextTypes.DEFAULT_TYPE):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(thread1(upd, ctx))
loop.close()
t = threading.Thread(target=thread_the_thread, args=(update, context))
t.start()
app = ApplicationBuilder().token("TOKEN HERE").build()
app.add_handler(CommandHandler("start", start))
app.run_polling()
But when i used the bot with two different users...
telegram.error.NetworkError: Unknown error in HTTP implementation: RuntimeError('<asyncio.locks.Event object at 0x0000022361E0A920 [unset]> is bound to a different event loop')
Somehow the related documentation about asynchronous working in PTB (python-telegram-bot) is really hard to google. Hint: use 'concurrency' keyword.
The answer to your question in official docs is here.
Before jumping to PTB itself, it's worth mentioning that you use blocking time.sleep() function. In order for it to be asynchronous, you should use the appropriate function: asyncio.sleep(). You could refer to that discussion.
As for PTB concurrency: there are 2 ways to make the bot running asynchronously:
1. block=False
Use 'block=False' option during adding the handler:
app.add_handler(CommandHandler("start", start, block=False))
Be cautious with that (a quote from PTB docs):
However, by using block=False in a handler, you can no longer rely on handlers in different groups being called one after the other. Depending on your use case, this can be an issue. Hence, PTB comes with a second option.
2. concurrent_updates
Activate concurrent_updates option during building the Application instance:
app = ApplicationBuilder().token('TOKEN HERE').concurrent_updates(True).build()
This is my piece of code which running as you expect it to (I'm using Loguru for logging purposes, you could replace it with print() for simplicity):
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
import asyncio
import random
import sys
from loguru import logger
COUNTER = 0
TOKEN = "TOKEN HERE"
logger.add(sys.stdout, level="TRACE", format="<green>{time}</green> | <blue>{function}</blue> | {message}", serialize=False)
async def logic(num: int) -> int:
"""Some logic to run"""
delay = random.randint(5, 10)
logger.trace(f"Logic-{num} to be delayed for {delay} seconds")
await asyncio.sleep(delay)
return delay
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
msg = 'started'
logger.trace(msg)
await context.bot.send_message(
chat_id=update.message.chat_id,
text=msg
)
global COUNTER
result = await logic(COUNTER)
COUNTER +=1
msg = f"Finished after {result} seconds of working"
await context.bot.send_message(
chat_id=update.message.chat_id,
text=msg
)
logger.trace(msg)
def main():
# option 1: block=False
# app = ApplicationBuilder().token(TOKEN).build()
# app.add_handler(CommandHandler("start", start, block=False))
# option 2: set concurrent_updates
app = ApplicationBuilder().token(TOKEN).concurrent_updates(True).build()
app.add_handler(CommandHandler("start", start))
app.run_polling()
if __name__ == '__main__':
main()
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With