Skip to content

Instantly share code, notes, and snippets.

@Vexs
Created July 4, 2017 21:08
Show Gist options
  • Save Vexs/f2c1bfd6bda68a661a71accd300d2adc to your computer and use it in GitHub Desktop.
Save Vexs/f2c1bfd6bda68a661a71accd300d2adc to your computer and use it in GitHub Desktop.
Simple polling function for a discord.py discord bot
import discord
from discord.ext import commands
class QuickPoll:
""""""
def __init__(self, bot):
self.bot = bot
@commands.command(pass_context=True)
async def quickpoll(self, ctx, question, *options: str):
if len(options) <= 1:
await self.bot.say('You need more than one option to make a poll!')
return
if len(options) > 10:
await self.bot.say('You cannot make a poll for more than 10 things!')
return
if len(options) == 2 and options[0] == 'yes' and options[1] == 'no':
reactions = ['✅', '❌']
else:
reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟']
description = []
for x, option in enumerate(options):
description += '\n {} {}'.format(reactions[x], option)
embed = discord.Embed(title=question, description=''.join(description))
react_message = await self.bot.say(embed=embed)
for reaction in reactions[:len(options)]:
await self.bot.add_reaction(react_message, reaction)
embed.set_footer(text='Poll ID: {}'.format(react_message.id))
await self.bot.edit_message(react_message, embed=embed)
@commands.command(pass_context=True)
async def tally(self, ctx, id):
poll_message = await self.bot.get_message(ctx.message.channel, id)
if not poll_message.embeds:
return
embed = poll_message.embeds[0]
if poll_message.author != ctx.message.server.me:
return
if not embed['footer']['text'].startswith('Poll ID:'):
return
unformatted_options = [x.strip() for x in embed['description'].split('\n')]
opt_dict = {x[:2]: x[3:] for x in unformatted_options} if unformatted_options[0][0] == '1' \
else {x[:1]: x[2:] for x in unformatted_options}
# check if we're using numbers for the poll, or x/checkmark, parse accordingly
voters = [ctx.message.server.me.id] # add the bot's ID to the list of voters to exclude it's votes
tally = {x: 0 for x in opt_dict.keys()}
for reaction in poll_message.reactions:
if reaction.emoji in opt_dict.keys():
reactors = await self.bot.get_reaction_users(reaction)
for reactor in reactors:
if reactor.id not in voters:
tally[reaction.emoji] += 1
voters.append(reactor.id)
output = 'Results of the poll for "{}":\n'.format(embed['title']) + \
'\n'.join(['{}: {}'.format(opt_dict[key], tally[key]) for key in tally.keys()])
await self.bot.say(output)
def setup(bot):
bot.add_cog(QuickPoll(bot))
@bruhbeans
Copy link

@Soheab, you must have rewrite then, so do ctx.send() instead of bot.say()

@iAmCLY
Copy link

iAmCLY commented Apr 23, 2019

How to use the command to show the check and cross icon?

@TritonInc
Copy link

TritonInc commented Dec 12, 2019

Having issues making it rewrite and having it actually add the reactions i get HTTPException: 400 BAD REQUEST (error code: 10014): Unknown Emoji

@ephemeral8997
Copy link

@TritonInc You put wrong emoji, Emojis:

• Custom emoji: <:emoji_name:emoji_id>

• Emoji: Put unicode emoji or emojis like 😃

@Torxed
Copy link

Torxed commented Aug 7, 2020

This is slightly hard to adopt if you're sub-classing the discord Client object. Especially since the migration to v1.0. This works pretty great running as a bot, but if you're sub-classing and stuff, this is a more likely approach: https://github.com/Torxed/Scripts/blob/master/python/discord_vote_poll.py

Where you rely on on_raw_reaction_add, where you have to fetch the channel and then the message and then, finally, counting the votes.
Which also has examples of adding emoji's by text and not the short-hand <:emoji name:emoji id> which can look tricky when you have the fetch unknown id's and so on.

@GuardianCosmos
Copy link

For those still trying to use this with the newer versions, you can still make this work, just keep in mind that you will need to exchange at minimum the following functions:
self.bot.say => ctx.send(embed=embed)
self.bot.add_reaction => react_message.add_reactions(reaction)
self.bot.edit_message => react_message.edit(embed=embed)

You will want to make similar changes to the tally function, replacing any self.bot references with the updated method on either the object or master class function

@NotMe-2
Copy link

NotMe-2 commented Oct 8, 2020

1.5 discord.py rewrite

