From 857468ce4efbe26220d4c36a8840a13f89b30c44 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 30 Aug 2021 19:11:11 +0200 Subject: create a helper function to get the redis key of a doc item --- bot/exts/info/doc/_redis_cache.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py index ad764816f..0c635bf6e 100644 --- a/bot/exts/info/doc/_redis_cache.py +++ b/bot/exts/info/doc/_redis_cache.py @@ -24,8 +24,7 @@ class DocRedisCache(RedisObject): All keys from a single page are stored together, expiring a week after the first set. """ - url_key = remove_suffix(item.relative_url_path, ".html") - redis_key = f"{self.namespace}:{item.package}:{url_key}" + redis_key = f"{self.namespace}:{item_key(item)}" needs_expire = False with await self._get_pool_connection() as connection: @@ -43,10 +42,8 @@ class DocRedisCache(RedisObject): @namespace_lock async def get(self, item: DocItem) -> Optional[str]: """Return the Markdown content of the symbol `item` if it exists.""" - url_key = remove_suffix(item.relative_url_path, ".html") - with await self._get_pool_connection() as connection: - return await connection.hget(f"{self.namespace}:{item.package}:{url_key}", item.symbol_id, encoding="utf8") + return await connection.hget(f"{self.namespace}:{item_key(item)}", item.symbol_id, encoding="utf8") @namespace_lock async def delete(self, package: str) -> bool: @@ -61,10 +58,6 @@ class DocRedisCache(RedisObject): return False -def remove_suffix(string: str, suffix: str) -> str: - """Remove `suffix` from end of `string`.""" - # TODO replace usages with str.removesuffix on 3.9 - if string.endswith(suffix): - return string[:-len(suffix)] - else: - return string +def item_key(item: DocItem) -> str: + """Get the redis redis key string from `item`.""" + return f"{item.package}:{item.relative_url_path.removesuffix('.html')}" -- cgit v1.2.3 From 48b1a7b042ec23488243ae471842bdfcce8ee9a4 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 30 Aug 2021 20:23:23 +0200 Subject: Prevent erroneous symbols from always raising stale warnings Some doc symbols are improperly generated and never exist on the doc page the inventory file defines them in, causing the stale warning to get raised every time the page is parsed (at a maximum every week because of the redis expire). This can be prevented by keeping a counter in redis for the items which were stale, every time the item is warned for the counter is incremented and set to expire in 3 weeks. Then a warning is only raised when the counter is below 3, resulting in the unpreventable warning only being raised twice until it is fixed by the maintainers after it expires in 3 weeks after the last increment. --- bot/exts/info/doc/_batch_parser.py | 20 +++++++++++++------- bot/exts/info/doc/_redis_cache.py | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/bot/exts/info/doc/_batch_parser.py b/bot/exts/info/doc/_batch_parser.py index 369bb462c..cadf1e121 100644 --- a/bot/exts/info/doc/_batch_parser.py +++ b/bot/exts/info/doc/_batch_parser.py @@ -16,6 +16,7 @@ from bot.constants import Channels from bot.utils import scheduling from . import _cog, doc_cache from ._parsing import get_symbol_markdown +from ._redis_cache import StaleItemCounter log = logging.getLogger(__name__) @@ -23,6 +24,8 @@ log = logging.getLogger(__name__) class StaleInventoryNotifier: """Handle sending notifications about stale inventories through `DocItem`s to dev log.""" + symbol_counter = StaleItemCounter() + def __init__(self): self._init_task = bot.instance.loop.create_task( self._init_channel(), @@ -38,13 +41,16 @@ class StaleInventoryNotifier: async def send_warning(self, doc_item: _cog.DocItem) -> None: """Send a warning to dev log if one wasn't already sent for `item`'s url.""" if doc_item.url not in self._warned_urls: - self._warned_urls.add(doc_item.url) - await self._init_task - embed = discord.Embed( - description=f"Doc item `{doc_item.symbol_id=}` present in loaded documentation inventories " - f"not found on [site]({doc_item.url}), inventories may need to be refreshed." - ) - await self._dev_log.send(embed=embed) + # Only warn if the item got less than 3 warnings + # or if it has been more than 3 weeks since the last warning + if await self.symbol_counter.increment_for(doc_item) < 3: + self._warned_urls.add(doc_item.url) + await self._init_task + embed = discord.Embed( + description=f"Doc item `{doc_item.symbol_id=}` present in loaded documentation inventories " + f"not found on [site]({doc_item.url}), inventories may need to be refreshed." + ) + await self._dev_log.send(embed=embed) class QueueItem(NamedTuple): diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py index 0c635bf6e..3fa3460ca 100644 --- a/bot/exts/info/doc/_redis_cache.py +++ b/bot/exts/info/doc/_redis_cache.py @@ -58,6 +58,22 @@ class DocRedisCache(RedisObject): return False +class StaleItemCounter(RedisObject): + """Manage increment counters for stale `DocItem`s.""" + + @namespace_lock + async def increment_for(self, item: DocItem) -> int: + """ + Increment the counter for `item` by 1, set it to expire in 3 weeks and return the new value. + + If the counter didn't exist, initialize it with 1. + """ + key = f"{self.namespace}:{item_key(item)}:{item.symbol_id}" + with await self._get_pool_connection() as connection: + await connection.expire(key, WEEK_SECONDS * 3) + return int(await connection.incr(key)) + + def item_key(item: DocItem) -> str: """Get the redis redis key string from `item`.""" return f"{item.package}:{item.relative_url_path.removesuffix('.html')}" -- cgit v1.2.3 From 727ef751ec2bb308d4a2d8bb0e348e438620494c Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 30 Aug 2021 20:44:49 +0200 Subject: Delete stale item counters when clearing doc cache --- bot/exts/info/doc/_cog.py | 1 + bot/exts/info/doc/_redis_cache.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py index fb9b2584a..6c3110306 100644 --- a/bot/exts/info/doc/_cog.py +++ b/bot/exts/info/doc/_cog.py @@ -439,6 +439,7 @@ class DocCog(commands.Cog): ) -> None: """Clear the persistent redis cache for `package`.""" if await doc_cache.delete(package_name): + await self.item_fetcher.stale_inventory_notifier.symbol_counter.delete() await ctx.send(f"Successfully cleared the cache for `{package_name}`.") else: await ctx.send("No keys matching the package found.") diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py index 3fa3460ca..05871eef7 100644 --- a/bot/exts/info/doc/_redis_cache.py +++ b/bot/exts/info/doc/_redis_cache.py @@ -73,6 +73,18 @@ class StaleItemCounter(RedisObject): await connection.expire(key, WEEK_SECONDS * 3) return int(await connection.incr(key)) + @namespace_lock + async def delete(self, package: str) -> bool: + """Remove all values for `package`; return True if at least one key was deleted, False otherwise.""" + with await self._get_pool_connection() as connection: + package_keys = [ + package_key async for package_key in connection.iscan(match=f"{self.namespace}:{package}:*") + ] + if package_keys: + await connection.delete(*package_keys) + return True + return False + def item_key(item: DocItem) -> str: """Get the redis redis key string from `item`.""" -- cgit v1.2.3 From 23a3e5e53e1c9229433439de90e423499a9742b7 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 31 Aug 2021 03:59:19 +0200 Subject: Raise for status to prevent parsing of invalid pages --- bot/exts/info/doc/_batch_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/doc/_batch_parser.py b/bot/exts/info/doc/_batch_parser.py index cadf1e121..62b04b649 100644 --- a/bot/exts/info/doc/_batch_parser.py +++ b/bot/exts/info/doc/_batch_parser.py @@ -107,7 +107,7 @@ class BatchParser: if doc_item not in self._item_futures and doc_item not in self._queue: self._item_futures[doc_item].user_requested = True - async with bot.instance.http_session.get(doc_item.url) as response: + async with bot.instance.http_session.get(doc_item.url, raise_for_status=True) as response: soup = await bot.instance.loop.run_in_executor( None, BeautifulSoup, -- cgit v1.2.3 From 3846da7cfbd33844bf04846d7d4e9ea67a5cb0a3 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Fri, 24 Sep 2021 00:10:07 +0100 Subject: Direct users to the appeals server when banned This is a new appeals process we are trialing. Users who get banned join this server and DM a modmail bot, who relays the message to the main server for mods to discuss. I have updated the shortening logic to allow for extra information to be included at the end of the embed, while still staying under the limit. --- bot/exts/moderation/infraction/_utils.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bot/exts/moderation/infraction/_utils.py b/bot/exts/moderation/infraction/_utils.py index b20ef1d06..e93174ea3 100644 --- a/bot/exts/moderation/infraction/_utils.py +++ b/bot/exts/moderation/infraction/_utils.py @@ -27,16 +27,18 @@ RULES_URL = "https://pythondiscord.com/pages/rules" # Type aliases Infraction = t.Dict[str, t.Union[str, int, bool]] -APPEAL_EMAIL = "appeals@pythondiscord.com" +APPEAL_SERVER_INVITE = "https://discord.gg/WXrCJxWBnm" INFRACTION_TITLE = "Please review our rules" -INFRACTION_APPEAL_EMAIL_FOOTER = f"To appeal this infraction, send an e-mail to {APPEAL_EMAIL}" +INFRACTION_APPEAL_SERVER_FOOTER = f"\n\nTo appeal this infraction, join our [appeals server]({APPEAL_SERVER_INVITE})." INFRACTION_APPEAL_MODMAIL_FOOTER = ( - 'If you would like to discuss or appeal this infraction, ' - 'send a message to the ModMail bot' + '\n\nIf you would like to discuss or appeal this infraction, ' + 'send a message to the ModMail bot.' ) INFRACTION_AUTHOR_NAME = "Infraction information" +LONGEST_EXTRAS = max(len(INFRACTION_APPEAL_SERVER_FOOTER), len(INFRACTION_APPEAL_MODMAIL_FOOTER)) + INFRACTION_DESCRIPTION_TEMPLATE = ( "**Type:** {type}\n" "**Expires:** {expires}\n" @@ -170,8 +172,10 @@ async def notify_infraction( ) # For case when other fields than reason is too long and this reach limit, then force-shorten string - if len(text) > 4096: - text = f"{text[:4093]}..." + if len(text) > 4096 - LONGEST_EXTRAS: + text = f"{text[:4093-LONGEST_EXTRAS]}..." + + text += INFRACTION_APPEAL_SERVER_FOOTER if infr_type.lower() == 'ban' else INFRACTION_APPEAL_MODMAIL_FOOTER embed = discord.Embed( description=text, @@ -182,10 +186,6 @@ async def notify_infraction( embed.title = INFRACTION_TITLE embed.url = RULES_URL - embed.set_footer( - text=INFRACTION_APPEAL_EMAIL_FOOTER if infr_type == 'Ban' else INFRACTION_APPEAL_MODMAIL_FOOTER - ) - return await send_private_embed(user, embed) -- cgit v1.2.3 From a95885210b0caa693bdc916bf3aa42c97e8ef071 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Fri, 24 Sep 2021 00:28:36 +0100 Subject: Update infraction DM tests to reflect new output --- tests/bot/exts/moderation/infraction/test_utils.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/bot/exts/moderation/infraction/test_utils.py b/tests/bot/exts/moderation/infraction/test_utils.py index eb256f1fd..72eebb254 100644 --- a/tests/bot/exts/moderation/infraction/test_utils.py +++ b/tests/bot/exts/moderation/infraction/test_utils.py @@ -139,14 +139,14 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): type="Ban", expires="2020-02-26 09:20 (23 hours and 59 minutes)", reason="No reason provided." - ), + ) + utils.INFRACTION_APPEAL_SERVER_FOOTER, colour=Colours.soft_red, url=utils.RULES_URL ).set_author( name=utils.INFRACTION_AUTHOR_NAME, url=utils.RULES_URL, icon_url=Icons.token_removed - ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER), + ), "send_result": True }, { @@ -157,14 +157,14 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): type="Warning", expires="N/A", reason="Test reason." - ), + ) + utils.INFRACTION_APPEAL_MODMAIL_FOOTER, colour=Colours.soft_red, url=utils.RULES_URL ).set_author( name=utils.INFRACTION_AUTHOR_NAME, url=utils.RULES_URL, icon_url=Icons.token_removed - ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER), + ), "send_result": False }, # Note that this test case asserts that the DM that *would* get sent to the user is formatted @@ -177,14 +177,14 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): type="Note", expires="N/A", reason="No reason provided." - ), + ) + utils.INFRACTION_APPEAL_MODMAIL_FOOTER, colour=Colours.soft_red, url=utils.RULES_URL ).set_author( name=utils.INFRACTION_AUTHOR_NAME, url=utils.RULES_URL, icon_url=Icons.defcon_denied - ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER), + ), "send_result": False }, { @@ -195,14 +195,14 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): type="Mute", expires="2020-02-26 09:20 (23 hours and 59 minutes)", reason="Test" - ), + ) + utils.INFRACTION_APPEAL_MODMAIL_FOOTER, colour=Colours.soft_red, url=utils.RULES_URL ).set_author( name=utils.INFRACTION_AUTHOR_NAME, url=utils.RULES_URL, icon_url=Icons.defcon_denied - ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER), + ), "send_result": False }, { @@ -213,14 +213,14 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): type="Mute", expires="N/A", reason="foo bar" * 4000 - )[:4093] + "...", + )[:4093-utils.LONGEST_EXTRAS] + "..." + utils.INFRACTION_APPEAL_MODMAIL_FOOTER, colour=Colours.soft_red, url=utils.RULES_URL ).set_author( name=utils.INFRACTION_AUTHOR_NAME, url=utils.RULES_URL, icon_url=Icons.defcon_denied - ).set_footer(text=utils.INFRACTION_APPEAL_MODMAIL_FOOTER), + ), "send_result": True } ] -- cgit v1.2.3 From e082596ff49b22dbb47d3bf8aa75ae98e2264620 Mon Sep 17 00:00:00 2001 From: Izan Date: Fri, 24 Sep 2021 16:17:36 +0100 Subject: Add handling for when `message.author` is a `discord.User` NB: Will give a sentry warning when this happens. --- bot/exts/help_channels/_cog.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index cfc9cf477..ecffc59fd 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -125,14 +125,19 @@ class HelpChannels(commands.Cog): """ log.info(f"Channel #{message.channel} was claimed by `{message.author.id}`.") await self.move_to_in_use(message.channel) - await self._handle_role_change(message.author, message.author.add_roles) - await _message.pin(message) + # Handle odd edge case of `message.author` being a `discord.User` (see bot#1839) + if isinstance(message.author, discord.User): + log.warning("`message.author` is a `discord.User` so not handling role change or sending DM.") + else: + await self._handle_role_change(message.author, message.author.add_roles) - try: - await _message.dm_on_open(message) - except Exception as e: - log.warning("Error occurred while sending DM:", exc_info=e) + try: + await _message.dm_on_open(message) + except Exception as e: + log.warning("Error occurred while sending DM:", exc_info=e) + + await _message.pin(message) # Add user with channel for dormant check. await _caches.claimants.set(message.channel.id, message.author.id) -- cgit v1.2.3 From 581bea0ed8918cb38180f48ebf5e2ecda57a7192 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 16 Oct 2021 16:22:37 +0100 Subject: Formats Help Command Output Closes #1232 Modifies the docstring sent for per-command help to remove weird formatting issues mentioned in #1232. Removes newlines that are not used for paragraph breaks, after retrieving the docstring, and lets the embed handle it on the discord side. Allow overriding this behaviour via \u2003 to denote a non-escapable break. Co-authored-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- bot/exts/info/help.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/exts/info/help.py b/bot/exts/info/help.py index f413caded..743dfdd3f 100644 --- a/bot/exts/info/help.py +++ b/bot/exts/info/help.py @@ -1,4 +1,5 @@ import itertools +import re from collections import namedtuple from contextlib import suppress from typing import List, Union @@ -179,7 +180,10 @@ class CustomHelpCommand(HelpCommand): except CommandError: command_details += NOT_ALLOWED_TO_RUN_MESSAGE - command_details += f"*{command.help or 'No details provided.'}*\n" + # Remove line breaks from docstrings, if not used to separate paragraphs. + # Allow overriding this behaviour via putting \u2003 at the start of a line. + formatted_doc = re.sub("(? Date: Sun, 17 Oct 2021 11:48:00 +0200 Subject: Regex: add a word boundary before .gg (#1817) * Regex: add a word boundary before .gg and use named groups Before this commit, `an-arbitrary-domain.gg/notaninvite` would trigger the filter. This solve the issue by adding a word boundary before this branch of the pattern. * Regex: replace the word boundary by a word char Co-authored-by: ChrisJL Co-authored-by: ChrisJL Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/converters.py | 4 ++-- bot/exts/filters/filtering.py | 2 +- bot/utils/regex.py | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bot/converters.py b/bot/converters.py index 4a4d3b544..dd02f6ae6 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -71,10 +71,10 @@ class ValidDiscordServerInvite(Converter): async def convert(self, ctx: Context, server_invite: str) -> dict: """Check whether the string is a valid Discord server invite.""" - invite_code = INVITE_RE.search(server_invite) + invite_code = INVITE_RE.match(server_invite) if invite_code: response = await ctx.bot.http_session.get( - f"{URLs.discord_invite_api}/{invite_code[1]}" + f"{URLs.discord_invite_api}/{invite_code.group('invite')}" ) if response.status != 404: invite_data = await response.json() diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py index 7faf063b9..a151db1f0 100644 --- a/bot/exts/filters/filtering.py +++ b/bot/exts/filters/filtering.py @@ -507,7 +507,7 @@ class Filtering(Cog): # discord\.gg/gdudes-pony-farm text = text.replace("\\", "") - invites = INVITE_RE.findall(text) + invites = [m.group("invite") for m in INVITE_RE.finditer(text)] invite_data = dict() for invite in invites: if invite in invite_data: diff --git a/bot/utils/regex.py b/bot/utils/regex.py index 7bad1e627..d77f5950b 100644 --- a/bot/utils/regex.py +++ b/bot/utils/regex.py @@ -1,14 +1,14 @@ import re INVITE_RE = re.compile( - r"(?:discord(?:[\.,]|dot)gg|" # Could be discord.gg/ - r"discord(?:[\.,]|dot)com(?:\/|slash)invite|" # or discord.com/invite/ - r"discordapp(?:[\.,]|dot)com(?:\/|slash)invite|" # or discordapp.com/invite/ - r"discord(?:[\.,]|dot)me|" # or discord.me - r"discord(?:[\.,]|dot)li|" # or discord.li - r"discord(?:[\.,]|dot)io|" # or discord.io. - r"(?:[\.,]|dot)gg" # or .gg/ - r")(?:[\/]|slash)" # / or 'slash' - r"([a-zA-Z0-9\-]+)", # the invite code itself + r"(discord([\.,]|dot)gg|" # Could be discord.gg/ + r"discord([\.,]|dot)com(\/|slash)invite|" # or discord.com/invite/ + r"discordapp([\.,]|dot)com(\/|slash)invite|" # or discordapp.com/invite/ + r"discord([\.,]|dot)me|" # or discord.me + r"discord([\.,]|dot)li|" # or discord.li + r"discord([\.,]|dot)io|" # or discord.io. + r"((?[a-zA-Z0-9\-]+)", # the invite code itself flags=re.IGNORECASE ) -- cgit v1.2.3 From 77e83e01c3adbce4bd9ffac4eeb031cf8a283640 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Sun, 17 Oct 2021 10:55:14 +0100 Subject: Fixed delayed logs and made some other minor refactors (#1863) * Run debug log before help command invocation to avoid delayed logs * Refactored other areas of code slightly, ensuring logging is done as soon as possible. Removed outdated comment * Ensured debug logs were sent for disabled commands Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/backend/error_handler.py | 47 ++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py index 7644b93ae..6ab6634a6 100644 --- a/bot/exts/backend/error_handler.py +++ b/bot/exts/backend/error_handler.py @@ -59,17 +59,23 @@ class ErrorHandler(Cog): log.trace(f"Command {command} had its error already handled locally; ignoring.") return + debug_message = ( + f"Command {command} invoked by {ctx.message.author} with error " + f"{e.__class__.__name__}: {e}" + ) + if isinstance(e, errors.CommandNotFound) and not getattr(ctx, "invoked_from_error_handler", False): if await self.try_silence(ctx): return - # Try to look for a tag with the command's name - await self.try_get_tag(ctx) - return # Exit early to avoid logging. + await self.try_get_tag(ctx) # Try to look for a tag with the command's name elif isinstance(e, errors.UserInputError): + log.debug(debug_message) await self.handle_user_input_error(ctx, e) elif isinstance(e, errors.CheckFailure): + log.debug(debug_message) await self.handle_check_failure(ctx, e) elif isinstance(e, errors.CommandOnCooldown): + log.debug(debug_message) await ctx.send(e) elif isinstance(e, errors.CommandInvokeError): if isinstance(e.original, ResponseCodeError): @@ -80,22 +86,16 @@ class ErrorHandler(Cog): await ctx.send(f"Cannot infract that user. {e.original.reason}") else: await self.handle_unexpected_error(ctx, e.original) - return # Exit early to avoid logging. elif isinstance(e, errors.ConversionError): if isinstance(e.original, ResponseCodeError): await self.handle_api_error(ctx, e.original) else: await self.handle_unexpected_error(ctx, e.original) - return # Exit early to avoid logging. - elif not isinstance(e, errors.DisabledCommand): + elif isinstance(e, errors.DisabledCommand): + log.debug(debug_message) + else: # MaxConcurrencyReached, ExtensionError await self.handle_unexpected_error(ctx, e) - return # Exit early to avoid logging. - - log.debug( - f"Command {command} invoked by {ctx.message.author} with error " - f"{e.__class__.__name__}: {e}" - ) @staticmethod def get_help_command(ctx: Context) -> t.Coroutine: @@ -188,9 +188,6 @@ class ErrorHandler(Cog): if not any(role.id in MODERATION_ROLES for role in ctx.author.roles): await self.send_command_suggestion(ctx, ctx.invoked_with) - # Return to not raise the exception - return - async def send_command_suggestion(self, ctx: Context, command_name: str) -> None: """Sends user similar commands if any can be found.""" # No similar tag found, or tag on cooldown - @@ -235,38 +232,32 @@ class ErrorHandler(Cog): """ if isinstance(e, errors.MissingRequiredArgument): embed = self._get_error_embed("Missing required argument", e.param.name) - await ctx.send(embed=embed) - await self.get_help_command(ctx) self.bot.stats.incr("errors.missing_required_argument") elif isinstance(e, errors.TooManyArguments): embed = self._get_error_embed("Too many arguments", str(e)) - await ctx.send(embed=embed) - await self.get_help_command(ctx) self.bot.stats.incr("errors.too_many_arguments") elif isinstance(e, errors.BadArgument): embed = self._get_error_embed("Bad argument", str(e)) - await ctx.send(embed=embed) - await self.get_help_command(ctx) self.bot.stats.incr("errors.bad_argument") elif isinstance(e, errors.BadUnionArgument): embed = self._get_error_embed("Bad argument", f"{e}\n{e.errors[-1]}") - await ctx.send(embed=embed) - await self.get_help_command(ctx) self.bot.stats.incr("errors.bad_union_argument") elif isinstance(e, errors.ArgumentParsingError): embed = self._get_error_embed("Argument parsing error", str(e)) await ctx.send(embed=embed) self.get_help_command(ctx).close() self.bot.stats.incr("errors.argument_parsing_error") + return else: embed = self._get_error_embed( "Input error", "Something about your input seems off. Check the arguments and try again." ) - await ctx.send(embed=embed) - await self.get_help_command(ctx) self.bot.stats.incr("errors.other_user_input_error") + await ctx.send(embed=embed) + await self.get_help_command(ctx) + @staticmethod async def handle_check_failure(ctx: Context, e: errors.CheckFailure) -> None: """ @@ -299,8 +290,8 @@ class ErrorHandler(Cog): async def handle_api_error(ctx: Context, e: ResponseCodeError) -> None: """Send an error message in `ctx` for ResponseCodeError and log it.""" if e.status == 404: - await ctx.send("There does not seem to be anything matching your query.") log.debug(f"API responded with 404 for command {ctx.command}") + await ctx.send("There does not seem to be anything matching your query.") ctx.bot.stats.incr("errors.api_error_404") elif e.status == 400: content = await e.response.json() @@ -308,12 +299,12 @@ class ErrorHandler(Cog): await ctx.send("According to the API, your request is malformed.") ctx.bot.stats.incr("errors.api_error_400") elif 500 <= e.status < 600: - await ctx.send("Sorry, there seems to be an internal issue with the API.") log.warning(f"API responded with {e.status} for command {ctx.command}") + await ctx.send("Sorry, there seems to be an internal issue with the API.") ctx.bot.stats.incr("errors.api_internal_server_error") else: - await ctx.send(f"Got an unexpected status code from the API (`{e.status}`).") log.warning(f"Unexpected API response for command {ctx.command}: {e.status}") + await ctx.send(f"Got an unexpected status code from the API (`{e.status}`).") ctx.bot.stats.incr(f"errors.api_error_{e.status}") @staticmethod -- cgit v1.2.3 From 976a0e92e3f45fc7051abfbcd95c52544eb2d18b Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sun, 17 Oct 2021 12:51:28 +0100 Subject: Fix attr error in ModLog command Fixes #1881 Fixes BOT-1NX --- bot/exts/moderation/modlog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index b09eb2d14..900c3b610 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -539,7 +539,7 @@ class ModLog(Cog, name="ModLog"): channel = self.bot.get_channel(channel_id) # Ignore not found channels, DMs, and messages outside of the main guild. - if not channel or channel.guild and channel.guild.id != GuildConstant.id: + if not channel or not hasattr(channel, "guild") or channel.guild.id != GuildConstant.id: return True # Look at the parent channel of a thread. -- cgit v1.2.3 From a91b51dedf281bbc0c5ea0fae9cc2c150c4afb56 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sun, 17 Oct 2021 15:06:08 +0100 Subject: Use MISSING sentinal rather than None for no files Fixes #1884 Fixes BOT-1NY Discord.py 2.0 changed how this works, webhooks now look for the MISSING sentinal, rather than None to determine whether files are being passed. This was updated in this commit: https://github.com/Rapptz/discord.py/commit/a6f7213c89e9d592c69ea3c631b0cb2bdab19577 --- bot/exts/moderation/incidents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py index 097fa36f1..e265e29d3 100644 --- a/bot/exts/moderation/incidents.py +++ b/bot/exts/moderation/incidents.py @@ -105,7 +105,7 @@ async def make_embed(incident: discord.Message, outcome: Signal, actioned_by: di else: embed.set_author(name="[Failed to relay attachment]", url=attachment.proxy_url) # Embed links the file else: - file = None + file = discord.utils.MISSING return embed, file -- cgit v1.2.3 From 1895fe6555bf0143b8439696b04d53ffdb568ac5 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sun, 17 Oct 2021 17:24:27 +0100 Subject: Fix attr error since asset attrs have changed in 2.0 Fixes #1886 Fixes BOT-1NZ This was updated with Discord.py 2.0. --- bot/exts/moderation/modlog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index 900c3b610..b90480f0d 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -378,7 +378,7 @@ class ModLog(Cog, name="ModLog"): await self.send_log_message( Icons.guild_update, Colour.blurple(), "Guild updated", message, - thumbnail=after.icon_url_as(format="png") + thumbnail=after.icon.with_static_format("png") ) @Cog.listener() -- cgit v1.2.3 From f047a7013302b22308d213b66ed16412e96a36d8 Mon Sep 17 00:00:00 2001 From: Karlis Suvi <45097959+ks129@users.noreply.github.com> Date: Mon, 18 Oct 2021 07:57:07 +0300 Subject: Fix guild icon URL getting way in server command (#1888) --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 1b3e28e79..0dcb8de11 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -200,7 +200,7 @@ class Information(Cog): f"\nRoles: {num_roles}" f"\nMember status: {member_status}" ) - embed.set_thumbnail(url=ctx.guild.icon_url) + embed.set_thumbnail(url=ctx.guild.icon.url) # Members total_members = f"{ctx.guild.member_count:,}" -- cgit v1.2.3 From 379303520b1cbb0d777d05a2362a9df4006e636d Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 18 Oct 2021 21:22:35 +0100 Subject: Migrate to on_socket_event_type event Discord.py 2.0 (Namely this commit https://github.com/Rapptz/discord.py/commit/e2250d402e8ad035b2653eb411c8e744cc9eb3bf) removed the socket_response event, and replaced it with the socket_event_type event, which just sends the type of event triggered on the websocket. Since this event was removed, no socket stats were being incremented, as the event never triggered. I have looked through the rest of the bot, and we do not use the socket_response event type anywhere else. --- bot/exts/utils/internal.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/utils/internal.py b/bot/exts/utils/internal.py index 879735945..96664929b 100644 --- a/bot/exts/utils/internal.py +++ b/bot/exts/utils/internal.py @@ -37,11 +37,10 @@ class Internal(Cog): self.eval.add_check(is_owner().predicate) @Cog.listener() - async def on_socket_response(self, msg: dict) -> None: + async def on_socket_event_type(self, event_type: str) -> None: """When a websocket event is received, increase our counters.""" - if event_type := msg.get("t"): - self.socket_event_total += 1 - self.socket_events[event_type] += 1 + self.socket_event_total += 1 + self.socket_events[event_type] += 1 def _format(self, inp: str, out: Any) -> Tuple[str, Optional[discord.Embed]]: """Format the eval output into a string & attempt to format it into an Embed.""" -- cgit v1.2.3 From b2e8bfdc47339714ec014a13e2018e03c0931fe4 Mon Sep 17 00:00:00 2001 From: Izan Date: Tue, 19 Oct 2021 09:11:51 +0100 Subject: Invert `isinstance` check as per review --- bot/exts/help_channels/_cog.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index ecffc59fd..b3da1e315 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -126,9 +126,12 @@ class HelpChannels(commands.Cog): log.info(f"Channel #{message.channel} was claimed by `{message.author.id}`.") await self.move_to_in_use(message.channel) - # Handle odd edge case of `message.author` being a `discord.User` (see bot#1839) - if isinstance(message.author, discord.User): - log.warning("`message.author` is a `discord.User` so not handling role change or sending DM.") + # Handle odd edge case of `message.author` not being a `discord.Member` (see bot#1839) + if not isinstance(message.author, discord.Member): + log.warning( + f"`message.author` ({message.author} / {message.author.id}) isn't a `discord.Member` so not handling " + "role change or sending DM." + ) else: await self._handle_role_change(message.author, message.author.add_roles) -- cgit v1.2.3 From 5b7d8c41c88d3d0333f16dabd45efa770da87c82 Mon Sep 17 00:00:00 2001 From: Izan Date: Tue, 19 Oct 2021 09:31:32 +0100 Subject: Update log message for when author isn't `discord.Member` --- bot/exts/help_channels/_cog.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index b3da1e315..770a6360a 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -129,8 +129,7 @@ class HelpChannels(commands.Cog): # Handle odd edge case of `message.author` not being a `discord.Member` (see bot#1839) if not isinstance(message.author, discord.Member): log.warning( - f"`message.author` ({message.author} / {message.author.id}) isn't a `discord.Member` so not handling " - "role change or sending DM." + f"{message.author} ({message.author.id}) isn't a member. Not giving cooldown role or sending DM." ) else: await self._handle_role_change(message.author, message.author.add_roles) -- cgit v1.2.3 From 03d42584a185d0673df186377c1064b72825bc55 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 19 Oct 2021 17:32:36 +0200 Subject: Mod-log thread: use soft colors Seems like we have been using the wrong colors in mod-log. --- bot/exts/moderation/modlog.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index b90480f0d..fb6888755 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -786,11 +786,11 @@ class ModLog(Cog, name="ModLog"): return if not before.archived and after.archived: - colour = Colour.red() + colour = Colour.soft_red() action = "archived" icon = Icons.hash_red elif before.archived and not after.archived: - colour = Colour.green() + colour = Colour.soft_green() action = "un-archived" icon = Icons.hash_green else: @@ -808,7 +808,7 @@ class ModLog(Cog, name="ModLog"): """Log thread deletion.""" await self.send_log_message( Icons.hash_red, - Colour.red(), + Colour.soft_red(), "Thread deleted", f"Thread {thread.mention} (`{thread.id}`) from {thread.parent.mention} (`{thread.parent.id}`) deleted" ) @@ -823,7 +823,7 @@ class ModLog(Cog, name="ModLog"): await self.send_log_message( Icons.hash_green, - Colour.green(), + Colour.soft_green(), "Thread created", f"Thread {thread.mention} (`{thread.id}`) from {thread.parent.mention} (`{thread.parent.id}`) created" ) -- cgit v1.2.3 From acc09738dce8bf3324892f6c1a460ee54978dfd0 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 19 Oct 2021 23:10:45 +0200 Subject: Modlog: correct color names Solves https://github.com/python-discord/bot/issues/1896 --- bot/exts/moderation/modlog.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index fb6888755..9d1ae6853 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -786,11 +786,11 @@ class ModLog(Cog, name="ModLog"): return if not before.archived and after.archived: - colour = Colour.soft_red() + colour = Colours.soft_red action = "archived" icon = Icons.hash_red elif before.archived and not after.archived: - colour = Colour.soft_green() + colour = Colours.soft_green action = "un-archived" icon = Icons.hash_green else: @@ -808,7 +808,7 @@ class ModLog(Cog, name="ModLog"): """Log thread deletion.""" await self.send_log_message( Icons.hash_red, - Colour.soft_red(), + Colours.soft_red, "Thread deleted", f"Thread {thread.mention} (`{thread.id}`) from {thread.parent.mention} (`{thread.parent.id}`) deleted" ) @@ -823,7 +823,7 @@ class ModLog(Cog, name="ModLog"): await self.send_log_message( Icons.hash_green, - Colour.soft_green(), + Colours.soft_green, "Thread created", f"Thread {thread.mention} (`{thread.id}`) from {thread.parent.mention} (`{thread.parent.id}`) created" ) -- cgit v1.2.3