Search code examples
pythonpython-3.xbuttondiscord.pydiscord-interactions

Discord.py disabling buttons after they have been clicked


I have two buttons which I want them both to be disabled on the original message it was clicked on after one of them has been clicked. Even better than just disabling them I would like them to be removed from the original message, but that isn't something I need.

My current code:

import discord
from discord.ext import commands
from discord.ui import Button, button, View

# Class
class MyView(View):
    def __init__(self):
        super().__init__(timeout=None)

    @button(label="Hello!", style=discord.ButtonStyle.blurple)
    async def hello(self, interaction: discord.Interaction, button: Button):
        button.disabled=True
        embedHelloButton=discord.Embed(title="Hi There!", color=0x5865F2)
        embedHelloButton.set_author(name=f"Requested by {interaction.user.name}", icon_url=interaction.user.avatar.url)
        await interaction.response.send_message(embed=embedHelloButton, ephemeral=False)
    
    @button(label="No. Bye.", style=discord.ButtonStyle.red)
    async def bye(self, interaction: discord.Interaction, button: Button):
        button.disabled=True
        embedByeButton=discord.Embed(title="Bye :(", color=0xff0000)
        embedByeButton.set_author(name=f"Requested by {interaction.user.name}", icon_url=interaction.user.avatar.url)
        await interaction.response.send_message(embed=embedByeButton, ephemeral=False)

# Command
@client.hybrid_command(name="hello", description="Say hello to the bot!")
async def hello(ctx):
    embedHello=discord.Embed(title="Say hello to me!", color=0xff0000)
    embedHello.set_author(name=f"Requested by {ctx.author.name}", icon_url=ctx.author.avatar.url)
    await ctx.send(embed=embedHello, view=MyView())

My Output: The output of my code

The buttons on the first message are still clickable and I want the buttons to either be disabled on that first message or for them to be removed completely. Any solutions?


Solution

  • You could simply make these changes to your view class to disable both buttons

    class MyView(View):
      # do what you just did
      @button(label="Hello!", style=discord.ButtonStyle.blurple)
      async def hello(self, interaction: discord.Interaction, button: Button):
        # do sth
        # example to disable both buttons by clicking 'hello' button
        button.disabled = True  # disable the 'hello' button
        bye_btn = discord.utils.get(self.children, label="No. Bye.")  # get the 'bye' button
        bye_btn.disabled = True
        # send a msg or sth else
    

    If you would like to remove all buttons, it is even easier than disabling buttons

    class MyView(View):
      @button(label="Hello!", style=discord.ButtonStyle.blurple)
      async def hello(self, interaction: discord.Interaction, button: Button):
        # do sth
        await interaction.response.edit_message(view=None)  # this will remove all the components in the class
    

    And I would suggest changing your slash command slightly

    @client.hybrid_command(name="hello", description="Say hello to the bot!")
    async def hello(ctx):
        # do your embed stuff
        # the good way to do this is that you could access your message (in your view class) by 'self.message'
        # because functions like 'on_timeout' does not have 'interaction' param.
        view = MyView()
        view.message = await ctx.send(embed=embedHello, view=view)
    

    And just as a quick reminder, you could only respond to the interaction once to avoid triggering interaction responded exception. So, if you would like to send a message and edit the view, you can switch to followup

    class MyView(View):
      @button(label="Hello!", style=discord.ButtonStyle.blurple)
      async def hello(self, interaction: discord.Interaction, button: Button):
        await interaction.response.defer()
        # do sth
        await interaction.followup.send(content='...', ...)  # send the message you want
        await interaction.followup.edit_message(message_id=self.message.id, view=None)  # message_id is mandatory here
    

    Hope this would help.