async def join(self, ctx, exchange_name=""): """ Join the secret santa """ if not util.is_from_guild(ctx): await util.send(ctx, "This message only works in a server") return if exchange_name == "": await ctx.send("You must provide the name of an exchange.\n" + " Example: `!santa join RT2019`") return username = ctx.message.author.display_name user_id = ctx.message.author.id guild_id = ctx.message.guild.id session = self.sessionmaker() # Verify the exchange is created and open for the current guild exchange = session.query(Exchange) \ .filter_by(name=exchange_name, guild_id=guild_id) \ .one_or_none() if exchange is None: session.rollback() session.close() await ctx.send( f"Secret Santa exchange {exchange_name} was not found") return if not exchange.is_open: session.close() await ctx.send( f"Secret Santa exchange {exchange_name} is closed. Please join the next exchange" ) return # Check if the participant has already registered if session.query(Registrant) \ .filter_by(exchange=exchange_name, user_id=user_id) \ .count() > 0: session.rollback() session.close() await util.send(ctx, "Silly goose, you are already registered") return registration = Registrant(exchange=exchange_name, user_id=user_id) session.add(registration) try: session.commit() message = f"{username} has joined the secret santa {exchange_name}!" await util.send(ctx, message) except: session.rollback() message = f"There was an unknown error registering {username} for {exchange_name}!" await util.send(ctx, message) session.close()
async def list(self, ctx, exchange_name=""): """ List the Secret Santa exchanges or participants""" if not util.is_from_guild(ctx): await util.send(ctx, "This message only works in a server") if exchange_name == "": # List the possible exchanges await self.list_exchanges(ctx) else: # List the participants in the exchange await self.list_participants(ctx, exchange_name)
async def list(self, context, contest_name=""): """ List the contests or entries""" if not util.is_from_guild(context): await util.send(context, "This message only works in a server") if contest_name == "": # List the possible contests await self.list_contests(context) else: # List the entries in the contest await self.list_entries(context, contest_name)
async def enter(self, context, contest_name=""): """ Join the contest contest """ if not util.is_from_guild(context): await util.send(context, "This message only works in a server") return if contest_name == "": await context.send("You must provide the name of an contest.\n" + " Example: `!contest enter RT2019`") return username = context.message.author.display_name user_id = context.message.author.id guild_id = context.message.guild.id session = self.sessionmaker() # Verify the contest is created and open for the current guild contest = session.query(Contest) \ .filter_by(name=contest_name, guild_id=guild_id) \ .one_or_none() if contest is None: session.rollback() session.close() await context.send(f"Contest {contest_name} was not found") return if not contest.open: session.rollback() session.close() await context.send( f"Contest {contest_name} is closed. Please join the next contest" ) return # Check if the participant has already registered if session.query(Entry) \ .filter_by(contest=contest.cid, user_id=user_id) \ .count() > 0: session.rollback() session.close() await util.send(context, "No cheating! You already entered") return registration = Entry(contest=contest.cid, user_id=user_id) session.add(registration) try: session.commit() message = f"{username} has joined the contest {contest_name}!" except: session.rollback() message = f"There was an unknown error registering {username} for {contest_name}!" await util.send(context, message) session.close()
async def close(self, context, contest_name=""): """ Closes an open contest """ if not util.is_from_guild(context): await context.send("This command must be run from a server.") return if contest_name == "": await context.send( "You must include an contest name in this command" "\n\tYou must format the command this way: `!drawing close <contest_name>`" ) return guild_id = context.message.guild.id session = self.sessionmaker() contest = session.query(Contest) \ .filter_by(name=contest_name, guild_id=guild_id) \ .one_or_none() if contest is None: session.close() await context.send(f"The contest {contest_name} does not exist.") return if contest.owner_id != context.message.author.id: session.close() print("Author: {}, Owner: {}".format(context.message.author.id, contest.owner_id)) owner_name = context.message.guild.get_member( contest.owner_id).display_name await context.send( f"Only the owner of {contest_name} ({owner_name}) may close it." ) return if not contest.open: session.close() await context.send(f"The contest {contest_name} is already closed." ) return contest.open = False session.commit() await context.send(f"The contest {contest_name} has been closed.")
async def winners(self, context, contest_name=""): """ Show winners for a contest """ syntax = "You must format the command this way: `!contest winners contest_name [num_winners|all]`" if not util.is_from_guild(context): await context.send("This command must be run from a server.") return if contest_name == "": await context.send( "You must include an contest name in this command\n\t" + syntax) return session = self.sessionmaker() guild_id = context.message.guild.id session = self.sessionmaker() contest = (session.query(Contest).filter_by( name=contest_name, guild_id=guild_id).one_or_none()) if contest is None: session.close() await context.send(f"The contest {contest_name} does not exist.") return if contest.num_winners is None: await context.send(f"The contest {contest_name} has no winners") return winners = (session.query(Entry).filter( Entry.contest == contest.cid, Entry.win_rank.isnot(None)).order_by(Entry.win_rank).all()) message = ( "Congrats to the following winners: \n\t" + "\n\t".join([print_rank(winner, context) for winner in winners])) await context.send(message)
async def create(self, ctx, name=""): """ Create a secret santa """ if not util.is_from_guild(ctx): await util.send(ctx, "This message only works in a server") return if name == "": await util.send( ctx, "You must name your secret santa event." + "\nExample: `!santa create RT2019`") return exchange = Exchange( name=name, guild_id=ctx.message.guild.id, owner_id=ctx.message.author.id, is_open=True, ) session = self.sessionmaker() session.add(exchange) try: session.commit() await util.send( ctx, f"The Secret Santa exchange {name} has been created and opened." + f" Santas may join with the command `!santa join {name}`") except IntegrityError: session.rollback() await util.send( ctx, f"The exchange name {name} has already been taken. Please try another" ) except: session.rollback() await util.send( ctx, f"An error occurred trying to create exchange {name}") session.close()
async def open(self, context, name=""): """ Create a contest """ if not util.is_from_guild(context): await util.send(context, "This message only works in a server") return if name == "": await util.send( context, "You must name your contest event." + "\nExample: `!contest create RT2019`") return contest = Contest( name=name, guild_id=context.message.guild.id, owner_id=context.message.author.id, open=True, ) session = self.sessionmaker() session.add(contest) try: session.commit() await util.send( context, f"The contest {name} has been created and opened." + f" You may join with the command `!contest enter {name}`") except IntegrityError: session.rollback() await util.send( context, f"The contest name {name} has already been used. Please try another" ) except: session.rollback() await util.send( context, f"An error occurred trying to create contest {name}") session.close()
async def close(self, context, exchange_name=""): """ Closes an open exchange and matches santas with targets. """ if not util.is_from_guild(context): await context.send("This command must be run from a server.") return if exchange_name == "": await context.send( "You must include an exchange name in this command" "\n\tYou must format the command this way: `!santa close <exchange_name>`" ) return session = self.sessionmaker() exchange = session.query(Exchange).filter_by( name=exchange_name).one_or_none() if exchange is None: session.close() await context.send(f"The exchange {exchange_name} does not exist.") return if exchange.owner_id != context.message.author.id: session.close() print("Author: {}, Owner: {}".format(context.message.author.id, exchange.owner_id)) owner_name = context.message.guild.get_member( exchange.owner_id).display_name await context.send( f"Only the owner of {exchange_name} ({owner_name}) may close it." ) return if not exchange.is_open: session.close() await context.send( f"The exchange {exchange_name} is already closed.") return # Get participants participants = [ part.user_id for part in session.query(Registrant.user_id).filter_by( exchange=exchange_name).all() ] # Remove participants who have left the guild removed_users = list() for participant in participants: if context.message.guild.get_member(participant) is None: participants.remove(participant) user = context.bot.get_user(participant) if user is None: removed_users.append(f"Unknown participant {participant}") else: removed_users.append(user.name + "#" + user.id) if len(removed_users) > 0: await context.send( f"Users who have left the server have been removed from the exchange: " + ", ".join(removed_users)) if len(participants) < 2: session.close() await context.send( f"There must be at least 2 Santas in {exchange_name} for it to close." ) return # Prevent prohibited pairs tries = 0 matches = list() while True: tries = tries + 1 matches = match_santa_pairs(participants) # Check to see if the match is prohibited prohibited = False pairs = matches + [(b, a) for a, b in matches] for pair in pairs: if session.query(ProhibitedMatches).filter( ProhibitedMatches.first_id == pair[0], ProhibitedMatches.second_id == pair[1]).count() > 0: prohibited = True break if not prohibited: break # Break out if we are stuck in a loop if prohibited and tries > 100: session.close() message = f"Unable to make pairs for {exchange_name}. Please add more people and try again." await context.send(message) return pairings = [ Pairing( exchange=exchange_name, santa_id=match[0], target_id=match[1], ) for match in matches ] # Update the database exchange.is_open = False session.add_all(pairings) session.commit() # Alert Santas as to their targets awaits = list() for santa, target in matches: target_name = context.message.guild.get_member(target).display_name message = ( f"Congratulations Santa! You've been assigned {target_name} for {exchange_name}" f"\n\nPlease **reply to this message** using the following commands to message your target." f"\n\t> To send a message to your target, use `!santa message {exchange_name} Your message`" f"\n\t> To send a reply to your santa, use `!santa reply {exchange_name} Your message`" "\n\nIt may be worth setting yourself to invisible while communicating with your target to " "help keep your identity secret.") awaits.append(context.bot.get_user(santa).send(message)) message = f"The Secret Santa exchange {exchange_name} has been closed and PMs have been sent to Santas.\n" awaits.append(context.send(message)) await wait(awaits)
async def draw(self, context, contest_name="", num_winners=""): """ Draw winners for a contest num_winners must be either a positive number, or "all" """ syntax = "You must format the command this way: `!contest draw contest_name [num_winners|all]`" if not util.is_from_guild(context): await context.send("This command must be run from a server.") return if contest_name == "": await context.send( "You must include an contest name in this command\n\t" + syntax) return guild_id = context.message.guild.id session = self.sessionmaker() contest = session.query(Contest) \ .filter_by(name=contest_name, guild_id=guild_id) \ .one_or_none() if contest is None: session.close() await context.send(f"The contest {contest_name} does not exist.") return if contest.owner_id != context.message.author.id: session.close() print("Author: {}, Owner: {}".format(context.message.author.id, contest.owner_id)) owner_name = context.message.guild.get_member( contest.owner_id).display_name await context.send( f"Only the owner of {contest_name} ({owner_name}) may close it." ) return if num_winners == "": num_winners = 1 all_winners = False elif num_winners.lower() == "all": all_winners = True num_winners = 1 # Temporary, until we get number of entries else: try: num_winners = int(num_winners) except: await context.send( f"{num_winners} is not a valid number or 'all'\n\t" + syntax) return if num_winners < 1: await context.send( f"The number of winners must be greater than 1.\n\t" + syntax) return if contest.num_winners is not None: prev_winners = contest.num_winners else: prev_winners = 0 # Get entries who are not winners entries = (session.query(Entry).filter(Entry.contest == contest.cid, Entry.win_rank == None).all()) # Remove winners entries = [entry for entry in entries if entry.win_rank is None] # Remove entries who have left the guild removed_users = list() for entry in entries: if context.message.guild.get_member(entry.user_id) is None: entries.remove(entry) removed_users.append( util.get_displayname(entry.user_id, context)) session.delete(entry) if len(removed_users) > 0: await context.send( f"Users who have left the server have been removed from the contest: " + ", ".join(removed_users)) if all_winners or len(entries) < num_winners: num_winners = len(entries) # Randomize order, then pick the top few as winners shuffle(entries) winners = entries[:num_winners] for rank, winner in enumerate(winners, prev_winners + 1): winner.win_rank = rank contest.num_winners = prev_winners + num_winners # Update the database session.commit() message = ( "Congrats to the following winners: \n\t" + "\n\t".join([print_rank(winner, context) for winner in winners])) await context.send(message)