diff options
Diffstat (limited to 'bot/exts/utilities')
| -rw-r--r-- | bot/exts/utilities/conversationstarters.py | 2 | ||||
| -rw-r--r-- | bot/exts/utilities/emoji.py | 2 | ||||
| -rw-r--r-- | bot/exts/utilities/githubinfo.py | 4 | ||||
| -rw-r--r-- | bot/exts/utilities/issues.py | 28 | ||||
| -rw-r--r-- | bot/exts/utilities/realpython.py | 16 | ||||
| -rw-r--r-- | bot/exts/utilities/reddit.py | 10 | ||||
| -rw-r--r-- | bot/exts/utilities/wikipedia.py | 6 | ||||
| -rw-r--r-- | bot/exts/utilities/wtf_python.py | 138 | 
8 files changed, 177 insertions, 29 deletions
| diff --git a/bot/exts/utilities/conversationstarters.py b/bot/exts/utilities/conversationstarters.py index dcbfe4d5..8bf2abfd 100644 --- a/bot/exts/utilities/conversationstarters.py +++ b/bot/exts/utilities/conversationstarters.py @@ -53,7 +53,7 @@ class ConvoStarters(commands.Cog):          # No matter what, the form will be shown.          embed = discord.Embed(              description=f"Suggest more topics [here]({SUGGESTION_FORM})!", -            color=discord.Color.blurple() +            color=discord.Colour.og_blurple()          )          try: diff --git a/bot/exts/utilities/emoji.py b/bot/exts/utilities/emoji.py index 83df39cc..fa438d7f 100644 --- a/bot/exts/utilities/emoji.py +++ b/bot/exts/utilities/emoji.py @@ -111,7 +111,7 @@ class Emojis(commands.Cog):                  **Date:** {datetime.strftime(emoji.created_at.replace(tzinfo=None), "%d/%m/%Y")}                  **ID:** {emoji.id}              """), -            color=Color.blurple(), +            color=Color.og_blurple(),              url=str(emoji.url),          ).set_thumbnail(url=emoji.url) diff --git a/bot/exts/utilities/githubinfo.py b/bot/exts/utilities/githubinfo.py index d00b408d..539e388b 100644 --- a/bot/exts/utilities/githubinfo.py +++ b/bot/exts/utilities/githubinfo.py @@ -67,7 +67,7 @@ class GithubInfo(commands.Cog):              embed = discord.Embed(                  title=f"`{user_data['login']}`'s GitHub profile info",                  description=f"```\n{user_data['bio']}\n```\n" if user_data["bio"] else "", -                colour=discord.Colour.blurple(), +                colour=discord.Colour.og_blurple(),                  url=user_data["html_url"],                  timestamp=datetime.strptime(user_data["created_at"], "%Y-%m-%dT%H:%M:%SZ")              ) @@ -139,7 +139,7 @@ class GithubInfo(commands.Cog):          embed = discord.Embed(              title=repo_data["name"],              description=repo_data["description"], -            colour=discord.Colour.blurple(), +            colour=discord.Colour.og_blurple(),              url=repo_data["html_url"]          ) diff --git a/bot/exts/utilities/issues.py b/bot/exts/utilities/issues.py index 8a7ebed0..b6d5a43e 100644 --- a/bot/exts/utilities/issues.py +++ b/bot/exts/utilities/issues.py @@ -9,14 +9,7 @@ from discord.ext import commands  from bot.bot import Bot  from bot.constants import ( -    Categories, -    Channels, -    Colours, -    ERROR_REPLIES, -    Emojis, -    NEGATIVE_REPLIES, -    Tokens, -    WHITELISTED_CHANNELS +    Categories, Channels, Colours, ERROR_REPLIES, Emojis, NEGATIVE_REPLIES, Tokens, WHITELISTED_CHANNELS  )  from bot.utils.decorators import whitelist_override  from bot.utils.extensions import invoke_help_command @@ -185,7 +178,7 @@ class Issues(commands.Cog):          return resp      @whitelist_override(channels=WHITELISTED_CHANNELS, categories=WHITELISTED_CATEGORIES) -    @commands.command(aliases=("pr",)) +    @commands.command(aliases=("issues", "pr", "prs"))      async def issue(          self,          ctx: commands.Context, @@ -197,14 +190,23 @@ class Issues(commands.Cog):          # Remove duplicates          numbers = set(numbers) -        if len(numbers) > MAXIMUM_ISSUES: -            embed = discord.Embed( +        err_message = None +        if not numbers: +            err_message = "You must have at least one issue/PR!" + +        elif len(numbers) > MAXIMUM_ISSUES: +            err_message = f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" + +        # If there's an error with command invocation then send an error embed +        if err_message is not None: +            err_embed = discord.Embed(                  title=random.choice(ERROR_REPLIES),                  color=Colours.soft_red, -                description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" +                description=err_message              ) -            await ctx.send(embed=embed) +            await ctx.send(embed=err_embed)              await invoke_help_command(ctx) +            return          results = [await self.fetch_issues(number, repository, user) for number in numbers]          await ctx.send(embed=self.format_embed(results, user, repository)) diff --git a/bot/exts/utilities/realpython.py b/bot/exts/utilities/realpython.py index ef8b2638..bf8f1341 100644 --- a/bot/exts/utilities/realpython.py +++ b/bot/exts/utilities/realpython.py @@ -1,5 +1,6 @@  import logging  from html import unescape +from typing import Optional  from urllib.parse import quote_plus  from discord import Embed @@ -31,9 +32,18 @@ class RealPython(commands.Cog):      @commands.command(aliases=["rp"])      @commands.cooldown(1, 10, commands.cooldowns.BucketType.user) -    async def realpython(self, ctx: commands.Context, *, user_search: str) -> None: -        """Send 5 articles that match the user's search terms.""" -        params = {"q": user_search, "limit": 5, "kind": "article"} +    async def realpython(self, ctx: commands.Context, amount: Optional[int] = 5, *, user_search: str) -> None: +        """ +        Send some articles from RealPython that match the search terms. + +        By default the top 5 matches are sent, this can be overwritten to +        a number between 1 and 5 by specifying an amount before the search query. +        """ +        if not 1 <= amount <= 5: +            await ctx.send("`amount` must be between 1 and 5 (inclusive).") +            return + +        params = {"q": user_search, "limit": amount, "kind": "article"}          async with self.bot.http_session.get(url=API_ROOT, params=params) as response:              if response.status != 200:                  logger.error( diff --git a/bot/exts/utilities/reddit.py b/bot/exts/utilities/reddit.py index e6cb5337..782583d2 100644 --- a/bot/exts/utilities/reddit.py +++ b/bot/exts/utilities/reddit.py @@ -244,7 +244,7 @@ class Reddit(Cog):          # Use only starting summary page for #reddit channel posts.          embed.description = self.build_pagination_pages(posts, paginate=False) -        embed.colour = Colour.blurple() +        embed.colour = Colour.og_blurple()          return embed      @loop() @@ -312,7 +312,7 @@ class Reddit(Cog):          await ctx.send(f"Here are the top {subreddit} posts of all time!")          embed = Embed( -            color=Colour.blurple() +            color=Colour.og_blurple()          )          await ImagePaginator.paginate(pages, ctx, embed) @@ -325,7 +325,7 @@ class Reddit(Cog):          await ctx.send(f"Here are today's top {subreddit} posts!")          embed = Embed( -            color=Colour.blurple() +            color=Colour.og_blurple()          )          await ImagePaginator.paginate(pages, ctx, embed) @@ -338,7 +338,7 @@ class Reddit(Cog):          await ctx.send(f"Here are this week's top {subreddit} posts!")          embed = Embed( -            color=Colour.blurple() +            color=Colour.og_blurple()          )          await ImagePaginator.paginate(pages, ctx, embed) @@ -349,7 +349,7 @@ class Reddit(Cog):          """Send a paginated embed of all the subreddits we're relaying."""          embed = Embed()          embed.title = "Relayed subreddits." -        embed.colour = Colour.blurple() +        embed.colour = Colour.og_blurple()          await LinePaginator.paginate(              RedditConfig.subreddits, diff --git a/bot/exts/utilities/wikipedia.py b/bot/exts/utilities/wikipedia.py index eccc1f8c..e5e8e289 100644 --- a/bot/exts/utilities/wikipedia.py +++ b/bot/exts/utilities/wikipedia.py @@ -82,13 +82,11 @@ class WikipediaSearch(commands.Cog):          if contents:              embed = Embed(                  title="Wikipedia Search Results", -                colour=Color.blurple() +                colour=Color.og_blurple()              )              embed.set_thumbnail(url=WIKI_THUMBNAIL)              embed.timestamp = datetime.utcnow() -            await LinePaginator.paginate( -                contents, ctx, embed -            ) +            await LinePaginator.paginate(contents, ctx, embed, restrict_to_user=ctx.author)          else:              await ctx.send(                  "Sorry, we could not find a wikipedia article using that search term." diff --git a/bot/exts/utilities/wtf_python.py b/bot/exts/utilities/wtf_python.py new file mode 100644 index 00000000..980b3dba --- /dev/null +++ b/bot/exts/utilities/wtf_python.py @@ -0,0 +1,138 @@ +import logging +import random +import re +from typing import Optional + +import rapidfuzz +from discord import Embed, File +from discord.ext import commands, tasks + +from bot import constants +from bot.bot import Bot + +log = logging.getLogger(__name__) + +WTF_PYTHON_RAW_URL = "http://raw.githubusercontent.com/satwikkansal/wtfpython/master/" +BASE_URL = "https://github.com/satwikkansal/wtfpython" +LOGO_PATH = "./bot/resources/utilities/wtf_python_logo.jpg" + +ERROR_MESSAGE = f""" +Unknown WTF Python Query. Please try to reformulate your query. + +**Examples**: +```md +{constants.Client.prefix}wtf wild imports +{constants.Client.prefix}wtf subclass +{constants.Client.prefix}wtf del +``` +If the problem persists send a message in <#{constants.Channels.dev_contrib}> +""" + +MINIMUM_CERTAINTY = 55 + + +class WTFPython(commands.Cog): +    """Cog that allows getting WTF Python entries from the WTF Python repository.""" + +    def __init__(self, bot: Bot): +        self.bot = bot +        self.headers: dict[str, str] = {} +        self.fetch_readme.start() + +    @tasks.loop(minutes=60) +    async def fetch_readme(self) -> None: +        """Gets the content of README.md from the WTF Python Repository.""" +        async with self.bot.http_session.get(f"{WTF_PYTHON_RAW_URL}README.md") as resp: +            log.trace("Fetching the latest WTF Python README.md") +            if resp.status == 200: +                raw = await resp.text() +                self.parse_readme(raw) + +    def parse_readme(self, data: str) -> None: +        """ +        Parses the README.md into a dict. + +        It parses the readme into the `self.headers` dict, +        where the key is the heading and the value is the +        link to the heading. +        """ +        # Match the start of examples, until the end of the table of contents (toc) +        table_of_contents = re.search( +            r"\[👀 Examples\]\(#-examples\)\n([\w\W]*)<!-- tocstop -->", data +        )[0].split("\n") + +        for header in list(map(str.strip, table_of_contents)): +            match = re.search(r"\[▶ (.*)\]\((.*)\)", header) +            if match: +                hyper_link = match[0].split("(")[1].replace(")", "") +                self.headers[match[0]] = f"{BASE_URL}/{hyper_link}" + +    def fuzzy_match_header(self, query: str) -> Optional[str]: +        """ +        Returns the fuzzy match of a query if its ratio is above "MINIMUM_CERTAINTY" else returns None. + +        "MINIMUM_CERTAINTY" is the lowest score at which the fuzzy match will return a result. +        The certainty returned by rapidfuzz.process.extractOne is a score between 0 and 100, +        with 100 being a perfect match. +        """ +        match, certainty, _ = rapidfuzz.process.extractOne(query, self.headers.keys()) +        return match if certainty > MINIMUM_CERTAINTY else None + +    @commands.command(aliases=("wtf", "WTF")) +    async def wtf_python(self, ctx: commands.Context, *, query: Optional[str] = None) -> None: +        """ +        Search WTF Python repository. + +        Gets the link of the fuzzy matched query from https://github.com/satwikkansal/wtfpython. +        Usage: +            --> .wtf wild imports +        """ +        if query is None: +            no_query_embed = Embed( +                title="WTF Python?!", +                colour=constants.Colours.dark_green, +                description="A repository filled with suprising snippets that can make you say WTF?!\n\n" +                f"[Go to the Repository]({BASE_URL})" +            ) +            logo = File(LOGO_PATH, filename="wtf_logo.jpg") +            no_query_embed.set_thumbnail(url="attachment://wtf_logo.jpg") +            await ctx.send(embed=no_query_embed, file=logo) +            return + +        if len(query) > 50: +            embed = Embed( +                title=random.choice(constants.ERROR_REPLIES), +                description=ERROR_MESSAGE, +                colour=constants.Colours.soft_red, +            ) +            match = None +        else: +            match = self.fuzzy_match_header(query) + +        if not match: +            embed = Embed( +                title=random.choice(constants.ERROR_REPLIES), +                description=ERROR_MESSAGE, +                colour=constants.Colours.soft_red, +            ) +            await ctx.send(embed=embed) +            return + +        embed = Embed( +            title="WTF Python?!", +            colour=constants.Colours.dark_green, +            description=f"""Search result for '{query}': {match.split("]")[0].replace("[", "")} +            [Go to Repository Section]({self.headers[match]})""", +        ) +        logo = File(LOGO_PATH, filename="wtf_logo.jpg") +        embed.set_thumbnail(url="attachment://wtf_logo.jpg") +        await ctx.send(embed=embed, file=logo) + +    def cog_unload(self) -> None: +        """Unload the cog and cancel the task.""" +        self.fetch_readme.cancel() + + +def setup(bot: Bot) -> None: +    """Load the WTFPython Cog.""" +    bot.add_cog(WTFPython(bot)) | 
