Ejemplo n.º 1
0
async def get_category_and_embed(ctx):
    current_cat: DMCategory = await DMCategory.from_ctx(ctx)
    embed = create_default_embed(ctx)
    if current_cat is None:
        embed.title = f'{ctx.author.display_name} does not have a DM Category!'
        embed.description = f'Create a DM Category with `{ctx.prefix}dm setup`'
    return current_cat, embed, (current_cat is not None)
Ejemplo n.º 2
0
    async def change_prefix(self, ctx, to_change: str = None):
        """
        Changes the prefix for the current guild

        Can only be ran in a guild. If no prefix is specified, will show the current prefix.
        """
        embed = create_default_embed(ctx)
        guild_id = str(ctx.guild.id)
        if to_change is None:
            if guild_id in self.bot.prefixes:
                prefix = self.bot.prefixes.get(guild_id, self.bot.prefix)
            else:
                dbsearch = await self.bot.mdb['prefixes'].find_one({'guild_id': guild_id})
                if dbsearch is not None:
                    prefix = dbsearch.get('prefix', self.bot.prefix)
                else:
                    prefix = self.bot.prefix
                self.bot.prefixes[guild_id] = prefix
            embed.title = f'Prefix for {ctx.guild.name}'
            embed.description = f'Current Prefix: `{prefix}`'
            return await ctx.send(embed=embed)
        else:
            await ctx.bot.mdb['prefixes'].update_one({'guild_id': guild_id},
                                                     {'$set': {'prefix': to_change}}, upsert=True)
            ctx.bot.prefixes[guild_id] = to_change
            embed.title = f'Prefix updated for {ctx.guild.name}!'
            embed.description = f'Server prefix updated to `{to_change}`'
            return await ctx.send(embed=embed)
Ejemplo n.º 3
0
    async def new_sheet(self, ctx, *, content: str):
        """
        Adds a sheet to be approved
        """
        embed = create_default_embed(ctx)
        embed.title = f'Sheet Approval - {ctx.author.display_name}'
        embed.description = content

        if '(url)' in content:
            return await ctx.author.send(
                'You must include your *actual* sheet URL in the command, not `(url)`'
            )

        # If not character-submission or FrogBot dev
        if (ctx.channel.id != self.bot.personal_server['sheet_channel']
            ) and not (ctx.guild.id == 755202524859859004):
            return await ctx.send(
                'This channel is not valid for submitting sheets.')

        msg = await ctx.send(embed=embed)

        new_sheet = ToBeApproved(message_id=msg.id,
                                 approvals=[],
                                 channel_id=ctx.channel.id,
                                 owner_id=ctx.author.id)
        await self.bot.mdb['to_approve'].insert_one(new_sheet.to_dict())
Ejemplo n.º 4
0
    async def debug(self, ctx):
        """
        Debugging commands for FrogBot
        """
        embed = create_default_embed(ctx)
        embed.title = 'FrogBot Debug'
        # -- Calculate Values --
        proc = psutil.Process(os.getpid())
        cpu = psutil.cpu_percent()
        mem = psutil.virtual_memory()
        mem_used = proc.memory_full_info().uss
        if self._command_count is None:
            self._command_count = len([
                command for cog in self.bot.cogs
                for command in self.bot.get_cog(cog).walk_commands()
            ])
        command_count = self._command_count
        # -- Add fields ---
        embed.add_field(name='Memory Usage',
                        value=f'{round((mem_used / 1000000), 2)} '
                        f'/ {round((mem.total / 1000000), 2)} MB '
                        f'({round(100 * (mem_used / mem.total), 2)}%)')
        embed.add_field(name='CPU Usage', value=f'{round(cpu, 2)}%')
        embed.add_field(name='Commands',
                        value=f'{command_count} total commands loaded.')

        await ctx.send(embed=embed)
