diff options
-rw-r--r-- | bot/__init__.py | 2 | ||||
-rw-r--r-- | bot/cogs/help_channels.py | 61 |
2 files changed, 53 insertions, 10 deletions
diff --git a/bot/__init__.py b/bot/__init__.py index c9dbc3f40..2dd4af225 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -33,7 +33,7 @@ log_format = logging.Formatter(format_string) log_file = Path("logs", "bot.log") log_file.parent.mkdir(exist_ok=True) -file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7) +file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding="utf8") file_handler.setFormatter(log_format) root_log = logging.getLogger() diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 12bc4e279..4dd70d7bf 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -5,6 +5,7 @@ import logging import random import typing as t from collections import deque +from contextlib import suppress from datetime import datetime from pathlib import Path @@ -13,7 +14,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.decorators import with_role +from bot.utils.checks import with_role_check from bot.utils.scheduling import Scheduler log = logging.getLogger(__name__) @@ -108,6 +109,9 @@ class HelpChannels(Scheduler, commands.Cog): super().__init__() self.bot = bot + self.help_channel_claimants: ( + t.Dict[discord.TextChannel, t.Union[discord.Member, discord.User]] + ) = {} # Categories self.available_category: discord.CategoryChannel = None @@ -190,15 +194,39 @@ class HelpChannels(Scheduler, commands.Cog): log.trace("Populating the name queue with names.") return deque(available_names) + async def dormant_check(self, ctx: commands.Context) -> bool: + """Return True if the user is the help channel claimant or passes the role check.""" + if self.help_channel_claimants.get(ctx.channel) == ctx.author: + log.trace(f"{ctx.author} is the help channel claimant, passing the check for dormant.") + return True + + log.trace(f"{ctx.author} is not the help channel claimant, checking roles.") + return with_role_check(ctx, *constants.HelpChannels.cmd_whitelist) + @commands.command(name="dormant", aliases=["close"], enabled=False) - @with_role(*constants.HelpChannels.cmd_whitelist) async def dormant_command(self, ctx: commands.Context) -> None: - """Make the current in-use help channel dormant.""" - log.trace("dormant command invoked; checking if the channel is in-use.") + """ + Make the current in-use help channel dormant. + Make the channel dormant if the user passes the `dormant_check`, + delete the message that invoked this, + and reset the send permissions cooldown for the user who started the session. + """ + log.trace("dormant command invoked; checking if the channel is in-use.") if ctx.channel.category == self.in_use_category: - self.cancel_task(ctx.channel.id) - await self.move_to_dormant(ctx.channel, "command") + if await self.dormant_check(ctx): + with suppress(KeyError): + del self.help_channel_claimants[ctx.channel] + + with suppress(discord.errors.NotFound): + log.trace("Deleting dormant invokation message.") + await ctx.message.delete() + + with suppress(discord.errors.HTTPException, discord.errors.NotFound): + await self.reset_claimant_send_permission(ctx.channel) + + await self.move_to_dormant(ctx.channel, "command") + self.cancel_task(ctx.channel.id) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") @@ -382,14 +410,13 @@ class HelpChannels(Scheduler, commands.Cog): self.bot.stats.gauge("help.total.available", total_available) self.bot.stats.gauge("help.total.dormant", total_dormant) - @staticmethod - def is_dormant_message(message: t.Optional[discord.Message]) -> bool: + def is_dormant_message(self, message: t.Optional[discord.Message]) -> bool: """Return True if the contents of the `message` match `DORMANT_MSG`.""" if not message or not message.embeds: return False embed = message.embeds[0] - return embed.description.strip() == DORMANT_MSG.strip() + return message.author == self.bot.user and embed.description.strip() == DORMANT_MSG.strip() async def move_idle_channel(self, channel: discord.TextChannel, has_task: bool = True) -> None: """ @@ -571,6 +598,8 @@ class HelpChannels(Scheduler, commands.Cog): await self.move_to_in_use(channel) await self.revoke_send_permissions(message.author) + # Add user with channel for dormant check. + self.help_channel_claimants[channel] = message.author self.bot.stats.incr("help.claimed") @@ -631,6 +660,20 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Ensuring channels in `Help: Available` are synchronized after permissions reset.") await self.ensure_permissions_synchronization(self.available_category) + async def reset_claimant_send_permission(self, channel: discord.TextChannel) -> None: + """Reset send permissions in the Available category for the help `channel` claimant.""" + log.trace(f"Attempting to find claimant for #{channel.name} ({channel.id}).") + try: + member = self.help_channel_claimants[channel] + except KeyError: + log.trace(f"Channel #{channel.name} ({channel.id}) not in claimant cache, permissions unchanged.") + return + + log.trace(f"Resetting send permissions for {member} ({member.id}).") + await self.update_category_permissions(self.available_category, member, overwrite=None) + # Ignore missing task when claim cooldown has passed but the channel still isn't dormant. + self.cancel_task(member.id, ignore_missing=True) + async def revoke_send_permissions(self, member: discord.Member) -> None: """ Disallow `member` to send messages in the Available category for a certain time. |