Skip to content

Commit

Permalink
Merge pull request python-discord#823 from python-discord/tag-search-…
Browse files Browse the repository at this point in the history
…searches-tags-via-content-instead-of-names

Subcommand `search` and `search any` to search inside tags' contents and not names.
  • Loading branch information
MarkKoz authored Mar 9, 2020
2 parents 3cf0d56 + 8bf5ce5 commit 2c305c1
Showing 1 changed file with 70 additions and 1 deletion.
71 changes: 70 additions & 1 deletion bot/cogs/tags.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -86,11 +86,80 @@ async def _get_tag(self, tag_name: str) -> list:
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."""
Expand Down

0 comments on commit 2c305c1

Please sign in to comment.