Ejemplo n.º 5
0
    async def server_info(self, ctx):
        """
        Displays information about the current server.
        """
        embed = create_default_embed(ctx)
        guild = ctx.guild
        embed.title = f'{guild.name} - Server Information'
        general_info = f'**ID:** {guild.id}\n' \
                       f'**Owner:** {guild.owner.mention}\n' \
                       f'Created: {guild.created_at.strftime(DATE_FORMAT)}'
        embed.add_field(name='General Info', value=general_info, inline=False)
        emoji_x = 0
        emojis = []
        for emoji in guild.emojis:
            emoji_x += 1
            if emoji_x >= 10:
                break
            emojis.append(emoji)
        emoji_info = f'{len(guild.emojis)} emoji{"s" if len(guild.emojis) != 1 else ""}\n' \
                     f'{",".join([str(e) for e in emojis])} {"..." if emoji_x >= 10 else ""}'
        embed.add_field(name='Emojis', value=emoji_info, inline=False)
        bots = [member for member in guild.members if member.bot]
        member_stats = f'{guild.member_count - len(bots)} members ({len(bots)} bots)'
        embed.add_field(name='Member Info', value=member_stats)
        channels = f'{len([c for c in guild.categories])} categories, ' \
                   f'{len([c for c in guild.channels if isinstance(c, discord.TextChannel)])} text channels, ' \
                   f'{len([c for c in guild.channels if isinstance(c, discord.VoiceChannel)])} voice channels.'
        embed.add_field(name='Channel Info', value=channels)
        embed.set_thumbnail(url=str(guild.icon_url))

        return await ctx.send(embed=embed, allowed_mentions=None)
Ejemplo n.º 6
0
 async def format_page(self, menu, entries):
     offset = menu.current_page * self.per_page
     embed = create_default_embed(self.context)
     message = '\n'.join([f':white_small_square: `{cc.name}`' for _, cc in enumerate(entries, start=offset)])
     if message == '':
         message = 'No custom commands found for this server'
     embed.description = '**Current Custom Commands for this Server**:\n' + message
     return embed
Ejemplo n.º 7
0
 async def send_command_help(self, command):
     to_send = self.get_destination()
     embed = create_default_embed(self.context)
     embed.title = f'FrogBot Help - `{self.get_command_signature(command).strip()}`'
     embed.description = command.help or 'No help specified.'
     embed.set_footer(text=f'An underlined command has subcommands.\n'
                           f'See {self.clean_prefix}help <command name> for more details '
                           f'on individual commands')
     await to_send.send(embed=embed)
Ejemplo n.º 8
0
 async def source(self, ctx):
     """
     Returns the link to the source code of the bot.
     """
     embed = create_default_embed(ctx)
     embed.title = 'FrogBot Source'
     embed.description = '[Click here for the Source Code.](https://github.com/1drturtle/FrogBot)'
     embed.set_thumbnail(url=str(self.bot.user.avatar_url))
     await ctx.send(embed=embed)
Ejemplo n.º 9
0
 async def avatar(self, ctx, who: discord.Member = None):
     """
     Gives you the avatar of whoever you specify, or yourself if you don't specify anyone.
     """
     embed = create_default_embed(ctx)
     if not who:
         who = ctx.author
     embed.title = f'Avatar for {who.display_name}'
     embed.set_image(url=str(who.avatar_url_as(format='png')))
     return await ctx.send(embed=embed)
Ejemplo n.º 10
0
 async def format_page(self, menu, entries):
     offset = menu.current_page * self.per_page
     embed = create_default_embed(self.context)
     embed.title = self.embed_title
     embed.description = self.embed_desc
     embed.set_footer(text=self.embed_footer)
     message = '\n'.join([command for _, command in enumerate(entries, start=offset)])
     message = message + f'\n\nPage {menu.current_page+1}/{self.get_max_pages()}'
     embed.add_field(name='Commands', value=message)
     return embed
