diff options
| -rw-r--r-- | bot/exts/backend/error_handler.py | 34 | ||||
| -rw-r--r-- | bot/exts/info/doc/_cog.py | 1 | ||||
| -rw-r--r-- | bot/exts/info/doc/_redis_cache.py | 40 | ||||
| -rw-r--r-- | bot/exts/moderation/clean.py | 18 | ||||
| -rw-r--r-- | bot/exts/moderation/modlog.py | 47 | ||||
| -rw-r--r-- | bot/exts/utils/thread_bumper.py | 4 | ||||
| -rw-r--r-- | poetry.lock | 124 | ||||
| -rw-r--r-- | pyproject.toml | 2 | ||||
| -rw-r--r-- | tests/bot/exts/backend/test_error_handler.py | 3 | 
9 files changed, 170 insertions, 103 deletions
| diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py index 35dddd8dc..761991488 100644 --- a/bot/exts/backend/error_handler.py +++ b/bot/exts/backend/error_handler.py @@ -1,3 +1,4 @@ +import copy  import difflib  from botcore.site_api import ResponseCodeError @@ -65,6 +66,8 @@ class ErrorHandler(Cog):          if isinstance(e, errors.CommandNotFound) and not getattr(ctx, "invoked_from_error_handler", False):              if await self.try_silence(ctx):                  return +            if await self.try_run_eval(ctx): +                return              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) @@ -179,6 +182,30 @@ 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) +    async def try_run_eval(self, ctx: Context) -> bool: +        """ +        Attempt to run eval command with backticks directly after command. + +        For example: !eval```print("hi")``` + +        Return True if command was invoked, else False +        """ +        msg = copy.copy(ctx.message) + +        command, sep, end = msg.content.partition("```") +        msg.content = command + " " + sep + end +        new_ctx = await self.bot.get_context(msg) + +        eval_command = self.bot.get_command("eval") +        if eval_command is None or new_ctx.command != eval_command: +            return False + +        log.debug("Running fixed eval command.") +        new_ctx.invoked_from_error_handler = True +        await self.bot.invoke(new_ctx) + +        return True +      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 - @@ -284,8 +311,11 @@ class ErrorHandler(Cog):              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() -            log.error(f"API responded with 400 for command {ctx.command}: %r.", content) +            log.error( +                "API responded with 400 for command %s: %r.", +                ctx.command, +                e.response_json or e.response_text, +            )              await ctx.send("According to the API, your request is malformed.")              ctx.bot.stats.incr("errors.api_error_400")          elif 500 <= e.status < 600: diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py index dece44063..cbc329a06 100644 --- a/bot/exts/info/doc/_cog.py +++ b/bot/exts/info/doc/_cog.py @@ -464,5 +464,4 @@ class DocCog(commands.Cog):      async def cog_unload(self) -> None:          """Clear scheduled inventories, queued symbols and cleanup task on cog unload."""          self.inventory_scheduler.cancel_all() -        self.init_refresh_task.cancel()          await self.item_fetcher.clear() diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py index 107f2344f..8e08e7ae4 100644 --- a/bot/exts/info/doc/_redis_cache.py +++ b/bot/exts/info/doc/_redis_cache.py @@ -1,22 +1,28 @@  from __future__ import annotations  import datetime +import fnmatch +import time  from typing import Optional, TYPE_CHECKING  from async_rediscache.types.base import RedisObject, namespace_lock +from bot.log import get_logger +  if TYPE_CHECKING:      from ._cog import DocItem  WEEK_SECONDS = datetime.timedelta(weeks=1).total_seconds() +log = get_logger(__name__) +  class DocRedisCache(RedisObject):      """Interface for redis functionality needed by the Doc cog."""      def __init__(self, *args, **kwargs):          super().__init__(*args, **kwargs) -        self._set_expires = set() +        self._set_expires = dict[str, float]()      @namespace_lock      async def set(self, item: DocItem, value: str) -> None: @@ -29,16 +35,30 @@ class DocRedisCache(RedisObject):          needs_expire = False          with await self._get_pool_connection() as connection: -            if redis_key not in self._set_expires: +            set_expire = self._set_expires.get(redis_key) +            if set_expire is None:                  # An expire is only set if the key didn't exist before. -                # If this is the first time setting values for this key check if it exists and add it to -                # `_set_expires` to prevent redundant checks for subsequent uses with items from the same page. -                self._set_expires.add(redis_key) -                needs_expire = not await connection.exists(redis_key) +                ttl = await connection.ttl(redis_key) +                log.debug(f"Checked TTL for `{redis_key}`.") + +                if ttl == -1: +                    log.warning(f"Key `{redis_key}` had no expire set.") +                if ttl < 0:  # not set or didn't exist +                    needs_expire = True +                else: +                    log.debug(f"Key `{redis_key}` has a {ttl} TTL.") +                    self._set_expires[redis_key] = time.monotonic() + ttl - .1  # we need this to expire before redis + +            elif time.monotonic() > set_expire: +                # If we got here the key expired in redis and we can be sure it doesn't exist. +                needs_expire = True +                log.debug(f"Key `{redis_key}` expired in internal key cache.")              await connection.hset(redis_key, item.symbol_id, value)              if needs_expire: +                self._set_expires[redis_key] = time.monotonic() + WEEK_SECONDS                  await connection.expire(redis_key, WEEK_SECONDS) +                log.info(f"Set {redis_key} to expire in a week.")      @namespace_lock      async def get(self, item: DocItem) -> Optional[str]: @@ -49,12 +69,18 @@ class DocRedisCache(RedisObject):      @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.""" +        pattern = f"{self.namespace}:{package}:*" +          with await self._get_pool_connection() as connection:              package_keys = [ -                package_key async for package_key in connection.iscan(match=f"{self.namespace}:{package}:*") +                package_key async for package_key in connection.iscan(match=pattern)              ]              if package_keys:                  await connection.delete(*package_keys) +                log.info(f"Deleted keys from redis: {package_keys}.") +                self._set_expires = { +                    key: expire for key, expire in self._set_expires.items() if not fnmatch.fnmatchcase(key, pattern) +                }                  return True              return False diff --git a/bot/exts/moderation/clean.py b/bot/exts/moderation/clean.py index 80492d0c9..5b01494ed 100644 --- a/bot/exts/moderation/clean.py +++ b/bot/exts/moderation/clean.py @@ -7,7 +7,7 @@ from datetime import datetime  from itertools import takewhile  from typing import Callable, Iterable, Literal, Optional, TYPE_CHECKING, Union -from discord import Colour, Message, NotFound, TextChannel, User, errors +from discord import Colour, Message, NotFound, TextChannel, Thread, User, errors  from discord.ext.commands import Cog, Context, Converter, Greedy, group, has_any_role  from discord.ext.commands.converter import TextChannelConverter  from discord.ext.commands.errors import BadArgument @@ -130,8 +130,8 @@ class Clean(Cog):          else:              if channels == "*":                  channels = { -                    channel for channel in ctx.guild.channels -                    if isinstance(channel, TextChannel) +                    channel for channel in ctx.guild.channels + ctx.guild.threads +                    if isinstance(channel, (TextChannel, Thread))                      # Assume that non-public channels are not needed to optimize for speed.                      and channel.permissions_for(ctx.guild.default_role).view_channel                  } @@ -443,7 +443,7 @@ class Clean(Cog):          if log_url and is_mod_channel(ctx.channel):              try:                  await ctx.reply(success_message) -            except errors.NotFound: +            except errors.HTTPException:                  await ctx.send(success_message)          elif log_url:              if mods := self.bot.get_channel(Channels.mods): @@ -486,17 +486,17 @@ class Clean(Cog):          await self._clean_messages(ctx, channels, bots_only, users, regex, first_limit, second_limit) -    @clean_group.command(name="user", aliases=["users"]) -    async def clean_user( +    @clean_group.command(name="users", aliases=["user"]) +    async def clean_users(          self,          ctx: Context, -        user: User, +        users: Greedy[User],          message_or_time: CleanLimit,          *,          channels: CleanChannels = None      ) -> None:          """ -        Delete messages posted by the provided user, stop cleaning after reaching `message_or_time`. +        Delete messages posted by the provided users, stop cleaning after reaching `message_or_time`.          `message_or_time` can be either a message to stop at (exclusive), a timedelta for max message age, or an ISO          datetime. @@ -506,7 +506,7 @@ class Clean(Cog):          If a timedelta or an ISO datetime is specified, `channels` can be specified to clean across multiple channels.          An asterisk can also be used to designate cleanup across all channels.          """ -        await self._clean_messages(ctx, users=[user], channels=channels, first_limit=message_or_time) +        await self._clean_messages(ctx, users=users, channels=channels, first_limit=message_or_time)      @clean_group.command(name="bots", aliases=["bot"])      async def clean_bots(self, ctx: Context, message_or_time: CleanLimit, *, channels: CleanChannels = None) -> None: diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index 80f68e442..67991730e 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -6,12 +6,14 @@ from datetime import datetime, timezone  from itertools import zip_longest  import discord +from botcore.site_api import ResponseCodeError  from dateutil.relativedelta import relativedelta  from deepdiff import DeepDiff  from discord import Colour, Message, Thread  from discord.abc import GuildChannel  from discord.ext.commands import Cog, Context  from discord.utils import escape_markdown, format_dt, snowflake_time +from sentry_sdk import add_breadcrumb  from bot.bot import Bot  from bot.constants import Categories, Channels, Colours, Emojis, Event, Guild as GuildConstant, Icons, Roles, URLs @@ -53,24 +55,35 @@ class ModLog(Cog, name="ModLog"):          if attachments is None:              attachments = [] -        response = await self.bot.api_client.post( -            'bot/deleted-messages', -            json={ -                'actor': actor_id, -                'creation': datetime.now(timezone.utc).isoformat(), -                'deletedmessage_set': [ -                    { -                        'id': message.id, -                        'author': message.author.id, -                        'channel_id': message.channel.id, -                        'content': message.content.replace("\0", ""),  # Null chars cause 400. -                        'embeds': [embed.to_dict() for embed in message.embeds], -                        'attachments': attachment, -                    } -                    for message, attachment in zip_longest(messages, attachments, fillvalue=[]) -                ] +        deletedmessage_set = [ +            { +                "id": message.id, +                "author": message.author.id, +                "channel_id": message.channel.id, +                "content": message.content.replace("\0", ""),  # Null chars cause 400. +                "embeds": [embed.to_dict() for embed in message.embeds], +                "attachments": attachment,              } -        ) +            for message, attachment in zip_longest(messages, attachments, fillvalue=[]) +        ] + +        try: +            response = await self.bot.api_client.post( +                "bot/deleted-messages", +                json={ +                    "actor": actor_id, +                    "creation": datetime.now(timezone.utc).isoformat(), +                    "deletedmessage_set": deletedmessage_set, +                } +            ) +        except ResponseCodeError as e: +            add_breadcrumb( +                category="api_error", +                message=str(e), +                level="error", +                data=deletedmessage_set, +            ) +            raise          return f"{URLs.site_logs_view}/{response['id']}" diff --git a/bot/exts/utils/thread_bumper.py b/bot/exts/utils/thread_bumper.py index 7ffb79d5e..a2f208484 100644 --- a/bot/exts/utils/thread_bumper.py +++ b/bot/exts/utils/thread_bumper.py @@ -1,7 +1,6 @@  import typing as t  import discord -from async_rediscache import RedisCache  from botcore.site_api import ResponseCodeError  from discord.ext import commands @@ -18,9 +17,6 @@ THREAD_BUMP_ENDPOINT = "bot/bumped-threads"  class ThreadBumper(commands.Cog):      """Cog that allow users to add the current thread to a list that get reopened on archive.""" -    # RedisCache[discord.Thread.id, "sentinel"] -    threads_to_bump = RedisCache() -      def __init__(self, bot: Bot):          self.bot = bot diff --git a/poetry.lock b/poetry.lock index 267b9e22c..7e74cecdd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -144,11 +144,11 @@ type = "url"  url = "https://github.com/python-discord/bot-core/archive/refs/tags/v7.0.0.zip"  [[package]]  name = "certifi" -version = "2021.10.8" +version = "2022.5.18.1"  description = "Python package for providing Mozilla's CA Bundle."  category = "main"  optional = false -python-versions = "*" +python-versions = ">=3.6"  [[package]]  name = "cffi" @@ -297,16 +297,16 @@ testing = ["pre-commit"]  [[package]]  name = "fakeredis" -version = "1.7.4" +version = "1.7.5"  description = "Fake implementation of redis API for testing purposes."  category = "main"  optional = false -python-versions = ">=3.5" +python-versions = ">=3.7"  [package.dependencies]  lupa = {version = "*", optional = true, markers = "extra == \"lua\""}  packaging = "*" -redis = "<=4.2.2" +redis = "<=4.3.1"  six = ">=1.12"  sortedcontainers = "*" @@ -327,7 +327,7 @@ sgmllib3k = "*"  [[package]]  name = "filelock" -version = "3.6.0" +version = "3.7.0"  description = "A platform independent file lock."  category = "main"  optional = false @@ -478,7 +478,7 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve  [[package]]  name = "identify" -version = "2.5.0" +version = "2.5.1"  description = "File identification library for Python"  category = "dev"  optional = false @@ -549,15 +549,15 @@ source = ["Cython (>=0.29.7)"]  [[package]]  name = "markdownify" -version = "0.10.3" +version = "0.6.1"  description = "Convert HTML to markdown."  category = "main"  optional = false  python-versions = "*"  [package.dependencies] -beautifulsoup4 = ">=4.9,<5" -six = ">=1.15,<2" +beautifulsoup4 = "*" +six = "*"  [[package]]  name = "mccabe" @@ -686,14 +686,14 @@ virtualenv = ">=20.0.8"  [[package]]  name = "psutil" -version = "5.9.0" +version = "5.9.1"  description = "Cross-platform lib for process and system monitoring in Python."  category = "dev"  optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"  [package.extras] -test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] +test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"]  [[package]]  name = "ptable" @@ -765,7 +765,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"  [[package]]  name = "pyparsing" -version = "3.0.8" +version = "3.0.9"  description = "pyparsing module - Classes and methods to define and execute parsing grammars"  category = "main"  optional = false @@ -909,7 +909,7 @@ full = ["numpy"]  [[package]]  name = "redis" -version = "4.2.2" +version = "4.3.1"  description = "Python client for Redis database and key-value store"  category = "main"  optional = false @@ -1151,7 +1151,7 @@ multidict = ">=4.0"  [metadata]  lock-version = "1.1"  python-versions = "3.9.*" -content-hash = "cc77bc7d6bb7940767359a860b1ade2014573bab3046b864c3d265b4eee2cb7b" +content-hash = "953529931f133865df736f9a6f96f59c64336963ef9e6ce6c959e6bd8c73792c"  [metadata.files]  aiodns = [ @@ -1266,8 +1266,8 @@ beautifulsoup4 = [  ]  bot-core = []  certifi = [ -    {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, -    {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +    {file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"}, +    {file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"},  ]  cffi = [      {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, @@ -1401,16 +1401,16 @@ execnet = [      {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},  ]  fakeredis = [ -    {file = "fakeredis-1.7.4-py3-none-any.whl", hash = "sha256:cc033ebf9af9f42bba6aa538a3e1a9f1732686b8b7e9ef50c7a44955bbc2aff8"}, -    {file = "fakeredis-1.7.4.tar.gz", hash = "sha256:69697ffeeb09939073605eeac97f524bccabae04265757a575c7fc923087aa65"}, +    {file = "fakeredis-1.7.5-py3-none-any.whl", hash = "sha256:c4ca2be686e7e7637756ccc7dcad8472a5e4866b065431107d7a4b7a250d4e6f"}, +    {file = "fakeredis-1.7.5.tar.gz", hash = "sha256:49375c630981dd4045d9a92e2709fcd4476c91f927e0228493eefa625e705133"},  ]  feedparser = [      {file = "feedparser-6.0.8-py3-none-any.whl", hash = "sha256:1b7f57841d9cf85074deb316ed2c795091a238adb79846bc46dccdaf80f9c59a"},      {file = "feedparser-6.0.8.tar.gz", hash = "sha256:5ce0410a05ab248c8c7cfca3a0ea2203968ee9ff4486067379af4827a59f9661"},  ]  filelock = [ -    {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, -    {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, +    {file = "filelock-3.7.0-py3-none-any.whl", hash = "sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6"}, +    {file = "filelock-3.7.0.tar.gz", hash = "sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20"},  ]  flake8 = [      {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, @@ -1556,8 +1556,8 @@ humanfriendly = [      {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"},  ]  identify = [ -    {file = "identify-2.5.0-py2.py3-none-any.whl", hash = "sha256:3acfe15a96e4272b4ec5662ee3e231ceba976ef63fd9980ed2ce9cc415df393f"}, -    {file = "identify-2.5.0.tar.gz", hash = "sha256:c83af514ea50bf2be2c4a3f2fb349442b59dc87284558ae9ff54191bff3541d2"}, +    {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, +    {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"},  ]  idna = [      {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, @@ -1785,8 +1785,8 @@ lxml = [      {file = "lxml-4.8.0.tar.gz", hash = "sha256:f63f62fc60e6228a4ca9abae28228f35e1bd3ce675013d1dfb828688d50c6e23"},  ]  markdownify = [ -    {file = "markdownify-0.10.3-py3-none-any.whl", hash = "sha256:edad0ad3896ec7460d05537ad804bbb3614877c6cd0df27b56dee218236d9ce2"}, -    {file = "markdownify-0.10.3.tar.gz", hash = "sha256:782e310390cd5e4bde7543ceb644598c78b9824ee9f8d7ef9f9f4f8782e46974"}, +    {file = "markdownify-0.6.1-py3-none-any.whl", hash = "sha256:7489fd5c601536996a376c4afbcd1dd034db7690af807120681461e82fbc0acc"}, +    {file = "markdownify-0.6.1.tar.gz", hash = "sha256:31d7c13ac2ada8bfc7535a25fee6622ca720e1b5f2d4a9cbc429d167c21f886d"},  ]  mccabe = [      {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, @@ -1893,38 +1893,38 @@ pre-commit = [      {file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"},  ]  psutil = [ -    {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, -    {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, -    {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"}, -    {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"}, -    {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"}, -    {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"}, -    {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"}, -    {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"}, -    {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"}, -    {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"}, -    {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"}, -    {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"}, -    {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"}, -    {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"}, -    {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"}, -    {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"}, -    {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"}, -    {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"}, -    {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"}, -    {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"}, -    {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"}, -    {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"}, -    {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"}, -    {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"}, -    {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"}, -    {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"}, -    {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"}, -    {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"}, -    {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"}, -    {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"}, -    {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"}, -    {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, +    {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, +    {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, +    {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, +    {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, +    {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, +    {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, +    {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, +    {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, +    {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, +    {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, +    {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, +    {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, +    {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, +    {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, +    {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, +    {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, +    {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, +    {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, +    {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, +    {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, +    {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, +    {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, +    {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, +    {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, +    {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, +    {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, +    {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, +    {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, +    {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, +    {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, +    {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, +    {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"},  ]  ptable = [      {file = "PTable-0.9.2.tar.gz", hash = "sha256:aa7fc151cb40f2dabcd2275ba6f7fd0ff8577a86be3365cd3fb297cbe09cc292"}, @@ -1983,8 +1983,8 @@ pyflakes = [      {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},  ]  pyparsing = [ -    {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, -    {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, +    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, +    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},  ]  pyreadline3 = [      {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, @@ -2099,8 +2099,8 @@ rapidfuzz = [      {file = "rapidfuzz-2.0.7.tar.gz", hash = "sha256:93bf42784fd74ebf1a8e89ca1596e9bea7f3ac4a61b825ecc6eb2d9893ad6844"},  ]  redis = [ -    {file = "redis-4.2.2-py3-none-any.whl", hash = "sha256:4e95f4ec5f49e636efcf20061a5a9110c20852f607cfca6865c07aaa8a739ee2"}, -    {file = "redis-4.2.2.tar.gz", hash = "sha256:0107dc8e98a4f1d1d4aa00100e044287f77121a1e6d2085545c4b7fa94a7a27f"}, +    {file = "redis-4.3.1-py3-none-any.whl", hash = "sha256:84316970995a7adb907a56754d2b92d88fc2d252963dc5ac34c88f0f1a22c25d"}, +    {file = "redis-4.3.1.tar.gz", hash = "sha256:94b617b4cd296e94991146f66fc5559756fbefe9493604f0312e4d3298ac63e9"},  ]  regex = [      {file = "regex-2022.3.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:42eb13b93765c6698a5ab3bcd318d8c39bb42e5fa8a7fcf7d8d98923f3babdb1"}, diff --git a/pyproject.toml b/pyproject.toml index fd5c0c71a..2d6adb9c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ emoji = "1.7.0"  feedparser = "6.0.8"  rapidfuzz = "2.0.7"  lxml = "4.8.0" -markdownify = "0.10.3" +markdownify = "0.6.1"  more_itertools = "8.12.0"  python-dateutil = "2.8.2"  python-frontmatter = "1.0.0" diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py index d02bd7c34..0a58126e7 100644 --- a/tests/bot/exts/backend/test_error_handler.py +++ b/tests/bot/exts/backend/test_error_handler.py @@ -48,6 +48,7 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):          cog = ErrorHandler(self.bot)          cog.try_silence = AsyncMock()          cog.try_get_tag = AsyncMock() +        cog.try_run_eval = AsyncMock(return_value=False)          for case in test_cases:              with self.subTest(try_silence_return=case["try_silence_return"], try_get_tag=case["called_try_get_tag"]): @@ -76,6 +77,7 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):          cog = ErrorHandler(self.bot)          cog.try_silence = AsyncMock()          cog.try_get_tag = AsyncMock() +        cog.try_run_eval = AsyncMock()          error = errors.CommandNotFound() @@ -83,6 +85,7 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):          cog.try_silence.assert_not_awaited()          cog.try_get_tag.assert_not_awaited() +        cog.try_run_eval.assert_not_awaited()          self.ctx.send.assert_not_awaited()      async def test_error_handler_user_input_error(self): | 
