#All buttons fail

1 messages · Page 1 of 1 (latest)

mossy bloom
#

What am I doing wrong here that none of my buttons work? They all just say interaction failed.

light pivotBOT
#

Hey! Once your issue is solved, press the button below to close this thread!

mossy bloom
livid bolt
#

i think it cant see your component callbacks since theyre in a class. It may be worth moving everything in that class to an extension, and then loading the extension

#

or alternatively just taking all the interactions parts out of the class

#

actually a lot of stuff is weird here

#
# template.py

from interactions import (
    Button,
    ButtonStyle,
    Client,
    ComponentContext,
    Extension,
    component_callback,
    slash_command,
    SlashContext,
    Embed
    )

class Template(Extension):
    def __init__(self, client: Client):
        self.client = client

    @slash_command(
        name="template-command",
        description="Just a template command",
    )
    async def template_command(self, ctx: SlashContext):
        button = Button(style=ButtonStyle.BLUE, label="Test Button", custom_id="test_button")
        await ctx.send(embed=Embed(description=f"Sent from a template command!"), components=button)

    # Put component callbacks here
    @component_callback("test_button")
    async def test_button_callback(self, ctx: ComponentContext):
        await ctx.send("Responding to the test button!")

class Game:
    # Put your game stuff here

    def __init__(self) -> None:
        pass
    
def setup(bot):
    # This is called by interactions.py so it knows how to load the Extension
    Template(bot)

Try use this kind of structure, then load the file as an extension. It shouldnt need your on_socket_event or handle button click functions

mossy bloom
#

I don't know much if anything about extensions but I'll give it a try. If it doesn't need to handle button clicks that's great!

#

Thank you!

mossy bloom
#

Hello smart people, I have returned.

I'm trying to get the custom_id working for the buttons here to be assigned to whichever map they are on, but it always just ends up with interaction failed. Even the print statements don't fire signaling that it ain't working. How can I do this?

class Main:
    @component_callback()
    async def guess_callback(self, ctx: ComponentContext):
        if not ctx.component.custom_id.startswith("guess_"):
            return

        game = game_instances[ctx.guild.id]
        map_guess = ctx.component.custom_id.split('guess_', 1)[1]
        print(f"Received guess: {map_guess}")
        await game.handle_guess(ctx.author.id, map_guess)
        await game.update_message(ctx.author.id, ctx)

class Game:
      def update_message()
        components = spread_to_rows(
            Button(style=ButtonStyle.GREEN, label="Start New Game", custom_id="start", disabled=(self.game_status != "idle")),
            Button(style=ButtonStyle.RED, label="Leave Game", custom_id="leave"),
            *[
                Button(style=ButtonStyle.SECONDARY, label=f"{map_name}", custom_id=f"guess_{map_name}", disabled=(self.current_round is None or player_id in self.current_round["guesses"]))
                for map_name in maps
            ]
        )
#

I've also tried things like this:

class Main(Extension):
    def __init__(self, client: Client):
        self.client = client

        # Generate callback for all maps
        for i, map_name in enumerate(maps):
            self.make_guess_callback(i, map_name)

    def make_guess_callback(self, map_id, map_name):
        @component_callback(f"guess_{map_id}")
        async def guess_callback(self, ctx: ComponentContext):
            game = game_instances[ctx.guild.id]
            map_guess = maps[int(ctx.component.custom_id.split('guess_', 1)[1])]
            print(f"Received guess: {map_guess}")  # Add this line
            await game.handle_guess(ctx.author.id, map_guess)
            await game.update_message(ctx.author.id, ctx)
        setattr(self, f"guess_{map_id}_callback", guess_callback)

class Game:
    # ...

    async def update_message(self, player_id, ctx=ComponentContext):
        # ...

        components = spread_to_rows(
            Button(style=ButtonStyle.GREEN, label="Start New Game", custom_id="start", disabled=(self.game_status != "idle")),
            Button(style=ButtonStyle.RED, label="Leave Game", custom_id="leave"),
            *[
                Button(style=ButtonStyle.SECONDARY, label=f"{map_name}", custom_id=f"guess_{i}", disabled=(self.current_round is None or player_id in self.current_round["guesses"]))
                for i, map_name in enumerate(maps)
            ]
        )

        # ...

mossy bloom
#

@livid bolt Sorry to bug you again mate, but if you have a few seconds could you take a look at this?

#

To clarify further, I have a table of names, and for each of the names it creates a new button. The issue is that the custom_id's don't like this system of having one callback to cover multiple buttons and handle them properly. Essentially the end result should be that if the random map chosen matches the user's input then it should add points.

