Python aiogram bot:从另一个线程发送消息

Python aiogram bot: send message from another thread

我正在制作的电报机器人可以执行需要几分钟才能处理的功能,我希望能够在处理该功能时继续使用该机器人。

我正在使用 aiogram、asyncio,我尝试使用 Python 线程来实现这一点。

我目前的代码是:

import asyncio
from queue import Queue
from threading import Thread
import time
import logging
from aiogram import Bot, types
from aiogram.types.message import ContentType
from aiogram.contrib.middlewares.logging import LoggingMiddleware
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import Dispatcher, FSMContext
from aiogram.utils.executor import start_webhook
from aiogram.types import InputFile

...

loop = asyncio.get_event_loop()
bot = Bot(token=BOT_TOKEN, loop=loop)
dp = Dispatcher(bot, storage=MemoryStorage())
dp.middleware.setup(LoggingMiddleware())

task_queue = Queue()

...

async def send_result(id):
    logging.warning("entered send_result function")
    image_res = InputFile(path_or_bytesio="images/result/res.jpg")
    await bot.send_photo(id, image_res, FINISHED_MESSAGE)


def queue_processing():
    while True:
        if not task_queue.empty():
            task = task_queue.get()
            if task["type"] == "nst":
                nst.run(task["style"], task["content"])
            send_fut = asyncio.run_coroutine_threadsafe(send_result(task['id']), loop)
            send_fut.result()
            task_queue.task_done()
        time.sleep(2)


if __name__ == "__main__":

    executor_images = Thread(target=queue_processing, args=())
    executor_images.start()

    start_webhook(
        dispatcher=dp,
        webhook_path=WEBHOOK_PATH,
        skip_updates=False,
        on_startup=on_startup,
        host=WEBAPP_HOST,
        port=WEBAPP_PORT,
    ) 

所以我正在尝试设置一个单独的线程,该线程是 运行 一个正在处理慢速任务队列的循环,从而允许在此期间继续与机器人聊天并发送结果消息 ( image) 在完成任务后添加到聊天中。

但是,这不起作用。大约一年前,我的朋友在做类似的任务时想出了这个解决方案,确实在他的机器人中工作,但它似乎在我的机器人中不起作用。

根据日志判断,它甚至从未进入 send_result 函数,因为警告从未通过。第二个线程确实正常工作,结果图像被保存并在 nst.run 完成工作时位于其分配的路径中。

我尝试了很多不同的东西,我很困惑为什么这个解决方案对我不起作用,因为它确实适用于另一个机器人。例如,我尝试使用 asyncio.create_task 而不是 asyncio.run_coroutine_threadsafe,但没有用。

据我了解,您不再需要将循环传递给 aiogram 的 Bot 或 Dispatcher,但在那种情况下我不知道如何将任务从第二个线程发送到主线程。

我使用的版本:aiogram 2.18、asyncio 3.4.3、Python 3.9.10.

已解决,问题是您无法直接访问机器人的循环(使用 bot.loop 或 dp.loop),即使您将自己的异步循环传递给机器人或调度程序也是如此。

所以解决方案是通过使用 asyncio.get_event_loop()(returns 当前 运行 循环,如果有的话)从一个消息处理程序中访问主线程的循环,因为此时循环为 运行,并将其传递给 asyncio.run_coroutine_threadsafe(我为此使用了“任务”字典),如下所示:asyncio.run_coroutine_threadsafe(send_result(task['id']), task['loop']).