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

Discord Python机器人遇429限流错误:仅多用户测试时触发求助

解决Discord机器人多用户并发时的429限流问题

问题根源

你遇到的429限流是因为多用户并发发送临时消息(ephemeral=True)时,总发送频率超出了Discord交互Webhook的速率限制。单用户使用时发送频率较低,不会触发阈值;但三个用户同时运行命令时,多个协程并发发送消息,叠加后的总请求量超过了Discord的限制(Webhook通常限制为每5秒10次请求)。

你的代码中,每个用户的foo命令是独立循环执行的,单个循环内的sleep仅控制当前用户的发送间隔,无法全局限制所有用户的总发送频率,因此并发时总请求量会快速累积触发限流。

修复方案

1. 添加全局并发控制信号量

使用asyncio.Semaphore限制同时发送消息的协程数量,避免短时间内发送过多请求:

首先在机器人初始化时定义全局信号量:

import asyncio
import discord
from discord.ext import commands

# 初始化机器人(根据你的实际intents调整)
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix="!", intents=intents)

# 限制同时最多3个发送任务(可根据实际情况调整)
send_semaphore = asyncio.Semaphore(3)

然后在所有ctx.send调用处包裹信号量:

@bot.command()
async def foo(ctx, args=""):
    msm = StateMachine(ctx.author)

    while True:
        embeds, buttons_conf, button_timeout, is_exit = msm.current_data()
        if is_exit:
            break

        if embeds:
            for cur_embed in embeds:
                parameter_count = len(cur_embed)
                # 用信号量包裹发送逻辑
                async with send_semaphore:
                    if parameter_count == 1:
                        await ctx.send(embed=cur_embed[0], ephemeral=True)
                    elif parameter_count == 2:
                        await ctx.send(embed=cur_embed[0], file=cur_embed[1], ephemeral=True)
                    elif parameter_count == 3:
                        await ctx.send(embed=cur_embed[0], file=cur_embed[1], ephemeral=True)
                # 将sleep移到信号量外,避免占用并发名额
                if parameter_count == 3:
                    await asyncio.sleep(cur_embed[2])

        if buttons_conf:
            async with send_semaphore:
                view = ViewButtons(buttons_conf, ctx, timeout=button_timeout)
                view.message = await ctx.send(view=view, ephemeral=True)
            await view.wait()
            await view.disable_all_items()
            
            next_command = view.next_command()
            msm.set_last_button(next_command)

        msm.to_next_step()

2. 优化消息发送逻辑

若多个embed不需要单独发送,可将它们合并到同一条消息中(Discord单条消息支持最多10个embed),减少发送请求次数:

# 示例:合并无文件的embed批量发送
if embeds:
    non_file_embeds = []
    for cur_embed in embeds:
        if len(cur_embed) == 1:
            non_file_embeds.append(cur_embed[0])
        else:
            # 带文件的单独发送
            async with send_semaphore:
                if len(cur_embed) == 2:
                    await ctx.send(embed=cur_embed[0], file=cur_embed[1], ephemeral=True)
                else:
                    await ctx.send(embed=cur_embed[0], file=cur_embed[1], ephemeral=True)
            if len(cur_embed) == 3:
                await asyncio.sleep(cur_embed[2])
    # 批量发送无文件的embed
    if non_file_embeds:
        async with send_semaphore:
            await ctx.send(embeds=non_file_embeds, ephemeral=True)

3. 调整全局发送间隔

如果信号量仍不足以解决问题,可以在每次发送后添加全局统一的小延迟,避免短时间内请求扎堆:

# 全局变量记录上次发送时间
last_send_time = None
send_cooldown = 0.5  # 每次发送间隔0.5秒

async def send_with_cooldown(coro):
    global last_send_time
    if last_send_time is not None:
        elapsed = (asyncio.get_event_loop().time() - last_send_time)
        if elapsed < send_cooldown:
            await asyncio.sleep(send_cooldown - elapsed)
    await coro
    last_send_time = asyncio.get_event_loop().time()

然后在发送时调用这个函数替代直接await ctx.send:

await send_with_cooldown(ctx.send(embed=cur_embed[0], ephemeral=True))

额外提示

  • Discord的Webhook速率限制为每5秒最多10次请求,全局并发控制时需确保总请求量不超过这个阈值。
  • 临时消息(ephemeral)通过Discord交互Webhook发送,其限流规则与普通频道消息不同,需单独注意。

内容的提问来源于stack exchange,提问作者Andrii

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 20:40:39