Defxult/reactionmenu

ButtonsMenu - 400 Bad Request (error code: 50006): Cannot send an empty message

Arcanocito opened this issue · 13 comments

Hello, I restarted my PC and since then the Buttons on the Buttonmenu don't work anymore.
I get this error:

Exception in callback ButtonsMenu._done_callback(<Task finishe...pty message')>) handle: <Handle ButtonsMenu._done_callback(<Task finishe...pty message')>)> Traceback (most recent call last): File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\asyncio\events.py", line 80, in _run self._context.run(self._callback, *self._args) File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\reactionmenu\buttons_menu.py", line 624, in _done_callback task.result() File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\reactionmenu\buttons_menu.py", line 566, in _execute_interactive_session await inter.reply(type=ResponseType.DeferredUpdateMessage) File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\dislash\interactions\interaction.py", line 170, in reply return await self.channel.send( File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\dislash\slash_commands\_send_modifications.py", line 155, in send_with_components data = await send_message(state.http, channel.id, content, tts=tts, embed=embed, File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\http.py", line 254, in request raise HTTPException(r, data) discord.errors.HTTPException: 400 Bad Request (error code: 50006): Cannot send an empty message

I uninstalled and installed the latest versions of dislash and reactionmenu but the error still prevails :C I did not change any code previous to this happening so I really dont know how to tackle this issue...

(Also using "await menu.update(new_pages=[something], new_buttons=None)" deletes embed footers for me)

Tested more and the dynamic embed post a new page, in a new message, when I click a "next page" button. The static ones dont work at all :/

The .update() method removing footers was fixed recently with the latest Github update. Update to the latest Github version and lmk if it's still removing the footers. As for the other error, show your code please.

Ok, I further noticed that it is just the caller buttons that do not work for me, the exit button works fine, here is an example of one of the menus with caller buttons where it doesnt work:

`
try:

            #connect to database
            #dtbconnection = sqlite3.connect("Users.db")
            dtbconnection = mariadb.connect(user=dtbuser,password=dtbpw,host=dtbhost,port=dtbport,database=dtbdtb)
            cursor = dtbconnection.cursor()

            menu = ButtonsMenu(ctx, menu_type=ButtonsMenu.TypeEmbed, timeout=60, show_page_director=False, all_can_click=False,remove_buttons_on_timeout=True)
                
           #I am just filling the variables here
                
            #creating the buy menu embed
            emb_color = getcolor(ctx.author.id)
            embed=discord.Embed(title=" ", color=discord.Colour(int(emb_color, 0)))
            embed.set_author(name=f"{texts[f'{language}_show_buy_menu_embed_author'].format(ctx.author)}")
            embed.set_thumbnail(url=f"http://www.6m7.de/img/shopping-cart.png")
            embed.add_field(name="⠀⠀⠀⠀‎⠀‎‎⠀‎‎‎⠀", value=f"{texts[f'{language}_show_buy_menu_embed_field_0_value'].format(crystals)}", inline=False)
            if disclv == 99:
                embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_1_title']}", 
                                value=f"{texts[f'{language}_show_buy_menu_embed_field_1_value_1'].format(str(generatorlv).split('.',1)[0], generator_price, delta_ph_1gene, costdelta_ph_1gene, str(fuellv).split('.',1)[0], fuel_price, delta_ph_1fuel, costdelta_ph_1fuel, str(businesslv).split('.',1)[0], business_price, delta_ph_1bus, costdelta_ph_1bus, str(copierlv).split('.',1)[0], copier_price, delta_ph_1cop, costdelta_ph_1cop, str(pickaxes).split('.',1)[0], pickaxe_price, delta_pr_1pick, costdelta_pr_1pick, str(materiallv).split('.',1)[0], material_price, delta_pr_1mat, costdelta_pr_1mat)}", inline=False)
            else:
                embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_1_title']}", 
                                value=f"{texts[f'{language}_show_buy_menu_embed_field_1_value_2'].format(str(generatorlv).split('.',1)[0], generator_price, delta_ph_1gene, costdelta_ph_1gene, str(fuellv).split('.',1)[0], fuel_price, delta_ph_1fuel, costdelta_ph_1fuel, str(pickaxes).split('.',1)[0], pickaxe_price, delta_pr_1pick, costdelta_pr_1pick, str(materiallv).split('.',1)[0], material_price, delta_pr_1mat, costdelta_pr_1mat, str(disclv).split('.',1)[0], disc_price)}", inline=False)

            embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_2_title']}", 
                            value=f"{texts[f'{language}_show_buy_menu_embed_field_2_value'].format(pb_price, timeleft_str)}", inline=False) 

            embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_3_title']}",
                            value=f"{texts[f'{language}_show_buy_menu_embed_field_3_value'].format(level_discount_percentage, pet_discount_percentage, catch_discount_percentage, event_discount_percentage)}", inline=False) 

            embed.set_footer(text=f"{texts[f'{language}_show_buy_menu_embed_footer'].format(ctx.author)}")
            

            #BUTTON SETUP
            g_call_followup = ComponentsButton.Followup()
            g_call_followup.set_caller_details(bridgebuy, ctx, "g", menu)
            g_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🏭',label='', custom_id=ComponentsButton.ID_CALLER, followup=g_call_followup)

            f_call_followup = ComponentsButton.Followup()
            f_call_followup.set_caller_details(bridgebuy, ctx, "f", menu)
            f_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='⛽',label='', custom_id=ComponentsButton.ID_CALLER, followup=f_call_followup)

            if disclv == 99:
                b_call_followup = ComponentsButton.Followup()
                b_call_followup.set_caller_details(bridgebuy, ctx, "b", menu)
                b_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🏢',label='', custom_id=ComponentsButton.ID_CALLER, followup=b_call_followup)

                c_call_followup = ComponentsButton.Followup()
                c_call_followup.set_caller_details(bridgebuy, ctx, "c", menu)
                c_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🖨️',label='', custom_id=ComponentsButton.ID_CALLER, followup=c_call_followup)

            p_call_followup = ComponentsButton.Followup()
            p_call_followup.set_caller_details(bridgebuy, ctx, "p", menu)
            p_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='⛏️',label='', custom_id=ComponentsButton.ID_CALLER, followup=p_call_followup)

            e_call_followup = ComponentsButton.Followup()
            e_call_followup.set_caller_details(bridgebuy, ctx, "e", menu)
            e_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🥽',label='', custom_id=ComponentsButton.ID_CALLER, followup=e_call_followup)

            d_call_followup = ComponentsButton.Followup()
            d_call_followup.set_caller_details(bridgebuy, ctx, "d", menu)
            d_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='💵',label='', custom_id=ComponentsButton.ID_CALLER, followup=d_call_followup)

            pb_call_followup = ComponentsButton.Followup()
            pb_call_followup.set_caller_details(bridgebuy, ctx, "pb", menu)
            pb_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🎁',label='', custom_id=ComponentsButton.ID_CALLER, followup=pb_call_followup)

            profile_call_followup = ComponentsButton.Followup()
            profile_call_followup.set_caller_details(show_profile, ctx)
            profile_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='👤',label='', custom_id=ComponentsButton.ID_CALLER, followup=profile_call_followup)

            end_button = ComponentsButton(style=ComponentsButton.style.secondary, emoji='❌',label='', custom_id=ComponentsButton.ID_END_SESSION)

            menu.add_button(g_caller_button)
            menu.add_button(f_caller_button)
            if disclv == 99:
                menu.add_button(b_caller_button)
                menu.add_button(c_caller_button)
            menu.add_button(p_caller_button)
            menu.add_button(e_caller_button)
            menu.add_button(d_caller_button)
            menu.add_button(pb_caller_button)
            menu.add_button(profile_caller_button)
            menu.add_button(end_button)
            
            menu.add_page(embed)
            menu.add_page(embed)
            
            await menu.start(send_to = ctx.channel)
                
        except mariadb.Error as error:
            print("Failed to read data from the database", error)
                
        finally:
            if dtbconnection:
                dtbconnection.close()

`

The profile button just calls another command function, the other "bridgebuy" buttons call this function:

`

async def bridgebuy(ctx, purchasee: str, menuobject):

        await show_buy_menu(ctx, purchasee, None, True)
        newembed = refreshbuycontent(ctx)
        await menuobject.update(new_pages=[newembed], new_buttons=None)

`

the refreshbuycontent(ctx) just returns an embed, here is the function:
`
try:

    #connect to database
    #dtbconnection = sqlite3.connect("Users.db")
    dtbconnection = mariadb.connect(user=dtbuser,password=dtbpw,host=dtbhost,port=dtbport,database=dtbdtb)
    cursor = dtbconnection.cursor()

    #Again just filling variables here
        
    #creating the buy menu embed
    emb_color = getcolor(ctx.author.id)
    embed=discord.Embed(title=" ", color=discord.Colour(int(emb_color, 0)))
    embed.set_author(name=f"{texts[f'{language}_show_buy_menu_embed_author'].format(ctx.author)}")
    embed.set_thumbnail(url=f"http://www.6m7.de/img/shopping-cart.png")
    embed.add_field(name="⠀⠀⠀⠀‎⠀‎‎⠀‎‎‎⠀", value=f"{texts[f'{language}_show_buy_menu_embed_field_0_value'].format(crystals)}", inline=False)
    if disclv == 99:
        embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_1_title']}", 
                        value=f"{texts[f'{language}_show_buy_menu_embed_field_1_value_1'].format(str(generatorlv).split('.',1)[0], generator_price, delta_ph_1gene, costdelta_ph_1gene, str(fuellv).split('.',1)[0], fuel_price, delta_ph_1fuel, costdelta_ph_1fuel, str(businesslv).split('.',1)[0], business_price, delta_ph_1bus, costdelta_ph_1bus, str(copierlv).split('.',1)[0], copier_price, delta_ph_1cop, costdelta_ph_1cop, str(pickaxes).split('.',1)[0], pickaxe_price, delta_pr_1pick, costdelta_pr_1pick, str(materiallv).split('.',1)[0], material_price, delta_pr_1mat, costdelta_pr_1mat)}", inline=False)
    else:
        embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_1_title']}", 
                        value=f"{texts[f'{language}_show_buy_menu_embed_field_1_value_2'].format(str(generatorlv).split('.',1)[0], generator_price, delta_ph_1gene, costdelta_ph_1gene, str(fuellv).split('.',1)[0], fuel_price, delta_ph_1fuel, costdelta_ph_1fuel, str(pickaxes).split('.',1)[0], pickaxe_price, delta_pr_1pick, costdelta_pr_1pick, str(materiallv).split('.',1)[0], material_price, delta_pr_1mat, costdelta_pr_1mat, str(disclv).split('.',1)[0], disc_price)}", inline=False)

    embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_2_title']}", 
                    value=f"{texts[f'{language}_show_buy_menu_embed_field_2_value'].format(pb_price, timeleft_str)}", inline=False) 

    embed.add_field(name=f"{texts[f'{language}_show_buy_menu_embed_field_3_title']}",
                    value=f"{texts[f'{language}_show_buy_menu_embed_field_3_value'].format(level_discount_percentage, pet_discount_percentage, catch_discount_percentage, event_discount_percentage)}", inline=False) 

    embed.set_footer(text=f"{texts[f'{language}_show_buy_menu_embed_footer'].format(ctx.author)}")
    
    return embed 

except mariadb.Error as error:
    print("Failed to read data from the database", error)
        
finally:
    if dtbconnection:
        dtbconnection.close()

`

Sorry for the bad formatting, still dont understand how the backticks make these codeboxes work :C
The embed itself gets created fine, just the call buttons somehow dont do anything
Well it only stopped working after restarting windows, everything worked fine before :'(

I'm not sure what could be effecting it simply from a windows restart. I'd say:

1 - Update to the latest Github version
2 - If you're using dislash.py separately, read this because if not followed can cause some weird behaviors

If it's not number 2, it might have something to do with the restart

I am on the latest github version and I do not use dislash seperately :/

I see. I also had to restart windows a few days ago (windows mandatory update), but no issues with the lib arose from me restarting windows. If everything worked before the restart, I am unable to help with this issue because it doesn’t seem like it’s directly related to the lib if the lib was functioning as intended (except for the footer issues that should now be fixed) before restart.

Okay its been a few hours of agony with no result at all but I found this now: which says that old versions of the discord api result in an error 400
image
https://discord.com/developers/docs/reference
And I also saw that there were new changes to the API just yesterday:
image
https://discord.com/developers/docs/change-log
Could this be a remote cause for my issues? Do I have to update the Api version for the bot anywhere myself or ... ._.

Update:
I also found out that other people that just use dislash also had similar problems, when sending "Deferred Update Messages" with the exact same error. They seemed to get it working by "sending content with it" 🤔

Maybe it was just a coincidence to break at the same time as me restarting my windows :/

I just did some looking around as well, and I do see a few people that use dislash.py having issues with DeferredUpdateMessage, but that problem comes and goes it seems/is isolated. Some people are saying it happens often, some are saying it's happened to them but only ever a couple times. Personally I haven't had that issue when testing. The "sending content with it" you saw is when a button is clicked and a message is sent to discord specifically by that button interaction, and from that message that was sent, you can edit it via dislash.py's MessageInteraction.edit(), which has a content parameter. When using a caller button, there is no message that is sent. There could be, but that's something else entirely.

I tried to replicate the error by doing a test method that's pretty much doing the same thing as your methods.
1 - create the buttons
2 - add the buttons to the menu
3 - add pages to the menu
4 - start the menu
5 - (menu started) clicked caller buttons, calls function, and inside said function, update the menu via menu.update()

@bot.command()
async def test_bridge(ctx, title: str, menu: ButtonsMenu):
    page_1 = discord.Embed(title=title)
    page_2 = discord.Embed(title=title)

    await menu.update(new_pages=[page_1, page_2], new_buttons=None)

@bot.command()
async def test(ctx):
    menu = ButtonsMenu(ctx, menu_type=ButtonsMenu.TypeEmbed, show_page_director=False)

    p_call_followup = ComponentsButton.Followup()
    p_call_followup.set_caller_details(test_bridge, ctx, 'Hammer', menu)
    p_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='⛏️', label='', custom_id=ComponentsButton.ID_CALLER, followup=p_call_followup)

    e_call_followup = ComponentsButton.Followup()
    e_call_followup.set_caller_details(test_bridge, ctx, 'Goggles', menu)
    e_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🥽', label='', custom_id=ComponentsButton.ID_CALLER, followup=e_call_followup)

    menu.add_button(p_caller_button)
    menu.add_button(e_caller_button)

    menu.add_page(discord.Embed(title='Initial Page - 1'))
    menu.add_page(discord.Embed(title='Initial Page - 2'))

    await menu.start()

