From f6a8ffdb1f5c1094df75a0be032e6fc19b508a9a Mon Sep 17 00:00:00 2001 From: aikaterna Date: Sat, 27 Jan 2018 19:10:40 -0800 Subject: [PATCH] Start of the v3 branch --- README.md | 34 +- autoeconomy/autoeconomy.py | 129 -- autoeconomy/info.json | 8 - away/away.py | 88 - away/info.json | 7 - cah/cah.py | 1690 ---------------- cah/data/deck.json | 2434 ------------------------ cah/info.json | 7 - chatchart/chatchart.py | 94 - chatchart/info.json | 9 - forwarding/forwarding.py | 49 - forwarding/info.json | 7 - hunting/hunting.py | 240 --- hunting/info.json | 7 - imgwelcome/data/fonts/UniSansHeavy.otf | Bin 113772 -> 0 bytes imgwelcome/data/noimage.png | Bin 3798 -> 0 bytes imgwelcome/data/transparent.png | Bin 3303 -> 0 bytes imgwelcome/imgwelcome.py | 615 ------ imgwelcome/info.json | 9 - otherbot/otherbot.py | 17 - picwelcome/data/default.png | Bin 30623 -> 0 bytes picwelcome/info.json | 9 - picwelcome/picwelcome.py | 170 -- pug/info.json | 8 - pug/pug.py | 313 --- radio/info.json | 7 - radio/radio.py | 161 -- riot/info.json | 8 - riot/riot.py | 17 - seen/info.json | 7 - seen/seen.py | 119 -- serverlimit/info.json | 8 - serverlimit/serverlimit.py | 71 - 33 files changed, 2 insertions(+), 6340 deletions(-) delete mode 100644 autoeconomy/autoeconomy.py delete mode 100644 autoeconomy/info.json delete mode 100644 away/away.py delete mode 100644 away/info.json delete mode 100644 cah/cah.py delete mode 100644 cah/data/deck.json delete mode 100644 cah/info.json delete mode 100644 chatchart/chatchart.py delete mode 100644 chatchart/info.json delete mode 100644 forwarding/forwarding.py delete mode 100644 forwarding/info.json delete mode 100644 hunting/hunting.py delete mode 100644 hunting/info.json delete mode 100644 imgwelcome/data/fonts/UniSansHeavy.otf delete mode 100644 imgwelcome/data/noimage.png delete mode 100644 imgwelcome/data/transparent.png delete mode 100644 imgwelcome/imgwelcome.py delete mode 100644 imgwelcome/info.json delete mode 100644 otherbot/otherbot.py delete mode 100644 picwelcome/data/default.png delete mode 100644 picwelcome/info.json delete mode 100644 picwelcome/picwelcome.py delete mode 100644 pug/info.json delete mode 100644 pug/pug.py delete mode 100644 radio/info.json delete mode 100644 radio/radio.py delete mode 100644 riot/info.json delete mode 100644 riot/riot.py delete mode 100644 seen/info.json delete mode 100644 seen/seen.py delete mode 100644 serverlimit/info.json delete mode 100644 serverlimit/serverlimit.py 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 facd333211f339f4f79d35e1335ce377cdf7fe7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113772 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

fUhrTlo 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ⅆ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 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 cedc576c3885363312f772c9366dab15994df678..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30623 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 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))