我有一个用于确认的视图类,我在多个命令中使用它,但行为很奇怪,就像有时当我在响应消息上使用命令按钮时已经被禁用,有时当我单击/使用按钮时,即使在它调用 on_timeout 并编辑之后我的消息超时了。
这是我的视图类:
confirm = Button(label="Confirm", emoji=tick, style=discord.ButtonStyle.green, custom_id="confirm")
class ConfirmView(View):
def __init__(self, ctx, timeout):
self.ctx = ctx
super().__init__(timeout=timeout)
async def on_timeout(self) -> None:
for i in self.children:
i.disabled = True
await self.message.edit(content="Timeout You took too long to respond!", view=None)
async def interaction_check(self, interaction) -> bool:
if interaction.user.id == self.ctx.author.id:
return True
await interaction.response.send_message("You cannot interact with this view.", ephemeral=True)
return False
class Cancel(Button):
def __init__(self):
super().__init__(label="Cancel", emoji=cross, style=discord.ButtonStyle.red, custom_id="cancel")
async def callback(self, interaction):
await interaction.response.edit_message(content="Cancelled!", view=None)
cancel = Cancel()
这是我的命令示例:
@client.command()
async def my_command_1(ctx):
async def callback(interaction):
# do my stuff here
view = ConfirmView(ctx=ctx, timeout=30)
view.add_item(confirm)
view.add_item(cancel)
confirm.callback = callback
view.message = await ctx.send("One", view=view)
@client.command()
async def my_command_2(ctx):
async def callback(interaction):
# do my stuff here
view = ConfirmView(ctx=ctx, timeout=30)
view.add_item(confirm)
view.add_item(cancel)
confirm.callback = callback
view.message = await ctx.send("Two", view=view)
@client.command()
async def my_command_3(ctx):
async def callback(interaction):
# do my stuff here
view = ConfirmView(ctx=ctx, timeout=30)
view.add_item(confirm)
view.add_item(cancel)
confirm.callback = callback
view.message = await ctx.send("Three", view=view)
您提到的问题有两个;单击按钮后超时并且按钮被禁用。
发生这种情况是因为视图 timeout 的工作方式。从文档来看,
在不再接受输入之前与 UI 的上次交互的超时时间(以秒为单位)。
禁用 on_timeout 事件的一种方法是使用 stop 方法一起停止监听视图中的交互事件。在您的回调中,
async def callback(self, interaction):
self.view.stop() # <-
await interaction.response.edit_message(content="Cancelled!", view=None)
您的代码的逻辑似乎有点不对劲。您将所有按钮设置为禁用,然后编辑消息以使其根本没有任何按钮?
你所说的“行为怪异”其实是
假设您运行command_1,并让它超时。然后运行command_2,按钮将被禁用。但您会注意到,重新启动后它们将不再被禁用。
为了进一步理解这一点,我们必须研究面向对象编程的工作原理。
当您使用
add_item
添加这些按钮时,这些按钮将成为视图子级的一部分。在 on_timeout
中,您将所有子项设置为禁用。这意外地也导致按钮 cancel
和 confirm
保留属性 disabled=True
。
所以现在,当您再次使用该类时,按钮将被禁用,直到您重新启动代码,所有内容都会再次启用。
您可以通过添加打印语句来看到这一点。
@client.command()
async def command_2(ctx):
async def callback(interaction):
...
print(cancel.disabled) # <- prints "True"
view = ConfirmView(ctx=ctx, timeout=5)
view.add_item(confirm)
view.add_item(cancel)
confirm.callback = callback
view.message = await ctx.send("Two", view=view)
运行 command_1,让它超时,然后运行 command_2,将显示
disabled
变量本身等于 True。为了避免这种情况,您可以创建按钮的新实例。
cancel