0
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')
desertnaut
  • 57,590
  • 26
  • 140
  • 166
8harifi
  • 1
  • 1

1 Answers1

1

Documentation

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.

Blocking vs Async

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.

PTB concurrency

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()

Example

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.