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
相关产品推荐
相关产品推荐