And it successfully calls the functions

image

But yes, probably just a coincidence. If you get the chance, since my test methods to break it (trying to get the 400 Bad Request error) isn't working, if you could provide a short sample of code that causes the same issue that I can copy and paste, and I'll look into from there if that sample reproduces 400 Bad Request (error code: 50006): Cannot send an empty message

So I just used your code as pretty much the only code in my Bot to eliminate potential issues:


      # bot.py
      import os
      import discord
      from discord.ext import commands
      from dotenv import load_dotenv
      from reactionmenu import ButtonsMenu, ComponentsButton
      
      #loading the variables form the .env
      load_dotenv()
      TOKEN = os.getenv('DISCORD_TOKEN')
      OWNERID = os.getenv("OWNERID")
      
      #initiation bot with intents
      bot = commands.AutoShardedBot(command_prefix="-", case_insensitive=True, owner_id=OWNERID, help_command=None)
      ButtonsMenu.initialize(bot) # <-------- THIS IS REQUIRED
      
      #when the bot gets started
      @bot.event
      async def on_ready():    
          print("ready")
      
      #--------------------commands------------------------
      
      @bot.command()
      async def test_bridge(ctx, title: str, menu: ButtonsMenu):
          page_1 = discord.Embed(title=title)
          page_2 = discord.Embed(title=title)
      
          await menu.update(new_pages=[page_1, page_2], new_buttons=None)
      
      @bot.command()
      async def test(ctx):
          menu = ButtonsMenu(ctx, menu_type=ButtonsMenu.TypeEmbed, show_page_director=False)
      
          p_call_followup = ComponentsButton.Followup()
          p_call_followup.set_caller_details(test_bridge, ctx, 'Hammer', menu)
          p_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='⛏️', label='', custom_id=ComponentsButton.ID_CALLER, followup=p_call_followup)
      
          e_call_followup = ComponentsButton.Followup()
          e_call_followup.set_caller_details(test_bridge, ctx, 'Goggles', menu)
          e_caller_button = ComponentsButton(style=ComponentsButton.style.grey, emoji='🥽', label='', custom_id=ComponentsButton.ID_CALLER, followup=e_call_followup)
      
          menu.add_button(p_caller_button)
          menu.add_button(e_caller_button)
      
          menu.add_page(discord.Embed(title='Initial Page - 1'))
          menu.add_page(discord.Embed(title='Initial Page - 2'))
      
          await menu.start()
      
      bot.run(TOKEN)