Ejemplo n.º 11
0
 async def format_page(self, menu, entries):
     offset = menu.current_page * self.per_page
     embed = create_default_embed(self.context)
     embed.title = self.embed_title
     embed.description = self.embed_desc
     # Entries = List[tuple(Cog, Commands)]
     for _, item in enumerate(entries, start=offset):
         command_list = generate_command_names(item[1], short_doc=True)
         output = '\n'.join(command_list)
         embed.add_field(name=item[0].qualified_name, value=output, inline=False)
     embed.set_footer(text=f'Page {menu.current_page+1}/{self.get_max_pages()}\n'+self.embed_footer)
     return embed
Ejemplo n.º 12
0
 async def send_invite(self, ctx):
     """
     Sends a link to invite FrogBot
     """
     embed = create_default_embed(ctx)
     embed.title = 'FrogBot Invite Link'
     embed.description = '[Click here to invite FrogBot to your server!]' \
                         '(https://discord.com/api/oauth2/authorize' \
                         '?client_id=717467616700006482' \
                         '&permissions=470117462' \
                         '&scope=bot)'
     await ctx.send(embed=embed)
Ejemplo n.º 13
0
 async def raw_message(self, ctx, message_id: int):
     """
     Returns the escaped markdown for a message. The message must be in the same channel as this command.
     """
     embed = create_default_embed(ctx)
     try:
         message = await ctx.channel.fetch_message(message_id)
     except discord.NotFound:
         await ctx.send(f'Could not find the message with ID `{message_id}`'
                        )
     embed.title = f'Escaped Markdown for Message with ID `{message_id}`'
     embed.description = discord.utils.escape_markdown(message.content)
     await ctx.send(embed=embed)
Ejemplo n.º 14
0
    async def emoji_info(self, ctx, emoji_to_parse: discord.Emoji):
        """
        Returns custom emoji information.
        """
        embed = create_default_embed(ctx)
        embed.title = f'Emoji - :{emoji_to_parse.name}:'

        embed.add_field(name='Guild', value=emoji_to_parse.guild.name)
        embed.add_field(name='ID', value=emoji_to_parse.guild.id)

        embed.set_image(url=str(emoji_to_parse.url))

        await ctx.send(embed=embed)
Ejemplo n.º 15
0
    async def send_cog_help(self, cog):
        to_send = self.get_destination()
        embed = create_default_embed(self.context)
        title = f'FrogBot Help - `{cog.qualified_name}`'.strip()
        footer = f'An underlined command has sub-commands.\n' \
                 f'See {self.clean_prefix}help <command name> for more details on individual commands.'
        command_list = await self.filter_commands(cog.get_commands(), sort=True)
        embed.description = cog.description or 'No description specified.'
        out = generate_command_names(command_list)

        source = HelpCogMenu(data=out, ctx=self.context, embed_title=title, embed_footer=footer,
                             embed_desc=cog.description or 'No description specified.')
        command_menu = menus.MenuPages(source=source, clear_reactions_after=True)
        await command_menu.start(self.context, channel=to_send)
Ejemplo n.º 16
0
 async def uptime(self, ctx):
     """
     Displays the current uptime of the bot.
     """
     embed = create_default_embed(ctx)
     embed.title = 'Poddo Uptime'
     bot_up = time_to_readable(self.bot.uptime)
     embed.add_field(name='Bot Uptime', value=f'{bot_up}')
     if ctx.bot.is_ready():
         embed.add_field(
             name='Ready Uptime',
             value=
             f'{time_to_readable(datetime.utcnow() - self.bot.ready_time)}')
     return await ctx.send(embed=embed)
Ejemplo n.º 17
0
    async def show_perms(self, ctx, who: discord.Member = None):
        """
        Shows the permissions for the user specified in the current channel.
        Will show permissions for yourself if nobody is specified.
        """
        if who is None:
            who = ctx.author
        embed = create_default_embed(ctx)
        embed.title = f'Permissions for {who.display_name}'
        yes, no = "\U00002705", "\U0001f6ab"
        out = ''
        for perm, value in who.permissions_in(ctx.channel):
            out += f'{yes if value else no} | {perm.replace("_", " ").title()}\n'
        embed.description = out

        await ctx.send(embed=embed)
