Discord.py - Clicking button in a view can't access the interaction's message - discord.py

I'm trying to follow this thread: https://stackoverflow.com/a/75396860. I would have commented on it to get clarity, but SO won't allow me to.
I'm just trying to accomplish one simple thing: when Button 1 is pressed, enable Button 2
View Class
import discord
class Signup(discord.ui.View):
def __init__(self):
super().__init__()
#discord.ui.button(label="Button 1")
async def one_callback(self, button: discord.ui.Button, interaction: discord.Interaction):
await interaction.response.defer()
for child in self.children:
if type(child) == discord.ui.Button and child.label == "Button 2":
child.disabled = False
await interaction.message.edit(view=self)
await interaction.followup.send("Button 1 Pressed, Button 2 Enabled")
#discord.ui.button(label="Button 2", disabled=True)
async def two_callback(self, button: discord.ui.Button, interaction: discord.Interaction):
pass
I initially send the message like this:
await channel.send(content="Click Button 1 to get Button 2!", view=Signup())
With this code, I receive the following error when it gets to interaction.response.defer():
AttributeError: 'Button' object has no attribute 'response'
It seems like the Interaction is always type discord.Button for me, which doesn't have response or message as attributes. The same code appears to work for others, though, as this SO thread is not the only place that I've seen the same logic being used. I'm not quite sure where I've gone wrong.

Turns out the issue was in the answer at the other SO thread. The arguments were out of order in the callback function.
I had:
async def one_callback(self, button: discord.ui.Button, interaction: discord.Interaction):
Should be:
async def one_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
The docs state the order here: https://discordpy.readthedocs.io/en/stable/interactions/api.html#discord.ui.button
The function being decorated should have three parameters, self representing the discord.ui.View, the discord.Interaction you receive and the discord.ui.Button being pressed.
Thanks to the discord.py Discord for saving me from my self!

Related

discord.py multiple button same callback what button clicked?

I'm developing discord bot with discord.py I'm using UI Button, I wanna get clicked Button's label. How can I get specific id interactioned button.
import discord
from discord import Interaction,ui,ButtonStyle,app_commands
class MyClient(discord.Client):
async def on_ready(self):
await self.wait_until_ready()
await tree.sync(guild= discord.Object(id=GUILD_ID))
print(f"login {self.user}!")
intents= discord.Intents.all()
client = MyClient(intents=intents)
tree = app_commands.CommandTree(client)
#tree.command(guild=discord.Object(id=GUILD_ID), name="play", description="button")
async def test(interaction:Interaction):
async def button(interaction:Interaction):
pass # I wanna get clicked button's label
for idx in range(6):
b=ui.Button(label=f'{idx+1}',row=(idx)//3)
view.add_item(b)
await interaction.response.send_message(view=view)
client.run(token)

Mention username in a Discord Modal of the person who opened it [duplicate]

I want to create a modal like in the picture when the button is pressed. How can I make this model with Discord.py or nextcord?
You can find examples for modals in Nextcord here:
https://github.com/nextcord/nextcord/blob/master/examples/modals/
class Pet(nextcord.ui.Modal):
def __init__(self):
super().__init__("Your pet") # Modal title
# Create a text input and add it to the modal
self.name = nextcord.ui.TextInput(
label="Your pet's name",
min_length=2,
max_length=50,
)
self.add_item(self.name)
# Create a long text input and add it to the modal
self.description = nextcord.ui.TextInput(
label="Description",
style=nextcord.TextInputStyle.paragraph,
placeholder="Information that can help us recognise your pet",
required=False,
max_length=1800,
)
self.add_item(self.description)
async def callback(self, interaction: nextcord.Interaction) -> None:
"""This is the function that gets called when the submit button is pressed"""
response = f"{interaction.user.mention}'s favourite pet's name is {self.name.value}."
if self.description.value != "":
response += f"\nTheir pet can be recognized by this information:\n{self.description.value}"
await interaction.send(response)
#bot.slash_command(
name="pet",
description="Describe your favourite pet",
guild_ids=[TESTING_GUILD_ID],
)
async def send(interaction: nextcord.Interaction):
# sending the modal on an interaction (can be slash, buttons, or select menus)
modal = Pet()
await interaction.response.send_modal(modal)
class Modal(ui.Modal, title="test title"):
firstfield = ui.TextInput(label="firstfield",placeholder="write here", style=discord.TextStyle.short)
secondfield = ui.TextInput(label="secondfield",placeholder="write here", style=discord.TextStyle.short)
thirdfield = ui.TextInput(label="thirdfield:",placeholder="write here", style=discord.TextStyle.short)
first_big_field = ui.TextInput(label="first_big_field:",placeholder="write here", style=discord.TextStyle.long)
second_big_field = ui.TextInput(label="second_big_field:",placeholder="write here", style=discord.TextStyle.long)
Then u can call Modal() where u want

