pythonpython-3.xdiscord.pypycorddisnake

disable a button after being used


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


Solution

  • 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