如何实现discord.py视图持久化,让机器人重启后按钮仍可工作?
问题分析与解决方案
核心问题
机器人重启后点击按钮出现「This interaction failed」,是因为你只给GameRoleButtons设置了timeout=None,但没有在机器人启动时注册持久化视图。Discord需要机器人重启后能关联旧消息的按钮交互到对应的处理类,否则无法识别。
解决步骤
- 在
on_ready事件中添加client.add_view(GameRoleButtons()),注册你的持久化视图。 - 确保所有按钮的
custom_id唯一且固定(你已经正确设置了,比如er_btn、removegameroles_btn,这是持久化的关键标识)。 - 若后续修改View结构(增删按钮),需重新发送带View的消息,旧消息按钮会因结构不匹配失效。
修改后的完整代码
import discord from discord import Intents, Client, Message, app_commands # 替换为你的TOKEN TOKEN = "YOUR_TOKEN_HERE" intents: Intents = Intents.default() intents.message_content = True client: Client = Client(intents=intents) tree = app_commands.CommandTree(client) badcommand_embed = discord.Embed(color=0xfffb00, title='Invalid command', type='rich', description='You cannot use this command here.') class GameRoleButtons(discord.ui.View): def __init__(self) -> None: # 设置timeout=None让视图持久化 super().__init__(timeout=None) @discord.ui.button(label='Elden Ring', style=discord.ButtonStyle.secondary, emoji=discord.PartialEmoji(name='EldenRing', id='[EMOJI_ID_HERE]'), custom_id='er_btn') async def eldenring_button(self, interaction: discord.Interaction, button: discord.ui.Button): role = interaction.guild.get_role([ROLE_ID_HERE]) if role not in interaction.user.roles: await interaction.user.add_roles(role) await interaction.response.send_message( f'The <:EldenRing:[EMOJI_ID_HERE]> <@&[ROLE_ID_HERE]> role has been assigned to you.', ephemeral=True ) else: await interaction.user.remove_roles(role) await interaction.response.send_message( f'The <:EldenRing:[EMOJI_ID_HERE]> <@&[ROLE_ID_HERE]> role has been removed from you.', ephemeral=True ) @discord.ui.button(label='Remove game roles', style=discord.ButtonStyle.danger, emoji='✖', custom_id='removegameroles_btn') async def removegameroles_button(self, interaction: discord.Interaction, button: discord.ui.Button): # 这里可以添加多个角色移除逻辑,当前只移除了一个示例角色 role = interaction.guild.get_role([ROLE_ID_HERE]) await interaction.user.remove_roles(role) await interaction.response.send_message('Your game roles have been removed.', ephemeral=True) # 注意:get_response函数未定义,若不需要请删除此部分或补充定义 async def send_message(message: Message, user_message: str) -> None: if not user_message: print('Message was empty as Intents were not enabled.') return is_private = user_message[0] == '$' if is_private: user_message = user_message[1:] try: # 替换为你的实际响应逻辑,或删除此部分 # response: str = get_response(user_message, message.author) # await message.author.send(response) if is_private else await message.channel.send(response) pass except Exception as e: print(e) @client.event async def on_ready() -> None: await tree.sync(guild=discord.Object(id=[GUILD_ID_HERE])) # 注册持久化视图,关键步骤! client.add_view(GameRoleButtons()) print(f'{client.user} is now running.') # 游戏角色选择命令 @tree.command(name="gameroles", description="Displays the list of roles you can obtain.", guild=discord.Object(id=[GUILD_ID_HERE])) async def gameroles_command(interaction: discord.Interaction): if interaction.channel_id == [CHANNEL_ID_HERE]: gameroles_embed = discord.Embed(color=0xfffb00, title='Select a game role', type='rich', description='Choose roles for the games that you play.') await interaction.channel.send(embed=gameroles_embed, view=GameRoleButtons()) # 回复交互,避免Discord提示交互超时 await interaction.response.send_message("角色选择面板已发送!", ephemeral=True) else: await interaction.response.send_message(embed=badcommand_embed, ephemeral=True) @client.event async def on_message(message: Message) -> None: if message.author == client.user: return username: str = str(message.author) user_message: str = message.content channel: str = str(message.channel) guild: str = str(message.guild.name) print(f'[{guild} - #{channel}] {username}: {user_message}') # 若使用前缀命令,需添加此句处理;若仅用slash命令可删除 # await client.process_commands(message) await send_message(message, user_message) def main() -> None: client.run(token=TOKEN) if __name__ == '__main__': main()
额外优化建议
- 在slash命令中,使用
interaction.response.send_message替代interaction.channel.send来回复交互,避免Discord提示“交互已过期”(原代码中gameroles_command里直接发消息但没回复交互,会导致用户看到加载状态)。 - 移除未定义的
get_response函数,或补充其实现,否则运行会报错。 - 避免同时使用
on_message和slash命令的逻辑冲突,若仅用slash命令,可删除on_message相关的消息处理部分。
内容的提问来源于stack exchange,提问作者Taiki
相关产品推荐
相关产品推荐

