Как правильно одновременно запустить двух Telegram ботов одной программой на Python?

Есть следующий код на двух билбиотеках: Aiogram, Pyrogram
from threading import Thread
from aiogram import Bot, Dispatcher, executor, types
from pyrogram import Client, filters


def pyrobot():
    print("pyro started")
    @app.on_message(filters.chat("some_chat"))
    async def print_pyrogram():
        print("Pyrogram")
    app.run()


def aiobot():
    print("aio started")
    @dp.message_handler(content_types=['any'])
    async def print_aiogram():
        print("Aiogram")
    executor.start_polling(dp, skip_updates=True)


# Данные и инициализация Юзербота
api_id = "..."
api_hash = "..."
phone_number = "..."
app = Client("Pyrobot", api_id, api_hash, phone_number)

# Данные и инициализация Бота
bot = Bot(token="...")
dp = Dispatcher(bot)

th_bot = Thread(target=pyrobot(), args=())
th_userbot = Thread(target=aiobot(), args=())

th_userbot.start()
th_bot.start()

*за переопределение методов и тд знаю, с этим потом, пока суть имнно запустить

При таком варианте все равно запускается первый бот. И пока он не завершит работу, второй просто ждёт, хотя вроде как два разных потока. Самое интересное - если в функции с "ботами" просто написать циклы, то они работают одновременно.

Моя задача ловить обновления двумя ботами одновременно или по очереди. Какие варианты реализации вы можете предложить?
  • Вопрос задан
  • 4659 просмотров
Решения вопроса 1
@AlbertForest
Глянь тут мой ответ
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Vindicar
@Vindicar
RTFM!
Ну потому что ты фигню написал.
th_bot = Thread(target=pyrobot(), args=())
th_userbot = Thread(target=aiobot(), args=())

Ты пытаешься запустить в качестве потока значение, возвращаемое функцией pyrobot(). А так как она уходит в цикл и значения не возвращает, то далее ничего не происходит. До вызова конструктора Thread() дело не доходит. С aiobot() аналогично.

Еще раз:
pyrobot() - вызов функции
pyrobot - ссылка на функцию

EDIT:
Оба бота - асинхронные на базе asyncio, им для работы нужен цикл-реактор (event loop). Вообще не факт, что хорошая идея запускать их в потоках.
Тут есть два варианта, сразу даже не скажу, что проще.
Вариант А, лобовой: каждый бот создаёт своё собственные реактор через asyncio.new_event_loop(), потом задаёт его как текущий для своего потока через asyncio.set_event_loop(loop). Если ботам не требуется взаимодействовать, то это может быть проще. Если требуется... будут проблемы. Два реактора в одной программе - это не хорошо.

Вариант Б, адекватный:
И вызов app.run(), и вызов executor.start_polling(dp, skip_updates=True) скорее всего под капотом создают асинхронную функцию (корутину), и запускают её в реакторе. Тогда ты можешь обойтись без потоков, заставив обоих ботов работать на одном реакторе. Нужно будет зарыться в доки, или даже глянуть исходники.

Например, для пирограмма написано такое:
When calling this method (app.run()) without any argument it acts as a convenience method that calls start(), idle() and stop() in sequence. It makes running a single client less verbose.

Т.е. вместо вызова app.run() ты можешь изменить код так:
async def pyrobot():  # обрати внимание, теперь функция асинхронная!
    print("pyro started")
    @app.on_message(filters.chat("some_chat"))
    async def print_pyrogram():
        print("Pyrogram")
    # это вместо вызова app.run(), как написано в доках.
    await app.start()
    try:
        await app.idle()
    finally:
        await app.end()

Затем делаешь аналогичный трюк с aiobot(). Нужно посмотреть в доках на аиограм, как именно.

И потом запускаешь обоих ботов кодом вида...
asyncio.run(asyncio.gather(pyrobot(), aiobot()))
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы