diff options
| -rw-r--r-- | bot/exts/help_channels/_caches.py | 16 | ||||
| -rw-r--r-- | bot/exts/help_channels/_channel.py | 33 | ||||
| -rw-r--r-- | bot/exts/help_channels/_cog.py | 21 | ||||
| -rw-r--r-- | bot/exts/help_channels/_message.py | 31 | ||||
| -rw-r--r-- | bot/exts/help_channels/_stats.py | 6 | 
5 files changed, 48 insertions, 59 deletions
diff --git a/bot/exts/help_channels/_caches.py b/bot/exts/help_channels/_caches.py index d4676df87..834c5f4c2 100644 --- a/bot/exts/help_channels/_caches.py +++ b/bot/exts/help_channels/_caches.py @@ -8,20 +8,16 @@ 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_times = RedisCache(namespace="HelpChannels.last_message_times") -  # Stores the timestamp of the last message from the claimant of a help channel  # RedisCache[discord.TextChannel.id, UtcPosixTimestamp]  claimant_last_message_times = RedisCache(namespace="HelpChannels.claimant_last_message_times") +# This cache maps a help channel to the timestamp of the last, non-claimant, +# message. This cache being empty for a given help channel indicates the +# question is unanswered. +# RedisCache[discord.TextChannel.id, UtcPosixTimestamp] +non_claimant_last_message_times = RedisCache(namespace="HelpChannels.non_claimant_last_message_times") +  # 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") - -# This cache maps a help channel to whether it has had any -# activity other than the original claimant. True being no other -# activity and False being other activity. -# RedisCache[discord.TextChannel.id, bool] -unanswered = RedisCache(namespace="HelpChannels.unanswered") diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py index 6c17a26e0..3e3749041 100644 --- a/bot/exts/help_channels/_channel.py +++ b/bot/exts/help_channels/_channel.py @@ -25,8 +25,13 @@ def get_category_channels(category: discord.CategoryChannel) -> t.Iterable[disco              yield channel -async def get_closing_time(channel: discord.TextChannel) -> t.Optional[int]: -    """Return the timestamp at which the given help `channel` should be closed.""" +async def get_closing_time(channel: discord.TextChannel) -> datetime: +    """ +    Return the timestamp at which the given help `channel` should be closed. + +    If either cache is empty, use the last message in the channel to determine closign time. +    If the last message connt be retreived, return datetime.min, I.E close right now. +    """      log.trace(f"Getting the closing time for #{channel} ({channel.id}).")      if await _message.is_empty(channel): @@ -34,32 +39,32 @@ async def get_closing_time(channel: discord.TextChannel) -> t.Optional[int]:      else:          idle_minutes = constants.HelpChannels.idle_minutes_others -    last_message_time = await _caches.last_message_times.get(channel.id) +    non_claimant_last_message_time = await _caches.non_claimant_last_message_times.get(channel.id)      claimant_last_message_time = await _caches.claimant_last_message_times.get(channel.id) -    if not (last_message_time or claimant_last_message_time): +    if not (non_claimant_last_message_time or claimant_last_message_time):          # 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) +        # The time at which a channel should be closed. +        return msg.created_at + timedelta(minutes=idle_minutes) -        return closing_time - -    # We want to get the time at which a channel should be closed. -    last_message_time = datetime.fromtimestamp(last_message_time) +    # Get the later time at which a channel should be closed +    non_claimant_last_message_time = datetime.fromtimestamp(non_claimant_last_message_time)      claimant_last_message_time = datetime.fromtimestamp(claimant_last_message_time) -    last_message_time += timedelta(minutes=idle_minutes) +    non_claimant_last_message_time += timedelta(minutes=idle_minutes)      claimant_last_message_time += timedelta(minutes=constants.HelpChannels.idle_minutes_claimant)      # The further away closing time is what we should use. -    closing_time = max(claimant_last_message_time, last_message_time) -    log.trace(f"claimant closing time: {claimant_last_message_time}, last_message closing time: {last_message_time}") +    closing_time = max(claimant_last_message_time, non_claimant_last_message_time) +    log.trace( +        f"Claimant closing time: {claimant_last_message_time}, " +        f"last_message closing time: {non_claimant_last_message_time}" +    )      log.trace(f"#{channel} ({channel.id}) should be closed at {closing_time}.")      return closing_time diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index bac9aa9dd..8c97ef2d0 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -117,8 +117,7 @@ class HelpChannels(commands.Cog):          # Must use a timezone-aware datetime to ensure a correct POSIX timestamp.          timestamp = datetime.now(timezone.utc).timestamp()          await _caches.claim_times.set(message.channel.id, timestamp) - -        await _caches.unanswered.set(message.channel.id, True) +        await _caches.claimant_last_message_times.set(message.channel.id, timestamp)          # Not awaited because it may indefinitely hold the lock while waiting for a channel.          scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -378,6 +377,7 @@ class HelpChannels(commands.Cog):      async def _unclaim_channel(self, channel: discord.TextChannel, claimant_id: int, is_auto: bool) -> None:          """Actual implementation of `unclaim_channel`. See that for full documentation."""          await _caches.claimants.delete(channel.id) +        await _caches.non_claimant_last_message_times.delete(channel.id)          # Ignore missing tasks because a channel may still be dormant after the cooldown expires.          if claimant_id in self.scheduler: @@ -419,7 +419,7 @@ class HelpChannels(commands.Cog):          """          Move an available channel to the In Use category and replace it with a dormant one. -        Also updates the `last_message_times` cache based on the current timestamp. If the message +        Update the `last_message_times` cache based on the current timestamp. If the message          author is the claimant of this channel, also update the `claimant_last_message_times` cache.          """          if message.author.bot: @@ -430,21 +430,6 @@ 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_times.set( -                    message.channel.id, -                    message.created_at.timestamp() -                ) -                await _caches.last_message_times.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_times(message.channel.id, message.created_at.timestamp()) - -            await _caches.last_message_times.set(message.channel.id, message.created_at.timestamp())          else:              await _message.check_for_answer(message) diff --git a/bot/exts/help_channels/_message.py b/bot/exts/help_channels/_message.py index ec2daab45..9ba019550 100644 --- a/bot/exts/help_channels/_message.py +++ b/bot/exts/help_channels/_message.py @@ -1,7 +1,7 @@  import logging  import textwrap  import typing as t -from datetime import datetime +from datetime import datetime, timezone  import discord @@ -48,19 +48,22 @@ async def check_for_answer(message: discord.Message) -> None:      # Confirm the channel is an in use help channel      if is_in_category(channel, constants.Categories.help_in_use): -        log.trace(f"Checking if #{channel} ({channel.id}) has been answered.") - -        # Check if there is an entry in unanswered -        if await _caches.unanswered.contains(channel.id): -            claimant_id = await _caches.claimants.get(channel.id) -            if not claimant_id: -                # The mapping for this channel doesn't exist, we can't do anything. -                return - -            # Check the message did not come from the claimant -            if claimant_id != message.author.id: -                # Mark the channel as answered -                await _caches.unanswered.set(channel.id, False) +        log.trace(f"Checking if #{channel} ({channel.id}) has had a reply.") +        # Must use a timezone-aware datetime to ensure a correct POSIX timestamp. +        timestamp = datetime.now(timezone.utc).timestamp() + +        # Overwrite the claimant message time, if its from the claimant. +        if message.author == await _caches.claimants.get(channel.id): +            await _caches.claimant_last_message_times.set(channel.id, timestamp) +            return + +        claimant_id = await _caches.claimants.get(channel.id) +        if not claimant_id: +            # The mapping for this channel doesn't exist, we can't do anything. +            return + +        # Cache the timestamp of the non-claimants message +        await _caches.non_claimant_last_message_times.set(channel.id, timestamp)  async def get_last_message(channel: discord.TextChannel) -> t.Optional[discord.Message]: diff --git a/bot/exts/help_channels/_stats.py b/bot/exts/help_channels/_stats.py index b8778e7d9..e212c495d 100644 --- a/bot/exts/help_channels/_stats.py +++ b/bot/exts/help_channels/_stats.py @@ -35,8 +35,8 @@ async def report_complete_session(channel_id: int, is_auto: bool) -> None:      if in_use_time:          bot.instance.stats.timing("help.in_use_time", in_use_time) -    unanswered = await _caches.unanswered.get(channel_id) -    if unanswered: +    non_claimant_last_message_time = await _caches.non_claimant_last_message_times.get(channel_id) +    if non_claimant_last_message_time is None:          bot.instance.stats.incr("help.sessions.unanswered") -    elif unanswered is not None: +    else:          bot.instance.stats.incr("help.sessions.answered")  |