12
12
from dotenv import load_dotenv #
13
13
import random #
14
14
from discord .ext import commands #
15
+ from discord .ext import tasks #
15
16
import logging #
16
17
import time #
17
18
import asyncio #
18
19
import activity #
19
20
import string #
20
21
import schedule #
22
+ import threading #
21
23
#
22
24
#####################################
23
25
24
26
25
27
#####################################
26
28
#
27
29
COMMAND_PREFIX = '!' #
28
- VERSION = "v0.4.1 -alpha" #
30
+ VERSION = "v0.5.0 -alpha" #
29
31
ACTIVITY = discord .Game ("!help" ) #
30
32
LOG_LEVEL = logging .INFO #
31
33
#
@@ -74,6 +76,7 @@ async def on_ready():
74
76
for guild in bot .guilds :
75
77
add_all_users_from_guild_to_database (guild )
76
78
79
+ # Start scheduled events
77
80
logging .info ("Scheduling events..." )
78
81
logging .info ("Daily events" )
79
82
schedule .every ().day .at ("00:00" ).do (run_once_every_day )
@@ -82,6 +85,8 @@ async def on_ready():
82
85
logging .info ("Minutely events" )
83
86
schedule .every ().minute .do (run_once_every_minute )
84
87
logging .info ("Scheduling complete!" )
88
+ t = threading .Thread (target = schedule_worker )
89
+ t .start ()
85
90
86
91
87
92
@bot .event
@@ -293,6 +298,104 @@ async def about(ctx):
293
298
await ctx .message .channel .send (f"ProtoBot { VERSION } . Source code and bug tracker: https://github.com/klercke/ProtoBot" )
294
299
295
300
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 } \n Voting 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
+
296
399
def run_once_every_day ():
297
400
"""
298
401
Runs a block of code every day.
@@ -353,6 +456,13 @@ def add_user_to_database(uuid, name, score=0, allowmoderator=True, rankexempt=Fa
353
456
activity .write_database ()
354
457
355
458
459
+ def schedule_worker ():
460
+ logging .info ("Schedule worker thread initialized." )
461
+ while True :
462
+ schedule .run_pending ()
463
+ time .sleep (1 )
464
+
465
+
356
466
def main ():
357
467
# Load bot token from .env
358
468
load_dotenv ()
0 commit comments