diff options
| author | 2019-09-22 21:07:28 -0400 | |
|---|---|---|
| committer | 2019-09-22 21:13:39 -0400 | |
| commit | 452618c9d84c1cdf50ec0df5287a1fc167c19707 (patch) | |
| tree | 38d3475bcd71b55264acf90a23c49bca93774e7a | |
| parent | Apply suggestions from code review (diff) | |
Apply suggestions from code review
Co-Authored-By: Mark <[email protected]>
Diffstat (limited to '')
| -rw-r--r-- | bot/api.py | 13 | ||||
| -rw-r--r-- | bot/cogs/cogs.py | 8 | ||||
| -rw-r--r-- | bot/cogs/doc.py | 6 | ||||
| -rw-r--r-- | bot/cogs/error_handler.py | 24 | ||||
| -rw-r--r-- | bot/cogs/help.py | 145 | ||||
| -rw-r--r-- | bot/cogs/off_topic_names.py | 9 | ||||
| -rw-r--r-- | bot/cogs/snekbox.py | 9 | ||||
| -rw-r--r-- | bot/cogs/superstarify/__init__.py | 15 | ||||
| -rw-r--r-- | bot/cogs/utils.py | 14 | ||||
| -rw-r--r-- | bot/cogs/verification.py | 15 | ||||
| -rw-r--r-- | bot/cogs/watchchannels/watchchannel.py | 9 | ||||
| -rw-r--r-- | bot/cogs/wolfram.py | 2 | ||||
| -rw-r--r-- | bot/pagination.py | 37 | ||||
| -rw-r--r-- | bot/patches/message_edited_at.py | 5 | ||||
| -rw-r--r-- | bot/rules/attachments.py | 4 | ||||
| -rw-r--r-- | bot/rules/burst.py | 8 | ||||
| -rw-r--r-- | bot/rules/burst_shared.py | 8 | ||||
| -rw-r--r-- | bot/rules/chars.py | 8 | ||||
| -rw-r--r-- | bot/rules/discord_emojis.py | 8 | ||||
| -rw-r--r-- | bot/rules/duplicates.py | 8 | ||||
| -rw-r--r-- | bot/rules/links.py | 8 | ||||
| -rw-r--r-- | bot/rules/mentions.py | 8 | ||||
| -rw-r--r-- | bot/rules/newlines.py | 8 | ||||
| -rw-r--r-- | bot/rules/role_mentions.py | 8 | ||||
| -rw-r--r-- | bot/utils/time.py | 4 | 
25 files changed, 156 insertions, 235 deletions
| diff --git a/bot/api.py b/bot/api.py index 3fca5db57..7f26e5305 100644 --- a/bot/api.py +++ b/bot/api.py @@ -99,7 +99,6 @@ def loop_is_running() -> bool:      This helps enable "call this when event loop is running" logic (see: Twisted's `callWhenRunning`),      which is currently not provided by asyncio.      """ -      try:          asyncio.get_running_loop()      except RuntimeError: @@ -138,7 +137,7 @@ class APILoggingHandler(logging.StreamHandler):      def emit(self, record: logging.LogRecord) -> None:          """          Determine if a log record should be shipped to the logging API. -         +          If the asyncio event loop is not yet running, log records will instead be put in a queue          which will be consumed once the event loop is running. @@ -146,18 +145,8 @@ class APILoggingHandler(logging.StreamHandler):              1. Do not log anything below DEBUG (only applies to the monkeypatched `TRACE` level)              2. Ignore log records originating from this logging handler itself to prevent infinite recursion          """ -        # Two checks are performed here:          if ( -                # 1. Do not log anything below `DEBUG`. This is only applicable -                #    for the monkeypatched `TRACE` logging level, which has a -                #    lower numeric value than `DEBUG`.                  record.levelno >= logging.DEBUG -                # 2. Ignore logging messages which are sent by this logging -                #    handler itself. This is required because if we were to -                #    not ignore messages emitted by this handler, we would -                #    infinitely recurse back down into this logging handler, -                #    making the reactor run like crazy, and eventually OOM -                #    something. Let's not do that...                  and not record.__dict__.get('via_handler')          ):              payload = { diff --git a/bot/cogs/cogs.py b/bot/cogs/cogs.py index 0caf503a8..117c77d4b 100644 --- a/bot/cogs/cogs.py +++ b/bot/cogs/cogs.py @@ -1,8 +1,7 @@  import logging  import os -from typing import Optional -from discord import Colour, Embed, Message +from discord import Colour, Embed  from discord.ext.commands import Bot, Cog, Context, group  from bot.constants import ( @@ -146,7 +145,7 @@ class Cogs(Cog):      @cogs_group.command(name='reload', aliases=('r',))      @with_role(*MODERATION_ROLES, Roles.core_developer) -    async def reload_command(self, ctx: Context, cog: str) -> Optional[Message]: +    async def reload_command(self, ctx: Context, cog: str) -> None:          """          Reload an unloaded cog, given the module containing it. @@ -227,7 +226,8 @@ class Cogs(Cog):                  log.debug(f"{ctx.author} requested we reload all cogs. Here are the results: \n"                            f"{lines}") -                return await LinePaginator.paginate(lines, ctx, embed, empty=False) +                await LinePaginator.paginate(lines, ctx, embed, empty=False) +                return              elif full_cog in self.bot.extensions:                  try: diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index 2dcbad6e0..e5c51748f 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -140,11 +140,11 @@ class Doc(commands.Cog):          Where:              * `package_name` is the package name to use, appears in the log              * `base_url` is the root documentation URL for the specified package, used to build -                         absolute paths that link to specific symbols +                absolute paths that link to specific symbols              * `inventory_url` is the absolute URL to the intersphinx inventory, fetched by running -                              `intersphinx.fetch_inventory` in an executor on the bot's event loop +                `intersphinx.fetch_inventory` in an executor on the bot's event loop              * `config` is a `SphinxConfiguration` instance to mock the regular sphinx -                       project layout, required for use with intersphinx +                project layout, required for use with intersphinx          """          self.base_urls[package_name] = base_url diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index e74030c16..49411814c 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -31,7 +31,26 @@ class ErrorHandler(Cog):      @Cog.listener()      async def on_command_error(self, ctx: Context, e: CommandError) -> None: -        """Provide command error handling.""" +        """ +        Provide generic command error handling. + +        Error handling is deferred to any local error handler, if present. + +        Error handling emits a single error response, prioritized as follows: +            1. If the name fails to match a command but matches a tag, the tag is invoked +            2. Send a BadArgument error message to the invoking context & invoke the command's help +            3. Send a UserInputError error message to the invoking context & invoke the command's help +            4. Send a NoPrivateMessage error message to the invoking context +            5. Send a BotMissingPermissions error message to the invoking context +            6. Log a MissingPermissions error, no message is sent +            7. Send a InChannelCheckFailure error message to the invoking context +            8. Log CheckFailure, CommandOnCooldown, and DisabledCommand errors, no message is sent +            9. For CommandInvokeErrors, response is based on the type of error: +                * 404: Error message is sent to the invoking context +                * 400: Log the resopnse JSON, no message is sent +                * 500 <= status <= 600: Error message is sent to the invoking context +            10. Otherwise, handling is deferred to `handle_unexpected_error` +        """          command = ctx.command          parent = None @@ -58,7 +77,8 @@ class ErrorHandler(Cog):                  # Return to not raise the exception                  with contextlib.suppress(ResponseCodeError): -                    return await ctx.invoke(tags_get_command, tag_name=ctx.invoked_with) +                    await ctx.invoke(tags_get_command, tag_name=ctx.invoked_with) +                    return          elif isinstance(e, BadArgument):              await ctx.send(f"Bad argument: {e}\n")              await ctx.invoke(*help_command) diff --git a/bot/cogs/help.py b/bot/cogs/help.py index 0f2196e46..4971cd0bb 100644 --- a/bot/cogs/help.py +++ b/bot/cogs/help.py @@ -36,12 +36,8 @@ class HelpQueryNotFound(ValueError):      Contains the custom attribute of ``possible_matches``. -    Attributes -    ---------- -    possible_matches: dict -        Any commands that were close to matching the Query. -        The possible matched command names are the keys. -        The likeness match scores are the values. +    Instances of this object contain a dictionary of any command(s) that were close to matching the +    query, where keys are the possible matched command names and values are the likeness match scores.      """      def __init__(self, arg: str, possible_matches: dict = None): @@ -53,50 +49,30 @@ class HelpSession:      """      An interactive session for bot and command help output. -    Attributes -    ---------- -    title: str -        The title of the help message. -    query: Union[:class:`discord.ext.commands.Bot`, -                 :class:`discord.ext.commands.Command] -    description: str -        The description of the query. -    pages: list[str] -        A list of the help content split into manageable pages. -    message: :class:`discord.Message` -        The message object that's showing the help contents. -    destination: :class:`discord.abc.Messageable` -        Where the help message is to be sent to. +    Expected attributes include: +        * title: str +            The title of the help message. +        * query: Union[discord.ext.commands.Bot, discord.ext.commands.Command] +        * description: str +            The description of the query. +        * pages: list[str] +            A list of the help content split into manageable pages. +        * message: `discord.Message` +            The message object that's showing the help contents. +        * destination: `discord.abc.Messageable` +            Where the help message is to be sent to.      """      def __init__( -        self, ctx: Context, *command, cleanup: bool = False, only_can_run: bool = True, -        show_hidden: bool = False, max_lines: int = 15 +        self, +        ctx: Context, +        *command, +        cleanup: bool = False, +        only_can_run: bool = True, +        show_hidden: bool = False, +        max_lines: int = 15      ): -        """ -        Creates an instance of the HelpSession class. - -        Parameters -        ---------- -        ctx: :class:`discord.ext.commands.Context` -            The context of the invoked help command. -        *command: str -            A variable argument of the command being queried. -        cleanup: Optional[bool] -            Set to ``True`` to have the message deleted on timeout. -            If ``False``, it will clear all reactions on timeout. -            Defaults to ``False``. -        only_can_run: Optional[bool] -            Set to ``True`` to hide commands the user can't run. -            Defaults to ``False``. -        show_hidden: Optional[bool] -            Set to ``True`` to include hidden commands. -            Defaults to ``False``. -        max_lines: Optional[int] -            Sets the max number of lines the paginator will add to a -            single page. -            Defaults to 20. -        """ +        """Creates an instance of the HelpSession class."""          self._ctx = ctx          self._bot = ctx.bot          self.title = "Command Help" @@ -145,18 +121,9 @@ class HelpSession:          """          Handles when a query does not match a valid command or cog. -        Will pass on possible close matches along with the ``HelpQueryNotFound`` exception. - -        Parameters -        ---------- -        query: str -            The full query that was requested. - -        Raises -        ------ -        HelpQueryNotFound +        Will pass on possible close matches along with the `HelpQueryNotFound` exception.          """ -        # combine command and cog names +        # Combine command and cog names          choices = list(self._bot.all_commands) + list(self._bot.cogs)          result = process.extractBests(query, choices, scorer=fuzz.ratio, score_cutoff=90) @@ -164,14 +131,7 @@ class HelpSession:          raise HelpQueryNotFound(f'Query "{query}" not found.', dict(result))      async def timeout(self, seconds: int = 30) -> None: -        """ -        Waits for a set number of seconds, then stops the help session. - -        Parameters -        ---------- -        seconds: int -            Number of seconds to wait. -        """ +        """Waits for a set number of seconds, then stops the help session."""          await asyncio.sleep(seconds)          await self.stop() @@ -186,16 +146,7 @@ class HelpSession:          self._timeout_task = self._bot.loop.create_task(self.timeout())      async def on_reaction_add(self, reaction: Reaction, user: User) -> None: -        """ -        Event handler for when reactions are added on the help message. - -        Parameters -        ---------- -        reaction: :class:`discord.Reaction` -            The reaction that was added. -        user: :class:`discord.User` -            The user who added the reaction. -        """ +        """Event handler for when reactions are added on the help message."""          # ensure it was the relevant session message          if reaction.message.id != self.message.id:              return @@ -252,7 +203,7 @@ class HelpSession:      def _category_key(self, cmd: Command) -> str:          """ -        Returns a cog name of a given command for use as a key for ``sorted`` and ``groupby``. +        Returns a cog name of a given command for use as a key for `sorted` and `groupby`.          A zero width space is used as a prefix for results with no cogs to force them last in ordering.          """ @@ -263,7 +214,7 @@ class HelpSession:          """          Returns the command usage signature. -        This is a custom implementation of ``command.signature`` in order to format the command +        This is a custom implementation of `command.signature` in order to format the command          signature without aliases.          """          results = [] @@ -456,25 +407,15 @@ class HelpSession:          """          Create and begin a help session based on the given command context. -        Parameters -        ---------- -        ctx: :class:`discord.ext.commands.Context` -        The context of the invoked help command. -        *command: str -            A variable argument of the command being queried. -        cleanup: Optional[bool] -            Set to ``True`` to have the message deleted on session end. -            Defaults to ``False``. -        only_can_run: Optional[bool] -            Set to ``True`` to hide commands the user can't run. -            Defaults to ``False``. -        show_hidden: Optional[bool] -            Set to ``True`` to include hidden commands. -            Defaults to ``False``. -        max_lines: Optional[int] -            Sets the max number of lines the paginator will add to a -            single page. -            Defaults to 20. +        Available options kwargs: +            * cleanup: Optional[bool] +                Set to `True` to have the message deleted on session end. Defaults to `False`. +            * only_can_run: Optional[bool] +                Set to `True` to hide commands the user can't run. Defaults to `False`. +            * show_hidden: Optional[bool] +                Set to `True` to include hidden commands. Defaults to `False`. +            * max_lines: Optional[int] +                Sets the max number of lines the paginator will add to a single page. Defaults to 20.          """          session = cls(ctx, *command, **options)          await session.prepare() @@ -565,12 +506,12 @@ def setup(bot: Bot) -> None:      This is called automatically on `bot.load_extension` being run. -    Stores the original help command instance on the ``bot._old_help`` -    attribute for later reinstatement, before removing it from the -    command registry so the new help command can be loaded successfully. +    Stores the original help command instance on the `bot._old_help` attribute for later +    reinstatement, before removing it from the command registry so the new help command can be +    loaded successfully. -    If an exception is raised during the loading of the cog, ``unload`` -    will be called in order to reinstate the original help command. +    If an exception is raised during the loading of the cog, `unload` will be called in order to +    reinstate the original help command.      """      bot._old_help = bot.get_command('help')      bot.remove_command('help') @@ -588,6 +529,6 @@ def teardown(bot: Bot) -> None:      This is called automatically on `bot.unload_extension` being run. -    Calls ``unload`` in order to reinstate the original help command. +    Calls `unload` in order to reinstate the original help command.      """      unload(bot) diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py index 5a59dc663..8f1af347a 100644 --- a/bot/cogs/off_topic_names.py +++ b/bot/cogs/off_topic_names.py @@ -40,14 +40,7 @@ class OffTopicName(Converter):  async def update_names(bot: Bot) -> None: -    """ -    The background updater task that performs a channel name update daily. - -    Args: -        bot (Bot): -            The running bot instance, used for fetching data from the -            website via the bot's `api_client`. -    """ +    """Background updater task that performs the daily channel name update."""      while True:          # Since we truncate the compute timedelta to seconds, we add one second to ensure          # we go past midnight in the `seconds_to_sleep` set below. diff --git a/bot/cogs/snekbox.py b/bot/cogs/snekbox.py index 3e6f9299a..5accbdb5e 100644 --- a/bot/cogs/snekbox.py +++ b/bot/cogs/snekbox.py @@ -5,7 +5,6 @@ import textwrap  from signal import Signals  from typing import Optional, Tuple -from discord import Message  from discord.ext.commands import Bot, Cog, Context, command, guild_only  from bot.constants import Channels, STAFF_ROLES, URLs @@ -168,7 +167,7 @@ class Snekbox(Cog):      @command(name="eval", aliases=("e",))      @guild_only()      @in_channel(Channels.bot, bypass_roles=STAFF_ROLES) -    async def eval_command(self, ctx: Context, *, code: str = None) -> Optional[Message]: +    async def eval_command(self, ctx: Context, *, code: str = None) -> None:          """          Run Python code and get the results. @@ -177,13 +176,15 @@ class Snekbox(Cog):          issue with it!          """          if ctx.author.id in self.jobs: -            return await ctx.send( +            await ctx.send(                  f"{ctx.author.mention} You've already got a job running - "                  f"please wait for it to finish!"              ) +            return          if not code:  # None or empty string -            return await ctx.invoke(self.bot.get_command("help"), "eval") +            await ctx.invoke(self.bot.get_command("help"), "eval") +            return          log.info(              f"Received code from {ctx.author.name}#{ctx.author.discriminator} " diff --git a/bot/cogs/superstarify/__init__.py b/bot/cogs/superstarify/__init__.py index c42b94d28..f7d6a269d 100644 --- a/bot/cogs/superstarify/__init__.py +++ b/bot/cogs/superstarify/__init__.py @@ -1,9 +1,8 @@  import logging  import random  from datetime import datetime -from typing import Optional -from discord import Colour, Embed, Member, Message +from discord import Colour, Embed, Member  from discord.errors import Forbidden  from discord.ext.commands import Bot, Cog, Context, command @@ -155,7 +154,7 @@ class Superstarify(Cog):      @with_role(*MODERATION_ROLES)      async def superstarify(          self, ctx: Context, member: Member, expiration: ExpirationDate, reason: str = None -    ) -> Optional[Message]: +    ) -> None:          """          Force a random superstar name (like Taylor Swift) to be the user's nickname for a specified duration. @@ -172,10 +171,11 @@ class Superstarify(Cog):              }          )          if active_superstarifies: -            return await ctx.send( +            await ctx.send(                  ":x: According to my records, this user is already superstarified. "                  f"See infraction **#{active_superstarifies[0]['id']}**."              ) +            return          infraction = await post_infraction(              ctx, member, @@ -225,7 +225,7 @@ class Superstarify(Cog):      @command(name='unsuperstarify', aliases=('release_nick', 'unstar'))      @with_role(*MODERATION_ROLES) -    async def unsuperstarify(self, ctx: Context, member: Member) -> Optional[Message]: +    async def unsuperstarify(self, ctx: Context, member: Member) -> None:          """Remove the superstarify entry from our database, allowing the user to change their nickname."""          log.debug(f"Attempting to unsuperstarify the following user: {member.display_name}") @@ -241,9 +241,8 @@ class Superstarify(Cog):              }          )          if not active_superstarifies: -            return await ctx.send( -                ":x: There is no active superstarify infraction for this user." -            ) +            await ctx.send(":x: There is no active superstarify infraction for this user.") +            return          [infraction] = active_superstarifies          await self.bot.api_client.patch( diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index 965d532a0..62e2fb03f 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -3,9 +3,8 @@ import re  import unicodedata  from email.parser import HeaderParser  from io import StringIO -from typing import Optional -from discord import Colour, Embed, Message +from discord import Colour, Embed  from discord.ext.commands import Bot, Cog, Context, command  from bot.constants import Channels, STAFF_ROLES @@ -29,7 +28,8 @@ class Utils(Cog):          if pep_number.isdigit():              pep_number = int(pep_number)          else: -            return await ctx.invoke(self.bot.get_command("help"), "pep") +            await ctx.invoke(self.bot.get_command("help"), "pep") +            return          # Newer PEPs are written in RST instead of txt          if pep_number > 542: @@ -85,7 +85,7 @@ class Utils(Cog):      @command()      @in_channel(Channels.bot, bypass_roles=STAFF_ROLES) -    async def charinfo(self, ctx: Context, *, characters: str) -> Optional[Message]: +    async def charinfo(self, ctx: Context, *, characters: str) -> None:          """Shows you information on up to 25 unicode characters."""          match = re.match(r"<(a?):(\w+):(\d+)>", characters)          if match: @@ -97,12 +97,14 @@ class Utils(Cog):                  )              )              embed.colour = Colour.red() -            return await ctx.send(embed=embed) +            await ctx.send(embed=embed) +            return          if len(characters) > 25:              embed = Embed(title=f"Too many characters ({len(characters)}/25)")              embed.colour = Colour.red() -            return await ctx.send(embed=embed) +            await ctx.send(embed=embed) +            return          def get_info(char):              digit = f"{ord(char):x}" diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py index 0c4819f66..b0c250603 100644 --- a/bot/cogs/verification.py +++ b/bot/cogs/verification.py @@ -1,5 +1,4 @@  import logging -from typing import Optional  from discord import Message, NotFound, Object  from discord.ext.commands import Bot, Cog, Context, command @@ -96,7 +95,7 @@ class Verification(Cog):      @command(name='subscribe')      @in_channel(Channels.bot) -    async def subscribe_command(self, ctx: Context, *_) -> Optional[Message]:  # We don't actually care about the args +    async def subscribe_command(self, ctx: Context, *_) -> None:  # We don't actually care about the args          """Subscribe to announcement notifications by assigning yourself the role."""          has_role = False @@ -106,9 +105,8 @@ class Verification(Cog):                  break          if has_role: -            return await ctx.send( -                f"{ctx.author.mention} You're already subscribed!", -            ) +            await ctx.send(f"{ctx.author.mention} You're already subscribed!") +            return          log.debug(f"{ctx.author} called !subscribe. Assigning the 'Announcements' role.")          await ctx.author.add_roles(Object(Roles.announcements), reason="Subscribed to announcements") @@ -121,7 +119,7 @@ class Verification(Cog):      @command(name='unsubscribe')      @in_channel(Channels.bot) -    async def unsubscribe_command(self, ctx: Context, *_) -> Optional[Message]:  # We don't actually care about the args +    async def unsubscribe_command(self, ctx: Context, *_) -> None:  # We don't actually care about the args          """Unsubscribe from announcement notifications by removing the role from yourself."""          has_role = False @@ -131,9 +129,8 @@ class Verification(Cog):                  break          if not has_role: -            return await ctx.send( -                f"{ctx.author.mention} You're already unsubscribed!" -            ) +            await ctx.send(f"{ctx.author.mention} You're already unsubscribed!") +            return          log.debug(f"{ctx.author} called !unsubscribe. Removing the 'Announcements' role.")          await ctx.author.remove_roles(Object(Roles.announcements), reason="Unsubscribed from announcements") diff --git a/bot/cogs/watchchannels/watchchannel.py b/bot/cogs/watchchannels/watchchannel.py index 3af97e7fe..e78282900 100644 --- a/bot/cogs/watchchannels/watchchannel.py +++ b/bot/cogs/watchchannels/watchchannel.py @@ -54,8 +54,13 @@ class WatchChannel(metaclass=CogABCMeta):      @abstractmethod      def __init__( -        self, bot: Bot, destination: int, webhook_id: int, -        api_endpoint: str, api_default_params: dict, logger: logging.Logger +        self, +        bot: Bot, +        destination: int, +        webhook_id: int, +        api_endpoint: str, +        api_default_params: dict, +        logger: logging.Logger      ) -> None:          self.bot = bot diff --git a/bot/cogs/wolfram.py b/bot/cogs/wolfram.py index 7c218eb8c..ab0ed2472 100644 --- a/bot/cogs/wolfram.py +++ b/bot/cogs/wolfram.py @@ -95,7 +95,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable:  async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tuple]]: -    """Give feedback that the bot is working.""" +    """Get the Wolfram API pod pages for the provided query."""      async with ctx.channel.typing():          url_str = parse.urlencode({              "input": query, diff --git a/bot/pagination.py b/bot/pagination.py index 32e289e6a..76082f459 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -27,15 +27,14 @@ class LinePaginator(Paginator):      """      A class that aids in paginating code blocks for Discord messages. -    Attributes -    ----------- -    prefix: :class:`str` +    Available attributes include: +    * prefix: `str`          The prefix inserted to every page. e.g. three backticks. -    suffix: :class:`str` +    * suffix: `str`          The suffix appended at the end of every page. e.g. three backticks. -    max_size: :class:`int` +    * max_size: `int`          The maximum amount of codepoints allowed in a page. -    max_lines: :class:`int` +    * max_lines: `int`          The maximum amount of lines allowed in a page.      """ @@ -87,10 +86,20 @@ class LinePaginator(Paginator):      @classmethod      async def paginate( -        cls, lines: Iterable[str], ctx: Context, embed: Embed, -        prefix: str = "", suffix: str = "", max_lines: Optional[int] = None, max_size: int = 500, -        empty: bool = True, restrict_to_user: User = None, timeout: int = 300, -        footer_text: str = None, url: str = None, exception_on_empty_embed: bool = False +        cls, +        lines: Iterable[str], +        ctx: Context, +        embed: Embed, +        prefix: str = "", +        suffix: str = "", +        max_lines: Optional[int] = None, +        max_size: int = 500, +        empty: bool = True, +        restrict_to_user: User = None, +        timeout: int = 300, +        footer_text: str = None, +        url: str = None, +        exception_on_empty_embed: bool = False      ) -> Optional[Message]:          """          Use a paginator and set of reactions to provide pagination over a set of lines. @@ -304,8 +313,12 @@ class ImagePaginator(Paginator):      @classmethod      async def paginate( -        cls, pages: List[Tuple[str, str]], ctx: Context, embed: Embed, -        prefix: str = "", suffix: str = "", timeout: int = 300, +        cls, +        pages: List[Tuple[str, str]], +        ctx: Context, embed: Embed, +        prefix: str = "", +        suffix: str = "", +        timeout: int = 300,          exception_on_empty_embed: bool = False      ) -> Optional[Message]:          """ diff --git a/bot/patches/message_edited_at.py b/bot/patches/message_edited_at.py index 6a73af8c9..a0154f12d 100644 --- a/bot/patches/message_edited_at.py +++ b/bot/patches/message_edited_at.py @@ -1,6 +1,5 @@ -# flake8: noqa  """ -# message_edited_at patch +# message_edited_at patch.  Date: 2019-09-16  Author: Scragly @@ -17,7 +16,7 @@ from discord import message, utils  log = logging.getLogger(__name__) -def _handle_edited_timestamp(self, value) -> None: +def _handle_edited_timestamp(self: message.Message, value: str) -> None:      """Helper function that takes care of parsing the edited timestamp."""      self._edited_timestamp = utils.parse_time(value) diff --git a/bot/rules/attachments.py b/bot/rules/attachments.py index 9cf9877fd..c550aed76 100644 --- a/bot/rules/attachments.py +++ b/bot/rules/attachments.py @@ -1,5 +1,3 @@ -"""Detects total attachments exceeding the limit sent by a single user.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message @@ -8,7 +6,7 @@ from discord import Member, Message  async def apply(      last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply attachment spam detection filter.""" +    """Detects total attachments exceeding the limit sent by a single user."""      relevant_messages = [last_message] + [          msg          for msg in recent_messages diff --git a/bot/rules/burst.py b/bot/rules/burst.py index 8859f8d51..25c5a2f33 100644 --- a/bot/rules/burst.py +++ b/bot/rules/burst.py @@ -1,16 +1,12 @@ -"""Detects repeated messages sent by a single user.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply burst message spam detection filter.""" +    """Detects repeated messages sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/burst_shared.py b/bot/rules/burst_shared.py index b8c73ecb4..bbe9271b3 100644 --- a/bot/rules/burst_shared.py +++ b/bot/rules/burst_shared.py @@ -1,16 +1,12 @@ -"""Detects repeated messages sent by multiple users.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply burst repeated message spam filter.""" +    """Detects repeated messages sent by multiple users."""      total_recent = len(recent_messages)      if total_recent > config['max']: diff --git a/bot/rules/chars.py b/bot/rules/chars.py index ae8ac93ef..1f587422c 100644 --- a/bot/rules/chars.py +++ b/bot/rules/chars.py @@ -1,16 +1,12 @@ -"""Detects total message char count exceeding the limit sent by a single user.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply excessive character count detection filter.""" +    """Detects total message char count exceeding the limit sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/discord_emojis.py b/bot/rules/discord_emojis.py index 87d129f37..5bab514f2 100644 --- a/bot/rules/discord_emojis.py +++ b/bot/rules/discord_emojis.py @@ -1,5 +1,3 @@ -"""Detects total Discord emojis (excluding Unicode emojis) exceeding the limit sent by a single user.""" -  import re  from typing import Dict, Iterable, List, Optional, Tuple @@ -10,11 +8,9 @@ DISCORD_EMOJI_RE = re.compile(r"<:\w+:\d+>")  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply emoji spam detection filter.""" +    """Detects total Discord emojis (excluding Unicode emojis) exceeding the limit sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/duplicates.py b/bot/rules/duplicates.py index 8648fd955..455764b53 100644 --- a/bot/rules/duplicates.py +++ b/bot/rules/duplicates.py @@ -1,16 +1,12 @@ -"""Detects duplicated messages sent by a single user.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply duplicate message spam detection filter.""" +    """Detects duplicated messages sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/links.py b/bot/rules/links.py index 924f092b1..ec75a19c5 100644 --- a/bot/rules/links.py +++ b/bot/rules/links.py @@ -1,5 +1,3 @@ -"""Detects total links exceeding the limit sent by a single user.""" -  import re  from typing import Dict, Iterable, List, Optional, Tuple @@ -10,11 +8,9 @@ LINK_RE = re.compile(r"(https?://[^\s]+)")  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply link spam detection filter.""" +    """Detects total links exceeding the limit sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/mentions.py b/bot/rules/mentions.py index 3372fd1e1..79725a4b1 100644 --- a/bot/rules/mentions.py +++ b/bot/rules/mentions.py @@ -1,16 +1,12 @@ -"""Detects total mentions exceeding the limit sent by a single user.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply user mention spam detection filter.""" +    """Detects total mentions exceeding the limit sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/newlines.py b/bot/rules/newlines.py index d04f8c9ed..4e66e1359 100644 --- a/bot/rules/newlines.py +++ b/bot/rules/newlines.py @@ -1,5 +1,3 @@ -"""Detects total newlines exceeding the set limit sent by a single user.""" -  import re  from typing import Dict, Iterable, List, Optional, Tuple @@ -7,11 +5,9 @@ from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply newline spam detection filter.""" +    """Detects total newlines exceeding the set limit sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/rules/role_mentions.py b/bot/rules/role_mentions.py index a8b819d0d..0649540b6 100644 --- a/bot/rules/role_mentions.py +++ b/bot/rules/role_mentions.py @@ -1,16 +1,12 @@ -"""Detects total role mentions exceeding the limit sent by a single user.""" -  from typing import Dict, Iterable, List, Optional, Tuple  from discord import Member, Message  async def apply( -    last_message: Message, -    recent_messages: List[Message], -    config: Dict[str, int] +    last_message: Message, recent_messages: List[Message], config: Dict[str, int]  ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """Apply role mention spam detection filter.""" +    """Detects total role mentions exceeding the limit sent by a single user."""      relevant_messages = tuple(          msg          for msg in recent_messages diff --git a/bot/utils/time.py b/bot/utils/time.py index fe3ccc271..c529ccc2b 100644 --- a/bot/utils/time.py +++ b/bot/utils/time.py @@ -28,7 +28,7 @@ def _stringify_time_unit(value: int, unit: str) -> str:  def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units: int = 6) -> str:      """      Returns a human-readable version of the relativedelta. -     +      precision specifies the smallest unit of time to include (e.g. "seconds", "minutes").      max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours).      """ @@ -69,7 +69,7 @@ def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units:  def time_since(past_datetime: datetime.datetime, precision: str = "seconds", max_units: int = 6) -> str:      """      Takes a datetime and returns a human-readable string that describes how long ago that datetime was. -     +      precision specifies the smallest unit of time to include (e.g. "seconds", "minutes").      max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours).      """ | 
