discord.py中View按钮交互失效,关联RESUMED session问题求助
问题原因
当Discord Bot的WebSocket会话重连(日志显示"Shared ID None has successfully RESUMED session")时,之前发送的临时View会失效。这是因为临时View的交互处理逻辑依赖于当前会话的内存状态,会话重连后,Bot无法关联旧View的按钮交互到对应的回调方法,导致出现"This interaction failed"错误。
解决思路与实现
1. 改用持久化View(Persistent Views)
持久化View会在Bot启动时注册,并且通过自定义custom_id关联交互,不受会话重连的影响。需要做以下修改:
修改ReviewView类
- 初始化时设置
persistent=True - 避免直接存储
discord.User对象(无法序列化),改用用户ID、服务器ID和楼层参数 - 给按钮设置包含关键参数的唯一
custom_id
from discord import ui, ButtonStyle, Color, Interaction, Embed from discord.utils import get class ReviewView(ui.View): def __init__(self, user_id: int, guild_id: int, floor: int): super().__init__(persistent=True) self.user_id = user_id self.guild_id = guild_id self.floor = floor # 为按钮设置唯一custom_id,包含必要参数 self.approve_button.custom_id = f"review_accept_{user_id}_{guild_id}_{floor}" @ui.button(label="Accept", style=ButtonStyle.success) async def approve_button(self, interaction: Interaction, button: ui.Button): # 从Bot实例获取服务器和用户对象 guild = interaction.client.get_guild(self.guild_id) if not guild: await interaction.response.send_message("无法找到目标服务器", ephemeral=True) return user = guild.get_member(self.user_id) if not user: await interaction.response.send_message("用户已退出服务器", ephemeral=True) return embed = interaction.message.embeds[0] embed.color = Color.green() role = get(guild.roles, name=f"F{self.floor} Carrier") if not role: await interaction.response.send_message(f"角色F{self.floor} Carrier不存在", ephemeral=True) return # 检查并添加Carry Team角色 user_has_carry_team = any(r.name == "Carry Team" for r in user.roles) if not user_has_carry_team: carry_team_role = get(guild.roles, name="Carry Team") if carry_team_role: await user.add_roles(carry_team_role) # 构建通知Embed dm_embed = Embed( title=f"你的地牢楼层{self.floor}申请已通过", description="感谢加入我们的代打计划!当有你提供的代打服务需求时,会@你通知。" ) dm_embed.add_field(name="规则", value="<#990437238926110730>") dm_embed.add_field(name="审批人", value=f"{interaction.user.mention}", inline=False) dm_embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar.url) dm_embed.timestamp = interaction.created_at dm_embed.color = Color.green() try: await user.send(embed=dm_embed) except discord.Forbidden: print(f"用户{user.id}关闭了服务器DM,已在代打频道发送通知") carrier_chat = guild.get_channel(990438688561463297) if carrier_chat: await carrier_chat.send(embed=dm_embed) except Exception as e: print(f"发送通知时发生错误: {str(e)}") await interaction.response.send_message("发送通知失败", ephemeral=True) # 更新消息和角色 await interaction.message.edit(embed=embed) await user.add_roles(role) await interaction.response.send_message(f"{user.mention}的申请已被{interaction.user.mention}通过", ephemeral=False)
在Bot启动时注册持久化View
需要在on_ready事件中注册View,确保Bot启动后能处理所有关联的custom_id交互:
@bot.event async def on_ready(): # 注册持久化View,初始化参数仅用于占位,实际使用时会通过custom_id匹配 await bot.add_view(ReviewView(user_id=0, guild_id=0, floor=0)) print(f"Bot {bot.user}已启动,持久化View已注册")
发送View时传入正确参数
在/apply命令中创建View时,传入用户ID、服务器ID和楼层,而不是直接传用户对象:
# 假设这是/apply命令的回调部分 async def apply_command(interaction: Interaction, floor: int): # 构建申请Embed(根据你的需求自行实现) apply_embed = Embed(title=f"新的楼层{floor}代打申请", description=f"申请人: {interaction.user.mention}") apply_embed.color = Color.blue() # 创建持久化View view = ReviewView(user_id=interaction.user.id, guild_id=interaction.guild.id, floor=floor) # 发送到员工频道(替换为你的员工频道ID) staff_channel = interaction.guild.get_channel(1234567890) await staff_channel.send(embed=apply_embed, view=view) await interaction.response.send_message("申请已提交,请等待审批", ephemeral=True)
2. 确保Bot Intents配置正确
需要启用members intent,这样Bot才能通过用户ID获取服务器成员对象。在Bot初始化时配置:
import discord intents = discord.Intents.default() intents.members = True bot = discord.Bot(intents=intents)
同时,要在Discord开发者门户的Bot页面中开启"Server Members Intent"权限。
3. 优化异常处理
避免使用except Exception:捕获所有异常,改为捕获具体异常并打印错误信息,便于排查问题,比如DM失败通常是discord.Forbidden异常。
内容的提问来源于stack exchange,提问作者Skylier
相关产品推荐
相关产品推荐

