Python实现Telegram转Discord Bot时遇循环错误求助
问题分析与解决方案
错误根源
- Discord.py 版本兼容性问题: 新版本的discord.py禁止在非异步上下文直接访问
loop属性,原代码中discord_client.loop.create_task(background_task())违反了这一规则。 - 事件循环冲突: 连续调用
asyncio.run()会创建独立的事件循环,第二个调用(Telegram客户端)永远不会执行,因为第一个discord_client.run()会阻塞程序。 - Telethon客户端初始化不当: 同步调用
client.start()可能与异步事件循环产生冲突。
修复步骤
- 使用
discord.Client.setup_hook异步钩子启动后台任务,替代直接访问loop属性。 - 编写统一的异步主函数,通过
asyncio.gather()同时运行Discord和Telegram客户端。 - 异步初始化Telethon客户端(
await client.start())。 - 用
asyncio.Queue替代全局列表,实现线程安全的消息传递(避免多协程竞争问题)。
修正后的完整代码
from telethon import TelegramClient, events from telethon.tl.types import InputChannel import yaml import discord import asyncio # 用异步队列替代全局列表,保证线程安全 message_queue = asyncio.Queue() with open('config.yml', 'rb') as f: config = yaml.safe_load(f) """ TELEGRAM CLIENT STUFF """ telegram_client = TelegramClient("forwardgram", config["api_id"], config["api_hash"]) # 查找输入Telegram频道 input_channels_entities = [] async def init_telegram_channels(): async for d in telegram_client.iter_dialogs(): if d.name in config["input_channel_names"]: input_channels_entities.append(InputChannel(d.entity.id, d.entity.access_hash)) if not input_channels_entities: print("No input channels found, exiting") exit() # Telegram新消息处理 @telegram_client.on(events.NewMessage(chats=input_channels_entities)) async def handler(event): # 处理带URL的消息 try: parsed_response = f"{event.message.message}\n{event.message.entities[0].url}" except: parsed_response = event.message.message # 将消息放入队列 await message_queue.put(parsed_response) """ DISCORD CLIENT STUFF """ discord_client = discord.Client(intents=discord.Intents.default()) async def background_task(): await discord_client.wait_until_ready() discord_channel = discord_client.get_channel(config["discord_channel"]) while not discord_client.is_closed(): # 从队列获取消息(阻塞直到有消息) message = await message_queue.get() await discord_channel.send(message) # 标记任务完成(可选,用于队列跟踪) message_queue.task_done() # 使用setup_hook启动后台任务 @discord_client.event async def setup_hook(): discord_client.loop.create_task(background_task()) """ RUN EVERYTHING ASYNCHRONOUSLY """ async def main(): # 初始化Telegram频道信息 await init_telegram_channels() # 启动Telegram客户端 await telegram_client.start() # 同时运行Discord和Telegram客户端 await asyncio.gather( discord_client.start(config["discord_bot_token"]), telegram_client.run_until_disconnected() ) print("Listening now") asyncio.run(main())
关键改动说明
- 替换全局列表为
asyncio.Queue,避免协程间的消息竞争问题。 - 将Telegram频道初始化改为异步函数
init_telegram_channels,适配Telethon的异步API。 - 使用
setup_hook钩子启动Discord后台任务,符合discord.py的异步规范。 - 通过
asyncio.gather()并发运行两个客户端,确保它们共享同一个事件循环。 - 后台任务改为从队列阻塞获取消息,替代原有的轮询方式,更高效且资源友好。
内容的提问来源于stack exchange,提问作者zzBMRzz
相关产品推荐
相关产品推荐

