You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python实现Telegram转Discord Bot时遇循环错误求助

问题分析与解决方案

错误根源

  1. Discord.py 版本兼容性问题: 新版本的discord.py禁止在非异步上下文直接访问loop属性,原代码中discord_client.loop.create_task(background_task())违反了这一规则。
  2. 事件循环冲突: 连续调用asyncio.run()会创建独立的事件循环,第二个调用(Telegram客户端)永远不会执行,因为第一个discord_client.run()会阻塞程序。
  3. Telethon客户端初始化不当: 同步调用client.start()可能与异步事件循环产生冲突。

修复步骤

  1. 使用discord.Client.setup_hook异步钩子启动后台任务,替代直接访问loop属性。
  2. 编写统一的异步主函数,通过asyncio.gather()同时运行Discord和Telegram客户端。
  3. 异步初始化Telethon客户端(await client.start())。
  4. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:40:13