Skip to content

Commit 7fb985f

Browse files
committed
Add poll functionality (#24)
1 parent aa48b2c commit 7fb985f

File tree

1 file changed

+111
-1
lines changed

1 file changed

+111
-1
lines changed

protobot.py

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@
1212
from dotenv import load_dotenv #
1313
import random #
1414
from discord.ext import commands #
15+
from discord.ext import tasks #
1516
import logging #
1617
import time #
1718
import asyncio #
1819
import activity #
1920
import string #
2021
import schedule #
22+
import threading #
2123
#
2224
#####################################
2325

2426

2527
#####################################
2628
#
2729
COMMAND_PREFIX = '!' #
28-
VERSION = "v0.4.1-alpha" #
30+
VERSION = "v0.5.0-alpha" #
2931
ACTIVITY = discord.Game("!help") #
3032
LOG_LEVEL = logging.INFO #
3133
#
@@ -74,6 +76,7 @@ async def on_ready():
7476
for guild in bot.guilds:
7577
add_all_users_from_guild_to_database(guild)
7678

79+
# Start scheduled events
7780
logging.info("Scheduling events...")
7881
logging.info("Daily events")
7982
schedule.every().day.at("00:00").do(run_once_every_day)
@@ -82,6 +85,8 @@ async def on_ready():
8285
logging.info("Minutely events")
8386
schedule.every().minute.do(run_once_every_minute)
8487
logging.info("Scheduling complete!")
88+
t = threading.Thread(target=schedule_worker)
89+
t.start()
8590

8691

8792
@bot.event
@@ -293,6 +298,104 @@ async def about(ctx):
293298
await ctx.message.channel.send(f"ProtoBot {VERSION}. Source code and bug tracker: https://github.com/klercke/ProtoBot")
294299

295300

301+
@bot.command(name="poll", help="Creates a poll. Usage: !poll \"PROMPT\" TIME OPTION1 OPTION2 [OPTION3...]")
302+
async def poll(ctx):
303+
"""
304+
Allows users to create timed polls
305+
"""
306+
307+
user_input = ctx.message.content
308+
user = ctx.message.author
309+
310+
# Get the user's question
311+
prompt = ""
312+
writing = False
313+
count = 0
314+
for character in user_input:
315+
count += 1
316+
if not writing and character == "\"":
317+
writing = True
318+
elif writing:
319+
if character == "\"":
320+
writing = False
321+
break
322+
else:
323+
prompt += character
324+
elif count == len(user_input) - 1:
325+
await ctx.channel.send("Sorry, I couldn't understand your command. Please make sure the poll question is in quotes.")
326+
return
327+
328+
# Get the TTL for the poll
329+
user_input = user_input[count + 1:]
330+
if not user_input:
331+
await ctx.channel.send("You didn't provide a time. Assuming 5 minutes.")
332+
poll_time = "5m"
333+
else:
334+
poll_time = ""
335+
count = 0
336+
for character in user_input:
337+
count += 1
338+
if character != " ":
339+
poll_time += character
340+
else:
341+
break
342+
343+
unit_long = ""
344+
unit = poll_time[-1].lower()
345+
poll_time = int(poll_time[:-1])
346+
poll_time_in_sec = 0
347+
if unit == 's':
348+
unit_long = "seconds"
349+
poll_time_in_sec = poll_time
350+
elif unit == 'm':
351+
unit_long = "minutes"
352+
poll_time_in_sec = poll_time * 60
353+
elif unit == 'h':
354+
unit_long = "hours"
355+
poll_time_in_sec = poll_time * 3600
356+
elif unit == 'd':
357+
unit_long = "days"
358+
poll_time_in_sec = poll_time * 86400
359+
360+
361+
# Get emoji options
362+
user_input = user_input[count:]
363+
user_input = user_input.split()
364+
options = []
365+
message_sent = await ctx.message.channel.send(f"<@{user.id}> has started a poll:\n{prompt}\nVoting will last {poll_time} {unit_long}.")
366+
for emoji in user_input:
367+
options += emoji
368+
await message_sent.add_reaction(emoji)
369+
370+
async def count_poll_results(message_sent, poll_time_in_sec):
371+
# Wait for voting to finish
372+
await asyncio.sleep(poll_time_in_sec)
373+
374+
# Create dictionary for results
375+
results = {}
376+
for option in options:
377+
results[option] = 0
378+
379+
# Get original message
380+
message_sent = await message_sent.channel.fetch_message(message_sent.id)
381+
382+
# Count results
383+
total_votes = 0
384+
for reaction in message_sent.reactions:
385+
results[reaction.emoji] = reaction.count - 1
386+
total_votes += reaction.count - 1
387+
388+
if total_votes == 0:
389+
await message_sent.channel.send(f"Voting for \"{prompt}\" complete. Nobody voted!")
390+
else:
391+
# Display results
392+
winner = max(results, key = results.get)
393+
winner_percentage = round((results[winner] / total_votes) * 100, 2)
394+
await message_sent.channel.send(f"Voting for \"{prompt}\" complete. {winner} is the winner with {results[winner]} ({winner_percentage}%) votes!")
395+
396+
await count_poll_results(message_sent, poll_time_in_sec)
397+
398+
296399
def run_once_every_day():
297400
"""
298401
Runs a block of code every day.
@@ -353,6 +456,13 @@ def add_user_to_database(uuid, name, score=0, allowmoderator=True, rankexempt=Fa
353456
activity.write_database()
354457

355458

459+
def schedule_worker():
460+
logging.info("Schedule worker thread initialized.")
461+
while True:
462+
schedule.run_pending()
463+
time.sleep(1)
464+
465+
356466
def main():
357467
# Load bot token from .env
358468
load_dotenv()

0 commit comments

Comments
 (0)