i have a view class for confirmation which i am using in multiple commands but is behaving weirdly like sometimes when i use command buttons on response message are already disaled and sometimes when i do click/use button, even after that it calls on_timeout and edit my message to timed out.
heres my view class:
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()
heres my command examples:
@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)
There are two problems that you have mentioned; timing out after clicking the button and buttons being disabled.
This occurs because of how the view timeout works. From the documentation,
The timeout in seconds from last interaction with the UI before no longer accepting input.
A way to disable on_timeout event, is to stop listening to interaction events in the view all together, with the stop method. In your callbacks,
async def callback(self, interaction):
self.view.stop() # <-
await interaction.response.edit_message(content="Cancelled!", view=None)
The logic from your code seems a bit off. You are setting all the buttons to be disabled, but then edit the message to not have any buttons at all?
The "acting weird" you're talking about is actually,
Say you run command_1, and let it timeout. Then run command_2, the buttons will be disabled. But you would notice that they won't be disabled anymore after restart.
To understand this further, we have to look into how object-oriented programming works.
When you add these buttons with add_item
, these buttons become part of the view's children. In the on_timeout
, you set all the children to be disabled. This unexpectedly also causes the buttons cancel
and confirm
to retain the attribute disabled=True
.
So now, when you use the class again, the buttons will be disabled, until you restart the code, where everything becomes enabled again.
You can see this by adding a print statement.
@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)
Running command_1, letting it timeout, then running command_2, would show that the disabled
is equal to True in the cancel
variable itself.
To avoid this, you can create a new instance of the buttons.
@client.command()
async def command_1(ctx):
async def callback(interaction):
...
view = ConfirmView(ctx=ctx, timeout=5)
confirm = discord.ui.Button(label="Confirm",
emoji="✅",
style=discord.ButtonStyle.green,
custom_id="confirm") # Or use a function to create this
cancel = Cancel()
view.add_item(confirm)
view.add_item(cancel)
confirm.callback = callback
view.message = await ctx.send("One", view=view)