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

如何在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")

关键修改说明

  1. 用@tasks.loop替代手动循环:

    • Discord.py的tasks模块是专门为定时循环任务设计的,它会把循环任务加入事件队列,和消息、命令等事件并行处理,完全不会阻塞主线程。
    • 只需在on_ready里调用pay_employees.start()就能启动任务,比手动写while True简洁且可靠。
  2. 修复on_message的命令处理逻辑:

    • 将参数从ctx改为message,符合on_message事件的参数规范。
    • 添加await client.process_commands(message):自定义on_message会覆盖默认的命令处理流程,必须手动调用此方法才能让@client.command的命令生效。
    • 增加了跳过机器人自身消息的判断,避免触发无限循环。
  3. 代码健壮性优化:

    • 处理了新成员加入的场景,自动为未在data中的用户初始化余额。
    • 使用f-string替代字符串拼接,代码更清晰易读。
    • 将查询命令改名为stats,避免和dm命令混淆,逻辑更明确。

这样修改后,支付循环会在后台稳定运行,同时机器人能正常响应所有命令和消息事件啦!

内容的提问来源于stack exchange,提问作者D.sLyak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 15:32:45