Repository: python-discord/bot

Issue:
Add a dynamic message at the bottom of `#how-to-get-help`
Actions https://github.com/python-discord/organisation/issues/315. 

We should have a dynamic message at the bottom of #how-to-get-help saying something along the lines of 
“The currently available channels are #help-element1 and #help-element2, feel free to ask in any of those two channels!”, 
to avoid getting anyone getting confused by the currently open channels like we had in the past. 


Discussion:
How would the #how-to-get-help message be stored? Would the bot just get the latest message from the channel?

I could probably work on this.

Resolving patch:
diff --git a/bot/constants.py b/bot/constants.py
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -412,6 +412,7 @@ class Channels(metaclass=YAMLGetter):
     python_general: int
 
     cooldown: int
+    how_to_get_help: int
 
     attachment_log: int
     message_log: int
diff --git a/bot/exts/help_channels/_caches.py b/bot/exts/help_channels/_caches.py
--- a/bot/exts/help_channels/_caches.py
+++ b/bot/exts/help_channels/_caches.py
@@ -20,3 +20,7 @@
 # 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 keeps track of the dynamic message ID for
+# the continuously updated message in the #How-to-get-help channel.
+dynamic_message = RedisCache(namespace="HelpChannels.dynamic_message")
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py
--- a/bot/exts/help_channels/_cog.py
+++ b/bot/exts/help_channels/_cog.py
@@ -21,6 +21,7 @@
 HELP_CHANNEL_TOPIC = """
 This is a Python help channel. You can claim your own help channel in the Python Help: Available category.
 """
+AVAILABLE_HELP_CHANNELS = "**Currently available help channel(s):** {available}"
 
 
 class HelpChannels(commands.Cog):
@@ -75,6 +76,9 @@ def __init__(self, bot: Bot):
 
         self.last_notification: t.Optional[arrow.Arrow] = None
 
+        self.dynamic_message: t.Optional[int] = None
+        self.available_help_channels: t.Set[discord.TextChannel] = set()
+
         # Asyncio stuff
         self.queue_tasks: t.List[asyncio.Task] = []
         self.init_task = self.bot.loop.create_task(self.init_cog())
@@ -123,6 +127,9 @@ async def claim_channel(self, message: discord.Message) -> None:
         # Delete to indicate that the help session has yet to receive an answer.
         await _caches.non_claimant_last_message_times.delete(message.channel.id)
 
+        # Removing the help channel from the dynamic message, and editing/sending that message.
+        self.available_help_channels.remove(message.channel)
+
         # 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}")
 
@@ -240,6 +247,10 @@ async def init_available(self) -> None:
             for channel in channels[:abs(missing)]:
                 await self.unclaim_channel(channel, closed_on=_channel.ClosingReason.CLEANUP)
 
+        # Getting channels that need to be included in the dynamic message.
+        await self.update_available_help_channels()
+        log.trace("Dynamic available help message updated.")
+
     async def init_categories(self) -> None:
         """Get the help category objects. Remove the cog if retrieval fails."""
         log.trace("Getting the CategoryChannel objects for the help categories.")
@@ -284,6 +295,10 @@ async def init_cog(self) -> None:
         # This may confuse users. So would potentially long delays for the cog to become ready.
         self.close_command.enabled = True
 
+        # Acquiring the dynamic message ID, if it exists within the cache.
+        log.trace("Attempting to fetch How-to-get-help dynamic message ID.")
+        self.dynamic_message = await _caches.dynamic_message.get("message_id")
+
         await self.init_available()
         _stats.report_counts()
 
@@ -338,6 +353,10 @@ async def move_to_available(self) -> None:
             category_id=constants.Categories.help_available,
         )
 
+        # Adding the help channel to the dynamic message, and editing/sending that message.
+        self.available_help_channels.add(channel)
+        await self.update_available_help_channels()
+
         _stats.report_counts()
 
     async def move_to_dormant(self, channel: discord.TextChannel) -> None:
@@ -472,3 +491,32 @@ async def wait_for_dormant_channel(self) -> discord.TextChannel:
         self.queue_tasks.remove(task)
 
         return channel
+
+    async def update_available_help_channels(self) -> None:
+        """Updates the dynamic message within #how-to-get-help for available help channels."""
+        if not self.available_help_channels:
+            self.available_help_channels = set(
+                c for c in self.available_category.channels if not _channel.is_excluded_channel(c)
+            )
+
+        available_channels = AVAILABLE_HELP_CHANNELS.format(
+            available=', '.join(c.mention for c in self.available_help_channels) or None
+        )
+
+        if self.dynamic_message is not None:
+            try:
+                log.trace("Help channels have changed, dynamic message has been edited.")
+                await self.bot.http.edit_message(
+                    constants.Channels.how_to_get_help, self.dynamic_message, content=available_channels
+                )
+            except discord.NotFound:
+                pass
+            else:
+                return
+
+        log.trace("Dynamic message could not be edited or found. Creating a new one.")
+        new_dynamic_message = await self.bot.http.send_message(
+            constants.Channels.how_to_get_help, available_channels
+        )
+        self.dynamic_message = new_dynamic_message["id"]
+        await _caches.dynamic_message.set("message_id", self.dynamic_message)


Test patch: