diff options
-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). """ |