Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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)