How to use Button in discord.py

I can't figure out how to do it in discord.py so that pressing a button reads the user who pressed it, then writes all users who pressed the button to a temporary array. And if it's not difficult if you know YouTube lessons on this, please give a link. (translated with google translator, sorry)
You can add a callback function to your button for that, like this:
#bot.command()
async def button(ctx):
users = []
button = discord.ui.Button(style = discord.ButtonStyle.primary, label="Add a user")
async def button_callback(i):
users.append(i.user)
print(users)
button.callback = button_callback
# Need to create a view
buttons_view = discord.ui.View()
# and add the button component to the view
buttons_view.add_item(button)
# send the view with the message
await ctx.send("Add a user by clicking the button", view=buttons_view)

How to delete message using buttons in discord.py?

Its my first time using buttons in discord.py
I want the embed to delete itself when someone clicks the delete button. This is my code but there are some problems.
Anyone can delete the message by clicking the button. I only want the user who ran the command to interact with the button.
When I click delete button, it automatically deletes all messages which were previously sent by the bot.
msg = await ctx.channel.send(embed=embed,components = [Button(label = "Delete", style=ButtonStyle.red)])
interaction = await bot.wait_for("button_click")
if interaction.component.label.startswith("Delete"):
await msg.delete()
I am not familiar with this components, but you could add an if statement to check for author id == user id who press said button,
msg = await ctx.send(embed = embed,components=[[copy,delete]])
#Copy and delete are buttons
def check(res):
return ctx.author == res.user and res.channel == ctx.channel and msg == res.message
while True:
res = await bot.wait_for("button_click",timeout=60, check=check)
if res.component.label.startswith("Copy User Name"):
await msg.edit(embed=embed_copy,components = [[back,delete]])
elif res.component.label.startswith("Back"):
await msg.edit(embed=embed,components=[[copy,delete]])
elif res.component.label.startswith("Delete"):
await msg.delete()
As LeSauvage suggested , this problem can easily be solved by using checks!

How To Change Conversation Handler State with a CallbackQueryHandler Button function

I have a python telegram bot that sends messages with Buttons on them like this:
keyboard = [[telegram.InlineKeyboardButton("Finish", callback_data='1'),
telegram.InlineKeyboardButton("Edit", callback_data='2')]]
update_obj.message.reply_text(message_text, reply_markup=reply_markup,
parse_mode=telegram.ParseMode.MARKDOWN)
Now when the user hits the "Edit" button I want the ConversationHandler to move to another state.
Here is a part of my ConversationHandler:
WELCOME = 0
SEARCH = 2
EDITTITLE = 3
[...]
handler = telegram.ext.ConversationHandler(
entry_points=[telegram.ext.CommandHandler('start', start)],
states={
WELCOME: [
telegram.ext.MessageHandler(filters=telegram.ext.Filters.all, callback=welcome),
updater.dispatcher.add_handler(telegram.ext.CallbackQueryHandler(button))
],
SEARCH: [
telegram.ext.MessageHandler(filters=telegram.ext.Filters.all, callback=search),
updater.dispatcher.add_handler(telegram.ext.CallbackQueryHandler(button))
], EDITTITLE: [
telegram.ext.MessageHandler(filters=telegram.ext.Filters.all, callback=editTitle)
]
[...]
Now normally I can just return the new state in the callback function from the MesageHandler like so:
def search(update_obj, context):
[...]
return WELCOME
But if I return the new state in my button function it does not switch to that state. Here's the code:
def button(update_obj, context):
[...]
return EDITTITLE
How can I make the button change the ConversationHandler state or is there something I am missing?
Am I using the Buttons the right way?
Thanks :)
You're adding the CallbackQueryHandlers wrongly to the conversation. updater.dispatcher.add_handler(telegram.ext.CallbackQueryHandler(button)) returns None, so None will be added to the list of handlers in the states WELCOME and SEARCH. Instead it should be just telegram.ext.CallbackQueryHandler(button).
It could be that there are additional issues, but I'd have to see a full minimal working example to judge that.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Resources