如何在discord.py中让命令函数与循环任务并行运行?
问题根源与解决方案
你遇到的核心问题是**on_ready里的无限while True循环阻塞了Discord.py的事件循环**。Discord.py基于异步事件循环运行,这个手动写的循环会一直占用事件循环主线程,导致@client.command的命令处理任务根本没机会执行——而on_message能工作,是因为它在await asyncio.sleep(60)的间隙,事件循环还能抽空处理消息事件,但命令处理需要额外的流程,被阻塞后就彻底失效了。
另外你的代码还有两个小问题:on_message的参数错误(应该是message而非ctx),以及没有调用await client.process_commands(message),这会导致命令处理被自定义的on_message覆盖而无法触发。
下面是修复后的完整代码,我会一步步解释关键修改:
修复后的代码
import discord from discord.ext import commands from discord.ext import tasks client = commands.Bot(command_prefix='/') payroll = [] data = {} # 用tasks装饰器实现非阻塞的支付循环 @tasks.loop(seconds=60) async def pay_employees(): global data, payroll for person in payroll: data[person] += 10 print(data) @client.event async def on_ready(): global data, payroll server = client.get_guild(670006903815929889) # 初始化服务器成员的余额数据 for person in server.members: person_str = str(person) payroll.append(person_str) data[person_str] = 0 print(data) # 启动支付循环任务 pay_employees.start() print("支付任务已启动") @client.event async def on_message(message): global data # 必须调用此方法,否则@client.command的命令不会被处理 await client.process_commands(message) # 跳过机器人自身的消息,避免无限循环触发逻辑 if message.author == client.user: return author_str = str(message.author) # 兼容新成员加入的情况,自动初始化余额 if author_str not in data: data[author_str] = 0 balance = data[author_str] data[author_str] -= 5 if balance <= 25 and message.author.id != 734119994761150585: await message.channel.send(f'Be careful {message.author.mention}! Your balance is ${balance}.') if balance <= 0 and message.author.id != 734119994761150585: await message.channel.send(f'Oops! {message.author.mention}, you have run out of money. As a consequence, you will be deducted $15.') data[author_str] -= 15 if message.content.startswith("/dm"): await message.author.send('Hello World!') # 查询个人余额的命令 @client.command(name="stats") async def stats(ctx): author_str = str(ctx.author) balance = data.get(author_str, 0) await ctx.author.send(f'Your current balance is ${balance}') # 保留原有的dm命令(可根据需求调整逻辑) @client.command() async def dm(ctx): await ctx.author.send('stats go here') client.run("your_token_here")
关键修改说明
用
@tasks.loop替代手动循环:- Discord.py的
tasks模块是专门为定时循环任务设计的,它会把循环任务加入事件队列,和消息、命令等事件并行处理,完全不会阻塞主线程。 - 只需在
on_ready里调用pay_employees.start()就能启动任务,比手动写while True简洁且可靠。
- Discord.py的
修复
on_message的命令处理逻辑:- 将参数从
ctx改为message,符合on_message事件的参数规范。 - 添加
await client.process_commands(message):自定义on_message会覆盖默认的命令处理流程,必须手动调用此方法才能让@client.command的命令生效。 - 增加了跳过机器人自身消息的判断,避免触发无限循环。
- 将参数从
代码健壮性优化:
- 处理了新成员加入的场景,自动为未在
data中的用户初始化余额。 - 使用
f-string替代字符串拼接,代码更清晰易读。 - 将查询命令改名为
stats,避免和dm命令混淆,逻辑更明确。
- 处理了新成员加入的场景,自动为未在
这样修改后,支付循环会在后台稳定运行,同时机器人能正常响应所有命令和消息事件啦!
内容的提问来源于stack exchange,提问作者D.sLyak
相关产品推荐
相关产品推荐