import discord
from discord.ext import commands


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

    @commands.command(pass_context=True)
    async def poll(self, ctx, question, *options: str):
        if len(options) <= 1:
            await ctx.send('You need more than one option to make a poll!')
            return
        if len(options) > 10:
            await ctx.send('You cannot make a poll for more than 10 things!')
            return

        if len(options) == 2 and options[0] == 'yes' and options[1] == 'no':
            reactions = ['✅', '❌']
        else:
            reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟']

        description = []
        for x, option in enumerate(options):
            description += '\n {} {}'.format(reactions[x], option)
        embed = discord.Embed(title=question, description=''.join(description))
        react_message = await ctx.send(embed=embed)
        for reaction in reactions[:len(options)]:
            await react_message.add_reaction(reaction)
        embed.set_footer(text='Poll ID: {}'.format(react_message.id))
        await react_message.edit_message(embed=embed)

    @commands.command(pass_context=True)
    async def tally(self, ctx, id=None):
        poll_message = await ctx.channel.fetch_message(id)
        embed = poll_message.embeds[0]
        unformatted_options = [x.strip() for x in embed.description.split('\n')]
        print(f'unformatted{unformatted_options}')
        opt_dict = {x[:2]: x[3:] for x in unformatted_options} if unformatted_options[0][0] == '1' \
            else {x[:1]: x[2:] for x in unformatted_options}
        # check if we're using numbers for the poll, or x/checkmark, parse accordingly
        voters = [self.bot.user.id]  # add the bot's ID to the list of voters to exclude it's votes

        tally = {x: 0 for x in opt_dict.keys()}
        for reaction in poll_message.reactions:
            if reaction.emoji in opt_dict.keys():
                reactors = await reaction.users().flatten()
                for reactor in reactors:
                    if reactor.id not in voters:
                        tally[reaction.emoji] += 1
                        voters.append(reactor.id)
        output = f"Results of the poll for '{embed.title}':\n" + '\n'.join(['{}: {}'.format(opt_dict[key], tally[key]) for key in tally.keys()])
        await ctx.send(output)


def setup(bot):
    bot.add_cog(QuickPoll(bot))

@tomlin7
Copy link

tomlin7 commented Nov 23, 2020

help
image

you didn't give the required arguments

@ha1fdan
Copy link

ha1fdan commented Jan 4, 2021

help
image

use: await self.bot.send("...")

@Karjala22
Copy link

1.5 discord.py rewrite

import discord
from discord.ext import commands


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

    @commands.command(pass_context=True)
    async def poll(self, ctx, question, *options: str):
        if len(options) <= 1:
            await ctx.send('You need more than one option to make a poll!')
            return
        if len(options) > 10:
            await ctx.send('You cannot make a poll for more than 10 things!')
            return

        if len(options) == 2 and options[0] == 'yes' and options[1] == 'no':
            reactions = ['✅', '❌']
        else:
            reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟']

        description = []
        for x, option in enumerate(options):
            description += '\n {} {}'.format(reactions[x], option)
        embed = discord.Embed(title=question, description=''.join(description))
        react_message = await ctx.send(embed=embed)
        for reaction in reactions[:len(options)]:
            await react_message.add_reaction(reaction)
        embed.set_footer(text='Poll ID: {}'.format(react_message.id))
        await react_message.edit_message(embed=embed)

    @commands.command(pass_context=True)
    async def tally(self, ctx, id=None):
        poll_message = await ctx.channel.fetch_message(id)
        embed = poll_message.embeds[0]
        unformatted_options = [x.strip() for x in embed.description.split('\n')]
        print(f'unformatted{unformatted_options}')
        opt_dict = {x[:2]: x[3:] for x in unformatted_options} if unformatted_options[0][0] == '1' \
            else {x[:1]: x[2:] for x in unformatted_options}
        # check if we're using numbers for the poll, or x/checkmark, parse accordingly
        voters = [self.bot.user.id]  # add the bot's ID to the list of voters to exclude it's votes

        tally = {x: 0 for x in opt_dict.keys()}
        for reaction in poll_message.reactions:
            if reaction.emoji in opt_dict.keys():
                reactors = await reaction.users().flatten()
                for reactor in reactors:
                    if reactor.id not in voters:
                        tally[reaction.emoji] += 1
                        voters.append(reactor.id)
        output = f"Results of the poll for '{embed.title}':\n" + '\n'.join(['{}: {}'.format(opt_dict[key], tally[key]) for key in tally.keys()])
        await ctx.send(output)


def setup(bot):
    bot.add_cog(QuickPoll(bot))

I think this line no longer works in latest API.

await react_message.edit_message(embed=embed)

The correct one is just edit https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.edit

This worked for me
await react_message.edit(embed=embed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment