Beispiel #1
0
    async def post_reaction(self, ctx: neko.Context, *, react_name=''):
        """
        Posts a reaction. Run without any commands to see a list of reactions.

        Run `mewd` to destroy the calling message.
        """
        with ctx.typing():
            react_name = react_name.lower()

            # If the react is there, then send it!
            if react_name and react_name in self.images:
                try:
                    if ctx.invoked_with == 'mewd':
                        await ctx.message.delete()
                    file_name = random.choice(self.images[react_name])
                    await ctx.send(file=discord.File(file_name))
                except discord.Forbidden:
                    ctx.command.reset_cooldown(ctx)
                except FileNotFoundError:
                    ctx.command.reset_cooldown(ctx)
                    traceback.print_exc()
                    raise neko.NekoCommandError(
                        'Something broke and the dev '
                        'was shot. Please try again later ^w^')
            # Otherwise, if the react doesn't exist, or wasn't specified, then
            # list the reacts available.
            else:
                book = neko.PaginatedBook(title='Available reactions', ctx=ctx)
                book.add_line(', '.join(
                    map(lambda n: f'`{n}`', sorted(self.images))))
                await book.send()

                # Reset the cool down.
                ctx.command.reset_cooldown(ctx)
Beispiel #2
0
    async def list_commands(self, ctx):
        """
        Lists loaded commands and aliases.
        """

        book = neko.PaginatedBook(
            ctx=ctx,
            title='Loaded commands',
            max_lines=15,
            max_size=500
        )

        command_lines = []

        for command in sorted({*ctx.bot.walk_commands()},
                              key=lambda c: c.qualified_name):

            line = f'**{command.qualified_name}** '

            if command.aliases:
                line += f'\n\t({", ".join(command.aliases)}) '

            if not command.enabled:
                line += '\n\tdisabled '

            cn = command.cog_name
            if cn:
                line += f'\n\tcog: `{cn}` '

            line += f'\n\tmodule: `{command.module}`'
            command_lines.append(line)
        book.add_lines(command_lines)

        await book.send()
Beispiel #3
0
    async def tag_list(self, ctx):
        """
        This lists bot local and global tags.
        """
        book = neko.PaginatedBook(ctx=ctx, title='Tags', max_lines=15)

        async with ctx.typing():
            await self._add_tag_list_to_pag(ctx, book)

        await book.send()
Beispiel #4
0
    async def group(self, ctx):
        """
        A command group holding a collection of commands for querying
        the status of various online services.
        """
        book = neko.PaginatedBook(title='Available sub-commands', ctx=ctx)

        for command in sorted(self.group.commands, key=lambda c: c.name):
            book.add_line(f'**{command.name}** - {command.brief}')

        await book.send()
Beispiel #5
0
    async def command_grp(self, ctx):
        """
        Run without any arguments to show a list of available commands.
        """
        book = neko.PaginatedBook(title='Available commands',
                                  ctx=ctx)

        for command in self.command_grp.commands:
            book.add_line(command.name)

        await book.send()
Beispiel #6
0
 async def git_pull(self, ctx):
     """
     Attempts to stash any local changes, and then git-pull from the remote.
     """
     def _sp_call():
         return subprocess.check_output(
            './update', stderr=subprocess.STDOUT, universal_newlines=True)
     result = await ctx.bot.do_job_in_pool(_sp_call)
     book = neko.PaginatedBook(title='>>>UPDATE<<<', ctx=ctx)
     book.add_lines(result)
     await book.send()
Beispiel #7
0
 async def __get_tb(ctx):
     book = neko.PaginatedBook(
         title=f'Last traceback ({ctx.bot.last_error.date})', ctx=ctx,
         prefix='```python', suffix='```')
     if ctx.bot.last_error.value:
         book.add_line(
             ''.join(
                 traceback.format_exception(
                     ctx.bot.last_error.type,
                     ctx.bot.last_error.value,
                     ctx.bot.last_error.traceback)))
     else:
         book.add_lines('Nothing has broken \N{THINKING FACE}')
     await book.send()