finite badge
#

its not @component_callback that you are trying to use here its @listen

mossy bloom
#

But @component_callback is what is used for buttons no? I use @component_callback for all of the other buttons and it works (but it does say interaction failed all the time)

finite badge
#

i use @listen and i prefer using it as you can put multiple button callbacks into it

light pivotBOT
# finite badge https://github.com/LarssJakobsons/Larss_Bot/blob/1b3c4b894f9dc865397b600fe01e698...
LarssJakobsons/Larss_Bot
@listen()
async def on_component(ctx: ComponentContext):
    event = ctx.ctx
    if event.custom_id == "contributors":
        if event.channel.id not in channel_cooldown:
            embed = Embed(
                title="⭐ Contributors",
                description=f"Awesome people who have helped to make Larss_Bot what it is today!",
                timestamp=datetime.utcnow(),
                color=Color.from_hex("5e50d4"),
            )
            embed.set_footer(
                text=f"Requested by {event.author.display_name}", icon_url=event.author.avatar.url
            )

            value = ""
            for contributor in epiccontribbutingppl:
                value += f"> <@{contributor}> \n"

            embed.add_field(
                name="Contributors",
                value=value,
            )

            value = ""
            for lilhelper in lilhelpers:
                value += f"> <@{lilhelper}> \n"

            embed.add_field(
                name="Little helpers",
                value=value,
            )
            await event.send(embed=embed)
            channel_cooldown.append(event.channel.id)
            await asyncio.sleep(15)
            channel_cooldown.remove(event.channel.id)
        else:
            await event.send(
                "Looks like someone already pressed the button. No need to do it again.",
                ephemeral=True,
            )

    if event.custom_id == "guildsinvites":
        if event.channel.id not in invite_cooldown:
            embed = Embed(
                title="Partner servers",
                description=f"Press any of the buttons below to get invited to one of the partnered servers",
                timestamp=datetime.utcnow(),
                color=Color.from_hex("5e50d4"),
            )
            embed.set_footer(
                text=f"Requested by {event.author.display_name}", icon_url=event.author.avatar.url
            )

            btn1 = Button(
                style=ButtonStyle.URL,
                label="Dev lab",
                emoji="🧪",
                url="https://discord.gg/TReMEyBQsh",
            )
            btn2 = Button(
                style=ButtonStyle.URL,
                label="Big-Floppa software solutions",
                emoji="🐱",
                url="https://discord.gg/MDVcb8wdUd",
            )
            btn3 = Button(
                style=ButtonStyle.URL,
                label="Tyler army",
                emoji="🐸",
                url="https://discord.gg/w78rcjW8ck",
            )
            components: list[ActionRow] = spread_to_rows(
                btn1,
                btn2,
                btn3,
            )

            await event.send(embed=embed, components=components)
            channel_cooldown.append(event.channel.id)
            await asyncio.sleep(15)
            channel_cooldown.remove(event.channel.id)
        else:
            # ephemeral not gonna work, once again
            await event.send(
                "Looks like someone already pressed the button. No need to do it again.",
                ephemeral=True,
            )
mossy bloom
#

Let me try that, thanks

mossy bloom
#

@finite badge Now I get the problem of this:

event = ctx.ctx
AttributeError: 'Main' object has no attribute 'ctx'

File "/root/TTS/game.py", line 79, in on_component
if ctx.custom_id.startswith("guess_"):
AttributeError: 'Main' object has no attribute 'custom_id'

finite badge
#

can you send your code please

mossy bloom
#

I reverted back to something more readable. You can focus on the update_message funciton and make_guess_callback.

#

I apologize for the mess. I first make a giant pile of spagette and then clean it up once I get something working.

finite badge
#

its ctx.ctx.custom_id

mossy bloom
#

why two?

finite badge
#

because the first ctx isnt an object

#

i dunno i cant really explain it

#

might want to ask polls or someone else that has developed this lib

mossy bloom
# finite badge its `ctx.ctx.custom_id`

Alas that did not work:

 File "/root/TTS/game.py", line 79, in on_component
if ctx.ctx.custom_id.startswith("guess_"):
AttributeError: 'Main' object has no attribute 'ctx'
mossy bloom
#

@finite badge

livid bolt
#

on_component returns a component object which contains ctx, but i dont think theres any point using @listen instead of a component callback for your use case as far as i understand it

#

oh wait nvm it does look kind of useful here

#

