0
# Poll System
1
2
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.
3
4
## Capabilities
5
6
### Poll Media
7
8
Represents the content of poll questions and answers, supporting both text and emoji elements.
9
10
```python { .api }
11
class PollMedia:
12
def __init__(
13
self,
14
text: str,
15
emoji: Optional[Union[Emoji, PartialEmoji, str]] = None
16
):
17
"""
18
Create poll media (question or answer content).
19
20
Parameters:
21
- text: Text content for the media
22
- emoji: Optional emoji to display with the text
23
"""
24
25
@property
26
def text(self) -> Optional[str]:
27
"""Text content of the media."""
28
29
@property
30
def emoji(self) -> Optional[Union[Emoji, PartialEmoji]]:
31
"""Emoji associated with the media."""
32
```
33
34
### Poll Answer
35
36
Represents a single answer option in a poll with vote tracking.
37
38
```python { .api }
39
class PollAnswer:
40
def __init__(self, *, data: PollAnswerPayload):
41
"""
42
Poll answer option.
43
44
Parameters:
45
- data: Raw answer data from Discord API
46
"""
47
48
@property
49
def id(self) -> int:
50
"""Answer ID."""
51
52
@property
53
def media(self) -> PollMedia:
54
"""Media content for this answer."""
55
56
@property
57
def vote_count(self) -> int:
58
"""Number of votes this answer has received."""
59
60
@property
61
def me_voted(self) -> bool:
62
"""Whether the current user voted for this answer."""
63
64
def get_voters(self, *, limit: Optional[int] = None, after: Optional[Snowflake] = None) -> PollAnswerIterator:
65
"""
66
Get users who voted for this answer.
67
68
Parameters:
69
- limit: Maximum number of voters to retrieve
70
- after: Get voters after this user ID
71
72
Returns:
73
Iterator for poll answer voters
74
"""
75
```
76
77
### Poll
78
79
Complete poll object with question, answers, and configuration.
80
81
```python { .api }
82
class Poll:
83
def __init__(self, *, data: PollPayload, state: ConnectionState):
84
"""
85
Discord poll instance.
86
87
Parameters:
88
- data: Raw poll data from Discord API
89
- state: Connection state for API calls
90
"""
91
92
@property
93
def question(self) -> PollMedia:
94
"""Poll question content."""
95
96
@property
97
def answers(self) -> List[PollAnswer]:
98
"""All answer options for the poll."""
99
100
@property
101
def expiry(self) -> Optional[datetime]:
102
"""When the poll expires and stops accepting votes."""
103
104
@property
105
def allow_multiselect(self) -> bool:
106
"""Whether users can select multiple answers."""
107
108
@property
109
def layout_type(self) -> PollLayoutType:
110
"""Layout type for displaying the poll."""
111
112
@property
113
def total_vote_count(self) -> int:
114
"""Total number of votes across all answers."""
115
116
@property
117
def is_finalized(self) -> bool:
118
"""Whether the poll has been finalized (expired or manually ended)."""
119
120
def get_answer(self, answer_id: int) -> Optional[PollAnswer]:
121
"""
122
Get a specific answer by ID.
123
124
Parameters:
125
- answer_id: Answer ID to look up
126
127
Returns:
128
PollAnswer if found, None otherwise
129
"""
130
131
async def end(self) -> Message:
132
"""
133
End the poll immediately.
134
135
Returns:
136
Updated message with finalized poll
137
"""
138
139
@classmethod
140
def create(
141
cls,
142
question: Union[str, PollMedia],
143
*answers: Union[str, PollMedia],
144
duration: Optional[Union[int, timedelta]] = None,
145
allow_multiselect: bool = False,
146
) -> PollCreatePayload:
147
"""
148
Create poll data for sending in a message.
149
150
Parameters:
151
- question: Poll question text or media
152
- answers: Answer options (2-10 answers required)
153
- duration: Poll duration in hours (1-168) or timedelta
154
- allow_multiselect: Whether to allow multiple answer selection
155
156
Returns:
157
Poll creation payload for message sending
158
"""
159
```
160
161
## Usage Examples
162
163
### Creating Basic Polls
164
165
```python
166
import disnake
167
from disnake import Poll, PollMedia
168
169
@bot.slash_command(description="Create a simple poll")
170
async def poll(
171
inter: disnake.ApplicationCommandInteraction,
172
question: str = disnake.Param(description="Poll question"),
173
option1: str = disnake.Param(description="First option"),
174
option2: str = disnake.Param(description="Second option"),
175
option3: str = disnake.Param(description="Third option", default=None),
176
option4: str = disnake.Param(description="Fourth option", default=None),
177
duration: int = disnake.Param(description="Duration in hours (1-168)", default=24)
178
):
179
# Collect non-empty options
180
options = [option1, option2]
181
if option3:
182
options.append(option3)
183
if option4:
184
options.append(option4)
185
186
# Create poll
187
poll_data = Poll.create(
188
question=question,
189
*options,
190
duration=duration,
191
allow_multiselect=False
192
)
193
194
await inter.response.send_message(poll=poll_data)
195
196
@bot.slash_command(description="Create a poll with emojis")
197
async def emoji_poll(inter: disnake.ApplicationCommandInteraction):
198
poll_data = Poll.create(
199
question=PollMedia("What's your favorite fruit?", "π"),
200
PollMedia("Apple", "π"),
201
PollMedia("Banana", "π"),
202
PollMedia("Orange", "π"),
203
PollMedia("Grapes", "π"),
204
duration=48,
205
allow_multiselect=True
206
)
207
208
await inter.response.send_message(
209
"Vote for your favorite fruits! (You can pick multiple)",
210
poll=poll_data
211
)
212
```
213
214
### Advanced Poll Features
215
216
```python
217
from datetime import timedelta
218
219
@bot.slash_command(description="Create a multi-select poll")
220
async def multiselect_poll(inter: disnake.ApplicationCommandInteraction):
221
poll_data = Poll.create(
222
question="Which programming languages do you know?",
223
"Python",
224
"JavaScript",
225
"Java",
226
"C++",
227
"Go",
228
"Rust",
229
duration=timedelta(days=3),
230
allow_multiselect=True
231
)
232
233
await inter.response.send_message(
234
"Select all languages you're familiar with:",
235
poll=poll_data
236
)
237
238
@bot.slash_command(description="Create a timed poll")
239
async def quick_poll(inter: disnake.ApplicationCommandInteraction):
240
poll_data = Poll.create(
241
question="Should we start the meeting now?",
242
"Yes, let's start",
243
"Wait 5 more minutes",
244
"Postpone to later",
245
duration=1 # 1 hour only
246
)
247
248
await inter.response.send_message(
249
"Quick decision needed:",
250
poll=poll_data
251
)
252
```
253
254
### Monitoring Poll Results
255
256
```python
257
@bot.event
258
async def on_message(message):
259
# Check if message has a poll
260
if message.poll:
261
print(f"Poll found: {message.poll.question.text}")
262
print(f"Total votes: {message.poll.total_vote_count}")
263
264
for answer in message.poll.answers:
265
print(f" {answer.media.text}: {answer.vote_count} votes")
266
267
@bot.slash_command(description="Get poll results")
268
async def poll_results(
269
inter: disnake.ApplicationCommandInteraction,
270
message_id: str = disnake.Param(description="Message ID containing the poll")
271
):
272
try:
273
message = await inter.channel.fetch_message(int(message_id))
274
275
if not message.poll:
276
await inter.response.send_message("That message doesn't contain a poll.", ephemeral=True)
277
return
278
279
poll = message.poll
280
embed = disnake.Embed(
281
title="Poll Results",
282
description=f"**{poll.question.text}**",
283
color=disnake.Color.blue()
284
)
285
286
if poll.is_finalized:
287
embed.add_field(name="Status", value="β Finalized", inline=True)
288
elif poll.expiry:
289
embed.add_field(name="Expires", value=f"<t:{int(poll.expiry.timestamp())}:R>", inline=True)
290
291
embed.add_field(name="Total Votes", value=str(poll.total_vote_count), inline=True)
292
293
# Add results for each answer
294
for i, answer in enumerate(poll.answers, 1):
295
percentage = (answer.vote_count / poll.total_vote_count * 100) if poll.total_vote_count > 0 else 0
296
297
value = f"{answer.vote_count} votes ({percentage:.1f}%)"
298
if answer.media.emoji:
299
name = f"{answer.media.emoji} {answer.media.text}"
300
else:
301
name = f"{i}. {answer.media.text}"
302
303
embed.add_field(name=name, value=value, inline=False)
304
305
await inter.response.send_message(embed=embed)
306
307
except (ValueError, disnake.NotFound):
308
await inter.response.send_message("Message not found.", ephemeral=True)
309
310
@bot.slash_command(description="End a poll early")
311
async def end_poll(
312
inter: disnake.ApplicationCommandInteraction,
313
message_id: str = disnake.Param(description="Message ID containing the poll")
314
):
315
try:
316
message = await inter.channel.fetch_message(int(message_id))
317
318
if not message.poll:
319
await inter.response.send_message("That message doesn't contain a poll.", ephemeral=True)
320
return
321
322
if message.poll.is_finalized:
323
await inter.response.send_message("That poll is already finalized.", ephemeral=True)
324
return
325
326
# End the poll
327
updated_message = await message.poll.end()
328
await inter.response.send_message(f"Poll ended! Final results posted in {message.jump_url}")
329
330
except (ValueError, disnake.NotFound):
331
await inter.response.send_message("Message not found.", ephemeral=True)
332
except disnake.Forbidden:
333
await inter.response.send_message("I don't have permission to end that poll.", ephemeral=True)
334
```
335
336
### Getting Poll Voters
337
338
```python
339
@bot.slash_command(description="See who voted for a specific answer")
340
async def poll_voters(
341
inter: disnake.ApplicationCommandInteraction,
342
message_id: str = disnake.Param(description="Message ID containing the poll"),
343
answer_number: int = disnake.Param(description="Answer number (1, 2, 3, etc.)")
344
):
345
try:
346
message = await inter.channel.fetch_message(int(message_id))
347
348
if not message.poll:
349
await inter.response.send_message("That message doesn't contain a poll.", ephemeral=True)
350
return
351
352
poll = message.poll
353
if answer_number < 1 or answer_number > len(poll.answers):
354
await inter.response.send_message(f"Invalid answer number. Must be 1-{len(poll.answers)}.", ephemeral=True)
355
return
356
357
answer = poll.answers[answer_number - 1]
358
voters = []
359
360
# Get up to 25 voters for this answer
361
async for user in answer.get_voters(limit=25):
362
voters.append(user.display_name)
363
364
if not voters:
365
await inter.response.send_message(f"No votes yet for: {answer.media.text}", ephemeral=True)
366
return
367
368
embed = disnake.Embed(
369
title="Poll Voters",
370
description=f"**Answer:** {answer.media.text}\n**Votes:** {answer.vote_count}",
371
color=disnake.Color.green()
372
)
373
374
voters_text = "\n".join(voters)
375
if len(voters) == 25:
376
voters_text += "\n... and more"
377
378
embed.add_field(name="Voters", value=voters_text, inline=False)
379
380
await inter.response.send_message(embed=embed)
381
382
except (ValueError, disnake.NotFound):
383
await inter.response.send_message("Message not found.", ephemeral=True)
384
```