You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Discord.py动态创建的按钮实现持久化?

持久化按钮的正确实现方法

你的代码重启后按钮失效的核心问题是未正确注册持久化视图,以及重启后丢失了角色对象与按钮的关联逻辑,以下是具体修正方案:

问题根源

  1. 视图注册错误:setup_hook中使用self.add_dynamic_items的方式不符合Discord.py要求,持久化视图必须通过bot.add_view()注册整个视图类,让机器人能识别对应custom_id的交互处理逻辑。
  2. 依赖临时角色列表:Self_Roles类的回调依赖初始化时传入的roles列表,重启后这个临时列表不存在,导致无法找到角色对象。
  3. 冗余的数据存储:你存储了角色名称,但重启后应该通过角色ID直接从服务器获取角色对象——ID是唯一标识,名称可能被修改。

修正步骤

1. 修正视图注册逻辑

在Cog的setup_hook中注册Self_Roles视图类,确保机器人启动时就能识别该视图的交互:

async def setup_hook(self) -> None:
    # 注册持久化视图类,timeout=None表示永久有效
    self.bot.add_view(Self_Roles(self.file_path))

2. 重构Self_Roles类

去掉对初始化时roles参数的依赖,在回调中通过角色ID从服务器获取角色对象,同时拆分按钮添加逻辑:

class Self_Roles(discord.ui.View):
    def __init__(self, file_path):
        super().__init__(timeout=None)
        self.file_path = file_path

    # 动态添加按钮的方法,创建面板时调用
    def add_role_buttons(self, roles):
        for role in roles:
            button = discord.ui.Button(label=role.name, style=discord.ButtonStyle.primary, custom_id=str(role.id))
            button.callback = self.button_callback
            self.add_item(button)

    async def button_callback(self, interaction: discord.Interaction):
        try:
            role_id = int(interaction.data['custom_id'])
            # 直接从服务器获取角色对象,不再依赖临时列表
            role = interaction.guild.get_role(role_id)

            if role:
                if role in interaction.user.roles:
                    await interaction.user.remove_roles(role)
                    await interaction.response.send_message(f"O cargo '{role.name}' foi removido!", ephemeral=True)
                else:
                    await interaction.user.add_roles(role)
                    await interaction.response.send_message(f"O cargo '{role.name}' foi adicionado!", ephemeral=True)

                # 仅存储角色ID与名称(名称可选,核心用ID)
                with open(self.file_path, 'r') as f:
                    data = json.load(f)
                data[str(role_id)] = role.name
                with open(self.file_path, 'w') as f:
                    json.dump(data, f)

        except Exception as e:
            logging.error(f'Erro no callback: {e}')

3. 调整创建面板的逻辑

在_roles命令中,创建视图后调用add_role_buttons添加按钮:

# 创建视图并添加按钮
view = Self_Roles(self.file_path)
view.add_role_buttons(list_roles)
await channel.send(embed=eRole, view=view)

4. 确保机器人重启后加载数据

在Cog的__init__中确保数据文件夹存在,并加载存储的角色数据:

def __init__(self, bot: commands.Bot):
    self.bot = bot
    self.data_folder = os.path.join(os.getcwd(), 'data')  
    self.file_path = os.path.join(self.data_folder, 'persistent_role.json')  

    # 确保data文件夹存在
    if not os.path.exists(self.data_folder):
        os.makedirs(self.data_folder)
    # 初始化JSON文件
    if not os.path.exists(self.file_path):
        with open(self.file_path, 'w') as f:
            json.dump({}, f)
    # 加载存储的角色数据
    self.load_persistent_roles()

修改后的完整代码

import os
import json
import discord
from discord import app_commands
from discord.ext import commands
import logging

class AddRolesCog(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot
        self.data_folder = os.path.join(os.getcwd(), 'data')  
        self.file_path = os.path.join(self.data_folder, 'persistent_role.json')  

        # 确保data文件夹存在
        if not os.path.exists(self.data_folder):
            os.makedirs(self.data_folder)
        # 初始化JSON文件
        if not os.path.exists(self.file_path):
            with open(self.file_path, 'w') as f:
                json.dump({}, f)
        # 加载存储的角色数据
        self.load_persistent_roles()

    async def setup_hook(self) -> None:
        # 注册持久化视图类
        self.bot.add_view(Self_Roles(self.file_path))

    def load_persistent_roles(self):
        with open(self.file_path, 'r') as f:
            self.persistent_roles = json.load(f)

    @app_commands.command(name=app_commands.locale_str('add_roles', category='Moderation'), description='Create action roles.')
    @app_commands.default_permissions(mute_members=True)
    async def _roles(
        self, 
        interaction: discord.Interaction,
        channel: discord.TextChannel,
        role_1: discord.Role, 
        role_2: discord.Role = None, 
        role_3: discord.Role = None, 
        role_4: discord.Role = None, 
        role_5: discord.Role = None,
    ):
        try: 
            list_roles = [role for role in [role_1, role_2, role_3, role_4, role_5] if role is not None]
            await interaction.response.send_message(f'Painel de cargos criado com sucesso em {channel.mention}', ephemeral=True)
            
            eRole = discord.Embed(
                title="Pegue seu cargo!",
                description="Clique nos botões abaixo para adicionar ou remover os cargos desejados.",
                color=discord.Color.random()
            )

            # 更新JSON存储的角色信息
            for role in list_roles:
                self.persistent_roles[str(role.id)] = role.name
            with open(self.file_path, 'w') as f:
                json.dump(self.persistent_roles, f)

            # 创建视图并添加按钮
            view = Self_Roles(self.file_path)
            view.add_role_buttons(list_roles)
            await channel.send(embed=eRole, view=view)

        except Exception as e:
            logging.error(str(e))

class Self_Roles(discord.ui.View):
    def __init__(self, file_path):
        super().__init__(timeout=None)
        self.file_path = file_path

    def add_role_buttons(self, roles):
        try:
            for role in roles:
                button = discord.ui.Button(label=role.name, style=discord.ButtonStyle.primary, custom_id=str(role.id))
                button.callback = self.button_callback
                self.add_item(button)
        except Exception as e:
            print(f'Erro no for: {e}')

    async def button_callback(self, interaction: discord.Interaction):
        try:
            role_id = int(interaction.data['custom_id'])
            # 从服务器获取角色对象
            role = interaction.guild.get_role(role_id)

            if role:
                if role in interaction.user.roles:
                    await interaction.user.remove_roles(role)
                    await interaction.response.send_message(f"O cargo '{role.name}' foi removido!", ephemeral=True)
                else:
                    await interaction.user.add_roles(role)
                    await interaction.response.send_message(f"O cargo '{role.name}' foi adicionado!", ephemeral=True)

                # 更新存储的角色名称(可选)
                with open(self.file_path, 'r') as f:
                    data = json.load(f)
                data[str(role_id)] = role.name
                with open(self.file_path, 'w') as f:
                    json.dump(data, f)

        except Exception as e:
            logging.error(f'Erro no callback: {e}')

async def setup(bot: commands.Bot):
    await bot.add_cog(AddRolesCog(bot))

内容的提问来源于stack exchange,提问作者Eduardo Viana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:39:59