aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar sco1 <[email protected]>2019-09-22 21:07:28 -0400
committerGravatar sco1 <[email protected]>2019-09-22 21:13:39 -0400
commit452618c9d84c1cdf50ec0df5287a1fc167c19707 (patch)
tree38d3475bcd71b55264acf90a23c49bca93774e7a
parentApply suggestions from code review (diff)
Apply suggestions from code review
Co-Authored-By: Mark <[email protected]>
-rw-r--r--bot/api.py13
-rw-r--r--bot/cogs/cogs.py8
-rw-r--r--bot/cogs/doc.py6
-rw-r--r--bot/cogs/error_handler.py24
-rw-r--r--bot/cogs/help.py145
-rw-r--r--bot/cogs/off_topic_names.py9
-rw-r--r--bot/cogs/snekbox.py9
-rw-r--r--bot/cogs/superstarify/__init__.py15
-rw-r--r--bot/cogs/utils.py14
-rw-r--r--bot/cogs/verification.py15
-rw-r--r--bot/cogs/watchchannels/watchchannel.py9
-rw-r--r--bot/cogs/wolfram.py2
-rw-r--r--bot/pagination.py37
-rw-r--r--bot/patches/message_edited_at.py5
-rw-r--r--bot/rules/attachments.py4
-rw-r--r--bot/rules/burst.py8
-rw-r--r--bot/rules/burst_shared.py8
-rw-r--r--bot/rules/chars.py8
-rw-r--r--bot/rules/discord_emojis.py8
-rw-r--r--bot/rules/duplicates.py8
-rw-r--r--bot/rules/links.py8
-rw-r--r--bot/rules/mentions.py8
-rw-r--r--bot/rules/newlines.py8
-rw-r--r--bot/rules/role_mentions.py8
-rw-r--r--bot/utils/time.py4
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).
"""