Ejemplo n.º 18
0
    async def hexcolor(self, ctx, *, color: str):
        """
        Takes a color name and converts it to a hex code.
        For possible color options, see [this link](https://gist.github.com/Soheab/d9cf3f40e34037cfa544f464fc7d919e)
        """
        embed = create_default_embed(ctx)
        color_converter = commands.ColourConverter()
        try:
            color: discord.Colour = await color_converter.convert(ctx, color)
        except commands.BadArgument:
            return await ctx.send(
                'You have provided an invalid color. See the link in the help page for a list of '
                'possible colors.')
        embed.title = str(hex(color.value))
        embed.colour = color

        await ctx.send(embed=embed)
Ejemplo n.º 19
0
    async def info(self, ctx):
        """
        Displays some information about the bot.
        """
        embed = create_default_embed(ctx)
        embed.title = 'FrogBot Information'
        embed.description = 'Bot built by Dr Turtle#1771 made for D&D and personal servers!'
        members = sum([guild.member_count for guild in self.bot.guilds])
        embed.add_field(name='Guilds', value=f'{len(self.bot.guilds)}')
        embed.add_field(name='Members', value=f'{members}')
        embed.add_field(
            name='Concerns',
            value=
            'Do you have any concerns/privacy issues/security issues?\nContact me by'
            ' [joining the support server!](https://discord.gg/nNutJ8PyFu)',
            inline=False)
        embed.url = 'https://github.com/1drturtle/FrogBot'

        await ctx.send(embed=embed)
Ejemplo n.º 20
0
    async def dm_setup(self, ctx):
        """
        Creates a new DM Category.

        Will also create one channel for you, this is supposed to be your hub channel, but you can use it for whatever.
        """
        try:
            new_category = await DMCategory.new(self.bot, ctx.guild,
                                                ctx.author)
        except CategoryExists:
            return await ctx.send(
                f'You already have a DM Category in this server. If this is an error, '
                f'run `{ctx.prefix}dm delete` and then run this command again.'
            )
        embed = create_default_embed(ctx)
        embed.title = f'{ctx.author.display_name} creates their DM Category!'
        embed.description = f'Your DM Category has been created.\nThe default channel is ' \
                            f'<#{new_category.channels[0].channel.id}>'
        return await ctx.send(embed=embed)
Ejemplo n.º 21
0
    async def prompt(self,
                     title: str,
                     description: str,
                     timeout=30,
                     sendable=None) -> str:
        """
        Prompts the Context author for a question, and returns the result. Returns None if they do not respond.
        :param str title: The title of the prompt
        :param str description: The description of the prompt
        :param int timeout:
        :param discord.Messagable sendable: Where to send the message. (Optional, defaults to context channel.)
        :return: The response, or None
        :rtype: str or None
        """
        embed = create_default_embed(self)
        embed.title = title or 'Question Prompt'

        if not description:
            raise Exception('Missing required argument Description on prompt.')
        embed.description = description

        if sendable:
            question = await sendable.send(embed=embed)
        else:
            question = await self.channel.send(embed=embed)

        def check(msg: discord.Message):
            return msg.author.id == self.author.id and msg.channel.id == self.channel.id

        try:
            result = await self.bot.wait_for('message',
                                             check=check,
                                             timeout=timeout)
        except asyncio.TimeoutError:
            return None

        content = result.content
        await try_delete(question)
        await try_delete(result)

        return content or None
