Discord Bot控制台输入报错求助:事件循环被input阻塞
问题描述
我和朋友开发项目时,希望运行!setup命令后,能通过控制台输入内容让Discord Bot发送到聊天里。我们写的代码如下:
import discord from discord.ext import commands intents = discord.Intents.all() bot = commands.Bot(command_prefix="!", intents=intents) @bot.event async def on_ready(): print("Logged in as {}".format(bot.user)) @bot.command() async def setup(ctx): await ctx.message.delete() while True: msg = input("Message to send: ") ctx.send(msg)
运行后报错:
Loop thread traceback (most recent call last): File "C:\Users\bobot\OneDrive\Documents\projs\py\penissie.py", line 128, in <module> bot.run(token) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\client.py", line 860, in run asyncio.run(runner()) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run return runner.run(main) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run return self._loop.run_until_complete(task) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 640, in run_until_complete self.run_forever() File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\windows_events.py", line 321, in run_forever super().run_forever() File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 607, in run_forever self._run_once() File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 1922, in _run_once handle._run() File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\asyncio\events.py", line 80, in _run self._context.run(self._callback, *self._args) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\client.py", line 441, in _run_event await coro(*args, **kwargs) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\ext\commands\bot.py", line 1395, in on_message await self.process_commands(message) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\ext\commands\bot.py", line 1392, in process_commands await self.invoke(ctx) # type: ignore File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\ext\commands\bot.py", line 1350, in invoke await ctx.command.invoke(ctx) File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\ext\commands\core.py", line 1029, in invoke await injected(*ctx.args, **ctx.kwargs) # type: ignore File "C:\Users\bobot\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\ext\commands\core.py", line 235, in wrapped ret = await coro(*args, **kwargs) File "C:\Users\bobot\OneDrive\Documents\projs\py\cool.py", line 48, in setup msg = input("Message to send: ")
我推测是input()阻塞了Discord Bot的事件循环,求解决方法。
解决方法
你的推测完全正确,input()是同步阻塞函数,会卡住asyncio的事件循环,导致Bot无法处理其他事件(比如接收消息、心跳包)。下面给两种可行的修复方案:
方案一:用asyncio.run_in_executor异步获取输入
把同步的input()放到线程池里执行,不阻塞事件循环,同时记得给ctx.send()加上await(原代码里漏了,会导致消息发不出去):
import discord import asyncio from discord.ext import commands from concurrent.futures import ThreadPoolExecutor intents = discord.Intents.all() bot = commands.Bot(command_prefix="!", intents=intents) # 创建线程池,用来处理同步输入 executor = ThreadPoolExecutor(max_workers=1) @bot.event async def on_ready(): print("Logged in as {}".format(bot.user)) def get_input(prompt): return input(prompt) @bot.command() async def setup(ctx): await ctx.message.delete() while True: # 在线程池里执行input,不阻塞事件循环 msg = await bot.loop.run_in_executor(executor, get_input, "Message to send: ") # 发送消息必须加await await ctx.send(msg)
方案二:单独开线程处理控制台输入
另一种方式是启动一个独立线程专门监听控制台输入,这样事件循环和输入处理互不干扰:
import discord import threading from discord.ext import commands intents = discord.Intents.all() bot = commands.Bot(command_prefix="!", intents=intents) # 保存目标频道对象,用于线程里发送消息 target_channel = None @bot.event async def on_ready(): print("Logged in as {}".format(bot.user)) def console_input_loop(): global target_channel while True: msg = input("Message to send: ") if target_channel: # 用bot.loop.create_task把同步线程里的操作放到事件循环执行 bot.loop.create_task(target_channel.send(msg)) @bot.command() async def setup(ctx): global target_channel await ctx.message.delete() target_channel = ctx.channel print("Now you can send messages via console!")
注意启动Bot前要先启动输入线程:
# 在bot.run前添加 threading.Thread(target=console_input_loop, daemon=True).start() bot.run("YOUR_TOKEN_HERE")
额外注意点
- 原代码里
ctx.send(msg)没有加await,这是错误的,必须用await才能正确发送消息。 - 如果要退出输入循环,可以加个终止条件,比如输入
exit时break:msg = await bot.loop.run_in_executor(executor, get_input, "Message to send (type 'exit' to quit): ") if msg.lower() == "exit": print("Exiting input mode.") break
内容的提问来源于stack exchange,提问作者Xenan
相关产品推荐
相关产品推荐