Beispiel #8
0
    async def list_cogs(self, ctx):
        """
        Lists loaded cogs, along with their base-types, and the file they are
        defined in.
        """
        book = neko.PaginatedBook(
            ctx=ctx,
            title='Loaded cogs',
            max_lines=15,
            max_size=800
        )

        for name, _cog in sorted(ctx.bot.cogs.items(), key=lambda k: k[0]):
            try:
                file = os.path.relpath(inspect.getsourcefile(_cog.__class__))
            except BaseException:
                continue

            line = (
                f'**{name}** in `{_cog.__module__}` '
                f'(`{file}`).'
                'With bases '
                f'`{"`, `".join(b.__name__ for b in type(_cog).__bases__)}`.'
            )

            if hasattr(_cog, 'events'):
                events = list(_cog.events())
                if events:
                    line += (
                        f'Defines events: '
                        f'`{"`, `".join(event.__name__ for event in events)}`'
                    )

            if hasattr(_cog, 'commands'):
                cmds = list(_cog.commands())
                if cmds:
                    line += (
                        f'Defines commands: '
                        f'`{"`, `".join(cmd.name for cmd in cmds)}`'
                    )

            book.add_line(line)

        await book.send()
Beispiel #9
0
    async def host_health(self, ctx):
        """Gets the host health and resource utilisation."""

        if os.name == 'nt':
            raise NotImplementedError(
                'Here\'s a quarter, kid. Go get yourself a real '
                'operating system.'
            )

        up_fut = asyncio.create_subprocess_exec(
            'uptime',
            stdout=asyncio.subprocess.PIPE,
            encoding='ascii'
        )

        # Gets the username
        user = getpass.getuser()

        ps_fut = asyncio.create_subprocess_exec(
            'ps', '-U', user, '-f', '-o', 'pid,comm,%cpu,%mem,cputime,start',
            stdout=asyncio.subprocess.PIPE,
            encoding='ascii'
        )

        up_res = await up_fut
        ps_res = await ps_fut

        up_stdout = [await up_res.stdout.read()]
        ps_stdout = [await ps_res.stdout.read()]

        up_out = b''.join(up_stdout).decode('ascii')
        ps_out = b''.join(ps_stdout).decode('ascii')

        book = neko.PaginatedBook(
            title=up_out,
            ctx=ctx,
            prefix='```',
            suffix='```'
        )
        book.add_lines(ps_out)

        await book.send()
Beispiel #10
0
    async def list_extensions(self, ctx):
        """
        Lists loaded extensions. These are modules imported that contain
        a setup() function, and often consist of groups of commands, listeners
        or cogs.
        """
        book = neko.PaginatedBook(
            ctx=ctx,
            title='Loaded extensions',
            max_lines=15,
            max_size=800
        )

        for name, module in sorted(ctx.bot.extensions.items(),
                                   key=lambda e: e[0]):
            file = os.path.relpath(inspect.getfile(module))
            line = f'**{name}** `({file})`'

            if hasattr(module, '__all__'):
                line += 'Exposed via `__all__`: '
                line += ', '.join(
                    sorted(
                        f'`{imp}`' for imp in getattr(module, '__all__')
                    )
                )

            line += '. Exposes: '
            line += ', '.join(
                sorted(
                    f'`{member[0]}`' for member
                    in inspect.getmembers(module)
                    if not member[0].startswith('_'),
                )
            )

            book.add_line(line + '.')
        await book.send()
Beispiel #11
0
    async def tag_my(self, ctx):
        """Shows tags you own globally and in this guild."""
        async with ctx.bot.postgres_pool.acquire() as conn:
            async with ctx.typing():
                results = await conn.fetch(
                    '''
                    SELECT name, is_nsfw, guild
                    FROM nekozilla.tags
                    WHERE author = ($1) AND (guild IS NULL OR guild = ($2))
                    ORDER BY name, created;
                    ''', ctx.author.id, ctx.guild.id)

                book = neko.PaginatedBook(ctx=ctx,
                                          title='My Tags',
                                          max_lines=10)

                for result in results:
                    name = f'`{result["name"]}`'
                    if result['is_nsfw']:
                        name = f'_{name}_'
                    name += ' in this guild' if result['guild'] else ' globally'
                    book.add_line(name)

            await book.send()