Ejemplo n.º 22
0
    async def personal_server(self, ctx):
        """
        Base command for personal server commands.

        Displays information about currently set personal server.
        """
        if self.bot.personal_server['server_id'] is None:
            return await ctx.send('Personal Server not set, no information available.')
        personal_server = self.bot.get_guild(self.bot.personal_server['server_id'])
        sheet_channel = channel_id_to_link(personal_server.get_channel(self.bot.personal_server['sheet_channel'])) \
            if self.bot.personal_server['sheet_channel'] is not None else 'Not set.'
        general_channel = channel_id_to_link(personal_server.get_channel(self.bot.personal_server['general_channel'])) \
            if self.bot.personal_server['general_channel'] is not None else 'Not set.'

        embed = create_default_embed(ctx)
        embed.title = 'FrogBot Personal Server Information'
        embed.add_field(name='Server Info', value=f'Server ID: {personal_server.id}\n'
                                                  f'Server Name: {personal_server.name}')
        embed.add_field(name='Channel Info', value=f'Sheet Channel: {sheet_channel}\n'
                                                   f'General Channel: {general_channel}')
        await ctx.send(embed=embed)
Ejemplo n.º 23
0
    async def remove_sheets(self, ctx):
        """
        Removes deleted sheets from database. Run every once and a while.
        """
        db = self.bot.mdb['to_approve']

        embed = create_default_embed(ctx)
        embed.title = f'Pruning Old Sheets from Database.'
        all_sheets = await db.find().to_list(None)
        count = 0
        for sheet in all_sheets:
            sheet.pop('_id')
            sheet = ToBeApproved.from_dict(sheet)
            channel = ctx.guild.get_channel(sheet.channel_id)
            if channel is None:
                continue
            try:
                await channel.fetch_message(sheet.message_id)
            except discord.NotFound:
                count += 1
                await db.delete_one({'message_id': sheet.message_id})
        embed.description = f'Pruned {count} Sheet{"s" if count != 1 else ""} from the DB.'
        await ctx.send(embed=embed)
Ejemplo n.º 24
0
    async def member_info(self, ctx, who: discord.Member = None):
        """
        Shows information about a member in this server.
        """
        if who is None:
            who = ctx.author
        embed = create_default_embed(ctx)
        badges = ''
        if who.id == self.bot.owner:
            badges += f'{BADGE_EMOJIS["bot_owner"]} '
        if who.id == ctx.guild.owner.id:
            badges += f'{BADGE_EMOJIS["server_owner"]} '
        support_server = self.bot.get_guild(SUPPORT_SERVER_ID)
        if support_server is not None and member_in_guild(who.id, support_server):
            badges += f'{BADGE_EMOJIS["support_server"]}'

        embed.title = f'Member Information - {who.display_name} {badges}'

        # -- Basics --
        embed.add_field(name='Name', value=f'{who.mention}')
        embed.add_field(name='Username', value=f'{who.name}#{who.discriminator}')
        embed.add_field(name='ID', value=f'{who.id}')

        # -- Roles --
        embed.add_field(name='Roles', value=f'{len(who.roles)} role(s)')
        embed.add_field(name='Top Role',
                        value=f'{who.top_role.mention if who.top_role.name != "@everyone" else "Default Role"}'
                              f' (Position {who.top_role.position}/{ctx.guild.roles[-1].position})')
        embed.add_field(name='Is Server Owner', value=f'{"True" if ctx.guild.owner.id == who.id else "False"}')

        # -- Date Information --
        embed.add_field(name='Account Created At', value=who.created_at.strftime(DATE_FORMAT))
        embed.add_field(name='Joined Server At', value=who.joined_at.strftime(DATE_FORMAT))

        embed.set_thumbnail(url=who.avatar_url)

        await ctx.send(embed=embed)
