CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-disnake

A modern, easy-to-use, feature-rich async-ready API wrapper for Discord written in Python

Pending
Overview
Eval results
Files

polls.mddocs/

Poll System

Discord's native poll system allows creating interactive polls with multiple choice answers, emojis, and automatic vote counting. Disnake provides full support for creating, managing, and responding to polls.

Capabilities

Poll Media

Represents the content of poll questions and answers, supporting both text and emoji elements.

class PollMedia:
    def __init__(
        self,
        text: str,
        emoji: Optional[Union[Emoji, PartialEmoji, str]] = None
    ):
        """
        Create poll media (question or answer content).
        
        Parameters:
        - text: Text content for the media
        - emoji: Optional emoji to display with the text
        """

    @property
    def text(self) -> Optional[str]:
        """Text content of the media."""

    @property
    def emoji(self) -> Optional[Union[Emoji, PartialEmoji]]:
        """Emoji associated with the media."""

Poll Answer

Represents a single answer option in a poll with vote tracking.

class PollAnswer:
    def __init__(self, *, data: PollAnswerPayload):
        """
        Poll answer option.
        
        Parameters:
        - data: Raw answer data from Discord API
        """

    @property
    def id(self) -> int:
        """Answer ID."""

    @property
    def media(self) -> PollMedia:
        """Media content for this answer."""

    @property
    def vote_count(self) -> int:
        """Number of votes this answer has received."""

    @property
    def me_voted(self) -> bool:
        """Whether the current user voted for this answer."""

    def get_voters(self, *, limit: Optional[int] = None, after: Optional[Snowflake] = None) -> PollAnswerIterator:
        """
        Get users who voted for this answer.
        
        Parameters:
        - limit: Maximum number of voters to retrieve
        - after: Get voters after this user ID
        
        Returns:
        Iterator for poll answer voters
        """

Poll

Complete poll object with question, answers, and configuration.

class Poll:
    def __init__(self, *, data: PollPayload, state: ConnectionState):
        """
        Discord poll instance.
        
        Parameters:
        - data: Raw poll data from Discord API
        - state: Connection state for API calls
        """

    @property
    def question(self) -> PollMedia:
        """Poll question content."""

    @property
    def answers(self) -> List[PollAnswer]:
        """All answer options for the poll."""

    @property
    def expiry(self) -> Optional[datetime]:
        """When the poll expires and stops accepting votes."""

    @property
    def allow_multiselect(self) -> bool:
        """Whether users can select multiple answers."""

    @property
    def layout_type(self) -> PollLayoutType:
        """Layout type for displaying the poll."""

    @property
    def total_vote_count(self) -> int:
        """Total number of votes across all answers."""

    @property
    def is_finalized(self) -> bool:
        """Whether the poll has been finalized (expired or manually ended)."""

    def get_answer(self, answer_id: int) -> Optional[PollAnswer]:
        """
        Get a specific answer by ID.
        
        Parameters:
        - answer_id: Answer ID to look up
        
        Returns:
        PollAnswer if found, None otherwise
        """

    async def end(self) -> Message:
        """
        End the poll immediately.
        
        Returns:
        Updated message with finalized poll
        """

    @classmethod
    def create(
        cls,
        question: Union[str, PollMedia],
        *answers: Union[str, PollMedia],
        duration: Optional[Union[int, timedelta]] = None,
        allow_multiselect: bool = False,
    ) -> PollCreatePayload:
        """
        Create poll data for sending in a message.
        
        Parameters:
        - question: Poll question text or media
        - answers: Answer options (2-10 answers required)
        - duration: Poll duration in hours (1-168) or timedelta
        - allow_multiselect: Whether to allow multiple answer selection
        
        Returns:
        Poll creation payload for message sending
        """

Usage Examples

Creating Basic Polls

import disnake
from disnake import Poll, PollMedia

@bot.slash_command(description="Create a simple poll")
async def poll(
    inter: disnake.ApplicationCommandInteraction,
    question: str = disnake.Param(description="Poll question"),
    option1: str = disnake.Param(description="First option"),
    option2: str = disnake.Param(description="Second option"),
    option3: str = disnake.Param(description="Third option", default=None),
    option4: str = disnake.Param(description="Fourth option", default=None),
    duration: int = disnake.Param(description="Duration in hours (1-168)", default=24)
):
    # Collect non-empty options
    options = [option1, option2]
    if option3:
        options.append(option3)
    if option4:
        options.append(option4)
    
    # Create poll
    poll_data = Poll.create(
        question=question,
        *options,
        duration=duration,
        allow_multiselect=False
    )
    
    await inter.response.send_message(poll=poll_data)

@bot.slash_command(description="Create a poll with emojis")
async def emoji_poll(inter: disnake.ApplicationCommandInteraction):
    poll_data = Poll.create(
        question=PollMedia("What's your favorite fruit?", "🍎"),
        PollMedia("Apple", "🍎"),
        PollMedia("Banana", "🍌"),
        PollMedia("Orange", "🍊"),
        PollMedia("Grapes", "🍇"),
        duration=48,
        allow_multiselect=True
    )
    
    await inter.response.send_message(
        "Vote for your favorite fruits! (You can pick multiple)",
        poll=poll_data
    )

Advanced Poll Features

from datetime import timedelta

