diff options
| -rw-r--r-- | bot/cogs/tags.py | 71 | 
1 files changed, 70 insertions, 1 deletions
| diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 5da9a4148..c6b442912 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -1,7 +1,7 @@  import logging  import re  import time -from typing import Dict, List, Optional +from typing import Callable, Dict, Iterable, List, Optional  from discord import Colour, Embed  from discord.ext.commands import Cog, Context, group @@ -86,11 +86,80 @@ class Tags(Cog):              return self._get_suggestions(tag_name)          return found +    async def _get_tags_via_content(self, check: Callable[[Iterable], bool], keywords: str) -> list: +        """ +        Search for tags via contents. + +        `predicate` will be the built-in any, all, or a custom callable. Must return a bool. +        """ +        await self._get_tags() + +        keywords_processed: List[str] = [] +        for keyword in keywords.split(','): +            keyword_sanitized = keyword.strip().casefold() +            if not keyword_sanitized: +                # this happens when there are leading / trailing / consecutive comma. +                continue +            keywords_processed.append(keyword_sanitized) + +        if not keywords_processed: +            # after sanitizing, we can end up with an empty list, for example when keywords is ',' +            # in that case, we simply want to search for such keywords directly instead. +            keywords_processed = [keywords] + +        matching_tags = [] +        for tag in self._cache.values(): +            if check(query in tag['embed']['description'].casefold() for query in keywords_processed): +                matching_tags.append(tag) + +        return matching_tags + +    async def _send_matching_tags(self, ctx: Context, keywords: str, matching_tags: list) -> None: +        """Send the result of matching tags to user.""" +        if not matching_tags: +            pass +        elif len(matching_tags) == 1: +            await ctx.send(embed=Embed().from_dict(matching_tags[0]['embed'])) +        else: +            is_plural = keywords.strip().count(' ') > 0 or keywords.strip().count(',') > 0 +            embed = Embed( +                title=f"Here are the tags containing the given keyword{'s' * is_plural}:", +                description='\n'.join(tag['title'] for tag in matching_tags[:10]) +            ) +            await LinePaginator.paginate( +                sorted(f"**ยป**   {tag['title']}" for tag in matching_tags), +                ctx, +                embed, +                footer_text="To show a tag, type !tags <tagname>.", +                empty=False, +                max_lines=15 +            ) +      @group(name='tags', aliases=('tag', 't'), invoke_without_command=True)      async def tags_group(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:          """Show all known tags, a single tag, or run a subcommand."""          await ctx.invoke(self.get_command, tag_name=tag_name) +    @tags_group.group(name='search', invoke_without_command=True) +    async def search_tag_content(self, ctx: Context, *, keywords: str) -> None: +        """ +        Search inside tags' contents for tags. Allow searching for multiple keywords separated by comma. + +        Only search for tags that has ALL the keywords. +        """ +        matching_tags = await self._get_tags_via_content(all, keywords) +        await self._send_matching_tags(ctx, keywords, matching_tags) + +    @search_tag_content.command(name='any') +    async def search_tag_content_any_keyword(self, ctx: Context, *, keywords: Optional[str] = None) -> None: +        """ +        Search inside tags' contents for tags. Allow searching for multiple keywords separated by comma. + +        Search for tags that has ANY of the keywords. +        """ +        matching_tags = await self._get_tags_via_content(any, keywords or 'any') +        await self._send_matching_tags(ctx, keywords, matching_tags) +      @tags_group.command(name='get', aliases=('show', 'g'))      async def get_command(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:          """Get a specified tag, or a list of all tags if no tag is specified.""" | 
