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'])
.
我正在制作的电报机器人可以执行需要几分钟才能处理的功能,我希望能够在处理该功能时继续使用该机器人。
我正在使用 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'])
.