Ejemplo n.º 25
0
    async def create_quest_role(self, ctx):
        """
        Creates a Role for Quests

        The role created will have the default permissions that \@everyone has at the time of creation.
        """
        author = ctx.author
        channel = ctx.channel
        user_mention = discord.AllowedMentions(users=[ctx.author])

        color_converter = commands.ColorConverter()

        def chk(m):
            return m.author == author and m.channel == channel

        async def prompt(message: discord.Message,
                         ebd: discord.Embed,
                         content: str,
                         mentions: discord.AllowedMentions = None):
            if content:
                ebd.description = content
            await message.edit(embed=ebd, allowed_mentions=mentions)
            result = await self.bot.wait_for('message', check=chk, timeout=60)
            if result is None:
                return result
            content = result.content
            await try_delete(result)
            return content, ebd

        def check_stop(content):
            if content.lower() in ['stop', 'cancel', 'quit', 'exit']:
                return True
            else:
                return False

        async def stop(question_msg):
            await try_delete(question_msg)
            await ctx.send('Operation Cancelled, stopping.', delete_after=10)

        embed = create_default_embed(ctx)
        embed.title = 'Quest Role Creation'
        question_msg = await ctx.send(embed=embed)
        role_name, embed = await prompt(
            question_msg,
            embed,
            f'{ctx.author.mention}, what would you like this role to be called?',
            mentions=user_mention)
        if check_stop(role_name):
            return await stop(question_msg)

        role_color, embed = await prompt(
            question_msg, embed,
            f'Role Name: `{role_name}`\nWhat color would you like this role to be?'
        )
        if check_stop(role_color):
            return await stop(question_msg)
        try:
            color = await color_converter.convert(ctx=ctx,
                                                  argument=role_color.lower())
        except (commands.CommandError, commands.BadColourArgument):
            await try_delete(question_msg)
            return await ctx.send('Invalid Color provided, exiting.',
                                  delete_after=10)

        embed.color = color

        confirm_content, embed = await prompt(
            question_msg, embed,
            f'Role `{role_name}` with the color of this embed '
            f'will be created. Please confirm.')
        if get_positivity(confirm_content):
            new_role = await ctx.guild.create_role(
                name=role_name, color=color, reason='Quest Role Creation')
            await ctx.send(
                f'Role {new_role.mention} created.',
                delete_after=10,
                allowed_mentions=discord.AllowedMentions(roles=[new_role]))
            return await try_delete(question_msg)
        else:
            return await stop(question_msg)
