aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/__main__.py6
-rw-r--r--bot/exts/backend/alias.py87
-rw-r--r--bot/exts/fun/duck_pond.py13
-rw-r--r--bot/exts/fun/off_topic_names.py5
-rw-r--r--bot/exts/info/doc.py2
-rw-r--r--bot/exts/info/reddit.py8
-rw-r--r--bot/exts/info/source.py24
-rw-r--r--bot/exts/info/tags.py2
-rw-r--r--bot/exts/moderation/infraction/management.py4
-rw-r--r--bot/exts/utils/reminders.py2
-rw-r--r--bot/exts/utils/snekbox.py4
-rw-r--r--bot/exts/utils/utils.py4
-rw-r--r--bot/patches/__init__.py6
-rw-r--r--bot/patches/message_edited_at.py32
-rw-r--r--tests/bot/exts/backend/sync/test_cog.py4
-rw-r--r--tests/bot/exts/info/test_information.py106
-rw-r--r--tests/bot/exts/moderation/test_silence.py4
-rw-r--r--tests/bot/exts/utils/test_snekbox.py14
-rw-r--r--tests/bot/patches/__init__.py0
19 files changed, 99 insertions, 228 deletions
diff --git a/bot/__main__.py b/bot/__main__.py
index a07bc21d6..152ddbf92 100644
--- a/bot/__main__.py
+++ b/bot/__main__.py
@@ -9,7 +9,7 @@ from sentry_sdk.integrations.aiohttp import AioHttpIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
from sentry_sdk.integrations.redis import RedisIntegration
-from bot import constants, patches
+from bot import constants
from bot.bot import Bot
from bot.utils.extensions import EXTENSIONS
@@ -65,8 +65,4 @@ if not constants.HelpChannels.enable:
for extension in extensions:
bot.load_extension(extension)
-# Apply `message_edited_at` patch if discord.py did not yet release a bug fix.
-if not hasattr(discord.message.Message, '_handle_edited_timestamp'):
- patches.message_edited_at.apply_patch()
-
bot.run(constants.Bot.token)
diff --git a/bot/exts/backend/alias.py b/bot/exts/backend/alias.py
deleted file mode 100644
index c6ba8d6f3..000000000
--- a/bot/exts/backend/alias.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import inspect
-import logging
-
-from discord import Colour, Embed
-from discord.ext.commands import (
- Cog, Command, Context,
- clean_content, command, group,
-)
-
-from bot.bot import Bot
-from bot.converters import TagNameConverter
-from bot.pagination import LinePaginator
-
-log = logging.getLogger(__name__)
-
-
-class Alias (Cog):
- """Aliases for commonly used commands."""
-
- def __init__(self, bot: Bot):
- self.bot = bot
-
- async def invoke(self, ctx: Context, cmd_name: str, *args, **kwargs) -> None:
- """Invokes a command with args and kwargs."""
- log.debug(f"{cmd_name} was invoked through an alias")
- cmd = self.bot.get_command(cmd_name)
- if not cmd:
- return log.info(f'Did not find command "{cmd_name}" to invoke.')
- elif not await cmd.can_run(ctx):
- return log.info(
- f'{str(ctx.author)} tried to run the command "{cmd_name}" but lacks permission.'
- )
-
- await ctx.invoke(cmd, *args, **kwargs)
-
- @command(name='aliases')
- async def aliases_command(self, ctx: Context) -> None:
- """Show configured aliases on the bot."""
- embed = Embed(
- title='Configured aliases',
- colour=Colour.blue()
- )
- await LinePaginator.paginate(
- (
- f"• `{ctx.prefix}{value.name}` "
- f"=> `{ctx.prefix}{name[:-len('_alias')].replace('_', ' ')}`"
- for name, value in inspect.getmembers(self)
- if isinstance(value, Command) and name.endswith('_alias')
- ),
- ctx, embed, empty=False, max_lines=20
- )
-
- @command(name="exception", hidden=True)
- async def tags_get_traceback_alias(self, ctx: Context) -> None:
- """Alias for invoking <prefix>tags get traceback."""
- await self.invoke(ctx, "tags get", tag_name="traceback")
-
- @group(name="get",
- aliases=("show", "g"),
- hidden=True,
- invoke_without_command=True)
- async def get_group_alias(self, ctx: Context) -> None:
- """Group for reverse aliases for commands like `tags get`, allowing for `get tags` or `get docs`."""
- pass
-
- @get_group_alias.command(name="tags", aliases=("tag", "t"), hidden=True)
- async def tags_get_alias(
- self, ctx: Context, *, tag_name: TagNameConverter = None
- ) -> None:
- """
- Alias for invoking <prefix>tags get [tag_name].
-
- tag_name: str - tag to be viewed.
- """
- await self.invoke(ctx, "tags get", tag_name=tag_name)
-
- @get_group_alias.command(name="docs", aliases=("doc", "d"), hidden=True)
- async def docs_get_alias(
- self, ctx: Context, symbol: clean_content = None
- ) -> None:
- """Alias for invoking <prefix>docs get [symbol]."""
- await self.invoke(ctx, "docs get", symbol)
-
-
-def setup(bot: Bot) -> None:
- """Load the Alias cog."""
- bot.add_cog(Alias(bot))
diff --git a/bot/exts/fun/duck_pond.py b/bot/exts/fun/duck_pond.py
index 6c2d22b9c..82084ea88 100644
--- a/bot/exts/fun/duck_pond.py
+++ b/bot/exts/fun/duck_pond.py
@@ -145,6 +145,10 @@ class DuckPond(Cog):
amount of ducks specified in the config under duck_pond/threshold, it will
send the message off to the duck pond.
"""
+ # Ignore other guilds and DMs.
+ if payload.guild_id != constants.Guild.id:
+ return
+
# Was this reaction issued in a blacklisted channel?
if payload.channel_id in constants.DuckPond.channel_blacklist:
return
@@ -154,6 +158,9 @@ class DuckPond(Cog):
return
channel = discord.utils.get(self.bot.get_all_channels(), id=payload.channel_id)
+ if channel is None:
+ return
+
message = await channel.fetch_message(payload.message_id)
member = discord.utils.get(message.guild.members, id=payload.user_id)
@@ -175,7 +182,13 @@ class DuckPond(Cog):
@Cog.listener()
async def on_raw_reaction_remove(self, payload: RawReactionActionEvent) -> None:
"""Ensure that people don't remove the green checkmark from duck ponded messages."""
+ # Ignore other guilds and DMs.
+ if payload.guild_id != constants.Guild.id:
+ return
+
channel = discord.utils.get(self.bot.get_all_channels(), id=payload.channel_id)
+ if channel is None:
+ return
# Prevent the green checkmark from being removed
if payload.emoji.name == "✅":
diff --git a/bot/exts/fun/off_topic_names.py b/bot/exts/fun/off_topic_names.py
index b9d235fa2..7fc93b88c 100644
--- a/bot/exts/fun/off_topic_names.py
+++ b/bot/exts/fun/off_topic_names.py
@@ -1,10 +1,10 @@
-import asyncio
import difflib
import logging
from datetime import datetime, timedelta
from discord import Colour, Embed
from discord.ext.commands import Cog, Context, group, has_any_role
+from discord.utils import sleep_until
from bot.api import ResponseCodeError
from bot.bot import Bot
@@ -23,8 +23,7 @@ async def update_names(bot: Bot) -> None:
# we go past midnight in the `seconds_to_sleep` set below.
today_at_midnight = datetime.utcnow().replace(microsecond=0, second=0, minute=0, hour=0)
next_midnight = today_at_midnight + timedelta(days=1)
- seconds_to_sleep = (next_midnight - datetime.utcnow()).seconds + 1
- await asyncio.sleep(seconds_to_sleep)
+ await sleep_until(next_midnight)
try:
channel_0_name, channel_1_name, channel_2_name = await bot.api_client.get(
diff --git a/bot/exts/info/doc.py b/bot/exts/info/doc.py
index e50b9b32b..c16a99225 100644
--- a/bot/exts/info/doc.py
+++ b/bot/exts/info/doc.py
@@ -345,7 +345,7 @@ class Doc(commands.Cog):
@commands.group(name='docs', aliases=('doc', 'd'), invoke_without_command=True)
async def docs_group(self, ctx: commands.Context, symbol: commands.clean_content = None) -> None:
"""Lookup documentation for Python symbols."""
- await ctx.invoke(self.get_command, symbol)
+ await self.get_command(ctx, symbol)
@docs_group.command(name='get', aliases=('g',))
async def get_command(self, ctx: commands.Context, symbol: commands.clean_content = None) -> None:
diff --git a/bot/exts/info/reddit.py b/bot/exts/info/reddit.py
index 635162308..debe40c82 100644
--- a/bot/exts/info/reddit.py
+++ b/bot/exts/info/reddit.py
@@ -10,7 +10,7 @@ from aiohttp import BasicAuth, ClientError
from discord import Colour, Embed, TextChannel
from discord.ext.commands import Cog, Context, group, has_any_role
from discord.ext.tasks import loop
-from discord.utils import escape_markdown
+from discord.utils import escape_markdown, sleep_until
from bot.bot import Bot
from bot.constants import Channels, ERROR_REPLIES, Emojis, Reddit as RedditConfig, STAFF_ROLES, Webhooks
@@ -203,13 +203,13 @@ class Reddit(Cog):
@loop()
async def auto_poster_loop(self) -> None:
"""Post the top 5 posts daily, and the top 5 posts weekly."""
- # once we upgrade to d.py 1.3 this can be removed and the loop can use the `time=datetime.time.min` parameter
+ # once d.py get support for `time` parameter in loop decorator,
+ # this can be removed and the loop can use the `time=datetime.time.min` parameter
now = datetime.utcnow()
tomorrow = now + timedelta(days=1)
midnight_tomorrow = tomorrow.replace(hour=0, minute=0, second=0)
- seconds_until = (midnight_tomorrow - now).total_seconds()
- await asyncio.sleep(seconds_until)
+ await sleep_until(midnight_tomorrow)
await self.bot.wait_until_guild_available()
if not self.webhook:
diff --git a/bot/exts/info/source.py b/bot/exts/info/source.py
index 205e0ba81..7b41352d4 100644
--- a/bot/exts/info/source.py
+++ b/bot/exts/info/source.py
@@ -2,7 +2,7 @@ import inspect
from pathlib import Path
from typing import Optional, Tuple, Union
-from discord import Embed
+from discord import Embed, utils
from discord.ext import commands
from bot.bot import Bot
@@ -35,8 +35,10 @@ class SourceConverter(commands.Converter):
elif argument.lower() in tags_cog._cache:
return argument.lower()
+ escaped_arg = utils.escape_markdown(argument)
+
raise commands.BadArgument(
- f"Unable to convert `{argument}` to valid command{', tag,' if show_tag else ''} or Cog."
+ f"Unable to convert '{escaped_arg}' to valid command{', tag,' if show_tag else ''} or Cog."
)
@@ -66,14 +68,8 @@ class BotSource(commands.Cog):
Raise BadArgument if `source_item` is a dynamically-created object (e.g. via internal eval).
"""
if isinstance(source_item, commands.Command):
- if source_item.cog_name == "Alias":
- cmd_name = source_item.callback.__name__.replace("_alias", "")
- cmd = self.bot.get_command(cmd_name.replace("_", " "))
- src = cmd.callback.__code__
- filename = src.co_filename
- else:
- src = source_item.callback.__code__
- filename = src.co_filename
+ src = source_item.callback.__code__
+ filename = src.co_filename
elif isinstance(source_item, str):
tags_cog = self.bot.get_cog("Tags")
filename = tags_cog._cache[source_item]["location"]
@@ -113,13 +109,7 @@ class BotSource(commands.Cog):
title = "Help Command"
description = source_object.__doc__.splitlines()[1]
elif isinstance(source_object, commands.Command):
- if source_object.cog_name == "Alias":
- cmd_name = source_object.callback.__name__.replace("_alias", "")
- cmd = self.bot.get_command(cmd_name.replace("_", " "))
- description = cmd.short_doc
- else:
- description = source_object.short_doc
-
+ description = source_object.short_doc
title = f"Command: {source_object.qualified_name}"
elif isinstance(source_object, str):
title = f"Tag: {source_object}"
diff --git a/bot/exts/info/tags.py b/bot/exts/info/tags.py
index d01647312..ae95ac1ef 100644
--- a/bot/exts/info/tags.py
+++ b/bot/exts/info/tags.py
@@ -160,7 +160,7 @@ class Tags(Cog):
@group(name='tags', aliases=('tag', 't'), invoke_without_command=True)
async def tags_group(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:
"""Show all known tags, a single tag, or run a subcommand."""
- await ctx.invoke(self.get_command, tag_name=tag_name)
+ await self.get_command(ctx, tag_name=tag_name)
@tags_group.group(name='search', invoke_without_command=True)
async def search_tag_content(self, ctx: Context, *, keywords: str) -> None:
diff --git a/bot/exts/moderation/infraction/management.py b/bot/exts/moderation/infraction/management.py
index 856a4e1a2..cdab1a6c7 100644
--- a/bot/exts/moderation/infraction/management.py
+++ b/bot/exts/moderation/infraction/management.py
@@ -179,9 +179,9 @@ class ModManagement(commands.Cog):
async def infraction_search_group(self, ctx: Context, query: t.Union[UserMention, Snowflake, str]) -> None:
"""Searches for infractions in the database."""
if isinstance(query, int):
- await ctx.invoke(self.search_user, discord.Object(query))
+ await self.search_user(ctx, discord.Object(query))
else:
- await ctx.invoke(self.search_reason, query)
+ await self.search_reason(ctx, query)
@infraction_search_group.command(name="user", aliases=("member", "id"))
async def search_user(self, ctx: Context, user: t.Union[discord.User, proxy_user]) -> None:
diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py
index 6806f2889..efba7ad6e 100644
--- a/bot/exts/utils/reminders.py
+++ b/bot/exts/utils/reminders.py
@@ -228,7 +228,7 @@ class Reminders(Cog):
self, ctx: Context, mentions: Greedy[Mentionable], expiration: Duration, *, content: str
) -> None:
"""Commands for managing your reminders."""
- await ctx.invoke(self.new_reminder, mentions=mentions, expiration=expiration, content=content)
+ await self.new_reminder(ctx, mentions=mentions, expiration=expiration, content=content)
@remind_group.command(name="new", aliases=("add", "create"))
async def new_reminder(
diff --git a/bot/exts/utils/snekbox.py b/bot/exts/utils/snekbox.py
index 18b9a5014..ca6fbf5cb 100644
--- a/bot/exts/utils/snekbox.py
+++ b/bot/exts/utils/snekbox.py
@@ -241,12 +241,12 @@ class Snekbox(Cog):
)
code = await self.get_code(new_message)
- await ctx.message.clear_reactions()
+ await ctx.message.clear_reaction(REEVAL_EMOJI)
with contextlib.suppress(HTTPException):
await response.delete()
except asyncio.TimeoutError:
- await ctx.message.clear_reactions()
+ await ctx.message.clear_reaction(REEVAL_EMOJI)
return None
return code
diff --git a/bot/exts/utils/utils.py b/bot/exts/utils/utils.py
index 6b6941064..3e9230414 100644
--- a/bot/exts/utils/utils.py
+++ b/bot/exts/utils/utils.py
@@ -84,7 +84,7 @@ class Utils(Cog):
# Assemble the embed
pep_embed = Embed(
title=f"**PEP {pep_number} - {pep_header['Title']}**",
- description=f"[Link]({self.base_pep_url}{pep_number:04})",
+ url=f"{self.base_pep_url}{pep_number:04}"
)
pep_embed.set_thumbnail(url=ICON_URL)
@@ -250,7 +250,7 @@ class Utils(Cog):
"""Send information about PEP 0."""
pep_embed = Embed(
title="**PEP 0 - Index of Python Enhancement Proposals (PEPs)**",
- description="[Link](https://www.python.org/dev/peps/)"
+ url="https://www.python.org/dev/peps/"
)
pep_embed.set_thumbnail(url=ICON_URL)
pep_embed.add_field(name="Status", value="Active")
diff --git a/bot/patches/__init__.py b/bot/patches/__init__.py
deleted file mode 100644
index 60f6becaa..000000000
--- a/bot/patches/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""Subpackage that contains patches for discord.py."""
-from . import message_edited_at
-
-__all__ = [
- message_edited_at,
-]
diff --git a/bot/patches/message_edited_at.py b/bot/patches/message_edited_at.py
deleted file mode 100644
index a0154f12d..000000000
--- a/bot/patches/message_edited_at.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-# message_edited_at patch.
-
-Date: 2019-09-16
-Author: Scragly
-Added by: Ves Zappa
-
-Due to a bug in our current version of discord.py (1.2.3), the edited_at timestamp of
-`discord.Messages` are not being handled correctly. This patch fixes that until a new
-release of discord.py is released (and we've updated to it).
-"""
-import logging
-
-from discord import message, utils
-
-log = logging.getLogger(__name__)
-
-
-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)
-
-
-def apply_patch() -> None:
- """Applies the `edited_at` patch to the `discord.message.Message` class."""
- message.Message._handle_edited_timestamp = _handle_edited_timestamp
- message.Message._HANDLERS['edited_timestamp'] = message.Message._handle_edited_timestamp
- log.info("Patch applied: message_edited_at")
-
-
-if __name__ == "__main__":
- apply_patch()
diff --git a/tests/bot/exts/backend/sync/test_cog.py b/tests/bot/exts/backend/sync/test_cog.py
index 1b89564f2..063a82754 100644
--- a/tests/bot/exts/backend/sync/test_cog.py
+++ b/tests/bot/exts/backend/sync/test_cog.py
@@ -392,14 +392,14 @@ class SyncCogCommandTests(SyncCogTestCase, CommandTestCase):
async def test_sync_roles_command(self):
"""sync() should be called on the RoleSyncer."""
ctx = helpers.MockContext()
- await self.cog.sync_roles_command.callback(self.cog, ctx)
+ await self.cog.sync_roles_command(self.cog, ctx)
self.cog.role_syncer.sync.assert_called_once_with(ctx.guild, ctx)
async def test_sync_users_command(self):
"""sync() should be called on the UserSyncer."""
ctx = helpers.MockContext()
- await self.cog.sync_users_command.callback(self.cog, ctx)
+ await self.cog.sync_users_command(self.cog, ctx)
self.cog.user_syncer.sync.assert_called_once_with(ctx.guild, ctx)
diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py
index d3f2995fb..36a35c8e2 100644
--- a/tests/bot/exts/info/test_information.py
+++ b/tests/bot/exts/info/test_information.py
@@ -1,4 +1,3 @@
-import asyncio
import textwrap
import unittest
import unittest.mock
@@ -13,7 +12,7 @@ from tests import helpers
COG_PATH = "bot.exts.info.information.Information"
-class InformationCogTests(unittest.TestCase):
+class InformationCogTests(unittest.IsolatedAsyncioTestCase):
"""Tests the Information cog."""
@classmethod
@@ -29,16 +28,14 @@ class InformationCogTests(unittest.TestCase):
self.ctx = helpers.MockContext()
self.ctx.author.roles.append(self.moderator_role)
- def test_roles_command_command(self):
+ async def test_roles_command_command(self):
"""Test if the `role_info` command correctly returns the `moderator_role`."""
self.ctx.guild.roles.append(self.moderator_role)
self.cog.roles_info.can_run = unittest.mock.AsyncMock()
self.cog.roles_info.can_run.return_value = True
- coroutine = self.cog.roles_info.callback(self.cog, self.ctx)
-
- self.assertIsNone(asyncio.run(coroutine))
+ self.assertIsNone(await self.cog.roles_info(self.cog, self.ctx))
self.ctx.send.assert_called_once()
_, kwargs = self.ctx.send.call_args
@@ -48,7 +45,7 @@ class InformationCogTests(unittest.TestCase):
self.assertEqual(embed.colour, discord.Colour.blurple())
self.assertEqual(embed.description, f"\n`{self.moderator_role.id}` - {self.moderator_role.mention}\n")
- def test_role_info_command(self):
+ async def test_role_info_command(self):
"""Tests the `role info` command."""
dummy_role = helpers.MockRole(
name="Dummy",
@@ -73,9 +70,7 @@ class InformationCogTests(unittest.TestCase):
self.cog.role_info.can_run = unittest.mock.AsyncMock()
self.cog.role_info.can_run.return_value = True
- coroutine = self.cog.role_info.callback(self.cog, self.ctx, dummy_role, admin_role)
-
- self.assertIsNone(asyncio.run(coroutine))
+ self.assertIsNone(await self.cog.role_info(self.cog, self.ctx, dummy_role, admin_role))
self.assertEqual(self.ctx.send.call_count, 2)
@@ -98,7 +93,7 @@ class InformationCogTests(unittest.TestCase):
self.assertEqual(admin_embed.colour, discord.Colour.red())
@unittest.mock.patch('bot.exts.info.information.time_since')
- def test_server_info_command(self, time_since_patch):
+ async def test_server_info_command(self, time_since_patch):
time_since_patch.return_value = '2 days ago'
self.ctx.guild = helpers.MockGuild(
@@ -132,8 +127,7 @@ class InformationCogTests(unittest.TestCase):
icon_url='a-lemon.jpg',
)
- coroutine = self.cog.server_info.callback(self.cog, self.ctx)
- self.assertIsNone(asyncio.run(coroutine))
+ self.assertIsNone(await self.cog.server_info(self.cog, self.ctx))
time_since_patch.assert_called_once_with(self.ctx.guild.created_at, precision='days')
_, kwargs = self.ctx.send.call_args
@@ -170,7 +164,7 @@ class InformationCogTests(unittest.TestCase):
self.assertEqual(embed.thumbnail.url, 'a-lemon.jpg')
-class UserInfractionHelperMethodTests(unittest.TestCase):
+class UserInfractionHelperMethodTests(unittest.IsolatedAsyncioTestCase):
"""Tests for the helper methods of the `!user` command."""
def setUp(self):
@@ -180,7 +174,7 @@ class UserInfractionHelperMethodTests(unittest.TestCase):
self.cog = information.Information(self.bot)
self.member = helpers.MockMember(id=1234)
- def test_user_command_helper_method_get_requests(self):
+ async def test_user_command_helper_method_get_requests(self):
"""The helper methods should form the correct get requests."""
test_values = (
{
@@ -202,11 +196,11 @@ class UserInfractionHelperMethodTests(unittest.TestCase):
endpoint, params = test_value["expected_args"]
with self.subTest(method=helper_method, endpoint=endpoint, params=params):
- asyncio.run(helper_method(self.member))
+ await helper_method(self.member)
self.bot.api_client.get.assert_called_once_with(endpoint, params=params)
self.bot.api_client.get.reset_mock()
- def _method_subtests(self, method, test_values, default_header):
+ async def _method_subtests(self, method, test_values, default_header):
"""Helper method that runs the subtests for the different helper methods."""
for test_value in test_values:
api_response = test_value["api response"]
@@ -216,11 +210,11 @@ class UserInfractionHelperMethodTests(unittest.TestCase):
self.bot.api_client.get.return_value = api_response
expected_output = "\n".join(expected_lines)
- actual_output = asyncio.run(method(self.member))
+ actual_output = await method(self.member)
self.assertEqual((default_header, expected_output), actual_output)
- def test_basic_user_infraction_counts_returns_correct_strings(self):
+ async def test_basic_user_infraction_counts_returns_correct_strings(self):
"""The method should correctly list both the total and active number of non-hidden infractions."""
test_values = (
# No infractions means zero counts
@@ -251,9 +245,9 @@ class UserInfractionHelperMethodTests(unittest.TestCase):
header = "Infractions"
- self._method_subtests(self.cog.basic_user_infraction_counts, test_values, header)
+ await self._method_subtests(self.cog.basic_user_infraction_counts, test_values, header)
- def test_expanded_user_infraction_counts_returns_correct_strings(self):
+ async def test_expanded_user_infraction_counts_returns_correct_strings(self):
"""The method should correctly list the total and active number of all infractions split by infraction type."""
test_values = (
{
@@ -306,9 +300,9 @@ class UserInfractionHelperMethodTests(unittest.TestCase):
header = "Infractions"
- self._method_subtests(self.cog.expanded_user_infraction_counts, test_values, header)
+ await self._method_subtests(self.cog.expanded_user_infraction_counts, test_values, header)
- def test_user_nomination_counts_returns_correct_strings(self):
+ async def test_user_nomination_counts_returns_correct_strings(self):
"""The method should list the number of active and historical nominations for the user."""
test_values = (
{
@@ -336,12 +330,12 @@ class UserInfractionHelperMethodTests(unittest.TestCase):
header = "Nominations"
- self._method_subtests(self.cog.user_nomination_counts, test_values, header)
+ await self._method_subtests(self.cog.user_nomination_counts, test_values, header)
@unittest.mock.patch("bot.exts.info.information.time_since", new=unittest.mock.MagicMock(return_value="1 year ago"))
@unittest.mock.patch("bot.exts.info.information.constants.MODERATION_CHANNELS", new=[50])
-class UserEmbedTests(unittest.TestCase):
+class UserEmbedTests(unittest.IsolatedAsyncioTestCase):
"""Tests for the creation of the `!user` embed."""
def setUp(self):
@@ -354,14 +348,14 @@ class UserEmbedTests(unittest.TestCase):
f"{COG_PATH}.basic_user_infraction_counts",
new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))
)
- def test_create_user_embed_uses_string_representation_of_user_in_title_if_nick_is_not_available(self):
+ async def test_create_user_embed_uses_string_representation_of_user_in_title_if_nick_is_not_available(self):
"""The embed should use the string representation of the user if they don't have a nick."""
ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1))
user = helpers.MockMember()
user.nick = None
user.__str__ = unittest.mock.Mock(return_value="Mr. Hemlock")
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
self.assertEqual(embed.title, "Mr. Hemlock")
@@ -369,14 +363,14 @@ class UserEmbedTests(unittest.TestCase):
f"{COG_PATH}.basic_user_infraction_counts",
new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))
)
- def test_create_user_embed_uses_nick_in_title_if_available(self):
+ async def test_create_user_embed_uses_nick_in_title_if_available(self):
"""The embed should use the nick if it's available."""
ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1))
user = helpers.MockMember()
user.nick = "Cat lover"
user.__str__ = unittest.mock.Mock(return_value="Mr. Hemlock")
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
self.assertEqual(embed.title, "Cat lover (Mr. Hemlock)")
@@ -384,7 +378,7 @@ class UserEmbedTests(unittest.TestCase):
f"{COG_PATH}.basic_user_infraction_counts",
new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))
)
- def test_create_user_embed_ignores_everyone_role(self):
+ async def test_create_user_embed_ignores_everyone_role(self):
"""Created `!user` embeds should not contain mention of the @everyone-role."""
ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1))
admins_role = helpers.MockRole(name='Admins')
@@ -393,14 +387,18 @@ class UserEmbedTests(unittest.TestCase):
# A `MockMember` has the @Everyone role by default; we add the Admins to that.
user = helpers.MockMember(roles=[admins_role], top_role=admins_role)
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
self.assertIn("&Admins", embed.fields[1].value)
self.assertNotIn("&Everyone", embed.fields[1].value)
@unittest.mock.patch(f"{COG_PATH}.expanded_user_infraction_counts", new_callable=unittest.mock.AsyncMock)
@unittest.mock.patch(f"{COG_PATH}.user_nomination_counts", new_callable=unittest.mock.AsyncMock)
- def test_create_user_embed_expanded_information_in_moderation_channels(self, nomination_counts, infraction_counts):
+ async def test_create_user_embed_expanded_information_in_moderation_channels(
+ self,
+ nomination_counts,
+ infraction_counts
+ ):
"""The embed should contain expanded infractions and nomination info in mod channels."""
ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=50))
@@ -411,7 +409,7 @@ class UserEmbedTests(unittest.TestCase):
nomination_counts.return_value = ("Nominations", "nomination info")
user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role)
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
infraction_counts.assert_called_once_with(user)
nomination_counts.assert_called_once_with(user)
@@ -434,7 +432,7 @@ class UserEmbedTests(unittest.TestCase):
)
@unittest.mock.patch(f"{COG_PATH}.basic_user_infraction_counts", new_callable=unittest.mock.AsyncMock)
- def test_create_user_embed_basic_information_outside_of_moderation_channels(self, infraction_counts):
+ async def test_create_user_embed_basic_information_outside_of_moderation_channels(self, infraction_counts):
"""The embed should contain only basic infraction data outside of mod channels."""
ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=100))
@@ -444,7 +442,7 @@ class UserEmbedTests(unittest.TestCase):
infraction_counts.return_value = ("Infractions", "basic infractions info")
user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role)
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
infraction_counts.assert_called_once_with(user)
@@ -474,7 +472,7 @@ class UserEmbedTests(unittest.TestCase):
f"{COG_PATH}.basic_user_infraction_counts",
new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))
)
- def test_create_user_embed_uses_top_role_colour_when_user_has_roles(self):
+ async def test_create_user_embed_uses_top_role_colour_when_user_has_roles(self):
"""The embed should be created with the colour of the top role, if a top role is available."""
ctx = helpers.MockContext()
@@ -482,7 +480,7 @@ class UserEmbedTests(unittest.TestCase):
moderators_role.colour = 100
user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role)
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
self.assertEqual(embed.colour, discord.Colour(moderators_role.colour))
@@ -490,12 +488,12 @@ class UserEmbedTests(unittest.TestCase):
f"{COG_PATH}.basic_user_infraction_counts",
new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))
)
- def test_create_user_embed_uses_blurple_colour_when_user_has_no_roles(self):
+ async def test_create_user_embed_uses_blurple_colour_when_user_has_no_roles(self):
"""The embed should be created with a blurple colour if the user has no assigned roles."""
ctx = helpers.MockContext()
user = helpers.MockMember(id=217)
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
self.assertEqual(embed.colour, discord.Colour.blurple())
@@ -503,20 +501,20 @@ class UserEmbedTests(unittest.TestCase):
f"{COG_PATH}.basic_user_infraction_counts",
new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))
)
- def test_create_user_embed_uses_png_format_of_user_avatar_as_thumbnail(self):
+ async def test_create_user_embed_uses_png_format_of_user_avatar_as_thumbnail(self):
"""The embed thumbnail should be set to the user's avatar in `png` format."""
ctx = helpers.MockContext()
user = helpers.MockMember(id=217)
user.avatar_url_as.return_value = "avatar url"
- embed = asyncio.run(self.cog.create_user_embed(ctx, user))
+ embed = await self.cog.create_user_embed(ctx, user)
user.avatar_url_as.assert_called_once_with(static_format="png")
self.assertEqual(embed.thumbnail.url, "avatar url")
@unittest.mock.patch("bot.exts.info.information.constants")
-class UserCommandTests(unittest.TestCase):
+class UserCommandTests(unittest.IsolatedAsyncioTestCase):
"""Tests for the `!user` command."""
def setUp(self):
@@ -536,16 +534,16 @@ class UserCommandTests(unittest.TestCase):
# used as a default value for a parameter, which gets defined upon import.
self.bot_command_channel = helpers.MockTextChannel(id=constants.Channels.bot_commands)
- def test_regular_member_cannot_target_another_member(self, constants):
+ async def test_regular_member_cannot_target_another_member(self, constants):
"""A regular user should not be able to use `!user` targeting another user."""
constants.MODERATION_ROLES = [self.moderator_role.id]
ctx = helpers.MockContext(author=self.author)
- asyncio.run(self.cog.user_info.callback(self.cog, ctx, self.target))
+ await self.cog.user_info(self.cog, ctx, self.target)
ctx.send.assert_called_once_with("You may not use this command on users other than yourself.")
- def test_regular_member_cannot_use_command_outside_of_bot_commands(self, constants):
+ async def test_regular_member_cannot_use_command_outside_of_bot_commands(self, constants):
"""A regular user should not be able to use this command outside of bot-commands."""
constants.MODERATION_ROLES = [self.moderator_role.id]
constants.STAFF_ROLES = [self.moderator_role.id]
@@ -553,49 +551,49 @@ class UserCommandTests(unittest.TestCase):
msg = "Sorry, but you may only use this command within <#50>."
with self.assertRaises(InWhitelistCheckFailure, msg=msg):
- asyncio.run(self.cog.user_info.callback(self.cog, ctx))
+ await self.cog.user_info(self.cog, ctx)
@unittest.mock.patch("bot.exts.info.information.Information.create_user_embed")
- def test_regular_user_may_use_command_in_bot_commands_channel(self, create_embed, constants):
+ async def test_regular_user_may_use_command_in_bot_commands_channel(self, create_embed, constants):
"""A regular user should be allowed to use `!user` targeting themselves in bot-commands."""
constants.STAFF_ROLES = [self.moderator_role.id]
ctx = helpers.MockContext(author=self.author, channel=self.bot_command_channel)
- asyncio.run(self.cog.user_info.callback(self.cog, ctx))
+ await self.cog.user_info(self.cog, ctx)
create_embed.assert_called_once_with(ctx, self.author)
ctx.send.assert_called_once()
@unittest.mock.patch("bot.exts.info.information.Information.create_user_embed")
- def test_regular_user_can_explicitly_target_themselves(self, create_embed, _):
+ async def test_regular_user_can_explicitly_target_themselves(self, create_embed, _):
"""A user should target itself with `!user` when a `user` argument was not provided."""
constants.STAFF_ROLES = [self.moderator_role.id]
ctx = helpers.MockContext(author=self.author, channel=self.bot_command_channel)
- asyncio.run(self.cog.user_info.callback(self.cog, ctx, self.author))
+ await self.cog.user_info(self.cog, ctx, self.author)
create_embed.assert_called_once_with(ctx, self.author)
ctx.send.assert_called_once()
@unittest.mock.patch("bot.exts.info.information.Information.create_user_embed")
- def test_staff_members_can_bypass_channel_restriction(self, create_embed, constants):
+ async def test_staff_members_can_bypass_channel_restriction(self, create_embed, constants):
"""Staff members should be able to bypass the bot-commands channel restriction."""
constants.STAFF_ROLES = [self.moderator_role.id]
ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(id=200))
- asyncio.run(self.cog.user_info.callback(self.cog, ctx))
+ await self.cog.user_info(self.cog, ctx)
create_embed.assert_called_once_with(ctx, self.moderator)
ctx.send.assert_called_once()
@unittest.mock.patch("bot.exts.info.information.Information.create_user_embed")
- def test_moderators_can_target_another_member(self, create_embed, constants):
+ async def test_moderators_can_target_another_member(self, create_embed, constants):
"""A moderator should be able to use `!user` targeting another user."""
constants.MODERATION_ROLES = [self.moderator_role.id]
constants.STAFF_ROLES = [self.moderator_role.id]
ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(id=50))
- asyncio.run(self.cog.user_info.callback(self.cog, ctx, self.target))
+ await self.cog.user_info(self.cog, ctx, self.target)
create_embed.assert_called_once_with(ctx, self.target)
ctx.send.assert_called_once()
diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py
index e2d44c637..3c2d52ae0 100644
--- a/tests/bot/exts/moderation/test_silence.py
+++ b/tests/bot/exts/moderation/test_silence.py
@@ -122,7 +122,7 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
starting_unsilenced_state=_silence_patch_return
):
with mock.patch.object(self.cog, "_silence", return_value=_silence_patch_return):
- await self.cog.silence.callback(self.cog, self.ctx, duration)
+ await self.cog.silence(self.cog, self.ctx, duration)
self.ctx.send.assert_called_once_with(result_message)
self.ctx.reset_mock()
@@ -138,7 +138,7 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
result_message=result_message
):
with mock.patch.object(self.cog, "_unsilence", return_value=_unsilence_patch_return):
- await self.cog.unsilence.callback(self.cog, self.ctx)
+ await self.cog.unsilence(self.cog, self.ctx)
self.ctx.send.assert_called_once_with(result_message)
self.ctx.reset_mock()
diff --git a/tests/bot/exts/utils/test_snekbox.py b/tests/bot/exts/utils/test_snekbox.py
index 40b2202aa..6601fad2c 100644
--- a/tests/bot/exts/utils/test_snekbox.py
+++ b/tests/bot/exts/utils/test_snekbox.py
@@ -154,7 +154,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
self.cog.send_eval = AsyncMock(return_value=response)
self.cog.continue_eval = AsyncMock(return_value=None)
- await self.cog.eval_command.callback(self.cog, ctx=ctx, code='MyAwesomeCode')
+ await self.cog.eval_command(self.cog, ctx=ctx, code='MyAwesomeCode')
self.cog.prepare_input.assert_called_once_with('MyAwesomeCode')
self.cog.send_eval.assert_called_once_with(ctx, 'MyAwesomeFormattedCode')
self.cog.continue_eval.assert_called_once_with(ctx, response)
@@ -168,7 +168,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
self.cog.continue_eval = AsyncMock()
self.cog.continue_eval.side_effect = ('MyAwesomeCode-2', None)
- await self.cog.eval_command.callback(self.cog, ctx=ctx, code='MyAwesomeCode')
+ await self.cog.eval_command(self.cog, ctx=ctx, code='MyAwesomeCode')
self.cog.prepare_input.has_calls(call('MyAwesomeCode'), call('MyAwesomeCode-2'))
self.cog.send_eval.assert_called_with(ctx, 'MyAwesomeFormattedCode')
self.cog.continue_eval.assert_called_with(ctx, response)
@@ -180,7 +180,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.author.mention = '@LemonLemonishBeard#0042'
ctx.send = AsyncMock()
self.cog.jobs = (42,)
- await self.cog.eval_command.callback(self.cog, ctx=ctx, code='MyAwesomeCode')
+ await self.cog.eval_command(self.cog, ctx=ctx, code='MyAwesomeCode')
ctx.send.assert_called_once_with(
"@LemonLemonishBeard#0042 You've already got a job running - please wait for it to finish!"
)
@@ -188,8 +188,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
async def test_eval_command_call_help(self):
"""Test if the eval command call the help command if no code is provided."""
ctx = MockContext(command="sentinel")
- await self.cog.eval_command.callback(self.cog, ctx=ctx, code='')
- ctx.send_help.assert_called_once_with("sentinel")
+ await self.cog.eval_command(self.cog, ctx=ctx, code='')
+ ctx.send_help.assert_called_once_with(ctx.command)
async def test_send_eval(self):
"""Test the send_eval function."""
@@ -290,7 +290,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
)
)
ctx.message.add_reaction.assert_called_once_with(snekbox.REEVAL_EMOJI)
- ctx.message.clear_reactions.assert_called_once()
+ ctx.message.clear_reaction.assert_called_once_with(snekbox.REEVAL_EMOJI)
response.delete.assert_called_once()
async def test_continue_eval_does_not_continue(self):
@@ -299,7 +299,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
actual = await self.cog.continue_eval(ctx, MockMessage())
self.assertEqual(actual, None)
- ctx.message.clear_reactions.assert_called_once()
+ ctx.message.clear_reaction.assert_called_once_with(snekbox.REEVAL_EMOJI)
async def test_get_code(self):
"""Should return 1st arg (or None) if eval cmd in message, otherwise return full content."""
diff --git a/tests/bot/patches/__init__.py b/tests/bot/patches/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/tests/bot/patches/__init__.py
+++ /dev/null