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

能否在discord.py中创建不依赖View的持久化按钮?

问题描述

我手动定义了一个按钮类,希望在创建时直接设置label、style和custom_id参数:

class But(Button):
    def __init__(self,label,style,id):
        super().__init__(label=label,style=style, custom_id=id)
    
    async def callback(self,interaction):
        pass

官方示例只展示了创建持久化View的方法,但我想知道有没有办法创建不嵌套在View里的持久化按钮?

另外我尝试过创建持久化View并添加自定义按钮,但发现只有View内部用装饰器定义的按钮能保持持久化,手动添加的按钮不行:

async def setup_hook(self) -> None:
        self.add_view(persist())
class persist(View):
    def __init__(self):
        super().__init__(timeout=None)

    @discord.ui.button(label="Test persistence", style=discord.ButtonStyle.red,custom_id="persistent_view")
    async def callback(self,interaction,button):
        await interaction.response.send_message("Persists!")
view = persist()
button1 = But(label=label,style=style, id='1')# 从 slash 命令获取 style 和 label
view.add_item(button1)
await interaction.response.send_message("text",view=view)

解答

1. 不存在脱离View的持久化按钮

在discord.py的交互体系里,所有组件(包括按钮)必须依附于View才能实现持久化。Discord官方的交互协议本身就要求组件属于某个View容器,所以单独的持久化按钮无法实现,必须把按钮放到设置了timeout=None的View中。

2. 手动添加的按钮无法持久化的原因及解决方法

你遇到的问题核心是:手动添加的自定义按钮没有被bot的持久化视图系统正确识别。官方示例中用装饰器定义的按钮会自动和View绑定,而你动态添加的按钮需要满足两个条件才能持久化:

  • 按钮的custom_id必须唯一且可被bot重启后识别
  • 承载该按钮的View必须在bot启动时通过add_view完成注册,且该View能处理对应custom_id的交互

解决方法:通用持久化View承载动态按钮

创建一个通用的持久化View,用来承载所有动态生成的按钮,并在bot启动时注册这个View,然后在回调中根据custom_id处理不同逻辑:

# 定义通用持久化View
class PersistentDynamicView(View):
    def __init__(self):
        super().__init__(timeout=None)

    # 处理所有自定义按钮的交互
    async def interaction_check(self, interaction: discord.Interaction) -> bool:
        # 可在此添加权限检查等逻辑,默认返回True允许交互
        return True

# 在bot启动钩子中注册这个View
async def setup_hook(self) -> None:
    self.add_view(PersistentDynamicView())

# 在slash命令中创建并发送动态按钮
@bot.tree.command()
async def create_custom_button(interaction: discord.Interaction, label: str, style: int):
    # 转换输入的style为合法的ButtonStyle枚举
    try:
        button_style = discord.ButtonStyle(style)
    except ValueError:
        await interaction.response.send_message("无效的按钮样式编号")
        return
    
    # 创建自定义按钮,这里用带前缀的custom_id方便后续识别
    custom_button = But(label=label, style=button_style, id=f"custom_btn_{interaction.user.id}_{int(time.time())}")
    
    # 创建通用View并添加按钮
    view = PersistentDynamicView()
    view.add_item(custom_button)
    await interaction.response.send_message("已创建持久化按钮", view=view)

# 处理自定义按钮的回调(可以写在But类里,也可以在View中统一处理)
class But(Button):
    def __init__(self,label,style,id):
        super().__init__(label=label,style=style, custom_id=id)
    
    async def callback(self,interaction):
        # 根据custom_id执行对应逻辑
        if self.custom_id.startswith("custom_btn_"):
            await interaction.response.send_message(f"你点击了按钮:{self.label}")

关键说明

  • 必须确保bot启动时注册的View能覆盖所有可能的动态按钮custom_id,要么通过View的interaction_check统一拦截,要么让按钮自身的callback逻辑能被识别。
  • 如果需要bot重启后仍能处理历史发送的动态按钮,要确保custom_id的规则可预测,且对应的回调逻辑在重启后依然存在。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 22:10:11