@mossy bloom you can get rid of ```py

Generate callback for all maps

for i, map_name in enumerate(maps):
self.make_guess_callback(i, map_name)```
from your init and change

def make_guess_callback(self, map_id, map_name):
        @listen()
        async def on_component(ctx: ComponentContext):
            if ctx.component.custom_id == f"guess_{map_id}":
                game = game_instances[ctx.guild.id]
                map_guess = maps[int(ctx.component.custom_id.split('guess_', 1)[1])]
                print(f"Received guess: {map_guess}")
                await game.handle_guess(ctx.author.id, map_guess)
                await game.update_message(ctx.author.id, ctx)
        setattr(self, f"guess_{map_id}_callback", on_component)

to ```py
@listen("on_component")
async def make_guess_callback(self, component):

ignore non guess buttons

if not component.ctx.custom_id.startswith("guess"):
return

map_id = component.ctx.custom_id.split("_")[1]
game = game_instances[component.ctx.guild.id]
map_guess = maps[int(map_id)]
print(f"Received guess: {map_guess}")
await game.handle_guess(component.ctx.author.id, map_guess)
await game.update_message(component.ctx.author.id, ctx)

#

idrk whats happening with the game instances and maps stuff so might need to massage the code a bit to make it work

mossy bloom
#

Thank you! I'll give that a whirl and see what happens :)

finite badge
mossy bloom
mossy bloom
#

@livid bolt Any clue how I can fix the interaction failed message for everything?

livid bolt
mossy bloom
#

But I am, I'm editting the message

livid bolt
#

is it being edited successfully?

mossy bloom
#

Sort of

#

Yes

livid bolt
#

can u send the bit where u edit the message

mossy bloom
#
    async def update_message(self, player_id, ctx: ComponentContext):
        # Load the scores before updating the message
        self.load_scores()

        if self.message is None and ctx is not None:
            self.message = await ctx.send("Starting game...")
        
        leaderboard_text = '\n'.join([f"<@{player}>: {score}" for player, score in sorted(self.players.items(), key=lambda x: x[1], reverse=True)])

        components = spread_to_rows(
            Button(style=ButtonStyle.GREEN, label="Start New Game", custom_id="start", disabled=(self.game_status != "idle")),
            Button(style=ButtonStyle.RED, label="Leave Game", custom_id="leave"),
            *[
                Button(style=ButtonStyle.SECONDARY, label=f"{map_name}", custom_id=f"guess_{i}", disabled=(self.current_round is None or player_id in self.current_round["guesses"]))
                for i, map_name in enumerate(maps)
            ]
        )

        if self.current_round is not None:
            with open(os.path.join(self.image_dir, self.current_round["image_file"]), "rb") as img_file:
                file = File(img_file, file_name=self.current_round["image_file"])
                await self.message.edit(content=leaderboard_text, components=components, files=[file])
        else:
            await self.message.edit(content=f"Not Functioning", components=components)

#
        game = game_instances[ctx.guild.id]
        initial_message = "Welcome to Geoguesser! Click 'Start Game' to begin a new game, or 'Join Game' to join an existing game. You can also view the leaderboard."
        components = [
            ActionRow(
                Button(style=ButtonStyle.GREEN, label="Start Game", custom_id="start"),
                Button(style=ButtonStyle.GREEN, label="Join Game", custom_id="join"),
                Button(style=ButtonStyle.BLURPLE, label="Leaderboard", custom_id="leaderboard"),
            )
        ]
        await game.message.edit(content=initial_message, components=components)
livid bolt
#

oh

#

when u edit the message it doesnt count as being a response to the interaction cause its not through the interactions context

#

so u gotta use ctx.edit_origin() instead

#

or ctx.edit() if its not from a button

mossy bloom
#

gotcha, let me try that

#

I get this back: await self.message.edit_origin(content=f"Not Functioning", components=components) AttributeError: 'Message' object has no attribute 'edit_origin'

livid bolt
#

no you cant do it from the message

#

use ctx

#

ctx.edit_origin

mossy bloom
# livid bolt `ctx.edit_origin`

Ignoring exception in Component Callback for start: HTTPException: 400|Bad Request || Interaction has already been acknowledged.

#

It can only edit once it seems

livid bolt
#

yeah only once per interaction

#

after the first u need to edit the message like how u were before

mossy bloom
#

So I understand correctly, I only edit the message once with edit_origin? But wouldn't the other buttons then just have the interaction error still?

livid bolt
#

So you must be editing multiple times per button press

#

Or just responding generally multiple times

#

If that’s the case then u can avoid the error by responding only once, and then editing the message separately afterward