@bot.slash_command(description="Create a multi-select poll")
async def multiselect_poll(inter: disnake.ApplicationCommandInteraction):
    poll_data = Poll.create(
        question="Which programming languages do you know?",
        "Python",
        "JavaScript", 
        "Java",
        "C++",
        "Go",
        "Rust",
        duration=timedelta(days=3),
        allow_multiselect=True
    )
    
    await inter.response.send_message(
        "Select all languages you're familiar with:",
        poll=poll_data
    )

@bot.slash_command(description="Create a timed poll")
async def quick_poll(inter: disnake.ApplicationCommandInteraction):
    poll_data = Poll.create(
        question="Should we start the meeting now?",
        "Yes, let's start",
        "Wait 5 more minutes",
        "Postpone to later",
        duration=1  # 1 hour only
    )
    
    await inter.response.send_message(
        "Quick decision needed:",
        poll=poll_data
    )

Monitoring Poll Results

@bot.event
async def on_message(message):
    # Check if message has a poll
    if message.poll:
        print(f"Poll found: {message.poll.question.text}")
        print(f"Total votes: {message.poll.total_vote_count}")
        
        for answer in message.poll.answers:
            print(f"  {answer.media.text}: {answer.vote_count} votes")

@bot.slash_command(description="Get poll results")
async def poll_results(
    inter: disnake.ApplicationCommandInteraction,
    message_id: str = disnake.Param(description="Message ID containing the poll")
):
    try:
        message = await inter.channel.fetch_message(int(message_id))
        
        if not message.poll:
            await inter.response.send_message("That message doesn't contain a poll.", ephemeral=True)
            return
        
        poll = message.poll
        embed = disnake.Embed(
            title="Poll Results",
            description=f"**{poll.question.text}**",
            color=disnake.Color.blue()
        )
        
        if poll.is_finalized:
            embed.add_field(name="Status", value="✅ Finalized", inline=True)
        elif poll.expiry:
            embed.add_field(name="Expires", value=f"<t:{int(poll.expiry.timestamp())}:R>", inline=True)
        
        embed.add_field(name="Total Votes", value=str(poll.total_vote_count), inline=True)
        
        # Add results for each answer
        for i, answer in enumerate(poll.answers, 1):
            percentage = (answer.vote_count / poll.total_vote_count * 100) if poll.total_vote_count > 0 else 0
            
            value = f"{answer.vote_count} votes ({percentage:.1f}%)"
            if answer.media.emoji:
                name = f"{answer.media.emoji} {answer.media.text}"
            else:
                name = f"{i}. {answer.media.text}"
            
            embed.add_field(name=name, value=value, inline=False)
        
        await inter.response.send_message(embed=embed)
        
    except (ValueError, disnake.NotFound):
        await inter.response.send_message("Message not found.", ephemeral=True)

@bot.slash_command(description="End a poll early")
async def end_poll(
    inter: disnake.ApplicationCommandInteraction,
    message_id: str = disnake.Param(description="Message ID containing the poll")
):
    try:
        message = await inter.channel.fetch_message(int(message_id))
        
        if not message.poll:
            await inter.response.send_message("That message doesn't contain a poll.", ephemeral=True)
            return
        
        if message.poll.is_finalized:
            await inter.response.send_message("That poll is already finalized.", ephemeral=True)
            return
        
        # End the poll
        updated_message = await message.poll.end()
        await inter.response.send_message(f"Poll ended! Final results posted in {message.jump_url}")
        
    except (ValueError, disnake.NotFound):
        await inter.response.send_message("Message not found.", ephemeral=True)
    except disnake.Forbidden:
        await inter.response.send_message("I don't have permission to end that poll.", ephemeral=True)

Getting Poll Voters

@bot.slash_command(description="See who voted for a specific answer")
async def poll_voters(
    inter: disnake.ApplicationCommandInteraction,
    message_id: str = disnake.Param(description="Message ID containing the poll"),
    answer_number: int = disnake.Param(description="Answer number (1, 2, 3, etc.)")
):
    try:
        message = await inter.channel.fetch_message(int(message_id))
        
        if not message.poll:
            await inter.response.send_message("That message doesn't contain a poll.", ephemeral=True)
            return
        
        poll = message.poll
        if answer_number < 1 or answer_number > len(poll.answers):
            await inter.response.send_message(f"Invalid answer number. Must be 1-{len(poll.answers)}.", ephemeral=True)
            return
        
        answer = poll.answers[answer_number - 1]
        voters = []
        
        # Get up to 25 voters for this answer
        async for user in answer.get_voters(limit=25):
            voters.append(user.display_name)
        
        if not voters:
            await inter.response.send_message(f"No votes yet for: {answer.media.text}", ephemeral=True)
            return
        
        embed = disnake.Embed(
            title="Poll Voters",
            description=f"**Answer:** {answer.media.text}\n**Votes:** {answer.vote_count}",
            color=disnake.Color.green()
        )
        
        voters_text = "\n".join(voters)
        if len(voters) == 25:
            voters_text += "\n... and more"
        
        embed.add_field(name="Voters", value=voters_text, inline=False)
        
        await inter.response.send_message(embed=embed)
        
    except (ValueError, disnake.NotFound):
        await inter.response.send_message("Message not found.", ephemeral=True)

Install with Tessl CLI

npx tessl i tessl/pypi-disnake

docs

application-commands.md

automod.md

channels-messaging.md

client-bot.md

command-framework.md

error-handling.md

events-gateway.md

guild-management.md

index.md

interactions-ui.md

localization-i18n.md

permissions-security.md

polls.md

users-members.md

voice-audio.md

tile.json