
discord.py 机器人因频繁调用 `change_presence()` 触发 websocket 网关限流(429 错误),核心原因是未使用 `await asyncio.sleep()` 导致异步循环“空转”,实际请求间隔远小于预期。本文提供合规、鲁棒的状态轮播实现方案。
在 Discord API 中,change_presence() 属于网关(Gateway)操作,受严格的速率限制约束:同一 shard 每 5 秒最多允许 1 次状态更新(即 0.2 次/秒)。你原代码中虽写 asyncio.sleep(300),但缺少 await 关键字,导致该语句仅创建协程对象而未真正挂起执行——循环瞬间重复运行,短时间内发起大量请求,最终触发 Discord 的限流机制,并打印类似 WebSocket in shard ID None is ratelimited, waiting 57 seconds 的警告。
✅ 正确做法是使用 await asyncio.sleep(300),确保每次状态变更后完整等待 5 分钟,让事件循环有机会处理其他任务并遵守限流窗口。
以下是优化后的完整实现:
import asyncio
import random
import discord
from discord.ext import commands
actaray = ["PcktWtchr's Videos", "Cams", "and Listening Always", "or Listening or Both"]
@bot.event
async def on_ready():
print(f'Logged in as {bot.user}')
while True:
try:
activity = discord.Activity(
type=discord.ActivityType.watching,
name=random.choice(actaray)
)
await bot.change_presence(activity=activity)
await asyncio.sleep(300) # ✅ 正确:await 确保真实休眠
except discord.HTTPException as e:
if e.status == 429: # 遇到限流响应
retry_after = int(e.response.headers.get("Retry-After", "5"))
print(f"Rate limited! Retrying after {retry_after} seconds...")
await asyncio.sleep(retry_after + 1) # 加 1 秒缓冲,避免边界重试
else:
print(f"HTTP error during presence update: {e}")
await asyncio.sleep(60) # 其他 HTTP 错误降频重试
except Exception as e:
print(f"Unexpected error in presence loop: {e}")
await asyncio.sleep(60)
# 可选:添加优雅关闭支持(如需热重载或清理)
@bot.event
async def on_disconnect():
print("Bot disconnected — presence loop paused.")? 关键注意事项:
- 永远不要在 while True 循环中遗漏 await:asyncio.sleep() 是协程,不加 await 不会暂停,等同于无限轮询;
- 异常捕获必须覆盖 discord.HTTPException:Discord 在限流时返回 HTTP 429 响应,Retry-After 头部指明精确等待秒数,应优先遵循;
- 避免全局 on_error 泛化处理:原答案中 on_error 事件无法捕获 change_presence() 抛出的异常(它不在此事件链路中),因此应将错误处理内聚在轮播逻辑内部;
- 推荐最小间隔 ≥ 300 秒(5 分钟):虽然 Discord 文档未公开确切阈值,但实测低于 240 秒易触发限流,5 分钟是稳定安全的实践值;
- 若需更高频更新(如实时计数器),应改用 Rich Presence + activity.timestamps 或前端轮询,而非反复调用 change_presence()。
通过以上修正,你的机器人将严格遵循 Discord 网关速率策略,彻底消除 ratelimited 警告,同时保持状态轮播功能稳定可靠。