ezgif com-gif-maker

Same error, tho looks like it really is some outside factor
hope its just a tempoary issue :/
I very much appreciate the effort though <3

No problem. I'll keep the issue open until next week so people can see it just in case someone has a solution the issue.

So I went through the files mentioned in the error message and I was able to stop the error message from appearing by changing line 172 in
C:\Users\user\AppData\Local\Programs\Python\Python39\Lib\site-packages\dislash\interactions\interctions.py
to:
content="something", embed=embed,
instead of
content=content, embed=embed,

sadly the interactions still fail.... with no error message in the terminal at all

OK! I got it to work!
I just removed the line 581 "await inter.reply(type=ResponseType.DeferredUpdateMessage)" in
C:\Users\user\AppData\Local\Programs\Python\Python39\Lib\site-packages\reactionmenu\buttons_menu.py

which makes the buttons work, it does say "interaction failed" under the button every time, but my functions execute correctly

This is without the fix of the previous comment

ezgif com-gif-maker (1)

Here is what it looks like, works so I'm happy :D though I hope someone knows something for it to 100% work again >.>

Also shows that the footers don't get removed anymore ;)

The interaction fails every time because you removed that line. Once a button is clicked, the discord API expects a reply within 3 seconds after that button click. Since that line is removed, it moves on to calling the function. So weird it was working fine one day then not the next for you, but yes, lets hope someone knows something to get it back working again. And glad to know the footer information is no longer being removed 🙂