discord.py机器人等待input()时心跳阻塞问题求助
问题根源
你这问题本质是同步的input()函数彻底阻塞了asyncio事件循环。Discord.py靠asyncio的事件循环维持和Discord网关的心跳连接,当input()卡在那等你输入时,事件循环根本没法处理心跳包,超过10秒就触发网关断开警告,机器人直接离线。
解决办法
办法1:用异步方式处理输入(适合本地调试)
把input()丢到单独线程里执行,不让它卡住事件循环,用asyncio.to_thread()就能实现:
import os import discord import asyncio MyDiscordID = os.environ["DiscordID"] people = os.environ["people"] message = os.environ["message"] client = discord.Client(intents=discord.Intents.all()) names = [people, "yourself"] @client.event async def on_ready(): print(f"已登录:{client.user}") while True: # 把input放到线程里,不阻塞事件循环 user_input = await asyncio.to_thread(input, "要给哪个用户发DM? ") if user_input == "yourself": user = await client.fetch_user(MyDiscordID) await user.send("# ```(此消息由机器人发送)```") await user.send(message) print("已给自己发送DM") elif user_input not in names: print("找不到这个用户ID") client.run(os.environ["Token"])
办法2:改用Discord命令触发(推荐Replit托管用这个)
Replit托管的机器人后台运行时,控制台输入体验很差,不如直接在Discord里加个命令来触发发DM,完全不用依赖控制台:
import os import discord from discord import app_commands MyDiscordID = os.environ["DiscordID"] people = os.environ["people"] message = os.environ["message"] client = discord.Client(intents=discord.Intents.all()) tree = app_commands.CommandTree(client) # 允许使用命令的用户ID列表 allowed_users = [int(MyDiscordID), int(people)] @client.event async def on_ready(): await tree.sync() print(f"已登录:{client.user}") @tree.command(name="send_dm", description="给指定用户发送预设DM") async def send_dm(interaction: discord.Interaction, user_id: str): # 只让指定用户用这个命令 if interaction.user.id not in allowed_users: await interaction.response.send_message("你没权限用这个命令", ephemeral=True) return try: target_user = await client.fetch_user(int(user_id)) await target_user.send("# ```(此消息由机器人发送)```") await target_user.send(message) await interaction.response.send_message(f"已成功给用户{user_id}发DM", ephemeral=True) except discord.NotFound: await interaction.response.send_message("找不到这个用户", ephemeral=True) except Exception as e: await interaction.response.send_message(f"发送失败:{str(e)}", ephemeral=True) client.run(os.environ["Token"])
办法3:单独开线程处理控制台输入
非要保留控制台输入的话,就开个独立线程专门处理输入,然后把发DM的操作丢回事件循环执行:
import os import discord import threading import asyncio MyDiscordID = os.environ["DiscordID"] people = os.environ["people"] message = os.environ["message"] client = discord.Client(intents=discord.Intents.all()) names = [people, "yourself"] def input_handler(): while True: user_input = input("要给哪个用户发DM? ") # 把异步任务调度到事件循环里执行 asyncio.run_coroutine_threadsafe(send_dm(user_input), client.loop) async def send_dm(user_input): if user_input == "yourself": user = await client.fetch_user(MyDiscordID) await user.send("# ```(此消息由机器人发送)```") await user.send(message) print("已给自己发送DM") elif user_input not in names: print("找不到这个用户ID") @client.event async def on_ready(): print(f"已登录:{client.user}") # 启动输入线程,设为守护线程随主程序退出 threading.Thread(target=input_handler, daemon=True).start() client.run(os.environ["Token"])
注意点
- Replit托管时,优先选办法2,控制台输入在后台环境下基本没法正常用。
- 所有和Discord API交互的操作必须在asyncio事件循环里执行,不能在普通线程里直接调用异步方法,得用
asyncio.run_coroutine_threadsafe或者loop.call_soon_threadsafe来调度。
内容的提问来源于stack exchange,提问作者jhonsocool
相关产品推荐
相关产品推荐

