
Discord.py 中 View 的 on_timeout() 方法在长时间超时(如24小时)下不触发,通常并非代码问题,而是因进程重启导致视图实例丢失——尤其在免费云环境(如 AWS 免费层)中,内存限制、空闲休眠或自动重启会销毁运行中的 View 对象。
discord.py 中 view 的 `on_timeout()` 方法在长时间超时(如24小时)下不触发,通常并非代码问题,而是因进程重启导致视图实例丢失——尤其在免费云环境(如 aws 免费层)中,内存限制、空闲休眠或自动重启会销毁运行中的 view 对象。
on_timeout() 是一个内存内(in-memory)生命周期回调:它仅在当前 Python 进程中、View 实例存活的前提下被异步调用。一旦 bot 进程意外退出、被系统终止(如 AWS Free Tier 的 EC2 实例因空闲被休眠/重启)、或手动重载,所有未过期但仍在内存中的 View 实例将被彻底销毁,其 on_timeout() 永远不会执行。
✅ 正确做法:避免依赖长时超时 + 服务端持久化兜底
- ❌ 不推荐:
timeout=24*60*60(86400 秒) - ✅ 推荐:设置较短超时(如
timeout=15*60),配合数据库记录状态,并通过定时任务(如 APScheduler 或外部 cron)检查并触发“逻辑超时”处理:
# 示例:使用 SQLite 记录待处理 View 状态
import sqlite3
import asyncio
from datetime import datetime, timedelta
# 初始化数据库
conn = sqlite3.connect("views.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS pending_views (
message_id INTEGER PRIMARY KEY,
channel_id INTEGER,
created_at TIMESTAMP,
timeout_at TIMESTAMP
)
""")
# 发送带 View 的消息时存档
async def send_timed_message(ctx):
view = MyView(timeout=900) # 15 分钟内存超时
msg = await ctx.send("请选择操作(15分钟内有效)", view=view)
# 持久化记录(用于后续补偿)
conn.execute(
"INSERT INTO pending_views VALUES (?, ?, ?, ?)",
(msg.id, msg.channel.id, datetime.now(), datetime.now() + timedelta(seconds=86400))
)
conn.commit()
⚠️ 注意事项:
- Discord API 不支持“唤醒已销毁 View”,
on_timeout()不是 Webhook 回调,无法跨进程触发; - AWS 免费 EC2 实例可能因内存不足(
- 使用
systemd或pm2启动 bot 时,务必配置Restart=always并启用日志监控,及时发现非预期重启; - 若必须支持“长期交互”,建议改用
discord.ui.Modal(即时响应)或设计为“状态驱动”:用户操作写入数据库,后台任务定期扫描过期项并更新消息(通过channel.fetch_message()+message.edit())。
总结:on_timeout() 是轻量级、短周期的 UI 生命周期钩子,不是可靠的延迟任务调度器。高可用 bot 应将业务超时逻辑下沉至持久化层 + 外部调度,而非寄托于单次进程内的 View 实例生命周期。










