能否在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
相关产品推荐
相关产品推荐