Beispiel #12
0
    async def tag_group(self, ctx: neko.Context, tag_name=None, *args):
        """
        Displays the tag if it can be found. The local tags are searched
        first, and then the global tags.

        If we start the tag with an "!", then we try the global tags list first
        instead.

        **22nd Jan 2018**:

        I have added a few meta-commands into the mix. From now on, the
        following can be added into a tag, and it will be resolved when the tag
        is retrieved:

        ${args} -> any text you put after the tag name.\r
        ${channel} -> the channel name.\r
        ${channel_mention} -> the channel name, but as a mention.\r
        ${channel_id} -> the channel snowflake ID.\r
        ${author} -> the display name of whoever invoked the tag.\r
        ${author_mention} -> mentions whoever invoked the tag.\r
        ${author_discriminator} -> shows the discriminator of whoever invoked
        the tag.\r
        ${author_username} -> shows the username of whoever invoked the tag.
        ${author_id} -> shows the snowflake ID for the user who invoked the tag.
        ${guild} -> shows the name of the guild (server) the tag is called on.
        ${guild_id} -> shows the ID of the guild (server) the tag is called on.
        """
        if tag_name is None:
            book = neko.PaginatedBook(ctx=ctx, title='Tags', max_lines=15)

            desc = f'Run {ctx.prefix}help tag <command> for more info.\n\n'

            page = neko.Page(title='Tag commands')

            cmds = {*self.tag_group.walk_commands()}
            for cmd in copy.copy(cmds):
                # Remove any commands we cannot run.
                if not await cmd.can_run(ctx) or cmd.hidden:
                    cmds.remove(cmd)

            # Generate strings.
            cmds = {
                '**' + ' '.join(cmd.qualified_name.split(' ')[1:]) + '** - ' +
                cmd.brief
                for cmd in cmds
            }

            for line in sorted(cmds):
                desc += f'{line}\n'

            desc += '\nThe following pages will list the available tags.'

            page.description = desc

            book += page

            async with ctx.typing():
                await self._add_tag_list_to_pag(ctx, book)

            await book.send()
            return

        if tag_name.startswith('!'):
            tag_name = tag_name[1:]
            local_first = True
        else:
            local_first = False

        async with self.bot.postgres_pool.acquire() as conn:
            with ctx.channel.typing():
                results = await conn.fetch(
                    '''
                    SELECT 
                      content,
                      file_name,
                      b64data
                    FROM nekozilla.tags
                    LEFT OUTER JOIN nekozilla.tags_attach
                    ON pk = tag_pk
                    WHERE
                      name = ($1) AND
                      (guild = ($2) OR guild IS NULL) AND 
                      -- This will be TRUE OR FALSE -> TRUE if chan is NSFW
                      -- and FALSE OR FALSE -> FALSE if chan is NOT NSFW.
                      is_nsfw = (($3) OR FALSE) 
                    ORDER BY guild NULLS LAST;
                    ''', tag_name, ctx.guild.id, ctx.channel.nsfw)

            if not results:
                raise neko.NekoCommandError('No tag found with that name.')
            else:
                first = results.pop(0 if local_first else -1)
                content = first['content']
                attachment_name = first['file_name']
                attachment_data = first['b64data']

                if attachment_name is not None and attachment_data is not None:
                    # Decode attachment data.
                    attachment_data = base64.b64decode(attachment_data)

                # We allow a few dynamic bits and pieces.
                # TODO: document this.
                replacements = {
                    ('${args}', ' '.join(str(arg) for arg in args)),
                    ('${channel}', str(ctx.channel.name)),
                    ('${channel_mention}', f'<#{ctx.channel.id}>'),
                    ('${channel_id}', str(ctx.channel.id)),
                    ('${author}', str(ctx.author.display_name)),
                    ('${author_mention}', str(ctx.author.mention)),
                    ('${author_discriminator}', str(ctx.author.discriminator)),
                    ('${author_username}', str(ctx.author.name)),
                    ('${author_id}', str(ctx.author.id)),
                    ('${guild}', str(ctx.guild.name)),
                    ('${guild_id}', str(ctx.guild.id))
                }

                for replacement in replacements:
                    content = content.replace(*replacement)

                if attachment_name is not None and attachment_data is not None:
                    with io.BytesIO(
                            initial_bytes=attachment_data) as att_bytes:
                        att_bytes.seek(0)
                        file = discord.File(att_bytes,
                                            filename=attachment_name)
                        await ctx.send(content, file=file)
                else:
                    await ctx.send(content)