Search code examples
discord.pydiscord-buttons

How can I edit an embed field with a button in Discord.py?


Basically, I'm making a poll command. I want it to put the user's vote in the embed field, and obviously make it so they can't vote again, again, and again. No idea how to do this.

Command Preview Here

Here's the code:

class Buttons(discord.ui.View):
    def __init__(self, *, timeout = 180):
        super().__init__(timeout=timeout)

    @discord.ui.button(label="Yes", style=discord.ButtonStyle.success, emoji='👍')
    async def upvote_button(self, button : discord.ui.Button, interaction : discord.Interaction):
        pass

    @discord.ui.button(label="No", style=discord.ButtonStyle.danger, emoji='👎')
    async def downvote_button(self, button : discord.ui.Button, interaction : discord.Interaction):
        pass

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

    @app_commands.command(name="poll", description="Ask a question and receive upvotes or downvotes (Yes/No)")
    async def poll(self, interaction : discord.Interaction, *, question : str):
        embed = discord.Embed(
            description=question,
            color = 0xb037e1
        )
        embed.set_author(name="Poll", icon_url=self.bot.user.avatar)
        embed.set_footer(text="Asked by: " + str(interaction.user), icon_url=interaction.user.avatar)

        embed.add_field(name="Upvotes", value="X", inline=True)
        embed.add_field(name="Downvotes", value="X", inline=True)

        await interaction.response.send_message(embed=embed, view=Buttons())

Probably very simple, although I still haven't thought about a way


Solution

  • The easiest way to do this is by using dictionaries, lists, or even sets to store the users that have clicked.

    For this example, I will use one list to store the users who have clicked the upvote and downvote buttons. Like here:

    class Buttons(discord.ui.View):
        def __init__(self, *, timeout=None):
            super().__init__(timeout=timeout or 180)
    
            self.voted_users = []
    
            self.upvote_count = 0
            self.downvote_count = 0
    
        @discord.ui.button(
            label="Yes", style=discord.ButtonStyle.success, emoji="👍"
        )
        async def upvote_button(
            self, interaction: discord.Interaction, button: discord.ui.Button
        ):
            if (
                interaction.user in self.voted_users
            ):  # check if the user has already voted or not and return if true
                return await interaction.response.send_message(
                    content="You've already voted!", ephemeral=True
                )
    
            self.upvote_count += 1
            self.voted_users.append(interaction.user)  # add the user to voted list
    
            await interaction.response.send_message("Upvoted!", ephemeral=True)
    
        @discord.ui.button(label="No", style=discord.ButtonStyle.danger, emoji="👎")
        async def downvote_button(
            self, interaction: discord.Interaction, button: discord.ui.Button
        ):
            if (
                interaction.user in self.voted_users
            ):  # check if the user has already voted or not and return if true
                return await interaction.response.send_message(
                    content="You've already voted!", ephemeral=True
                )
    
            self.downvote_count += 1
            self.voted_users.append(interaction.user)  # add the user to voted list
    
            await interaction.response.send_message("Downvoted!", ephemeral=True)
    

    By the way, interaction always comes before the button in the button callback argument if you're using discord.py.