class Rank(Category): # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Class Fields # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Errors # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # INVALID_INTERACTION = "INVALID_INTERACTION" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Constructor # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __init__(self, client): super().__init__( client, "Rank", description = "The ranking system is strong with this category.", embed_color = 0x008080, server_category = True, locally_inactive_error = Server.getInactiveError, globally_inactive_error = OmegaPsi.getInactiveError, locally_active_check = Server.isCommandActive, globally_active_check = OmegaPsi.isCommandActive ) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Commands self._rank = Command(commandDict = { "alternatives": ["rank", "r"], "info": "Shows you your current level and experience in this server.", "run_in_private": False, "errors": { Category.TOO_MANY_PARAMETERS: { "messages": [ "When you are getting your ranking card, you don't need any parameters." ] } }, "command": self.rank }) self._levelUp = Command(commandDict = { "alternatives": ["levelUp"], "info": "Shows you how many profane words, reactions, or normal messages you need to send to level up.", "run_in_private": False, "parameters": { "interaction": { "info": "The type of interaction to check the number of until you level up.", "optional": True, "accepted_parameters": { "profanity": { "alternatives": ["profanity", "profane"], "info": "Check how many profane words you need to level up." }, "reactions": { "alternatives": ["reactions", "reacts"], "info": "Check how many reactions you need to level up." }, "normal": { "alternatives": ["normal", "basic"], "info": "Check how many regular messages you need to send to level up." } } } }, "errors": { Rank.INVALID_INTERACTION: { "messages": [ "That is not a valid interaction type to check." ] }, Category.TOO_MANY_PARAMETERS: { "messages": [ " When you are getting your level up info, you don't need more than just the inquiry." ] } }, "command": self.levelUp }) self.setCommands({ self._rank, self._levelUp }) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Command Methods # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def rank(self, message, parameters): """Returns an image displaying the rank of the member in this server.\n discordMember - The member to get the rank of.\n """ # Check for too many parameters if len(parameters) > self._rank.getMaxParameters(): result = getErrorMessage(self._rank, Rank.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: result = await createRankImage(message.author) # Check for an error if type(result) == discord.Embed: await sendMessage( self.client, message, embed = result.set_footer( text = "Requested by {}#{}".format( message.author.name, message.author.discriminator ), icon_url = message.author.avatar_url ) ) else: # Send rank image and remove await sendMessage( self.client, message, filename = result ) os.remove(result) async def levelUp(self, message, parameters): """Returns the amount of different interactions a Discord Member needs to level up. Parameters: discordMember (discord.Member): The Discord Member to get the interactions of. interactionType (str): The type of interaction to get. """ # Check for too many parameters if len(parameters) > self._levelUp.getMaxParameters(): embed = getErrorMessage(self._levelUp, Rank.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: interactionType = None if len(parameters) == 0 else parameters[0] # Get member info from server member = await Server.getMember(message.guild, message.author) # Get member's current and next experience currentExp = member["experience"] nextExp = Server.getExpFromLevel(member["level"] + 1) # Get each interaction type before checking the interaction type profanity = math.ceil( (nextExp - currentExp) / (Server.PROFANE_XP + Server.NORMAL_XP) ) reactions = math.ceil( (nextExp - currentExp) / Server.REACTION_XP ) normal = math.ceil( (nextExp - currentExp) / Server.NORMAL_XP ) # Check if interaction type is None; Get all stats if interactionType == None: embed = discord.Embed( title = "In order to level up, you need either", description = "{} Profane Messages\nor\n{} Reactions\nor\n{} Regular Messages".format( profanity, reactions, normal ), colour = self.getEmbedColor() if message.guild == None else message.author.top_role.color ) # Check if interaction type is valid elif interactionType in self._levelUp.getAcceptedParameter("interaction", "profanity").getAlternatives(): embed = discord.Embed( title = "In order to level up, you need", description = "{} Profane Messages".format(profanity), colour = self.getEmbedColor() if message.guild == None else message.author.top_role.color ) elif interactionType in self._levelUp.getAcceptedParameter("interaction", "reactions").getAlternatives(): embed = discord.Embed( title = "In order to level up, you need", description = "{} Reactions".format(reactions), colour = self.getEmbedColor() if message.guild == None else message.author.top_role.color ) elif interactionType in self._levelUp.getAcceptedParameter("interaction", "normal").getAlternatives(): embed = discord.Embed( title = "In order to level up, you need", description = "{} Regular Messages".format(normal), colour = self.getEmbedColor() if message.guild == None else message.author.top_role.color ) # Interaction type is invalid; error else: embed = getErrorMessage(self._levelUp, Rank.INVALID_INTERACTION) await sendMessage( self.client, message, embed = embed.set_footer( text = "Requested by {}#{}".format( message.author.name, message.author.discriminator ), icon_url = message.author.avatar_url ) ) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Parsing # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def on_message(self, message): """Parses a message and runs a Rank Category command if it can.\n message - The Discord Message to parse.\n """ # Make sure message starts with the prefix if await Server.startsWithPrefix(message.guild, message.content) and not message.author.bot: # Split up into command and parameters if possible command, parameters = Category.parseText(await Server.getPrefixes(message.guild), message.content) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Iterate through commands for cmd in self.getCommands(): if command in cmd.getAlternatives(): async with message.channel.typing(): # Run the command but don't try running others await self.run(message, cmd, cmd.getCommand(), message, parameters) break
class NSFW(Category): # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Class Fields # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # BOOBS_URL = "http://media.oboobs.ru/boobs_preview/{}.jpg" BOOTY_URL = "http://media.obutts.ru/butts_preview/{}.jpg" URBAN_API_CALL = "https://api.urbandictionary.com/v0/define?term={}" URBAN_ICON = "https://vignette.wikia.nocookie.net/creation/images/b/b7/Urban_dictionary_--_logo.jpg/revision/latest?cb=20161002212954" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Errors # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # NO_TERM = "NO_TERM" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Constructor # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __init__(self, client): super().__init__( client, "NSFW", description="An NSFW category for 18+", embed_color=0xFFAAAA, restriction_info= "You should be 18+ to run the commands in this category!", nsfw_category=True, nsfw_channel_error=Server.getNSFWChannelError) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Commands self._boobs = Command( commandDict={ "alternatives": ["boobs", "boob"], "info": "Sends a random picture of boobs.", "errors": { NSFW.TOO_MANY_PARAMETERS: { "messages": [ "In order to get some boobs, you don't need any parameters." ] } }, "command": self.boobs }) self._booty = Command( commandDict={ "alternatives": ["booty", "ass"], "info": "Sends a random picture of some booty.", "errors": { NSFW.TOO_MANY_PARAMETERS: { "messages": [ "In order to get some booty, you don't need any parameters." ] } }, "command": self.booty }) self._urban = Command( commandDict={ "alternatives": ["urban", "urbanDictionary", "urbanDict"], "info": "Gives you the top 5 urban dictionary entries for a term.", "nsfw": True, "parameters": { "term": { "info": "The term to look up in urban dictionary.", "optional": False } }, "errors": { NSFW.NOT_ENOUGH_PARAMETERS: { "messages": [ "You need the term to look something up in urban dictionary." ] }, NSFW.NO_TERM: { "messages": [ "The term you entered does not exist on urban dictionary." ] } }, "command": self.urban }) self.setCommands([self._boobs, self._booty, self._urban]) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Command Methods # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def boobs(self, message, parameters): """Sends a random picture of some boobs. """ # Check for too many parameters if len(parameters) > self._boobs.getMaxParameters(): embed = getErrorMessage(self._boobs, NSFW.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: # Load image from URL, temporarily save it, send image while True: # Generate random number for an image; Add 0's to beginning until pad is reached (5) boobNumber = str(random.randint(1, 12500)) boobNumber = boobNumber.rjust(5, "0") # Try loading the Image try: loadImageFromUrl(NSFW.BOOBS_URL.format(boobNumber)) break # Image load failed; Retry using new number. except: pass embed = discord.Embed( title="Boobs Number {}".format(boobNumber), description=" ", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image( url=NSFW.BOOBS_URL.format(boobNumber)) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def booty(self, message, parameters): """Sends a random picture of some booty. """ # Check for too many parameters if len(parameters) > self._booty.getMaxParameters(): return getErrorMessage(self._booty, NSFW.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: # Load image from URL, temporarily save it, send image while True: # Generate random number for an image; Add 0's to beginning until pad is reached (5) bootyNumber = str(random.randint(1, 5400)) bootyNumber = bootyNumber.rjust(5, "0") # Try loading the image try: loadImageFromUrl(NSFW.BOOTY_URL.format(bootyNumber)) break # Image load failed; Retry using new number. except: pass embed = discord.Embed( title="Booty Number {}".format(bootyNumber), description=" ", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image( url=NSFW.BOOTY_URL.format(bootyNumber)) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def urban(self, message, parameters): """Returns the top 5 urban dictionary entries for the specified term.\n - term - The term to search on urban dictionary.\n - discordChannel - The Discord Channel the definition is being sent in.\n """ # Check for not enough parameters if len(parameters) < self._urban.getMinParameters(): embed = getErrorMessage(self._urban, NSFW.NOT_ENOUGH_PARAMETERS) # There were the proper amount of parameters else: term = " ".join(parameters) # Use requests to get the data in JSON try: urlCall = NSFW.URBAN_API_CALL.format(term.replace(" ", "+")) urbanData = await loop.run_in_executor(None, requests.get, urlCall) urbanData = urbanData.json() # Get first 5 values (or values if there are less than 5) if len(urbanData["list"]) < 5: definitions = urbanData["list"] else: definitions = urbanData["list"][:5] # Create discord embed embed = discord.Embed( title="{} Results Of `{}`".format( "Top 5" if len(definitions) > 5 else "", term), description=" ", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_thumbnail( url=NSFW.URBAN_ICON) # Add definitions defCount = 0 for definition in definitions: defCount += 1 # Get definition; Split up into multiple fields if necessary definitionFields = splitText(definition["definition"], OmegaPsi.MESSAGE_THRESHOLD) count = 0 for field in definitionFields: count += 1 embed.add_field(name="Definition {} {}".format( defCount, "({} / {})".format(count, len(definitionFields)) if len(definitionFields) > 1 else ""), value=field, inline=False) except: embed = getErrorMessage(self._urban, NSFW.NO_TERM) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Parsing # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def on_message(self, message): """Parses a message and runs an NSFW Category command if it can.\n message - The Discord Message to parse.\n """ # Make sure message starts with the prefix if await Server.startsWithPrefix( message.guild, message.content) and not message.author.bot: # Split up into command and parameters if possible command, parameters = Category.parseText( await Server.getPrefixes(message.guild), message.content) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Iterate through commands for cmd in self.getCommands(): if command in cmd.getAlternatives(): async with message.channel.typing(): # Run the command but don't try running others await self.run(message, cmd, cmd.getCommand(), message, parameters) break
class BotModerator(Category): # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Class Fields # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # EMBED_COLOR = 0xA456B0 BOT_MARKDOWN = "botMarkdown.md" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Errors # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # CANT_BE_DEACTIVATED = "CANT_BE_DEACTIVATED" INVALID_ACTIVITY = "INVALID_ACTIVITY" INVALID_COMMAND = "INVALID_COMMAND" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Constructor # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __init__(self, client): super().__init__(client, "Bot Moderator", description="Very private stuff.", restriction_info= "You must be a Bot Moderator to run these commands.", bot_mod_category=True, bot_mod_error=OmegaPsi.getNoAccessError, locally_inactive_error=Server.getInactiveError, globally_inactive_error=OmegaPsi.getInactiveError, locally_active_check=Server.isCommandActive, globally_active_check=OmegaPsi.isCommandActive) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Commands self._addModerator = Command( commandDict={ "alternatives": ["addBotModerator", "addBotMod", "abm"], "info": "Allows you to add a bot moderator to the bot.", "bot_moderator_only": True, "min_parameters": 1, "parameters": { "member(s)...": { "info": "The member(s) to add as a bot moderator.", "optional": False } }, "errors": { BotModerator.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to add a bot moderator, you need to mention them." ] } }, "command": self.addModerator }) self._removeModerator = Command( commandDict={ "alternatives": ["removeBotModerator", "removeBotMod", "remBotMod", "rbm"], "info": "Allows you to remove a bot moderator from the bot.", "bot_moderator_only": True, "min_parameters": 1, "parameters": { "member(s)...": { "info": "The member(s) to remove as a bot moderator.", "optional": False } }, "errors": { BotModerator.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to remove a bot moderator, you need to mention them." ] } }, "command": self.removeModerator }) self._activate = Command( commandDict={ "alternatives": ["activateGlobally", "enableGlobally"], "info": "Allows you to activate a command, or commands, globally.", "bot_moderator_only": True, "min_parameters": 1, "parameters": { "command(s)": { "info": "The command(s) to activate globally.", "optional": False } }, "errors": { BotModerator.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to activate a command globally, you need to type it in." ] }, BotModerator.INVALID_COMMAND: { "messages": ["That is not a valid command."] } }, "command": self.activate }) self._deactivate = Command( commandDict={ "alternatives": ["deactivateGlobally", "disableGlobally"], "info": "Allows you to deactivate a command globally.", "bot_moderator_only": True, "min_parameters": 1, "parameters": { "command": { "info": "The command to deactivate globally.", "optional": False }, "reason": { "info": "The reason the command is being deactivated globally.", "optional": True } }, "errors": { BotModerator.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to deactivate a command globally, you need to type it in." ] }, BotModerator.INVALID_COMMAND: { "messages": ["That is not a valid command."] }, BotModerator.CANT_BE_DEACTIVATED: { "messages": ["This command cannot be deactivated."] } }, "command": self.deactivate }) self._servers = Command( commandDict={ "alternatives": ["servers", "botServers"], "info": "Allows you to get a list of servers the bot is in.", "bot_moderator_only": True, "parameters": { "markdown": { "info": "Whether or not to send a markdown version of all the server information.", "optional": True } }, "errors": { BotModerator.TOO_MANY_PARAMETERS: { "messages": [ "In order to get a list of servers the bot is in, you only need 1 parameter which is optional." ] } }, "command": self.getServers }) self._status = Command( commandDict={ "alternatives": ["setStatus", "status"], "info": "Allows you to change the presence of the bot.", "bot_moderator_only": True, "min_parameters": 2, "parameters": { "activity": { "info": "The activity to set for the presence.", "optional": False, "accepted_parameters": { "playing": { "alternatives": ["playing", "Playing"], "info": "The playing activity type." }, "streaming": { "alternatives": ["streaming", "Streaming"], "info": "The streaming activity type." }, "listening": { "alternatives": [ "listening", "Listening", "listening to", "Listening to" ], "info": "The listening activity type." }, "watching": { "alternatives": ["watching", "Watching"], "info": "The watching activity type." } } }, "text": { "info": "The text to set as the presence.", "optional": False } }, "errors": { BotModerator.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to set the status, you need the status type and the text to set." ] }, BotModerator.INVALID_ACTIVITY: { "messages": ["The given activity is not a valid activity."] } }, "command": self.setStatus }) self._todo = Command( commandDict={ "alternatives": ["todo"], "info": "Adds, removes, or lists things in the TODO list.", "parameters": { "action": { "info": "The action to do.", "optional": True, "accepted": { "add": { "alternatives": ["add", "a"], "info": "Adds something to the TODO list." }, "remove": { "alternatives": ["remove", "r"], "info": "Removes something from the TODO list." } } }, "item": { "info": "The item to add or remove.", "optional": True } }, "errors": { BotModerator.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to add or remove something, you need the item to add or remove." ] }, BotModerator.INVALID_PARAMETER: { "messages": ["That is not a valid parameter."] } }, "command": self.todo }) self._kill = Command( commandDict={ "alternatives": ["stop", "quit", "kill"], "info": "Kills the bot.", "bot_moderator_only": True, "max_parameters": 1, "parameters": { "process": { "info": "The process number to kill.", "optional": True } }, "errors": { BotModerator.TOO_MANY_PARAMETERS: { "messages": [ "In order to kill the bot, you don't need anything other than the process." ] } }, "command": self.kill }) self._debug = Command( commandDict={ "alternatives": ["debug"], "info": "Debugs the bot.", "bot_moderator_only": True, "max_parameters": 0, "errors": { BotModerator.TOO_MANY_PARAMETERS: { "messages": ["To debug the bot, you don't need any parameters."] } }, "command": self.debug }) self.setCommands([ self._addModerator, self._removeModerator, self._activate, self._deactivate, self._servers, self._status, self._todo, self._kill, self._debug ]) self._todo.setBotModeratorOnly(False) self._categories = { "Code": Code(None), "Game": Game(None), "Image": Image(None), "Insult": Insult(None), "Internet": Internet(None), "Math": Math(None), "Meme": Meme(None), "Misc": Misc(None), "NSFW": NSFW(None), "Rank": Rank(None), "Updates": Updates(None) } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Command Methods # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def addModerator(self, message, parameters): """Adds bot moderators to the bot.\n Parameters: parameters: The Discord Users to add as a bot moderator.\n """ # Check if message has no mentions if len(message.mentions) < self._addModerator.getMinParameters(): return getErrorMessage(self._addModerator, BotModerator.NOT_ENOUGH_PARAMETERS) # There was at least one mention else: # Iterate through each member result = "" for member in message.mentions: result += "{} {}".format( member.mention, " was successfully added as a bot moderator." if await OmegaPsi.addModerator(member) else (" is already a bot moderator.")) embed = discord.Embed(name="Added bot Moderators", description=result, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def removeModerator(self, message, parameters): """Removes a bot moderator from the bot.\n Parameters: parameters: The Discord Users to remove as a bot moderator.\n """ # Check if message has no mentions if len(message.mentions) < self._removeModerator.getMinParameters(): embed = getErrorMessage(self._removeModerator, BotModerator.NOT_ENOUGH_PARAMETERS) # There was at least one mention else: # Iterate through each member result = "" for member in message.mentions: result += "{} {} a bot moderator.".format( member.mention, "was successfully removed as" if await OmegaPsi.removeModerator(member) else ("is not")) embed = discord.Embed(name="Removed Moderators", description=result, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def activate(self, message, parameters): """Activates commands globally in the bot. Parameters: parameters: The parameters to process. """ # Check if there are not enough parameters if len(parameters) < self._activate.getMinParameters(): embed = getErrorMessage(self._activate, BotModerator.NOT_ENOUGH_PARAMETERS) # Parameters had the minimum amount of parameters else: # Commands held in each parameter commands = parameters # Open bot file bot = await OmegaPsi.openOmegaPsi() # Iterate through commands if len(commands) > 0: result = "" acCommands = [] for command in commands: # Iterate through categories added = False for category in self._categories: # Check if command was part of category commandObject = self._categories[category].getCommand( command) if commandObject != None: acCommands.append(commandObject) added = True if not added: result += "`{}` is not a valid command.\n".format( command) # Activate commands for command in acCommands: if command.getAlternatives( )[0] in bot["inactive_commands"]: bot["inactive_commands"].pop( command.getAlternatives()[0]) result += "`{}` was activated globally.\n".format( command.getAlternatives()[0]) else: result += "`{}` is already globally active.\n".format( command.getAlternatives()[0]) else: result = "" for command in bot["inactive_commands"]: result += "`{}` was activated globally.\n".format(command) bot["inactive_commands"] = {} # Close bot file await OmegaPsi.closeOmegaPsi(bot) embed = discord.Embed(name="Activated", description=result, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def deactivate(self, message, parameters): """Deactivates a command globally in the bot.\n Parameters: command: The command to globally deactivate.\n reason: The reason the command is being globally deactivated.\n """ # Check for minimum amount of parameters if len(parameters) < self._deactivate.getMinParameters(): embed = getErrorMessage(self._deactivate, BotModerator.NOT_ENOUGH_PARAMETERS) # Parameters had the minimum amount of parameters else: # Command to be deactivated is first parameter; Reason is every parameter after command = parameters[0] reason = "No Reason" if len(parameters) > 1: reason = " ".join(parameters[1:]) # Open bot file bot = await OmegaPsi.openOmegaPsi() # Check if command is valid commandObject = None for category in self._categories: commandObject = self._categories[category].getCommand(command) if commandObject != None: break if commandObject == None: result = "`{}` is not a valid command.".format(command) else: bot["inactive_commands"][commandObject.getAlternatives() [0]] = reason result = "`{}` was globally deactivated.\nReason: {}".format( commandObject.getAlternatives()[0], reason) # Close bot file await OmegaPsi.closeOmegaPsi(bot) embed = discord.Embed(name="Deactivated", description=result, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def getServers(self, message, parameters): """Returns a list of servers the bot is in.\n """ # Check if parameters exceeds maximum parameters if len(parameters) > self._servers.getMaxParameters(): embed = getErrorMessage(self._servers, BotModerator.TOO_MANY_PARAMETERS) # Parameters do not exceed maximum parameters else: # Getting results through embed if len(parameters) == 0: # Add results to fields fields = [] fieldText = "" for server in self.client.guilds: text = "`{}` | Owner: {}\n".format(server.name, server.owner.mention) if len(fieldText) + len( text) >= OmegaPsi.MESSAGE_THRESHOLD: fields.append(fieldText) fieldText = "" fieldText += text # Add trailing field text if len(fieldText) > 0: fields.append(fieldText) # Create embed object embed = discord.Embed( title="Servers", description="A list of servers that Omega Psi is in.", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Add fields to embed object count = 0 for field in fields: count += 1 embed.add_field( name="Servers {}".format("({} / {})".format( count, len(fields)) if len(fields) > 1 else ""), value=field, inline=False) # Getting results through markdown file else: # Setup markdown text markdown = "# Omega Psi Server Information\n" # Iterate through servers bot is in for guild in self.client.guilds: # Load file server = await Server.openServer(guild) # Add server information (owner, name) try: markdown += "## {} - {}\n".format( guild.name, guild.owner.name + "#" + guild.owner.discriminator) except: markdown += "## {} - No Owner\n".format(guild.name) # Iterate through members in server dictionary for member in server["members"]: member = server["members"][member] discordMember = guild.get_member(int(member["id"])) markdown += ( " * {} ({})\n" + " * Moderator? {}\n" + " * Experience: {}\n" + " * Level: {}\n" + " * Experience until next level: {}\n").format( discordMember.name + "#" + discordMember.discriminator, discordMember.nick, "Yes" if discordMember.guild_permissions.manage_guild else "No", member["experience"], member["level"], Server.getExpFromLevel(member["level"] + 1) - member["experience"]) # Save markdown temporarily mdFile = open(BotModerator.BOT_MARKDOWN, "w") mdFile.write(markdown) mdFile.close() mdFile = open(BotModerator.BOT_MARKDOWN, "r") # Send file to DMs; Then delete await message.author.send(file=discord.File(mdFile)) os.remove(BotModerator.BOT_MARKDOWN) embed = discord.Embed( title="File sent.", description= "The server information has been sent to your DM's", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def setStatus(self, message, parameters): """Sets the presence of the bot given the activity type and text.\n Parameters: activityType: The type of activity to set for the presence.\n text: The text to set.\n """ # Check if parameters is less than minimum parameters if len(parameters) < self._status.getMinParameters(): embed = getErrorMessage(self._status, BotModerator.NOT_ENOUGH_PARAMETERS) # Parameters has minumum parameters else: # Activity type is first parameter; Text is every parameter after activityType = parameters[0] text = " ".join(parameters[1:]) # Get the specific activity type activityText = activityType if activityType in self._status.getAcceptedParameter( "activity", "playing").getAlternatives(): activityType = discord.ActivityType.playing activityText = "Playing" elif activityType in self._status.getAcceptedParameter( "activity", "streaming").getAlternatives(): activityType = discord.ActivityType.streaming activityText = "Streaming" elif activityType in self._status.getAcceptedParameter( "activity", "listening").getAlternatives(): activityType = discord.ActivityType.listening activityText = "Listening" elif activityType in self._status.getAcceptedParameter( "activity", "watching").getAlternatives(): activityType = discord.ActivityType.watching activityText = "Watching" # Update the bot's activity setting await OmegaPsi.setActivityType(activityType) await OmegaPsi.setActivityName(text) # Change the presence of the bot await self.client.change_presence( status=discord.Status.online, activity=discord.Activity( name=text, type=activityType, url="https://www.twitch.tv/FellowHashbrown")) embed = discord.Embed( title="Presence Set", description="Activity: {}\nText: {}\n".format( activityText, text), colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def todo(self, message, parameters): """Runs the todo command. Parameters: action (str): What action to perform for the todo command. item (str): The item to add/remove to/from the TODO list. """ # Check for no parameters; list the TODO list if len(parameters) == 0: todoList = await OmegaPsi.getToDoList() todoText = "" for item in range(len(todoList)): todoText += "`{}.) {}`\n".format(item + 1, todoList[item]) embed = discord.Embed( title="TODO List", description=todoText if len(todoText) > 0 else "Nothing Yet", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Check for 2 or more parameters else: # Check if author is a bot moderator if await OmegaPsi.isAuthorModerator(message.author): action = parameters[0] # There is nothing to add if len(parameters) == 1: embed = getErrorMessage(self._todo, BotModerator.NOT_ENOUGH_PARAMETERS) # Check if action is valid elif action in self._todo.getAcceptedParameter( "action", "add").getAlternatives(): # Check if index is given if parameters[1].isdigit(): index = int(parameters[1]) item = " ".join(parameters[2:]) else: index = 0 item = " ".join(parameters[1:]) success = await OmegaPsi.addToDo(item, index) embed = discord.Embed( title="Added TODO Item" if success["success"] else "Failed to add TODO Item", description=success["reason"], colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) elif action in self._todo.getAcceptedParameter( "action", "remove").getAlternatives(): success = await OmegaPsi.removeToDo(" ".join( parameters[1:])) embed = discord.Embed( title="Removed TODO Item" if success["success"] else "Failed to remove TODO Item", description=success["reason"], colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Action is invalid else: embed = getErrorMessage(self._todo, BotModerator.INVALID_PARAMETER) # Author is not bot moderator else: embed = OmegaPsi.getNoAccessError() await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def kill(self, message, parameters): """Kills the bot and logs out. """ processId = OmegaPsi.PROCESS_ID # Check if parameters exceeds maximum parameters if len(parameters) > self._kill.getMaxParameters(): embed = getErrorMessage(self._kill, BotModerator.TOO_MANY_PARAMETERS) # Parameters do not exceed maximum parameters else: processId = OmegaPsi.PROCESS_ID if len( parameters) == 0 else parameters[0] embed = discord.Embed( title="Bot Killed", description="Omega Psi was killed (Process {})".format( OmegaPsi.PROCESS_ID), colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Only kill if processId is OmegaPsi.PROCESS_ID if str(processId) == str(OmegaPsi.PROCESS_ID): await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) await self.client.logout() async def debug(self, message, parameters): """Debugs the bot. """ # Check if parameters exceeds maximum parameters if len(parameters) > self._debug.getMaxParameters(): embed = getErrorMessage(self._debug, BotModerator.TOO_MANY_PARAMETERS) # Parameters do not exceed maximum parameters else: embed = discord.Embed( title="Omega Psi Debugging", description="Process ID: {}".format(OmegaPsi.PROCESS_ID), colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Parsing # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def on_message(self, message): """Parses a message and runs a Bot Moderator command if it can.\n Parameters: message: The Discord Message to parse.\n """ # Make sure message starts with the prefix if await Server.startsWithPrefix( message.guild, message.content) and not message.author.bot: # Split up into command and parameters if possible command, parameters = Category.parseText( await Server.getPrefixes(message.guild), message.content) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Iterate through commands for cmd in self.getCommands(): if command in cmd.getAlternatives(): async with message.channel.typing(): # Run the command but don't try running others await self.run(message, cmd, cmd.getCommand(), message, parameters) break
class Code(Category): # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Class Fields # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # MAX_BRAINFUCK_LENGTH = 2**15 # 32736 QR_API_CALL = "https://api.qrserver.com/v1/create-qr-code/?size={0}x{0}&data={1}" MORSE_API_CALL = "https://www.fellowhashbrown.com/api/morse/{}?text={}" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Errors # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # BASE_MISMATCH = "BASE_MISMATCH" BASE_OUT_OF_RANGE = "BASE_OUT_OF_RANGE" INVALID_LANGUAGE = "INVALID_LANGUAGE" INVALID_BASE = "INVALID_BASE" INVALID_PARAMETER = "INVALID_PARAMETER" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Constructor # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __init__(self, client): super().__init__(client, "Code", description="Commands that have to do with coding!", embed_color=0xFFFF00, locally_inactive_error=Server.getInactiveError, globally_inactive_error=OmegaPsi.getInactiveError, locally_active_check=Server.isCommandActive, globally_active_check=OmegaPsi.isCommandActive) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Commands self._brainfuck = Command( commandDict={ "alternatives": ["brainf", "bf"], "info": "Runs brainfuck code. Kinda confusing stuff at first glance.", "min_parameters": 1, "parameters": { "code": { "optional": False, "info": "The code to run." }, "parameters": { "optional": True, "info": "The parameters to use in the code." } }, "errors": { Code.NOT_ENOUGH_PARAMETERS: { "messages": [ "The brainfuck command requires at least the brainfuck code." ] }, Code.TOO_MANY_PARAMETERS: { "messages": [ "The brainfuck command only needs the code and the parameters. Make sure you remove spaces from both." ] } }, "command": self.brainfuck }) self._convert = Command( commandDict={ "alternatives": ["convert", "conversion", "baseConversion", "baseConverter"], "info": "Converts a number from one base to another base.", "min_parameters": 2, "max_parameters": 3, "parameters": { "startBase": { "info": "The base the number starts at.", "optional": True }, "endBase": { "info": "The base the number ends at.", "optional": False }, "number": { "info": "The number to convert.", "optional": False } }, "errors": { Code.NOT_ENOUGH_PARAMETERS: { "messages": [ "You need at least the end base and the number to convert." ] }, Code.TOO_MANY_PARAMETERS: { "messages": [ "You only need the start base, the end base, and the number." ] }, Code.INVALID_BASE: { "messages": ["A base you entered is not a valid base."] }, Code.BASE_MISMATCH: { "messages": [ "The number you entered does not match the start base." ] } }, "command": self.convert }) self._base64 = Command( commandDict={ "alternatives": ["base64", "b64"], "info": "Encodes or decodes text to base64.", "min_parameters": 2, "parameters": { "conversion": { "info": "Whether to encode/decode text into/from base64.", "optional": False, "accepted_parameters": { "encode": { "alternatives": ["encode", "enc", "e"], "info": "Encode text into base64." }, "decode": { "alternatives": ["decode", "dec", "d"], "info": "Decode text from base64." } } }, "text": { "info": "The text to encode or decode.", "optional": False } }, "errors": { Code.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to encode or decode text, you need the conversion type and the text." ] }, Code.INVALID_PARAMETER: { "messages": ["That is not a valid conversion type."] } }, "command": self.base64 }) self._morse = Command( commandDict={ "alternatives": ["morse", "m"], "info": "Encodes or decodes text to Morse code.", "parameters": { "conversion": { "info": "Whether to encode/decode text into/from Morse Code.", "optional": False, "accepted": { "encode": { "alternatives": ["encode", "enc", "e"], "info": "Encodes text into Morse Code." }, "decode": { "alternatives": ["decode", "dec", "d"], "info": "Decodes text from Morse Code." } } }, "text": { "info": "The text to encode or decode.", "optional": False } }, "errors": { Code.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to encode or decode text, you need the conversion type and the text." ] }, Code.INVALID_PARAMETER: { "messages": ["That is not a valid conversion type."] } }, "command": self.morse }) self._qrCode = Command( commandDict={ "alternatives": ["qrCode", "qr"], "info": "Turns text into a QR code.", "parameters": { "data": { "info": "The data to set for the QR code.", "optional": False } }, "errors": { Code.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to get the QR code for data, you need to type in the data." ] } }, "command": self.qrCode }) self.setCommands([ self._brainfuck, self._convert, self._base64, self._morse, self._qrCode ]) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Command Methods # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __brainfuck(self, code, parameters): # Keep track of pointers and data data = [0] * Code.MAX_BRAINFUCK_LENGTH dataPointer = 0 paramPointer = 0 output = "" loop = 0 # Iterate through code char = 0 while char < len(code): # char is > (move pointer right) if code[char] == ">": dataPointer = 0 if dataPointer == Code.MAX_BRAINFUCK_LENGTH - 1 else dataPointer + 1 # char is < (move pointer left) elif code[char] == "<": dataPointer = Code.MAX_BRAINFUCK_LENGTH - 1 if dataPointer == 0 else dataPointer - 1 # char is + (increase value at pointer) elif code[char] == "+": data[dataPointer] += 1 if data[dataPointer] > 255: data[dataPointer] -= 256 # char is - (decrease value at pointer) elif code[char] == "-": data[dataPointer] -= 1 if data[dataPointer] < 0: data[dataPointer] += 256 # char is . (add data to output) elif code[char] == ".": output += str(chr(data[dataPointer])) # char is , (add data to input) elif code[char] == ",": if paramPointer >= len(parameters): data[dataPointer] = 0 else: data[dataPointer] = ord(parameters[paramPointer]) paramPointer += 1 # char is [ (open loop) elif code[char] == "[": if data[dataPointer] == 0: char += 1 while loop > 0 or code[char] != "]": if code[char] == "[": loop += 1 if code[char] == "]": loop -= 1 char += 1 # char is ] (close loop) elif code[char] == "]": if data[dataPointer] != 0: char -= 1 while loop > 0 or code[char] != "[": if code[char] == "]": loop += 1 if code[char] == "[": loop -= 1 char -= 1 char -= 1 char += 1 return output async def brainfuck(self, message, parameters): """Runs brainfuck code and returns the result.\n Parameters: code: The brainfuck code to run.\n parameters: The parameters to insert into the brainfuck code.\n """ # Check for not enough parameters if len(parameters) < self._brainfuck.getMinParameters(): embed = getErrorMessage(self._brainfuck, Code.NOT_ENOUGH_PARAMETERS) # Check for too many parameters elif len(parameters) > self._brainfuck.getMaxParameters(): embed = getErrorMessage(self._brainfuck, Code.TOO_MANY_PARAMETERS) # There were a proper amount of parameters else: code = parameters[0] if len(parameters) == 2: parameters = parameters[1] else: parameters = [] # Remove all invalid symbols validSymbols = "<>+-.,[]" newCode = "" for char in code: if char in validSymbols: newCode += char code = newCode try: async with async_timeout.timeout(5): description = await loop.run_in_executor( None, self.__brainfuck, code, parameters) except: description = "Timed out" # Create and return embed for result embed = discord.Embed(title="Result", description=description, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def convert(self, message, parameters): """Converts a number from the start base to the end base.\n Parameters: startBase: The base to convert from.\n endBase: The base to convert to.\n number: The number to convert.\n """ # Check for not enough parameters if len(parameters) < self._convert.getMinParameters(): embed = getErrorMessage(self._convert, Code.NOT_ENOUGH_PARAMETERS) # Check for too many parameters elif len(parameters) > self._convert.getMaxParameters(): embed = getErrorMessage(self._convert, Code.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: startBase = "10" endBase = parameters[0] number = parameters[1] if len(parameters) == 3: startBase = parameters[0] endBase = parameters[1] number = parameters[2] # Only run if start base and end base are valid if startBase.isdigit() and endBase.isdigit(): startBase = int(startBase) endBase = int(endBase) if startBase >= 2 and startBase <= 64 and endBase >= 2 and endBase <= 64: # Try converting number from startBase to base-10 # Test to see if number is not zero start = number title = "Base-{} to Base-{}".format(startBase, endBase) description = "`{} --> {}`".format(start, number) # Check if number is not zero; Convert it if number not in ["0", 0]: number = convert(number, startBase, endBase) # Check if number is None; Invalid number for a base if number == None: embed = getErrorMessage(self._convert, Code.BASE_MISMATCH) # Number is not None; Valid base else: # Return number description = "`{} --> {}`".format(start, number) embed = discord.Embed( title=title, description=description, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Number is zero; Just send that else: embed = discord.Embed( title=title, description=description, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Bases were not within range else: embed = getErrorMessage(self._convert, Code.BASE_OUT_OF_RANGE) # Bases were not numbers else: embed = getErrorMessage(self._convert, Code.INVALID_BASE) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def base64(self, message, parameters): """Encodes or decodes text to or from base64.\n Parameters: conversionType: Whether to encode or decode text.\n text: The text to encode or decode.\n """ # Check for not enough parameters if len(parameters) < self._base64.getMinParameters(): embed = getErrorMessage(self._base64, Code.NOT_ENOUGH_PARAMETERS) # There were enough parameters else: # Conversion type is first parameter; Text is all parameters after conversionType = parameters[0] text = " ".join(parameters[1:]) # Conversion is Encode if conversionType in self._base64.getAcceptedParameter( "conversion", "encode").getAlternatives(): converted = base64.b64encode(text.encode()).decode() encoded = True embed = discord.Embed( title="`{}` {} Base64".format( text if len(text) < 180 else "[text is greater than 200 characters]", "encoded to" if encoded else "decoded from"), description=converted, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Conversion is Decode elif conversionType in self._base64.getAcceptedParameter( "conversion", "decode").getAlternatives(): converted = base64.b64decode(text.encode()).decode() encoded = False embed = discord.Embed( title="`{}` {} Base64".format( text if len(text) < 180 else "[text is greater than 200 characters]", "encoded to" if encoded else "decoded from"), description=converted, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Conversion is Invalid else: embed = getErrorMessage(self._base64, Code.INVALID_PARAMETER) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def morse(self, message, parameters): """Turns text into/from morse code """ # Check for not enough parameters if len(parameters) < self._morse.getMinParameters(): embed = getErrorMessage(self._morse, Code.NOT_ENOUGH_PARAMETERS) # There were the proper amount of parameters else: conversion = parameters[0] text = " ".join(parameters[1:]) # Check if the conversion is valid valid = True if conversion in self._morse.getAcceptedParameter( "conversion", "encode").getAlternatives(): conversion = "encode" elif conversion in self._morse.getAcceptedParameter( "conversion", "decode").getAlternatives(): conversion = "decode" # Conversion is invalid else: embed = getErrorMessage(self._morse, Code.INVALID_PARAMETER) valid = False if valid: response = await loop.run_in_executor( None, requests.get, Code.MORSE_API_CALL.format(conversion, text)) response = response.json() # Check if the API call was a success if response["success"]: value = response["value"] else: value = response["error"] embed = discord.Embed( title="{}".format("Text to Morse" if conversion == "encode" else "Morse To Text") if response["success"] else "Failed to convert", description="`{}`".format(value), colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def qrCode(self, message, parameters): """Turns data into a QR code. """ # Check for not enough parameters if len(parameters) < self._qrCode.getMinParameters(): embed = getErrorMessage(self._qrCode, Code.NOT_ENOUGH_PARAMETERS) # There were the proper amount of parameters else: data = " ".join(parameters) # The size should be a function of the data's length # Use this --> size = 10(length // 20) + 200 size = 10 * (len(data) // 20) + 200 embed = discord.Embed( title=" ", description=" ", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image( url=Code.QR_API_CALL.format(size, data.replace(" ", "+"))) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Parsing # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def on_message(self, message): """Parses a message and runs a Code Category command if it can Parameters: message: The Discord Message to parse.\n """ # Make sure message starts with the prefix if await Server.startsWithPrefix( message.guild, message.content) and not message.author.bot: # Split up into command and parameters if possible command, parameters = Category.parseText( await Server.getPrefixes(message.guild), message.content) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Iterate through commands for cmd in self.getCommands(): if command in cmd.getAlternatives(): async with message.channel.typing(): # Run the command but don't try running others await self.run(message, cmd, cmd.getCommand(), message, parameters) break
class Image(Category): # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Class Fields # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # IMGUR_ALBUM_API = "https://api.imgur.com/3/album" IMGUR_ALBUM_GET_API = "https://api.imgur.com/3/album/{}" IMGUR_IMAGE_API = "https://api.imgur.com/3/image" IMGUR_ALBUM_URL = "https://imgur.com/a/{}" IMGUR_IMAGE_URL = "https://i.imgur.com/{}" AVATAR_LIST = "https://api.adorable.io/avatars/list" AVATAR_API = "https://api.adorable.io/avatars/face/{}/{}/{}/{}" DOG_API = "https://dog.ceo/api/breeds/image/random" CAT_API = "https://api.thecatapi.com/v1/images/search" ROBOHASH_API = "https://robohash.org/{}" TIMCHEN_API = "https://timchen.tk/api" NASA_RANDOM = "https://images-api.nasa.gov/search?media_type=image&year_start=1960" NASA_SEARCH = "https://images-api.nasa.gov/search?q={}&media_type=image" DESCRIPTION_THRESHOLD = 2000 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Errors # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # NO_GIFS_FOUND = "NO_GIFS_FOUND" NO_IMAGE = "NO_IMAGE" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Constructor # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __init__(self, client): super().__init__( client, "Image", description="Anything having to do with images is here.", embed_color=0x800080, locally_inactive_error=Server.getInactiveError, globally_inactive_error=OmegaPsi.getInactiveError, locally_active_check=Server.isCommandActive, globally_active_check=OmegaPsi.isCommandActive) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Commands self._gif = Command( commandDict={ "alternatives": ["gif", "giphy", "g"], "info": "Sends a random gif from Giphy.", "parameters": { "keywords": { "optional": True, "info": "The keywords to search for in Giphy." } }, "errors": { Image.NO_GIFS_FOUND: { "messages": [ "Hmmm. No gifs were found with those keywords. Perhaps broaden your search?" ] } }, "command": self.gif }) self._imgur = Command( commandDict={ "alternatives": ["imgur"], "info": "Allows you to upload an image to an anonymous imgur album.", "parameters": { "image": { "info": "The URL or image file of the image to upload.", "optional": False, "accepted": { "me": { "alternatives": ["me", "self"], "info": "Gets your album of images from imgur." } } } }, "errors": { Image.NOT_ENOUGH_PARAMETERS: { "messages": [ "In order to upload an image to imgur, you need the image." ] } }, "command": self.imgur }) self._dog = Command( commandDict={ "alternatives": ["dog", "doggy", "dogMe"], "info": "Sends a random picture of a random dog from the internet.", "errors": { Image.TOO_MANY_PARAMETERS: { "messages": [ "In order to get a picture of a dog, you don't need any parameters" ] } }, "command": self.dog }) self._cat = Command( commandDict={ "alternatives": ["cat", "kitty", "catMe"], "info": "Sends a random picture of a random cat from the internet.", "errors": { Image.TOO_MANY_PARAMETERS: { "messages": [ "In order to get a picture of a cat, you don't need any parameters." ] } }, "command": self.cat }) self._avatar = Command( commandDict={ "alternatives": ["avatar", "avatarMe"], "info": "Sends a random cute avatar.", "errors": { Image.TOO_MANY_PARAMETERS: { "messages": [ "In order to get an avatar, you don't need any parameters." ] } }, "command": self.avatar }) self._robohash = Command( commandDict={ "alternatives": ["robohash", "robo"], "info": "Sends a robohash avatar based off the text you enter.", "parameters": { "content": { "info": "The text to use to generate the robohash avatar.", "optional": True, "accepted": { "random": { "alternatives": ["random", "surprise", "surpriseMe"], "info": "Allows you to have a completely random robohash to be sent." } } } }, "command": self.robohash }) self._timchen = Command( commandDict={ "alternatives": ["timchen", "tim", "chen", "t"], "info": { "text": "Sends a random picture of Timchen (a Repl.it moderator) using an API made by {}", "hyperlink": "https://repl.it/@mat1", "hyperlink_text": "mat#6207" }, "errors": { Image.TOO_MANY_PARAMETERS: { "messages": [ "In order to get a picture of Timchen, you don't need any parameters." ] } }, "command": self.timchen }) self._nasaImage = Command( commandDict={ "alternatives": [ "nasa", "NASA", "nasaImage", "NASAImage", "nasaImg", "NASAImg" ], "info": "Gives you a random NASA image given a search term or no search term.", "parameters": { "term": { "info": "The term to search for a NASA image.", "optional": True } }, "errors": { Image.NO_IMAGE: { "messages": [ "There were no images matching that search. Try again or broaden your search term." ] } }, "command": self.nasaImage }) self.setCommands([ self._gif, self._imgur, self._dog, self._cat, self._avatar, self._robohash, self._timchen, self._nasaImage ]) self._scrollEmbeds = {} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Command Methods # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def gif(self, message, parameters): """Returns a gif from giphy. """ keywords = " ".join(parameters) # Get data involving gifs from Giphy if keywords == "random": gifData = await loop.run_in_executor( None, requests.get, os.environ["GIPHY_RANDOM_API_URL"]) gifData = gifData.json() result = gifData["data"]["embed_url"] else: gifsData = await loop.run_in_executor( None, requests.get, os.environ["GIPHY_SEARCH_API_URL"].format( keywords.replace(" ", "+"))) gifsData = gifsData.json() # Return random embed url if len(gifsData) > 0: gifData = choose(gifsData["data"]) result = gifData["embed_url"] else: result = getErrorMessage(self._gif, Image.NO_GIFS_FOUND) if type(result) == discord.Embed: await sendMessage( self.client, message, embed=result.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) else: await sendMessage(self.client, message, message=result) async def imgur(self, message, parameters): """ """ attachments = message.attachments canScroll = False # Check for no attachments and no parameters if len(attachments) == 0 and len(parameters) == 0: embed = getErrorMessage(self._imgur, Image.NOT_ENOUGH_PARAMETERS) # There was at least one of either else: # Check if the first parameter is in the accepted for getting the album to the images # and getting a scroll embed to show the images try: if parameters[0] in self._imgur.getAcceptedParameter( "image", "me").getAlternatives(): me = True else: me = False except: me = False # Get user's imgur album ID album = await User.getImgurAlbum(message.author) albumHash = album["hash"] albumId = album["id"] # See if user does not have an album attached to them fields = [] fieldValue = "" if not albumId: response = await loop.run_in_executor( None, partial( requests.post, Image.IMGUR_ALBUM_API, data={ "title": "{}#{}".format(message.author.name, message.author.discriminator), "description": "An anonymous imgur album made for the above Discord User" }, headers={ "Authorization": "Client-ID {}".format(os.environ["IMGUR_API_KEY"]) })) response = response.json() # See if album creation failed if response["status"] != 200: error = response["data"]["error"] + "\n" # Add the error to the result field if len(fieldValue) + len( error) > OmegaPsi.MESSAGE_THRESHOLD: fields.append(fieldValue) fieldValue = "" fieldValue += error # Album creation did not fail else: success = "Anonymous Album Created at [this link]({}).\n".format( Image.IMGUR_ALBUM_URL.format(response["data"]["id"])) # Add the success message to the result field if len(fieldValue) + len( success) > OmegaPsi.MESSAGE_THRESHOLD: fields.append(fieldValue) fieldValue = "" fieldValue += success # Set the user's imgur album albumHash = response["data"]["deletehash"] albumId = response["data"]["id"] await User.setImgurAlbum(message.author, { "hash": albumHash, "id": albumId }) # Not getting their album and images; Adding one if not me: # Get url for each attachment for attachment in range(len(attachments)): attachments[attachment] = attachments[attachment].url attachments += parameters # Iterate through attachments for attachment in attachments: # Upload image response = await loop.run_in_executor( None, partial(requests.post, Image.IMGUR_IMAGE_API, data={ "image": attachment, "album": albumHash }, headers={ "Authorization": "Client-ID {}".format( os.environ["IMGUR_API_KEY"]) })) print(response.content) response = response.json() # See if image upload failed if response["status"] != 200: error = response["data"]["error"] + "\n" # Add the error to the result field if len(fieldValue) + len( error) > OmegaPsi.MESSAGE_THRESHOLD: fields.append(fieldValue) fieldValue = "" fieldValue += error # Image upload did not fail else: success = "Anonymous Image Uploaded and Added to your album. [Here]({}) is the direct link to the image.\n".format( Image.IMGUR_IMAGE_URL.format( response["data"]["id"])) # Add the success message to the result field if len(fieldValue) + len( success) > OmegaPsi.MESSAGE_THRESHOLD: fields.append(fieldValue) fieldValue = "" fieldValue += success # Add the trailing result field if len(fieldValue) > 0: fields.append(fieldValue) # Create embed embed = discord.Embed( title="Results {}".format("({} / {})".format( 1, len(fields)) if len(fields) > 1 else ""), description=fields[0], colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color) # Add all the fields to the embed count = 1 for field in fields[1:]: count += 1 embed.add_field( name="Results {}".format("({} / {})".format( count, len(fields)) if len(fields) > 1 else ""), value=field, inline=False) # Getting the author's images else: # Get the list of images album = await loop.run_in_executor( None, partial( requests.get, Image.IMGUR_ALBUM_GET_API.format(albumId), headers={"Authorization": "Client-ID f473d8889fc2daf"})) album = album.json() # Create the first embed embed = discord.Embed( title="Image {}".format( "({} / {})".format(1, len(album["data"]["images"])) if len(album["data"]["images"]) > 1 else ""), description=album["data"]["description"], colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color, url=album["data"]["link"]).set_image( url=None if len(album["data"]["images"]) == 0 else album["data"]["images"][0]["link"]).set_author( name=album["data"]["title"], icon_url=message.author.avatar_url if album["data"]["cover"] == None else Image.IMGUR_IMAGE_URL.format( album["data"]["cover"])) # Create the scrolling embed self._scrollEmbeds[str(message.author.id)] = { "message": None, "images": album["data"]["images"], "value": 0, "min": 0, "max": len(album["data"]["images"]) - 1 } canScroll = len(album["data"]["images"]) > 0 msg = await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) if canScroll: for reaction in reactions: await msg.add_reaction(reaction) self._scrollEmbeds[str(message.author.id)]["message"] = msg async def dog(self, message, parameters): """Returns a random dog from the internet """ # Check for too many parameters if len(parameters) > self._dog.getMaxParameters(): embed = getErrorMessage(self._dog, Image.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: result = await loop.run_in_executor(None, requests.get, Image.DOG_API) result = result.json() embed = discord.Embed( title="Dog from the internet", description=" ", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image(url=result["message"]) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def cat(self, message, parameters): """Returns a random cat from the internet. """ # Check for too many parameters if len(parameters) > self._cat.getMaxParameters(): embed = getErrorMessage(self._cat, Image.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: result = await loop.run_in_executor( None, partial(requests.get, Image.CAT_API, headers={"x-api-key": os.environ["CAT_API_KEY"]})) result = result.json() embed = discord.Embed( title="Cat from the internet", description=" ", colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image(url=result[0]["url"]) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def avatar(self, message, parameters): """Returns a random cute avatar that can be used as a placeholder. Parameters: parameters (list): The parameters that detect for too many parameters. """ # Check for too many parameters if len(parameters) > self._avatar.getMaxParameters(): embed = getErrorMessage(self._avatar, Image.TOO_MANY_PARAMETERS) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) # There were the proper amount of parameters else: # Get list of face features faceValues = await loop.run_in_executor(None, requests.get, Image.AVATAR_LIST) faceValues = faceValues.json()["face"] # Choose random eyes, nose, mouth, and color eyes = choose(faceValues["eyes"]) nose = choose(faceValues["nose"]) mouth = choose(faceValues["mouth"]) color = hex(randint(0, 16777215))[2:].rjust(6, "0") # Load image image = await loop.run_in_executor( None, loadImageFromUrl, Image.AVATAR_API.format(eyes, nose, mouth, color)) # Save image temporarily avatarFile = "{}_{}_{}_{}.png".format(eyes, nose, mouth, color) pygame.image.save(image, avatarFile) # Send file then delete image await sendMessage(self.client, message, filename=avatarFile) os.remove(avatarFile) async def robohash(self, message, parameters): """Sends a random robohash avatar or a generated one based off of the content. """ # Generate personal robohash if content is empty content = " ".join(parameters) if len(content) == 0: content = "{}-{}".format(message.author.name, message.author.discriminator) # Generate totally random robohash if content is random elif content in self._robohash.getAcceptedParameter( "content", "random").getAlternatives(): content = generateRandomString() # Load image image = await loop.run_in_executor(None, loadImageFromUrl, Image.ROBOHASH_API.format(content)) # Save image temporarily avatarFile = "{}.png".format(content) pygame.image.save(image, avatarFile) # Send the file and then delete it await sendMessage(self.client, message, filename=avatarFile) os.remove(avatarFile) async def timchen(self, message, parameters): """Returns a random picture of Timchen with the caption. """ # Check for too many parameters if len(parameters) > self._timchen.getMaxParameters(): embed = getErrorMessage(self._timchen, Image.TOO_MANY_PARAMETERS) # There were the proper amount of parameters else: # Get a random image timchenData = await timchen.get_random() embed = discord.Embed( title="Timchen!", description=capitalizeSentences(timchenData.description), colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image(url=timchenData.url) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) async def nasaImage(self, message, parameters): """Returns an image from NASA. """ keywords = " ".join(parameters) # Get data involving NASA images if keywords == "random": imageData = await loop.run_in_executor(None, requests.get, Image.NASA_RANDOM) else: imageData = await loop.run_in_executor( None, requests.get, Image.NASA_SEARCH.format(keywords.replace(" ", "+"))) imageData = imageData.json() # Check if there are no images if len(imageData["collection"]["items"]) == 0: embed = getErrorMessage(self._nasaImage, Image.NO_IMAGE) # There are images else: # Choose random item from collection item = choose(imageData["collection"]["items"]) # Get href from item imageLink = item["links"][0]["href"] # Make sure description is less than 2000 characters if len(item["data"][0] ["description"]) < Image.DESCRIPTION_THRESHOLD: description = item["data"][0]["description"] else: description = item["data"][0][ "description"][:Image.DESCRIPTION_THRESHOLD] + "[...]" embed = discord.Embed( title=item["data"][0]["title"], description=description, colour=self.getEmbedColor() if message.guild == None else message.author.top_role.color).set_image(url=imageLink) await sendMessage( self.client, message, embed=embed.set_footer(text="Requested by {}#{}".format( message.author.name, message.author.discriminator), icon_url=message.author.avatar_url)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Parsing # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def on_message(self, message): """Parses a message and runs an Image Category command if it can.\n message - The Discord Message to parse.\n """ # Make sure message starts with the prefix if await Server.startsWithPrefix( message.guild, message.content) and not message.author.bot: # Split up into command and parameters if possible command, parameters = Category.parseText( await Server.getPrefixes(message.guild), message.content) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Iterate through commands for cmd in self.getCommands(): if command in cmd.getAlternatives(): async with message.channel.typing(): # Run the command but don't try running others await self.run(message, cmd, cmd.getCommand(), message, parameters) break # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Reactions # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # async def manage_scrolling(self, reaction, member): """Manages the scrolling of any scroll embeds """ # Check for message ID in scrollable embeds memberId = str(member.id) if memberId in self._scrollEmbeds: initial = self._scrollEmbeds[memberId]["value"] # Rewind reaction was added; Move to first field if str(reaction) == "⏪": self._scrollEmbeds[memberId]["value"] = self._scrollEmbeds[ memberId]["min"] # Fast Forward reaction was added; Move to last field elif str(reaction) == "⏩": self._scrollEmbeds[memberId]["value"] = self._scrollEmbeds[ memberId]["max"] # Arrow Left reaction was added; Move field left elif str(reaction) == "⬅": self._scrollEmbeds[memberId]["value"] -= 1 if self._scrollEmbeds[memberId]["value"] < self._scrollEmbeds[ memberId]["min"]: self._scrollEmbeds[memberId]["value"] = self._scrollEmbeds[ memberId]["min"] # Arrow Right reaction was added; Move field right elif str(reaction) == "➡": self._scrollEmbeds[memberId]["value"] += 1 if self._scrollEmbeds[memberId]["value"] > self._scrollEmbeds[ memberId]["max"]: self._scrollEmbeds[memberId]["value"] = self._scrollEmbeds[ memberId]["max"] # Update the scroll embed if self._scrollEmbeds[memberId][ "value"] != initial and reaction.message.id == self._scrollEmbeds[ memberId]["message"].id: value = self._scrollEmbeds[memberId]["value"] # Get the image Id at the current value image = self._scrollEmbeds[memberId]["images"][value] # Update the embed currentEmbed = self._scrollEmbeds[str( member.id)]["message"].embeds[0] currentEmbed.title = "Image {}".format("({} / {})".format( value + 1, self._scrollEmbeds[str(member.id)]["max"] + 1 ) if self._scrollEmbeds[str(member.id)]["max"] > 0 else "") currentEmbed.set_image(url=image["link"]) await self._scrollEmbeds[str(member.id) ]["message"].edit(embed=currentEmbed) async def on_reaction_add(self, reaction, member): """Determines which reaction was added to a message. Only reactions right now are :arrow_left: which tells the embed to scroll back a field. :arrow_right: which tells the embed to scroll forward a field. :rewind: which tells the embed to go back to the beginning. :fast_forward: which tells the embed to go to the end. """ await self.manage_scrolling(reaction, member) async def on_reaction_remove(self, reaction, member): """Determines which reaction was removed from a message. Only reactions right now are :arrow_left: which tells the embed to scroll back a field. :arrow_right: which tells the embed to scroll forward a field. :rewind: which tells the embed to go back to the beginning. :fast_forward: which tells the embed to go to the end. """ await self.manage_scrolling(reaction, member)