升级aiohttp至3.8.1后,Telethon与aiohttp.server共存运行异常
解决aiohttp 3.8.1与Telethon无法同时运行的问题
我有一个同时运行Telethon机器人和aiohttp服务器的脚本,在aiohttp 3.7.4.post0版本下可正常工作,但升级到3.8.1后失效。环境为Python 3.9.6,Telethon版本未变更,预期实现Web服务器与Telegram机器人同时运行。简化代码如下:
from aiohttp import web from telethon import TelegramClient api_id="1234567" api_hash="1234567890adcbefgh" bot_token="1234567:abcdefgh0987654321" bot=TelegramClient("my",api_id,api_hash).start(bot_token=bot_token) # Simple TG handler async def tg_handler(event): print(event.raw_text) await event.reply("HEY") # Simple GET handler at / async def http_handler_main(request): return web.Response(body="Hi",content_type="text/plain",charset="utf-8",status=200) async def build_app(): app=web.Application() app.add_routes([web.get("/",http_handler_main)]) return app bot.add_event_handler(tg_handler,events.NewMessage()) web.run_app(build_app(),port="80")
问题根源
aiohttp 3.8.x版本对web.run_app的事件循环管理做了调整,它会创建独立的事件循环并接管执行流程,而原代码中Telethon客户端是在默认事件循环中初始化启动的,两者没有共享同一个事件循环,导致Telethon的消息处理任务无法被调度执行。
解决方案
方法一:手动共享事件循环
修改代码,让Telethon和aiohttp共享同一个事件循环,通过asyncio.gather同时运行客户端任务和web服务器:
import asyncio from aiohttp import web from telethon import TelegramClient, events api_id="1234567" api_hash="1234567890adcbefgh" bot_token="1234567:abcdefgh0987654321" bot=TelegramClient("my", api_id, api_hash) # Simple TG handler async def tg_handler(event): print(event.raw_text) await event.reply("HEY") # Simple GET handler at / async def http_handler_main(request): return web.Response(body="Hi", content_type="text/plain", charset="utf-8", status=200) async def build_app(): app=web.Application() app.add_routes([web.get("/", http_handler_main)]) return app async def main(): # 添加事件处理器 bot.add_event_handler(tg_handler, events.NewMessage()) # 启动Telethon客户端 await bot.start(bot_token=bot_token) # 构建web应用并获取运行器 app = await build_app() runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, port=80) await site.start() # 保持运行直到被中断 await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())
方法二:利用aiohttp应用生命周期钩子
通过aiohttp的on_startup信号,在web应用启动时初始化Telethon客户端,确保两者使用同一个事件循环:
from aiohttp import web from telethon import TelegramClient, events api_id="1234567" api_hash="1234567890adcbefgh" bot_token="1234567:abcdefgh0987654321" bot=TelegramClient("my", api_id, api_hash) # Simple TG handler async def tg_handler(event): print(event.raw_text) await event.reply("HEY") # Simple GET handler at / async def http_handler_main(request): return web.Response(body="Hi", content_type="text/plain", charset="utf-8", status=200) async def start_telegram_bot(app): # 添加事件处理器 bot.add_event_handler(tg_handler, events.NewMessage()) # 启动客户端 await bot.start(bot_token=bot_token) # 把客户端存入app,方便后续使用(可选) app["telegram_bot"] = bot async def build_app(): app=web.Application() app.add_routes([web.get("/", http_handler_main)]) # 注册启动钩子 app.on_startup.append(start_telegram_bot) return app if __name__ == "__main__": web.run_app(build_app(), port=80)
关键修改说明
- 不再直接同步调用
bot.start(),而是在异步函数中等待其完成,确保客户端在当前事件循环中正确初始化 - 方法一中通过
AppRunner和TCPSite手动启动web服务,与Telethon任务共享同一个事件循环 - 方法二中利用aiohttp的启动钩子,让Telethon客户端在web应用启动时一同初始化,自然共享事件循环
内容的提问来源于stack exchange,提问作者karurosagu
相关产品推荐
相关产品推荐

