aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/moderation/silence.py86
1 files changed, 46 insertions, 40 deletions
diff --git a/bot/cogs/moderation/silence.py b/bot/cogs/moderation/silence.py
index 10185761c..e12b6c606 100644
--- a/bot/cogs/moderation/silence.py
+++ b/bot/cogs/moderation/silence.py
@@ -29,21 +29,59 @@ class FirstHash(tuple):
return self[0] == other[0]
+class SilenceNotifier(tasks.Loop):
+ """Loop notifier for posting notices to `alert_channel` containing added channels."""
+
+ def __init__(self, alert_channel: TextChannel):
+ super().__init__(self._notifier, seconds=1, minutes=0, hours=0, count=None, reconnect=True, loop=None)
+ self._silenced_channels = set()
+ self._alert_channel = alert_channel
+
+ def add_channel(self, channel: TextChannel) -> None:
+ """Add channel to `_silenced_channels` and start loop if not launched."""
+ if not self._silenced_channels:
+ self.start()
+ log.trace("Starting notifier loop.")
+ self._silenced_channels.add(FirstHash(channel, self._current_loop))
+
+ def remove_channel(self, channel: TextChannel) -> None:
+ """Remove channel from `_silenced_channels` and stop loop if no channels remain."""
+ with suppress(KeyError):
+ self._silenced_channels.remove(FirstHash(channel))
+ if not self._silenced_channels:
+ self.stop()
+ log.trace("Stopping notifier loop.")
+
+ async def _notifier(self) -> None:
+ """Post notice of `_silenced_channels` with their silenced duration to `_alert_channel` periodically."""
+ # Wait for 15 minutes between notices with pause at start of loop.
+ if self._current_loop and not self._current_loop/60 % 15:
+ log.debug(
+ f"Sending notice with channels: "
+ f"{', '.join(f'#{channel} ({channel.id})' for channel, _ in self._silenced_channels)}."
+ )
+ channels_text = ', '.join(
+ f"{channel.mention} for {(self._current_loop-start)//60} min"
+ for channel, start in self._silenced_channels
+ )
+ await self._alert_channel.send(f"<@&{Roles.moderators}> currently silenced channels: {channels_text}")
+
+
class Silence(commands.Cog):
"""Commands for stopping channel messages for `verified` role in a channel."""
def __init__(self, bot: Bot):
self.bot = bot
- self.loop_alert_channels = set()
- self.bot.loop.create_task(self._get_server_values())
+ self.bot.loop.create_task(self._get_instance_vars())
- async def _get_server_values(self) -> None:
- """Fetch required internal values after they're available."""
+ async def _get_instance_vars(self) -> None:
+ """Get instance variables after they're available to get from the guild."""
await self.bot.wait_until_guild_available()
guild = self.bot.get_guild(Guild.id)
self._verified_role = guild.get_role(Roles.verified)
self._mod_alerts_channel = self.bot.get_channel(Channels.mod_alerts)
self._mod_log_channel = self.bot.get_channel(Channels.mod_log)
+ self.notifier = SilenceNotifier(self._mod_log_channel)
@commands.command(aliases=("hush",))
async def silence(
@@ -87,8 +125,7 @@ class Silence(commands.Cog):
"""
Silence `channel` for `self._verified_role`.
- If `persistent` is `True` add `channel` with current iteration of `self._notifier`
- to `self.self.loop_alert_channels` and attempt to start notifier.
+ If `persistent` is `True` add `channel` to notifier.
`duration` is only used for logging; if None is passed `persistent` should be True to not log None.
"""
if channel.overwrites_for(self._verified_role).send_messages is False:
@@ -97,9 +134,7 @@ class Silence(commands.Cog):
await channel.set_permissions(self._verified_role, overwrite=PermissionOverwrite(send_messages=False))
if persistent:
log.debug(f"Silenced #{channel} ({channel.id}) indefinitely.")
- self.loop_alert_channels.add(FirstHash(channel, self._notifier.current_loop))
- with suppress(RuntimeError):
- self._notifier.start()
+ self.notifier.add_channel(channel)
return True
log.debug(f"Silenced #{channel} ({channel.id}) for {duration} minute(s).")
@@ -110,45 +145,16 @@ class Silence(commands.Cog):
Unsilence `channel`.
Check if `channel` is silenced through a `PermissionOverwrite`,
- if it is unsilence it, attempt to remove it from `self.loop_alert_channels`
- and if `self.loop_alert_channels` are left empty, stop the `self._notifier`
+ if it is unsilence it and remove it from the notifier.
"""
if channel.overwrites_for(self._verified_role).send_messages is False:
await channel.set_permissions(self._verified_role, overwrite=None)
log.debug(f"Unsilenced channel #{channel} ({channel.id}).")
-
- with suppress(KeyError):
- self.loop_alert_channels.remove(FirstHash(channel))
- if not self.loop_alert_channels:
- self._notifier.cancel()
+ self.notifier.remove_channel(channel)
return True
log.debug(f"Tried to unsilence channel #{channel} ({channel.id}) but the channel was not silenced.")
return False
- @tasks.loop()
- async def _notifier(self) -> None:
- """Post notice of permanently silenced channels to `mod_alerts` periodically."""
- # Wait for 15 minutes between notices with pause at start of loop.
- await asyncio.sleep(15*60)
- current_iter = self._notifier.current_loop+1
- channels_text = ', '.join(
- f"{channel.mention} for {current_iter-start} min"
- for channel, start in self.loop_alert_channels
- )
- channels_log_text = ', '.join(
- f'#{channel} ({channel.id})' for channel, _ in self.loop_alert_channels
- )
- log.debug(f"Sending notice with channels: {channels_log_text}")
- await self._mod_alerts_channel.send(f"<@&{Roles.moderators}> currently silenced channels: {channels_text}")
-
- @_notifier.before_loop
- async def _log_notifier_start(self) -> None:
- log.trace("Starting notifier loop.")
-
- @_notifier.after_loop
- async def _log_notifier_end(self) -> None:
- log.trace("Stopping notifier loop.")
-
# This cannot be static (must have a __func__ attribute).
def cog_check(self, ctx: Context) -> bool:
"""Only allow moderators to invoke the commands in this cog."""