
本文详解如何在 discord.py 中安全地轮换 bot 的在线状态(如活动状态),避免触发 websocket 速率限制警告,重点修正 `asyncio.sleep` 未 await 导致的高频请求问题,并提供错误重试机制与最佳实践。
在 Discord.py 中,频繁调用 bot.change_presence()(例如在无限循环中)极易触发网关速率限制(Gateway Rate Limit),表现为控制台持续输出类似 WARNING discord.gateway WebSocket in shard ID None is ratelimited, waiting X seconds 的警告——即使你设置了 5 分钟(300 秒)间隔,问题仍存在,根本原因在于:asyncio.sleep(300) 本身是协程对象,若未用 await 调用,它不会真正暂停执行,而是立即返回并进入下一轮循环,导致 change_presence 实际以毫秒级频率被反复调用,远超 Discord 的允许阈值(通常为每 15 秒最多 1 次)。
以下是修复后的推荐实现,兼顾稳定性、可维护性与健壮性:
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}')
# 启动后台任务(推荐方式,避免阻塞事件循环)
bot.loop.create_task(presence_rotation())
async def presence_rotation():
while True:
try:
activity = discord.Activity(
type=discord.ActivityType.watching,
name=random.choice(actaray)
)
await bot.change_presence(activity=activity)
# ✅ 正确使用 await:真正暂停协程 300 秒
await asyncio.sleep(300)
except discord.HTTPException as e:
if e.status == 429: # 429 Too Many Requests
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 rotation: {e}")
await asyncio.sleep(60)
# 可选:全局错误处理器(补充兜底)
@bot.event
async def on_error(event, *args, **kwargs):
if event == "on_ready" and args and isinstance(args[0], discord.HTTPException):
if args[0].status == 429:
retry_after = int(args[0].response.headers.get("Retry-After", "5"))
print(f"on_ready rate limit hit. Waiting {retry_after}s before retry...")
await asyncio.sleep(retry_after + 1)关键改进说明:
- await asyncio.sleep(300) 替代 asyncio.sleep(300):这是最核心的修复。协程必须 await 才会挂起当前任务,否则循环体瞬间执行完毕,造成“伪延迟”。
- 封装为独立异步任务:使用 bot.loop.create_task() 启动 presence_rotation(),比直接在 on_ready 中写 while True 更清晰,也便于后续扩展(如动态启停)。
- 精细化异常捕获:单独处理 discord.HTTPException,尤其识别 429 状态码,并读取响应头中的 Retry-After 字段进行精准退避,而非盲目等待固定时间。
- 防御性编程:对非 429 错误(如网络中断、认证失效)设置合理 fallback 延迟(如 60 秒),防止异常导致高频重试。
注意事项:
- Discord 官方未公开精确的 change_presence 速率限制规则,但实测建议 最低间隔 ≥ 15 秒,5 分钟(300 秒)完全安全——前提是真正生效。
- 避免在 on_ready 内直接写阻塞式 while True 循环(尤其未加 await),这会干扰事件循环调度,甚至影响其他事件响应。
- 若 Bot 运行于多分片(shard)环境,请确保状态更新逻辑对所有分片一致,或使用 bot.wait_until_ready() 做前置校验。
通过以上改造,你的 Bot 将稳定、合规地轮换状态,彻底告别烦人的速率限制警告。









