aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar laundmo <[email protected]>2021-03-19 22:56:50 +0100
committerGravatar laundmo <[email protected]>2021-03-19 22:56:50 +0100
commit457cb14deade3d023d01ee0342bf3c84911f6d0b (patch)
tree43bbb4376eb54c7470588e0910c98fa448f90730
parentMore descriptive comment for when an if block is entered (diff)
reset cache on bot start, stats for different close reasons
-rw-r--r--bot/exts/help_channels/_channel.py28
-rw-r--r--bot/exts/help_channels/_cog.py35
-rw-r--r--bot/exts/help_channels/_stats.py4
3 files changed, 39 insertions, 28 deletions
diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py
index ad683b218..1e152eaa3 100644
--- a/bot/exts/help_channels/_channel.py
+++ b/bot/exts/help_channels/_channel.py
@@ -25,11 +25,13 @@ def get_category_channels(category: discord.CategoryChannel) -> t.Iterable[disco
yield channel
-async def get_closing_time(channel: discord.TextChannel) -> datetime:
- """Return the timestamp at which the given help `channel` should be closed."""
+async def get_closing_time(channel: discord.TextChannel) -> t.Tuple[datetime, str]:
+ """Return the timestamp at which the given help `channel` should be closed along with the reason."""
log.trace(f"Getting the closing time for #{channel} ({channel.id}).")
- if is_empty := await _message.is_empty(channel):
+ is_empty = await _message.is_empty(channel)
+
+ if is_empty:
idle_minutes = constants.HelpChannels.deleted_idle_minutes
else:
idle_minutes = constants.HelpChannels.idle_minutes_others
@@ -40,14 +42,16 @@ async def get_closing_time(channel: discord.TextChannel) -> datetime:
if is_empty or not (non_claimant_last_message_time and claimant_last_message_time):
# Current help session has no messages, or at least one of the caches is empty.
# Use the last message in the channel to determine closing time instead.
+
msg = await _message.get_last_message(channel)
+
if not msg:
# last message can't be retreived, return datetime.min so channel closes right now.
log.debug(f"No idle time available; #{channel} ({channel.id}) has no messages, closing now.")
- return datetime.min
+ return datetime.min, "deleted"
# The time at which a channel should be closed.
- return msg.created_at + timedelta(minutes=idle_minutes)
+ return msg.created_at + timedelta(minutes=idle_minutes), "latest_message"
# Get the later time at which a channel should be closed
non_claimant_last_message_time = datetime.fromtimestamp(non_claimant_last_message_time)
@@ -57,14 +61,12 @@ async def get_closing_time(channel: discord.TextChannel) -> datetime:
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, 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
-
+ if claimant_last_message_time >= non_claimant_last_message_time:
+ log.trace(f"#{channel} ({channel.id}) should be closed at {claimant_last_message_time} due to claimant timeout.")
+ return claimant_last_message_time, "claimant_timeout"
+ else:
+ log.trace(f"#{channel} ({channel.id}) should be closed at {non_claimant_last_message_time} due to others timeout.")
+ return non_claimant_last_message_time, "others_timeout"
async def get_in_use_time(channel_id: int) -> t.Optional[timedelta]:
"""Return the duration `channel_id` has been in use. Return None if it's not in use."""
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py
index a372fa868..ef6a286d6 100644
--- a/bot/exts/help_channels/_cog.py
+++ b/bot/exts/help_channels/_cog.py
@@ -118,6 +118,9 @@ class HelpChannels(commands.Cog):
timestamp = datetime.now(timezone.utc).timestamp()
await _caches.claim_times.set(message.channel.id, timestamp)
await _caches.claimant_last_message_times.set(message.channel.id, timestamp)
+ # non_claimant needs to be set too, to satisfy the condition in `_channel.get_closing_time` the first time.
+ # Otherwise it will fall back to the old method if no other messages are sent.
+ await _caches.non_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}")
@@ -188,7 +191,7 @@ class HelpChannels(commands.Cog):
# Don't use a discord.py check because the check needs to fail silently.
if await self.close_check(ctx):
log.info(f"Close command invoked by {ctx.author} in #{ctx.channel}.")
- await self.unclaim_channel(ctx.channel, is_auto=False)
+ await self.unclaim_channel(ctx.channel, closed_on="command")
async def get_available_candidate(self) -> discord.TextChannel:
"""
@@ -234,7 +237,7 @@ class HelpChannels(commands.Cog):
elif missing < 0:
log.trace(f"Moving {abs(missing)} superfluous available channels over to the Dormant category.")
for channel in channels[:abs(missing)]:
- await self.unclaim_channel(channel)
+ await self.unclaim_channel(channel, closed_on="cleanup")
async def init_categories(self) -> None:
"""Get the help category objects. Remove the cog if retrieval fails."""
@@ -272,6 +275,8 @@ class HelpChannels(commands.Cog):
log.trace("Moving or rescheduling in-use channels.")
for channel in _channel.get_category_channels(self.in_use_category):
+ # clear the cache here so moving doesn't rely on old cached messages.
+ await self._delete_message_time_caches(channel)
await self.move_idle_channel(channel, has_task=False)
# Prevent the command from being used until ready.
@@ -294,16 +299,16 @@ class HelpChannels(commands.Cog):
"""
log.trace(f"Handling in-use channel #{channel} ({channel.id}).")
- closing_time = await _channel.get_closing_time(channel)
+ closing_time, closed_on = await _channel.get_closing_time(channel)
# The time at which the channel should be closed, based on messages sent.
if closing_time < datetime.utcnow():
log.info(
f"#{channel} ({channel.id}) is idle past {closing_time} "
- f"and will be made dormant."
+ f"and will be made dormant. Reason: {closed_on}"
)
- await self.unclaim_channel(channel)
+ await self.unclaim_channel(channel, closed_on=closed_on)
else:
# Cancel the existing task, if any.
if has_task:
@@ -353,7 +358,7 @@ class HelpChannels(commands.Cog):
_stats.report_counts()
@lock.lock_arg(f"{NAMESPACE}.unclaim", "channel")
- async def unclaim_channel(self, channel: discord.TextChannel, *, is_auto: bool = True) -> None:
+ async def unclaim_channel(self, channel: discord.TextChannel, *, closed_on: str) -> None:
"""
Unclaim an in-use help `channel` to make it dormant.
@@ -361,7 +366,7 @@ class HelpChannels(commands.Cog):
Remove the cooldown role from the channel claimant if they have no other channels claimed.
Cancel the scheduled cooldown role removal task.
- Set `is_auto` to True if the channel was automatically closed or False if manually closed.
+ `closed_on` is the reason that the channel was closed for. Examples: "cleanup", "command", "claimant_timeout"
"""
claimant_id = await _caches.claimants.get(channel.id)
_unclaim_channel = self._unclaim_channel
@@ -372,13 +377,17 @@ class HelpChannels(commands.Cog):
decorator = lock.lock_arg(f"{NAMESPACE}.unclaim", "claimant_id", wait=True)
_unclaim_channel = decorator(_unclaim_channel)
- return await _unclaim_channel(channel, claimant_id, is_auto)
+ return await _unclaim_channel(channel, claimant_id, closed_on)
+
+ async def _delete_message_time_caches(self, channel: discord.TextChannel) -> None:
+ """Delete message time caches """
+ await _caches.claimant_last_message_times.delete(channel.id)
+ await _caches.non_claimant_last_message_times.delete(channel.id)
- async def _unclaim_channel(self, channel: discord.TextChannel, claimant_id: int, is_auto: bool) -> None:
+ async def _unclaim_channel(self, channel: discord.TextChannel, claimant_id: int, closed_on: str) -> None:
"""Actual implementation of `unclaim_channel`. See that for full documentation."""
await _caches.claimants.delete(channel.id)
- await _caches.claimant_last_message_times.delete(channel.id)
- await _caches.non_claimant_last_message_times.delete(channel.id)
+ await self._delete_message_time_caches(channel)
# Ignore missing tasks because a channel may still be dormant after the cooldown expires.
if claimant_id in self.scheduler:
@@ -392,12 +401,12 @@ class HelpChannels(commands.Cog):
await _cooldown.remove_cooldown_role(claimant)
await _message.unpin(channel)
- await _stats.report_complete_session(channel.id, is_auto)
+ await _stats.report_complete_session(channel.id, closed_on)
await self.move_to_dormant(channel)
# Cancel the task that makes the channel dormant only if called by the close command.
# In other cases, the task is either already done or not-existent.
- if not is_auto:
+ if not closed_on:
self.scheduler.cancel(channel.id)
async def move_to_in_use(self, channel: discord.TextChannel) -> None:
diff --git a/bot/exts/help_channels/_stats.py b/bot/exts/help_channels/_stats.py
index e212c495d..cc9a053c4 100644
--- a/bot/exts/help_channels/_stats.py
+++ b/bot/exts/help_channels/_stats.py
@@ -22,13 +22,13 @@ def report_counts() -> None:
log.warning(f"Couldn't find category {name!r} to track channel count stats.")
-async def report_complete_session(channel_id: int, is_auto: bool) -> None:
+async def report_complete_session(channel_id: int, closed_on: str) -> None:
"""
Report stats for a completed help session channel `channel_id`.
Set `is_auto` to True if the channel was automatically closed or False if manually closed.
"""
- caller = "auto" if is_auto else "command"
+ caller = f"auto.{closed_on}" if closed_on else "command"
bot.instance.stats.incr(f"help.dormant_calls.{caller}")
in_use_time = await _channel.get_in_use_time(channel_id)