diff options
| -rw-r--r-- | bot/constants.py | 6 | ||||
| -rw-r--r-- | bot/exts/help_channels/_channel.py | 4 | ||||
| -rw-r--r-- | bot/exts/help_channels/_cog.py | 23 | ||||
| -rw-r--r-- | bot/exts/help_channels/_message.py | 93 | ||||
| -rw-r--r-- | config-default.yml | 17 | 
5 files changed, 97 insertions, 46 deletions
diff --git a/bot/constants.py b/bot/constants.py index 77c01bfa3..b775848fb 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -620,10 +620,12 @@ class HelpChannels(metaclass=YAMLGetter):      max_available: int      max_total_channels: int      name_prefix: str -    notify: bool      notify_channel: int      notify_minutes: int -    notify_roles: List[int] +    notify_none_remaining: bool +    notify_none_remaining_roles: List[int] +    notify_running_low: bool +    notify_running_low_threshold: int  class RedirectOutput(metaclass=YAMLGetter): diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py index ea7d972b5..d9cebf215 100644 --- a/bot/exts/help_channels/_channel.py +++ b/bot/exts/help_channels/_channel.py @@ -24,7 +24,7 @@ class ClosingReason(Enum):      """All possible closing reasons for help channels."""      COMMAND = "command" -    LATEST_MESSSAGE = "auto.latest_message" +    LATEST_MESSAGE = "auto.latest_message"      CLAIMANT_TIMEOUT = "auto.claimant_timeout"      OTHER_TIMEOUT = "auto.other_timeout"      DELETED = "auto.deleted" @@ -77,7 +77,7 @@ async def get_closing_time(channel: discord.TextChannel, init_done: bool) -> t.T          # Use the greatest offset to avoid the possibility of prematurely closing the channel.          time = Arrow.fromdatetime(msg.created_at) + timedelta(minutes=idle_minutes_claimant) -        reason = ClosingReason.DELETED if is_empty else ClosingReason.LATEST_MESSSAGE +        reason = ClosingReason.DELETED if is_empty else ClosingReason.LATEST_MESSAGE          return time, reason      claimant_time = Arrow.utcfromtimestamp(claimant_time) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index f276a7993..a93acffb6 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -78,7 +78,10 @@ class HelpChannels(commands.Cog):          self.channel_queue: asyncio.Queue[discord.TextChannel] = None          self.name_queue: t.Deque[str] = None -        self.last_notification: t.Optional[arrow.Arrow] = None +        # Notifications +        # Using a very old date so that we don't have to use Optional typing. +        self.last_none_remaining_notification = arrow.get('1815-12-10T18:00:00.00000+00:00') +        self.last_running_low_notification = arrow.get('1815-12-10T18:00:00.00000+00:00')          self.dynamic_message: t.Optional[int] = None          self.available_help_channels: t.Set[discord.TextChannel] = set() @@ -252,13 +255,21 @@ class HelpChannels(commands.Cog):              if not channel:                  log.info("Couldn't create a candidate channel; waiting to get one from the queue.") -                notify_channel = self.bot.get_channel(constants.HelpChannels.notify_channel) -                last_notification = await _message.notify(notify_channel, self.last_notification) +                last_notification = await _message.notify_none_remaining(self.last_none_remaining_notification) +                  if last_notification: -                    self.last_notification = last_notification -                    self.bot.stats.incr("help.out_of_channel_alerts") +                    self.last_none_remaining_notification = last_notification + +                channel = await self.wait_for_dormant_channel()  # Blocks until a new channel is available + +        else: +            last_notification = await _message.notify_running_low( +                self.channel_queue.qsize(), +                self.last_running_low_notification +            ) -                channel = await self.wait_for_dormant_channel() +            if last_notification: +                self.last_running_low_notification = last_notification          return channel diff --git a/bot/exts/help_channels/_message.py b/bot/exts/help_channels/_message.py index 241dd606c..7ceed9b4d 100644 --- a/bot/exts/help_channels/_message.py +++ b/bot/exts/help_channels/_message.py @@ -124,52 +124,93 @@ async def dm_on_open(message: discord.Message) -> None:          ) -async def notify(channel: discord.TextChannel, last_notification: t.Optional[Arrow]) -> t.Optional[Arrow]: +async def notify_none_remaining(last_notification: Arrow) -> t.Optional[Arrow]:      """ -    Send a message in `channel` notifying about a lack of available help channels. +    Send a pinging message in `channel` notifying about there being no dormant channels remaining.      If a notification was sent, return the time at which the message was sent.      Otherwise, return None.      Configuration: - -    * `HelpChannels.notify` - toggle notifications -    * `HelpChannels.notify_minutes` - minimum interval between notifications -    * `HelpChannels.notify_roles` - roles mentioned in notifications +        * `HelpChannels.notify_minutes`              - minimum interval between notifications +        * `HelpChannels.notify_none_remaining`       - toggle none_remaining notifications +        * `HelpChannels.notify_none_remaining_roles` - roles mentioned in notifications      """ -    if not constants.HelpChannels.notify: -        return +    if not constants.HelpChannels.notify_none_remaining: +        return None + +    if (arrow.utcnow() - last_notification).total_seconds() < (constants.HelpChannels.notify_minutes * 60): +        log.trace("Did not send none_remaining notification as it hasn't been enough time since the last one.") +        return None      log.trace("Notifying about lack of channels.") -    if last_notification: -        elapsed = (arrow.utcnow() - last_notification).seconds -        minimum_interval = constants.HelpChannels.notify_minutes * 60 -        should_send = elapsed >= minimum_interval -    else: -        should_send = True +    mentions = " ".join(f"<@&{role}>" for role in constants.HelpChannels.notify_none_remaining_roles) +    allowed_roles = [discord.Object(id_) for id_ in constants.HelpChannels.notify_none_remaining_roles] -    if not should_send: -        log.trace("Notification not sent because it's too recent since the previous one.") -        return +    channel = bot.instance.get_channel(constants.HelpChannels.notify_channel) +    if channel is None: +        log.trace("Did not send none_remaining notification as the notification channel couldn't be gathered.")      try: -        log.trace("Sending notification message.") - -        mentions = " ".join(f"<@&{role}>" for role in constants.HelpChannels.notify_roles) -        allowed_roles = [discord.Object(id_) for id_ in constants.HelpChannels.notify_roles] - -        message = await channel.send( +        await channel.send(              f"{mentions} A new available help channel is needed but there " -            f"are no more dormant ones. Consider freeing up some in-use channels manually by " +            "are no more dormant ones. Consider freeing up some in-use channels manually by "              f"using the `{constants.Bot.prefix}dormant` command within the channels.",              allowed_mentions=discord.AllowedMentions(everyone=False, roles=allowed_roles)          ) - -        return Arrow.fromdatetime(message.created_at)      except Exception:          # Handle it here cause this feature isn't critical for the functionality of the system.          log.exception("Failed to send notification about lack of dormant channels!") +    else: +        bot.instance.stats.incr("help.out_of_channel_alerts") +        return arrow.utcnow() + + +async def notify_running_low(number_of_channels_left: int, last_notification: Arrow) -> t.Optional[Arrow]: +    """ +    Send a non-pinging message in `channel` notifying about there being a low amount of dormant channels. + +    This will include the number of dormant channels left `number_of_channels_left` + +    If a notification was sent, return the time at which the message was sent. +    Otherwise, return None. + +    Configuration: +        * `HelpChannels.notify_minutes`               - minimum interval between notifications +        * `HelpChannels.notify_running_low`           - toggle running_low notifications +        * `HelpChannels.notify_running_low_threshold` - minimum amount of channels to trigger running_low notifications +    """ +    if not constants.HelpChannels.notify_running_low: +        return None + +    if number_of_channels_left > constants.HelpChannels.notify_running_low_threshold: +        log.trace("Did not send notify_running_low notification as the threshold was not met.") +        return None + +    if (arrow.utcnow() - last_notification).total_seconds() < (constants.HelpChannels.notify_minutes * 60): +        log.trace("Did not send notify_running_low notification as it hasn't been enough time since the last one.") +        return None + +    log.trace("Notifying about getting close to no dormant channels.") + +    channel = bot.instance.get_channel(constants.HelpChannels.notify_channel) +    if channel is None: +        log.trace("Did not send notify_running notification as the notification channel couldn't be gathered.") + +    try: +        if number_of_channels_left == 1: +            message = f"There is only {number_of_channels_left} dormant channel left. " +        else: +            message = f"There are only {number_of_channels_left} dormant channels left. " +        message += "Consider participating in some help channels so that we don't run out." +        await channel.send(message) +    except Exception: +        # Handle it here cause this feature isn't critical for the functionality of the system. +        log.exception("Failed to send notification about running low of dormant channels!") +    else: +        bot.instance.stats.incr("help.running_low_alerts") +        return arrow.utcnow()  async def pin(message: discord.Message) -> None: diff --git a/config-default.yml b/config-default.yml index 583733fda..dae923158 100644 --- a/config-default.yml +++ b/config-default.yml @@ -513,19 +513,16 @@ help_channels:      # Prefix for help channel names      name_prefix: 'help-' -    # Notify if more available channels are needed but there are no more dormant ones -    notify: true +    notify_channel:                *HELPERS  # Channel in which to send notifications messages +    notify_minutes:                15        # Minimum interval between none_remaining or running_low notifications -    # Channel in which to send notifications -    notify_channel: *HELPERS - -    # Minimum interval between helper notifications -    notify_minutes: 15 - -    # Mention these roles in notifications -    notify_roles: +    notify_none_remaining:         true      # Pinging notification for the Helper role when no dormant channels remain +    notify_none_remaining_roles:             # Mention these roles in the none_remaining notification          - *HELPERS_ROLE +    notify_running_low:            true      # Non-pinging notification which is triggered when the channel count is equal or less than the threshold +    notify_running_low_threshold:  4         # The amount of channels at which a running_low notification will be sent +  redirect_output:      delete_delay: 15  |