disable a button after being used

Question:

Recently i decided to rewrite my discord bot and add buttons also.
the main problem i encountered about this so far, i can’t a disable a button just after being pressed people told be about button.disabled=True and in deed, it will disabling the button, but it’s just sending it disabled, so it can’t never be pressed. What i want is to be able to click it and do it’s thing and then disable it.

As a reference i’ll put some of the code

I use disnake, a discord.py fork, it does have the same syntaxes as dpy but we have buttons and slash commands, dropdown menus, etc

class BlurpleButton(Button):
    def __init__(self, label, emoji=None):
        super().__init__(label=label, style=discord.ButtonStyle.blurple, emoji=emoji)

this is to use easier the buttons, i created a template and i can use it on any command

class CustomView(View):
    def __init__(self, member: disnake.Member):
        self.member = member
        super().__init__(timeout=180)

    async def interaction_check(self, inter: disnake.MessageInteraction) -> bool:
        if inter.author != self.member:
            await inter.response.send_message(content="You don't have permission to press this button.", ephemeral=True)
            return False
        return True

and this is for buttons being able to be pressed just by a mentioned member for example if i do /test @member (i migrated to slash commands due to discord new privileged intent) just the member will be able to press it and no one else.

So far so good all working fine, now after we "assemble" this in a command

@commands.slash_command(description='test')
    async def test(self, inter):

         (do stuff in there)
         . . .
        button1 = BlurpleButton("Button name")
        view=CustomView(member)
        view.add_item(button1)

        async def button_callback(inter):
            await inter.send(embed=embedname2)

        button1.callback = button_callback
        await inter.send(embed=embed1, view=view)

Now again, this piece of code it’s doing what it’s intended to do, sends an embed (let’s just say where i put the . . . are few embeds) and attached to that embed we have button1 when it’s clicked it sends embedname2 and there is where things are not working anymore, i keep trying in any ways after the embedname2 it’s being sent, the button to disable itself by being clicked one time if i add button1.disabled=True in the callback, the button it will just be sent disabled without any possibility of being clicked. The main reason i put the callback inside the command is to be able to use embeds when the button triggers, if i put it in the subclassed button or view i can’t do that anymore.

So this is my whole problem, if you know a better resolving to thing that includes the embed using and just members can press the button, please tell me, i have over a week trying to solve this and i can’t get it right

Asked By: Ares

||

Answers:

I tend to, and would recommend, creating new classes for each view callback. This way I can deal with multiple buttons easier and in a way that is easier to read. Feel free to edit my code accordingly, I’ll leave comments as directions

To start, give your BlurpleButton a custom ID, or let the user define one inside the class

(label=label, style=discord.ButtonStyle.blurple, emoji=emoji, custom_id="bttn1") # bttn1 is your custom ID, you'll use it to select this button from the view when disabling it or modifying its contents

In you callback, try this code:

b = [x for x in self.children if x.custom_id == "bttn1"][0] # Selects the blurpleButton from the view's buttons based on the custom_id
b.disabled = True # Disable

Then finish it off by editing your message, with this new view

await inter.response.edit_message(view=self) # Edit that the buttons are attached to, but replacing the view with the new one containing our edits (disabling the button)
Answered By: Trent112232

I use Pycord, not Disnake, but this should work. You might have to change the exact method call here or in your button callback.

async def interaction_check(self, inter: disnake.MessageInteraction) -> bool:
    if inter.author != self.member:
        await inter.response.send_message(content="You don't have permission to press this button.", ephemeral=True)
        return False

    # The interaction is allowed, so let's disable the button.
    # Interactions have a data field that stores the custom ID of the
    # component sent the interaction. We will use this to find our button
    # in the view.

    button_id = iter.data["custom_id"]
    [child for child in self.children if child.custom_id == button_id][0].disabled = True
    await inter.edit_original_message(view=self)

    # From this point on, you will be dealing with inter.followup, since
    # an interaction can only be responded to once.
    
    return True
Answered By: tiltowait

Explanation

You can accomplish this by setting button.disabled to True in your button’s callback. Then, you also would need to edit your original message to reflect this change.

Code

    @commands.slash_command(description='test')
    async def test(self, slash_inter: disnake.ApplicationCommandInteraction, member: disnake.Member):

        view = CustomView(member)
        button1 = BlurpleButton("TEST")
        view.add_item(button1)

        async def button_callback(button_inter: disnake.MessageInteraction):
            button1.disabled = True
            await button_inter.send(embed=embedname2)
            await slash_inter.edit_original_message(view=view)

        button1.callback = button_callback

        await slash_inter.send(embed=embed1, view=view)

Note: For /test @member to work, you need to add a disnake.Member parameter to your slash command.

Reference

disnake.MessageInteraction

disnake.ui.Button.callback

disnake slash commands

Answered By: TheFungusAmongUs
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.