Example #1
0
    async def cpp(self, ctx, *, query):
        """
        This is still very experimental, as there is nothing of use in
        the implementation of the en.cppreference.com MediaWiki API, so
        this relies on crawling HTML. This may be relatively slow and
        buggy.
        """

        async with ctx.typing():
            book = neko.Book(ctx)

            results = await self.search_for(query)

            curr_page = None

            for i, result in enumerate(results):
                if curr_page is None:
                    curr_page = neko.Page(
                        title=f'Search results for `{query}`',
                        url=f'http://en.cppreference.com',
                        color=neko.random_colour())

                curr_page.add_field(name=result.name,
                                    value='\n'.join((result.desc, result.url)))

                if len(curr_page.fields) > 2 or i + 1 >= len(results):
                    book += curr_page
                    curr_page = None

        if not len(book):
            await ctx.send('No results', delete_after=10)
        else:
            await book.send()
Example #2
0
    async def help_command(self, ctx: neko.Context, *, query=None):
        """
        Shows a set of help pages outlining the available commands, and
        details on how to operate each of them.

        If a command name is passed as a parameter (`help command`) then the
        parameter is searched for as a command name and that page is opened.
        """
        # TODO: maybe try to cache this! It's a fair amount of work each time.

        # Generates the book
        bk = neko.Book(ctx)

        # Maps commands to pages, so we can just jump to the page
        command_to_page = {}

        bk += await self.gen_front_page(ctx)
        command_to_page[None] = 0

        # Walk commands
        all_cmds = sorted(set(self.bot.walk_commands()),
                          key=lambda c: c.qualified_name)

        # We offset each index in the enumeration by this to get the
        # correct page number.
        offset = len(bk)

        # Strip out any commands we don't want to show.
        cmds = [cmd for cmd in all_cmds if await should_show(cmd, ctx)]

        page_index = None
        for i, cmd in enumerate(cmds):

            bk += await self.gen_spec_page(ctx, cmd)

            if page_index is None and query in cmd.qualified_names:
                # I assume checking equality of commands is slower
                # than checking for is None each iteration.
                page_index = i + offset

        # Set the page
        if page_index is None and query:
            await ctx.send(f'I could not find a command called {query}!',
                           delete_after=10)
        else:
            if page_index is None:
                page_index = 0

            bk.index = page_index
            await bk.send()
Example #3
0
    async def tag_inspect(self, ctx, tag_name):
        """
        This is only runnable by the bot owner.
        """
        async with ctx.bot.postgres_pool.acquire() as conn:
            book = neko.Book(ctx)
            with ctx.typing():
                tag_name = tag_name.lower()
                results = await conn.fetch(
                    '''
                    SELECT * FROM nekozilla.tags 
                    LEFT JOIN nekozilla.tags_attach
                    ON pk = tag_pk
                    WHERE LOWER(name) = ($1);
                    ''', tag_name)

            if not results:
                raise neko.NekoCommandError('No results.')

            for result in results:
                data = dict(result)
                content = data.pop('content')
                author = data.pop('author')
                file = data.pop('file_name')

                # Don't want to send this!!!
                data.pop('b64data')

                user: discord.User = await ctx.bot.get_user_info(author)
                data['author'] = ' '.join([
                    'BOT' if user.bot else '', user.display_name,
                    str(user.id)
                ])

                page = neko.Page(title=f'`{data.pop("name")}`',
                                 description=content)

                page.set_thumbnail(url=user.avatar_url)

                if file is not None:
                    page.set_footer(text=f'Attached file: {file}')

                page.add_field(name='Attributes',
                               value='\n'.join(f'**{k}**: `{v}`'
                                               for k, v in data.items()))

                book += page

            await book.send()
