aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/exts/filters/filtering.py2
-rw-r--r--bot/exts/moderation/infraction/_scheduler.py10
-rw-r--r--bot/exts/moderation/infraction/infractions.py7
-rw-r--r--tests/bot/exts/moderation/test_silence.py79
5 files changed, 53 insertions, 46 deletions
diff --git a/bot/__main__.py b/bot/__main__.py
index e0d2e6ad5..02af2e9ef 100644
--- a/bot/__main__.py
+++ b/bot/__main__.py
@@ -26,6 +26,7 @@ async def _create_redis_session() -> RedisSession:
max_connections=20,
use_fakeredis=constants.Redis.use_fakeredis,
global_namespace="bot",
+ decode_responses=True,
)
try:
return await redis_session.connect()
diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py
index ca6ad0064..e4df0b1fd 100644
--- a/bot/exts/filters/filtering.py
+++ b/bot/exts/filters/filtering.py
@@ -413,7 +413,7 @@ class Filtering(Cog):
await context.invoke(
context.command,
msg.author,
- arrow.utcnow() + AUTO_BAN_DURATION,
+ (arrow.utcnow() + AUTO_BAN_DURATION).datetime,
reason=AUTO_BAN_REASON
)
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py
index c7f03b2e9..280b0fb0c 100644
--- a/bot/exts/moderation/infraction/_scheduler.py
+++ b/bot/exts/moderation/infraction/_scheduler.py
@@ -12,7 +12,7 @@ from discord.ext.commands import Context
from bot import constants
from bot.bot import Bot
-from bot.constants import Colours
+from bot.constants import Colours, Roles
from bot.converters import MemberOrUser
from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.modlog import ModLog
@@ -189,7 +189,10 @@ class InfractionScheduler:
f"Infraction #{id_} actor is bot; including the reason in the confirmation message."
)
if reason:
- end_msg = f" (reason: {textwrap.shorten(reason, width=1500, placeholder='...')})"
+ end_msg = (
+ f" (reason: {textwrap.shorten(reason, width=1500, placeholder='...')})."
+ f"\n\nThe <@&{Roles.moderators}> have been alerted for review"
+ )
purge = infraction.get("purge", "")
@@ -243,7 +246,8 @@ class InfractionScheduler:
# Send a confirmation message to the invoking context.
log.trace(f"Sending infraction #{id_} confirmation message.")
- await ctx.send(f"{dm_result}{confirm_msg}{infr_message}.")
+ mentions = discord.AllowedMentions(users=[user], roles=False)
+ await ctx.send(f"{dm_result}{confirm_msg}{infr_message}.", allowed_mentions=mentions)
# Send a log message to the mod log.
# Don't use ctx.message.author for the actor; antispam only patches ctx.author.
diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py
index 46fd3381c..08a3609a7 100644
--- a/bot/exts/moderation/infraction/infractions.py
+++ b/bot/exts/moderation/infraction/infractions.py
@@ -1,6 +1,7 @@
import textwrap
import typing as t
+import arrow
import discord
from discord import Member
from discord.ext import commands
@@ -11,6 +12,7 @@ from bot.bot import Bot
from bot.constants import Event
from bot.converters import Age, Duration, Expiry, MemberOrUser, UnambiguousMemberOrUser
from bot.decorators import ensure_future_timestamp, respect_role_hierarchy
+from bot.exts.filters.filtering import AUTO_BAN_DURATION, AUTO_BAN_REASON
from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.infraction._scheduler import InfractionScheduler
from bot.log import get_logger
@@ -151,6 +153,11 @@ class Infractions(InfractionScheduler, commands.Cog):
ctx.send = send
await infr_manage_cog.infraction_append(ctx, infraction, None, reason=f"[Clean log]({log_url})")
+ @command()
+ async def compban(self, ctx: Context, user: UnambiguousMemberOrUser) -> None:
+ """Same as cleanban, but specifically with the ban reason and duration used for compromised accounts."""
+ await self.cleanban(ctx, user, duration=(arrow.utcnow() + AUTO_BAN_DURATION).datetime, reason=AUTO_BAN_REASON)
+
@command(aliases=("vban",))
async def voiceban(self, ctx: Context) -> None:
"""
diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py
index 98547e2bc..2622f46a7 100644
--- a/tests/bot/exts/moderation/test_silence.py
+++ b/tests/bot/exts/moderation/test_silence.py
@@ -1,4 +1,3 @@
-import asyncio
import itertools
import unittest
from datetime import datetime, timezone
@@ -23,8 +22,24 @@ class PatchedDatetime(datetime):
now = mock.create_autospec(datetime, "now")
-class SilenceNotifierTests(unittest.IsolatedAsyncioTestCase):
+class SilenceTest(RedisTestCase):
+ """A base class for Silence tests that correctly sets up the cog and redis."""
+
+ @autospec(silence, "Scheduler", pass_mocks=False)
+ @autospec(silence.Silence, "_reschedule", pass_mocks=False)
+ def setUp(self) -> None:
+ self.bot = MockBot(get_channel=lambda _id: MockTextChannel(id=_id))
+ self.cog = silence.Silence(self.bot)
+
+ @autospec(silence, "SilenceNotifier", pass_mocks=False)
+ async def asyncSetUp(self) -> None:
+ await super().asyncSetUp()
+ await self.cog.cog_load() # Populate instance attributes.
+
+
+class SilenceNotifierTests(SilenceTest):
def setUp(self) -> None:
+ super().setUp()
self.alert_channel = MockTextChannel()
self.notifier = silence.SilenceNotifier(self.alert_channel)
self.notifier.stop = self.notifier_stop_mock = Mock()
@@ -89,34 +104,24 @@ class SilenceNotifierTests(unittest.IsolatedAsyncioTestCase):
@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False)
-class SilenceCogTests(RedisTestCase):
+class SilenceCogTests(SilenceTest):
"""Tests for the general functionality of the Silence cog."""
- @autospec(silence, "Scheduler", pass_mocks=False)
- def setUp(self) -> None:
- self.bot = MockBot()
- self.cog = silence.Silence(self.bot)
-
@autospec(silence, "SilenceNotifier", pass_mocks=False)
async def test_cog_load_got_guild(self):
"""Bot got guild after it became available."""
- await self.cog.cog_load()
self.bot.wait_until_guild_available.assert_awaited_once()
self.bot.get_guild.assert_called_once_with(Guild.id)
@autospec(silence, "SilenceNotifier", pass_mocks=False)
async def test_cog_load_got_channels(self):
"""Got channels from bot."""
- self.bot.get_channel.side_effect = lambda id_: MockTextChannel(id=id_)
-
await self.cog.cog_load()
self.assertEqual(self.cog._mod_alerts_channel.id, Channels.mod_alerts)
@autospec(silence, "SilenceNotifier")
async def test_cog_load_got_notifier(self, notifier):
"""Notifier was started with channel."""
- self.bot.get_channel.side_effect = lambda id_: MockTextChannel(id=id_)
-
await self.cog.cog_load()
notifier.assert_called_once_with(MockTextChannel(id=Channels.mod_log))
self.assertEqual(self.cog.notifier, notifier.return_value)
@@ -229,13 +234,9 @@ class SilenceCogTests(RedisTestCase):
self.assertEqual(member.move_to.call_count, 1 if member == failing_member else 2)
-class SilenceArgumentParserTests(RedisTestCase):
+class SilenceArgumentParserTests(SilenceTest):
"""Tests for the silence argument parser utility function."""
- def setUp(self):
- self.bot = MockBot()
- self.cog = silence.Silence(self.bot)
-
@autospec(silence.Silence, "send_message", pass_mocks=False)
@autospec(silence.Silence, "_set_silence_overwrites", return_value=False, pass_mocks=False)
@autospec(silence.Silence, "parse_silence_args")
@@ -303,17 +304,19 @@ class SilenceArgumentParserTests(RedisTestCase):
@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False)
-class RescheduleTests(unittest.IsolatedAsyncioTestCase):
+class RescheduleTests(RedisTestCase):
"""Tests for the rescheduling of cached unsilences."""
- @autospec(silence, "Scheduler", "SilenceNotifier", pass_mocks=False)
- def setUp(self):
+ @autospec(silence, "Scheduler", pass_mocks=False)
+ def setUp(self) -> None:
self.bot = MockBot()
self.cog = silence.Silence(self.bot)
self.cog._unsilence_wrapper = mock.create_autospec(self.cog._unsilence_wrapper)
- with mock.patch.object(self.cog, "_reschedule", autospec=True):
- asyncio.run(self.cog.cog_load()) # Populate instance attributes.
+ @autospec(silence, "SilenceNotifier", pass_mocks=False)
+ async def asyncSetUp(self) -> None:
+ await super().asyncSetUp()
+ await self.cog.cog_load() # Populate instance attributes.
async def test_skipped_missing_channel(self):
"""Did nothing because the channel couldn't be retrieved."""
@@ -388,20 +391,14 @@ def voice_sync_helper(function):
@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False)
-class SilenceTests(RedisTestCase):
+class SilenceTests(SilenceTest):
"""Tests for the silence command and its related helper methods."""
- @autospec(silence.Silence, "_reschedule", pass_mocks=False)
- @autospec(silence, "Scheduler", "SilenceNotifier", pass_mocks=False)
def setUp(self) -> None:
- self.bot = MockBot(get_channel=lambda _: MockTextChannel())
- self.cog = silence.Silence(self.bot)
+ super().setUp()
# Avoid unawaited coroutine warnings.
self.cog.scheduler.schedule_later.side_effect = lambda delay, task_id, coro: coro.close()
-
- asyncio.run(self.cog.cog_load()) # Populate instance attributes.
-
self.text_channel = MockTextChannel()
self.text_overwrite = PermissionOverwrite(
send_messages=True,
@@ -659,22 +656,13 @@ class SilenceTests(RedisTestCase):
@autospec(silence.Silence, "unsilence_timestamps", pass_mocks=False)
-class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
+class UnsilenceTests(SilenceTest):
"""Tests for the unsilence command and its related helper methods."""
- @autospec(silence.Silence, "_reschedule", pass_mocks=False)
- @autospec(silence, "Scheduler", "SilenceNotifier", pass_mocks=False)
def setUp(self) -> None:
- self.bot = MockBot(get_channel=lambda _: MockTextChannel())
- self.cog = silence.Silence(self.bot)
-
- overwrites_cache = mock.create_autospec(self.cog.previous_overwrites, spec_set=True)
- self.cog.previous_overwrites = overwrites_cache
-
- asyncio.run(self.cog.cog_load()) # Populate instance attributes.
+ super().setUp()
self.cog.scheduler.__contains__.return_value = True
- overwrites_cache.get.return_value = '{"send_messages": true, "add_reactions": false}'
self.text_channel = MockTextChannel()
self.text_overwrite = PermissionOverwrite(send_messages=False, add_reactions=False)
self.text_channel.overwrites_for.return_value = self.text_overwrite
@@ -683,6 +671,13 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
self.voice_overwrite = PermissionOverwrite(connect=True, speak=True)
self.voice_channel.overwrites_for.return_value = self.voice_overwrite
+ async def asyncSetUp(self) -> None:
+ await super().asyncSetUp()
+ overwrites_cache = mock.create_autospec(self.cog.previous_overwrites, spec_set=True)
+ self.cog.previous_overwrites = overwrites_cache
+
+ overwrites_cache.get.return_value = '{"send_messages": true, "add_reactions": false}'
+
async def test_sent_correct_message(self):
"""Appropriate failure/success message was sent by the command."""
unsilenced_overwrite = PermissionOverwrite(send_messages=True, add_reactions=True)