diff --git a/README.md b/README.md index 410a67a..109d208 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,6 @@ # aikaterna-cogs -Cogs for Red-DiscordBot by Twentysix26. +v3 Cogs for Red-DiscordBot by Twentysix26. -autoeconomy - New users that join the server will be automatically given a bank account. - -away - By Paddolicious#8880. Set and unset a user as being "away". - -cah - Cards Against Humanity. A port of CorpBot's module: https://github.com/corpnewt/CorpBot.py - -chatchart - Generates a pie chart to display chat activity over the last 5000 messages. Requested by violetnyte. - -forwarding - Forwards DMs sent to the bot to the owner of the bot. A port of the forwarding module from: https://github.com/jacobcheatley/dankbot - -hunting - By Paddolicious#8880. It hunts birds... and things that fly. - -imgwelcome - Welcome users to your server with a customized image. - -otherbot - Have multiple Red instances and want to know when one goes offline? Edit this cog and load it on your watcher bot. - -pug - Warcraft pug checker. A port of PugBot's module: https://github.com/reznok/PugBot - -radio - A hidden unpublished gem from Paddo, with a couple edits. Plays http audio streams like icecast and mp3 streams. - -riot - an old Fredboat command, requested by Mewleficent. - -seen - By Paddolicious#8880. Check when the user was last active on a server. - -serverlimit - Limit the bot to joining servers with over 25 members. - - -The chatterbot cog that was previously on this repo has been removed in favor of: https://github.com/Nobleasskicker/GrandeCogs/tree/master/chat - -Q: Why do you have some of Paddo's cogs on your repo? -A: He recently made an announcement about finding a new home for a few cogs. I've picked up a couple and I welcome PRs or feature requests. I'm also intending to port these for Red v3 as I can. +away - Originally by Paddo, written for v3 by Axas, final tests by aikaterna. Set and unset a user as being "away". Feel free to join the server for these cogs if you'd like. https://discord.gg/th6eS3T diff --git a/autoeconomy/autoeconomy.py b/autoeconomy/autoeconomy.py deleted file mode 100644 index 6c03e6a..0000000 --- a/autoeconomy/autoeconomy.py +++ /dev/null @@ -1,129 +0,0 @@ -import asyncio -import discord -import os -from __main__ import send_cmd_help -from cogs.utils import checks -from cogs.utils.dataIO import dataIO -from copy import deepcopy -from discord.ext import commands - -default_settings = {"CHANNEL": None, - "DEBUG": False, - "TOGGLE": False, - } - - -class AutoEconomy: - """Auto-registers new users to the bank. Must have Economy loaded.""" - - def __init__(self, bot): - self.bot = bot - self.settings = dataIO.load_json('data/autoeconomy/settings.json') - self.banksettings = dataIO.load_json('data/economy/settings.json') - self.version = "0.1.1b" - - async def save_settings(self): - dataIO.save_json('data/autoeconomy/settings.json', self.settings) - - async def _data_check(self, ctx): - server = ctx.message.server - if server.id not in self.settings: - self.settings[server.id] = deepcopy(default_settings) - self.settings[server.id]["CHANNEL"] = ctx.message.channel.id - await self.save_settings() - econ_cog = self.bot.get_cog('Economy') - if not econ_cog: - await self.bot.say("You must have Economy loaded to use this cog. \nAny settings saved will not work until the cog is loaded.") - return - - @checks.admin_or_permissions(manage_server=True) - @commands.group(pass_context=True) - async def autoeconomy(self, ctx): - """Configuration options for auto-registering Economy accounts.""" - if ctx.invoked_subcommand is None: - await send_cmd_help(ctx) - return - - @autoeconomy.command(pass_context=True, name="debug", no_pm=True) - async def autoeconomy_debug(self, ctx): - """Toggle autoeconomy debug messages.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]["DEBUG"] = not self.settings[server.id]["DEBUG"] - if self.settings[server.id]["DEBUG"]: - await self.bot.say("Debug messages on.") - else: - await self.bot.say("Debug messages off.") - await self.save_settings() - - @autoeconomy.command(pass_context=True, name="channel", no_pm=True) - async def autoeconomy_channel(self, ctx, channel: discord.Channel): - """Set a channel for the debug messages.""" - server = ctx.message.server - await self._data_check(ctx) - if not server.me.permissions_in(channel).send_messages: - await self.bot.say("No permissions to speak in that channel.") - return - self.settings[server.id]["CHANNEL"] = channel.id - await self.save_settings() - await self.bot.send_message(channel, "This channel will be used for debug messages.") - - @autoeconomy.command(pass_context=True, name="toggle", no_pm=True) - async def autoeconomy_toggle(self, ctx): - """Toggle autoeconomy on the server.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]["TOGGLE"] = not self.settings[server.id]["TOGGLE"] - if self.settings[server.id]["TOGGLE"]: - await self.bot.say("New users will automatically be registered for a bank account.") - else: - await self.bot.say("No longer auto-registering new users.") - await self.save_settings() - - @autoeconomy.command(name="version", pass_context=True, hidden=True) - async def autoeconomy_version(self): - """Displays the autoeconomy version.""" - await self.bot.say("autoeconomy version {}.".format(self.version)) - - async def on_member_join(self, member): - server = member.server - if server.id not in self.settings: - self.settings[server.id] = deepcopy(default_settings) - await self.save_settings() - if not self.settings[server.id]["TOGGLE"]: - return - channel = self.settings[server.id]["CHANNEL"] - channel_object = self.bot.get_channel(channel) - econ_cog = self.bot.get_cog('Economy') - if not econ_cog: - return - bank = self.bot.get_cog('Economy').bank - try: - bank.create_account(member) - except Exception: - if self.settings[server.id]["DEBUG"]: - await self.bot.send_message(channel_object, "Economy account already exists for {}.".format(member.name)) - return - if self.banksettings[server.id]["REGISTER_CREDITS"]: - reg_credits = self.banksettings[server.id]["REGISTER_CREDITS"] - bank.deposit_credits(member, reg_credits) - if self.settings[server.id]["DEBUG"]: - await self.bot.send_message(channel_object, "Bank account opened for {} and initial credits given.".format(member.name)) - return - - -def check_folders(): - if not os.path.exists('data/autoeconomy/'): - os.mkdir('data/autoeconomy/') - - -def check_files(): - if not dataIO.is_valid_json('data/autoeconomy/settings.json'): - defaults = {} - dataIO.save_json('data/autoeconomy/settings.json', defaults) - - -def setup(bot): - check_folders() - check_files() - bot.add_cog(AutoEconomy(bot)) diff --git a/autoeconomy/info.json b/autoeconomy/info.json deleted file mode 100644 index 17e01bc..0000000 --- a/autoeconomy/info.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "Use [p]autoeconomy to change settings.", - "NAME" : "autoeconomy", - "SHORT" : "Autoregister new users to the bot bank on server join.", - "DESCRIPTION" : "Auto-registers new users to the bot bank on server join. You must have the Economy cog loaded for this cog to work.", - "TAGS": ["bank", "economy", "register"] -} diff --git a/away/away.py b/away/away.py deleted file mode 100644 index 10937ad..0000000 --- a/away/away.py +++ /dev/null @@ -1,88 +0,0 @@ -import os -import discord -from .utils import checks -from discord.ext import commands -from cogs.utils.dataIO import dataIO - - -class Away: - """Le away cog""" - def __init__(self, bot): - self.bot = bot - self.data = dataIO.load_json('data/away/away.json') - - async def listener(self, message): - tmp = {} - server = message.server - if server.id not in self.data: - for mention in message.mentions: - tmp[mention] = True - if message.author.id != self.bot.user.id: - for author in tmp: - if author.id in self.data: - try: - avatar = author.avatar_url if author.avatar else author.default_avatar_url - if self.data[author.id]['MESSAGE']: - em = discord.Embed(description=self.data[author.id]['MESSAGE'], color=discord.Color.blue()) - em.set_author(name='{} is currently away'.format(author.display_name), icon_url=avatar) - else: - em = discord.Embed(color=discord.Color.blue()) - em.set_author(name='{} is currently away'.format(author.display_name), icon_url=avatar) - await self.bot.send_message(message.channel, embed=em) - except: - if self.data[author.id]['MESSAGE']: - msg = '{} is currently away and has set the following message: `{}`'.format(author.display_name, self.data[author.id]['MESSAGE']) - else: - msg = '{} is currently away'.format(author.display_name) - await self.bot.send_message(message.channel, msg) - - @commands.command(pass_context=True, name="away") - async def _away(self, context, *message: str): - """Tell the bot you're away or back.""" - author = context.message.author - if author.id in self.data: - del self.data[author.id] - msg = 'You\'re now back.' - else: - self.data[context.message.author.id] = {} - if len(str(message)) < 256: - self.data[context.message.author.id]['MESSAGE'] = ' '.join(context.message.clean_content.split()[1:]) - else: - self.data[context.message.author.id]['MESSAGE'] = True - msg = 'You\'re now set as away.' - dataIO.save_json('data/away/away.json', self.data) - await self.bot.say(msg) - - @commands.command(pass_context=True, name="toggleaway") - @checks.mod_or_permissions(administrator=True) - async def _ignore(self, context): - server = context.message.server - if server.id in self.data: - del self.data[server.id] - message = 'Not ignoring this server anymore.' - else: - self.data[server.id] = True - message = 'Ignoring this server.' - dataIO.save_json('data/away/away.json', self.data) - await self.bot.say(message) - - -def check_folder(): - if not os.path.exists('data/away'): - print('Creating data/away folder...') - os.makedirs('data/away') - - -def check_file(): - f = 'data/away/away.json' - if not dataIO.is_valid_json(f): - dataIO.save_json(f, {}) - print('Creating default away.json...') - - -def setup(bot): - check_folder() - check_file() - n = Away(bot) - bot.add_listener(n.listener, 'on_message') - bot.add_cog(n) diff --git a/away/info.json b/away/info.json deleted file mode 100644 index af9161c..0000000 --- a/away/info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AUTHOR" : "Paddolicious#8880", - "NAME" : "Away", - "SHORT" : "Sets and unsets a user away.", - "DESCRIPTION" : "Sets the user as away. When someone mentions the users the bot replies with either its own message or a message set by the user that the user is away.", - "TAGS": ["away", "member", "tool"] -} diff --git a/cah/cah.py b/cah/cah.py deleted file mode 100644 index 59418ad..0000000 --- a/cah/cah.py +++ /dev/null @@ -1,1690 +0,0 @@ -import asyncio -import discord -import re -import os -import random -import string -import json -import time -import html -import codecs -from random import shuffle -from discord.ext import commands - - -class CardsAgainstHumanity: - - # Init with the bot reference, and a reference to the deck file - def __init__(self, bot, file = None): - self.bot = bot - self.games = [] - self.maxBots = 5 # Max number of bots that can be added to a game - don't count toward max players - self.maxPlayers = 10 # Max players for ranjom joins - self.maxDeadTime = 3600 # Allow an hour of dead time before killing a game - self.checkTime = 300 # 5 minutes between dead time checks - self.winAfter = 10 # 10 wins for the game - self.botWaitMin = 5 # Minimum number of seconds before the bot makes a decision (default 5) - self.botWaitMax = 30 # Max number of seconds before a bot makes a decision (default 30) - self.userTimeout = 300 # 5 minutes to timeout - self.utCheck = 30 # Check timeout every 30 seconds - self.utWarn = 60 # Warn the user if they have 60 seconds or less before being kicked - self.charset = "1234567890" - self.botName = 'Rando Cardrissian' - self.minMembers = 3 - self.loopsleep = 0.05 - if file == None: - file = "data/cah/deck.json" - # Let's load our deck file - # Can be found at http://www.crhallberg.com/cah/json - if os.path.exists(file): - f = open(file,'r') - filedata = f.read() - f.close() - - self.deck = json.loads(filedata) - else: - # File doesn't exist - create a placeholder - self.deck = {} - self.bot.loop.create_task(self.checkDead()) - self.bot.loop.create_task(self.checkUserTimeout()) - - def cleanJson(self, json): - json = html.unescape(json) - # Clean out html formatting - json = json.replace('_','[blank]') - json = json.replace('
','\n') - json = json.replace('
','\n') - json = json.replace('', '*') - json = json.replace('', '*') - return json - - def displayname(self, member : discord.Member): - # A helper function to return the member's display name - nick = name = None - try: - nick = member.nick - except AttributeError: - pass - try: - name = member.name - except AttributeError: - pass - if nick: - return nick - if name: - return name - return None - - def memberforname(self, name, server): - # Check nick first - then name - for member in server.members: - if member.nick: - if member.nick.lower() == name.lower(): - return member - for member in server.members: - if member.name.lower() == name.lower(): - return member - # No member yet - try ID - memID = ''.join(list(filter(str.isdigit, name))) - newMem = memberForID(memID, server) - if newMem: - return newMem - return None - - def getreadabletimebetween(first, last): - # A helper function to make a readable string between two times - timeBetween = int(last-first) - weeks = int(timeBetween/604800) - days = int((timeBetween-(weeks*604800))/86400) - hours = int((timeBetween-(days*86400 + weeks*604800))/3600) - minutes = int((timeBetween-(hours*3600 + days*86400 + weeks*604800))/60) - seconds = int(timeBetween-(minutes*60 + hours*3600 + days*86400 + weeks*604800)) - msg = "" - - if weeks > 0: - if weeks == 1: - msg = '{}{} week, '.format(msg, str(weeks)) - else: - msg = '{}{} weeks, '.format(msg, str(weeks)) - if days > 0: - if days == 1: - msg = '{}{} day, '.format(msg, str(days)) - else: - msg = '{}{} days, '.format(msg, str(days)) - if hours > 0: - if hours == 1: - msg = '{}{} hour, '.format(msg, str(hours)) - else: - msg = '{}{} hours, '.format(msg, str(hours)) - if minutes > 0: - if minutes == 1: - msg = '{}{} minute, '.format(msg, str(minutes)) - else: - msg = '{}{} minutes, '.format(msg, str(minutes)) - if seconds > 0: - if seconds == 1: - msg = '{}{} second, '.format(msg, str(seconds)) - else: - msg = '{}{} seconds, '.format(msg, str(seconds)) - - if not msg: - return "0 seconds" - else: - return msg[:-2] - - async def checkUserTimeout(self): - while not self.bot.is_closed: - # Wait first - then check - await asyncio.sleep(self.utCheck) - for game in self.games: - if not game['Timeout']: - continue - if len(game['Members']) >= self.minMembers: - # Game is started - for member in game['Members']: - if member['IsBot']: - continue - if game['Judging']: - if not member == game['Members'][game['Judge']]: - # Not the judge - don't hold against the user - member['Time'] = int(time.time()) - continue - else: - # Not judging - if member == game['Members'][game['Judge']]: - # The judge - don't hold that against them - member['Time'] = int(time.time()) - continue - currentTime = int(time.time()) - userTime = member['Time'] - downTime = currentTime - userTime - # Check if downTime results in a kick - if downTime >= self.userTimeout: - # You gettin kicked, son. - await self.removeMember(member['User']) - self.checkGame(game) - continue - # Check if downTime is in warning time - if downTime >= (self.userTimeout - self.utWarn): - # Check if we're at warning phase - if self.userTimeout - downTime >= (self.utWarn - self.utCheck): - kickTime = self.userTimeout - downTime - if kickTime % self.utCheck: - # Kick time isn't exact time - round out to the next loop - kickTime = kickTime-(kickTime % self.utCheck)+self.utCheck - # Warning time! - timeString = self.getreadabletimebetween(0, kickTime) - msg = '**WARNING** - You will be kicked from the game if you do not make a move in *{}!*'.format(timeString) - await self.bot.send_message(member['User'], msg) - else: - for member in game['Members']: - # Reset timer - member['Time'] = int(time.time()) - - - async def checkDead(self): - while not self.bot.is_closed: - # Wait first - then check - await asyncio.sleep(self.checkTime) - for game in self.games: - gameTime = game['Time'] - currentTime = int(time.time()) - timeRemain = currentTime - gameTime - if timeRemain > self.maxDeadTime: - # Game is dead - quit it and alert members - for member in game['Members']: - if member['IsBot']: - # Clear pending tasks and set to None - if not member['Task'] == None: - task = member['Task'] - if not task.done(): - task.cancel() - member['Task'] = None - continue - msg = "Game id: *{}* has been closed due to inactivity.".format(game['ID']) - await self.bot.send_message(member['User'], msg) - - # Set running to false - game['Running'] = False - self.games.remove(game) - - async def checkPM(self, message): - # Checks if we're talking in PM, and if not - outputs an error - if message.channel.is_private: - # PM - return True - else: - # Not in PM - await self.bot.send_message(message.channel, 'Cards Against Humanity commands must be run in PM.') - return False - - - def randomID(self, length = 8): - # Create a random id that doesn't already exist - while True: - # Repeat until found - newID = ''.join(random.choice(self.charset) for i in range(length)) - exists = False - for game in self.games: - if game['ID'] == newID: - exists = True - break - if not exists: - break - return newID - - def randomBotID(self, game, length = 4): - # Returns a random id for a bot that doesn't already exist - while True: - # Repeat until found - newID = ''.join(random.choice(self.charset) for i in range(length)) - exists = False - for member in game['Members']: - if member['ID'] == newID: - exists = True - break - if not exists: - break - return newID - - def userGame(self, user): - # Returns the game the user is currently in - if not type(user) is str: - # Assume it's a discord.Member/User - user = user.id - - for game in self.games: - for member in game['Members']: - if member['ID'] == user: - # Found our user - return game - return None - - def gameForID(self, id): - # Returns the game with the passed id - for game in self.games: - if game['ID'] == id: - return game - return None - - async def removeMember(self, user, game = None): - if not type(user) is str: - # Assume it's a discord.Member/User - user = user.id - outcome = False - removed = None - if not game: - game = self.userGame(user) - if game: - for member in game['Members']: - if member['ID'] == user: - removed = member - outcome = True - judgeChanged = False - # Reset judging flag to retrigger actions - game['Judging'] = False - # Get current Judge - only if game has started - if len(game['Members']) >= self.minMembers: - judge = game['Members'][game['Judge']] - game['Members'].remove(member) - # Check if we're removing the current judge - if judge == member: - # Judge will change - judgeChanged = True - # Find out if our member was the last in line - if game['Judge'] >= len(game['Members']): - game['Judge'] = 0 - # Reset judge var - judge = game['Members'][game['Judge']] - else: - # Judge didn't change - so let's reset judge index - index = game['Members'].index(judge) - game['Judge'] = index - else: - judge = None - # Just remove the member - game['Members'].remove(member) - - if member['Creator']: - # We're losing the game creator - pick a new one - for newCreator in game['Members']: - if not newCreator['IsBot']: - newCreator['Creator'] = True - await self.bot.send_message(newCreator['User'], 'The creator of this game left. **YOU** are now the creator.') - break - - # Remove submissions - for sub in game['Submitted']: - # Remove deleted member and new judge's submissions - if sub['By'] == member or sub['By'] == judge: - # Found it! - game['Submitted'].remove(sub) - break - if member['IsBot']: - if not member['Task'] == None: - task = member['Task'] - if not task.done(): - task.cancel() - member['Task'] = None - else: - msg = '**You were removed from game id:** ***{}.***'.format(game['ID']) - await self.bot.send_message(member['User'], msg) - # Removed, no need to finish the loop - break - if not outcome: - return outcome - # We removed someone - let's tell the world - for member in game['Members']: - if member['IsBot']: - continue - if removed['IsBot']: - msg = '***{} ({})*** **left the game - reorganizing...**'.format(self.botName, removed['ID']) - else: - msg = '***{}*** **left the game - reorganizing...**'.format(self.displayname(removed['User'])) - # Check if the judge changed - if judgeChanged: - # Judge changed - newJudge = game['Members'][game['Judge']] - if newJudge['IsBot']: - msg += '\n\n***{} ({})*** **is now judging!**'.format(self.botName, newJudge['ID']) - # Schedule judging task - else: - if newJudge == member: - msg += '\n\n***YOU*** **are now judging!**' - else: - msg += '\n\n***{}*** **is now judging!**'.format(self.displayname(newJudge['User'])) - await self.bot.send_message(member['User'], msg) - return game - - - def checkGame(self, game): - for member in game['Members']: - if not member['IsBot']: - return True - # If we got here - only bots, or empty game - # Kill all bots' loops - for member in game['Members']: - if member['IsBot']: - # Clear pending tasks and set to None - if not member['Task'] == None: - task = member['Task'] - if not task.done(): - task.cancel() - member['Task'] = None - # Set running to false - game['Running'] = False - self.games.remove(game) - return False - - async def typing(self, game, typeTime = 5): - # Allows us to show the bot typing - waitTime = random.randint(self.botWaitMin, self.botWaitMax) - preType = waitTime-typeTime - if preType > 0: - await asyncio.sleep(preType) - for member in game['Members']: - if member['IsBot']: - continue - # Show that we're typing - await self.bot.send_typing(member['User']) - await asyncio.sleep(self.loopsleep) - await asyncio.sleep(typeTime) - else: - for member in game['Members']: - if member['IsBot']: - continue - # Show that we're typing - await self.bot.send_typing(member['User']) - await asyncio.sleep(self.loopsleep) - await asyncio.sleep(waitTime) - - async def botPick(self, ctx, bot, game): - # Has the bot pick their card - blackNum = game['BlackCard']['Pick'] - if blackNum == 1: - cardSpeak = 'card' - else: - cardSpeak = 'cards' - i = 0 - cards = [] - while i < blackNum: - randCard = random.randint(0, len(bot['Hand'])-1) - cards.append(bot['Hand'].pop(randCard)['Text']) - i += 1 - - await self.typing(game) - - # Make sure we haven't laid any cards - if bot['Laid'] == False and game['Judging'] == False: - newSubmission = { 'By': bot, 'Cards': cards } - game['Submitted'].append(newSubmission) - # Shuffle cards - shuffle(game['Submitted']) - bot['Laid'] = True - game['Time'] = currentTime = int(time.time()) - await self.checkSubmissions(ctx, game, bot) - - - async def botPickWin(self, ctx, game): - totalUsers = len(game['Members'])-1 - submitted = len(game['Submitted']) - if submitted >= totalUsers: - # Judge is a bot - and all cards are in! - await self.typing(game) - # Pick a winner - winner = random.randint(0, totalUsers-1) - await self.winningCard(ctx, game, winner) - - - async def checkSubmissions(self, ctx, game, user = None): - totalUsers = len(game['Members'])-1 - submitted = len(game['Submitted']) - for member in game['Members']: - msg = '' - # Is the game running? - if len(game['Members']) < self.minMembers: - if member['IsBot']: - # Clear pending tasks and set to None - if not member['Task'] == None: - task = member['Task'] - if not task.done(): - # Task isn't finished - we're on a new hand, cancel it - task.cancel() - member['Task'] = None - continue - # not enough members - send the embed - stat_embed = discord.Embed(color=discord.Color.red()) - stat_embed.set_author(name='Not enough players to continue! ({}/{})'.format(len(game['Members']), self.minMembers)) - stat_embed.set_footer(text='Have other users join with: {}joincah {}'.format(ctx.prefix, game['ID'])) - await self.bot.send_message(member['User'], embed=stat_embed) - continue - if member['IsBot'] == True: - continue - # Check if we have a user - if user: - blackNum = game['BlackCard']['Pick'] - if blackNum == 1: - card = 'card' - else: - card = 'cards' - if user['IsBot']: - msg = '*{} ({})* submitted their {}! '.format(self.botName, user['ID'], card) - else: - if not member == user: - # Don't say this to the submitting user - msg = '*{}* submitted their {}! '.format(self.displayname(user['User']), card) - if submitted < totalUsers: - msg += '{}/{} cards submitted...'.format(submitted, totalUsers) - if len(msg): - # We have something to say - await self.bot.send_message(member['User'], msg) - await asyncio.sleep(self.loopsleep) - - - async def checkCards(self, ctx, game): - while not self.bot.is_closed: - if not game['Running']: - break - # wait for 1 second - await asyncio.sleep(1) - # Check for all cards - if len(game['Members']) < self.minMembers: - # Not enough members - continue - # Enough members - let's check if we're judging - if game['Judging']: - continue - # Enough members, and not judging - let's check cards - totalUsers = len(game['Members'])-1 - submitted = len(game['Submitted']) - if submitted >= totalUsers: - game['Judging'] = True - # We have enough cards - for member in game['Members']: - if member['IsBot']: - continue - msg = 'All cards have been submitted!' - # if - await self.bot.send_message(member['User'], msg) - await self.showOptions(ctx, member['User']) - await asyncio.sleep(self.loopsleep) - - # Check if a bot is the judge - judge = game['Members'][game['Judge']] - if not judge['IsBot']: - continue - # task = self.bot.loop.create_task(self.botPickWin(ctx, game)) - task = asyncio.ensure_future(self.botPickWin(ctx, game)) - judge['Task'] = task - - - - async def winningCard(self, ctx, game, card): - # Let's pick our card and alert everyone - winner = game['Submitted'][card] - if winner['By']['IsBot']: - winnerName = '{} ({})'.format(self.botName, winner['By']['ID']) - winner['By']['Points'] += 1 - winner['By']['Won'].append(game['BlackCard']['Text']) - else: - winnerName = self.displayname(winner['By']['User']) - for member in game['Members']: - if member['IsBot']: - continue - stat_embed = discord.Embed(color=discord.Color.gold()) - stat_embed.set_footer(text='Cards Against Humanity - id: {}'.format(game['ID'])) - index = game['Members'].index(member) - if index == game['Judge']: - stat_embed.set_author(name='You picked {}\'s card!'.format(winnerName)) - elif member == winner['By']: - stat_embed.set_author(name='YOU WON!!') - member['Points'] += 1 - member['Won'].append(game['BlackCard']['Text']) - else: - stat_embed.set_author(name='{} won!'.format(winnerName)) - if len(winner['Cards']) == 1: - msg = 'The **Winning** card was:\n\n{}'.format('{}'.format(' - '.join(winner['Cards']))) - else: - msg = 'The **Winning** cards were:\n\n{}'.format('{}'.format(' - '.join(winner['Cards']))) - await self.bot.send_message(member['User'], embed=stat_embed) - await self.bot.send_message(member['User'], msg) - await asyncio.sleep(self.loopsleep) - - # await self.nextPlay(ctx, game) - - # Start the game loop - event = game['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set) - game['Time'] = currentTime = int(time.time()) - - async def gameCheckLoop(self, ctx, game): - task = game['NextHand'] - while True: - if not game['Running']: - break - # Clear the pending task - task.clear() - # Queue up the next hand - await self.nextPlay(ctx, game) - # Wait until our next clear - await task.wait() - - async def messagePlayers(self, ctx, message, game, judge = False): - # Messages all the users on in a game - for member in game['Members']: - if member['IsBot']: - continue - # Not bots - if member is game['Members'][game['Judge']]: - # Is the judge - if judge: - await self.bot.send_message(member['User'], message) - else: - # Not the judge - await self.bot.send_message(member['User'], message) - - ################################################ - - async def showPlay(self, ctx, user): - # Creates an embed and displays the current game stats - stat_embed = discord.Embed(color=discord.Color.blue()) - game = self.userGame(user) - if not game: - return - # Get the judge's name - if game['Members'][game['Judge']]['User'] == user: - judge = '**YOU** are' - else: - if game['Members'][game['Judge']]['IsBot']: - # Bot - judge = '*{} ({})* is'.format(self.botName, game['Members'][game['Judge']]['ID']) - else: - judge = '*{}* is'.format(self.displayname(game['Members'][game['Judge']]['User'])) - - # Get the Black Card - try: - blackCard = game['BlackCard']['Text'] - blackNum = game['BlackCard']['Pick'] - except Exception: - blackCard = 'None.' - blackNum = 0 - - msg = '{} the judge.\n\n'.format(judge) - msg += '__Black Card:__\n\n**{}**\n\n'.format(blackCard) - - totalUsers = len(game['Members'])-1 - submitted = len(game['Submitted']) - if len(game['Members']) >= self.minMembers: - if submitted < totalUsers: - msg += '{}/{} cards submitted...'.format(submitted, totalUsers) - else: - msg += 'All cards have been submitted!' - await self.showOptions(ctx, user) - return - if not judge == '**YOU** are': - # Judge doesn't need to lay a card - if blackNum == 1: - # Singular - msg += '\n\nLay a card with `{}lay [card number]`'.format(ctx.prefix) - elif blackNum > 1: - # Plural - msg += '\n\nLay **{} cards** with `{}lay [card numbers separated by commas (1,2,3)]`'.format(blackNum, ctx.prefix) - - stat_embed.set_author(name='Current Play') - stat_embed.set_footer(text='Cards Against Humanity - id: {}'.format(game['ID'])) - await self.bot.send_message(user, embed=stat_embed) - await self.bot.send_message(user, msg) - - async def showHand(self, ctx, user): - # Shows the user's hand in an embed - stat_embed = discord.Embed(color=discord.Color.green()) - game = self.userGame(user) - if not game: - return - i = 0 - msg = '' - points = '? points' - for member in game['Members']: - if member['ID'] == user.id: - # Got our user - if member['Points']==1: - points = '1 point' - else: - points = '{} points'.format(member['Points']) - for card in member['Hand']: - i += 1 - msg += '{}. {}\n'.format(i, card['Text']) - - try: - blackCard = '**{}**'.format(game['BlackCard']['Text']) - except Exception: - blackCard = '**None.**' - stat_embed.set_author(name='Your Hand - {}'.format(points)) - stat_embed.set_footer(text='Cards Against Humanity - id: {}'.format(game['ID'])) - await self.bot.send_message(user, embed=stat_embed) - await self.bot.send_message(user, msg) - - async def showOptions(self, ctx, user): - # Shows the judgement options - stat_embed = discord.Embed(color=discord.Color.orange()) - game = self.userGame(user) - if not game: - return - # Add title - stat_embed.set_author(name='JUDGEMENT TIME!!') - stat_embed.set_footer(text='Cards Against Humanity - id: {}'.format(game['ID'])) - await self.bot.send_message(user, embed=stat_embed) - - if game['Members'][game['Judge']]['User'] == user: - judge = '**YOU** are' - else: - if game['Members'][game['Judge']]['IsBot']: - # Bot - judge = '*{} ({})* is'.format(self.botName, game['Members'][game['Judge']]['ID']) - else: - judge = '*{}* is'.format(self.displayname(game['Members'][game['Judge']]['User'])) - blackCard = game['BlackCard']['Text'] - - msg = '{} judging.\n\n'.format(judge) - msg += '__Black Card:__\n\n**{}**\n\n'.format(blackCard) - msg += '__Submitted White Cards:__\n\n' - - i = 0 - for sub in game['Submitted']: - i+=1 - msg += '{}. {}\n'.format(i, ' - '.join(sub['Cards'])) - if judge == '**YOU** are': - msg += '\nPick a winner with `{}pick [submission number]`.'.format(ctx.prefix) - await self.bot.send_message(user, msg) - - async def drawCard(self, game): - # Draws a random unused card and shuffles the deck if needed - totalDiscard = len(game['Discard']) - for member in game['Members']: - totalDiscard += len(member['Hand']) - if totalDiscard >= len(self.deck['whiteCards']): - # Tell everyone the cards were shuffled - for member in game['Members']: - if member['IsBot']: - continue - user = member['User'] - await self.bot.send_message(user, 'Shuffling white cards...') - # Shuffle the cards - self.shuffle(game) - while True: - # Random grab a unique card - index = random.randint(0, len(self.deck['whiteCards'])-1) - if not index in game['Discard']: - game['Discard'].append(index) - text = self.deck['whiteCards'][index] - text = self.cleanJson(text) - card = { 'Index': index, 'Text': text } - return card - - - def shuffle(self, game): - # Adds discards back into the deck - game['Discard'] = [] - for member in game['Members']: - for card in member['Hand']: - game['Discard'].append(card['Index']) - - - async def drawCards(self, user, cards = 10): - if not type(user) is str: - # Assume it's a discord.Member/User - user = user.id - # fills the user's hand up to number of cards - game = self.userGame(user) - for member in game['Members']: - if member['ID'] == user: - # Found our user - let's draw cards - i = len(member['Hand']) - while i < cards: - # Draw unique cards until we fill our hand - newCard = await self.drawCard(game) - member['Hand'].append(newCard) - i += 1 - - - async def drawBCard(self, game): - # Draws a random black card - totalDiscard = len(game['BDiscard']) - if totalDiscard >= len(self.deck['blackCards']): - # Tell everyone the cards were shuffled - for member in game['Members']: - if member['IsBot']: - continue - user = member['User'] - await self.bot.send_message(user, 'Shuffling black cards...') - # Shuffle the cards - game['BDiscard'] = [] - while True: - # Random grab a unique card - index = random.randint(0, len(self.deck['blackCards'])-1) - if not index in game['BDiscard']: - game['BDiscard'].append(index) - text = self.deck['blackCards'][index]['text'] - text = self.cleanJson(text) - game['BlackCard'] = { 'Text': text, 'Pick': self.deck['blackCards'][index]['pick'] } - return game['BlackCard'] - - - async def nextPlay(self, ctx, game): - # Advances the game - if len(game['Members']) < self.minMembers: - stat_embed = discord.Embed(color=discord.Color.red()) - stat_embed.set_author(name='Not enough players to continue! ({}/{})'.format(len(game['Members']), self.minMembers)) - stat_embed.set_footer(text='Have other users join with: {}joincah {}'.format(ctx.prefix, game['ID'])) - for member in game['Members']: - if member['IsBot']: - continue - await self.bot.send_message(member['User'], embed=stat_embed) - return - - # Find if we have a winner - winner = False - stat_embed = discord.Embed(color=discord.Color.lighter_grey()) - for member in game['Members']: - if member['IsBot']: - # Clear pending tasks and set to None - if not member['Task'] == None: - task = member['Task'] - if not task.done(): - # Task isn't finished - we're on a new hand, cancel it - task.cancel() - member['Task'] = None - if member['Points'] >= self.winAfter: - # We have a winner! - winner = True - if member['IsBot']: - stat_embed.set_author(name='{} ({}) is the WINNER!!'.format(self.botName, member['ID'])) - else: - stat_embed.set_author(name='{} is the WINNER!!'.format(self.displayname(member['User']))) - stat_embed.set_footer(text='Congratulations!'.format(game['ID'])) - break - if winner: - for member in game['Members']: - if not member['IsBot']: - await self.bot.send_message(member['User'], embed=stat_embed) - # Reset all users - member['Hand'] = [] - member['Points'] = 0 - member['Won'] = [] - member['Laid'] = False - member['Refreshed'] = False - await asyncio.sleep(self.loopsleep) - - game['Judging'] = False - # Clear submitted cards - game['Submitted'] = [] - # We have enough members - if game['Judge'] == -1: - # First game - randomize judge - game['Judge'] = random.randint(0, len(game['Members'])-1) - else: - game['Judge']+=1 - # Reset the judge if out of bounds - if game['Judge'] >= len(game['Members']): - game['Judge'] = 0 - - # Draw the next black card - bCard = await self.drawBCard(game) - - # Draw cards - for member in game['Members']: - member['Laid'] = False - await self.drawCards(member['ID']) - - # Show hands - for member in game['Members']: - if member['IsBot']: - continue - await self.showPlay(ctx, member['User']) - index = game['Members'].index(member) - if not index == game['Judge']: - await self.showHand(ctx, member['User']) - await asyncio.sleep(self.loopsleep) - - # Have the bots lay their cards - for member in game['Members']: - if not member['IsBot']: - continue - if member['ID'] == game['Members'][game['Judge']]['ID']: - continue - # Not a human player, and not the judge - # task = self.bot.loop.create_task(self.botPick(ctx, member, game))\ - task = asyncio.ensure_future(self.botPick(ctx, member, game)) - member['Task'] = task - # await self.botPick(ctx, member, game) - - - @commands.command(pass_context=True) - async def game(self, ctx, *, message = None): - """Displays the game's current status.""" - if not await self.checkPM(ctx.message): - return - userGame = self.userGame(ctx.message.author) - if not userGame: - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - await self.showPlay(ctx, ctx.message.author) - - - @commands.command(pass_context=True) - async def chat(self, ctx, *, message = None): - """Broadcasts a message to the other players in your game.""" - if not await self.checkPM(ctx.message): - return - userGame = self.userGame(ctx.message.author) - if not userGame: - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - userGame['Time'] = int(time.time()) - if message == None: - msg = "Ooookay, you say *nothing...*" - await self.bot.send_message(ctx.message.author, msg) - return - msg = '*{}* says: {}'.format(ctx.message.author.name, message) - for member in userGame['Members']: - if member['IsBot']: - continue - # Tell them all!! - if not member['User'] == ctx.message.author: - # Don't tell yourself - await self.bot.send_message(member['User'], msg) - else: - # Update member's time - member['Time'] = int(time.time()) - await self.bot.send_message(ctx.message.author, 'Message sent!') - - - @commands.command(pass_context=True) - async def lay(self, ctx, *, card = None): - """Lays a card or cards from your hand. If multiple cards are needed, separate them by a comma (1,2,3).""" - if not await self.checkPM(ctx.message): - return - userGame = self.userGame(ctx.message.author) - if not userGame: - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - userGame['Time'] = int(time.time()) - for member in userGame['Members']: - if member['User'] == ctx.message.author: - member['Time'] = int(time.time()) - user = member - index = userGame['Members'].index(member) - if index == userGame['Judge']: - await self.bot.send_message(ctx.message.author, "You're the judge. You don't get to lay cards this round.") - return - for submit in userGame['Submitted']: - if submit['By']['User'] == ctx.message.author: - await self.bot.send_message(ctx.message.author, "You already made your submission this round.") - return - if card == None: - await self.bot.send_message(ctx.message.author, 'You need you input *something.*') - return - card = card.strip() - card = card.replace(" ", "") - # Not the judge - if len(userGame['Members']) < self.minMembers: - stat_embed = discord.Embed(color=discord.Color.red()) - stat_embed.set_author(name='Not enough players to continue! ({}/{})'.format(len(userGame['Members']), self.minMembers)) - stat_embed.set_footer(text='Have other users join with: {}joincah {}'.format(ctx.prefix, userGame['ID'])) - await self.bot.send_message(ctx.message.author, embed=stat_embed) - return - - numberCards = userGame['BlackCard']['Pick'] - cards = [] - if numberCards > 1: - cardSpeak = "cards" - try: - card = card.split(',') - except Exception: - card = [] - if not len(card) == numberCards: - msg = 'You need to lay **{} cards** (no duplicates) with `{}lay [card numbers separated by commas (1,2,3)]`'.format(numberCards, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - # Got something - # Check for duplicates - if not len(card) == len(set(card)): - msg = 'You need to lay **{} cards** (no duplicates) with `{}lay [card numbers separated by commas (1,2,3)]`'.format(numberCards, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - # Works - for c in card: - try: - c = int(c) - except Exception: - msg = 'You need to lay **{} cards** (no duplicates) with `{}lay [card numbers separated by commas (1,2,3)]`'.format(numberCards, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - - if c < 1 or c > len(user['Hand']): - msg = 'Card numbers must be between 1 and {}.'.format(len(user['Hand'])) - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - cards.append(user['Hand'][c-1]['Text']) - # Remove from user's hand - card = sorted(card, key=lambda card:int(card), reverse=True) - for c in card: - user['Hand'].pop(int(c)-1) - # Valid cards - - newSubmission = { 'By': user, 'Cards': cards } - else: - cardSpeak = "card" - try: - card = int(card) - except Exception: - msg = 'You need to lay a valid card with `{}lay [card number]`'.format(ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - if card < 1 or card > len(user['Hand']): - msg = 'Card numbers must be between 1 and {}.'.format(len(user['Hand'])) - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - # Valid card - newSubmission = { 'By': user, 'Cards': [ user['Hand'].pop(card-1)['Text'] ] } - userGame['Submitted'].append(newSubmission) - - # Shuffle cards - shuffle(userGame['Submitted']) - - user['Laid'] = True - await self.bot.send_message(ctx.message.author, 'You submitted your {}!'.format(cardSpeak)) - await self.checkSubmissions(ctx, userGame, user) - - - @commands.command(pass_context=True) - async def pick(self, ctx, *, card = None): - """As the judge - pick the winning card(s).""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - userGame['Time'] = int(time.time()) - isJudge = False - for member in userGame['Members']: - if member['User'] == ctx.message.author: - member['Time'] = int(time.time()) - user = member - index = userGame['Members'].index(member) - if index == userGame['Judge']: - isJudge = True - if not isJudge: - msg = "You're not the judge - I guess you'll have to wait your turn.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - # Am judge - totalUsers = len(userGame['Members'])-1 - submitted = len(userGame['Submitted']) - if submitted < totalUsers: - if totalUsers - submitted == 1: - msg = "Still waiting on 1 card..." - else: - msg = "Still waiting on {} cards...".format(totalUsers-submitted) - await self.bot.send_message(ctx.message.author, msg) - return - try: - card = int(card)-1 - except Exception: - card = -1 - if card < 0 or card >= totalUsers: - msg = "Your pick must be between 1 and {}.".format(totalUsers) - await self.bot.send_message(ctx.message.author, msg) - return - # Pick is good! - await self.winningCard(ctx, userGame, card) - - - @commands.command(pass_context=True) - async def hand(self, ctx): - """Shows your hand.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - await self.showHand(ctx, ctx.message.author) - userGame['Time'] = currentTime = int(time.time()) - - - @commands.command(pass_context=True) - async def newcah(self, ctx): - """Starts a new Cards Against Humanity game.""" - #if not await self.checkPM(ctx.message): - #return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if userGame: - # Already in a game - msg = "You're already in a game (id: *{}*)\nType `{}leavecah` to leave that game.".format(userGame['ID'], ctx.prefix) - await self.bot.send_message(ctx.message.channel, msg) - return - - # Not in a game - create a new one - gameID = self.randomID() - currentTime = int(time.time()) - newGame = { 'ID': gameID, 'Members': [], 'Discard': [], 'BDiscard': [], 'Judge': -1, 'Time': currentTime, 'BlackCard': None, 'Submitted': [], 'NextHand': asyncio.Event(), 'Judging': False, 'Timeout': True } - member = { 'ID': ctx.message.author.id, 'User': ctx.message.author, 'Points': 0, 'Won': [], 'Hand': [], 'Laid': False, 'Refreshed': False, 'IsBot': False, 'Creator': True, 'Task': None, 'Time': currentTime } - newGame['Members'].append(member) - newGame['Running'] = True - task = self.bot.loop.create_task(self.gameCheckLoop(ctx, newGame)) - task = self.bot.loop.create_task(self.checkCards(ctx, newGame)) - self.games.append(newGame) - # Tell the user they created a new game and list its ID - await self.bot.send_message(ctx.message.channel, 'You created game id: *{}*'.format(gameID)) - await self.drawCards(ctx.message.author) - # await self.showHand(ctx, ctx.message.author) - # await self.nextPlay(ctx, newGame) - - - @commands.command(pass_context=True) - async def leavecah(self, ctx): - """Leaves the current game you're in.""" - removeCheck = await self.removeMember(ctx.message.author) - if not removeCheck: - msg = 'You are not in a game.' - await self.bot.send_message(ctx.message.channel, msg) - return - if self.checkGame(removeCheck): - # await self.nextPlay(ctx, removeCheck) - - """# Start the game loop - event = removeCheck['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set)""" - # Player was removed - try to handle it calmly... - await self.checkSubmissions(ctx, removeCheck) - - - @commands.command(pass_context=True) - async def joincah(self, ctx, *, id = None): - """Join a Cards Against Humanity game. If no id or user is passed, joins a random game.""" - #if not await self.checkPM(ctx.message): - #return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - isCreator = False - if userGame: - # Already in a game - msg = "You're already in a game (id: *{}*)\nType `{}leavecah` to leave that game.".format(userGame['ID'], ctx.prefix) - await self.bot.send_message(ctx.message.channel, msg) - return - if len(self.games): - if id: - game = self.gameForID(id) - if game == None: - # That id doesn't exist - or is possibly a user - # If user, has to be joined from server chat - if not ctx.message.server: - msg = "I couldn't find a game attached to that id. If you are trying to join a user - run the `{}joincah [user]` command in a channel on a server you share with that user.".format(ctx.prefix) - await self.bot.send_message(ctx.message.channel, msg) - return - else: - # We have a server - let's try for a user - member = self.memberforname(id, ctx.message.server) - if not member: - # Couldn't find user! - msg = "I couldn't find a game attached to that id. If you are trying to join a user - run the `{}joincah [user]` command in a channel on a server you share with that user.".format(ctx.prefix) - await self.bot.send_message(ctx.message.channel, msg) - return - # Have a user - check if they're in a game - game = self.userGame(member) - if not game: - # That user is NOT in a game! - msg = "That user doesn't appear to be playing." - await self.bot.send_message(ctx.message.channel, msg) - return - - else: - game = random.choice(self.games) - else: - # No games - create a new one - gameID = self.randomID() - currentTime = int(time.time()) - game = { 'ID': gameID, 'Members': [], 'Discard': [], 'BDiscard': [], 'Judge': -1, 'Time': currentTime, 'BlackCard': None, 'Submitted': [], 'NextHand': asyncio.Event(), 'Judging': False, 'Timeout': True } - game['Running'] = True - task = self.bot.loop.create_task(self.gameCheckLoop(ctx, game)) - task = self.bot.loop.create_task(self.checkCards(ctx, game)) - self.games.append(game) - # Tell the user they created a new game and list its ID - await self.bot.send_message(ctx.message.channel, '**You created game id:** ***{}***'.format(gameID)) - isCreator = True - - # Tell everyone else you joined - for member in game['Members']: - if member['IsBot']: - continue - await self.bot.send_message(member['User'], '***{}*** **joined the game!**'.format(self.displayname(ctx.message.author))) - - # We got a user! - currentTime = int(time.time()) - member = { 'ID': ctx.message.author.id, 'User': ctx.message.author, 'Points': 0, 'Won': [], 'Hand': [], 'Laid': False, 'Refreshed': False, 'IsBot': False, 'Creator': isCreator, 'Task': None, 'Time': currentTime } - game['Members'].append(member) - await self.drawCards(ctx.message.author) - if len(game['Members'])==1: - # Just created the game - await self.drawCards(ctx.message.author) - else: - msg = "**You've joined game id:** ***{}!***\n\nThere are *{} users* in this game.".format(game['ID'], len(game['Members'])) - await self.bot.send_message(ctx.message.channel, msg) - - # Check if adding put us at minimum members - if len(game['Members']) - 1 < self.minMembers: - # It was - *actually* start a game - event = game['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set) - else: - # It was not - just incorporate new players - await self.checkSubmissions(ctx, game) - # Reset judging flag to retrigger actions - game['Judging'] = False - # Show the user the current card and their hand - await self.showPlay(ctx, member['User']) - await self.showHand(ctx, member['User']) - event = game['NextHand'] - - game['Time'] = int(time.time()) - - - @commands.command(pass_context=True) - async def joinbot(self, ctx): - """Adds a bot to the game. Can only be done by the player who created the game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - botCount = 0 - for member in userGame['Members']: - if member['IsBot']: - botCount += 1 - continue - if member['User'] == ctx.message.author: - if not member['Creator']: - # You didn't make this game - msg = 'Only the player that created the game can add bots.' - await self.bot.send_message(ctx.message.author, msg) - return - member['Time'] = int(time.time()) - # We are the creator - let's check the number of bots - if botCount >= self.maxBots: - # Too many bots! - msg = 'You already have enough bots (max is {}).'.format(self.maxBots) - await self.bot.send_message(ctx.message.author, msg) - return - # We can get another bot! - botID = self.randomBotID(userGame) - lobot = { 'ID': botID, 'User': None, 'Points': 0, 'Won': [], 'Hand': [], 'Laid': False, 'Refreshed': False, 'IsBot': True, 'Creator': False, 'Task': None } - userGame['Members'].append(lobot) - await self.drawCards(lobot['ID']) - msg = '***{} ({})*** **joined the game!**'.format(self.botName, botID) - for member in userGame['Members']: - if member['IsBot']: - continue - await self.bot.send_message(member['User'], msg) - # await self.nextPlay(ctx, userGame) - - # Check if adding put us at minimum members - if len(userGame['Members']) - 1 < self.minMembers: - # It was - *actually* start a game - event = userGame['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set) - else: - # It was not - just incorporate new players - await self.checkSubmissions(ctx, userGame) - # Reset judging flag to retrigger actions - userGame['Judging'] = False - # Schedule stuff - task = asyncio.ensure_future(self.botPick(ctx, lobot, userGame)) - lobot['Task'] = task - - - @commands.command(pass_context=True) - async def joinbots(self, ctx, number = None): - """Adds bots to the game. Can only be done by the player who created the game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - botCount = 0 - for member in userGame['Members']: - if member['IsBot']: - botCount += 1 - continue - if member['User'] == ctx.message.author: - if not member['Creator']: - # You didn't make this game - msg = 'Only the player that created the game can add bots.' - await self.bot.send_message(ctx.message.author, msg) - return - member['Time'] = int(time.time()) - if number == None: - # No number specified - let's add the max number of bots - number = self.maxBots - botCount - - try: - number = int(number) - except Exception: - msg = 'Number of bots to add must be an integer.' - await self.bot.send_message(ctx.message.author, msg) - return - - # We are the creator - let's check the number of bots - if botCount >= self.maxBots: - # Too many bots! - msg = 'You already have enough bots (max is {}).'.format(self.maxBots) - await self.bot.send_message(ctx.message.author, msg) - return - - if number > (self.maxBots - botCount): - number = self.maxBots - botCount - - if number == 1: - msg = '**Adding {} bot:**\n\n'.format(number) - else: - msg = '**Adding {} bots:**\n\n'.format(number) - - newBots = [] - for i in range(0, number): - # We can get another bot! - botID = self.randomBotID(userGame) - lobot = { 'ID': botID, 'User': None, 'Points': 0, 'Won': [], 'Hand': [], 'Laid': False, 'Refreshed': False, 'IsBot': True, 'Creator': False, 'Task': None } - userGame['Members'].append(lobot) - newBots.append(lobot) - await self.drawCards(lobot['ID']) - msg += '***{} ({})*** **joined the game!**\n'.format(self.botName, botID) - # await self.nextPlay(ctx, userGame) - - for member in userGame['Members']: - if member['IsBot']: - continue - await self.bot.send_message(member['User'], msg) - - # Check if adding put us at minimum members - if len(userGame['Members']) - number < self.minMembers: - # It was - *actually* start a game - event = userGame['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set) - else: - # It was not - just incorporate new players - await self.checkSubmissions(ctx, userGame) - # Reset judging flag to retrigger actions - game['Judging'] = False - for bot in newBots: - # Schedule stuff - task = asyncio.ensure_future(self.botPick(ctx, bot, userGame)) - bot['Task'] = task - - @commands.command(pass_context=True) - async def removebot(self, ctx, *, id = None): - """Removes a bot from the game. Can only be done by the player who created the game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - botCount = 0 - for member in userGame['Members']: - if member['IsBot']: - botCount += 1 - continue - if member['User'] == ctx.message.author: - if not member['Creator']: - # You didn't make this game - msg = 'Only the player that created the game can remove bots.' - await self.bot.send_message(ctx.message.author, msg) - return - member['Time'] = int(time.time()) - # We are the creator - let's check the number of bots - if id == None: - # Just remove the first bot we find - for member in userGame['Members']: - if member['IsBot']: - await self.removeMember(member['ID']) - """# Start the game loop - event = userGame['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set)""" - # Bot was removed - try to handle it calmly... - await self.checkSubmissions(ctx, userGame) - return - msg = 'No bots to remove!' - await self.bot.send_message(ctx.message.author, msg) - return - else: - # Remove a bot by id - if not await self.removeMember(id): - # not found - msg = 'I couldn\'t locate that bot on this game. If you\'re trying to remove a player, try the `{}removeplayer [name]` command.'.format(ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - # await self.nextPlay(ctx, userGame) - - """# Start the game loop - event = userGame['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set)""" - # Bot was removed - let's try to handle it calmly... - await self.checkSubmissions(ctx, userGame) - - - @commands.command(pass_context=True) - async def cahgames(self, ctx): - """Displays up to 10 CAH games in progress.""" - shuffledGames = list(self.games) - random.shuffle(shuffledGames) - if not len(shuffledGames): - await self.bot.send_message(ctx.message.channel, 'No games being played currently.') - return - - max = 10 - if len(shuffledGames) < 10: - max = len(shuffledGames) - msg = '__Current CAH Games__:\n\n' - - for i in range(0, max): - playerCount = 0 - botCount = 0 - gameID = shuffledGames[i]['ID'] - for j in shuffledGames[i]['Members']: - if j['IsBot']: - botCount += 1 - else: - playerCount += 1 - botText = '{} bot'.format(botCount) - if not botCount == 1: - botText += 's' - playerText = '{} player'.format(playerCount) - if not playerCount == 1: - playerText += 's' - - msg += '{}. {} - {} | {}\n'.format(i+1, gameID, playerText, botText) - - await self.bot.send_message(ctx.message.channel, msg) - - - - @commands.command(pass_context=True) - async def score(self, ctx): - """Display the score of the current game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - stat_embed = discord.Embed(color=discord.Color.purple()) - stat_embed.set_author(name='Current Score') - stat_embed.set_footer(text='Cards Against Humanity - id: {}'.format(userGame['ID'])) - await self.bot.send_message(ctx.message.author, embed=stat_embed) - users = sorted(userGame['Members'], key=lambda card:int(card['Points']), reverse=True) - msg = '' - i = 0 - if len(users) > 10: - msg += '__10 of {} Players:__\n\n'.format(len(users)) - else: - msg += '__Players:__\n\n' - for user in users: - i += 1 - if i > 10: - break - if user['Points'] == 1: - if user['User']: - # Person - msg += '{}. *{}* - 1 point\n'.format(i, self.displayname(user['User'])) - else: - # Bot - msg += '{}. *{} ({})* - 1 point\n'.format(i, self.botName, user['ID']) - else: - if user['User']: - # Person - msg += '{}. *{}* - {} points\n'.format(i, self.displayname(user['User']), user['Points']) - else: - # Bot - msg += '{}. *{} ({})* - {} points\n'.format(i, self.botName, user['ID'], user['Points']) - await self.bot.send_message(ctx.message.author, msg) - - @commands.command(pass_context=True) - async def laid(self, ctx): - """Shows who laid their cards and who hasn't.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - stat_embed = discord.Embed(color=discord.Color.purple()) - stat_embed.set_author(name='Card Check') - stat_embed.set_footer(text='Cards Against Humanity - id: {}'.format(userGame['ID'])) - await self.bot.send_message(ctx.message.author, embed=stat_embed) - users = sorted(userGame['Members'], key=lambda card:int(card['Laid'])) - msg = '' - i = 0 - if len(users) > 10: - msg += '__10 of {} Players:__\n\n'.format(len(users)) - else: - msg += '__Players:__\n\n' - for user in users: - if len(userGame['Members']) >= self.minMembers: - if user == userGame['Members'][userGame['Judge']]: - continue - i += 1 - if i > 10: - break - - if user['Laid']: - if user['User']: - # Person - msg += '{}. *{}* - Cards are in.\n'.format(i, self.displayname(user['User'])) - else: - # Bot - msg += '{}. *{} ({})* - Cards are in.\n'.format(i, self.botName, user['ID']) - else: - if user['User']: - # Person - msg += '{}. *{}* - Waiting for cards...\n'.format(i, self.displayname(user['User'])) - else: - # Bot - msg += '{}. *{} ({})* - Waiting for cards...\n'.format(i, self.botName, user['ID']) - await self.bot.send_message(ctx.message.author, msg) - - @commands.command(pass_context=True) - async def removeplayer(self, ctx, *, name = None): - """Removes a player from the game. Can only be done by the player who created the game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - botCount = 0 - for member in userGame['Members']: - if member['IsBot']: - botCount += 1 - continue - if member['User'] == ctx.message.author: - if not member['Creator']: - # You didn't make this game - msg = 'Only the player that created the game can remove players.' - await self.bot.send_message(ctx.message.author, msg) - return - member['Time'] = int(time.time()) - # We are the creator - let's check the number of bots - if name == None: - # Nobody named! - msg = 'Okay, I removed... no one from the game...' - await self.bot.send_message(ctx.message.author, msg) - return - - # Let's get the person either by name, or by id - nameID = ''.join(list(filter(str.isdigit, name))) - for member in userGame['Members']: - toRemove = False - if member['IsBot']: - continue - if name.lower() == self.displayname(member['User']).lower(): - # Got em! - toRemove = True - elif nameID == member['ID']: - # Got em! - toRemove = True - if toRemove: - await self.removeMember(member['ID']) - break - # await self.nextPlay(ctx, userGame) - - if toRemove: - """# Start the game loop - event = userGame['NextHand'] - self.bot.loop.call_soon_threadsafe(event.set)""" - # Player was removed - try to handle it calmly... - await self.checkSubmissions(ctx, userGame) - else: - msg = 'I couldn\'t locate that player on this game. If you\'re trying to remove a bot, try the `{}removebot [id]` command.'.format(ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - - @commands.command(pass_context=True) - async def flushhand(self, ctx): - """Flushes the cards in your hand - can only be done once per game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - if userGame['Judge'] == -1: - msg = "The game hasn't started yet. Probably not worth it to flush your hand before you get it..." - await self.bot.send_message(ctx.message.author, msg) - return - for member in userGame['Members']: - if member['IsBot']: - continue - if member['User'] == ctx.message.author: - member['Time'] = int(time.time()) - # Found us! - if member['Refreshed']: - # Already flushed their hand - msg = 'You have already flushed your hand this game.' - await self.bot.send_message(ctx.message.author, msg) - return - else: - member['Hand'] = [] - await self.drawCards(member['ID']) - member['Refreshed'] = True - msg = 'Flushing your hand!' - await self.bot.send_message(ctx.message.author, msg) - await self.showHand(ctx, ctx.message.author) - return - - @commands.command(pass_context=True) - async def idlekick(self, ctx, *, setting = None): - """Sets whether or not to kick members if idle for 5 minutes or more. Can only be done by the player who created the game.""" - if not await self.checkPM(ctx.message): - return - # Check if the user is already in game - userGame = self.userGame(ctx.message.author) - if not userGame: - # Not in a game - msg = "You're not in a game - you can create one with `{}newcah` or join one with `{}joincah`.".format(ctx.prefix, ctx.prefix) - await self.bot.send_message(ctx.message.author, msg) - return - botCount = 0 - for member in userGame['Members']: - if member['IsBot']: - botCount += 1 - continue - if member['User'] == ctx.message.author: - if not member['Creator']: - # You didn't make this game - msg = 'Only the player that created the game can remove bots.' - await self.bot.send_message(ctx.message.author, msg) - return - # We are the creator - let's check the number of bots - if setting == None: - # Output idle kick status - if userGame['Timeout']: - await self.bot.send_message(ctx.message.channel, 'Idle kick is enabled.') - else: - await self.bot.send_message(ctx.message.channel, 'Idle kick is disabled.') - return - elif setting.lower() == "yes" or setting.lower() == "on" or setting.lower() == "true": - setting = True - elif setting.lower() == "no" or setting.lower() == "off" or setting.lower() == "false": - setting = False - else: - setting = None - - if setting == True: - if userGame['Timeout'] == True: - msg = 'Idle kick remains enabled.' - else: - msg = 'Idle kick now enabled.' - for member in userGame['Members']: - member['Time'] = int(time.time()) - else: - if userGame['Timeout'] == False: - msg = 'Idle kick remains disabled.' - else: - msg = 'Idle kick now disabled.' - userGame['Timeout'] = setting - - await self.bot.send_message(ctx.message.channel, msg) - - @commands.command() - async def cahcredits(self): - """Code credits.""" - message = await self._credit() - await self.bot.say(message) - - async def _credit(self): - message = "```This cog is made possible by CorpBot.\n" - message+= "Please visit https://github.com/corpnewt/CorpBot.py for more information.```" - return message - -def setup(bot): - bot.add_cog(CardsAgainstHumanity(bot)) diff --git a/cah/data/deck.json b/cah/data/deck.json deleted file mode 100644 index 208bf32..0000000 --- a/cah/data/deck.json +++ /dev/null @@ -1,2434 +0,0 @@ -{ - "blackCards":[ - { - "text":"Why can't I sleep at night?", - "pick":1 - }, - { - "text":"I got 99 problems but _ ain't one.", - "pick":1 - }, - { - "text":"What's a girl's best friend?", - "pick":1 - }, - { - "text":"What's that smell?", - "pick":1 - }, - { - "text":"This is the way the world ends \/ This is the way the world ends \/ Not with a bang but with _.", - "pick":1 - }, - { - "text":"What is Batman's guilty pleasure?", - "pick":1 - }, - { - "text":"TSA guidelines now prohibit _ on airplanes.", - "pick":1 - }, - { - "text":"What ended my last relationship?", - "pick":1 - }, - { - "text":"MTV's new reality show features eight washed-up celebrities living with _.", - "pick":1 - }, - { - "text":"I drink to forget _.", - "pick":1 - }, - { - "text":"I'm sorry, Professor, but I couldn't complete my homework because of _.", - "pick":1 - }, - { - "text":"Alternative medicine is now embracing the curative powers of _.", - "pick":1 - }, - { - "text":"What's that sound?", - "pick":1 - }, - { - "text":"What's the next Happy Meal toy?", - "pick":1 - }, - { - "text":"It's a pity that kids these days are all getting involved with _.", - "pick":1 - }, - { - "text":"In the new Disney Channel Original Movie, Hannah Montana struggles with _ for the first time.", - "pick":1 - }, - { - "text":"_. That's how I want to die.", - "pick":1 - }, - { - "text":"What does Dick Cheney prefer?", - "pick":1 - }, - { - "text":"What's the most emo?", - "pick":1 - }, - { - "text":"Instead of coal, Santa now gives the bad children _.", - "pick":1 - }, - { - "text":"Next from J.K. Rowling: Harry Potter and the Chamber of _.", - "pick":1 - }, - { - "text":"A romantic, candlelit dinner would be incomplete without _.", - "pick":1 - }, - { - "text":"White people like _.", - "pick":1 - }, - { - "text":"_. Betcha can't have just one!", - "pick":1 - }, - { - "text":"War! What is it good for?", - "pick":1 - }, - { - "text":"BILLY MAYS HERE FOR _.", - "pick":1 - }, - { - "text":"_. High five, bro.", - "pick":1 - }, - { - "text":"During sex, I like to think about _.", - "pick":1 - }, - { - "text":"What did I bring back from Mexico?", - "pick":1 - }, - { - "text":"What are my parents hiding from me?", - "pick":1 - }, - { - "text":"What will always get you laid?", - "pick":1 - }, - { - "text":"What would grandma find disturbing, yet oddly charming?", - "pick":1 - }, - { - "text":"What did the U.S. airdrop to the children of Afghanistan?", - "pick":1 - }, - { - "text":"What helps Obama unwind?", - "pick":1 - }, - { - "text":"What's there a ton of in heaven?", - "pick":1 - }, - { - "text":"Major League Baseball has banned _ for giving players an unfair advantage.", - "pick":1 - }, - { - "text":"When I am a billionaire, I shall erect a 50-foot statue to commemorate _.", - "pick":1 - }, - { - "text":"What's the new fad diet?", - "pick":1 - }, - { - "text":"When I am the President of the United States, I will create the Department of _.", - "pick":1 - }, - { - "text":"_. It's a trap!", - "pick":1 - }, - { - "text":"How am I maintaining my relationship status?", - "pick":1 - }, - { - "text":"What will I bring back in time to convince people that I am a powerful wizard?", - "pick":1 - }, - { - "text":"While the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on _.", - "pick":1 - }, - { - "text":"Coming to Broadway this season, _: The Musical.", - "pick":1 - }, - { - "text":"What's my secret power?", - "pick":1 - }, - { - "text":"What gives me uncontrollable gas?", - "pick":1 - }, - { - "text":"But before I kill you, Mr. Bond, I must show you _.", - "pick":1 - }, - { - "text":"What never fails to liven up the party?", - "pick":1 - }, - { - "text":"What am I giving up for Lent?", - "pick":1 - }, - { - "text":"What do old people smell like? ", - "pick":1 - }, - { - "text":"The class field trip was completely ruined by _.", - "pick":1 - }, - { - "text":"When Pharaoh remained unmoved, Moses called down a plague of _.", - "pick":1 - }, - { - "text":"I do not know with which weapons World War III will be fought, but World War IV will be fought with _.", - "pick":1 - }, - { - "text":"What's Teach for America using to inspire inner city students to succeed?", - "pick":1 - }, - { - "text":"In Michael Jackson's final moments, he thought about _.", - "pick":1 - }, - { - "text":"Why do I hurt all over?", - "pick":1 - }, - { - "text":"Studies show that lab rats navigate mazes 50% faster after being exposed to _.", - "pick":1 - }, - { - "text":"Why am I sticky?", - "pick":1 - }, - { - "text":"What's my anti-drug?", - "pick":1 - }, - { - "text":"And the Academy Award for _ goes to _.", - "pick":2 - }, - { - "text":"For my next trick, I will pull _ out of _.", - "pick":2 - }, - { - "text":"_: Good to the last drop.", - "pick":1 - }, - { - "text":"What did Vin Diesel eat for dinner?", - "pick":1 - }, - { - "text":"_: kid-tested, mother-approved.", - "pick":1 - }, - { - "text":"What gets better with age?", - "pick":1 - }, - { - "text":"I never truly understood _ until I encountered _.", - "pick":2 - }, - { - "text":"Rumor has it that Vladimir Putin's favorite delicacy is _ stuffed with _.", - "pick":2 - }, - { - "text":"Lifetime presents _, the story of _.", - "pick":2 - }, - { - "text":"Make a haiku.", - "pick":3 - }, - { - "text":"In M. Night Shyamalan's new movie, Bruce Willis discovers that _ had really been _ all along.", - "pick":2 - }, - { - "text":"_ is a slippery slope that leads to _.", - "pick":2 - }, - { - "text":"In a world ravaged by _, our only solace is _.", - "pick":2 - }, - { - "text":"That's right, I killed _. How, you ask? _.", - "pick":2 - }, - { - "text":"When I was tripping on acid, _ turned into _.", - "pick":2 - }, - { - "text":"_ + _ = _.", - "pick":3 - }, - { - "text":"What's the next superhero\/sidekick duo?", - "pick":2 - }, - { - "text":"Dear Abby, I'm having some trouble with _ and would like your advice.", - "pick":1 - }, - { - "text":"After the earthquake, Sean Penn brought _ to the people of Haiti.", - "pick":1 - }, - { - "text":"In L.A. County Jail, word is you can trade 200 cigarettes for _.", - "pick":1 - }, - { - "text":"Maybe she's born with it. Maybe it's _.", - "pick":1 - }, - { - "text":"Life for American Indians was forever changed when the White Man introduced them to _.", - "pick":1 - }, - { - "text":"Next on ESPN2, the World Series of _.", - "pick":1 - }, - { - "text":"Step 1: _. Step 2: _. Step 3: Profit.", - "pick":2 - }, - { - "text":"Here is the church... Here is the steeple... Open the doors... And there is _.", - "pick":1 - }, - { - "text":"How did I lose my virginity?", - "pick":1 - }, - { - "text":"During his childhood, Salvador Dali produced hundreds of paintings of _.", - "pick":1 - }, - { - "text":"In 1,000 years, when paper money is a distant memory, how will we pay for goods and services?", - "pick":1 - }, - { - "text":"What don't you want to find in your Kung Pao chicken?", - "pick":1 - }, - { - "text":"The Smithsonian Museum of Natural History has just opened an exhibit on _.", - "pick":1 - }, - { - "text":"Daddy, why is Mommy crying?", - "pick":1 - }, - { - "text":"An international tribunal has found _ guilty of _.", - "pick":2 - }, - { - "text":"And I would have gotten away with it, too, if it hadn't been for _!", - "pick":1 - }, - { - "text":"Dear Sir or Madam, We regret to inform you that the Office of _ has denied your request for _.", - "pick":2 - }, - { - "text":"He who controls _ controls the world.", - "pick":1 - }, - { - "text":"I learned the hard way that you can't cheer up a grieving friend with _.", - "pick":1 - }, - { - "text":"In a pinch, _ can be a suitable substitute for _.", - "pick":2 - }, - { - "text":"In his new self-produced album, Kanye West raps over the sounds of _.", - "pick":1 - }, - { - "text":"In its new tourism campaign, Detroit proudly proclaims that it has finally eliminated _.", - "pick":1 - }, - { - "text":"In Rome, there are whisperings that the Vatican has a secret room devoted to _.", - "pick":1 - }, - { - "text":"In the distant future, historians will agree that _ marked the beginning of America's decline.", - "pick":1 - }, - { - "text":"Michael Bay's new three-hour action epic pits _ against _.", - "pick":2 - }, - { - "text":"My plan for world domination begins with _.", - "pick":1 - }, - { - "text":"Next season on Man vs, Wild, Bear Grylls must survive the depths of the Amazon with only _ and his wits.", - "pick":1 - }, - { - "text":"Science will never explain _.", - "pick":1 - }, - { - "text":"Science will never explain the origin of _.", - "pick":1 - }, - { - "text":"The CIA now interrogates enemy agents by repeatedly subjecting them to _.", - "pick":1 - }, - { - "text":"The secret to a lasting marriage is communication, communication, and _.", - "pick":1 - }, - { - "text":"The socialist governments of Scandinavia have declared that access to _ is a basic human right.", - "pick":1 - }, - { - "text":"This season on Man vs. Wild, Bear Grylls must survive in the depths of the", - "pick":1 - }, - { - "text":"Amazon with only _ and his wits.", - "pick":1 - }, - { - "text":"What brought the orgy to a grinding halt?", - "pick":1 - }, - { - "text":"What has been making life difficult at the nudist colony?", - "pick":1 - }, - { - "text":"What's the gift that keeps on giving?", - "pick":1 - }, - { - "text":"When all else fails, I can always masturbate to _.", - "pick":1 - }, - { - "text":"When I pooped, what came out of my butt?", - "pick":1 - }, - { - "text":"_ would be woefully incomplete without _.", - "pick":2 - }, - { - "text":"After months of debate, the Occupy Wall Street General Assembly could only agree on \"More _!\"", - "pick":1 - }, - { - "text":"Before _, all we had was _.", - "pick":2 - }, - { - "text":"Before I run for president, I must destroy all evidence of my involvement with _.", - "pick":1 - }, - { - "text":"Charades was ruined for me forever when my mom had to act out _.", - "pick":1 - }, - { - "text":"During his midlife crisis, my dad got really into _.", - "pick":1 - }, - { - "text":"Everyone down on the ground! We don't want to hurt anyone. We're just here for _.", - "pick":1 - }, - { - "text":"I spent my whole life working toward _, only to have it ruined by _.", - "pick":2 - }, - { - "text":"I went from _ to _, all thanks to _.", - "pick":3 - }, - { - "text":"If God didn't want us to enjoy _, he wouldn't have given us _.", - "pick":2 - }, - { - "text":"In his newest and most difficult stunt, David Blaine must escape from _.", - "pick":1 - }, - { - "text":"Little Miss Muffet Sat on a tuffet, Eating her curds and _.", - "pick":1 - }, - { - "text":"Members of New York's social elite are paying thousands of dollars just to experience _.", - "pick":1 - }, - { - "text":"My country, 'tis of thee, sweet land of _.", - "pick":1 - }, - { - "text":"My mom freaked out when she looked at my browser history and found _.com\/_.", - "pick":2 - }, - { - "text":"My new favorite porn star is Joey \"_\" McGee.", - "pick":1 - }, - { - "text":"Next time on Dr. Phil: How to talk to your child about _.", - "pick":1 - }, - { - "text":"Only two things in life are certain: death and _.", - "pick":1 - }, - { - "text":"The Five Stages of Grief: denial, anger, bargaining, _, acceptance.", - "pick":1 - }, - { - "text":"The healing process began when I joined a support group for victims of _.", - "pick":1 - }, - { - "text":"The votes are in, and the new high school mascot is _.", - "pick":1 - }, - { - "text":"This is your captain speaking. Fasten your seatbelts and prepare for _.", - "pick":1 - }, - { - "text":"This month's Cosmo: \"Spice up your sex life by bringing _ into the bedroom.\"", - "pick":1 - }, - { - "text":"Tonight on 20\/20: What you don't know about _ could kill you.", - "pick":1 - }, - { - "text":"You haven't truly lived until you've experienced _ and _ at the same time.", - "pick":2 - }, - { - "text":"A remarkable new study has shown that chimps have evolved their own primitive version of _.", - "pick":1 - }, - { - "text":"What's harshing my mellow, man?", - "pick":1 - }, - { - "text":"Your persistence is admirable, my dear Prince. But you cannot win my heart with _ alone.", - "pick":1 - }, - { - "text":"In the seventh circle of Hell, sinners must endure _ for all eternity.", - "pick":1 - }, - { - "text":"A successful job interview begins with a firm handshake and ends with _.", - "pick":1 - }, - { - "text":"Lovin' you is easy 'cause you're _.", - "pick":1 - }, - { - "text":"My life is ruled by a vicious cycle of _ and _.", - "pick":2 - }, - { - "text":"The blind date was going horribly until we discovered our shared interest in _.", - "pick":1 - }, - { - "text":"_. Awesome in theory, kind of a mess in practice.", - "pick":1 - }, - { - "text":"I'm not like the rest of you. I'm too rich and busy for _.", - "pick":1 - }, - { - "text":"_: Hours of fun. Easy to use. Perfect for _!", - "pick":2 - }, - { - "text":"What left this stain on my couch?", - "pick":1 - }, - { - "text":"Call the law offices of Goldstein & Goldstein, because no one should have to tolerate _ in the workplace.", - "pick":1 - }, - { - "text":"When you get right down to it, _ is just _.", - "pick":2 - }, - { - "text":"Turns out that _-Man was neither the hero we needed nor wanted.", - "pick":1 - }, - { - "text":"As part of his daily regimen, Anderson Cooper sets aside 15 minutes for _.", - "pick":1 - }, - { - "text":"Money can't buy me love, but it can buy me _.", - "pick":1 - }, - { - "text":"With enough time and pressure, _ will turn into _.", - "pick":2 - }, - { - "text":"And what did you bring for show and tell?", - "pick":1 - }, - { - "text":"During high school I never really fit in until I found _ club.", - "pick":1 - }, - { - "text":"Hey baby, come back to my place and I'll show you _.", - "pick":1 - }, - { - "text":"After months of practice with _, I think I'm finally ready for _.", - "pick":2 - }, - { - "text":"To prepare for his upcoming role, Daniel Day-Lewis immersed himself in the world of _.", - "pick":1 - }, - { - "text":"Finally! A service that delivers _ right to your door.", - "pick":1 - }, - { - "text":"My gym teacher got fired for adding _ to the obstacle course.", - "pick":1 - }, - { - "text":"Having problems with _? Try _!", - "pick":2 - }, - { - "text":"As part of his contract, Prince won't perform without _ in his dressing room.", - "pick":1 - }, - { - "text":"Listen, son. If you want to get involved with _, I won't stop you. Just steer clear of _.", - "pick":2 - }, - { - "text":"What's fun until it gets weird?", - "pick":1 - }, - { - "text":"In the beginning, there was _. And the Lord said, \"Let there be _.\"", - "pick":2 - }, - { - "text":"Wes Anderson's new film tells the story of a precocious child coming to terms with _.", - "pick":1 - }, - { - "text":"_ will never be the same after _.", - "pick":2 - }, - { - "text":"I'm sorry, sir, but we don't allow _ at the country club.", - "pick":1 - }, - { - "text":"How am I compensating for my tiny penis?", - "pick":1 - }, - { - "text":"You've seen the bearded lady! You've seen the ring of fire! Now, ladies and gentlemen, feast your eyes upon _!", - "pick":1 - }, - { - "text":"We never did find _, but along the way we sure learned a lot about _.", - "pick":2 - }, - { - "text":"She's up all night for good fun. I'm up all night for _.", - "pick":1 - }, - { - "text":"_ may pass, but _ will last forever.", - "pick":2 - }, - { - "text":"Dear Leader Kim Jong-un, our village praises your", - "pick":1 - }, - { - "text":"infinite wisdom with a humble offering of _.", - "pick":1 - }, - { - "text":"Man, this is bullshit. Fuck _.", - "pick":1 - }, - { - "text":"You guys, I saw this crazy movie last night. It opens on _, and then there's some stuff about _, and then it ends with _.", - "pick":3 - }, - { - "text":"In return for my soul, the Devil promised me _, but all I got was _.", - "pick":2 - }, - { - "text":"The Japanese have developed a smaller, more efficient version of _.", - "pick":1 - }, - { - "text":"Alright, bros. Our frat house is condemned, and all the hot slampieces are over at Gamma Phi. That time has come to commence Operation _.", - "pick":1 - }, - { - "text":"This is the prime of my life. I'm young, hot, and full of _.", - "pick":1 - }, - { - "text":"I'm pretty sure I'm high right now, because I'm absolutely mesmerized by _.", - "pick":1 - }, - { - "text":"It lurks in the night. It hungers for flesh. This summer, no one is safe from _.", - "pick":1 - }, - { - "text":"If you can't handle _, you'd better stay away from _.", - "pick":2 - }, - { - "text":"Forget everything you know about _, because now we've supercharged it with _!", - "pick":2 - }, - { - "text":"Honey, I have a new role-play I want to try tonight!", - "pick":1 - }, - { - "text":"You can be _, and I'll be _.", - "pick":2 - }, - { - "text":"This year's hottest album is \"_\" by _.", - "pick":2 - }, - { - "text":"Every step towards _ gets me a little bit closer to _.", - "pick":2 - }, - { - "text":"Oprah's book of the month is \"_ For _: A Story of Hope.\"", - "pick":2 - }, - { - "text":"Do not fuck with me! I am literally _ right now.", - "pick":1 - }, - { - "text":"2 AM in the city that never sleeps. The door swings open and she walks in, legs up to here. Something in her eyes tells me she's looking for _.", - "pick":1 - }, - { - "text":"As king, how will I keep the peasants in line?", - "pick":1 - }, - { - "text":"Adventure. Romance. _. From Paramount Pictures, \"_.\"", - "pick":2 - }, - { - "text":"I am become _, destroyer of _!", - "pick":2 - }, - { - "text":"And today's soup is Cream of _.", - "pick":1 - }, - { - "text":"Armani suit: $1,000. Dinner for two at that swanky restaurant: $300. The look on her face when you surprise her with _: priceless.", - "pick":1 - }, - { - "text":"Do the Dew with our most extreme flavor yet! Get ready for Mountain Dew _!", - "pick":1 - }, - { - "text":"Do you lack energy? Does it sometimes feel like the whole world is _ ? Zoloft.", - "pick":1 - }, - { - "text":"Don't forget! Beginning this week, Casual Friday will officially become \"_ Friday.\"", - "pick":1 - }, - { - "text":"Get ready for the movie of the summer! One cop plays by the book. The other's only interested in one thing: _.", - "pick":1 - }, - { - "text":"Having the worst day EVER. #_", - "pick":1 - }, - { - "text":"Heed my voice, mortals! I am the god of _ , and I will not tolerate _!", - "pick":2 - }, - { - "text":"Help me doctor, I've got _ in my butt!", - "pick":1 - }, - { - "text":"Here at the Academy for Gifted Children, we all students to explore _ at their own pace.", - "pick":1 - }, - { - "text":"Hi MTV! My name is Kendra, I live in Malibu, I'm into _, and I love to have a good time.", - "pick":1 - }, - { - "text":"Hi, this is Jim from accounting. We noticed a $1,200 charge labeled \"_.\" Can you explain?", - "pick":1 - }, - { - "text":"I don't mean to brag, but they call me the Micheal Jordan of _.", - "pick":1 - }, - { - "text":"In his farewell address, George Washington famously warned Americans about the dangers of _.", - "pick":1 - }, - { - "text":"In his new action comedy, Jackie Chan must fend off ninjas while also dealing with _.", - "pick":1 - }, - { - "text":"Life's pretty tough in the fast lane. That's why I never leave the house without _.", - "pick":1 - }, - { - "text":"Now in bookstores: \"The Audacity of _\" by Barack Obama.", - "pick":1 - }, - { - "text":"Patient presents with _ . Likely a result of _ .", - "pick":2 - }, - { - "text":"Well if _ is good enough for _, it's good enough for me.", - "pick":2 - }, - { - "text":"Well what do you have to say for yourself, Casey? This is the third time you've been sent to the principal's office for _.", - "pick":1 - }, - { - "text":"What killed my boner?", - "pick":1 - }, - { - "text":"What's making things awkward in the sauna?", - "pick":1 - }, - { - "text":"WHOOO! God damn I love _!", - "pick":1 - }, - { - "text":"Why am I broke?", - "pick":1 - }, - { - "text":"Yo' mama's so fat she _!", - "pick":1 - }, - { - "text":"I work my ass off all day for this family, and this what I come home to? _!?", - "pick":1 - }, - { - "text":"I have a strict policy. First date, dinner. Second date, kiss. Third date, _.", - "pick":1 - }, - { - "text":"When I was a kid we used to play Cowboys and _.", - "pick":1 - }, - { - "text":"This is America. If you don't work hard, you don't succeed. I don't care if you're black, white, purple, or _.", - "pick":1 - }, - { - "text":"You Won't Believe These 15 Hilarious _ Bloopers!", - "pick":1 - }, - { - "text":"James is a lonely boy. But when he discovers a secret door in his attic, he meets a magical new friend: _.", - "pick":1 - }, - { - "text":"Don't worry, kid. It gets better. I've been living with _ for 20 years.", - "pick":1 - }, - { - "text":"My grandfather worked his way up from nothing.", - "pick":1 - }, - { - "text":"When he came to this country, all he had was the shoes on his feet and _.", - "pick":1 - }, - { - "text":"Behind every powerful man is _.", - "pick":1 - }, - { - "text":"You are not alone. Millions of Americans struggle with _ every day.", - "pick":1 - }, - { - "text":"Come to Dubai, where you can relax in our world-famous spas, experience the nightlife, or simply enjoy _ by the poolside.", - "pick":1 - }, - { - "text":"'This is madness!' 'No. THIS IS _!'", - "pick":1 - }, - { - "text":"Listen, Gary, I like you. But if you want that corner office, you're going to have to show me _.", - "pick":1 - }, - { - "text":"I went to the desert and ate of the peyote cactus.", - "pick":1 - }, - { - "text":"Turns out my spirit animal is _.", - "pick":1 - }, - { - "text":"And would you like those buffalo wings mild, hot, or _?", - "pick":1 - }, - { - "text":"The six things I could never do without: oxygen, facebook, chocolate, netflix, friends, and _ LOL!", - "pick":1 - }, - { - "text":"Why won't you make love to me anymore? Is it _?", - "pick":1 - }, - { - "text":"Puberty is a time of change. You might notice hair growing in new places. You might develop an interest in _. This is normal.", - "pick":1 - }, - { - "text":"I'm sorry, Mrs. Chen, but there was nothing we could do. At 4:15 this morning, your son succumbed to _.", - "pick":1 - }, - { - "text":"I'm Miss Tennessee, and if I could make the world better by changing one thing, I would get rid of _.", - "pick":1 - }, - { - "text":"Tonight, we will have sex. And afterwards, if you'd like, a little bit of _.", - "pick":1 - }, - { - "text":"Everybody join hands and close your eyes. Do you sense that? That's the presence of _ in this room.", - "pick":1 - }, - { - "text":"To become a true Yanomamo warrior, you must prove that you can withstand _ without crying out.", - "pick":1 - }, - { - "text":"Y'all ready to get this thing started? I'm Nick Cannon, and this is America's Got _.", - "pick":1 - }, - { - "text":"If you had to describe me, the Card Czar, using only one of the cards in your hand, which one would it be?", - "pick":1 - }, - { - "text":"I may not be much to look at, but I fuck like _.", - "pick":1 - }, - { - "text":"Errbody in the club _.", - "pick":1 - }, - { - "text":"The top Google auto-complete results for \"Barack Obama\": Barack Obama Height. Barack Obama net worth. Barack Obama _.", - "pick":1 - }, - { - "text":"Barack Obama Height.", - "pick":1 - }, - { - "text":"Barack Obama net worth.", - "pick":1 - }, - { - "text":"Barack Obama ___.", - "pick":1 - }, - { - "text":"Son, take it from someone who's been around the block a few times. Nothin' puts her in the mood like _.", - "pick":1 - }, - { - "text":"Art isn't just a painting in a stuffy museum. Art is alive. Art is _.", - "pick":1 - }, - { - "text":"You won't believe what's in my pussy. It's _.", - "pick":1 - }, - { - "text":"What's the most problematic?", - "pick":1 - }, - { - "text":"Feeling so grateful! #amazing #mylife #_.", - "pick":1 - }, - { - "text":"What's a total waste of Hillary Clinton's time?", - "pick":1 - }, - { - "text":"What's the gayest?", - "pick":1 - }, - { - "text":"_ be all like _.", - "pick":2 - }, - { - "text":"Girls just wanna have _.", - "pick":1 - }, - { - "text":"One more thing. Watch out for Big Mike. They say he killed a man with _.", - "pick":1 - }, - { - "text":"Well, shit. My eyes ain't so good, but I'll eat my own boot if that ain't _!", - "pick":1 - }, - { - "text":"If at first you don't succeed, try _.", - "pick":1 - }, - { - "text":"What will end racism once and for all?", - "pick":1 - }, - { - "text":"I'll take the BBQ bacon burger with friend egg and fuck it how about _.", - "pick":1 - }, - { - "text":"Well if _ is a crime, then lock me up!", - "pick":1 - }, - { - "text":"We do not shake with our left hands in this country. That is the hand we use for _.", - "pick":1 - }, - { - "text":"You know who else liked _? Hitler.", - "pick":1 - }, - { - "text":"Poor Brandon, still living in his parent's basement. I heard he never got over _.", - "pick":1 - }, - { - "text":"What totally destroyed my asshole?", - "pick":1 - }, - { - "text":"I don't believe in God. I believe in _.", - "pick":1 - }, - { - "text":"Then the princess kissed the frog, and all of a sudden the frog was _!", - "pick":1 - }, - { - "text":"I got rhythm, I've got music, I've got _. Who could ask for anything more?", - "pick":1 - }, - { - "text":"No, no, no, no, no, no, NO! I will NOT let _ ruin this wedding.", - "pick":1 - }, - { - "text":"Coming to Red Lobster this month, _.", - "pick":1 - }, - { - "text":"Ooo, daddy like _.", - "pick":1 - }, - { - "text":"Best you go back where you came from, now. We don't take too kindly to _ in these parts.", - "pick":1 - }, - { - "text":"Summer lovin', had me a blast. _, happened so fast.", - "pick":1 - }, - { - "text":"LSD + _ = really bad time.", - "pick":1 - }, - { - "text":"What are all those whales singing about?", - "pick":1 - }, - { - "text":"As Teddy Roosevelt said, the four manly virtues are honor, temperance, industry, and _.", - "pick":1 - }, - { - "text":"I tell you, it was a non-stop fuckfest. When it was over, my asshole looked like _.", - "pick":1 - }, - { - "text":"What turned me into a Republican?", - "pick":1 - }, - { - "text":"I'm sorry, sir, but your insurance plan doesn't cover injuries caused by _.", - "pick":1 - }, - { - "text":"Run, run, as fast as you can! You can't catch me, I'm _!", - "pick":1 - }, - { - "text":"There is no God. It's just _ and then you die.", - "pick":1 - }, - { - "text":"She's just one of the guys, you know? She likes beer, and football, and _.", - "pick":1 - }, - { - "text":"This Friday at the Liquid Lunge, it's _ Night! Ladies drink free.", - "pick":1 - }, - { - "text":"What sucks balls?", - "pick":1 - }, - { - "text":"I've had a horrible vision, father. I saw mountains crumbling, stars falling from the sky. I saw _.", - "pick":1 - }, - { - "text":"Oh no! Siri, how do I fix _?", - "pick":1 - }, - { - "text":"Dance like there's nobody watching, love like you'll never be hurt, and live like you're _.", - "pick":1 - }, - { - "text":"Mom's to-do list: Buy Groceries Clean up ___. Soccer Practice.", - "pick":1 - }, - { - "text":"Why am I laughing and crying and taking off my clothes?", - "pick":1 - }, - { - "text":"She's a lady in the streets, _ in the sheets.", - "pick":1 - }, - { - "text":"What's about to take dance floor to the next level?", - "pick":1 - }, - { - "text":"CNN breaking news! Scientists discover _.", - "pick":1 - }, - { - "text":"Most Americans would not vote for a candidate who is openly _.", - "pick":1 - }, - { - "text":"Congratulations! You have been selected for our summer internship program. While we are unable to offer a salary, we can offer you _.", - "pick":1 - }, - { - "text":"In the 1950s, psychologists prescribed _ as a cure for homosexually.", - "pick":1 - }, - { - "text":"As reparations for slavery, all African Americans will receive _.", - "pick":1 - }, - { - "text":"With a one-time gift of just $10, you can save this child from _.", - "pick":1 - }, - { - "text":"Google Calendar alert: _ in 10 minutes.", - "pick":1 - } - ], - "whiteCards":[ - "Coat hanger abortions.", - "Man meat.", - "Autocannibalism.", - "Vigorous jazz hands.", - "Flightless birds.", - "Pictures of boobs.", - "Doing the right thing.", - "The violation of our most basic human rights.", - "Viagra.", - "Self-loathing.", - "Spectacular abs.", - "A balanced breakfast.", - "Roofies.", - "Concealing a boner.", - "Amputees.", - "The Big Bang.", - "Former President George W. Bush.", - "The Rev. Dr. Martin Luther King, Jr.", - "Smegma.", - "Being marginalized.", - "Cuddling.", - "Laying an egg.", - "The Pope.", - "Aaron Burr.", - "Genital piercings.", - "Fingering.", - "A bleached asshole.", - "Horse meat.", - "Fear itself.", - "Science.", - "Elderly Japanese men.", - "Stranger danger.", - "The terrorists.", - "Praying the gay away.", - "Same-sex ice dancing.", - "Ethnic cleansing.", - "Cheating in the Special Olympics.", - "German dungeon porn.", - "Bingeing and purging.", - "Making a pouty face.", - "William Shatner.", - "Heteronormativity.", - "Nickelback.", - "Tom Cruise.", - "The profoundly handicapped.", - "The placenta.", - "Chainsaws for hands.", - "Arnold Schwarzenegger.", - "An icepick lobotomy.", - "Goblins.", - "Object permanence.", - "Dying.", - "Foreskin.", - "A falcon with a cap on its head.", - "Hormone injections.", - "Dying of dysentery.", - "Sexy pillow fights.", - "The invisible hand.", - "A really cool hat.", - "Sean Penn.", - "Heartwarming orphans.", - "The clitoris.", - "The Three-Fifths compromise.", - "A sad handjob.", - "Men.", - "Historically black colleges.", - "A micropenis.", - "Raptor attacks.", - "Agriculture.", - "Vikings.", - "Pretending to care.", - "The Underground Railroad.", - "My humps.", - "Being a dick to children.", - "Geese.", - "Bling.", - "Sniffing glue.", - "The South.", - "An Oedipus complex.", - "Eating all of the cookies before the AIDS bake-sale.", - "Sexting.", - "YOU MUST CONSTRUCT ADDITIONAL PYLONS.", - "Mutually-assured destruction.", - "Sunshine and rainbows.", - "Count Chocula.", - "Sharing needles.", - "Being rich.", - "Skeletor.", - "A sausage festival.", - "Michael Jackson.", - "Emotions.", - "Farting and walking away.", - "The Chinese gymnastics team.", - "Necrophilia.", - "Spontaneous human combustion.", - "Yeast.", - "Leaving an awkward voicemail.", - "Dick Cheney.", - "White people.", - "Penis envy.", - "Teaching a robot to love.", - "Sperm whales.", - "Scrubbing under the folds.", - "Panda sex.", - "Whipping it out.", - "Catapults.", - "Masturbation.", - "Natural selection.", - "Opposable thumbs.", - "A sassy black woman.", - "AIDS.", - "The KKK.", - "Figgy pudding.", - "Seppuku.", - "Gandhi.", - "Preteens.", - "Toni Morrison's vagina.", - "Five-Dollar Footlongs.", - "Land mines.", - "A sea of troubles.", - "A zesty breakfast burrito.", - "Christopher Walken.", - "Friction.", - "Balls.", - "Dental dams.", - "A can of whoop-ass.", - "A tiny horse.", - "Waiting 'til marriage.", - "Authentic Mexican cuisine.", - "Genghis Khan.", - "Old-people smell.", - "Feeding Rosie O'Donnell.", - "Pixelated bukkake.", - "Friends with benefits.", - "The token minority.", - "The Tempur-Pedic Swedish Sleep System.", - "A thermonuclear detonation.", - "Take-backsies.", - "The Rapture.", - "A cooler full of organs.", - "Sweet, sweet vengeance.", - "RoboCop.", - "Keanu Reeves.", - "Drinking alone.", - "Giving 110%.", - "Flesh-eating bacteria.", - "The American Dream.", - "Taking off your shirt.", - "Me time.", - "A murder most foul.", - "The inevitable heat death of the universe.", - "The folly of man.", - "That thing that electrocutes your abs.", - "Cards Against Humanity.", - "Fiery poops.", - "Poor people.", - "Edible underpants.", - "Britney Spears at 55.", - "All-you-can-eat shrimp for $4.99.", - "Pooping back and forth. Forever.", - "Fancy Feast.", - "Jewish fraternities.", - "Being a motherfucking sorcerer.", - "Pulling out.", - "Picking up girls at the abortion clinic.", - "The homosexual agenda.", - "The Holy Bible.", - "Passive-agression.", - "Ronald Reagan.", - "Vehicular manslaughter.", - "Nipple blades.", - "Assless chaps.", - "Full frontal nudity.", - "Hulk Hogan.", - "Daddy issues.", - "The hardworking Mexican.", - "Natalie Portman.", - "Waking up half-naked in a Denny's parking lot.", - "God.", - "Sean Connery.", - "Saxophone solos.", - "Gloryholes.", - "The World of Warcraft.", - "Homeless people.", - "Scalping.", - "Darth Vader.", - "Eating the last known bison.", - "Guys who don't call.", - "Hot Pockets.", - "A time travel paradox.", - "The milk man.", - "Testicular torsion.", - "Dropping a chandelier on your enemies and riding the rope up.", - "World peace.", - "A salty surprise.", - "Poorly-timed Holocaust jokes.", - "Smallpox blankets.", - "Licking things to claim them as your own.", - "The heart of a child.", - "Robert Downey, Jr.", - "Lockjaw.", - "Eugenics.", - "A good sniff.", - "Friendly fire.", - "The taint; the grundle; the fleshy fun-bridge.", - "Wearing underwear inside-out to avoid doing laundry.", - "Hurricane Katrina.", - "Free samples.", - "Jerking off into a pool of children's tears.", - "A foul mouth.", - "The glass ceiling.", - "Republicans.", - "Explosions.", - "Michelle Obama's arms.", - "Getting really high.", - "Attitude.", - "Sarah Palin.", - "The Ubermensch.", - "Altar boys.", - "My soul.", - "My sex life.", - "Pedophiles.", - "72 virgins.", - "Pabst Blue Ribbon.", - "Domino's Oreo Dessert Pizza.", - "A snapping turtle biting the tip of your penis.", - "The Blood of Christ.", - "Half-assed foreplay.", - "My collection of high-tech sex toys.", - "A middle-aged man on roller skates.", - "Bitches.", - "Bill Nye the Science Guy.", - "Italians.", - "A windmill full of corpses.", - "Adderall.", - "Crippling debt.", - "A stray pube.", - "Prancing.", - "Passing a kidney stone.", - "A brain tumor.", - "Leprosy.", - "Puppies!", - "Bees?", - "Frolicking.", - "Repression.", - "Road head.", - "A bag of magic beans.", - "An asymmetric boob job.", - "Dead parents.", - "Public ridicule.", - "A mating display.", - "A mime having a stroke.", - "Stephen Hawking talking dirty.", - "African children.", - "Mouth herpes.", - "Overcompensation.", - "Riding off into the sunset.", - "Being on fire.", - "Tangled Slinkys.", - "Civilian casualties.", - "Auschwitz.", - "My genitals.", - "Not reciprocating oral sex.", - "Lactation.", - "Being fabulous.", - "Shaquille O'Neal's acting career.", - "My relationship status.", - "Asians who aren't good at math.", - "Alcoholism.", - "Incest.", - "Grave robbing.", - "Hope.", - "8 oz. of sweet Mexican black-tar heroin.", - "Kids with ass cancer.", - "Winking at old people.", - "The Jews.", - "Justin Bieber.", - "Doin' it in the butt.", - "A lifetime of sadness.", - "The Hamburglar.", - "Swooping.", - "Classist undertones.", - "New Age music.", - "Not giving a shit about the Third World.", - "The Kool-Aid Man.", - "A hot mess.", - "Tentacle porn.", - "Lumberjack fantasies.", - "The gays.", - "Scientology.", - "Estrogen.", - "GoGurt.", - "Judge Judy.", - "Dick fingers.", - "Racism.", - "Surprise sex!", - "Police brutality.", - "Passable transvestites.", - "The Virginia Tech Massacre.", - "When you fart and a little bit comes out.", - "Oompa-Loompas.", - "A fetus.", - "Obesity.", - "Tasteful sideboob.", - "Hot people.", - "BATMAN!!!", - "Black people.", - "A gassy antelope.", - "Sexual tension.", - "Third base.", - "Racially-biased SAT questions.", - "Porn stars.", - "A Super Soaker full of cat pee.", - "Muhammed (Praise Be Unto Him).", - "Puberty.", - "A disappointing birthday party.", - "An erection that lasts longer than four hours.", - "White privilege.", - "Getting so angry that you pop a boner.", - "Wifely duties.", - "Two midgets shitting into a bucket.", - "Queefing.", - "Wiping her butt.", - "Golden showers.", - "Barack Obama.", - "Nazis.", - "A robust mongoloid.", - "An M. Night Shyamalan plot twist.", - "Getting drunk on mouthwash.", - "Lunchables.", - "Women in yogurt commercials.", - "John Wilkes Booth.", - "Powerful thighs.", - "Mr. Clean, right behind you.", - "Multiple stab wounds.", - "Cybernetic enhancements.", - "Serfdom.", - "Kanye West.", - "Women's suffrage.", - "Children on leashes.", - "Harry Potter erotica.", - "The Dance of the Sugar Plum Fairy.", - "Lance Armstrong's missing testicle.", - "Parting the Red Sea.", - "The Amish.", - "Dead babies.", - "Child beauty pageants.", - "AXE Body Spray.", - "Centaurs.", - "Copping a feel.", - "Grandma.", - "Famine.", - "The Trail of Tears.", - "The miracle of childbirth.", - "Finger painting.", - "A monkey smoking a cigar.", - "The Make-A-Wish Foundation.", - "Anal beads.", - "The Force.", - "Kamikaze pilots.", - "Dry heaving.", - "Active listening.", - "Ghosts.", - "The Hustle.", - "Peeing a little bit.", - "Another goddamn vampire movie.", - "Shapeshifters.", - "The Care Bear Stare.", - "Hot cheese.", - "A mopey zoo lion.", - "A defective condom.", - "Teenage pregnancy.", - "A Bop It.", - "Expecting a burp and vomiting on the floor.", - "Horrifying laser hair removal accidents.", - "Boogers.", - "Unfathomable stupidity.", - "Breaking out into song and dance.", - "Soup that is too hot.", - "Morgan Freeman's voice.", - "Getting naked and watching Nickelodeon.", - "MechaHitler.", - "Flying sex snakes.", - "The true meaning of Christmas.", - "My inner demons.", - "Pac-Man uncontrollably guzzling cum.", - "My vagina.", - "A homoerotic volleyball montage.", - "Actually taking candy from a baby.", - "Crystal meth.", - "Exactly what you'd expect.", - "Natural male enhancement.", - "Passive-aggressive Post-it notes.", - "Inappropriate yodeling.", - "Lady Gaga.", - "The Little Engine That Could.", - "Vigilante justice.", - "A death ray.", - "Poor life choices.", - "A gentle caress of the inner thigh.", - "Embryonic stem cells.", - "Nicolas Cage.", - "Firing a rifle into the air while balls deep in a squealing hog.", - "Switching to Geico.", - "The chronic.", - "Erectile dysfunction.", - "Home video of Oprah sobbing into a Lean Cuisine.", - "A bucket of fish heads.", - "50,000 volts straight to the nipples.", - "Being fat and stupid.", - "Hospice care.", - "A pyramid of severed heads.", - "Getting married, having a few kids, buying some stuff, retiring to Florida, and dying.", - "A subscription to Men's Fitness.", - "Crucifixion.", - "A micropig wearing a tiny raincoat and booties.", - "Some god-damn peace and quiet.", - "Used panties.", - "A tribe of warrior women.", - "The penny whistle solo from \"My Heart Will Go On.\"", - "An oversized lollipop.", - "Helplessly giggling at the mention of Hutus and Tutsis.", - "Not wearing pants.", - "Consensual sex.", - "Her Majesty, Queen Elizabeth II.", - "Funky fresh rhymes.", - "The art of seduction.", - "The Devil himself.", - "Advice from a wise, old black man.", - "Destroying the evidence.", - "The light of a billion suns.", - "Wet dreams.", - "Synergistic management solutions.", - "Growing a pair.", - "Silence.", - "An M16 assault rifle.", - "Poopy diapers.", - "A live studio audience.", - "The Great Depression.", - "A spastic nerd.", - "Rush Limbaugh's soft, shitty body.", - "Tickling Sean Hannity, even after he tells you to stop.", - "Stalin.", - "Brown people.", - "Rehab.", - "Capturing Newt Gingrich and forcing him to dance in a monkey suit.", - "Battlefield amputations.", - "An uppercut.", - "Shiny objects.", - "An ugly face.", - "Menstrual rage.", - "A bitch slap.", - "One trillion dollars.", - "Chunks of dead prostitute.", - "The entire Mormon Tabernacle Choir.", - "The female orgasm.", - "Extremely tight pants.", - "The Boy Scouts of America.", - "Stormtroopers.", - "Throwing a virgin into a volcano.", - "24-hour media coverage", - "A beached whale.", - "A big black dick.", - "A bloody pacifier.", - "A crappy little hand.", - "A fat bald man from the internet.", - "A low standard of living.", - "A nuanced critique.", - "A panty raid.", - "A passionate Latino lover.", - "A plunger to the face.", - "A rival dojo.", - "A smiling black man, a latina businesswoman, a cool asian, and some whites.", - "A web of lies.", - "A woman scorned.", - "An atomic wedgie.", - "An Etsy steampunk strap-on.", - "An evil man in evil clothes.", - "AndrÈ the Giant's enormous, leathery scrotum.", - "Apologizing.", - "Appreciative snapping.", - "Ashton Kutcher.", - "Beating your wives.", - "Being a busy adult with many important things to do.", - "Being a dinosaur.", - "Blaxploitation.", - "Bosnian chicken farmers.", - "Breaking nip slip news.", - "Carnies.", - "Clams.", - "Clenched butt cheeks.", - "Coughing into a vagina.", - "Cutting.", - "Dancing with a broom.", - "Deflowering a princess.", - "Deflowering the princess.", - "Dorito breath.", - "Eating an albino.", - "Enormous Scandinavian women.", - "Fabricating statistics.", - "Finding a skeleton.", - "Gandalf.", - "Genetically engineered super-soldiers.", - "George Clooney's musk.", - "Getting abducted by Peter Pan.", - "Getting in her pants, politely.", - "Gladiatorial combat.", - "Good grammar.", - "having a penis", - "Hipsters.", - "Historical revisionism.", - "Insatiable bloodlust.", - "Jafar.", - "Jean-Claude Van Damme in slow motion.", - "Jean-Claude Van Damme.", - "Just the tip.", - "Leveling up.", - "Literally eating shit.", - "Mad hacky-sack skills.", - "Making the penises kiss.", - "Media coverage.", - "Medieval Times Dinner & Tournament.", - "Mom.", - "Moral ambiguity.", - "My machete.", - "Neil Patrick Harris.", - "NOOOOOOOOO!!!", - "Nubile slave boys.", - "Ominous background music.", - "One thousand Slim Jims.", - "Overpowering your father.", - "Panty raids.", - "Pistol-whipping a hostage.", - "Quiche.", - "Quivering jowls.", - "Revenge fucking.", - "Ripping into a man's chest and pulling out his still-beating heart.", - "Ryan Gosling riding in on a white horse.", - "Salvia.", - "Sanding off a man's nose.", - "Santa Claus.", - "Savagely beating a mascot", - "Scrotum tickling.", - "Sexual humiliation.", - "Sexy Siamese twins.", - "Shaft.", - "Slow motion.", - "Space muffins.", - "Statistically validated stereotypes.", - "Stockholm syndrome", - "Sudden Poop Explosion Disease.", - "Suicidal thoughts.", - "Syphilitic insanity", - "The boners of the elderly.", - "The economy.", - "The Fanta girls.", - "The four arms of Vishnu.", - "The gulags.", - "The harsh light of day.", - "The hiccups.", - "The ooze", - "The shambling corpse of Larry King.", - "This guy!", - "Tripping balls.", - "Walking in on Dad peeing into Mom's mouth.", - "Words, words, words.", - "Zeus's sexual appetites.", - "A 55-gallon drum of lube.", - "A bigger, blacker dick.", - "A Burmese tiger pit.", - "A dollop of sour cream.", - "A fortuitous turnip harvest.", - "A magic hippie love cloud.", - "A man in yoga pants with a ponytail and feather earrings.", - "A piÒata full of scorpions", - "A sad fat dragon with no friends.", - "A slightly shittier parallel universe.", - "A sofa that says \"I have style, but I like to be comfortable.\"", - "A soulful rendition of \"Ol' Man River.\"", - "A squadron of moles wearing aviator goggles.", - "A sweaty, panting leather daddy.", - "A sweet spaceship.", - "All of this blood.", - "An army of skeletons.", - "An ether-soaked rag.", - "An unhinged ferris wheel rolling toward the sea.", - "Another shot of morphine.", - "Basic human decency.", - "Beefin' over turf.", - "Being awesome at sex.", - "Boris the Soviet Love Hammer.", - "Bullshit.", - "Catastrophic urethral trauma.", - "Crushing Mr. Peanut's brittle body.", - "Daddy's belt.", - "Death by Steven Seagal.", - "Dennis the Menace.", - "Dining with cardboard cutouts of the cast of \"Friends.\"", - "Double penetration.", - "Existing.", - "Fetal alcohol syndrome.", - "Finding Waldo.", - "Fuck Mountain.", - "Getting hilariously gang-banged by the Blue Man Group.", - "Grandpa's ashes.", - "Graphic violence, adult language, and some sexual content.", - "Hillary Clinton's death stare.", - "Intimacy problems.", - "Jeff Goldblum.", - "Living in a trashcan.", - "Loki, the trickster god.", - "Making a friend.", - "Maximal insertion.", - "Me.", - "Mild autism.", - "Mooing.", - "My first kill.", - "Nunchuck moves.", - "Oncoming traffic.", - "One Ring to rule them all.", - "Power", - "Pretty Pretty Princess Board Game.", - "Pumping out a baby every nine months.", - "Rising from the grave.", - "Scrotal frostbite.", - "Some really fucked-up shit.", - "Special musical guest, Cher.", - "Spring break!", - "Subduing a grizzly bear and making her your wife.", - "Survivor's guilt.", - "Swiftly achieving orgasm.", - "Taking a man's eyes and balls out and putting his eyes where his balls go and then his balls in the eye holes.", - "The black Power Ranger", - "The corporations.", - "The day the birds attacked.", - "The Google.", - "The grey nutrient broth that sustains Mitt Romney.", - "The human body.", - "The mere concept of Applebee's.", - "The mixing of the races.", - "The new Radiohead album.", - "Tiny nipples.", - "Tongue.", - "Upgrading homeless people to mobile hotspots.", - "Weapons-grade plutonium.", - "Wearing an octopus for a hat.", - "Whining like a little bitch.", - "Whipping a disobedient slave.", - "That ass.", - "Nothing.", - "Shutting the fuck up.", - "The primal, ball-slapping sex your parents are having right now.", - "A cat video so cute that your eyes roll back and your spine slides out of your anus.", - "Cock.", - "A cop who is also a dog.", - "Dying alone and in pain.", - "Gay aliens.", - "The way white people is.", - "Reverse cowgirl.", - "The Quesadilla Explosion Salad from Chili's.", - "Actually getting shot, for real.", - "Not having sex.", - "Vietnam flashbacks.", - "Running naked through a mall, pissing and shitting everywhere.", - "Warm, velvety muppet sex.", - "Self-flagellation.", - "The systematic destruction of an entire people and their way of life.", - "Samuel L. Jackson.", - "A boo-boo.", - "Going around punching people.", - "The entire Internet.", - "Some kind of bird-man.", - "Chugging a lava lamp.", - "Having sex on top of a pizza.", - "Indescribable loneliness.", - "An ass disaster.", - "All my friends dying.", - "Putting an entire peanut butter and jelly sandwich into the VCR.", - "Spending lots of money.", - "Some douche with an acoustic guitar.", - "Flying robots that kill people.", - "A greased-up Matthew McConaughey.", - "An unstoppable wave of fire ants.", - "Not contributing to society in any meaningful way.", - "An all-midget production of Shakespeare's Richard III.", - "Screaming like a maniac.", - "The moist, demanding chasm of his mouth.", - "Filling every orifice with butterscotch pudding.", - "Unlimited soup, salad, and breadsticks.", - "Crying into the pages of Sylvia Plath.", - "Velcro.", - "A PowerPoint presentation.", - "A surprising amount of hair.", - "Eating Tom Selleck's mustache to gain his powers.", - "Roland the Farter, flatulist to the king.", - "A pile of squirming bodies.", - "Buying the right pants to be, cool.", - "Blood farts.", - "Three months in the hole.", - "A botched circumcision.", - "The Land of Chocolate.", - "Slapping a racist old lady.", - "A lamprey swimming up the toilet and latching onto your taint.", - "Jumping out at people.", - "A black male in his early 20s, last seen wearing a hoodie.", - "Mufasa's death scene.", - "Bill Clinton, naked on a bearskin rug with a saxophone.", - "Demonic possession.", - "The Harlem Globetrotters.", - "Vomiting mid-blowjob.", - "My manservant, Claude.", - "Having shotguns for legs.", - "Letting everyone down.", - "A spontaneous conga line.", - "A vagina that leads to another dimension.", - "Disco fever.", - "Getting your dick stuck in a Chinese finger trap with another dick.", - "Fisting.", - "The thin veneer of situational causality that underlies porn.", - "Girls that always be textin'.", - "Blowing some dudes in an alley.", - "Drinking ten 5-hour ENERGYs to get fifty continuous hours of energy.", - "Sneezing, farting, and coming at the same time.", - "A bunch of idiots playing a card game instead of interacting like normal humans.", - "A sex goblin with a carnival penis.", - "Lots and lots of abortions.", - "Injecting speed into one arm and horse tranquilizer into the other.", - "Sharks with legs.", - "A sex comet from Neptune that plunges the Earth into eternal sexiness.", - "How awesome I am.", - "Smoking crack, for instance.", - "A dance move that's just sex.", - "A hopeless amount of spiders.", - "Drinking responsibly.", - "Angelheaded hipsters burning for the ancient heavenly connection to the starry dynamo in the machinery of the night.", - "Bouncing up and down.", - "A shiny rock that proves I love you.", - "Crazy opium eyes.", - "Moderate-to-severe joint pain.", - "Actual mutants with medical conditions and no superpowers.", - "The complex geopolitical quagmire that is the Middle East.", - "Finally finishing off the Indians.", - "Neil Diamond's Greatest Hits.", - "No clothes on, penis in vagina.", - "Whispering all sexy.", - "A horse with no legs.", - "Depression.", - "Almost giving money to a homeless person.", - "Interspecies marriage.", - "Blackula.", - "What Jesus would do.", - "A manhole.", - "My dad's dumb fucking face.", - "A Ugandan warlord.", - "My worthless son.", - "A Native American who solves crimes by going into the spirit world.", - "A kiss on the lips.", - "A fart.", - "The peaceful and nonthreatening rise of China.", - "Snorting coke off a clown's boner.", - "Three consecutive seconds of happiness.", - "Falling into the toilet.", - "Ass to mouth.", - "Some sort of Asian.", - "The size of my penis.", - "The safe word.", - "Party Mexicans.", - "Ambiguous sarcasm.", - "Jizz.", - "An interracial handshake.", - "Incredible Facts About the Anus.", - "The secret formula for ultimate female satisfaction.", - "Sugar madness.", - "Calculating every mannerism so as not to suggest homosexuality.", - "Fucking a corpse back to life.", - "All the single ladies.", - "Whatever a McRib is made of.", - "Africa.", - "The euphoric rush of strangling a drifter.", - "Khakis.", - "A gender identity that can only be conveyed through slam poetry.", - "Stuffing a child's face with Fun Dip until he starts having fun.", - "A for-real lizard that spits blood from its eyes.", - "The tiniest shred of evidence that God is real.", - "Prince Ali, fabulous he, Ali Ababwa.", - "Dem titties.", - "Exploding pigeons.", - "My sex dungeon.", - "Child Protective Services.", - "Doo-doo.", - "Sports.", - "Unquestioning obedience.", - "Grammar nazis who are also regular Nazis.", - "40 acres and a mule.", - "A crazy little thing called love.", - "A disappointing salad.", - "A face full of horse cum.", - "A giant powdery manbaby.", - "A mouthful of potato salad.", - "A one-way ticket to Gary, Indiana.", - "A powered exoskeleton.", - "A reason not to commit suicide.", - "A team of lawyers.", - "A whole new kind of porn.", - "A zero-risk way to make $2,000 from home.", - "AIDS monkeys.", - "All these decorative pillows.", - "An unforgettable quinceanera.", - "An uninterrupted history of imperialism and exploitation.", - "Anal fissures like you wouldn't believe.", - "Ancient Athenian boy-fucking.", - "Backwards knees.", - "Being nine years old.", - "Being paralyzed from the neck down.", - "Being worshiped as the one true God.", - "Blackface.", - "Blowjobs for everyone.", - "Boring Vaginal sex.", - "Butt stuff.", - "Changing a person's mind with logic and facts.", - "Child support payments.", - "Cutting off a flamingo's legs with garden shears.", - "Daddy's credit card.", - "Deez nuts.", - "Denzel.", - "Doing the right stuff to her nipples.", - "Ejaculating live bees and the bees are angry.", - "Ennui.", - "Figuring out how to have sex with a dolphin.", - "Free ice cream, yo.", - "Genghis Khan's DNA.", - "Getting caught by the police and going to jail.", - "Getting drive-by shot.", - "Getting eaten alive by Guy Fieri.", - "Giant sperm from outer space.", - "Going to a high school reunion on ketamine.", - "Having been dead a while.", - "Mom's new boyfriend.", - "My boyfriends stupid penis.", - "My dead son's baseball glove.", - "My first period.", - "Not believing in giraffes.", - "Oil!", - "Out-of-this-world bazongas.", - "P.F. Chang himself.", - "Russian super-tuberculosis.", - "Seeing my village burned and my family slaughtered before my eyes.", - "Seeing things from Hitler's perspective.", - "September 11th, 2001.", - "Slowly easing down onto a cucumber.", - "Social justice warriors with flamethrowers of compassion.", - "Some shit-hot guitar licks.", - "The Abercrombie & Fitch lifestyle.", - "The basic suffering that pervades all of existence.", - "The eight gay warlocks who dictate the rules of fashion.", - "The ghost of Marlon Brando.", - "The inability to form meaningful relationships.", - "The passage of time.", - "The swim team, all at once.", - "The tiger that killed my father.", - "The unbelievable world of mushrooms.", - "The black half of Barack Obama.", - "The white half of Barack Obama.", - "Too much cocaine.", - "Unrelenting genital punishment.", - "Vegetarian options.", - "Wearing glasses and sounding smart.", - "Western standards of beauty.", - "A bass drop so huge it tears the starry vault asunder to reveal the face of God.", - "Growing up chained to a radiator in perpetual darkness.", - "Shitting all over the floor like a bad, bad girl.", - "A buttload of candy.", - "Sucking all the milk out of a yak.", - "Bullets.", - "A man who is so cool that he rides on a motorcycle.", - "Sudden penis loss.", - "Getting all offended.", - "Crying and shitting and eating spaghetti.", - "One unforgettable night of passion.", - "Being popular and good at sports.", - "Filling a man's anus with concrete.", - "Two whales fucking the shit out of each other.", - "Cool, relatable cancer teens.", - "The amount of gay I am.", - "A possible Muslim.", - "Unsheathing my massive horse cock.", - "A bowl of gourds.", - "The male gaze.", - "The power of the Dark Side.", - "Ripping a dog in half.", - "A constant need for validation.", - "Meaningless sex.", - "Such a big boy.", - "Throwing stones at a man until he dies.", - "Cancer.", - "Like a million alligators.", - "Eating together like a god damn family for once.", - "Cute boys.", - "Pussy.", - "Being a terrible mother.", - "Never having sex again.", - "A pizza guy who fucked up.", - "A whole lotta woman.", - "The all-new Nissan Pathfinder with 0.9% APR financing!", - "A peyote-fueled vision quest.", - "Kale.", - "Breastfeeding a ten year old.", - "Crippling social anxiety.", - "Immortality cream.", - "Texas.", - "Teaching a girl how to handjob the penis.", - "A turd.", - "Shapes and colors.", - "Whatever you wish, mother.", - "The haunting stare of an Iraqi child.", - "Robots who just want to party.", - "A self-microwaving burrito.", - "Forgetting grandma's first name.", - "Our new Buffalo Chicken Dippers!", - "Treasures beyond your wildest dreams.", - "Getting shot out of a cannon.", - "The sweet song of sword against sword and the braying of mighty war beasts.", - "Walking into a glass door.", - "The color \"puce.\"", - "Every ounce of charisma left in Mick Jagger's tired body.", - "The eighth graders.", - "Setting my balls on fire and cartwheeling to Ohio.", - "The dentist.", - "Gwyneth Paltrow's opinions.", - "Turning the rivers red with the blood of infidels.", - "Rabies.", - "Important news about Taylor Swift.", - "Ejaculating inside another man's wife.", - "Owls, the perfect predator.", - "Being John Malkovich.", - "Bathing in moonsblood and dancing around the ancient oak.", - "An oppressed people with a vibrant culture.", - "An overwhelming variety of cheeses.", - "Reading the entire End-User License Agreement.", - "Morpheus.", - "Peeing into a girl's butt to make a baby.", - "Generally having no idea what's going on.", - "No longer finding any Cards Against Humanity card funny.", - "Content.", - "Fucking me good and taking me to Red Lobster.", - "Self-identifying as a DJ.", - "Getting high with mom.", - "Beyonce.", - "Gazpacho.", - "Discovering that what I really want in life is to kill people and have sex with their corpses.", - "A man with the head of a goat and the body of a goat.", - "How good lead paint taste.", - "Dropping dead in a Sbarro's bathroom and not being found for 72 hours.", - "Eating too many Cinnabons and then vomiting and then eating the vomit.", - "Some of that good dick.", - "Two shitty kids and a garbage husband.", - "Pooping in the potty.", - "Bad emotions I don't want.", - "Mixing M&Ms and Skittles like some kind of psychopath.", - "Fucking my therapist.", - "The best, deepest quotes from The Dark Knight.", - "Meatloaf, the man.", - "Meatloaf, the food.", - "My huge penis and substantial fortune.", - "Hot lettuce.", - "It being too late to stop having sex with a horse.", - "Becoming the President of the United States.", - "Microaggressions.", - "Getting the Dorito crumbs out of my purse.", - "The sweet, forbidden meat of the monkey.", - "Consensual, nonreproductive incest.", - "Grunting for ten minutes and then peeing sand.", - "Prematurely ejaculating like a total loser.", - "Jazz.", - "Straight blazin' 24/7.", - "Having sex with a beautiful person.", - "Going around pulling people's tampons out.", - "Reaching an age where barbecue chips are better than sex.", - "Daddy going away forever.", - "Three hours of nonstop penetration.", - "Holding the proper political beliefs of my time to attract a mate.", - "Scissoring, if that's a thing.", - "Creamy slices of real, California avocado.", - "ISIS.", - "A weird guy who says weird stuff and weirds me out.", - "Rubbing my bush all over your bald head.", - "Farting all over my face with your tight little asshole.", - "Quinoa.", - "How sad it will be when Morgan Freeman dies.", - "A cheerfulness that belies a deep-seated self-loathing.", - "Farting a huge shit out of my pussy.", - "Defeating a gorilla in single combat.", - "A big ol' plate of fettuccine alfredo.", - "Brunch.", - "Anal.", - "A women's perspective.", - "A long business meeting with no obvious purpose.", - "Thinking about what eating even is.", - "Doing a somersault and barfing.", - "Trees.", - "Hating Jews.", - "Whooping your ass at Mario Kart.", - "A massive collection of child pornography.", - "Systems and policies designed to preserve centuries-old power structures.", - "Having an awesome time drinking and driving.", - "Muchin' puss.", - "Moon people.", - "Picking up a glass of water and taking a sip and being the president.", - "Critical thinking.", - "Showing all the boys my pussy.", - "Homework.", - "China.", - "Putting more black people in jail.", - "The ol' penis-in-the-popcorn surprise.", - "One of them big-city Jew lawyers.", - "Informing you that I am a registered sex offender.", - "Rolling so hard.", - "Who really did 9\/11.", - "Being turned into sausages.", - "Eating ass.", - "A dolphin that learns to talk and becomes the Dead of Harvard Law School.", - "Gay thoughts.", - "My dog dying.", - "Dominating a man by peeing on his eldest son.", - "Dis bitch.", - "A strong horse and enough rations for thirty days.", - "Feminism.", - "A cold and indifferent universe.", - "An incurable homosexual.", - "The amount of baby carrots I can fit up my ass.", - "Huge big balls full of jizz.", - "Gregor, my largest son.", - "Esmeralda, my most beautiful daughter.", - "Trevor, the world's greatest boyfriend.", - "Jason, the teen mayor.", - "That bitch, Stacy.", - "Gayle from HR.", - "Gary.", - "Just now finding out about the Armenian Genocide.", - "Opening your mouth to talk and a big penis fops out.", - "Twisting my cock and balls into a balloon poodle.", - "The wind.", - "A gun that shoots cobras.", - "Out-of-control teenage blowjob parties.", - "A black friend.", - "The body of a 46-year-old man.", - "Art.", - "Water.", - "Doritos and a Fruit Roll-Up.", - "Sucking each other's penises for hours on end.", - "10,000 shrieking teenage girls.", - "Whomsoever let the dogs out.", - "The chicken from Popeyes.", - "Assassinating the president.", - "Having sex with a man and then eating his head.", - "A burrito that's just sour cream.", - "An arrangement wherein I give a person money they have sex with me.", - "Facilitating dialogue and deconstructing binaries.", - "Taking the form of a falcon.", - "Watching you die.", - "An X-Man whose power is that he has sex with dogs and children.", - "Loud, scary thunder.", - "Every man's ultimate fantasy: a perfectly cylindrical vagina.", - "Tiny, rancid girl farts.", - "Math.", - "Founding a major world religion.", - "Plowing that ass like a New England corn farmer.", - "Period poops.", - "The feeling of going to McDonald's as a 6-year-old.", - "Misogyny.", - "Tables.", - "Feeling the emotion of anger.", - "One of those \"blow jobs\" I've been hearing so much about.", - "A creepy child singing a nursery rhyme.", - "Blossoming into a beautiful young woman.", - "The secret to truly resilient hair.", - "Rock-hard tits and a huge vagina.", - "The lived experience of African Americans.", - "Mental illness.", - "Getting eaten out by a dog.", - "10 football players with erections barreling towards you at full speed.", - "Starting a shitty podcast.", - "Overthrowing the democratically-elected government of Chile.", - "Guns.", - "A hug.", - "Getting aborted.", - "Crazy anal orgasms.", - "Getting this party started!", - "Being sexually attracted to children.", - "Antidepressants.", - "Getting trapped in a conversation about Ayn Rand.", - "Swearing praise upon the Sultan's hideous daughters.", - "Turning 32.", - "Sudden and unwanted slam poetry.", - "A negative body image that is totally justified.", - "Exploring each other's buttholes.", - "You.", - "Quacking like a duck in lieu of a cogent argument.", - "Catching a live salmon in your mouth.", - "Eating people.", - "Our baby.", - "Breastfeeding in public like a radiant earth goddess.", - "Big, smart money boys tap-tapping on their keyboards.", - "Finding a nice elevator to poop in.", - "The mysterious fog rolling into town.", - "Two beautiful pig sisters.", - "Condoleezza Rice.", - "The full force of the American military.", - "A woman's right to choose.", - "A terrified fat child who won't come out of the bushes.", - "A medium horchata.", - "How great my ass looks in these jeans.", - "Raising three kids on minimum wage.", - "The bond between a woman and her horse.", - "Slamming a dunk.", - "Chris Hemsworth.", - "Ejaculating at the apex of a cartwheel.", - "Child labor.", - "An older man.", - "Crushing the patriarchy.", - "Denying the Holocaust.", - "Falling into a pit of waffles.", - "Objectifying women.", - "Everything.", - "A creature made of penises that must constantly arouse itself to survive.", - "Getting blasted in the face by a t-shirt cannon.", - "Onions.", - "Dumpster juice.", - "Forty-five minutes of finger blasting.", - "Film roles for actresses over 40.", - "Having sex with your mom.", - "Having a vagina.", - "Regurgitating a half-digested sparrow.", - "The hottest MILF in Dallas.", - "Some real spicy shrimps.", - "The Rwandan Genocide.", - "The LGBT community.", - "Twenty bucks.", - "The full blown marginalization of ugly people.", - "A finger up the butt.", - "A big, beautiful mouth packed to the brim with sparkling teeth.", - "Getting laid like all the time.", - "Happy daddies with happy sandals.", - "Libertarians.", - "Late-stage dementia.", - "How strange it is to be anything at all.", - "Pooping in a leotard and hoping no one notices.", - "Restoring Germany to its former glory.", - "Participating.", - "Going to bed at a reasonable hour.", - "Smashing my balls at the moment of climax.", - "Making out and stuff.", - "Menopause.", - "The government.", - "The graceful path of an autumn leaf as it falls to its earthen cradle.", - "Tender chunks of all-white-meat chicken.", - "Twenty cheerleaders laughing at your tiny penis.", - "Mommy and daddy fighting all the time.", - "Playing my asshole like a trumpet.", - "Getting naked too soon.", - "A slowly encroaching circle of wolves.", - "A man in a suit with perfect hair who tells you beautiful lies.", - "Seizing control of the means of production.", - "Comprehensive immigration reform.", - "Awesome pictures of planets and stuff.", - "Aborting the shit out of a fetus.", - "A genetic predisposition for alcoholism.", - "The flaming wreckage of the International Space Station.", - "Waking up inside of a tornado.", - "When the big truck goes \"Toot! Toot!\"", - "The fear and hatred in men's hearts.", - "Getting killed and dragged up a tree by a leopard.", - "Eternal screaming madness.", - "My brother's hot friends.", - "Salsa Night at Dave's Cantina.", - "The clown that followed me home from the grocery store.", - "Watching a hot person eat.", - "Pretending to be one of the guys but actually being the spider god.", - "Getting pegged.", - "An empowered woman.", - "An old dog full of tumors.", - "A duffel bag full of lizards.", - "All these people I've killed.", - "Working so hard to have muscles and then having them.", - "Political correctness.", - "A tiny fireman who puts out tiny fires.", - "Albert Einstein but if he had a huge muscles and a rhinoceros cock.", - "Chipotle." - ] -} diff --git a/cah/info.json b/cah/info.json deleted file mode 100644 index 9e1351f..0000000 --- a/cah/info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "Thanks for installing Cards Against Humanity. I highly advise only playing this game with 3-4 people on one server, on a private bot, because of the high amount of DM's this game generates.", - "NAME" : "cah", - "SHORT" : "Cards Against Humanity.", - "DESCRIPTION" : "This cog was made possible by CorpBot: https://github.com/corpnewt/CorpBot.py" -} diff --git a/chatchart/chatchart.py b/chatchart/chatchart.py deleted file mode 100644 index d337892..0000000 --- a/chatchart/chatchart.py +++ /dev/null @@ -1,94 +0,0 @@ -# Lines 54 through 68 are influenced heavily by cacobot's stats module: -# https://github.com/Orangestar12/cacobot/blob/master/cacobot/stats.py -# Big thanks to Redjumpman for changing the beta version from -# Imagemagick/cairosvg to matplotlib. -# Thanks to violetnyte for suggesting this cog. -import discord -import heapq -import os -from io import BytesIO -import matplotlib -matplotlib.use('agg') -import matplotlib.pyplot as plt -plt.switch_backend('agg') -from discord.ext import commands - - -class ChatChart: - """Show activity.""" - - def __init__(self, bot): - self.bot = bot - - def create_chart(self, top, others): - plt.clf() - sizes = [x[1] for x in top] - labels = ["{} {:g}%".format(x[0], x[1]) for x in top] - if len(top) >= 10: - sizes = sizes + [others] - labels = labels + ["Others {:g}%".format(others)] - - title = plt.title('User activity in the last 5000 messages') - title.set_va("top") - title.set_ha("left") - plt.gca().axis("equal") - colors = ['r', 'darkorange', 'gold', 'y', 'olivedrab', 'green', 'darkcyan', 'mediumblue', 'darkblue', 'blueviolet', 'indigo'] - pie = plt.pie(sizes, colors=colors, startangle=0) - plt.legend(pie[0], labels, bbox_to_anchor=(0.7, 0.5), loc="center", fontsize=10, - bbox_transform=plt.gcf().transFigure) - plt.subplots_adjust(left=0.0, bottom=0.1, right=0.45) - image_object = BytesIO() - plt.savefig(image_object, format='PNG') - image_object.seek(0) - return image_object - - @commands.command(pass_context=True) - @commands.cooldown(1, 10, commands.BucketType.channel) - async def chatchart(self, ctx): - """ - Generates a pie chart, representing the last 5000 messages in this channel. - """ - channel = ctx.message.channel - e = discord.Embed(description="Loading...", colour=0x00ccff) - e.set_thumbnail(url="https://i.imgur.com/vSp4xRk.gif") - em = await self.bot.say(embed=e) - - history = [] - async for msg in self.bot.logs_from(channel, 5000): - history.append(msg) - msg_data = {'total count': 0, 'users': {}} - - for msg in history: - if msg.author.bot: - pass - elif msg.author.name in msg_data['users']: - msg_data['users'][msg.author.name]['msgcount'] += 1 - msg_data['total count'] += 1 - else: - msg_data['users'][msg.author.name] = {} - msg_data['users'][msg.author.name]['msgcount'] = 1 - msg_data['total count'] += 1 - - for usr in msg_data['users']: - pd = float(msg_data['users'][usr]['msgcount']) / float(msg_data['total count']) - msg_data['users'][usr]['percent'] = round(pd * 100, 1) - - top_ten = heapq.nlargest(10, [(x, msg_data['users'][x][y]) - for x in msg_data['users'] - for y in msg_data['users'][x] - if y == 'percent'], key=lambda x: x[1]) - others = 100 - sum(x[1] for x in top_ten) - img = self.create_chart(top_ten, others) - await self.bot.delete_message(em) - await self.bot.send_file(channel, img, filename="chart.png") - - -def check_folders(): - if not os.path.exists("data/chatchart"): - print("Creating data/chatchart folder...") - os.makedirs("data/chatchart") - - -def setup(bot): - check_folders() - bot.add_cog(ChatChart(bot)) diff --git a/chatchart/info.json b/chatchart/info.json deleted file mode 100644 index 1e380b9..0000000 --- a/chatchart/info.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "AUTHOR" : "aikaterna and Redjumpman", - "INSTALL_MSG" : "Use [p]chatchart to generate a chart for that channel.", - "NAME" : "chatchart", - "SHORT" : "Generate a pie chart from the last 5000 messages.", - "DESCRIPTION" : "Generate a pie chart from the last 5000 messages in a channel to see who's been talking the most.", - "REQUIREMENTS": ["matplotlib"], - "TAGS": ["data", "chart", "activity"] -} diff --git a/forwarding/forwarding.py b/forwarding/forwarding.py deleted file mode 100644 index 1239dff..0000000 --- a/forwarding/forwarding.py +++ /dev/null @@ -1,49 +0,0 @@ -# forwarding.py is ported from another bot: -# https://github.com/jacobcheatley/dankbot - -import discord -from discord.ext import commands -from .utils.dataIO import dataIO -from .utils import checks - - -class Forwarding: - def __init__(self, bot: commands.Bot): - self.bot = bot - self.owner = self.get_owner() - - def get_owner(self): - owner_id = dataIO.load_json("data/red/settings.json")["OWNER"] - return discord.utils.find(lambda m: m.id == owner_id, self.bot.get_all_members()) - - async def send_to_owner(self, **kwargs): - if self.owner is None: - self.owner = self.get_owner() - await self.bot.send_message(self.owner, **kwargs) - - async def on_message(self, message: discord.Message): - if self.owner is None: - self.owner = self.get_owner() - if not message.channel.is_private or message.channel.user.id == self.owner.id: - return - - embed = discord.Embed() - if message.author == self.bot.user: - embed.title = 'Sent PM to {}#{} ({}).'.format(message.channel.user.name, message.channel.user.discriminator, message.channel.user.id) - else: - embed.set_author(name=message.author, icon_url=message.author.avatar_url or message.author.default_avatar_url) - embed.title = '{} messaged me:'.format(message.channel.user.id) - embed.description = message.content - embed.timestamp = message.timestamp - - await self.send_to_owner(embed=embed) - - @commands.command() - @checks.is_owner() - async def pm(self, user: discord.User, *, content: str): - """PMs a person.""" - await self.bot.send_message(user, content) - - -def setup(bot): - bot.add_cog(Forwarding(bot)) diff --git a/forwarding/info.json b/forwarding/info.json deleted file mode 100644 index b040c7d..0000000 --- a/forwarding/info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "NAME" : "forwarding", - "SHORT" : "Forwards DMs sent to the bot to the owner.", - "DESCRIPTION" : "Forwards DMs sent to the bot to the owner.", - "TAGS": ["message", "dm"] -} diff --git a/hunting/hunting.py b/hunting/hunting.py deleted file mode 100644 index ae38cc8..0000000 --- a/hunting/hunting.py +++ /dev/null @@ -1,240 +0,0 @@ -from __main__ import send_cmd_help -from .utils.dataIO import dataIO -from discord.ext import commands -from .utils import checks -import datetime -import asyncio -import discord -import random -import time -import os - -# TODO -# Show error when timing intervals are the same - - -class Hunting: - def __init__(self, bot): - self.bot = bot - self.scores = dataIO.load_json('data/hunting/scores.json') - self.subscriptions = dataIO.load_json('data/hunting/subscriptions.json') - self.settings = dataIO.load_json('data/hunting/settings.json') - self.animals = {'duck': ':duck: **_Quack!_**', 'penguin': ':penguin: **_Noot!_**', 'chicken': ':rooster: **_Bah-gawk!_**', 'pigeon': ':dove: **_Coo!_**'} - self.in_game = [] - self.paused_games = [] - self._latest_message_check_message_limit = 5 - self._latest_message_check_wait_limit = self.settings['hunt_interval_maximum'] * 2 - self.next = None - - async def _save_scores(self): - dataIO.save_json('data/hunting/scores.json', self.scores) - - async def _save_subscriptions(self): - dataIO.save_json('data/hunting/subscriptions.json', self.subscriptions) - - async def _save_settings(self): - dataIO.save_json('data/hunting/settings.json', self.settings) - - @commands.group(pass_context=True, no_pm=True, name='hunting') - async def _hunting(self, context): - """Hunting, it hunts birds... and things that fly""" - if context.invoked_subcommand is None: - await send_cmd_help(context) - - @_hunting.command(pass_context=True, no_pm=True, name='start') - async def _start(self, context): - """Start the hunt""" - server = context.message.server - channel = context.message.channel - if server.id in self.subscriptions: - message = '**We\'re already hunting!**' - else: - self.subscriptions[server.id] = channel.id - message = '**The hunt has started. Good luck to all.**' - await self._save_subscriptions() - await self.bot.say(message) - - @_hunting.command(pass_context=True, no_pm=True, name='stop') - async def _stop(self, context): - """Stop the hunt""" - server = context.message.server - if server.id not in self.subscriptions: - message = '**We\'re not hunting!**' - else: - del self.subscriptions[server.id] - message = '**The hunt has stopped.**' - await self._save_subscriptions() - await self.bot.say(message) - - @_hunting.command(no_pm=True, name='timing') - @checks.is_owner() - async def _timing(self, interval_min: int, interval_max: int, bang_timeout: int): - """Change the timing""" - if interval_min > interval_max: - message = '**`interval_min` needs to be lower than `interval_max`**' - elif interval_min < 0 and interval_max < 0 and bang_timeout < 0: - message = '**Please no negative numbers!**' - elif interval_min == interval_max: - message = '**`interval_min` and `interval_max` cannot be the same**' - else: - self.settings['hunt_interval_minimum'] = interval_min - self.settings['hunt_interval_maximum'] = interval_max - self.settings['wait_for_bang_timeout'] = bang_timeout - await self._save_settings() - message = '**Timing has been set.**' - await self.bot.say(message) - - @_hunting.command(pass_context=True, no_pm=True, name='next') - @checks.is_owner() - async def _next(self, context): - """When will the next occurance happen?""" - if self.next: - time = abs(datetime.datetime.utcnow() - self.next) - total_seconds = int(time.total_seconds()) - hours, remainder = divmod(total_seconds, 60*60) - minutes, seconds = divmod(remainder, 60) - message = '**The next occurance will be in {} hours and {} minutes.**'.format(hours, minutes) - else: - message = '**There is currently no hunt.**' - await self.bot.say(message) - - @_hunting.command(pass_context=True, no_pm=True, name='score') - async def _score(self, context, member: discord.Member): - """This will show the score of a hunter""" - server = context.message.server - if server.id in self.scores: - if member.id in self.scores[server.id]: - message = '**{} shot a total of {} animals ({})**'.format(member.mention, self.scores[server.id][member.id]['total'], ', '.join([str(self.scores[server.id][member.id]['score'][x]) + ' ' + x.capitalize() + 's' for x in self.scores[server.id][member.id]['score']])) # (', '.join([str(self.scores[server.id][member.id]['score'][x]) + ' ' + x.capitalize() + 's' for x in self.scores[server.id][member.id]['score']])) - else: - message = '**Please shoot something before you can brag about it.**' - else: - message = '**Please shoot something before you can brag about it.**' - await self.bot.say(message) - - @_hunting.command(pass_context=True, no_pm=True, name='clearscore') - @checks.serverowner() - async def _clearscore(self, context): - """Clear the leaderboard""" - server = context.message.server - if server.id in self.scores: - self.scores[server.id] = {} - await self._save_scores() - message = 'Leaderboard is cleared' - else: - message = 'There\'s nothing to clear' - await self.bot.say(message) - - @_hunting.command(pass_context=True, no_pm=True, name='leaderboard', aliases=['scores']) - async def _huntingboard(self, context): - """This will show the top hunters on this server""" - server = context.message.server - if server.id in self.scores: - p = self.scores[server.id] - scores = sorted(p, key=lambda x: (p[x]['total']), reverse=True) - message = '```\n{:<4}{:<8}{}\n\n'.format('#', 'TOTAL', 'USERNAME') - for i, hunter in enumerate(scores, 1): - if i > 10: - break - message += '{:<4}{:<8}{} ({})\n'.format(i, p[hunter]['total'], p[hunter]['author_name'], ', '.join([str(p[hunter]['score'][x]) + ' ' + x.capitalize() + 's' for x in p[hunter]['score']])) - message += '```' - else: - message = '**Please shoot something before you can brag about it.**' - await self.bot.say(message) - - async def add_score(self, server, author, avian): - if server.id not in self.scores: - self.scores[server.id] = {} - if author.id not in self.scores[server.id]: - self.scores[server.id][author.id] = {} - self.scores[server.id][author.id]['score'] = {} - self.scores[server.id][author.id]['total'] = 0 - self.scores[server.id][author.id]['author_name'] = '' - for a in list(self.animals.keys()): - self.scores[server.id][author.id]['score'][a] = 0 - if avian not in self.scores[server.id][author.id]['score']: - self.scores[server.id][author.id]['score'][avian] = 0 - self.scores[server.id][author.id]['author_name'] = author.display_name - self.scores[server.id][author.id]['score'][avian] += 1 - self.scores[server.id][author.id]['total'] += 1 - await self._save_scores() - - async def _wait_for_bang(self, server, channel): - def check(message): - return message.content.lower().split(' ')[0] == 'bang' or message.content.lower() == 'b' if message.content else False - - animal = random.choice(list(self.animals.keys())) - await self.bot.send_message(channel, self.animals[animal]) - message = await self.bot.wait_for_message(channel=channel, timeout=self.settings['wait_for_bang_timeout'], check=check) - if message: - author = message.author - if random.randrange(0, 17) > 1: - await self.add_score(server, author, animal) - msg = '**{} shot a {}!**'.format(author.mention, animal) - else: - msg = '**{} missed the shot and the {} got away!**'.format(author.mention, animal) - else: - msg = '**The {} got away!** :confused:'.format(animal) - self.in_game.remove(channel.id) - await self.bot.send_message(channel, msg) - - async def _latest_message_check(self, channel): - async for message in self.bot.logs_from(channel, limit=self._latest_message_check_message_limit, reverse=True): - delta = datetime.datetime.utcnow() - message.timestamp - if delta.total_seconds() < self._latest_message_check_wait_limit and message.author.id != self.bot.user.id: - if channel.id in self.paused_games: - self.paused_games.remove(channel.id) - return True - if channel.id not in self.paused_games: - self.paused_games.append(channel.id) - await self.bot.send_message(channel, '**It seems there are no hunters here. The hunt will be resumed when someone treads here again.**') - return False - - async def _hunting_loop(self): - while self == self.bot.get_cog('Hunting'): - wait_time = random.randrange(self.settings['hunt_interval_minimum'], self.settings['hunt_interval_maximum']) - self.next = datetime.datetime.fromtimestamp(int(time.mktime(datetime.datetime.utcnow().timetuple())) + wait_time) - await asyncio.sleep(wait_time) - for server in self.subscriptions: - if self.subscriptions[server] not in self.in_game: - channel = self.bot.get_channel(self.subscriptions[server]) - server = self.bot.get_server(server) - if await self._latest_message_check(channel): - self.in_game.append(self.subscriptions[server.id]) - self.bot.loop = asyncio.get_event_loop() - self.bot.loop.create_task(self._wait_for_bang(server, channel)) - - -def check_folder(): - if not os.path.exists('data/hunting'): - print('Creating data/hunting folder...') - os.makedirs('data/hunting') - - -def check_files(): - f = 'data/hunting/settings.json' - if not dataIO.is_valid_json(f): - print('Creating empty settings.json...') - data = {} - data['hunt_interval_minimum'] = 300 - data['hunt_interval_maximum'] = 600 - data['wait_for_bang_timeout'] = 30 - dataIO.save_json(f, data) - - f = 'data/hunting/subscriptions.json' - if not dataIO.is_valid_json(f): - print('Creating empty subscriptions.json...') - dataIO.save_json(f, {}) - - f = 'data/hunting/scores.json' - if not dataIO.is_valid_json(f): - print('Creating empty scores.json...') - dataIO.save_json(f, {}) - - -def setup(bot): - check_folder() - check_files() - cog = Hunting(bot) - loop = asyncio.get_event_loop() - loop.create_task(cog._hunting_loop()) - bot.add_cog(cog) diff --git a/hunting/info.json b/hunting/info.json deleted file mode 100644 index 32d2ee7..0000000 --- a/hunting/info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AUTHOR" : "Paddolicious#8880", - "NAME" : "Hunting", - "SHORT" : "Hunting, it hunts birds... and things that fly", - "DESCRIPTION" : "Hunting, it hunts birds... and things that fly", - "TAGS": ["Hunting", "hunt", "birds", "paddo"] -} diff --git a/imgwelcome/data/fonts/UniSansHeavy.otf b/imgwelcome/data/fonts/UniSansHeavy.otf deleted file mode 100644 index facd333..0000000 Binary files a/imgwelcome/data/fonts/UniSansHeavy.otf and /dev/null differ diff --git a/imgwelcome/data/noimage.png b/imgwelcome/data/noimage.png deleted file mode 100644 index 77e494f..0000000 Binary files a/imgwelcome/data/noimage.png and /dev/null differ diff --git a/imgwelcome/data/transparent.png b/imgwelcome/data/transparent.png deleted file mode 100644 index bf2672c..0000000 Binary files a/imgwelcome/data/transparent.png and /dev/null differ diff --git a/imgwelcome/imgwelcome.py b/imgwelcome/imgwelcome.py deleted file mode 100644 index 52751cf..0000000 --- a/imgwelcome/imgwelcome.py +++ /dev/null @@ -1,615 +0,0 @@ -# _is_hex, _hex_to_rgb, _rgb_to_hex are from Stevy's leveler.py -# Also thanks to Stevy for nice, smooth circles. -# https://github.com/AznStevy/Maybe-Useful-Cogs -# imgwelcomeset_upload is based on code in orels' drawing.py -# https://github.com/orels1/ORELS-Cogs -# Parts of _create_welcome and on_member_join are from the Welcomer bot: -# https://discordbots.org/bot/330416853971107840 -# Font switcher, font outline, and bonus text announcement toggles -# thanks to Sitryk. -# Font listing from FlapJack + aikaterna's yet unpublished wordcloud cog. -import asyncio -import aiohttp -import datetime -import discord -import os -import re -import time -from __main__ import send_cmd_help -from cogs.utils.dataIO import dataIO -from cogs.utils import checks -from copy import deepcopy -from discord.ext import commands -from io import BytesIO -from PIL import Image, ImageFont, ImageOps, ImageDraw - - -default_settings = {"ANNOUNCE": False, - "BACKGROUND": "data/imgwelcome/transparent.png", - "BONUSES": {"ACCOUNT_WARNINGS": True, - "SPECIAL_USERS": True - }, - "BORDER": [255, 255, 255, 230], - "CHANNEL": None, - "OUTLINE": [0, 0, 0, 255], - "SERVERTEXT": [255, 255, 255, 230], - "TEXT": [255, 255, 255, 230], - "FONT": {"WELCOME_FONT": {"PATH": "data/imgwelcome/fonts/UniSansHeavy.otf", - "SIZE": 50}, - "SERVER_FONT": {"PATH": "data/imgwelcome/fonts/UniSansHeavy.otf", - "SIZE": 20}, - "NAME_FONT": {"PATH": "data/imgwelcome/fonts/UniSansHeavy.otf", - "SIZE": {"NORMAL": 30, - "MEDIUM": 22, - "SMALL": 18, - "SMALLEST": 12 - } - } - } - } - - -class ImgWelcome: - """Welcomes a user to the server with an image.""" - - def __init__(self, bot): - self.bot = bot - self.settings = dataIO.load_json('data/imgwelcome/settings.json') - self.version = "0.1.6" - - async def save_settings(self): - dataIO.save_json('data/imgwelcome/settings.json', self.settings) - - async def _create_welcome(self, member, url, test_member_number: int = None): - server = member.server - wfont = self.settings[server.id]["FONT"]["WELCOME_FONT"] - sfont = self.settings[server.id]["FONT"]["SERVER_FONT"] - nfont = self.settings[server.id]["FONT"]["NAME_FONT"] - welcome_font = ImageFont.truetype(wfont["PATH"], wfont["SIZE"]) - server_font = ImageFont.truetype(sfont["PATH"], sfont["SIZE"]) - - name_font = ImageFont.truetype(nfont["PATH"], nfont["SIZE"]["NORMAL"]) - name_font_medium = ImageFont.truetype(nfont["PATH"], nfont["SIZE"]["MEDIUM"]) - name_font_small = ImageFont.truetype(nfont["PATH"], nfont["SIZE"]["SMALL"]) - name_font_smallest = ImageFont.truetype(nfont["PATH"], nfont["SIZE"]["SMALLEST"]) - background = Image.open(self.settings[server.id]["BACKGROUND"]).convert('RGBA') - no_profile_picture = Image.open("data/imgwelcome/noimage.png") - - global welcome_picture - welcome_picture = Image.new("RGBA", (500, 150)) - welcome_picture = ImageOps.fit(background, (500, 150), centering=(0.5, 0.5)) - welcome_picture.paste(background) - welcome_picture = welcome_picture.resize((500, 150), Image.NEAREST) - - profile_area = Image.new("L", (512, 512), 0) - draw = ImageDraw.Draw(profile_area) - draw.ellipse(((0, 0), (512, 512)), fill=255) - circle_img_size = tuple(self.settings[member.server.id]["CIRCLE"]) - profile_area = profile_area.resize((circle_img_size), Image.ANTIALIAS) - try: - url = url.replace('webp?size=1024', 'png') - url = url.replace('gif?size=1024', 'png') - await self._get_profile(url) - profile_picture = Image.open('data/imgwelcome/profilepic.png') - except: - profile_picture = no_profile_picture - profile_area_output = ImageOps.fit(profile_picture, (circle_img_size), centering=(0, 0)) - profile_area_output.putalpha(profile_area) - - bordercolor = tuple(self.settings[member.server.id]["BORDER"]) - fontcolor = tuple(self.settings[member.server.id]["TEXT"]) - servercolor = tuple(self.settings[member.server.id]["SERVERTEXT"]) - textoutline = tuple(self.settings[server.id]["OUTLINE"]) - - mask = Image.new('L', (512, 512), 0) - draw_thumb = ImageDraw.Draw(mask) - draw_thumb.ellipse((0, 0) + (512, 512), fill=255, outline=0) - circle = Image.new("RGBA", (512, 512)) - draw_circle = ImageDraw.Draw(circle) - draw_circle.ellipse([0, 0, 512, 512], fill=(bordercolor[0], bordercolor[1], bordercolor[2], 180), outline=(255, 255, 255, 250)) - circle_border_size = await self._circle_border(circle_img_size) - circle = circle.resize((circle_border_size), Image.ANTIALIAS) - circle_mask = mask.resize((circle_border_size), Image.ANTIALIAS) - circle_pos = (7 + int((136 - circle_border_size[0]) / 2)) - border_pos = (11 + int((136 - circle_border_size[0]) / 2)) - drawtwo = ImageDraw.Draw(welcome_picture) - welcome_picture.paste(circle, (circle_pos, circle_pos), circle_mask) - welcome_picture.paste(profile_area_output, (border_pos, border_pos), profile_area_output) - - uname = (str(member.name) + "#" + str(member.discriminator)) - - def _outline(original_position: tuple, text: str, pixel_displacement: int, font, textoutline): - op = original_position - pd = pixel_displacement - - left = (op[0] - pd, op[1]) - right = (op[0] + pd, op[1]) - up = (op[0], op[1] - pd) - down = (op[0], op[1] + pd) - - drawtwo.text(left, text, font=font, fill=(textoutline)) - drawtwo.text(right, text, font=font, fill=(textoutline)) - drawtwo.text(up, text, font=font, fill=(textoutline)) - drawtwo.text(down, text, font=font, fill=(textoutline)) - - drawtwo.text(op, text, font=font, fill=(textoutline)) - - _outline((150, 16), "Welcome", 1, welcome_font, (textoutline)) - drawtwo.text((150, 16), "Welcome", font=welcome_font, fill=(fontcolor)) - - if len(uname) <= 17: - _outline((152, 63), uname, 1, name_font, (textoutline)) - drawtwo.text((152, 63), uname, font=name_font, fill=(fontcolor)) - - if len(uname) > 17: - if len(uname) <= 23: - _outline((152, 66), uname, 1, name_font_medium, (textoutline)) - drawtwo.text((152, 66), uname, font=name_font_medium, fill=(fontcolor)) - - if len(uname) >= 24: - if len(uname) <= 32: - _outline((152, 70), uname, 1, name_font_small, (textoutline)) - drawtwo.text((152, 70), uname, font=name_font_small, fill=(fontcolor)) - - if len(uname) >= 33: - drawtwo.text((152, 73), uname, 1, name_font_smallest, (textoutline)) - drawtwo.text((152, 73), uname, font=name_font_smallest, fill=(fontcolor)) - - if test_member_number is None: - members = sorted(server.members, - key=lambda m: m.joined_at).index(member) + 1 - else: - members = test_member_number - - member_number = str(members) + self._get_suffix(members) - sname = str(member.server.name) + '!' if len(str(member.server.name)) <= 28 else str(member.server.name)[:23] + '...' - - _outline((152, 96), "You are the " + str(member_number) + " member", 1, server_font, (textoutline)) - drawtwo.text((152, 96), "You are the " + str(member_number) + " member", font=server_font, fill=(servercolor)) - _outline((152, 116), 'of ' + sname, 1, server_font, (textoutline)) - drawtwo.text((152, 116), 'of ' + sname, font=server_font, fill=(servercolor)) - - image_object = BytesIO() - welcome_picture.save(image_object, format="PNG") - image_object.seek(0) - return image_object - - async def _circle_border(self, circle_img_size: tuple): - border_size = [] - for i in range(len(circle_img_size)): - border_size.append(circle_img_size[0] + 8) - return tuple(border_size) - - async def _data_check(self, ctx): - server = ctx.message.server - if server.id not in self.settings: - self.settings[server.id] = deepcopy(default_settings) - self.settings[server.id]["CHANNEL"] = ctx.message.channel.id - await self.save_settings() - - if "BONUSES" not in self.settings[server.id].keys(): - self.settings[server.id]["BONUSES"] = {"ACCOUNT_WARNINGS": True, - "SPECIAL_USERS": True - } - await self.save_settings() - - if "CIRCLE" not in self.settings[server.id].keys(): - self.settings[server.id]["CIRCLE"] = [128, 128] - await self.save_settings() - - if "CHANNEL" not in self.settings[server.id].keys(): - self.settings[server.id]["CHANNEL"] = ctx.message.channel.id - await self.save_settings() - - if "FONT" not in self.settings[server.id].keys(): - self.settings[server.id]["FONT"] = {"WELCOME_FONT": {"PATH": "data/imgwelcome/fonts/UniSansHeavy.otf", - "SIZE": 50}, - "SERVER_FONT": {"PATH": "data/imgwelcome/fonts/UniSansHeavy.otf", - "SIZE": 20}, - "NAME_FONT": {"PATH": "data/imgwelcome/fonts/UniSansHeavy.otf", - "SIZE": {"NORMAL": 30, - "MEDIUM": 22, - "SMALL": 18, - "SMALLEST": 12 - } - } - } - - if "OUTLINE" not in self.settings[server.id].keys(): - self.settings[server.id]["OUTLINE"] = [0, 0, 0, 255] - await self.save_settings() - - async def _get_profile(self, url): - async with aiohttp.get(url) as r: - image = await r.content.read() - with open('data/imgwelcome/profilepic.png', 'wb') as f: - f.write(image) - - def _get_suffix(self, num): - suffixes = {1: 'st', 2: 'nd', 3: 'rd'} - if 10 <= num % 100 <= 20: - suffix = 'th' - else: - suffix = suffixes.get(num % 10, 'th') - return suffix - - def _hex_to_rgb(self, hex_num: str, a: int): - h = hex_num.lstrip('#') - - # if only 3 characters are given - if len(str(h)) == 3: - expand = ''.join([x*2 for x in str(h)]) - h = expand - - colors = [int(h[i:i+2], 16) for i in (0, 2, 4)] - colors.append(a) - return tuple(colors) - - def _is_hex(self, color: str): - if color is not None and len(color) != 4 and len(color) != 7: - return False - - reg_ex = r'^#(?:[0-9a-fA-F]{3}){1,2}$' - return re.search(reg_ex, str(color)) - - def _rgb_to_hex(self, rgb): - rgb = tuple(rgb[:3]) - return '#%02x%02x%02x' % rgb - - @checks.admin_or_permissions(manage_server=True) - @commands.group(pass_context=True) - async def imgwelcome(self, ctx): - """Configuration options for the welcome image.""" - if ctx.invoked_subcommand is None: - await send_cmd_help(ctx) - return - - @imgwelcome.command(pass_context=True, name="border", no_pm=True) - async def imgwelcome_border(self, ctx, bordercolor=None): - """Set the profile image border color. - Use hex codes for colors and ‘clear’ for transparent.""" - server = ctx.message.server - await self._data_check(ctx) - default_a = 230 - valid = True - - if bordercolor == "clear": - self.settings[server.id]["BORDER"] = [0, 0, 0, 0] - elif self._is_hex(bordercolor): - self.settings[server.id]["BORDER"] = self._hex_to_rgb(bordercolor, default_a) - else: - await self.bot.say('Border color is invalid. Use #000000 as a format.') - valid = False - - if valid: - await self.bot.say('The profile border color has been set.') - await self.save_settings() - - @imgwelcome.command(pass_context=True, name="channel", no_pm=True) - async def imgwelcome_channel(self, ctx, channel: discord.Channel): - """Set the announcement channel.""" - server = ctx.message.server - if not server.me.permissions_in(channel).send_messages: - await self.bot.say("No permissions to speak in that channel.") - return - await self._data_check(ctx) - self.settings[server.id]["CHANNEL"] = channel.id - await self.save_settings() - await self.bot.send_message(channel, "This channel will be used for welcome messages.") - - @imgwelcome.command(name='clear', pass_context=True, no_pm=True) - async def imgwelcome_clear(self, ctx): - """Set the background to transparent.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]['BACKGROUND'] = 'data/imgwelcome/transparent.png' - await self.save_settings() - await self.bot.say('Welcome image background is now transparent.') - - @imgwelcome.command(pass_context=True, name="outline", no_pm=True) - async def imgwelcome_outline(self, ctx, outline=None): - """Set the text outline. White or black.""" - server = ctx.message.server - await self._data_check(ctx) - valid = True - if outline == "white": - self.settings[server.id]["OUTLINE"] = [255, 255, 255, 255] - await self.save_settings() - elif outline == "black": - self.settings[server.id]["OUTLINE"] = [0, 0, 0, 255] - await self.save_settings() - else: - await self.bot.say('Outline color is invalid. Use white or black.') - valid = False - - if valid: - await self.bot.say('The text outline has been set.') - - @imgwelcome.command(name="preview", pass_context=True, no_pm=True) - async def imagewelcome_preview(self, ctx, member: discord.Member=None, number: int=None): - """Show a welcome image with the current settings.""" - server = ctx.message.server - channel = ctx.message.channel - if member is None: - member = ctx.message.author - await self._data_check(ctx) - channel_object = self.bot.get_channel(channel.id) - await self.bot.send_typing(channel_object) - image_object = await self._create_welcome(member, member.avatar_url, number) - await self.bot.send_file(channel_object, image_object, filename="welcome.png") - - @imgwelcome.command(pass_context=True, name="size", no_pm=True) - async def imgwelcome_profilesize(self, ctx, profilesize: int): - """Set the profile size in pixels. Use one number, 128 is recommended.""" - server = ctx.message.server - await self._data_check(ctx) - if profilesize is 0: - await self.bot.say("Profile picture size must be larger than 0.") - return - else: - self.settings[server.id]["CIRCLE"] = [profilesize, profilesize] - await self.save_settings() - await self.bot.say('The profile picture size has been set.') - - @imgwelcome.command(pass_context=True, name="text", no_pm=True) - async def imgwelcome_text(self, ctx, textcolor: str, servercolor: str): - """Set text colors. Use hex code for colors.""" - server = ctx.message.server - await self._data_check(ctx) - default_a = 230 - valid = True - - if self._is_hex(textcolor): - self.settings[server.id]["TEXT"] = self._hex_to_rgb(textcolor, default_a) - else: - await self.bot.say('Welcome text color is invalid. Use #000000 as a format.') - valid = False - - if self._is_hex(servercolor): - self.settings[server.id]["SERVERTEXT"] = self._hex_to_rgb(servercolor, default_a) - else: - await self.bot.say('Server text color is invalid. Use #000000 as a format.') - valid = False - - if valid: - await self.bot.say('The text colors have been set.') - await self.save_settings() - - @imgwelcome.command(pass_context=True, name="toggle", no_pm=True) - async def imgwelcome_toggle(self, ctx): - """Toggle welcome messages on the server.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]["ANNOUNCE"] = not self.settings[server.id]["ANNOUNCE"] - if self.settings[server.id]["ANNOUNCE"]: - await self.bot.say("Now welcoming new users.") - else: - await self.bot.say("No longer welcoming new users.") - await self.save_settings() - - @imgwelcome.command(name='upload', pass_context=True, no_pm=True) - async def imgwelcome_upload(self, ctx, default=None): - """Upload a background through Discord. 500px x 150px. - This must be an image file and not a url.""" - server = ctx.message.server - await self._data_check(ctx) - await self.bot.say("Please send the file to use as a background. File must be 500px x 150px.") - answer = await self.bot.wait_for_message(timeout=30, author=ctx.message.author) - - try: - bg_url = answer.attachments[0]["url"] - success = True - except Exception as e: - success = False - print(e) - - serverimage = Image - - if success: - try: - async with aiohttp.get(bg_url) as r: - image = await r.content.read() - if not os.path.exists('data/imgwelcome/{}'.format(server.id)): - os.makedirs('data/imgwelcome/{}'.format(server.id)) - serverbg = 'data/imgwelcome/{}/serverbg.png'.format(server.id) - with open(serverbg, 'wb') as f: - f.write(image) - serverimage = Image.open(serverbg).convert('RGBA') - success = True - - except Exception as e: - success = False - print(e) - if success: - if serverimage.size == (500, 150): - self.settings[server.id]['BACKGROUND'] = "data/imgwelcome/" + ctx.message.server.id + "/serverbg.png" - await self.save_settings() - else: - await self.bot.say("Image needs to be 500x150.") - return - background_img = ('data/imgwelcome/{}/serverbg.png'.format(server.id)) - self.settings[server.id]['BACKGROUND'] = (background_img) - await self.save_settings() - await self.bot.say('Welcome image for this server set to uploaded file.') - else: - await self.bot.say("Couldn't get the image from Discord.") - else: - await self.bot.say("Couldn't get the image.") - - @imgwelcome.group(pass_context=True, name='bonus', no_pm=True) - async def imgwelcome_bonus(self, ctx): - """Toggle display of additional text welcome messages when a user joins the server.""" - if ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group): - await send_cmd_help(ctx) - return - - @imgwelcome_bonus.command(pass_context=True, name='user', no_pm=True) - async def bonus_user(self, ctx): - """Toggle text announcement when a user is x 100th to join or #1337.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]["BONUSES"]["SPECIAL_USERS"] = not self.settings[server.id]["BONUSES"]["SPECIAL_USERS"] - await self.save_settings() - if self.settings[server.id]["BONUSES"]["SPECIAL_USERS"]: - msg = "I will now announce when special users join." - else: - msg = "I will no longer announce when special users join." - await self.bot.say(msg) - - @imgwelcome_bonus.command(pass_context=True, name='warn', no_pm=True) - async def bonus_warn(self, ctx): - """Toggle text announcement when a new user's account is <7d old.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]["BONUSES"]["ACCOUNT_WARNINGS"] = not self.settings[server.id]["BONUSES"]["ACCOUNT_WARNINGS"] - await self.save_settings() - if self.settings[server.id]["BONUSES"]["ACCOUNT_WARNINGS"]: - msg = "I will now announce when new accounts join." - else: - msg = "I will no longer announce when new accounts join." - await self.bot.say(msg) - - @imgwelcome.group(pass_context=True, name='font', no_pm=True) - async def imgwelcome_font(self, ctx): - """Place your font files in the data/imgwelcome/fonts/ directory. - Valid font areas to change are: welcome, server and name. - """ - if ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group): - await send_cmd_help(ctx) - return - - @imgwelcome_font.command(pass_context=True, name='list', no_pm=True) - async def fontg_list(self, ctx): - """List fonts in the directory.""" - channel = ctx.message.channel - directory = "data/imgwelcome/fonts/" - fonts = sorted(os.listdir(directory)) - - if len(fonts) == 0: - await self.bot.send_message(channel, "No fonts found. Place " - "fonts in /data/imgwelcome/fonts/.") - return - - pager = commands.formatter.Paginator(prefix='```', suffix='```', max_size=2000) - pager.add_line('Current fonts:') - for font_name in fonts: - pager.add_line(font_name) - for page in pager.pages: - await self.bot.send_message(channel, page) - - @imgwelcome_font.command(pass_context=True, name='name', no_pm=True) - async def fontg_name(self, ctx, font_name: str, size: int=None): - """Change the name text font. - e.g. [p]imgwelcome font name "UniSansHeavy.otf" - """ - await self._data_check(ctx) - server = ctx.message.server - - directory = "data/imgwelcome/fonts/" - if size is None: - size = self.settings[server.id]["FONT"]["NAME_FONT"]["SIZE"]["NORMAL"] - - try: - ImageFont.truetype(directory + font_name, size) - except: - await self.bot.say("I could not find that font file.") - return - - self.settings[server.id]["FONT"]["NAME_FONT"]["PATH"] = directory + font_name - self.settings[server.id]["FONT"]["NAME_FONT"]["SIZE"]["NORMAL"] = size - self.settings[server.id]["FONT"]["NAME_FONT"]["SIZE"]["MEDIUM"] = size - 8 - self.settings[server.id]["FONT"]["NAME_FONT"]["SIZE"]["SMALL"] = size - 12 - self.settings[server.id]["FONT"]["NAME_FONT"]["SIZE"]["SMALLEST"] = size - 18 - await self.save_settings() - await self.bot.say("Name font changed to: {}".format(font_name[:-4])) - - @imgwelcome_font.command(pass_context=True, name='server', no_pm=True) - async def fontg_server(self, ctx, font_name: str, size: int=None): - """Change the server text font.""" - await self._data_check(ctx) - server = ctx.message.server - - directory = "data/imgwelcome/fonts/" - if size is None: - size = self.settings[server.id]["FONT"]["SERVER_FONT"]["SIZE"] - - try: - ImageFont.truetype(directory + font_name, size) - except: - await self.bot.say("I could not find that font file.") - return - - self.settings[server.id]["FONT"]["SERVER_FONT"]["PATH"] = directory + font_name - self.settings[server.id]["FONT"]["SERVER_FONT"]["SIZE"] = size - await self.save_settings() - await self.bot.say("Server text font changed to: {}".format(font_name[:-4])) - pass - - @imgwelcome_font.command(pass_context=True, name='welcome', no_pm=True) - async def fontg_welcome(self, ctx, font_name: str, size: int=None): - """Change the welcome text font.""" - # try open file_name, if fail tell user - # if opens change settings, tell user success - # if file_name doesn't exist, list available fonts - await self._data_check(ctx) - server = ctx.message.server - - directory = "data/imgwelcome/fonts/" - if size is None: - size = self.settings[server.id]["FONT"]["WELCOME_FONT"]["SIZE"] - - try: - ImageFont.truetype(directory + font_name, size) - except: - await self.bot.say("I could not find that font file.") - return - - self.settings[server.id]["FONT"]["WELCOME_FONT"]["PATH"] = directory + font_name - self.settings[server.id]["FONT"]["WELCOME_FONT"]["SIZE"] = size - await self.save_settings() - await self.bot.say("Welcome font changed to: {}".format(font_name[:-4])) - pass - - @imgwelcome.command(name="version", pass_context=True, hidden=True) - async def imagewelcomeset_version(self): - """Displays the imgwelcome version.""" - await self.bot.say("imgwelcome version {}.".format(self.version)) - - async def on_member_join(self, member): - server = member.server - if server.id not in self.settings: - self.settings[server.id] = deepcopy(default_settings) - await self.save_settings() - if not self.settings[server.id]["ANNOUNCE"]: - return - channelid = self.settings[server.id]["CHANNEL"] - channel_object = self.bot.get_channel(channelid) - await self.bot.send_typing(channel_object) - image_object = await self._create_welcome(member, member.avatar_url) - await self.bot.send_file(channel_object, image_object, filename="welcome.png") - if (len(member.server.members) % 100) == 0 or (len(member.server.members) == 1337) and self.settings[server.id]["SPECIAL_USERS"]: - msg = "\N{PARTY POPPER} Thanks <@" + member.id + ">, you're the ***" + str(len(member.server.members)) + "*** th user on this server! \N{PARTY POPPER}" - await self.bot.send_message(channel_object, msg) - date_join = datetime.datetime.strptime(str(member.created_at), "%Y-%m-%d %H:%M:%S.%f") - date_now = datetime.datetime.now(datetime.timezone.utc) - date_now = date_now.replace(tzinfo=None) - since_join = date_now - date_join - if since_join.days < 7 and self.settings[server.id]["ACCOUNT_WARNINGS"]: - await self.bot.send_message(channel_object, "\N{WARNING SIGN} This account was created less than a week ago (" + str(since_join.days) + " days ago)") - - -def check_folders(): - if not os.path.exists('data/imgwelcome/'): - os.mkdir('data/imgwelcome/') - - -def check_files(): - if not dataIO.is_valid_json('data/imgwelcome/settings.json'): - defaults = {} - dataIO.save_json('data/imgwelcome/settings.json', defaults) - - -def setup(bot): - check_folders() - check_files() - bot.add_cog(ImgWelcome(bot)) diff --git a/imgwelcome/info.json b/imgwelcome/info.json deleted file mode 100644 index c608df4..0000000 --- a/imgwelcome/info.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "Use [p]imgwelcome to change settings.", - "NAME" : "imgwelcome", - "SHORT" : "Welcome users to your server with an image.", - "DESCRIPTION" : "Welcome users to your server with an image.", - "REQUIREMENTS": ["Pillow"], - "TAGS": ["welcome", "welcome image", "images"] -} diff --git a/otherbot/otherbot.py b/otherbot/otherbot.py deleted file mode 100644 index b5225eb..0000000 --- a/otherbot/otherbot.py +++ /dev/null @@ -1,17 +0,0 @@ -import discord - -class OtherbotStatus: - def __init__(self, bot): - self.bot = bot - - async def on_member_update(self, before, after): - if after.status == discord.Status.offline and after.id == "000000000000000000": # this is the bot id that you want to watch - channel_object = self.bot.get_channel("000000000000000000") # this is the channel id for the watcher bot to scream in - await self.bot.send_message(channel_object, "<@000000000000000000>, the bot is offline.") # this is the person to ping and the message - else: - pass - - -def setup(bot): - n = OtherbotStatus(bot) - bot.add_cog(n) diff --git a/picwelcome/data/default.png b/picwelcome/data/default.png deleted file mode 100644 index cedc576..0000000 Binary files a/picwelcome/data/default.png and /dev/null differ diff --git a/picwelcome/info.json b/picwelcome/info.json deleted file mode 100644 index 05647f5..0000000 --- a/picwelcome/info.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "Use [p]picwelcome to change settings.", - "NAME" : "picwelcome", - "SHORT" : "Welcome users to your server with a picture.", - "DESCRIPTION" : "Welcome users to your server with a picture.", - "REQUIREMENTS": ["Pillow"], - "TAGS": ["welcome", "welcome image", "images"] -} diff --git a/picwelcome/picwelcome.py b/picwelcome/picwelcome.py deleted file mode 100644 index c2de3d3..0000000 --- a/picwelcome/picwelcome.py +++ /dev/null @@ -1,170 +0,0 @@ -# picwelcomeset_upload is based on code in orels' drawing.py -# https://github.com/orels1/ORELS-Cogs - -import asyncio -import aiohttp -import datetime -import discord -import os -from __main__ import send_cmd_help -from cogs.utils.dataIO import dataIO -from cogs.utils import checks -from copy import deepcopy -from discord.ext import commands -from PIL import Image - - -default_settings = {"ANNOUNCE": False, - "PICTURE": "data/picwelcome/default.png", - "CHANNEL": None, - } - - -class PicWelcome: - """Welcome new users with a static image.""" - - def __init__(self, bot): - self.bot = bot - self.settings = dataIO.load_json('data/picwelcome/settings.json') - self.version = "0.0.1" - - async def save_settings(self): - dataIO.save_json('data/picwelcome/settings.json', self.settings) - - async def _data_check(self, ctx): - server = ctx.message.server - if server.id not in self.settings: - self.settings[server.id] = deepcopy(default_settings) - self.settings[server.id]["CHANNEL"] = ctx.message.channel.id - await self.save_settings() - - @checks.admin_or_permissions(manage_server=True) - @commands.group(pass_context=True) - async def picwelcome(self, ctx): - """Configuration options for a welcome picture.""" - if ctx.invoked_subcommand is None: - await send_cmd_help(ctx) - return - - @picwelcome.command(pass_context=True, name="channel", no_pm=True) - async def picwelcome_channel(self, ctx, channel: discord.Channel): - """Set the announcement channel.""" - server = ctx.message.server - if not server.me.permissions_in(channel).send_messages: - await self.bot.say("No permissions to speak in that channel.") - return - await self._data_check(ctx) - self.settings[server.id]["CHANNEL"] = channel.id - await self.save_settings() - await self.bot.send_message(channel, "This channel will be used for welcome pictures.") - - @picwelcome.command(name='reset', pass_context=True, no_pm=True) - async def picwelcome_reset(self, ctx): - """Set the welcome picture back to the default.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]['PICTURE'] = 'data/picwelcome/default.png' - await self.save_settings() - await self.bot.say('Welcome picture reset to default.') - - @picwelcome.command(name="preview", pass_context=True, no_pm=True) - async def picwelcome_preview(self, ctx, member: discord.Member=None, number: int=None): - """Show a the welcome picture with the current settings.""" - server = ctx.message.server - channel = ctx.message.channel - if member is None: - member = ctx.message.author - await self._data_check(ctx) - channel_object = self.bot.get_channel(channel.id) - await self.bot.send_typing(channel_object) - serverpicture = self.settings[server.id]["PICTURE"] - await self.bot.send_file(channel_object, serverpicture) - - @picwelcome.command(pass_context=True, name="toggle", no_pm=True) - async def picwelcome_toggle(self, ctx): - """Toggle welcome pictures on the server.""" - server = ctx.message.server - await self._data_check(ctx) - self.settings[server.id]["ANNOUNCE"] = not self.settings[server.id]["ANNOUNCE"] - if self.settings[server.id]["ANNOUNCE"]: - await self.bot.say("Now welcoming new users with a picture.") - else: - await self.bot.say("No longer welcoming new users with a picture.") - await self.save_settings() - - @picwelcome.command(name='upload', pass_context=True, no_pm=True) - async def picwelcome_upload(self, ctx, default=None): - """Upload a picture through Discord. - This must be an image file and not a url.""" - server = ctx.message.server - await self._data_check(ctx) - await self.bot.say("Please send the file to use as a welcome picture.") - answer = await self.bot.wait_for_message(timeout=30, author=ctx.message.author) - - try: - bg_url = answer.attachments[0]["url"] - success = True - except Exception as e: - success = False - print(e) - - serverimage = Image - - if success: - try: - async with aiohttp.get(bg_url) as r: - image = await r.content.read() - if not os.path.exists('data/picwelcome/{}'.format(server.id)): - os.makedirs('data/picwelcome/{}'.format(server.id)) - serverbg = 'data/picwelcome/{}/serverpic.png'.format(server.id) - with open(serverbg, 'wb') as f: - f.write(image) - serverimage = Image.open(serverbg).convert('RGBA') - success = True - - except Exception as e: - success = False - print(e) - if success: - self.settings[server.id]['PICTURE'] = "data/picwelcome/" + ctx.message.server.id + "/serverpic.png" - await self.save_settings() - await self.bot.say('Welcome image for this server set to uploaded file.') - else: - await self.bot.say("Couldn't get the image from Discord.") - else: - await self.bot.say("Couldn't get the image.") - - @picwelcome.command(name="version", pass_context=True, hidden=True) - async def picwelcome_version(self): - """Displays the picwelcome version.""" - await self.bot.say("picwelcome version {}.".format(self.version)) - - async def on_member_join(self, member): - server = member.server - if server.id not in self.settings: - self.settings[server.id] = deepcopy(default_settings) - await self.save_settings() - if not self.settings[server.id]["ANNOUNCE"]: - return - channelid = self.settings[server.id]["CHANNEL"] - channel_object = self.bot.get_channel(channelid) - await self.bot.send_typing(channel_object) - serverpicture = self.settings[server.id]["PICTURE"] - await self.bot.send_file(channel_object, serverpicture) - - -def check_folders(): - if not os.path.exists('data/picwelcome/'): - os.mkdir('data/picwelcome/') - - -def check_files(): - if not dataIO.is_valid_json('data/picwelcome/settings.json'): - defaults = {} - dataIO.save_json('data/picwelcome/settings.json', defaults) - - -def setup(bot): - check_folders() - check_files() - bot.add_cog(PicWelcome(bot)) diff --git a/pug/info.json b/pug/info.json deleted file mode 100644 index 562239b..0000000 --- a/pug/info.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "Thanks for installing Pug. Now you can check out your Warcraft pugs for raid-readiness through Discord.", - "NAME" : "Pug", - "SHORT" : "Warcraft character lookup.", - "DESCRIPTION" : "This tool lets you check potential group pugs for enchants, gems, and raid completion. It was made possible by PugBot: https://github.com/reznok/PugBot", - "TAGS" : ["warcraft", "raiding", "raid"] -} diff --git a/pug/pug.py b/pug/pug.py deleted file mode 100644 index 44b609c..0000000 --- a/pug/pug.py +++ /dev/null @@ -1,313 +0,0 @@ -import json -import os -import requests -from .utils.dataIO import dataIO -from __main__ import send_cmd_help -from discord.ext import commands -from .utils import checks - - -class Pug: - - def __init__(self, bot): - self.bot = bot - self.settings = dataIO.load_json("data/pug/config.json") - self.fp = "data/pug/config.json" - API_KEY = self.settings["blizzard_api_key"] - default_region = self.settings["default_region"] - - LEG_WITH_SOCKET = [ - 132369, 132410, 137044, 132444, 132449, 132452, 132460, 133973, 133974, 137037, 137038, 137039, 137040, 137041, - 137042, 137043, 132378, 137045, 137046, 137047, 137048, 137049, 137050, 137051, 137052, 137054, 137055, 137220, - 137223, 137276, 137382, 138854 - ] - - ENCHANTABLE_SLOTS = ["neck", "back", "finger1", "finger2"] - - region_locale = { - 'us': ['us', 'en_US', 'en'], - 'eu': ['eu', 'en_GB', 'en'] - # 'kr': ['kr', 'ko_KR', 'ko'], - # 'tw': ['tw', 'zh_TW', 'zh'], - # 'es': ['es', 'es_MX', 'es'], es lookup is broken until the armory site is migrated to the new format - } - - def get_sockets(self, player_dictionary): - """ - Return dict with total sockets and count of equipped gems and slots that are missing - - :param player_dictionary: Retrieved player dict from API - :return: dict() - """ - sockets = 0 - equipped_gems = 0 - - for item in player_dictionary["items"]: - if item in "averageItemLevel" or item in "averageItemLevelEquipped": - continue - - if int(player_dictionary["items"][item]["id"]) in self.LEG_WITH_SOCKET: - sockets += 1 - - for bonus in player_dictionary["items"][item]["bonusLists"]: - if bonus == 1808: # 1808 is Legion prismatic socket bonus - sockets += 1 - - if item in ["neck", "finger1", "finger2"]: - if player_dictionary["items"][item]["context"] == "trade-skill": - sockets += 1 - - for ttip in player_dictionary["items"][item]["tooltipParams"]: - if item in "mainHand" or item in "offHand": # Ignore Relic - continue - if "gem" in ttip: # Equipped gems are listed as gem0, gem1, etc... - equipped_gems += 1 - - return {"total_sockets": sockets, - "equipped_gems": equipped_gems} - - def get_enchants(self, player_dictionary): - """ - Get count of enchants missing and slots that are missing - :param player_dictionary: - :return: dict() - """ - self.missing_enchant_slots = [] - for slot in self.ENCHANTABLE_SLOTS: - if "enchant" not in player_dictionary["items"][slot]["tooltipParams"]: - self.missing_enchant_slots.append(slot) - - return { - "enchantable_slots": len(self.ENCHANTABLE_SLOTS), - "missing_slots": self.missing_enchant_slots, - "total_missing": len(self.missing_enchant_slots) - } - - def get_raid_progression(self, player_dictionary, raid): - r = [x for x in player_dictionary["progression"] - ["raids"] if x["name"] in raid][0] - normal = 0 - heroic = 0 - mythic = 0 - - for boss in r["bosses"]: - if boss["normalKills"] > 0: - normal += 1 - if boss["heroicKills"] > 0: - heroic += 1 - if boss["mythicKills"] > 0: - mythic += 1 - - return {"normal": normal, - "heroic": heroic, - "mythic": mythic, - "total_bosses": len(r["bosses"])} - - def get_mythic_progression(self, player_dictionary): - achievements = player_dictionary["achievements"] - plus_two = 0 - plus_five = 0 - plus_ten = 0 - - if 33096 in achievements["criteria"]: - index = achievements["criteria"].index(33096) - plus_two = achievements["criteriaQuantity"][index] - - if 33097 in achievements["criteria"]: - index = achievements["criteria"].index(33097) - plus_five = achievements["criteriaQuantity"][index] - - if 33098 in achievements["criteria"]: - index = achievements["criteria"].index(33098) - plus_ten = achievements["criteriaQuantity"][index] - - return { - "plus_two": plus_two, - "plus_five": plus_five, - "plus_ten": plus_ten - } - - def get_char(self, name, server, target_region): - self.settings = dataIO.load_json("data/pug/config.json") # Load Configs - API_KEY = self.settings["blizzard_api_key"] - r = requests.get("https://%s.api.battle.net/wow/character/%s/%s?fields=items+progression+achievements&locale=%s&apikey=%s" % ( - self.region_locale[target_region][0], server, name, self.region_locale[target_region][1], API_KEY)) - - if r.status_code != 200: - raise Exception("Could not find character (No 200 from API).") - - player_dict = json.loads(r.text) - - r = requests.get( - "https://%s.api.battle.net/wow/data/character/classes?locale=%s&apikey=%s" % ( - self.region_locale[target_region][0], self.region_locale[target_region][1], API_KEY)) - if r.status_code != 200: - raise Exception("Could Not Find Character Classes (No 200 From API)") - class_dict = json.loads(r.text) - class_dict = {c['id']: c['name'] for c in class_dict["classes"]} - - r = requests.get("https://%s.api.battle.net/wow/character/%s/%s?fields=stats&locale=%s&apikey=%s" % ( - self.region_locale[target_region][0], server, name, self.region_locale[target_region][1], API_KEY)) - if r.status_code != 200: - raise Exception("Could not find character stats (No 200 From API).") - stats_dict = json.loads(r.text) - - health = stats_dict["stats"]["health"] - power = stats_dict["stats"]["power"] - powertype = stats_dict["stats"]["powerType"] - powertypeproper = powertype.title() - strength = stats_dict["stats"]["str"] - agi = stats_dict["stats"]["agi"] - int = stats_dict["stats"]["int"] - sta = stats_dict["stats"]["sta"] - crit = stats_dict["stats"]["critRating"] - critrating = stats_dict["stats"]["crit"] - haste = stats_dict["stats"]["hasteRating"] - hasterating = stats_dict["stats"]["haste"] - mastery = stats_dict["stats"]["masteryRating"] - masteryrating = stats_dict["stats"]["mastery"] - vers = stats_dict["stats"]["versatility"] - versrating = stats_dict["stats"]["versatilityDamageDoneBonus"] - equipped_ivl = player_dict["items"]["averageItemLevelEquipped"] - sockets = self.get_sockets(player_dict) - enchants = self.get_enchants(player_dict) - tov_progress = self.get_raid_progression(player_dict, "Trial of Valor") - en_progress = self.get_raid_progression(player_dict, "The Emerald Nightmare") - nh_progress = self.get_raid_progression(player_dict, "The Nighthold") - tos_progress = self.get_raid_progression(player_dict, "Tomb of Sargeras") - ant_progress = self.get_raid_progression(player_dict, "Antorus, the Burning Throne") - mythic_progress = self.get_mythic_progression(player_dict) - - armory_url = 'http://{}.battle.net/wow/{}/character/{}/{}/advanced'.format( - self.region_locale[target_region][0], self.region_locale[target_region][2], server, name) - - return_string = '' - return_string += "**%s** - **%s** - **%s %s**\n" % ( - name.title(), server.title(), player_dict['level'], class_dict[player_dict['class']]) - return_string += '<{}>\n'.format(armory_url) - return_string += '```ini\n' # start Markdown - - # iLvL - return_string += "[Equipped Item Level]: %s\n" % equipped_ivl - - # Mythic Progression - return_string += "[Mythics]: +2: %s, +5: %s, +10: %s\n" % (mythic_progress["plus_two"], - mythic_progress["plus_five"], - mythic_progress["plus_ten"]) - - # Raid Progression - return_string += "[EN]: {1}/{0} (N), {2}/{0} (H), {3}/{0} (M)\n".format(en_progress["total_bosses"], - en_progress["normal"], - en_progress["heroic"], - en_progress["mythic"]) - return_string += "[TOV]: {1}/{0} (N), {2}/{0} (H), {3}/{0} (M)\n".format(tov_progress["total_bosses"], - tov_progress["normal"], - tov_progress["heroic"], - tov_progress["mythic"]) - return_string += "[NH]: {1}/{0} (N), {2}/{0} (H), {3}/{0} (M)\n".format(nh_progress["total_bosses"], - nh_progress["normal"], - nh_progress["heroic"], - nh_progress["mythic"]) - return_string += "[TOS]: {1}/{0} (N), {2}/{0} (H), {3}/{0} (M)\n".format(tos_progress["total_bosses"], - tos_progress["normal"], - tos_progress["heroic"], - tos_progress["mythic"]) - return_string += "[ANT]: {1}/{0} (N), {2}/{0} (H), {3}/{0} (M)\n".format(ant_progress["total_bosses"], - ant_progress["normal"], - ant_progress["heroic"], - ant_progress["mythic"]) - # Gems - return_string += "[Gems Equipped]: %s/%s\n" % ( - sockets["equipped_gems"], sockets["total_sockets"]) - - # Enchants - return_string += "[Enchants]: %s/%s\n" % (enchants["enchantable_slots"] - enchants["total_missing"], - enchants["enchantable_slots"]) - if enchants["total_missing"] > 0: - return_string += "[Missing Enchants]: {0}".format( - ", ".join(enchants["missing_slots"])) - - # Stats - return_string += "\n" - return_string += "[Health]: {} [{}]: {}\n".format(health, powertypeproper, power) - return_string += "[Str]: {} [Agi]: {}\n".format(strength, agi, int, sta) - return_string += "[Int]: {} [Sta]: {}\n".format(int, sta) - return_string += "[Crit]: {}, {}% [Haste]: {}, {}%\n".format(crit, critrating, haste, hasterating,) - return_string += "[Mastery]: {}, {}% [Vers]: {}, {}% bonus damage\n".format(mastery, masteryrating, vers, versrating) - - return_string += '```' # end Markdown - return return_string - - @commands.command(name="pug", pass_context=True, no_pm=True) - async def _pug(self, ctx, *, message): - """A Warcraft Armory character lookup tool. - Use: !pug - Hyphenate two-word servers (Ex: Twisting-Nether).""" - self.settings = dataIO.load_json("data/pug/config.json") # Load Configs - default_region = self.settings["default_region"] - target_region = default_region - channel = ctx.message.channel - try: - i = str(ctx.message.content).split(' ') - name = i[1] - server = i[2] - if len(i) == 4 and i[3].lower() in self.region_locale.keys(): - target_region = i[3].lower() - character_info = self.get_char(name, server, target_region) - await self.bot.send_message(ctx.message.channel, character_info) - except Exception as e: - print(e) - await self.bot.send_message(ctx.message.channel, "Error with character name or server.") - - @commands.command(pass_context=True, name='pugtoken') - @checks.is_owner() - async def _pugtoken(self, context, key: str): - """Sets the token for the Blizzard API. - You can use this command in a private message to the bot. - - Get an API token at: https://dev.battle.net/member/register""" - settings = dataIO.load_json(self.fp) - settings['blizzard_api_key'] = key - dataIO.save_json(self.fp, settings) - await self.bot.say("API key set.") - - @commands.command(pass_context=True, name='pugregion') - @checks.is_owner() - async def _pugregion(self, context, key: str): - """Sets the default region.""" - settings = dataIO.load_json(self.fp) - settings['default_region'] = key - dataIO.save_json(self.fp, settings) - await self.bot.say("Default region set.") - - @commands.command() - async def pugcredits(self): - """Code credits.""" - message = await self._credit() - await self.bot.say(message) - - async def _credit(self): - message = "```This cog is made possible by Pugbot.\n" - message+= "Please visit https://github.com/reznok/PugBot for more information.\n" - message+= "```" - return message - - -def check_folders(): - if not os.path.exists("data/pug"): - print("Creating data/pug folder...") - os.mkdir("data/pug") - - -def check_files(): - fp = "data/pug/config.json" - if not dataIO.is_valid_json(fp): - print("Creating config.json...") - dataIO.save_json(fp, {"blizzard_api_key": "", "default_region": "us"}) - - -def setup(bot): - check_folders() - check_files() - n = Pug(bot) - bot.add_cog(n) diff --git a/radio/info.json b/radio/info.json deleted file mode 100644 index 9acd0a0..0000000 --- a/radio/info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AUTHOR" : "Paddo, with edits by aikaterna", - "NAME" : "radio", - "SHORT" : "Plays http streams like icecast or mp3 streams.", - "DESCRIPTION" : "This cog can only be used with audio unloaded. To play m3u or pls files, extract the http stream url they contain and use that - this cog can remember urls for later playback.", - "TAGS": ["radio", "stream", "icecast"] -} diff --git a/radio/radio.py b/radio/radio.py deleted file mode 100644 index 64fd054..0000000 --- a/radio/radio.py +++ /dev/null @@ -1,161 +0,0 @@ -import os -import discord -import asyncio -from discord.ext import commands -from __main__ import send_cmd_help -from cogs.utils.dataIO import dataIO - - -class Radio: - def __init__(self, bot): - self.bot = bot - self.players = {} - self.memory_path = 'data/radio/memory.json' - self.memory = dataIO.load_json(self.memory_path) - - @commands.group(pass_context=True, no_pm=True, name='radio') - async def _radio(self, ctx): - """Streaming audio commands.""" - audio_cog = self.bot.get_cog('Audio') - if audio_cog: - return await self.bot.say("Please unload the audio cog before using this cog.") - if ctx.invoked_subcommand is None: - await send_cmd_help(ctx) - - @_radio.command(pass_context=True, no_pm=True, name='stop') - async def _leave(self, ctx): - """Stops playback.""" - server = ctx.message.server - voice_client = await self.voice_client(server) - await self.stop_playing(server) - if voice_client: - await voice_client.disconnect() - - @_radio.command(no_pm=True, pass_context=True, name='play') - async def _play(self, ctx, url: str): - """Play a http stream.""" - server = ctx.message.server - if server.id in self.players: - await self.stop_playing(server) - await self.play_stream(ctx, url) - await self.bot.say("Now playing: <{}>".format(url)) - - @_radio.command(no_pm=True, pass_context=True, name='list') - async def _list(self, ctx): - """List saved stream URLs.""" - server = ctx.message.server - message = '```\n' - message += '{:<30}{}\n\n'.format('NAME', 'URL') - if server.id in self.memory: - for stream in self.memory[server.id]: - message += '{:<30}{}\n'.format(stream, self.memory[server.id][stream]) - message += '```' - await self.bot.say(message) - - @_radio.command(no_pm=True, pass_context=True, name='add') - async def _add(self, ctx, name: str, url: str): - """Add a url to save for radio playback.""" - server = ctx.message.server - await self.add_to_memory(server, name, url) - await self.bot.say('Added to memory') - - @_radio.command(no_pm=True, pass_context=True, name='load') - async def _load(self, ctx, name: str): - """Load a saved url for radio playback.""" - server = ctx.message.server - if server.id in self.memory: - if name.lower() in self.memory[server.id]: - url = self.memory[server.id][name.lower()] - if server.id in self.players: - await self.stop_playing(server) - await self.play_stream(ctx, url) - await self.bot.say("Now playing: <{}>".format(url)) - else: - await self.bot.say('"{}" is not in memory.'.format(name.lower())) - else: - await self.bot.say('Nothing in memory yet') - - async def save_memory(self): - dataIO.save_json(self.memory_path, self.memory) - - async def add_to_memory(self, server, name, url): - if server.id not in self.memory: - self.memory[server.id] = {} - self.memory[server.id][name.lower()] = url - await self.save_memory() - - async def join_voice_channel(self, channel): - try: - await self.bot.join_voice_channel(channel) - return True - except discord.InvalidArgument: - await self.bot.say('You need to be in a voice channel yourself.') - except discord.Forbidden: - await self.bot.say('I don\'t have permissions to join this channel.') - return False - - async def leave_voice_channel(self, server): - voice_client = await self.voice_client(server) - if server.id in self.players: - self.players[server.id].stop() - del self.players[server.id] - await self.stop_playing(server) - await voice_client.disconnect() - - async def voice_connected(self, server): - return self.bot.is_voice_connected(server) - - async def voice_client(self, server): - return self.bot.voice_client_in(server) - - async def stop_playing(self, server): - if server.id in self.players: - self.players[server.id].stop() - del self.players[server.id] - - async def start_playing(self, server, url): - if server.id not in self.players: - voice_client = await self.voice_client(server) - audio_player = voice_client.create_ffmpeg_player(url) - self.players[server.id] = audio_player - self.players[server.id].start() - - async def play_stream(self, ctx, url): - server = ctx.message.server - channel = ctx.message.author.voice_channel - if not ctx.message.channel.is_private: - check = True - if not await self.voice_connected(server): - check = await self.join_voice_channel(channel) - if check: - await self.start_playing(server, url) - - async def _playing_check(self): - while self == self.bot.get_cog('Radio'): - for player in self.players: - if not self.players[player].is_playing(): - server = self.bot.get_server(player) - await self.leave_voice_channel(server) - break - await asyncio.sleep(30) - - -def check_folder(): - if not os.path.exists('data/radio'): - print('Creating data/radio folder...') - os.makedirs('data/radio') - - -def check_file(): - if not dataIO.is_valid_json('data/radio/memory.json'): - print('Creating memory.json...') - dataIO.save_json('data/radio/memory.json', {}) - - -def setup(bot): - check_folder() - check_file() - cog = Radio(bot) - loop = asyncio.get_event_loop() - loop.create_task(cog._playing_check()) - bot.add_cog(cog) diff --git a/riot/info.json b/riot/info.json deleted file mode 100644 index b0a2efe..0000000 --- a/riot/info.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "Thanks for installing riot.", - "NAME" : "Riot", - "SHORT" : "RIOT!", - "DESCRIPTION" : "Riot was a feature on the now-extinct Fredboat, requested by Mewleficent.", - "TAGS" : ["riot"] -} diff --git a/riot/riot.py b/riot/riot.py deleted file mode 100644 index 3c748c9..0000000 --- a/riot/riot.py +++ /dev/null @@ -1,17 +0,0 @@ -import discord -from discord.ext import commands - - -class Riot: - - def __init__(self, bot): - self.bot = bot - - @commands.command(pass_context=True, no_pm=True) - async def riot(self, ctx, *, text: str): - """RIOT!""" - await self.bot.say('ヽ༼ຈل͜ຈ༽ノ **' + str(text) + '** ヽ༼ຈل͜ຈ༽ノ') - - -def setup(bot): - bot.add_cog(Riot(bot)) diff --git a/seen/info.json b/seen/info.json deleted file mode 100644 index f582db7..0000000 --- a/seen/info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AUTHOR" : "Paddolicious#8880", - "NAME" : "Seen", - "SHORT" : "Check when the user was last active on a server.", - "DESCRIPTION" : "Check when the user was last active on a server.", - "TAGS": ["Seen", "member", "tools"] -} diff --git a/seen/seen.py b/seen/seen.py deleted file mode 100644 index 8aace8c..0000000 --- a/seen/seen.py +++ /dev/null @@ -1,119 +0,0 @@ -from discord.ext import commands -from cogs.utils.dataIO import dataIO -import discord -import os -import asyncio -from datetime import datetime - - -DB_VERSION = 2 - - -class Seen: - '''Check when someone was last seen.''' - def __init__(self, bot): - self.bot = bot - self.seen = dataIO.load_json('data/seen/seen.json') - self.new_data = False - - async def data_writer(self): - while self == self.bot.get_cog('Seen'): - if self.new_data: - dataIO.save_json('data/seen/seen.json', self.seen) - self.new_data = False - await asyncio.sleep(60) - else: - await asyncio.sleep(30) - - @commands.command(pass_context=True, no_pm=True, name='seen') - async def _seen(self, context, username: discord.Member): - '''seen <@username>''' - server = context.message.server - author = username - timestamp_now = context.message.timestamp - if server.id in self.seen: - if author.id in self.seen[server.id]: - data = self.seen[server.id][author.id] - timestamp_then = datetime.fromtimestamp(data['TIMESTAMP']) - timestamp = timestamp_now - timestamp_then - days = timestamp.days - seconds = timestamp.seconds - hours = seconds // 3600 - seconds = seconds - (hours * 3600) - minutes = seconds // 60 - if sum([days, hours, minutes]) < 1: - ts = 'just now' - else: - ts = '' - if days == 1: - ts += '{} day, '.format(days) - elif days > 1: - ts += '{} days, '.format(days) - if hours == 1: - ts += '{} hour, '.format(hours) - elif hours > 1: - ts += '{} hours, '.format(hours) - if minutes == 1: - ts += '{} minute ago'.format(minutes) - elif minutes > 1: - ts += '{} minutes ago'.format(minutes) - em = discord.Embed(color=discord.Color.green()) - avatar = author.avatar_url if author.avatar else author.default_avatar_url - em.set_author(name='{} was seen {}'.format(author.display_name, ts), icon_url=avatar) - await self.bot.say(embed=em) - else: - message = 'I haven\'t seen {} yet.'.format(author.display_name) - await self.bot.say('{}'.format(message)) - else: - message = 'I haven\'t seen {} yet.'.format(author.display_name) - await self.bot.say('{}'.format(message)) - - async def on_message(self, message): - if not message.channel.is_private and self.bot.user.id != message.author.id: - if not any(message.content.startswith(n) for n in self.bot.settings.prefixes): - server = message.server - author = message.author - ts = message.timestamp.timestamp() - data = {} - data['TIMESTAMP'] = ts - if server.id not in self.seen: - self.seen[server.id] = {} - self.seen[server.id][author.id] = data - self.new_data = True - - -def check_folder(): - if not os.path.exists('data/seen'): - print('Creating data/seen folder...') - os.makedirs('data/seen') - - -def check_file(): - data = {} - data['db_version'] = DB_VERSION - f = 'data/seen/seen.json' - if not dataIO.is_valid_json(f): - print('Creating seen.json...') - dataIO.save_json(f, data) - else: - check = dataIO.load_json(f) - if 'db_version' in check: - if check['db_version'] < DB_VERSION: - data = {} - data['db_version'] = DB_VERSION - dataIO.save_json(f, data) - print('SEEN: Database version too old, resetting!') - else: - data = {} - data['db_version'] = DB_VERSION - dataIO.save_json(f, data) - print('SEEN: Database version too old, resetting!') - - -def setup(bot): - check_folder() - check_file() - n = Seen(bot) - loop = asyncio.get_event_loop() - loop.create_task(n.data_writer()) - bot.add_cog(n) diff --git a/serverlimit/info.json b/serverlimit/info.json deleted file mode 100644 index 890730f..0000000 --- a/serverlimit/info.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "AUTHOR" : "aikaterna", - "INSTALL_MSG" : "No settings, when loaded the bot will leave servers with under 25 members on join.", - "NAME" : "serverlimit", - "SHORT" : "Serverlimit", - "DESCRIPTION" : "Limits the bot joining servers that have under 25 members.", - "TAGS": [] -} diff --git a/serverlimit/serverlimit.py b/serverlimit/serverlimit.py deleted file mode 100644 index 496e047..0000000 --- a/serverlimit/serverlimit.py +++ /dev/null @@ -1,71 +0,0 @@ -# get_default_channel_or_other is from Squid's Admin cog: -# https://github.com/tekulvw/Squid-Plugins - -import discord -import traceback - - -class ServerLimit: - def __init__(self, bot): - self.bot = bot - - async def _message(self, server): - server_owner = server.owner - notice_msg = "Hi, I tried to make an announcement in your "\ - + "server, " + server.name + ", but I don't have "\ - + "permissions to send messages in the default "\ - + "channel there!" - await self.bot.send_message(server_owner, notice_msg) - await self.bot.leave_server(server) - - async def on_server_join(self, server): - chan = self.get_default_channel_or_other(server, - discord.ChannelType.text, - send_messages=True) - me = server.me - server_owner = server.owner - msg = "I can only join servers which have more than 25 members. "\ - + "Please try again later when the server is larger." - if len(server.members) <= 25: - if chan is not None: - if chan.permissions_for(me).send_messages: - await self.bot.send_message(chan, msg) - await self.bot.leave_server(server) - else: - await self._message(server) - await self.bot.send_message(server_owner, msg) - else: - await self._message(server) - await self.bot.send_message(server_owner, msg) - - def get_default_channel_or_other(self, server, - ctype: discord.ChannelType=None, - **perms_required): - - perms = discord.Permissions.none() - perms.update(**perms_required) - if ctype is None: - types = [discord.ChannelType.text, discord.ChannelType.voice] - elif ctype == discord.ChannelType.text: - types = [discord.ChannelType.text] - else: - types = [discord.ChannelType.voice] - try: - channel = server.default_channel - except Exception: - channel = None - if channel is not None: - if channel.permissions_for(server.me).is_superset(perms): - return channel - - chan_list = [c for c in sorted(server.channels, - key=lambda ch: ch.position) - if c.type in types] - for ch in chan_list: - if ch.permissions_for(server.me).is_superset(perms): - return ch - return None - - -def setup(bot): - bot.add_cog(ServerLimit(bot))