1

I am brand new to using discord buttons. This command is just a test command to see how everything works. My problem is that the buttons work but I can only press them once. after I press them once and then press them again I get the error at the bottom of the button saying "This interaction failed". how could I have the buttons be pressed as much as the user wants to? My Code is below. Any help would be great!

Code:

@commands.command()
async def button(self, ctx):
  await ctx.send("This is a button test", components=[Button(style=ButtonStyle.blue, label="Btn1"), Button(style=ButtonStyle.red, label="Btn2"), Button(style=ButtonStyle.grey, label="Btn3")])

  res = await self.client.wait_for("button_click")
  if res.channel == ctx.channel:
    await res.respond(type=InteractionType.ChannelMessageWithSource, content=f"{res.component.label} has been clicked! this is button 1")

  res2 = await self.client.wait_for("button_click")
  if res2.channel == ctx.channel:
    await res2.respond(type=InteractionType.ChannelMessageWithSource, content=f"{res2.component.label} has been clicked! this is button 2")

  res3 = await self.client.wait_for("button_click")
  if res3.channel == ctx.channel:
    await res3.respond(type=InteractionType.ChannelMessageWithSource, content=f"{res3.component.label} has been clicked! this is button 3")

2 Answers 2

4

It's possible to create an on_button_click listener.

The following code gives an example how to get it working

import discord
import settings
from discord_components import DiscordComponents, Button

class MyClient(discord.Client):
    async def on_ready(self):
        buttons = [Button(label="button 1", custom_id="1"), Button(label="button 2", custom_id="2"),
                   Button(label="button 3", custom_id="3")]

        channel = self.get_channel(settings.DISCORD_ROLEBOT_SETTINGS_CHANNEL)
        await channel.send("test", components=buttons)

    async def on_button_click(self, interaction):
        await interaction.respond(content=f"you clicked button {interaction.component.custom_id}")


if __name__ == "__main__":
    client = MyClient()
    DiscordComponents(client)
    client.run(settings.DISCORD_TOKEN)

1

It seems like you used this code example.

Explanation

It looks like you have a misconception on how wait_for operates.

The method wait_for will return, once the bot has received an event specified in the arguments. It will not return before this event was received.

This means your code will not return from your first statement until you click a button. You can observe this behaviour by clicking your buttons out of order. You will e.g. receive the chat message Btn3 has been clicked! this is button 1

From this explanation you see why that your 3 wait_for statements (in series) only allow you to press any buttons 3 times before the method returns, resulting in failed interactions for further button presses.

Solution

As the wait_for method does not differentiate between the different buttons, you can simply call a single wait_for method within a (infinite) while-loop and detect the pressed button by accessing res.component.label.

while True:
    res = await bot.wait_for("button_click")
    if res.channel == ctx.channel:
        await res.respond(type=InteractionType.ChannelMessageWithSource, content=f"{res.component.label} has been clicked!")
        # do more stuff
        # maybe break

Further Improvement

As described in the linked wiki, you should use the check= argument when using wait_for. This allows you to filter for specific labelled Buttons which will get usefull for big bots running mutliple components at the same time.

You can aswell add the timeout= argument to wait_for in order to return if the user didn't press any buttons after a certain time. If the timeout is reached, an exception is raised.

2
  • Thank you for giving me all of that. It didn't quite work tho. I have while statements for each res. I can now click them as much as I want to but it only puts out the first answer which is {button} has been clicked! this is button 1, for each click of the button. I haven't looked at the doc but I plan to. If you could help with anything that would be great! Commented Jun 28, 2021 at 2:41
  • Your code will never leave the first while statement, as it waits for the event. Therfore, the 2 other statements will never be executed. This behaviour is fine, as you do not need more than 1 while statement, as the events of all 3 buttons will be received by this single loop. You can then add some if-conditions within the loop, to determin the clicked button, using its label or id.
    – C. Mayer
    Commented Jun 28, 2021 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.