Search code examples
pythondiscord.pypersistent

Discord.py button persistency confusion


I'm looking to make my buttons on an information post persistent so that they remain interactable even after my bot is restarted but I'm not entirely sure how it would work and how it would apply to my code. I am currently using cogs for my discord bot. I have researched about persistency and even checked the example but was struggling to implement it into my code

main.py

import discord
from discord.ext import commands
from discord import app_commands
import asyncio
from variables import cogs, success, failed, check_is_it_us, MY_GUILD, TOKEN, status

bot = commands.Bot(command_prefix=">", intents=discord.Intents.all())

@bot.event
async def on_ready():
    synced = await bot.tree.sync()
    print(f"Logged in as {bot.user} (ID: {bot.user.id})")
    print(f"Loaded {success}") if failed == [] else print(f"Loaded {success}\nFailed to load {failed}")
    print(f"Synced {len(synced)} commands.")  
    await bot.change_presence(activity = discord.Game(name=status))

@bot.tree.command(name="sync")
@app_commands.check(check_is_it_us)
async def sync(interaction: discord.Interaction):
    bot.tree.copy_global_to(guild=MY_GUILD)
    synced = await bot.tree.sync()
    print(f"Synced {len(synced)} commands.")  
    await interaction.response.send_message(f"Synced {len(synced)} commands.", ephemeral=True)
        
async def loadCogs():
    for cog in cogs:
        try:
            await bot.load_extension(f"Cogs.{cog}")
            success.append(cog)
        except Exception as e:
            print(e)
            failed.append(cog)

async def main():
    await loadCogs()

asyncio.run(main())
bot.run(TOKEN)

The cog that I would like to have persistent buttons in is called info.py and the command is called /sendinfo and basically I just want the bot to send a message where the user can click on the buttons to see an ephemeral message of the rules and be able to get the Member role.

info.py

import discord
from discord.ext import commands
from discord import app_commands
from typing import Literal
import platform
from variables import suggestionChannel, pollChannel, pollRole, timeformat, stats, convertTime, check_is_it_us, rules
import asyncio
import random
from discord.utils import get

class info(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
    
    

    createGroup = app_commands.Group(name="create", description="Allows you to create suggestions and polls")
    infoGroup = app_commands.Group(name="info", description="Shows you info")

    @app_commands.command(name="sendinfo", description="Sends the information message")
    @app_commands.check(check_is_it_us)
    async def sendinfo(self, interaction:discord.Interaction):
        view=discord.ui.View(timeout=None)

        async def rulesButtoncallback(interaction:discord.Interaction):
            rulesEmbed = discord.Embed(title="Official Rules for DRags Club", description="Please agree to our guidelines.")
            for item in rules:
                rule = item.split("|")
                rulesEmbed.add_field(name=rule[0], value=rule[1], inline=False)
            await interaction.response.send_message(embed=rulesEmbed, ephemeral=True)
        
        rulesButton = discord.ui.Button(style = discord.ButtonStyle.blurple, label = "Rules")
        rulesButton.callback = rulesButtoncallback
        
        view.add_item(rulesButton)
        async def verifyButtoncallback(interaction:discord.Interaction):
            role = get(interaction.guild.roles, name = "Member")
            await interaction.user.add_roles(role)
            await interaction.response.send_message("You have been verified!", ephemeral=True)

        verifyButton = discord.ui.Button(style = discord.ButtonStyle.blurple, label = "Verify")
        verifyButton.callback = verifyButtoncallback
        view.add_item(verifyButton)
        embed=discord.Embed(colour= 000, title = "DRags' Club", description = "Welcome to the home of [DRags](https://www.youtube.com/@_DRags). Explore the **2 year old** server and see what we have to offer!")
        await interaction.channel.send(embed=embed, view=view)
        await interaction.response.send_message(f"The information message has been sent in {interaction.channel}!", ephemeral=True)
        
async def setup(bot):
    await bot.add_cog(info(bot))

Please note that there a more commands in this cog that I removed for simplicity.

The structure of rules in variables.py

rules = ["rule name|rule description","rule name|rule description"]

So basically I want to find out how I could make the buttons on this command persistent and I have linked the screenshot of the command here too.

Screenshot of the command


Solution

  • Change your view to be class-based, it's gonna be easier to manage.

    class MyPersistentView(discord.ui.View):
        def __init__(self):
            super().__init__(timeout=None)
    
        @discord.ui.button(label="Rules", custom_id="rules-button", style=discord.ButtonStyle.blurple)
        async def rules_callback(self, interaction, button):
            rulesEmbed = discord.Embed(title="Official Rules for DRags Club", description="Please agree to our guidelines.")
            for item in rules:
                rule = item.split("|")
                rulesEmbed.add_field(name=rule[0], value=rule[1], inline=False)
            await interaction.response.send_message(embed=rulesEmbed, ephemeral=True)
    
        @discord.ui.button(label="Verify", custom_id="verify_button", style=discord.ButtonStyle.blurple)
        async def verify_callback(self, interaction, button)
            role = get(interaction.guild.roles, name = "Member")
            await interaction.user.add_roles(role)
            await interaction.response.send_message("You have been verified!", ephemeral=True)
    
    
    class info(commands.Cog):
        def __init__(self, bot):
            self.bot = bot
    
        createGroup = app_commands.Group(name="create", description="Allows you to create suggestions and polls")
        infoGroup = app_commands.Group(name="info", description="Shows you info")
    
        @app_commands.command(name="sendinfo", description="Sends the information message")
        @app_commands.check(check_is_it_us)
        async def sendinfo(self, interaction:discord.Interaction):
            view = MyPersistentView(timeout=None)
            await interaction.response.send_message(view=view)
    

    Now to create a persistent view it's really simple. Inside the setup_hook method call bot.add_view and pass the MESSAGE_ID (you can do this by copying the message ID in discord after sending the initial message)

    from cogs.info import MyPersistentView  # or wherever you defined your view class
    
    bot = commands.Bot(command_prefix=">", intents=discord.Intents.all())
    
    MESSAGE_ID = 0
    
    async def setup_hook():
        bot.add_view(MyPersistentView(), message_id=MESSAGE_ID)
    
    bot.setup_hook = setup_hook