Example #4
0
    async def discord(self, ctx: neko.Context):
        """
        Gets a list of all Discord systems, and their service
        status.

        Lists of upcoming scheduled maintenances and unresolved
        incidents will be implemented eventually.
        """
        book = neko.Book(ctx)

        async with ctx.message.channel.typing():

            bot = ctx.bot
            """
            status = await self.get_status(bot)
            components = await self.get_components(bot)
            incidents = await self.get_incidents(bot)
            sms = await self.get_scheduled_maintenances(bot)
            """

            stat_res, comp_res, inc_res, sms_res = await asyncio.gather(
                bot.http_pool.get(get_endpoint('summary.json')),
                bot.http_pool.get(get_endpoint('components.json')),
                bot.http_pool.get(get_endpoint('incidents.json')),
                bot.http_pool.get(get_endpoint('scheduled-maintenances.json')))

            status, components, incidents, sms = await asyncio.gather(
                self.get_status(stat_res), self.get_components(comp_res),
                self.get_incidents(inc_res),
                self.get_scheduled_maintenances(sms_res))

            # Make the front page!
            if status['indicator'] == 'None':
                desc = ''
            else:
                desc = f'**{status["indicator"]}**\n\n'

            desc += (f'{status["description"]}\n\n'
                     f'Last updated: {friendly_date(status["updated_at"])}.')

            if not incidents['unresolved']:
                color = status['color']
            else:
                color = get_impact_color(
                    find_highest_impact(incidents['unresolved']))
            """
            PAGE 1
            ------

            Overall status
            """
            page = neko.Page(title='Discord API Status',
                             description=desc,
                             color=color,
                             url=status['url'])

            if incidents['unresolved']:
                first = incidents['unresolved'][0]
                name = first['Name']
                body = make_incident_body(first)

                page.add_field(name=name, value=body, inline=False)

            book += page
            """
            PAGE 2
            ------

            Overall status again, but with more information on showcase
            components.
            """
            page = neko.Page(title='Discord API Status',
                             description=desc,
                             color=color,
                             url=status['url'])

            for component in components['showcase']:
                title = component.pop('Name')
                desc = []
                for k, v in component.items():
                    line = f'**{k}**: '
                    if isinstance(v, datetime.datetime):
                        line += friendly_date(v)
                    else:
                        line += str(v)
                    desc.append(line)
                page.add_field(name=title, value='\n'.join(desc), inline=False)

            book += page
            """
            PAGE 3
            ======

            Non showcase components
            """
            page = None

            fields = 0
            for component in components['rest']:
                if fields >= max_fields:
                    book += page
                    page = None
                    fields = 0

                if page is None:
                    page = neko.Page(
                        title='Other components',
                        description='Other minor components for Discord.',
                        color=color,
                        url=status['url'])

                title = component.pop('Name')
                desc = []
                for k, v in component.items():
                    line = f'**{k}**: '
                    if isinstance(v, datetime.datetime):
                        line += friendly_date(v)
                    else:
                        line += str(v)
                    desc.append(line)

                page.add_field(name=title, value='\n'.join(desc), inline=False)
                fields += 1

            if fields > 0:
                book += page
            """
            PAGE 3
            ======
            
            Incidents.
            """
            page = neko.Page(title='Unresolved incidents', color=color)

            if incidents['unresolved']:
                incident = incidents['unresolved'][0]

                name = f'**{incident["Name"]}**'
                desc = make_incident_body(incident)

                page.description = name + '\n\n' + desc.strip()

            for incident in incidents['unresolved'][1:3]:
                body = make_incident_body(incident)
                name = incident['Name']

                body = name + '\n\n' + body

                page.add_field(name='\u200b', value=body.strip())

            book += page
            """
            PAGE 4
            ======

            Resolved incidents.
            """
            page = neko.Page(
                title='Resolved incidents',
                color=color,
            )

            if incidents['resolved']:
                incident = incidents['resolved'][0]

                name = f'**{incident["Name"]}**'
                desc = make_incident_body(incident)
                page.description = name + '\n\n' + desc.strip()

            # Add the next three most recent.
            for incident in incidents['resolved'][1:3]:
                body = make_incident_body(incident)
                name = f'**{incident["Name"]}**'

                body = name + '\n\n' + body.strip()

                page.add_field(name='\u200b', value=body)

            book += page

            await book.send()
