I'm trying to make dynamically persistent buttons and am not sure how to go about it. I'm making a polls bot. What I want to happen is if the bot resets, I want the buttons on the polls to still work. I tried looking at some tutorials and the discord.py documentation but couldn't find anything helpful or relevant.
Below is my code:
import discord
from discord.ext import commands
from discord import app_commands
#My button class is a call for when someone presses a button
class MyButton(discord.ui.Button):
async def callback(self, interaction: discord.Interaction):
#Setting vars for the class.
button_id = self.custom_id
user_id = interaction.user.id
timestamp = interaction.created_at
message_id = interaction.message.id # Access the message ID #getting message id
print(f"Received button click: {button_id}") #Print for the msg ID
#The response message to clicking a button
await interaction.response.send_message(f"You clicked {self.label}")
#the My View class for
class MyView(discord.ui.View):
def __init__(self, options_and_ids): #The options_and_ids variable is a list thats ziped from the polls class
super().__init__(timeout=None)
#dynamically creating the Buttons for the message depending in the list.
for option, button_id in options_and_ids:
button = MyButton(label=option, custom_id=button_id)
self.add_item(button)
class polls(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
#setup hook for non-dynamic buttons that I tried to use.
async def setup_hook(self) -> None:
self.add_view(MyView())
@app_commands.command(name = "poll", description="Make a poll.")
@app_commands.describe(question = "The question you want to ask for your poll.")
@app_commands.describe(choices = 'Add choices with commas. ex. "One, Two, Three". (MAX IS 20)
Put commas within a choice using a double coma ",,".')
async def create_poll(self, interaction: discord.Interaction, question: str, choices: str)
#function for putting string into a list separated by single commas. Returns a list of str.
text_options = split_text(choices)
#Function that creates a 5 char id using Uppercase, lowercase, and digits.
# Returns a list of IDs the same size as the choices.
id_list = make_id(text_options)
#pairs text_options and id_list into a new list
names_and_ids = list(zip(text_options, id_list))
view = MyView(names_and_ids) #calls the MyView class sending over the list of buttons and their IDs.
await channel.send(embed=embed, view=view)
async def setup(bot:commands.Bot) -> None:
await bot.add_cog(polls(bot))
discord.pydiscorddiscord-buttons[How to make persistant buttons in discord.py]
I tried to use this to help me; though, I couldn't figure out how to use their example to make dynamic persistent buttons. I also thought about saving the button IDs in a database and then loading them in an on_ready event but I couldn't find the method to do this so I was out of luck with that. Thank you so much for your time. Hopefully you can help me!
Hi all, after some tinkering and looking at how Discord stores persistent buttons I figured it out.
Storing dynamic persistent button in discord.py is fairly simple; though, it requires you to use a database to store the button_name and button_id.
I also did this in a cog so this will vary depending on how you make your bot... Here's how I did it...
Saving buttons to the DB:
def save_buttons(names_and_id): #touple list [(name1,id1),(name2,id2),...] will always be the same amount of each
conn = sqlite3.connect('your_SQLite.db')
cursor = conn.cursor()
for name, id in names_and_id:
cursor.execute('INSERT INTO buttons (button_name, button_id) VALUES (?, ?)', (name,id))
conn.commit()
Fetching buttons from the DB:
def get_buttons():
conn = sqlite3.connect('criticalbot.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS buttons (
button_name TEXT,
button_id TEXT PRIMARY KEY)
''')
conn.commit()
#getting all buttons from the db. Will conveniently be the same format as the touple that I use to make the View when making buttons.
cursor.execute('SELECT * FROM buttons')
names_and_ids = cursor.fetchall()
if names_and_ids == None:
return None
return names_and_ids
Now I didn't have to do much to my original code to make it work...
The MyButton class stays the same except getting rid of unneeded bloat:
#My button class is a call for when someone presses a button
class MyButton(discord.ui.Button):
async def callback(self, interaction: discord.Interaction):
#Setting vars for the class.
button_id = self.custom_id
print(f"Received button click: {button_id}") #Print for the msg ID
#The response message to clicking a button
await interaction.response.send_message(f"You clicked {self.label}")
Here is the MyView class where the dynamic buttons view will be made as well as what we call to make the buttons to be persistent.
#the My View class for creating the view
class MyView(discord.ui.View):
def __init__(self, options_and_ids): #The options_and_ids variable is a list thats ziped from the polls class
super().__init__(timeout=None)
#dynamically creating the Buttons for the message depending in the list.
for option, button_id in options_and_ids:
button = MyButton(label=option, custom_id=button_id)
self.add_item(button)
This is where we can make the call when the bot goes online to make the buttons persistent. What's nice about this is because its a cog class, the vars will be set before the bot goes completely online so it will auto-run our add_view()
method before the bot starts.
class polls(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
try: #We use this in case there's nothing in the DB
self.bot.add_view(MyView(get_buttons())) #THIS IS WHAT YOU NEED TO MAKE THE BUTTONS PERSISTANT
except: # ^This is where we call the get buttons function to get the touple we need to make the dynamic persistent view.
pass
#REPLACEING THIS WITH WHATS ABOVE
#setup hook for non-dynamic buttons that I tried to use.
#async def setup_hook(self) -> None:
# self.add_view(MyView())
Here is my main app_command
that i use
@app_commands.command(name = "poll", description="Make a poll.")
@app_commands.describe(question = "The question you want to ask for your poll.")
@app_commands.describe(choices = 'Add choices with commas. ex. "One, Two, Three". (MAX IS 20)
Put commas within a choice using a double coma ",,".')
async def create_poll(self, interaction: discord.Interaction, question: str, choices: str)
#function for putting string into a list separated by single commas. Returns a list of str.
text_options = split_text(choices)
#Function that creates a 5 char id using Uppercase, lowercase, and digits.
# Returns a list of IDs the same size as the choices.
id_list = make_id(text_options)
#pairs text_options and id_list into a new list
names_and_ids = list(zip(text_options, id_list))
save_buttons(names_and_ids) #<--- this is where we save the commands when we create new dynamic buttons when a pole is made.
view = MyView(names_and_ids) #calls the MyView class sending over the list of buttons and their IDs.
await channel.send("", view=view)
async def setup(bot:commands.Bot) -> None:
await bot.add_cog(polls(bot))