diff options
| author | 2021-03-16 20:08:53 +0000 | |
|---|---|---|
| committer | 2021-03-16 20:08:53 +0000 | |
| commit | b3c66a6fb07ebc92c0b53d946cf10df6c1107303 (patch) | |
| tree | 931a10367c9810ea92231c421345b7aba4abc7c5 | |
| parent | Update help availible footer (diff) | |
Extend close time logic to differentiate between the claimant and other users.
| -rw-r--r-- | bot/exts/help_channels/_caches.py | 8 | ||||
| -rw-r--r-- | bot/exts/help_channels/_channel.py | 54 | ||||
| -rw-r--r-- | bot/exts/help_channels/_cog.py | 42 |
3 files changed, 75 insertions, 29 deletions
diff --git a/bot/exts/help_channels/_caches.py b/bot/exts/help_channels/_caches.py index 4cea385b7..c790a37b1 100644 --- a/bot/exts/help_channels/_caches.py +++ b/bot/exts/help_channels/_caches.py @@ -8,6 +8,14 @@ claim_times = RedisCache(namespace="HelpChannels.claim_times") # RedisCache[discord.TextChannel.id, t.Union[discord.User.id, discord.Member.id]] claimants = RedisCache(namespace="HelpChannels.help_channel_claimants") +# Stores the timestamp of the last message in a help channel +# RedisCache[discord.TextChannel.id, UtcPosixTimestamp] +last_message = RedisCache(namespace="HelpChannels.last_message") + +# Stores the timestamp of the last message from the claimant of a help channel +# RedisCache[discord.TextChannel.id, UtcPosixTimestamp] +claimant_last_message = RedisCache(namespace="HelpChannels.claimant_last_message") + # This cache maps a help channel to original question message in same channel. # RedisCache[discord.TextChannel.id, discord.Message.id] question_messages = RedisCache(namespace="HelpChannels.question_messages") diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py index 224214b00..95b8cdc1f 100644 --- a/bot/exts/help_channels/_channel.py +++ b/bot/exts/help_channels/_channel.py @@ -25,23 +25,43 @@ def get_category_channels(category: discord.CategoryChannel) -> t.Iterable[disco yield channel -async def get_idle_time(channel: discord.TextChannel) -> t.Optional[int]: - """ - Return the time elapsed, in seconds, since the last message sent in the `channel`. - - Return None if the channel has no messages. - """ - log.trace(f"Getting the idle time for #{channel} ({channel.id}).") - - msg = await _message.get_last_message(channel) - if not msg: - log.debug(f"No idle time available; #{channel} ({channel.id}) has no messages.") - return None - - idle_time = (datetime.utcnow() - msg.created_at).seconds - - log.trace(f"#{channel} ({channel.id}) has been idle for {idle_time} seconds.") - return idle_time +async def get_closing_time(channel: discord.TextChannel) -> t.Optional[int]: + """Return the timestamp at which the given help `channel` should be closed.""" + log.trace(f"Getting the closing time for #{channel} ({channel.id}).") + + if await _message.is_empty(channel): + idle_minutes = constants.HelpChannels.deleted_idle_minutes + else: + idle_minutes = constants.HelpChannels.idle_minutes_others + + last_message = await _caches.last_message.get(channel.id) + claimant_last_message = await _caches.claimant_last_message.get(channel.id) + + if not (last_message or claimant_last_message): + # Using the old method if we can't get cached info. + msg = await _message.get_last_message(channel) + if not msg: + log.debug(f"No idle time available; #{channel} ({channel.id}) has no messages.") + return datetime.min + + # We want to get the time at which a channel should be closed. + closing_time = msg.created_at + closing_time += timedelta(minutes=idle_minutes) + + return closing_time + + # We want to get the time at which a channel should be closed. + last_message = datetime.fromtimestamp(last_message) + claimant = datetime.fromtimestamp(claimant_last_message) + + last_message += timedelta(minutes=idle_minutes) + claimant += timedelta(minutes=constants.HelpChannels.idle_minutes_claimant) + + # The further away closing time is what we should use. + closing_time = max(claimant, last_message) + log.trace(f"claimant: {claimant}, last_message: {last_message}") + log.trace(f"#{channel} ({channel.id}) should be closed at {closing_time}.") + return closing_time async def get_in_use_time(channel_id: int) -> t.Optional[timedelta]: diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 1c730dce9..db14ce0ef 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -43,7 +43,9 @@ class HelpChannels(commands.Cog): In Use Category * Contains all channels which are occupied by someone needing help - * Channel moves to dormant category after `constants.HelpChannels.idle_minutes` of being idle + * Channel moves to dormant category after `constants.HelpChannels.idle_minutes_other` minutes + since the last user message, or `constants.HelpChannels.idle_minutes_claimant` minutes + since the last claimant message. * Command can prematurely mark a channel as dormant * Channel claimant is allowed to use the command * Allowed roles for the command are configurable with `constants.HelpChannels.cmd_whitelist` @@ -293,16 +295,12 @@ class HelpChannels(commands.Cog): """ log.trace(f"Handling in-use channel #{channel} ({channel.id}).") - if not await _message.is_empty(channel): - idle_seconds = constants.HelpChannels.idle_minutes * 60 - else: - idle_seconds = constants.HelpChannels.deleted_idle_minutes * 60 - - time_elapsed = await _channel.get_idle_time(channel) + closing_time = await _channel.get_closing_time(channel) + # The time at which the channel should be closed, based on messages sent. + if closing_time < datetime.utcnow(): - if time_elapsed is None or time_elapsed >= idle_seconds: log.info( - f"#{channel} ({channel.id}) is idle longer than {idle_seconds} seconds " + f"#{channel} ({channel.id}) is idle past {closing_time} " f"and will be made dormant." ) @@ -312,7 +310,7 @@ class HelpChannels(commands.Cog): if has_task: self.scheduler.cancel(channel.id) - delay = idle_seconds - time_elapsed + delay = (closing_time - datetime.utcnow()).seconds log.info( f"#{channel} ({channel.id}) is still active; " f"scheduling it to be moved after {delay} seconds." @@ -410,7 +408,7 @@ class HelpChannels(commands.Cog): category_id=constants.Categories.help_in_use, ) - timeout = constants.HelpChannels.idle_minutes * 60 + timeout = constants.HelpChannels.idle_minutes_others * 60 log.trace(f"Scheduling #{channel} ({channel.id}) to become dormant in {timeout} sec.") self.scheduler.schedule_later(timeout, channel.id, self.move_idle_channel(channel)) @@ -418,7 +416,12 @@ class HelpChannels(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: - """Move an available channel to the In Use category and replace it with a dormant one.""" + """ + Move an available channel to the In Use category and replace it with a dormant one. + + Also updates the `message_times` cache based on the current timestamp. If the message + author is the claimant of this channel, also update the claimant_last_message. + """ if message.author.bot: return # Ignore messages sent by bots. @@ -427,6 +430,21 @@ class HelpChannels(commands.Cog): if channel_utils.is_in_category(message.channel, constants.Categories.help_available): if not _channel.is_excluded_channel(message.channel): await self.claim_channel(message) + # Initialise the cache for this channel + await _caches.claimant_last_message.set( + message.channel.id, + message.created_at.timestamp() + ) + await _caches.last_message.set( + message.channel.id, + message.created_at.timestamp() + ) + elif channel_utils.is_in_category(message.channel, constants.Categories.help_in_use): + # Overwrite the claimant message time, if its from the claimant. + if message.author == await _caches.claimants.get(message.channel.id): + await _caches.claimant_last_message(message.channel.id, message.created_at.timestamp()) + + await _caches.last_message.set(message.channel.id, message.created_at.timestamp()) else: await _message.check_for_answer(message) |