Example #5
0
    async def get_word(self, ctx, *, word: str):
        """
        Gets a definition of a given word or phrase from WordNik
        """
        def _define():
            # Much complex. Very definition. Such API! Wow!
            api = wordapi.WordApi(self.client)

            # *prays to god this isn't lazy iterative.
            return api.getDefinitions(word,
                                      sourceDictionaries=wordnik_dictionaries,
                                      includeRelated=True)

        with ctx.typing():
            words: typing.List[
                wordnik_definition.Definition] = await ctx.bot.do_job_in_pool(
                    _define)

        # Attempt to favour gcide and wordnet, as they have better definitions
        # imho.
        # Fixes #9
        if not words:
            await ctx.send('I couldn\'t find a definition for that.',
                           delete_after=10)
        else:

            front = []
            back = []

            for word in words:
                if word.sourceDictionary in ('gcide', 'wordnet'):
                    front.append(word)
                else:
                    back.append(word)

            # Re-join.
            words = [*front, *back]

            words: typing.List[wordnik_definition.Definition] = [
                word for word in words
                if not word.sourceDictionary.startswith('ahd')
            ]

            # Max results to get is 100.
            max_count = min(100, len(words))

            book = neko.Book(ctx)

            for i in range(0, max_count):
                word = words[i]

                text = ''
                if word.partOfSpeech:
                    text += f'**{word.partOfSpeech}** '

                if word.text:
                    text += word.text

                if word.extendedText:
                    text += '\n\n'
                    text += word.extendedText

                page = neko.Page(title=neko.capitalize(word.word),
                                 description=text,
                                 color=neko.random_colour())

                if word.exampleUses:
                    example = word.exampleUses[0]
                    ex_text = neko.ellipses(example.text, 800)

                    page.add_field(name='Example', value=ex_text, inline=False)

                if word.relatedWords:

                    related = ', '.join(
                        [', '.join(rw.words) for rw in word.relatedWords])

                    page.add_field(name='Synonyms', value=related)

                if word.textProns:
                    pron = '\n'.join([tp.raw for tp in word.textProns])
                    pron = neko.ellipses(pron, 400)

                    page.add_field(
                        name='Pronunciations',
                        value=pron,
                    )

                if word.score:
                    page.add_field(name='Scrabble score',
                                   value=word.score.value)

                if word.labels:
                    labels = ', '.join(label.text for label in word.labels)
                    labels = neko.ellipses(labels, 300)

                    page.add_field(name='Labels', value=labels)

                if word.notes:
                    notes = []
                    for j, note in enumerate(word.notes):
                        notes.append(f'[{j+1}] {note.value}')

                    notes = neko.ellipses('\n\n'.join(notes), 300)

                    page.add_field(name='Notes', value=notes)

                if word.attributionText:
                    attr = word.attributionText
                else:
                    attr = ('Extracted from '
                            f'{neko.capitalise(word.sourceDictionary)}')

                page.set_footer(text=attr)

                book += page

            await book.send()
Example #6
0
    async def urban(self, ctx, *, phrase: str = None):
        """
        Searches urban dictionary for the given phrase or word.

        If no word is specified, we pick a few random entries.
        """
        with ctx.typing():
            if phrase:
                api, user = ud_define_def
                resp = await ctx.bot.request('GET',
                                             api,
                                             params={'term': phrase})
                user = user + '?' + urllib.parse.urlencode({'term': phrase})
            else:
                api, user = ud_random_def
                resp = await ctx.bot.request('GET', api)

        # Discard the rest, only be concerned with upto the first 10 results.
        resp = await resp.json()
        results = resp['list'][0:10]

        book = neko.Book(ctx)

        for definition in results:
            page = neko.Page(title=definition['word'].title(),
                             description=definition['definition'],
                             color=0xFFFF00,
                             url=user)

            page.add_field(name='Example of usage',
                           value=definition['example'],
                           inline=False)

            page.add_field(name='Author', value=definition['author'])

            ups = definition['thumbs_up']
            downs = definition['thumbs_down']

            page.add_field(
                name=f'\N{THUMBS UP SIGN} {ups}',
                # No content (little discord trick)
                value=f'\N{THUMBS DOWN SIGN} {downs}ᅠ')

            page.set_thumbnail(url=ud_thumb_url)

            if 'tags' in resp:
                # Seems the tags can contain duplicates. Quick messy solution
                # is to pass it into a set first.
                page.set_footer(text=' '.join({*resp['tags']}),
                                icon_url=ud_icon_url)
            else:
                page.set_footer(text=definition['permalink'],
                                icon_url=ud_icon_url)

            book += page

        if book.pages:
            await book.send()
        else:
            await ctx.send('I couldn\'t find a definition for that.',
                           delete_after=10)