aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Chris <[email protected]>2021-03-16 20:08:53 +0000
committerGravatar Chris <[email protected]>2021-03-16 20:08:53 +0000
commitb3c66a6fb07ebc92c0b53d946cf10df6c1107303 (patch)
tree931a10367c9810ea92231c421345b7aba4abc7c5
parentUpdate help availible footer (diff)
Extend close time logic to differentiate between the claimant and other users.
-rw-r--r--bot/exts/help_channels/_caches.py8
-rw-r--r--bot/exts/help_channels/_channel.py54
-rw-r--r--bot/exts/help_channels/_cog.py42
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)