diff options
| author | 2020-10-18 10:40:17 -0700 | |
|---|---|---|
| committer | 2020-11-20 15:57:09 -0800 | |
| commit | 3843ae1546a1a1cbf7ca05756eee223bf7c2f317 (patch) | |
| tree | aa570021226e32896ff1723a067344d0138b4549 | |
| parent | Help channels: move time functions to channel module (diff) | |
Help channels: move cooldown/role functions to cooldown module
| -rw-r--r-- | bot/exts/help_channels/_cog.py | 88 | ||||
| -rw-r--r-- | bot/exts/help_channels/_cooldown.py | 100 |
2 files changed, 104 insertions, 84 deletions
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 390528fde..169238937 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.exts.help_channels import _channel, _message +from bot.exts.help_channels import _channel, _cooldown, _message from bot.exts.help_channels._name import create_name_queue from bot.utils import channel as channel_utils from bot.utils.scheduling import Scheduler @@ -22,8 +22,6 @@ HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -CoroutineFunc = t.Callable[..., t.Coroutine] - class HelpChannels(commands.Cog): """ @@ -164,7 +162,7 @@ class HelpChannels(commands.Cog): log.trace("close command invoked; checking if the channel is in-use.") if ctx.channel.category == self.in_use_category: if await self.dormant_check(ctx): - await self.remove_cooldown_role(ctx.author) + await _cooldown.remove_cooldown_role(ctx.author) # Ignore missing task when cooldown has passed but the channel still isn't dormant. if ctx.author.id in self.scheduler: @@ -246,7 +244,7 @@ class HelpChannels(commands.Cog): log.trace("Initialising the cog.") await self.init_categories() - await self.check_cooldowns() + await _cooldown.check_cooldowns(self.scheduler) self.channel_queue = self.create_channel_queue() self.name_queue = create_name_queue( @@ -462,7 +460,7 @@ class HelpChannels(commands.Cog): log.info(f"Channel #{channel} was claimed by `{message.author.id}`.") await self.move_to_in_use(channel) - await self.revoke_send_permissions(message.author) + await _cooldown.revoke_send_permissions(message.author, self.scheduler) await _message.pin(message) @@ -505,84 +503,6 @@ class HelpChannels(commands.Cog): delay = constants.HelpChannels.deleted_idle_minutes * 60 self.scheduler.schedule_later(delay, msg.channel.id, self.move_idle_channel(msg.channel)) - async def check_cooldowns(self) -> None: - """Remove expired cooldowns and re-schedule active ones.""" - log.trace("Checking all cooldowns to remove or re-schedule them.") - guild = self.bot.get_guild(constants.Guild.id) - cooldown = constants.HelpChannels.claim_minutes * 60 - - for channel_id, member_id in await self.help_channel_claimants.items(): - member = guild.get_member(member_id) - if not member: - continue # Member probably left the guild. - - in_use_time = await self.get_in_use_time(channel_id) - - if not in_use_time or in_use_time.seconds > cooldown: - # Remove the role if no claim time could be retrieved or if the cooldown expired. - # Since the channel is in the claimants cache, it is definitely strange for a time - # to not exist. However, it isn't a reason to keep the user stuck with a cooldown. - await self.remove_cooldown_role(member) - else: - # The member is still on a cooldown; re-schedule it for the remaining time. - delay = cooldown - in_use_time.seconds - self.scheduler.schedule_later(delay, member.id, self.remove_cooldown_role(member)) - - async def add_cooldown_role(self, member: discord.Member) -> None: - """Add the help cooldown role to `member`.""" - log.trace(f"Adding cooldown role for {member} ({member.id}).") - await self._change_cooldown_role(member, member.add_roles) - - async def remove_cooldown_role(self, member: discord.Member) -> None: - """Remove the help cooldown role from `member`.""" - log.trace(f"Removing cooldown role for {member} ({member.id}).") - await self._change_cooldown_role(member, member.remove_roles) - - async def _change_cooldown_role(self, member: discord.Member, coro_func: CoroutineFunc) -> None: - """ - Change `member`'s cooldown role via awaiting `coro_func` and handle errors. - - `coro_func` is intended to be `discord.Member.add_roles` or `discord.Member.remove_roles`. - """ - guild = self.bot.get_guild(constants.Guild.id) - role = guild.get_role(constants.Roles.help_cooldown) - if role is None: - log.warning(f"Help cooldown role ({constants.Roles.help_cooldown}) could not be found!") - return - - try: - await coro_func(role) - except discord.NotFound: - log.debug(f"Failed to change role for {member} ({member.id}): member not found") - except discord.Forbidden: - log.debug( - f"Forbidden to change role for {member} ({member.id}); " - f"possibly due to role hierarchy" - ) - except discord.HTTPException as e: - log.error(f"Failed to change role for {member} ({member.id}): {e.status} {e.code}") - - async def revoke_send_permissions(self, member: discord.Member) -> None: - """ - Disallow `member` to send messages in the Available category for a certain time. - - The time until permissions are reinstated can be configured with - `HelpChannels.claim_minutes`. - """ - log.trace( - f"Revoking {member}'s ({member.id}) send message permissions in the Available category." - ) - - await self.add_cooldown_role(member) - - # Cancel the existing task, if any. - # Would mean the user somehow bypassed the lack of permissions (e.g. user is guild owner). - if member.id in self.scheduler: - self.scheduler.cancel(member.id) - - delay = constants.HelpChannels.claim_minutes * 60 - self.scheduler.schedule_later(delay, member.id, self.remove_cooldown_role(member)) - async def wait_for_dormant_channel(self) -> discord.TextChannel: """Wait for a dormant channel to become available in the queue and return it.""" log.trace("Waiting for a dormant channel.") diff --git a/bot/exts/help_channels/_cooldown.py b/bot/exts/help_channels/_cooldown.py new file mode 100644 index 000000000..c4fd4b662 --- /dev/null +++ b/bot/exts/help_channels/_cooldown.py @@ -0,0 +1,100 @@ +import logging +from typing import Callable, Coroutine + +import discord +from async_rediscache import RedisCache + +import bot +from bot import constants +from bot.exts.help_channels import _channel +from bot.utils.scheduling import Scheduler + +log = logging.getLogger(__name__) +CoroutineFunc = Callable[..., Coroutine] + +# This cache tracks which channels are claimed by which members. +# RedisCache[discord.TextChannel.id, t.Union[discord.User.id, discord.Member.id]] +_help_channel_claimants = RedisCache(namespace="HelpChannels.help_channel_claimants") + + +async def add_cooldown_role(member: discord.Member) -> None: + """Add the help cooldown role to `member`.""" + log.trace(f"Adding cooldown role for {member} ({member.id}).") + await _change_cooldown_role(member, member.add_roles) + + +async def check_cooldowns(scheduler: Scheduler) -> None: + """Remove expired cooldowns and re-schedule active ones.""" + log.trace("Checking all cooldowns to remove or re-schedule them.") + guild = bot.instance.get_guild(constants.Guild.id) + cooldown = constants.HelpChannels.claim_minutes * 60 + + for channel_id, member_id in await _help_channel_claimants.items(): + member = guild.get_member(member_id) + if not member: + continue # Member probably left the guild. + + in_use_time = await _channel.get_in_use_time(channel_id) + + if not in_use_time or in_use_time.seconds > cooldown: + # Remove the role if no claim time could be retrieved or if the cooldown expired. + # Since the channel is in the claimants cache, it is definitely strange for a time + # to not exist. However, it isn't a reason to keep the user stuck with a cooldown. + await remove_cooldown_role(member) + else: + # The member is still on a cooldown; re-schedule it for the remaining time. + delay = cooldown - in_use_time.seconds + scheduler.schedule_later(delay, member.id, remove_cooldown_role(member)) + + +async def remove_cooldown_role(member: discord.Member) -> None: + """Remove the help cooldown role from `member`.""" + log.trace(f"Removing cooldown role for {member} ({member.id}).") + await _change_cooldown_role(member, member.remove_roles) + + +async def revoke_send_permissions(member: discord.Member, scheduler: Scheduler) -> None: + """ + Disallow `member` to send messages in the Available category for a certain time. + + The time until permissions are reinstated can be configured with + `HelpChannels.claim_minutes`. + """ + log.trace( + f"Revoking {member}'s ({member.id}) send message permissions in the Available category." + ) + + await add_cooldown_role(member) + + # Cancel the existing task, if any. + # Would mean the user somehow bypassed the lack of permissions (e.g. user is guild owner). + if member.id in scheduler: + scheduler.cancel(member.id) + + delay = constants.HelpChannels.claim_minutes * 60 + scheduler.schedule_later(delay, member.id, remove_cooldown_role(member)) + + +async def _change_cooldown_role(member: discord.Member, coro_func: CoroutineFunc) -> None: + """ + Change `member`'s cooldown role via awaiting `coro_func` and handle errors. + + `coro_func` is intended to be `discord.Member.add_roles` or `discord.Member.remove_roles`. + """ + guild = bot.instance.get_guild(constants.Guild.id) + role = guild.get_role(constants.Roles.help_cooldown) + if role is None: + log.warning(f"Help cooldown role ({constants.Roles.help_cooldown}) could not be found!") + return + + try: + await coro_func(role) + except discord.NotFound: + log.debug(f"Failed to change role for {member} ({member.id}): member not found") + except discord.Forbidden: + log.debug( + f"Forbidden to change role for {member} ({member.id}); " + f"possibly due to role hierarchy" + ) + except discord.HTTPException as e: + log.error(f"Failed to change role for {member} ({member.id}): {e.status} {e.code}") |