Ejemplo n.º 26
0
    async def on_command_error(self, ctx, error):
        """The event triggered when an error is raised while invoking a command.
        Parameters
        ------------
        ctx: utils.Context.PoddoContext
            The context used for command invocation.
        error: commands.CommandError
            The Exception raised.
        """

        # This prevents any commands with local handlers being handled here in on_command_error.
        if hasattr(ctx.command, 'on_error'):
            return

        # This prevents any cogs with an overwritten cog_command_error being handled here.
        cog = ctx.cog
        if cog:
            if cog._get_overridden_method(cog.cog_command_error) is not None:
                return

        ignored = (commands.CommandNotFound, )

        # Allows us to check for original exceptions raised and sent to CommandInvokeError.
        # If nothing is found. We keep the exception passed to on_command_error.
        error = getattr(error, 'original', error)

        # Anything in ignored will return and prevent anything happening.
        if isinstance(error, ignored):
            return

        # Set up the Error Embed
        embed = create_default_embed(ctx, colour=discord.Colour.red())

        if isinstance(error, commands.DisabledCommand):
            embed.title = 'Command Disabled!'
            embed.description = f'{ctx.command.qualified_name} has been disabled.'
            await ctx.send(embed=embed)

        elif isinstance(error, commands.MissingAnyRole):
            roles = ', '.join(error.missing_roles)
            embed.title = 'Missing Roles!'
            embed.description = f'Error: You must have any of the following roles to run this command: {roles}'
            return await ctx.send(embed)

        elif isinstance(error, commands.EmojiNotFound):
            return await ctx.send(
                'I could not find the emoji that you provided. Either I do not have access to it, '
                'or it is a default emoji.')

        elif isinstance(error, commands.CheckFailure):
            embed.title = 'Permission Error!'
            msg = str(error) or 'You are not allowed to run this command.'
            embed.description = f'Error: {msg}'
            return await ctx.send(embed=embed)

        elif isinstance(error, commands.MissingRequiredArgument):
            embed.title = 'Missing Argument!'
            embed.description = 'Error: ' + str(
                error
            ) or "An error has occurred. Please contact the developer."
            return await ctx.send(embed=embed)

        elif isinstance(error, commands.BadArgument) or isinstance(
                error, commands.BadUnionArgument):
            embed.title = 'Invalid Argument!'
            embed.description = 'Error: ' + str(
                error) or "Unknown Bad Argument"
            return await ctx.send(embed=embed)

        elif isinstance(error, commands.ArgumentParsingError) or isinstance(
                error, commands.TooManyArguments):
            embed.title = 'Invalid Argument(s)!'
            embed.description = 'Error: ' + str(
                error) or "Unknown Argument Parsing Error"
            return await ctx.send(embed=embed)

        elif isinstance(error, commands.CommandOnCooldown):
            embed.title = 'Command on Cooldown!'
            cooldown = pendulum.duration(seconds=int(error.retry_after))
            embed.description = f'`{ctx.prefix}{ctx.command.qualified_name}`' \
                                f' is on cooldown for {cooldown.in_words()}'
            return await ctx.send(embed=embed)

        elif isinstance(error, discord.Forbidden):
            embed.title = 'Forbidden!'
            embed.description = 'Error: ' + str(
                error) or "Not allowed to perform this action."
            return await ctx.send(embed=embed)

        elif isinstance(error, commands.NoPrivateMessage):
            try:
                await ctx.author.send(
                    f'{ctx.command} can not be used in Private Messages.')
            except discord.HTTPException:
                pass

        else:
            # All other Errors not returned come here. And we can just print the default TraceBack.
            self.log_error(error, context=ctx)
            embed.title = 'Unknown Error!'
            embed.description = 'An unknown error has occurred! A notification has been sent to the bot developer.'
            embed.add_field(name='Error Type', value=f'{type(error)}')
            log.error('Ignoring exception in command {}:'.format(ctx.command))
            traceback.print_exception(type(error),
                                      error,
                                      error.__traceback__,
                                      file=sys.stderr)
Ejemplo n.º 27
0
    async def eval(self, ctx, *, body: str):
        """
        Evaluates input
        """
        embed = create_default_embed(ctx)

        env = {
            'bot': self.bot,
            'ctx': ctx,
            'channel': ctx.channel,
            'author': ctx.author,
            'guild': ctx.guild,
            'message': ctx.message,
            '_': self._last_result
        }

        env.update(globals())

        if not body.startswith('```'):
            body = f'```py\n' \
                   f'{body}\n' \
                   f'```'
        body = self.cleanup_code(body)
        stdout = io.StringIO()

        to_compile = f'async def func():\n{textwrap.indent(body, "  ")}'

        try:
            exec(to_compile, env)
            print(repr(env['func']))
        except Exception as e:
            return await ctx.send(
                embed=self.embed_split(embed,
                                       f'{e.__class__.__name__}: {str(e)}',
                                       discord.Colour.red(),
                                       title='Eval Compile Error'))
        func = env['func']
        try:
            with redirect_stdout(stdout):
                ret = await func()
        except Exception as e:
            value = stdout.getvalue()
            return await ctx.send(
                embed=self.embed_split(embed,
                                       f'{value}{traceback.format_exc()}',
                                       discord.Colour.red(),
                                       title='Error during Eval'))
        else:
            value = stdout.getvalue()

            embed.title = 'Eval Result'
            embed.colour = discord.Colour.green()
            desc = ''

            if ret is None:
                if value:
                    desc = value
            else:
                self._last_result = ret
                desc = f'{value}{ret}'

            return await ctx.send(embed=self.embed_split(
                embed, desc, discord.Colour.green(), title='Eval Result'))