2016-10-11 15:58:05 +13:00
|
|
|
from discord.ext import commands
|
2017-03-17 18:21:30 +13:00
|
|
|
from . import utils
|
2016-10-11 15:58:05 +13:00
|
|
|
|
2017-03-17 18:21:30 +13:00
|
|
|
import discord
|
2016-10-13 15:12:48 +13:00
|
|
|
import random
|
2016-10-11 15:58:05 +13:00
|
|
|
import pendulum
|
|
|
|
import re
|
2016-10-13 15:12:48 +13:00
|
|
|
import asyncio
|
2016-10-13 17:36:19 +13:00
|
|
|
import traceback
|
2016-10-13 15:12:48 +13:00
|
|
|
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
class Raffle:
|
|
|
|
def __init__(self, bot):
|
|
|
|
self.bot = bot
|
2016-10-13 15:23:01 +13:00
|
|
|
self.bot.loop.create_task(self.raffle_task())
|
2016-10-13 15:12:48 +13:00
|
|
|
|
|
|
|
async def raffle_task(self):
|
|
|
|
while True:
|
2016-10-13 17:36:19 +13:00
|
|
|
try:
|
|
|
|
await self.check_raffles()
|
|
|
|
except Exception as error:
|
|
|
|
with open("error_log", 'a') as f:
|
|
|
|
traceback.print_tb(error.__traceback__, file=f)
|
|
|
|
print('{0.__class__.__name__}: {0}'.format(error), file=f)
|
2016-10-13 15:12:48 +13:00
|
|
|
await asyncio.sleep(900)
|
2016-10-11 15:58:05 +13:00
|
|
|
|
2016-10-11 16:08:42 +13:00
|
|
|
async def check_raffles(self):
|
2016-10-11 15:58:05 +13:00
|
|
|
# This is used to periodically check the current raffles, and see if they have ended yet
|
|
|
|
# If the raffle has ended, we'll pick a winner from the entrants
|
2017-03-17 18:21:30 +13:00
|
|
|
raffles = await utils.get_content('raffles')
|
2016-10-13 15:12:48 +13:00
|
|
|
|
2016-10-14 16:26:54 +13:00
|
|
|
if raffles is None:
|
|
|
|
return
|
|
|
|
|
2016-10-13 15:12:48 +13:00
|
|
|
for raffle in raffles:
|
|
|
|
server = self.bot.get_server(raffle['server_id'])
|
|
|
|
|
|
|
|
# Check to see if this cog can find the server in question
|
|
|
|
if server is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
now = pendulum.utcnow()
|
|
|
|
expires = pendulum.parse(raffle['expires'])
|
|
|
|
|
|
|
|
# Now lets compare and see if this raffle has ended, if not just continue
|
2016-10-13 17:47:24 +13:00
|
|
|
if expires > now:
|
2016-10-13 15:12:48 +13:00
|
|
|
continue
|
|
|
|
|
|
|
|
title = raffle['title']
|
|
|
|
entrants = raffle['entrants']
|
|
|
|
raffle_id = raffle['id']
|
|
|
|
|
|
|
|
# Make sure there are actually entrants
|
|
|
|
if len(entrants) == 0:
|
|
|
|
fmt = 'Sorry, but there were no entrants for the raffle `{}`!'.format(title)
|
|
|
|
else:
|
|
|
|
winner = None
|
|
|
|
count = 0
|
|
|
|
while winner is None:
|
|
|
|
winner = server.get_member(random.SystemRandom().choice(entrants))
|
|
|
|
|
|
|
|
# Lets make sure we don't get caught in an infinite loop
|
|
|
|
# Realistically having more than 25 random entrants found that aren't in the server anymore
|
|
|
|
# Isn't something that should be an issue
|
|
|
|
count += 1
|
|
|
|
if count >= 25:
|
|
|
|
break
|
|
|
|
|
|
|
|
if winner is None:
|
|
|
|
fmt = 'I couldn\'t find an entrant that is still in this server, for the raffle `{}`!'.format(title)
|
|
|
|
else:
|
|
|
|
fmt = 'The raffle `{}` has just ended! The winner is {}!'.format(title, winner.display_name)
|
|
|
|
|
|
|
|
# No matter which one of these matches were met, the raffle has ended and we want to remove it
|
|
|
|
# We don't have to wait for it however, so create a task for it
|
2017-03-17 18:21:30 +13:00
|
|
|
self.bot.loop.create_task(utils.remove_content('raffles', raffle_id ))
|
|
|
|
|
|
|
|
server_settings = await utils.get_content('server_settings', str(server.id))
|
|
|
|
channel_id = server_settings.get('notification_channel', server.id)
|
|
|
|
channel = self.bot.get_channel(channel_id)
|
2017-02-07 16:56:52 +13:00
|
|
|
try:
|
2017-03-17 18:21:30 +13:00
|
|
|
await channel.send(fmt)
|
2017-02-07 16:56:52 +13:00
|
|
|
except discord.Forbidden:
|
|
|
|
pass
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
@commands.command(pass_context=True, no_pm=True)
|
2017-03-17 18:21:30 +13:00
|
|
|
@utils.custom_perms(send_messages=True)
|
2016-10-11 15:58:05 +13:00
|
|
|
async def raffles(self, ctx):
|
2016-11-29 17:55:55 +13:00
|
|
|
"""Used to print the current running raffles on the server
|
|
|
|
|
|
|
|
EXAMPLE: !raffles
|
|
|
|
RESULT: A list of the raffles setup on this server"""
|
2016-10-11 15:58:05 +13:00
|
|
|
r_filter = {'server_id': ctx.message.server.id}
|
2017-03-17 18:21:30 +13:00
|
|
|
raffles = await utils.filter_content('raffles', r_filter)
|
2016-10-11 15:58:05 +13:00
|
|
|
if raffles is None:
|
|
|
|
await self.bot.say("There are currently no raffles setup on this server!")
|
|
|
|
return
|
|
|
|
|
2016-10-11 16:14:45 +13:00
|
|
|
fmt = "\n\n".join("**Raffle:** {}\n**Title:** {}\n**Total Entrants:** {}\n**Ends:** {} UTC".format(
|
2016-10-13 15:12:48 +13:00
|
|
|
num + 1,
|
|
|
|
raffle['title'],
|
|
|
|
len(raffle['entrants']),
|
|
|
|
raffle['expires']) for num, raffle in enumerate(raffles))
|
2016-10-11 16:12:23 +13:00
|
|
|
await self.bot.say(fmt)
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
@commands.group(pass_context=True, no_pm=True, invoke_without_command=True)
|
2017-03-17 18:21:30 +13:00
|
|
|
@utils.custom_perms(send_messages=True)
|
2016-10-11 15:58:05 +13:00
|
|
|
async def raffle(self, ctx, raffle_id: int = 0):
|
|
|
|
"""Used to enter a raffle running on this server
|
2016-11-29 17:55:55 +13:00
|
|
|
If there is more than one raffle running, provide an ID of the raffle you want to enter
|
|
|
|
|
|
|
|
EXAMPLE: !raffle 1
|
|
|
|
RESULT: You've entered the first raffle!"""
|
2016-10-11 15:58:05 +13:00
|
|
|
# Lets let people use 1 - (length of raffles) and handle 0 base ourselves
|
|
|
|
raffle_id -= 1
|
|
|
|
r_filter = {'server_id': ctx.message.server.id}
|
|
|
|
author = ctx.message.author
|
|
|
|
|
2017-03-17 18:21:30 +13:00
|
|
|
raffles = await utils.filter_content('raffles', r_filter)
|
2016-10-11 15:58:05 +13:00
|
|
|
if raffles is None:
|
|
|
|
await self.bot.say("There are currently no raffles setup on this server!")
|
|
|
|
return
|
|
|
|
|
|
|
|
raffle_count = len(raffles)
|
|
|
|
|
|
|
|
# There is only one raffle, so use the first's info
|
|
|
|
if raffle_count == 1:
|
|
|
|
entrants = raffles[0]['entrants']
|
|
|
|
# Lets make sure that the user hasn't already entered the raffle
|
|
|
|
if author.id in entrants:
|
|
|
|
await self.bot.say("You have already entered this raffle!")
|
|
|
|
return
|
|
|
|
entrants.append(author.id)
|
|
|
|
|
|
|
|
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
|
|
|
|
update = {'entrants': entrants}
|
2017-03-17 18:21:30 +13:00
|
|
|
await utils.update_content('raffles', update, raffles[0]['id'])
|
2016-10-11 15:58:05 +13:00
|
|
|
await self.bot.say("{} you have just entered the raffle!".format(author.mention))
|
|
|
|
# Otherwise, make sure the author gave a valid raffle_id
|
2016-10-14 10:23:25 +13:00
|
|
|
elif raffle_id in range(raffle_count - 1):
|
2016-10-11 15:58:05 +13:00
|
|
|
entrants = raffles[raffle_id]['entrants']
|
|
|
|
|
|
|
|
# Lets make sure that the user hasn't already entered the raffle
|
|
|
|
if author.id in entrants:
|
|
|
|
await self.bot.say("You have already entered this raffle!")
|
|
|
|
return
|
|
|
|
entrants.append(author.id)
|
|
|
|
|
|
|
|
update = {'entrants': entrants}
|
2017-03-17 18:21:30 +13:00
|
|
|
await utils.update_content('raffles', update, raffles[raffle_id]['id'])
|
2016-10-11 15:58:05 +13:00
|
|
|
await self.bot.say("{} you have just entered the raffle!".format(author.mention))
|
|
|
|
else:
|
2016-10-13 15:12:48 +13:00
|
|
|
fmt = "Please provide a valid raffle ID, as there are more than one setup on the server! " \
|
|
|
|
"There are currently `{}` raffles running, use {}raffles to view the current running raffles".format(
|
|
|
|
raffle_count, ctx.prefix)
|
2016-10-11 15:58:05 +13:00
|
|
|
await self.bot.say(fmt)
|
|
|
|
|
|
|
|
@raffle.command(pass_context=True, no_pm=True, name='create', aliases=['start', 'begin', 'add'])
|
2017-03-17 18:21:30 +13:00
|
|
|
@utils.custom_perms(kick_members=True)
|
2016-10-11 15:58:05 +13:00
|
|
|
async def raffle_create(self, ctx):
|
2016-11-29 17:55:55 +13:00
|
|
|
"""This is used in order to create a new server raffle
|
|
|
|
|
|
|
|
EXAMPLE: !raffle create
|
|
|
|
RESULT: A follow-along for setting up a new raffle"""
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
author = ctx.message.author
|
|
|
|
server = ctx.message.server
|
|
|
|
channel = ctx.message.channel
|
|
|
|
now = pendulum.utcnow()
|
|
|
|
|
2016-10-13 15:12:48 +13:00
|
|
|
await self.bot.say(
|
|
|
|
"Ready to start a new raffle! Please respond with the title you would like to use for this raffle!")
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
msg = await self.bot.wait_for_message(author=author, channel=channel, timeout=120)
|
|
|
|
if msg is None:
|
|
|
|
await self.bot.say("You took too long! >:c")
|
|
|
|
return
|
|
|
|
|
|
|
|
title = msg.content
|
|
|
|
|
|
|
|
fmt = "Alright, your new raffle will be titled:\n\n{}\n\nHow long would you like this raffle to run for? " \
|
2016-10-13 15:12:48 +13:00
|
|
|
"The format should be [number] [length] for example, `2 days` or `1 hour` or `30 minutes` etc. " \
|
|
|
|
"The minimum for this is 10 minutes, and the maximum is 3 months"
|
2016-10-11 16:08:42 +13:00
|
|
|
await self.bot.say(fmt.format(title))
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
# Our check to ensure that a proper length of time was passed
|
|
|
|
check = lambda m: re.search("\d+ (minutes?|hours?|days?|weeks?|months?)", m.content.lower()) is not None
|
|
|
|
msg = await self.bot.wait_for_message(author=author, channel=channel, timeout=120, check=check)
|
|
|
|
if msg is None:
|
|
|
|
await self.bot.say("You took too long! >:c")
|
|
|
|
return
|
|
|
|
|
|
|
|
# Lets get the length provided, based on the number and type passed
|
|
|
|
num, term = re.search("\d+ (minutes?|hours?|days?|weeks?|months?)", msg.content.lower()).group(0).split(' ')
|
2016-10-11 16:08:42 +13:00
|
|
|
# This should be safe to convert, we already made sure with our check earlier this would match
|
|
|
|
num = int(num)
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
# Now lets ensure this meets our min/max
|
2016-10-13 15:31:36 +13:00
|
|
|
if "minute" in term and (num < 10 or num > 129600):
|
2016-10-13 15:12:48 +13:00
|
|
|
await self.bot.say(
|
|
|
|
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
|
2016-10-11 15:58:05 +13:00
|
|
|
return
|
|
|
|
elif "hour" in term and num > 2160:
|
2016-10-13 15:12:48 +13:00
|
|
|
await self.bot.say(
|
|
|
|
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
|
2016-10-11 15:58:05 +13:00
|
|
|
return
|
|
|
|
elif "day" in term and num > 90:
|
2016-10-13 15:12:48 +13:00
|
|
|
await self.bot.say(
|
|
|
|
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
|
2016-10-11 15:58:05 +13:00
|
|
|
return
|
|
|
|
elif "week" in term and num > 12:
|
2016-10-13 15:12:48 +13:00
|
|
|
await self.bot.say(
|
|
|
|
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
|
2016-10-11 15:58:05 +13:00
|
|
|
return
|
|
|
|
elif "month" in term and num > 3:
|
2016-10-13 15:12:48 +13:00
|
|
|
await self.bot.say(
|
|
|
|
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
|
2016-10-11 15:58:05 +13:00
|
|
|
return
|
|
|
|
|
2016-10-11 16:08:42 +13:00
|
|
|
# Pendulum only accepts the plural version of terms, lets make sure this is added
|
|
|
|
term = term if term.endswith('s') else '{}s'.format(term)
|
2016-10-11 15:58:05 +13:00
|
|
|
# If we're in the range, lets just pack this in a dictionary we can pass to set the time we want, then set that
|
|
|
|
payload = {term: num}
|
2016-10-11 16:08:42 +13:00
|
|
|
expires = now.add(**payload)
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
# Now we're ready to add this as a new raffle
|
|
|
|
entry = {'title': title,
|
2016-10-13 15:12:48 +13:00
|
|
|
'expires': expires.to_datetime_string(),
|
|
|
|
'entrants': [],
|
|
|
|
'author': author.id,
|
|
|
|
'server_id': server.id}
|
2016-10-11 15:58:05 +13:00
|
|
|
|
|
|
|
# We don't want to pass a filter to this, because we can have multiple raffles per server
|
2017-03-17 18:21:30 +13:00
|
|
|
await utils.add_content('raffles', entry)
|
2016-10-11 16:12:23 +13:00
|
|
|
await self.bot.say("I have just saved your new raffle!")
|
2016-10-11 15:58:05 +13:00
|
|
|
|
2016-10-13 15:12:48 +13:00
|
|
|
|
2016-10-11 15:58:05 +13:00
|
|
|
def setup(bot):
|
|
|
|
bot.add_cog(Raffle(bot))
|