From 6f4213fff0ed80d0376159dd84a27e276fbb303b Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 14:38:06 +0300 Subject: (Syncers): Fixed wrong except statement Replaced `TimeoutError` with `asyncio.TimeoutError`. --- bot/cogs/sync/syncers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/sync/syncers.py b/bot/cogs/sync/syncers.py index 003bf3727..e55bf27fd 100644 --- a/bot/cogs/sync/syncers.py +++ b/bot/cogs/sync/syncers.py @@ -1,4 +1,5 @@ import abc +import asyncio import logging import typing as t from collections import namedtuple @@ -122,7 +123,7 @@ class Syncer(abc.ABC): check=partial(self._reaction_check, author, message), timeout=constants.Sync.confirm_timeout ) - except TimeoutError: + except asyncio.TimeoutError: # reaction will remain none thus sync will be aborted in the finally block below. log.debug(f"The {self.name} syncer confirmation prompt timed out.") -- cgit v1.2.3 From 7434ed3152e6d3f89babe2fef332983925d04434 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 14:45:32 +0300 Subject: (Syncer Tests): Replaced wrong side effect Replaced `TimeoutError` with `asyncio.TimeoutError`. --- tests/bot/cogs/sync/test_base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/bot/cogs/sync/test_base.py b/tests/bot/cogs/sync/test_base.py index 6ee9dfda6..70aea2bab 100644 --- a/tests/bot/cogs/sync/test_base.py +++ b/tests/bot/cogs/sync/test_base.py @@ -1,3 +1,4 @@ +import asyncio import unittest from unittest import mock @@ -211,7 +212,7 @@ class SyncerConfirmationTests(unittest.IsolatedAsyncioTestCase): subtests = ( (constants.Emojis.check_mark, True, None), ("InVaLiD", False, None), - (None, False, TimeoutError), + (None, False, asyncio.TimeoutError), ) for emoji, ret_val, side_effect in subtests: -- cgit v1.2.3 From c608963a0672b8396190f521366799d649a8cb90 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 00:15:07 +0200 Subject: Remove dormant invokation message after move. --- bot/cogs/help_channels.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 697a4d3b7..75b61f6cb 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -196,6 +196,7 @@ class HelpChannels(Scheduler, commands.Cog): if ctx.channel.category == self.in_use_category: self.cancel_task(ctx.channel.id) await self.move_to_dormant(ctx.channel) + await ctx.message.delete() else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") -- cgit v1.2.3 From 1a58b34565e612a48d8dc59cb3f4ed75ee593744 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 01:46:57 +0200 Subject: Allow help session starters to invoke dormant. Removing the `with_role` check from the command and replcaing it with a new `dormant_check` that's used in the body, which also checks against a cache of users that started the sessions, allows them to close their own channels along with the role check. --- bot/cogs/help_channels.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 75b61f6cb..cdfe4e72c 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -13,7 +13,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.decorators import with_role +from bot.utils.checks import with_role_check from bot.utils.scheduling import Scheduler log = logging.getLogger(__name__) @@ -108,6 +108,7 @@ class HelpChannels(Scheduler, commands.Cog): super().__init__() self.bot = bot + self.help_channel_users: t.Dict[discord.User, discord.TextChannel] = {} # Categories self.available_category: discord.CategoryChannel = None @@ -187,16 +188,24 @@ class HelpChannels(Scheduler, commands.Cog): log.trace("Populating the name queue with names.") return deque(available_names) + async def dormant_check(self, ctx: commands.Context) -> bool: + """Return True if the user started the help channel session or passes the role check.""" + if self.help_channel_users.get(ctx.author) == ctx.channel: + log.trace(f"{ctx.author} started the help session, passing the check for dormant.") + return True + + log.trace(f"{ctx.author} did not start the help session, checking roles.") + return with_role_check(ctx, *constants.HelpChannels.cmd_whitelist) + @commands.command(name="dormant", aliases=["close"], enabled=False) - @with_role(*constants.HelpChannels.cmd_whitelist) async def dormant_command(self, ctx: commands.Context) -> None: """Make the current in-use help channel dormant.""" log.trace("dormant command invoked; checking if the channel is in-use.") - if ctx.channel.category == self.in_use_category: - self.cancel_task(ctx.channel.id) - await self.move_to_dormant(ctx.channel) - await ctx.message.delete() + if await self.dormant_check(ctx): + self.cancel_task(ctx.channel.id) + await self.move_to_dormant(ctx.channel) + await ctx.message.delete() else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") @@ -543,6 +552,8 @@ class HelpChannels(Scheduler, commands.Cog): await self.move_to_in_use(channel) await self.revoke_send_permissions(message.author) + # Add user with channel for dormant check. + self.help_channel_users[message.author] = channel log.trace(f"Releasing on_message lock for {message.id}.") -- cgit v1.2.3 From 7ccfa21f38b0df50f582ceb6e64d1ce1fe9c6617 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 02:02:41 +0200 Subject: Reset cooldown after channel is made dormant. --- bot/cogs/help_channels.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index cdfe4e72c..7bfb33875 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -206,6 +206,7 @@ class HelpChannels(Scheduler, commands.Cog): self.cancel_task(ctx.channel.id) await self.move_to_dormant(ctx.channel) await ctx.message.delete() + await self.reset_send_permissions_for_help_user(ctx.channel) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") @@ -610,6 +611,19 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Ensuring channels in `Help: Available` are synchronized after permissions reset.") await self.ensure_permissions_synchronization(self.available_category) + async def reset_send_permissions_for_help_user(self, channel: discord.TextChannel) -> None: + """Reset send permissions in the Available category for member that started the help session in `channel`.""" + # Get mapping of channels to users that started the help session in them. + channels_to_users = {value: key for key, value in self.help_channel_users.items()} + log.trace(f"Attempting to find user for help session in #{channel.name} ({channel.id}).") + try: + member: discord.Member = channels_to_users[channel] + except KeyError: + log.trace(f"Channel #{channel.name} ({channel.id}) not in help session cache, permissions unchanged.") + return + log.trace(f"Resetting send permissions for {member} ({member.id}).") + await self.available_category.set_permissions(member, send_messages=None) + async def revoke_send_permissions(self, member: discord.Member) -> None: """ Disallow `member` to send messages in the Available category for a certain time. -- cgit v1.2.3 From c6355f67192e247f6b207cb0e18b2ff80d5b710f Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 02:03:13 +0200 Subject: Extend docstrings to include new behaviour. --- bot/cogs/help_channels.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 7bfb33875..912ce4f44 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -199,7 +199,13 @@ class HelpChannels(Scheduler, commands.Cog): @commands.command(name="dormant", aliases=["close"], enabled=False) async def dormant_command(self, ctx: commands.Context) -> None: - """Make the current in-use help channel dormant.""" + """ + Make the current in-use help channel dormant. + + Make the channel dormant if the user passes the `dormant_check`, + delete the message that invoked this, + and reset the send permissions cooldown for the user who started the session. + """ log.trace("dormant command invoked; checking if the channel is in-use.") if ctx.channel.category == self.in_use_category: if await self.dormant_check(ctx): -- cgit v1.2.3 From acee84d044c65b9a8d6ab4a164d189fa0eaa174a Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 02:06:07 +0200 Subject: Handle dormant invokation not being found. --- bot/cogs/help_channels.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 912ce4f44..bc973cd4d 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -5,6 +5,7 @@ import logging import random import typing as t from collections import deque +from contextlib import suppress from datetime import datetime from pathlib import Path @@ -211,7 +212,9 @@ class HelpChannels(Scheduler, commands.Cog): if await self.dormant_check(ctx): self.cancel_task(ctx.channel.id) await self.move_to_dormant(ctx.channel) - await ctx.message.delete() + with suppress(discord.errors.NotFound): + await ctx.message.delete() + log.trace("Deleting dormant invokation message.") await self.reset_send_permissions_for_help_user(ctx.channel) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") -- cgit v1.2.3 From c2e9ec459cd28832a1e797d7c755b5aab57d69dd Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 02:13:36 +0200 Subject: Cancel permission restoration task. After the dormant command is used and the permissions are restored for the user that started the session, the task for restoring them after the claim time has passed is no longer necessary. --- bot/cogs/help_channels.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index bc973cd4d..03bac27a4 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -632,6 +632,8 @@ class HelpChannels(Scheduler, commands.Cog): return log.trace(f"Resetting send permissions for {member} ({member.id}).") await self.available_category.set_permissions(member, send_messages=None) + # Cancel task, ignore no task existing when the claim time passed but idle time has not. + self.cancel_task(member.id, ignore_missing=True) async def revoke_send_permissions(self, member: discord.Member) -> None: """ -- cgit v1.2.3 From 7f1be2882dde670d12000fe661424d3b6f406f89 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 6 Apr 2020 22:34:17 +0200 Subject: Reverse help_channel_user pairs. Pairing users to channels was a design flaw, because the keys didn't get overwritten. This allowed multiple users to access the dormant command for the running session of the bot. Replacing this with a reversed paring fixes both issues because the cache is overwritten on channel activation. --- bot/cogs/help_channels.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 03bac27a4..dbe7cedc1 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -109,7 +109,7 @@ class HelpChannels(Scheduler, commands.Cog): super().__init__() self.bot = bot - self.help_channel_users: t.Dict[discord.User, discord.TextChannel] = {} + self.help_channel_users: t.Dict[discord.TextChannel, discord.User] = {} # Categories self.available_category: discord.CategoryChannel = None @@ -191,7 +191,7 @@ class HelpChannels(Scheduler, commands.Cog): async def dormant_check(self, ctx: commands.Context) -> bool: """Return True if the user started the help channel session or passes the role check.""" - if self.help_channel_users.get(ctx.author) == ctx.channel: + if self.help_channel_users.get(ctx.channel) == ctx.author: log.trace(f"{ctx.author} started the help session, passing the check for dormant.") return True @@ -563,7 +563,7 @@ class HelpChannels(Scheduler, commands.Cog): await self.move_to_in_use(channel) await self.revoke_send_permissions(message.author) # Add user with channel for dormant check. - self.help_channel_users[message.author] = channel + self.help_channel_users[channel] = message.author log.trace(f"Releasing on_message lock for {message.id}.") @@ -622,11 +622,9 @@ class HelpChannels(Scheduler, commands.Cog): async def reset_send_permissions_for_help_user(self, channel: discord.TextChannel) -> None: """Reset send permissions in the Available category for member that started the help session in `channel`.""" - # Get mapping of channels to users that started the help session in them. - channels_to_users = {value: key for key, value in self.help_channel_users.items()} log.trace(f"Attempting to find user for help session in #{channel.name} ({channel.id}).") try: - member: discord.Member = channels_to_users[channel] + member = self.help_channel_users[channel] except KeyError: log.trace(f"Channel #{channel.name} ({channel.id}) not in help session cache, permissions unchanged.") return -- cgit v1.2.3 From 25c15171020613f0123ed83654adad5c7c584d84 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 7 Apr 2020 01:11:37 +0200 Subject: Delete overwrite instead of send_messages permission. Only resetting the permission caused the overwrites for the users to remain on the category, potentially piling up and causing further issues. --- bot/cogs/help_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index dbe7cedc1..f49149d8a 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -629,7 +629,7 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Channel #{channel.name} ({channel.id}) not in help session cache, permissions unchanged.") return log.trace(f"Resetting send permissions for {member} ({member.id}).") - await self.available_category.set_permissions(member, send_messages=None) + await self.available_category.set_permissions(member, overwrite=None) # Cancel task, ignore no task existing when the claim time passed but idle time has not. self.cancel_task(member.id, ignore_missing=True) -- cgit v1.2.3 From 4d6c342d2a35a54f65f25a973994c7dc55ca4be8 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 7 Apr 2020 01:12:57 +0200 Subject: Change names to more descriptive ones. --- bot/cogs/help_channels.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index f49149d8a..73c7adb15 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -109,7 +109,7 @@ class HelpChannels(Scheduler, commands.Cog): super().__init__() self.bot = bot - self.help_channel_users: t.Dict[discord.TextChannel, discord.User] = {} + self.help_channel_claimants: t.Dict[discord.TextChannel, discord.User] = {} # Categories self.available_category: discord.CategoryChannel = None @@ -191,7 +191,7 @@ class HelpChannels(Scheduler, commands.Cog): async def dormant_check(self, ctx: commands.Context) -> bool: """Return True if the user started the help channel session or passes the role check.""" - if self.help_channel_users.get(ctx.channel) == ctx.author: + if self.help_channel_claimants.get(ctx.channel) == ctx.author: log.trace(f"{ctx.author} started the help session, passing the check for dormant.") return True @@ -215,7 +215,7 @@ class HelpChannels(Scheduler, commands.Cog): with suppress(discord.errors.NotFound): await ctx.message.delete() log.trace("Deleting dormant invokation message.") - await self.reset_send_permissions_for_help_user(ctx.channel) + await self.reset_claimant_send_permission(ctx.channel) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") @@ -563,7 +563,7 @@ class HelpChannels(Scheduler, commands.Cog): await self.move_to_in_use(channel) await self.revoke_send_permissions(message.author) # Add user with channel for dormant check. - self.help_channel_users[channel] = message.author + self.help_channel_claimants[channel] = message.author log.trace(f"Releasing on_message lock for {message.id}.") @@ -620,11 +620,11 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Ensuring channels in `Help: Available` are synchronized after permissions reset.") await self.ensure_permissions_synchronization(self.available_category) - async def reset_send_permissions_for_help_user(self, channel: discord.TextChannel) -> None: + async def reset_claimant_send_permission(self, channel: discord.TextChannel) -> None: """Reset send permissions in the Available category for member that started the help session in `channel`.""" log.trace(f"Attempting to find user for help session in #{channel.name} ({channel.id}).") try: - member = self.help_channel_users[channel] + member = self.help_channel_claimants[channel] except KeyError: log.trace(f"Channel #{channel.name} ({channel.id}) not in help session cache, permissions unchanged.") return -- cgit v1.2.3 From 52f19185752358cff21aadc7902493f78030a94f Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 7 Apr 2020 01:14:21 +0200 Subject: Reword strings to reflect name changes. --- bot/cogs/help_channels.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 73c7adb15..3418ad210 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -190,12 +190,12 @@ class HelpChannels(Scheduler, commands.Cog): return deque(available_names) async def dormant_check(self, ctx: commands.Context) -> bool: - """Return True if the user started the help channel session or passes the role check.""" + """Return True if the user is the help channel claimant or passes the role check.""" if self.help_channel_claimants.get(ctx.channel) == ctx.author: - log.trace(f"{ctx.author} started the help session, passing the check for dormant.") + log.trace(f"{ctx.author} is the help channel claimant, passing the check for dormant.") return True - log.trace(f"{ctx.author} did not start the help session, checking roles.") + log.trace(f"{ctx.author} is not the help channel claimant, checking roles.") return with_role_check(ctx, *constants.HelpChannels.cmd_whitelist) @commands.command(name="dormant", aliases=["close"], enabled=False) @@ -621,12 +621,12 @@ class HelpChannels(Scheduler, commands.Cog): await self.ensure_permissions_synchronization(self.available_category) async def reset_claimant_send_permission(self, channel: discord.TextChannel) -> None: - """Reset send permissions in the Available category for member that started the help session in `channel`.""" - log.trace(f"Attempting to find user for help session in #{channel.name} ({channel.id}).") + """Reset send permissions in the Available category for the help `channel` claimant.""" + log.trace(f"Attempting to find claimant for #{channel.name} ({channel.id}).") try: member = self.help_channel_claimants[channel] except KeyError: - log.trace(f"Channel #{channel.name} ({channel.id}) not in help session cache, permissions unchanged.") + log.trace(f"Channel #{channel.name} ({channel.id}) not in claimant cache, permissions unchanged.") return log.trace(f"Resetting send permissions for {member} ({member.id}).") await self.available_category.set_permissions(member, overwrite=None) -- cgit v1.2.3 From a0daa7e77c4920fc51229fb751a48d08280ed42d Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 7 Apr 2020 01:20:03 +0200 Subject: Add spacing. Co-authored-by: MarkKoz --- bot/cogs/help_channels.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 3418ad210..421d41baf 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -628,6 +628,7 @@ class HelpChannels(Scheduler, commands.Cog): except KeyError: log.trace(f"Channel #{channel.name} ({channel.id}) not in claimant cache, permissions unchanged.") return + log.trace(f"Resetting send permissions for {member} ({member.id}).") await self.available_category.set_permissions(member, overwrite=None) # Cancel task, ignore no task existing when the claim time passed but idle time has not. -- cgit v1.2.3 From 3532d06db7083c8a7dc6a4b87a28d11423d2e605 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 7 Apr 2020 01:20:48 +0200 Subject: Reword comment. Co-authored-by: MarkKoz --- bot/cogs/help_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 421d41baf..52af03d27 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -631,7 +631,7 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Resetting send permissions for {member} ({member.id}).") await self.available_category.set_permissions(member, overwrite=None) - # Cancel task, ignore no task existing when the claim time passed but idle time has not. + # Ignore missing task when claim cooldown has passed but the channel still isn't dormant. self.cancel_task(member.id, ignore_missing=True) async def revoke_send_permissions(self, member: discord.Member) -> None: -- cgit v1.2.3 From 44333ae53b6692ab34b4fe967ecbf4f215e7fb0e Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Thu, 9 Apr 2020 15:20:02 +0200 Subject: Move message deletion up. --- bot/cogs/help_channels.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 52af03d27..8dca2ede0 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -210,11 +210,12 @@ class HelpChannels(Scheduler, commands.Cog): log.trace("dormant command invoked; checking if the channel is in-use.") if ctx.channel.category == self.in_use_category: if await self.dormant_check(ctx): - self.cancel_task(ctx.channel.id) - await self.move_to_dormant(ctx.channel) with suppress(discord.errors.NotFound): - await ctx.message.delete() log.trace("Deleting dormant invokation message.") + await ctx.message.delete() + + self.cancel_task(ctx.channel.id) + await self.move_to_dormant(ctx.channel) await self.reset_claimant_send_permission(ctx.channel) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") -- cgit v1.2.3 From 93a511da2be49305bc27fcce18f10f9758418dc5 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Thu, 9 Apr 2020 15:22:55 +0200 Subject: Move permissions reset up. --- bot/cogs/help_channels.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 8dca2ede0..f53f7a7ba 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -214,9 +214,10 @@ class HelpChannels(Scheduler, commands.Cog): log.trace("Deleting dormant invokation message.") await ctx.message.delete() + await self.reset_claimant_send_permission(ctx.channel) + self.cancel_task(ctx.channel.id) await self.move_to_dormant(ctx.channel) - await self.reset_claimant_send_permission(ctx.channel) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") -- cgit v1.2.3 From 221426d91fa1db5f334562ac0af52d93bbe7ab10 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Thu, 9 Apr 2020 15:23:26 +0200 Subject: Suppress errors when resetting permissions. --- bot/cogs/help_channels.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index f53f7a7ba..a6fa05d90 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -214,7 +214,8 @@ class HelpChannels(Scheduler, commands.Cog): log.trace("Deleting dormant invokation message.") await ctx.message.delete() - await self.reset_claimant_send_permission(ctx.channel) + with suppress(discord.errors.HTTPException, discord.errors.NotFound): + await self.reset_claimant_send_permission(ctx.channel) self.cancel_task(ctx.channel.id) await self.move_to_dormant(ctx.channel) -- cgit v1.2.3 From 9d6425474b8efa2dd4aba0086c1e8aaeb5eafeed Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Thu, 9 Apr 2020 08:00:39 -0700 Subject: HelpChannels: check author of dormant message In a testing environment, the bot may try to edit the message of a different bot. Therefore, the author of the message should be checked to ensure the current bot sent it. --- bot/cogs/help_channels.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index a6fa05d90..5f59a9d60 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -390,14 +390,13 @@ class HelpChannels(Scheduler, commands.Cog): log.info("Cog is ready!") self.ready.set() - @staticmethod - def is_dormant_message(message: t.Optional[discord.Message]) -> bool: + def is_dormant_message(self, message: t.Optional[discord.Message]) -> bool: """Return True if the contents of the `message` match `DORMANT_MSG`.""" if not message or not message.embeds: return False embed = message.embeds[0] - return embed.description.strip() == DORMANT_MSG.strip() + return message.author == self.bot.user and embed.description.strip() == DORMANT_MSG.strip() async def move_idle_channel(self, channel: discord.TextChannel, has_task: bool = True) -> None: """ -- cgit v1.2.3 From 65aba04130533154cf9cb3ebb64414f41d987305 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Thu, 9 Apr 2020 17:48:31 +0200 Subject: Reverse order of moving to dormant and task cancellation. Reversing the order ensures the task is not cancelled when moving to dormant fails which gives a fallback to move it after the initial period of time. --- bot/cogs/help_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 5f59a9d60..69812eda0 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -217,8 +217,8 @@ class HelpChannels(Scheduler, commands.Cog): with suppress(discord.errors.HTTPException, discord.errors.NotFound): await self.reset_claimant_send_permission(ctx.channel) - self.cancel_task(ctx.channel.id) await self.move_to_dormant(ctx.channel) + self.cancel_task(ctx.channel.id) else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") -- cgit v1.2.3 From d0a9404cb0cfee64bf26df5ec7cce2ea71cb4f15 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Thu, 9 Apr 2020 18:02:30 +0200 Subject: Delete channel from claimant cache. Deleting the channel from the claimant cache on invokation of the dormant command prevents users running the command multiple times before the bot moves it. --- bot/cogs/help_channels.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 69812eda0..346d35aa8 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -210,6 +210,9 @@ class HelpChannels(Scheduler, commands.Cog): log.trace("dormant command invoked; checking if the channel is in-use.") if ctx.channel.category == self.in_use_category: if await self.dormant_check(ctx): + with suppress(KeyError): + del self.help_channel_claimants[ctx.channel] + with suppress(discord.errors.NotFound): log.trace("Deleting dormant invokation message.") await ctx.message.delete() -- cgit v1.2.3 From 4ac17f806c2f9d98503067dbd181ae2a839bc74c Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Thu, 9 Apr 2020 18:31:55 +0200 Subject: Specify encoding to logging file handler. With the new addition of non latin-11 chars in channel names - which get logged, the logging to files fails on those entries on OSs where the default encoding is not utf8 or an other encoding capable of handling them. --- bot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/__init__.py b/bot/__init__.py index c9dbc3f40..2dd4af225 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -33,7 +33,7 @@ log_format = logging.Formatter(format_string) log_file = Path("logs", "bot.log") log_file.parent.mkdir(exist_ok=True) -file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7) +file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding="utf8") file_handler.setFormatter(log_format) root_log = logging.getLogger() -- cgit v1.2.3 From 3eecb147d6f788c83b230e9f79edf44da4d5c621 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Fri, 10 Apr 2020 15:38:19 +0200 Subject: Use synchronized permission reset. --- bot/cogs/help_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 346d35aa8..daadcc9a4 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -635,7 +635,7 @@ class HelpChannels(Scheduler, commands.Cog): return log.trace(f"Resetting send permissions for {member} ({member.id}).") - await self.available_category.set_permissions(member, overwrite=None) + await self.update_category_permissions(self.available_category, member, overwrite=None) # Ignore missing task when claim cooldown has passed but the channel still isn't dormant. self.cancel_task(member.id, ignore_missing=True) -- cgit v1.2.3 From d8e54d9921d204a0a95118136982186c790e0dd8 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Fri, 10 Apr 2020 15:43:34 +0200 Subject: Fix `help_channel_claimants` typehint. `ctx.author` that is used to populate the dict returns a `Member` object in most cases while only `User` was documented as a possible value. --- bot/cogs/help_channels.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index daadcc9a4..4404ecc17 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -109,7 +109,9 @@ class HelpChannels(Scheduler, commands.Cog): super().__init__() self.bot = bot - self.help_channel_claimants: t.Dict[discord.TextChannel, discord.User] = {} + self.help_channel_claimants: ( + t.Dict[discord.TextChannel, t.Union[discord.Member, discord.User]] + ) = {} # Categories self.available_category: discord.CategoryChannel = None -- cgit v1.2.3 From ef555490e222474a48fa470f30a1e600816c465f Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 18:14:07 +0100 Subject: StatsD integration --- Pipfile | 3 ++- Pipfile.lock | 59 ++++++++++++++++++++-------------------- bot/__main__.py | 1 + bot/bot.py | 14 ++++++++-- bot/cogs/antispam.py | 1 + bot/cogs/defcon.py | 1 + bot/cogs/error_handler.py | 2 ++ bot/cogs/filtering.py | 2 ++ bot/cogs/help_channels.py | 21 +++++++++++++-- bot/cogs/stats.py | 65 +++++++++++++++++++++++++++++++++++++++++++++ bot/cogs/tags.py | 3 +++ bot/cogs/token_remover.py | 2 ++ bot/cogs/webhook_remover.py | 2 ++ bot/constants.py | 1 + config-default.yml | 2 ++ 15 files changed, 145 insertions(+), 34 deletions(-) create mode 100644 bot/cogs/stats.py diff --git a/Pipfile b/Pipfile index 04cc98427..e7fb61957 100644 --- a/Pipfile +++ b/Pipfile @@ -19,7 +19,8 @@ requests = "~=2.22" more_itertools = "~=8.2" sentry-sdk = "~=0.14" coloredlogs = "~=14.0" -colorama = {version = "~=0.4.3", sys_platform = "== 'win32'"} +colorama = {version = "~=0.4.3",sys_platform = "== 'win32'"} +statsd = "~=3.3" [dev-packages] coverage = "~=5.0" diff --git a/Pipfile.lock b/Pipfile.lock index ad9a3173a..19e03bda4 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2d3ba484e8467a115126b2ba39fa5f36f103ea455477813dd658797875c79cc9" + "sha256": "10636aef5a07f17bd00608df2cc5214fcbfe3de4745cdeea7a076b871754620a" }, "pipfile-spec": 6, "requires": { @@ -87,18 +87,18 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a", - "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887", - "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae" + "sha256:594ca51a10d2b3443cbac41214e12dbb2a1cd57e1a7344659849e2e20ba6a8d8", + "sha256:a4bbe77fd30670455c5296242967a123ec28c37e9702a8a81bd2f20a4baf0368", + "sha256:d4e96ac9b0c3a6d3f0caae2e4124e6055c5dcafde8e2f831ff194c104f0775a0" ], - "version": "==4.8.2" + "version": "==4.9.0" }, "certifi": { "hashes": [ - "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", - "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" ], - "version": "==2019.11.28" + "version": "==2020.4.5.1" }, "cffi": { "hashes": [ @@ -167,10 +167,10 @@ }, "discord-py": { "hashes": [ - "sha256:7424be26b07b37ecad4404d9383d685995a0e0b3df3f9c645bdd3a4d977b83b4" + "sha256:406871b06d86c3dc49fba63238519f28628dac946fef8a0e22988ff58ec05580" ], "index": "pypi", - "version": "==1.3.2" + "version": "==1.3.3" }, "docutils": { "hashes": [ @@ -393,17 +393,10 @@ }, "pyparsing": { "hashes": [ - "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", - "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], - "version": "==2.4.6" - }, - "pyreadline": { - "hashes": [ - "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1" - ], - "markers": "sys_platform == 'win32'", - "version": "==2.1" + "version": "==2.4.7" }, "python-dateutil": { "hashes": [ @@ -524,6 +517,14 @@ ], "version": "==1.1.4" }, + "statsd": { + "hashes": [ + "sha256:c610fb80347fca0ef62666d241bce64184bd7cc1efe582f9690e045c25535eaa", + "sha256:e3e6db4c246f7c59003e51c9720a51a7f39a396541cb9b147ff4b14d15b5dd1f" + ], + "index": "pypi", + "version": "==3.3.0" + }, "urllib3": { "hashes": [ "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", @@ -717,11 +718,11 @@ }, "flake8-tidy-imports": { "hashes": [ - "sha256:5b6e75cec6d751e66534c522fbdce7dac1c2738b1216b0f6b10453995932e188", - "sha256:cf26fbb3ab31a398f265d53b6f711d80006450c19221e41b2b7b0e0b14ac39c5" + "sha256:62059ca07d8a4926b561d392cbab7f09ee042350214a25cf12823384a45d27dd", + "sha256:c30b40337a2e6802ba3bb611c26611154a27e94c53fc45639e3e282169574fd3" ], "index": "pypi", - "version": "==4.0.1" + "version": "==4.1.0" }, "flake8-todo": { "hashes": [ @@ -732,10 +733,10 @@ }, "identify": { "hashes": [ - "sha256:a7577a1f55cee1d21953a5cf11a3c839ab87f5ef909a4cba6cf52ed72b4c6059", - "sha256:ab246293e6585a1c6361a505b68d5b501a0409310932b7de2c2ead667b564d89" + "sha256:2bb8760d97d8df4408f4e805883dad26a2d076f04be92a10a3e43f09c6060742", + "sha256:faffea0fd8ec86bb146ac538ac350ed0c73908326426d387eded0bcc9d077522" ], - "version": "==1.4.13" + "version": "==1.4.14" }, "mccabe": { "hashes": [ @@ -835,10 +836,10 @@ }, "virtualenv": { "hashes": [ - "sha256:87831f1070534b636fea2241dd66f3afe37ac9041bcca6d0af3215cdcfbf7d82", - "sha256:f3128d882383c503003130389bf892856341c1da12c881ae24d6358c82561b55" + "sha256:00cfe8605fb97f5a59d52baab78e6070e72c12ca64f51151695407cc0eb8a431", + "sha256:c8364ec469084046c779c9a11ae6340094e8a0bf1d844330fc55c1cefe67c172" ], - "version": "==20.0.13" + "version": "==20.0.17" } } } diff --git a/bot/__main__.py b/bot/__main__.py index bf98f2cfd..2125e8590 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -57,6 +57,7 @@ bot.load_extension("bot.cogs.reminders") bot.load_extension("bot.cogs.site") bot.load_extension("bot.cogs.snekbox") bot.load_extension("bot.cogs.sync") +bot.load_extension("bot.cogs.stats") bot.load_extension("bot.cogs.tags") bot.load_extension("bot.cogs.token_remover") bot.load_extension("bot.cogs.utils") diff --git a/bot/bot.py b/bot/bot.py index 950ac6751..65081e438 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -6,10 +6,10 @@ from typing import Optional import aiohttp import discord +import statsd from discord.ext import commands -from bot import api -from bot import constants +from bot import DEBUG_MODE, api, constants log = logging.getLogger('bot') @@ -33,6 +33,16 @@ class Bot(commands.Bot): self._resolver = None self._guild_available = asyncio.Event() + statsd_url = constants.Bot.statsd_host + + if DEBUG_MODE: + # Since statsd is UDP, there are no errors for sending to a down port. + # For this reason, setting the statsd host to 127.0.0.1 for development + # will effectively disable stats. + statsd_url = "127.0.0.1" + + self.stats = statsd.StatsClient(statsd_url, 8125, prefix="bot") + def add_cog(self, cog: commands.Cog) -> None: """Adds a "cog" to the bot and logs the operation.""" super().add_cog(cog) diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py index baa6b9459..d63acbc4a 100644 --- a/bot/cogs/antispam.py +++ b/bot/cogs/antispam.py @@ -182,6 +182,7 @@ class AntiSpam(Cog): # which contains the reason for why the message violated the rule and # an iterable of all members that violated the rule. if result is not None: + self.bot.stats.incr(f"mod_alerts.{rule_name}") reason, members, relevant_messages = result full_reason = f"`{rule_name}` rule: {reason}" diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index cc0f79fe8..80dc6082f 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -104,6 +104,7 @@ class Defcon(Cog): log.exception(f"Unable to send rejection message to user: {member}") await member.kick(reason="DEFCON active, user is too new") + self.bot.stats.incr("defcon_leaves") message = ( f"{member} (`{member.id}`) was denied entry because their account is too new." diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 6a622d2ce..747ab4a6e 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -236,6 +236,8 @@ class ErrorHandler(Cog): f"```{e.__class__.__name__}: {e}```" ) + ctx.bot.stats.incr("command_error_count") + with push_scope() as scope: scope.user = { "id": ctx.author.id, diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 3f3dbb853..fa4420be1 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -207,6 +207,8 @@ class Filtering(Cog): log.debug(message) + self.bot.stats.incr(f"bot.filters.{filter_name}") + additional_embeds = None additional_embeds_msg = None diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 697a4d3b7..389a4ad2a 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -367,6 +367,18 @@ class HelpChannels(Scheduler, commands.Cog): log.info("Cog is ready!") self.ready.set() + self.report_stats() + + def report_stats(self) -> None: + """Report the channel count stats.""" + total_in_use = len(list(self.get_category_channels(self.in_use_category))) + total_available = len(list(self.get_category_channels(self.available_category))) + total_dormant = len(list(self.get_category_channels(self.dormant_category))) + + self.bot.stats.gauge("help.total.in_use", total_in_use) + self.bot.stats.gauge("help.total.available", total_available) + self.bot.stats.gauge("help.total.dormant", total_dormant) + @staticmethod def is_dormant_message(message: t.Optional[discord.Message]) -> bool: """Return True if the contents of the `message` match `DORMANT_MSG`.""" @@ -432,6 +444,7 @@ class HelpChannels(Scheduler, commands.Cog): f"synchronized permissions after moving `{channel}` into it." ) await self.ensure_permissions_synchronization(self.available_category) + self.report_stats() async def move_to_dormant(self, channel: discord.TextChannel) -> None: """Make the `channel` dormant.""" @@ -442,7 +455,6 @@ class HelpChannels(Scheduler, commands.Cog): category=self.dormant_category, sync_permissions=True, topic=DORMANT_TOPIC, - position=10000, ) log.trace(f"Position of #{channel} ({channel.id}) is actually {channel.position}.") @@ -453,6 +465,7 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Pushing #{channel} ({channel.id}) into the channel queue.") self.channel_queue.put_nowait(channel) + self.report_stats() async def move_to_in_use(self, channel: discord.TextChannel) -> None: """Make a channel in-use and schedule it to be made dormant.""" @@ -463,7 +476,6 @@ class HelpChannels(Scheduler, commands.Cog): category=self.in_use_category, sync_permissions=True, topic=IN_USE_TOPIC, - position=10000, ) timeout = constants.HelpChannels.idle_minutes * 60 @@ -471,6 +483,7 @@ class HelpChannels(Scheduler, commands.Cog): log.trace(f"Scheduling #{channel} ({channel.id}) to become dormant in {timeout} sec.") data = TaskData(timeout, self.move_idle_channel(channel)) self.schedule_task(channel.id, data) + self.report_stats() async def notify(self) -> None: """ @@ -511,6 +524,8 @@ class HelpChannels(Scheduler, commands.Cog): f"using the `{constants.Bot.prefix}dormant` command within the channels." ) + self.bot.stats.incr("help.out_of_channel_alerts") + self.last_notification = message.created_at except Exception: # Handle it here cause this feature isn't critical for the functionality of the system. @@ -543,6 +558,8 @@ class HelpChannels(Scheduler, commands.Cog): await self.move_to_in_use(channel) await self.revoke_send_permissions(message.author) + self.bot.stats.incr("help.claimed") + log.trace(f"Releasing on_message lock for {message.id}.") # Move a dormant channel to the Available category to fill in the gap. diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py new file mode 100644 index 000000000..b75d29b7e --- /dev/null +++ b/bot/cogs/stats.py @@ -0,0 +1,65 @@ +from discord import Member, Message, Status +from discord.ext.commands import Bot, Cog, Context + + +class Stats(Cog): + """A cog which provides a way to hook onto Discord events and forward to stats.""" + + def __init__(self, bot: Bot): + self.bot = bot + + @Cog.listener() + async def on_message(self, message: Message) -> None: + """Report message events in the server to statsd.""" + if message.guild is None: + return + + reformatted_name = message.channel.name.replace('-', '_') + + if reformatted_name.startswith("ot"): + # Off-topic channels change names, we don't want this for stats. + # This will change 'ot1-lemon-in-the-dishwasher' to just 'ot1' + reformatted_name = reformatted_name[:3] + + stat_name = f"channels.{reformatted_name}" + self.bot.stats.incr(stat_name) + + # Increment the total message count + self.bot.stats.incr("messages") + + @Cog.listener() + async def on_command_completion(self, ctx: Context) -> None: + """Report completed commands to statsd.""" + command_name = ctx.command.qualified_name.replace(" ", "_") + + self.bot.stats.incr(f"commands.{command_name}") + + @Cog.listener() + async def on_member_join(self, member: Member) -> None: + """Update member count stat on member join.""" + self.bot.stats.gauge(f"guild.total_members", len(member.guild.members)) + + @Cog.listener() + async def on_member_leave(self, member: Member) -> None: + """Update member count stat on member leave.""" + self.bot.stats.gauge(f"guild.total_members", len(member.guild.members)) + + @Cog.listener() + async def on_member_update(self, _before: Member, after: Member) -> None: + """Update presence estimates on member update.""" + members = after.guild.members + + online = len([m for m in members if m.status == Status.online]) + idle = len([m for m in members if m.status == Status.idle]) + dnd = len([m for m in members if m.status == Status.do_not_disturb]) + offline = len([m for m in members if m.status == Status.offline]) + + self.bot.stats.gauge("guild.status.online", online) + self.bot.stats.gauge("guild.status.idle", idle) + self.bot.stats.gauge("guild.status.do_not_disturb", dnd) + self.bot.stats.gauge("guild.status.offline", offline) + + +def setup(bot: Bot) -> None: + """Load the stats cog.""" + bot.add_cog(Stats(bot)) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index a6e5952ff..b81859db1 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -207,6 +207,9 @@ class Tags(Cog): "time": time.time(), "channel": ctx.channel.id } + + self.bot.stats.incr(f"tags.usages.{tag_name.replace('-', '_')}") + await wait_for_deletion( await ctx.send(embed=Embed.from_dict(tag['embed'])), [ctx.author.id], diff --git a/bot/cogs/token_remover.py b/bot/cogs/token_remover.py index 421ad23e2..6721f0e02 100644 --- a/bot/cogs/token_remover.py +++ b/bot/cogs/token_remover.py @@ -93,6 +93,8 @@ class TokenRemover(Cog): channel_id=Channels.mod_alerts, ) + self.bot.stats.incr("tokens.removed_tokens") + @classmethod def find_token_in_message(cls, msg: Message) -> t.Optional[str]: """Return a seemingly valid token found in `msg` or `None` if no token is found.""" diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 49692113d..1b5c3f821 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -54,6 +54,8 @@ class WebhookRemover(Cog): channel_id=Channels.mod_alerts ) + self.bot.stats.incr("tokens.removed_webhooks") + @Cog.listener() async def on_message(self, msg: Message) -> None: """Check if a Discord webhook URL is in `message`.""" diff --git a/bot/constants.py b/bot/constants.py index 60e3c4897..33c1d530d 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -199,6 +199,7 @@ class Bot(metaclass=YAMLGetter): prefix: str token: str sentry_dsn: str + statsd_host: str class Filter(metaclass=YAMLGetter): section = "filter" diff --git a/config-default.yml b/config-default.yml index 896003973..567caacbf 100644 --- a/config-default.yml +++ b/config-default.yml @@ -3,6 +3,8 @@ bot: token: !ENV "BOT_TOKEN" sentry_dsn: !ENV "BOT_SENTRY_DSN" + statsd_host: "graphite" + cooldowns: # Per channel, per tag. tags: 60 -- cgit v1.2.3 From 100a903d6604ac019adff0d4e197a092be2f273f Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 11 Apr 2020 10:28:47 -0700 Subject: HelpChannels: create a helper method for checking a chann --- bot/cogs/help_channels.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 797019f69..d91f3f91f 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -521,7 +521,8 @@ class HelpChannels(Scheduler, commands.Cog): return # Ignore messages sent by bots. channel = message.channel - if channel.category and channel.category.id != constants.Categories.help_available: + category = getattr(channel, "category", None) + if category and category.id != constants.Categories.help_available: return # Ignore messages outside the Available category. log.trace("Waiting for the cog to be ready before processing messages.") @@ -531,7 +532,8 @@ class HelpChannels(Scheduler, commands.Cog): async with self.on_message_lock: log.trace(f"on_message lock acquired for {message.id}.") - if channel.category and channel.category.id != constants.Categories.help_available: + category = getattr(channel, "category", None) + if category and category.id != constants.Categories.help_available: log.debug( f"Message {message.id} will not make #{channel} ({channel.id}) in-use " f"because another message in the channel already triggered that." -- cgit v1.2.3 From 8249ea49144123a81f33163e266691e465c6fd77 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 11 Apr 2020 10:37:43 -0700 Subject: HelpChannels: create helper method for checking channel's category --- bot/cogs/help_channels.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index d91f3f91f..56caa60af 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -376,6 +376,12 @@ class HelpChannels(Scheduler, commands.Cog): embed = message.embeds[0] return embed.description.strip() == DORMANT_MSG.strip() + @staticmethod + def is_in_category(channel: discord.TextChannel, category_id: int) -> bool: + """Return True if `channel` is within a category with `category_id`.""" + actual_category = getattr(channel, "category", None) + return actual_category and actual_category.id == category_id + async def move_idle_channel(self, channel: discord.TextChannel, has_task: bool = True) -> None: """ Make the `channel` dormant if idle or schedule the move if still active. @@ -521,8 +527,7 @@ class HelpChannels(Scheduler, commands.Cog): return # Ignore messages sent by bots. channel = message.channel - category = getattr(channel, "category", None) - if category and category.id != constants.Categories.help_available: + if not self.is_in_category(channel, constants.Categories.help_available): return # Ignore messages outside the Available category. log.trace("Waiting for the cog to be ready before processing messages.") @@ -532,8 +537,7 @@ class HelpChannels(Scheduler, commands.Cog): async with self.on_message_lock: log.trace(f"on_message lock acquired for {message.id}.") - category = getattr(channel, "category", None) - if category and category.id != constants.Categories.help_available: + if not self.is_in_category(channel, constants.Categories.help_available): log.debug( f"Message {message.id} will not make #{channel} ({channel.id}) in-use " f"because another message in the channel already triggered that." -- cgit v1.2.3 From bc17ec3f60b061ce93f627c1f69182f655d4fc37 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 19:18:31 +0100 Subject: Address review comments from Mark --- bot.svg | 3795 +++++++++++++++++++++++++++++++++++++++++++++ bot/__main__.py | 4 +- bot/cogs/defcon.py | 2 +- bot/cogs/error_handler.py | 2 +- bot/cogs/filtering.py | 2 +- bot/cogs/stats.py | 46 +- 6 files changed, 3837 insertions(+), 14 deletions(-) create mode 100644 bot.svg diff --git a/bot.svg b/bot.svg new file mode 100644 index 000000000..97a3914d4 --- /dev/null +++ b/bot.svg @@ -0,0 +1,3795 @@ + + + + + + +G + + + +bot_cogs_clean + +bot.cogs.clean + + + +requests + +requests + + + +discord_Webhook + +discord. +Webhook + + + +requests->discord_Webhook + + + + + +bot_cogs_doc + +bot.cogs.doc + + + +requests->bot_cogs_doc + + + + + +bot_bot + +bot.bot + + + +bot_bot->bot_cogs_clean + + + + + +bot_cogs_moderation_management + +bot. +cogs. +moderation. +management + + + +bot_bot->bot_cogs_moderation_management + + + + + +bot_cogs_webhook_remover + +bot. +cogs. +webhook_remover + + + +bot_bot->bot_cogs_webhook_remover + + + + + + + +bot_cogs_verification + +bot. +cogs. +verification + + + +bot_bot->bot_cogs_verification + + + + + +bot_cogs_wolfram + +bot. +cogs. +wolfram + + + +bot_bot->bot_cogs_wolfram + + + + + + +bot_cogs_site + +bot.cogs.site + + + +bot_bot->bot_cogs_site + + + + + +bot_cogs_watchchannels_watchchannel + +bot. +cogs. +watchchannels. +watchchannel + + + +bot_bot->bot_cogs_watchchannels_watchchannel + + + + + + +bot_cogs_extensions + +bot. +cogs. +extensions + + + +bot_bot->bot_cogs_extensions + + + + + +bot_cogs_antimalware + +bot. +cogs. +antimalware + + + +bot_bot->bot_cogs_antimalware + + + + + +bot___main__ + +bot.__main__ + + + +bot_bot->bot___main__ + + + + + +bot_cogs_moderation + +bot. +cogs. +moderation + + + +bot_bot->bot_cogs_moderation + + + + + +bot_cogs_duck_pond + +bot. +cogs. +duck_pond + + + +bot_bot->bot_cogs_duck_pond + + + + + + +bot_cogs_antispam + +bot. +cogs. +antispam + + + +bot_bot->bot_cogs_antispam + + + + + +bot_interpreter + +bot. +interpreter + + + +bot_bot->bot_interpreter + + + + + +bot_cogs_moderation_superstarify + +bot. +cogs. +moderation. +superstarify + + + +bot_bot->bot_cogs_moderation_superstarify + + + + +bot_cogs_defcon + +bot. +cogs. +defcon + + + +bot_bot->bot_cogs_defcon + + + + + + +bot_cogs_moderation_ModLog + +bot. +cogs. +moderation. +ModLog + + + +bot_bot->bot_cogs_moderation_ModLog + + + + + +bot_cogs_watchchannels_talentpool + +bot. +cogs. +watchchannels. +talentpool + + + +bot_bot->bot_cogs_watchchannels_talentpool + + + + +bot_cogs_information + +bot. +cogs. +information + + + +bot_bot->bot_cogs_information + + + + + +bot_cogs_off_topic_names + +bot. +cogs. +off_topic_names + + + +bot_bot->bot_cogs_off_topic_names + + + + + + +bot_cogs_alias + +bot.cogs.alias + + + +bot_bot->bot_cogs_alias + + + + + + +bot_cogs_moderation_silence + +bot. +cogs. +moderation. +silence + + + +bot_bot->bot_cogs_moderation_silence + + + + + +bot_cogs_sync_syncers + +bot. +cogs. +sync. +syncers + + + +bot_bot->bot_cogs_sync_syncers + + + + + + +bot_cogs_moderation_scheduler + +bot. +cogs. +moderation. +scheduler + + + +bot_bot->bot_cogs_moderation_scheduler + + + + + + +bot_cogs_eval + +bot.cogs.eval + + + +bot_bot->bot_cogs_eval + + + + + +bot_cogs_help_channels + +bot. +cogs. +help_channels + + + +bot_bot->bot_cogs_help_channels + + + + + +bot_cogs_tags + +bot.cogs.tags + + + +bot_bot->bot_cogs_tags + + + + + + +bot_cogs_reddit + +bot. +cogs. +reddit + + + +bot_bot->bot_cogs_reddit + + + + + + +bot_cogs_utils + +bot.cogs.utils + + + +bot_bot->bot_cogs_utils + + + + + +bot_cogs_sync + +bot.cogs.sync + + + +bot_bot->bot_cogs_sync + + + + + +bot_cogs_help + +bot.cogs.help + + + +bot_bot->bot_cogs_help + + + + + +bot_cogs_bot + +bot.cogs.bot + + + +bot_bot->bot_cogs_bot + + + + + + + + +bot_cogs_watchchannels_bigbrother + +bot. +cogs. +watchchannels. +bigbrother + + + +bot_bot->bot_cogs_watchchannels_bigbrother + + + + +bot_cogs_logging + +bot. +cogs. +logging + + + +bot_bot->bot_cogs_logging + + + + + + +bot_cogs_config_verifier + +bot. +cogs. +config_verifier + + + +bot_bot->bot_cogs_config_verifier + + + + + + +bot_cogs_moderation_modlog + +bot. +cogs. +moderation. +modlog + + + +bot_bot->bot_cogs_moderation_modlog + + + + + + +bot_cogs_sync_cog + +bot. +cogs. +sync. +cog + + + +bot_bot->bot_cogs_sync_cog + + + + + + +bot_cogs_snekbox + +bot. +cogs. +snekbox + + + +bot_bot->bot_cogs_snekbox + + + + + +bot_cogs_moderation_infractions + +bot. +cogs. +moderation. +infractions + + + +bot_bot->bot_cogs_moderation_infractions + + + + + +bot_cogs_watchchannels + +bot. +cogs. +watchchannels + + + +bot_bot->bot_cogs_watchchannels + + + + + +bot_bot->bot_cogs_doc + + + + + +bot_cogs_security + +bot. +cogs. +security + + + +bot_bot->bot_cogs_security + + + + + +bot_cogs_reminders + +bot. +cogs. +reminders + + + +bot_bot->bot_cogs_reminders + + + + + +bot_cogs_token_remover + +bot. +cogs. +token_remover + + + +bot_bot->bot_cogs_token_remover + + + + + + +bot_cogs_filtering + +bot. +cogs. +filtering + + + +bot_bot->bot_cogs_filtering + + + + + +bot_cogs_jams + +bot.cogs.jams + + + +bot_bot->bot_cogs_jams + + + + + +bot_cogs_error_handler + +bot. +cogs. +error_handler + + + +bot_bot->bot_cogs_error_handler + + + + + +bot_rules_attachments + +bot. +rules. +attachments + + + +bot_rules + +bot.rules + + + +bot_rules_attachments->bot_rules + + + + + +bot_cogs_stats + +bot.cogs.stats + + + +bot_api + +bot.api + + + +bot_api->bot_bot + + + + + +bot_api->bot_cogs_watchchannels_watchchannel + + + + +bot_cogs_moderation_utils + +bot. +cogs. +moderation. +utils + + + +bot_api->bot_cogs_moderation_utils + + + + + +bot_api->bot_cogs_watchchannels_talentpool + + + + +bot_api->bot_cogs_off_topic_names + + + + + +bot_api->bot_cogs_sync_syncers + + + + + +bot_api->bot_cogs_moderation_scheduler + + + + +bot_api->bot_cogs_sync_cog + + + + + +bot_api->bot_cogs_error_handler + + + + + + + +bot_cogs_moderation_management->bot_cogs_moderation + + + + + +urllib3_exceptions + +urllib3. +exceptions + + + +urllib3_exceptions->requests + + + + + +urllib3 + +urllib3 + + + +urllib3_exceptions->urllib3 + + + + + +urllib3_exceptions->bot_cogs_doc + + + + + +dateutil_parser + +dateutil. +parser + + + +bot_converters + +bot.converters + + + +dateutil_parser->bot_converters + + + + + +dateutil_parser->bot_cogs_watchchannels_watchchannel + + + + + + +bot_utils_time + +bot.utils.time + + + +dateutil_parser->bot_utils_time + + + + + +dateutil_parser->bot_cogs_moderation_scheduler + + + + + + +dateutil_parser->bot_cogs_reminders + + + + +dateutil_relativedelta + +dateutil. +relativedelta + + + +dateutil_relativedelta->bot_cogs_wolfram + + + + + + +dateutil_relativedelta->bot_converters + + + + + + +dateutil_relativedelta->bot_cogs_moderation_ModLog + + + + + + +dateutil_relativedelta->bot_utils_time + + + + + +dateutil_relativedelta->bot_cogs_utils + + + + + +dateutil_relativedelta->bot_cogs_moderation_modlog + + + + + +dateutil_relativedelta->bot_cogs_reminders + + + + +dateutil_relativedelta->bot_cogs_filtering + + + + + + +bot_rules_chars + +bot. +rules. +chars + + + +bot_rules_chars->bot_rules + + + + + +discord_Guild + +discord.Guild + + + +discord_Guild->bot_cogs_sync_syncers + + + + + +bot_converters->bot_cogs_moderation_management + + + + +bot_converters->bot_cogs_antispam + + + + + +bot_converters->bot_cogs_moderation_superstarify + + + + +bot_converters->bot_cogs_watchchannels_talentpool + + + + + + +bot_converters->bot_cogs_alias + + + + + +bot_converters->bot_cogs_moderation_silence + + + + +bot_converters->bot_cogs_tags + + + + + +bot_converters->bot_cogs_reddit + + + + + +bot_converters->bot_cogs_watchchannels_bigbrother + + + + +bot_converters->bot_cogs_moderation_infractions + + + + + +bot_converters->bot_cogs_doc + + + + + +bot_converters->bot_cogs_reminders + + + + +bot_converters->bot_cogs_error_handler + + + + + +bs4 + +bs4 + + + +bs4->bot_cogs_doc + + + + + +discord_Client + +discord.Client + + + +bot_utils_messages + +bot. +utils. +messages + + + +discord_Client->bot_utils_messages + + + + +bot_rules_burst_shared + +bot. +rules. +burst_shared + + + +bot_rules_burst_shared->bot_rules + + + + + +bot_cogs_watchchannels_watchchannel->bot_cogs_watchchannels_talentpool + + + + + +bot_cogs_watchchannels_watchchannel->bot_cogs_watchchannels_bigbrother + + + + + +aiohttp + +aiohttp + + + +aiohttp->bot_bot + + + + + +aiohttp->bot_api + + + + + + +aiohttp->bot_converters + + + + + +aiohttp->discord_Client + + + + + + +aiohttp->discord_Webhook + + + + + +aiohttp->bot_cogs_reddit + + + + + +bot_cogs_extensions->bot_cogs_alias + + + + + +discord_User + +discord.User + + + +discord_User->bot_cogs_clean + + + + + + +discord_User->bot_cogs_duck_pond + + + + + + + +discord_User->bot_cogs_sync_syncers + + + + + +discord_User->bot_cogs_help + + + + + +discord_User->bot_cogs_sync_cog + + + + + +discord_User->bot_cogs_snekbox + + + + + +bot_rules->bot_cogs_antispam + + + + + +bot_cogs_moderation->bot_cogs_clean + + + + + +bot_cogs_moderation->bot_cogs_webhook_remover + + + + + +bot_cogs_moderation->bot_cogs_verification + + + + + +bot_cogs_moderation->bot_cogs_watchchannels_watchchannel + + + + + +bot_cogs_moderation->bot_cogs_antispam + + + + + +bot_cogs_moderation->bot_cogs_defcon + + + + + +bot_cogs_moderation->bot_cogs_watchchannels_bigbrother + + + + +bot_cogs_moderation->bot_cogs_token_remover + + + + + +bot_cogs_moderation->bot_cogs_filtering + + + + + +discord + +discord + + + +discord->bot_cogs_clean + + + + + +discord->bot_bot + + + + + +discord->bot_rules_attachments + + + + + +discord->bot_cogs_stats + + + + + +discord->bot_cogs_moderation_management + + + + + +discord->bot_cogs_webhook_remover + + + + +discord->bot_cogs_verification + + + + + + +discord->bot_cogs_wolfram + + + + + + + +discord->bot_rules_chars + + + + + +discord->bot_converters + + + + + +discord->bot_cogs_site + + + + + + +discord->bot_rules_burst_shared + + + + + +discord->bot_cogs_watchchannels_watchchannel + + + + + +discord->bot_cogs_extensions + + + + + +discord->bot_cogs_antimalware + + + + + +discord->bot___main__ + + + + + + +discord->bot_cogs_duck_pond + + + + + +discord->bot_cogs_antispam + + + + +discord->bot_interpreter + + + + + +discord->bot_cogs_moderation_superstarify + + + + + +discord->bot_cogs_defcon + + + + + + +bot_pagination + +bot.pagination + + + +discord->bot_pagination + + + + + +discord->bot_cogs_moderation_ModLog + + + + + + +discord->bot_cogs_moderation_utils + + + + + + +bot_rules_newlines + +bot. +rules. +newlines + + + +discord->bot_rules_newlines + + + + + + +discord->bot_cogs_watchchannels_talentpool + + + + + +discord->bot_cogs_information + + + + + +discord->bot_cogs_off_topic_names + + + + + +discord->bot_cogs_alias + + + + + +discord->bot_cogs_moderation_silence + + + + + + +discord->bot_cogs_sync_syncers + + + + + + +discord->bot_cogs_moderation_scheduler + + + + + + +discord->bot_cogs_eval + + + + + + +bot_rules_burst + +bot. +rules. +burst + + + +discord->bot_rules_burst + + + + + +discord->bot_cogs_help_channels + + + + + + +discord->bot_cogs_tags + + + + + +discord->bot_cogs_reddit + + + + + + +discord->bot_cogs_utils + + + + + +bot_rules_discord_emojis + +bot. +rules. +discord_emojis + + + +discord->bot_rules_discord_emojis + + + + + +bot_rules_duplicates + +bot. +rules. +duplicates + + + +discord->bot_rules_duplicates + + + + + +discord->bot_cogs_help + + + + + +discord->bot_cogs_bot + + + + + + + +discord->bot_cogs_logging + + + + + + +bot_rules_mentions + +bot. +rules. +mentions + + + +discord->bot_rules_mentions + + + + + +bot_decorators + +bot.decorators + + + +discord->bot_decorators + + + + + +discord->bot_cogs_moderation_modlog + + + + + +discord->bot_cogs_sync_cog + + + + + +discord->bot_cogs_snekbox + + + + + +discord->bot_cogs_moderation_infractions + + + + + + +bot_rules_links + +bot. +rules. +links + + + +discord->bot_rules_links + + + + + + +discord->bot_cogs_doc + + + + + +discord->bot_utils_messages + + + + + +bot_patches_message_edited_at + +bot. +patches. +message_edited_at + + + +discord->bot_patches_message_edited_at + + + + + + + +discord->bot_cogs_reminders + + + + + +bot_rules_role_mentions + +bot. +rules. +role_mentions + + + +discord->bot_rules_role_mentions + + + + + +discord->bot_cogs_token_remover + + + + + +discord->bot_cogs_filtering + + + + + +discord->bot_cogs_jams + + + + + +bot_constants + +bot.constants + + + +bot_constants->bot_cogs_clean + + + + + +bot_constants->bot_bot + + + + + +bot_constants->bot_api + + + + + +bot_constants->bot_cogs_moderation_management + + + + + +bot_constants->bot_cogs_webhook_remover + + + + +bot_constants->bot_cogs_verification + + + + + +bot_constants->bot_cogs_wolfram + + + + +bot_constants->bot_cogs_site + + + + + + +bot_constants->bot_cogs_watchchannels_watchchannel + + + + + +bot_constants->bot_cogs_extensions + + + + + +bot_constants->bot_cogs_antimalware + + + + + +bot_constants->bot___main__ + + + + + +bot_constants->bot_cogs_duck_pond + + + + + + +bot_constants->bot_cogs_antispam + + + + + +bot_constants->bot_cogs_moderation_superstarify + + + + +bot_constants->bot_cogs_defcon + + + + + +bot_constants->bot_pagination + + + + + + +bot_constants->bot_cogs_moderation_ModLog + + + + + + +bot_constants->bot_cogs_moderation_utils + + + + + +bot_constants->bot_cogs_watchchannels_talentpool + + + + + + +bot_constants->bot_cogs_information + + + + + + +bot_constants->bot_cogs_off_topic_names + + + + + +bot_constants->bot_cogs_moderation_silence + + + + + + +bot_constants->bot_cogs_sync_syncers + + + + + +bot_constants->bot_cogs_moderation_scheduler + + + + + + +bot_constants->bot_cogs_eval + + + + + +bot_constants->bot_cogs_help_channels + + + + + +bot_constants->bot_cogs_tags + + + + + + +bot_constants->bot_cogs_reddit + + + + +bot_constants->bot_cogs_utils + + + + + +bot_constants->bot_cogs_help + + + + + + + +bot_constants->bot_cogs_bot + + + + + +bot_constants->bot_cogs_watchchannels_bigbrother + + + + + + +bot_constants->bot_cogs_logging + + + + + + +bot_constants->bot_cogs_config_verifier + + + + + +bot_constants->bot_decorators + + + + +bot_constants->bot_cogs_moderation_modlog + + + + + + +bot_constants->bot_cogs_sync_cog + + + + + + +bot_constants->bot_cogs_snekbox + + + + + + +bot_constants->bot_cogs_moderation_infractions + + + + + + +bot_constants->bot_cogs_doc + + + + + +bot_constants->bot_utils_messages + + + + + + +bot_constants->bot_cogs_reminders + + + + +bot_constants->bot_cogs_token_remover + + + + + +bot_constants->bot_cogs_filtering + + + + + + + + +bot_constants->bot_cogs_jams + + + + + +bot_constants->bot_cogs_error_handler + + + + +discord_File + +discord.File + + + +discord_File->bot_utils_messages + + + + +discord_Reaction + +discord. +Reaction + + + +discord_Reaction->bot_cogs_sync_syncers + + + + + +discord_Reaction->bot_cogs_help + + + + + +discord_Reaction->bot_cogs_snekbox + + + + + +discord_Reaction->bot_utils_messages + + + + + +bot_interpreter->bot_cogs_eval + + + + + + +bot_cogs_moderation_superstarify->bot_cogs_moderation + + + + + + +more_itertools + +more_itertools + + + +more_itertools->bot_cogs_jams + + + + + +statsd + +statsd + + + +statsd->bot_bot + + + + + +bot_pagination->bot_cogs_moderation_management + + + + + + + +bot_pagination->bot_cogs_wolfram + + + + + +bot_pagination->bot_cogs_site + + + + + +bot_pagination->bot_cogs_watchchannels_watchchannel + + + + + +bot_pagination->bot_cogs_extensions + + + + + +bot_pagination->bot_cogs_watchchannels_talentpool + + + + + +bot_pagination->bot_cogs_information + + + + + +bot_pagination->bot_cogs_off_topic_names + + + + + + +bot_pagination->bot_cogs_alias + + + + + +bot_pagination->bot_cogs_tags + + + + + +bot_pagination->bot_cogs_reddit + + + + + + +bot_pagination->bot_cogs_help + + + + + +bot_pagination->bot_cogs_doc + + + + + +bot_pagination->bot_cogs_reminders + + + + + +bot_utils_checks + +bot. +utils. +checks + + + +bot_utils_checks->bot_cogs_moderation_management + + + + + +bot_utils_checks->bot_cogs_verification + + + + + +bot_utils_checks->bot_cogs_extensions + + + + + + +bot_utils_checks->bot_cogs_moderation_superstarify + + + + + +bot_utils_checks->bot_cogs_information + + + + + +bot_utils_checks->bot_cogs_moderation_silence + + + + + + +bot_utils_checks->bot_decorators + + + + + +bot_utils_checks->bot_cogs_moderation_infractions + + + + + + +bot_utils_checks->bot_cogs_reminders + + + + +bot_cogs_moderation_ModLog->bot_cogs_clean + + + + + + +bot_cogs_moderation_ModLog->bot_cogs_verification + + + + + +bot_cogs_moderation_ModLog->bot_cogs_watchchannels_watchchannel + + + + + +bot_cogs_moderation_ModLog->bot_cogs_antispam + + + + + +bot_cogs_moderation_ModLog->bot_cogs_defcon + + + + + + +bot_cogs_moderation_ModLog->bot_cogs_token_remover + + + + + + + +bot_cogs_moderation_ModLog->bot_cogs_filtering + + + + + + + +discord_Object + +discord.Object + + + +discord_Object->bot_cogs_verification + + + + + +discord_Object->bot_cogs_antispam + + + + + + +bot_cogs_moderation_utils->bot_cogs_moderation_management + + + + + +bot_cogs_moderation_utils->bot_cogs_moderation_superstarify + + + + + + +bot_cogs_moderation_utils->bot_cogs_moderation_scheduler + + + + + +bot_cogs_moderation_utils->bot_cogs_watchchannels_bigbrother + + + + + + + +bot_cogs_moderation_utils->bot_cogs_moderation_infractions + + + + + +discord_Webhook->bot_utils_messages + + + + + + + +bot_rules_newlines->bot_rules + + + + + +bot_cogs_watchchannels_talentpool->bot_cogs_watchchannels + + + + + +discord_utils + +discord.utils + + + +discord_utils->discord_Guild + + + + + +discord_utils->discord_Client + + + + +discord_utils->discord_User + + + + + +discord_utils->discord + + + + + +discord_utils->bot_cogs_moderation_ModLog + + + + + + +discord_utils->discord_Object + + + + + +discord_utils->discord_Webhook + + + + + +discord_utils->bot_cogs_information + + + + + + +discord_Message + +discord. +Message + + + +discord_utils->discord_Message + + + + + +discord_abc + +discord.abc + + + +discord_utils->discord_abc + + + + + +discord_message + +discord. +message + + + +discord_utils->discord_message + + + + + +discord_Role + +discord.Role + + + +discord_utils->discord_Role + + + + + +discord_Member + +discord.Member + + + +discord_utils->discord_Member + + + + + + +discord_utils->bot_cogs_moderation_modlog + + + + + +discord_utils->bot_patches_message_edited_at + + + + + +discord_utils->bot_cogs_token_remover + + + + + +discord_utils->bot_cogs_filtering + + + + + + +discord_utils->bot_cogs_jams + + + + + +bot_cogs_moderation_silence->bot_cogs_moderation + + + + + +bot_utils_time->bot_cogs_moderation_management + + + + + + +bot_utils_time->bot_cogs_wolfram + + + + + +bot_utils_time->bot_cogs_watchchannels_watchchannel + + + + + +bot_utils_time->bot_cogs_moderation_superstarify + + + + + +bot_utils_time->bot_cogs_moderation_ModLog + + + + + +bot_utils_time->bot_cogs_watchchannels_talentpool + + + + +bot_utils_time->bot_cogs_information + + + + + + +bot_utils_time->bot_cogs_moderation_scheduler + + + + + +bot_utils_time->bot_cogs_utils + + + + + +bot_utils_time->bot_cogs_moderation_modlog + + + + + + + +bot_utils_time->bot_cogs_reminders + + + + + +discord_Message->bot_cogs_clean + + + + + +discord_Message->bot_rules_attachments + + + + + +discord_Message->bot_cogs_stats + + + + + +discord_Message->bot_cogs_webhook_remover + + + + + + + +discord_Message->bot_cogs_verification + + + + + +discord_Message->bot_rules_chars + + + + + + +discord_Message->bot_rules_burst_shared + + + + + +discord_Message->bot_cogs_watchchannels_watchchannel + + + + + + +discord_Message->bot_cogs_antimalware + + + + + +discord_Message->bot_cogs_duck_pond + + + + + +discord_Message->bot_cogs_antispam + + + + + +discord_Message->bot_rules_newlines + + + + + + +discord_Message->bot_cogs_information + + + + + +discord_Message->bot_cogs_sync_syncers + + + + + +discord_Message->bot_rules_burst + + + + + + + +discord_Message->bot_cogs_utils + + + + + +discord_Message->bot_rules_discord_emojis + + + + + + +discord_Message->bot_rules_duplicates + + + + + + +discord_Message->bot_cogs_help + + + + + + + + +discord_Message->bot_cogs_bot + + + + + + +discord_Message->bot_rules_mentions + + + + + + +discord_Message->bot_cogs_snekbox + + + + + + + +discord_Message->bot_rules_links + + + + + + +discord_Message->bot_utils_messages + + + + + +discord_Message->bot_rules_role_mentions + + + + + +discord_Message->bot_cogs_token_remover + + + + + + +discord_Message->bot_cogs_filtering + + + + + + + +bot_cogs_sync_syncers->bot_cogs_sync_cog + + + + + +dateutil + +dateutil + + + +dateutil->bot_cogs_wolfram + + + + + +dateutil->bot_converters + + + + + + + +dateutil->bot_cogs_watchchannels_watchchannel + + + + +dateutil->bot_cogs_moderation_ModLog + + + + + +dateutil->bot_utils_time + + + + + +dateutil->bot_cogs_moderation_scheduler + + + + + +dateutil->bot_cogs_utils + + + + + +dateutil->bot_cogs_moderation_modlog + + + + + +dateutil->bot_cogs_reminders + + + + +dateutil->bot_cogs_filtering + + + + + +bot_cogs_moderation_scheduler->bot_cogs_moderation_superstarify + + + + + +bot_cogs_moderation_scheduler->bot_cogs_moderation_infractions + + + + + +bot_rules_burst->bot_rules + + + + + +discord_abc->discord_User + + + + + +discord_abc->discord + + + + + +discord_abc->bot_pagination + + + + + +discord_abc->bot_cogs_moderation_ModLog + + + + + +discord_abc->discord_Member + + + + + +discord_abc->bot_cogs_moderation_modlog + + + + + +discord_abc->bot_utils_messages + + + + +discord_message->discord + + + + + +discord_message->discord_Webhook + + + + + +discord_message->bot_patches_message_edited_at + + + + + + +bot_rules_discord_emojis->bot_rules + + + + + +yaml + +yaml + + + +yaml->bot_constants + + + + + +bot_rules_duplicates->bot_rules + + + + + +urllib3->requests + + + + + +urllib3->bot_cogs_doc + + + + + + +discord_Role->bot_cogs_information + + + + + +discord_Role->bot_cogs_utils + + + + + +discord_Role->bot_cogs_sync_cog + + + + + +discord_Colour + +discord.Colour + + + +discord_Colour->bot_cogs_clean + + + + +discord_Colour->bot_cogs_webhook_remover + + + + + +discord_Colour->bot_cogs_verification + + + + + +discord_Colour->bot_cogs_site + + + + + + +discord_Colour->bot_cogs_extensions + + + + + +discord_Colour->bot_cogs_antispam + + + + + +discord_Colour->bot_cogs_moderation_superstarify + + + + + + +discord_Colour->bot_cogs_defcon + + + + + + +discord_Colour->bot_cogs_moderation_ModLog + + + + + + +discord_Colour->bot_cogs_information + + + + + +discord_Colour->bot_cogs_off_topic_names + + + + + +discord_Colour->bot_cogs_alias + + + + + +discord_Colour->bot_cogs_tags + + + + + +discord_Colour->bot_cogs_reddit + + + + + +discord_Colour->bot_cogs_utils + + + + + +discord_Colour->bot_cogs_help + + + + + +discord_Colour->bot_decorators + + + + + +discord_Colour->bot_cogs_moderation_modlog + + + + + + +discord_Colour->bot_cogs_token_remover + + + + + + +discord_Colour->bot_cogs_filtering + + + + + +bot_cogs_watchchannels_bigbrother->bot_cogs_watchchannels + + + + + +discord_Member->bot_rules_attachments + + + + + + + +discord_Member->bot_cogs_stats + + + + + +discord_Member->bot_rules_chars + + + + + +discord_Member->bot_rules_burst_shared + + + + + +discord_Member->bot_cogs_duck_pond + + + + + + +discord_Member->bot_cogs_antispam + + + + +discord_Member->bot_cogs_moderation_superstarify + + + + + +discord_Member->bot_cogs_defcon + + + + + +discord_Member->bot_rules_newlines + + + + + +discord_Member->bot_cogs_watchchannels_talentpool + + + + + + +discord_Member->bot_cogs_information + + + + + +discord_Member->bot_cogs_sync_syncers + + + + + +discord_Member->bot_rules_burst + + + + + +discord_Member->bot_rules_discord_emojis + + + + + +discord_Member->bot_rules_duplicates + + + + +discord_Member->bot_rules_mentions + + + + +discord_Member->bot_decorators + + + + + +discord_Member->bot_cogs_sync_cog + + + + + +discord_Member->bot_cogs_moderation_infractions + + + + + +discord_Member->bot_rules_links + + + + + +discord_Member->bot_utils_messages + + + + + + +discord_Member->bot_rules_role_mentions + + + + + +discord_Member->bot_cogs_filtering + + + + + + + +discord_Member->bot_cogs_jams + + + + + +bot_rules_mentions->bot_rules + + + + + +bot_decorators->bot_cogs_clean + + + + + + + + +bot_decorators->bot_cogs_verification + + + + +bot_decorators->bot_cogs_defcon + + + + + +bot_decorators->bot_cogs_watchchannels_talentpool + + + + + +bot_decorators->bot_cogs_information + + + + + +bot_decorators->bot_cogs_off_topic_names + + + + + +bot_decorators->bot_cogs_eval + + + + + +bot_decorators->bot_cogs_help_channels + + + + + + +bot_decorators->bot_cogs_reddit + + + + + +bot_decorators->bot_cogs_utils + + + + + +bot_decorators->bot_cogs_help + + + + + +bot_decorators->bot_cogs_bot + + + + +bot_decorators->bot_cogs_watchchannels_bigbrother + + + + + +bot_decorators->bot_cogs_snekbox + + + + + + +bot_decorators->bot_cogs_moderation_infractions + + + + + +bot_decorators->bot_cogs_doc + + + + + +bot_decorators->bot_cogs_jams + + + + + +bot_decorators->bot_cogs_error_handler + + + + + +bot_cogs_moderation_modlog->bot_cogs_moderation_management + + + + + + +bot_cogs_moderation_modlog->bot_cogs_webhook_remover + + + + + +bot_cogs_moderation_modlog->bot_cogs_moderation + + + + +bot_cogs_moderation_modlog->bot_cogs_moderation_scheduler + + + + + +bot_patches + +bot.patches + + + +bot_patches->bot___main__ + + + + + +bot_cogs_sync_cog->bot_cogs_sync + + + + + +bot_utils + +bot.utils + + + +bot_utils->bot_cogs_moderation_management + + + + +bot_utils->bot_cogs_verification + + + + + + +bot_utils->bot_cogs_wolfram + + + + + +bot_utils->bot_cogs_watchchannels_watchchannel + + + + + + +bot_utils->bot_cogs_extensions + + + + + +bot_utils->bot_cogs_duck_pond + + + + +bot_utils->bot_cogs_antispam + + + + + + +bot_utils->bot_cogs_moderation_superstarify + + + + + +bot_utils->bot_cogs_moderation_ModLog + + + + + + +bot_utils->bot_cogs_watchchannels_talentpool + + + + + + +bot_utils->bot_cogs_information + + + + +bot_utils->bot_cogs_moderation_silence + + + + + + + +bot_utils->bot_cogs_moderation_scheduler + + + + + +bot_utils->bot_cogs_help_channels + + + + + +bot_utils->bot_cogs_tags + + + + + +bot_utils->bot_cogs_utils + + + + + +bot_utils->bot_cogs_bot + + + + + + +bot_utils->bot_decorators + + + + + +bot_utils->bot_cogs_moderation_modlog + + + + + +bot_utils->bot_cogs_snekbox + + + + + +bot_utils->bot_cogs_moderation_infractions + + + + + + + +bot_utils->bot_cogs_reminders + + + + + + +bot_cogs_moderation_infractions->bot_cogs_moderation_management + + + + + +bot_cogs_moderation_infractions->bot_cogs_moderation + + + + + +bot_rules_links->bot_rules + + + + + +discord_errors + +discord.errors + + + +discord_errors->discord_Guild + + + + + +discord_errors->discord_Client + + + + + + +discord_errors->bot_cogs_watchchannels_watchchannel + + + + +discord_errors->discord_User + + + + + + +discord_errors->discord + + + + + +discord_errors->bot_cogs_duck_pond + + + + + +discord_errors->discord_Webhook + + + + + +discord_errors->discord_utils + + + + + +discord_errors->discord_Message + + + + + + +discord_errors->discord_abc + + + + + +discord_errors->discord_message + + + + + +discord_errors->discord_Role + + + + + + +discord_errors->bot_decorators + + + + + + +discord_errors->bot_cogs_doc + + + + + + +discord_errors->bot_utils_messages + + + + + +discord_errors->bot_cogs_filtering + + + + + +bot_utils_scheduling + +bot. +utils. +scheduling + + + +bot_utils_scheduling->bot_cogs_moderation_scheduler + + + + +bot_utils_scheduling->bot_cogs_help_channels + + + + + +bot_utils_scheduling->bot_cogs_reminders + + + + + +bs4_element + +bs4.element + + + +bs4_element->bs4 + + + + + +bs4_element->bot_cogs_doc + + + + + +dateutil_tz + +dateutil.tz + + + +dateutil_tz->bot_converters + + + + +bot_utils_messages->bot_cogs_watchchannels_watchchannel + + + + + + + +bot_utils_messages->bot_cogs_duck_pond + + + + + +bot_utils_messages->bot_cogs_antispam + + + + + + +bot_utils_messages->bot_cogs_tags + + + + + +bot_utils_messages->bot_cogs_bot + + + + +bot_utils_messages->bot_cogs_snekbox + + + + +bot_patches_message_edited_at->bot_patches + + + + + +bot_rules_role_mentions->bot_rules + + + + + +bot_cogs_token_remover->bot_cogs_bot + + + + + diff --git a/bot/__main__.py b/bot/__main__.py index 2125e8590..3aa36bfc0 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -46,8 +46,8 @@ bot.load_extension("bot.cogs.verification") # Feature cogs bot.load_extension("bot.cogs.alias") bot.load_extension("bot.cogs.defcon") -bot.load_extension("bot.cogs.eval") bot.load_extension("bot.cogs.duck_pond") +bot.load_extension("bot.cogs.eval") bot.load_extension("bot.cogs.information") bot.load_extension("bot.cogs.jams") bot.load_extension("bot.cogs.moderation") @@ -56,8 +56,8 @@ bot.load_extension("bot.cogs.reddit") bot.load_extension("bot.cogs.reminders") bot.load_extension("bot.cogs.site") bot.load_extension("bot.cogs.snekbox") -bot.load_extension("bot.cogs.sync") bot.load_extension("bot.cogs.stats") +bot.load_extension("bot.cogs.sync") bot.load_extension("bot.cogs.tags") bot.load_extension("bot.cogs.token_remover") bot.load_extension("bot.cogs.utils") diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index 80dc6082f..06b2f25c6 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -104,7 +104,7 @@ class Defcon(Cog): log.exception(f"Unable to send rejection message to user: {member}") await member.kick(reason="DEFCON active, user is too new") - self.bot.stats.incr("defcon_leaves") + self.bot.stats.incr("defcon.leaves") message = ( f"{member} (`{member.id}`) was denied entry because their account is too new." diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 747ab4a6e..722376cc6 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -236,7 +236,7 @@ class ErrorHandler(Cog): f"```{e.__class__.__name__}: {e}```" ) - ctx.bot.stats.incr("command_error_count") + ctx.bot.stats.incr("errors.commands") with push_scope() as scope: scope.user = { diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index fa4420be1..6a703f5a1 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -207,7 +207,7 @@ class Filtering(Cog): log.debug(message) - self.bot.stats.incr(f"bot.filters.{filter_name}") + self.bot.stats.incr(f"filters.{filter_name}") additional_embeds = None additional_embeds_msg = None diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index b75d29b7e..e963dc312 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -1,6 +1,16 @@ from discord import Member, Message, Status from discord.ext.commands import Bot, Cog, Context +from bot.constants import Guild + + +CHANNEL_NAME_OVERRIDES = { + Guild.channels.off_topic_0: "off_topic_0", + Guild.channels.off_topic_1: "off_topic_1", + Guild.channels.off_topic_2: "off_topic_2", + Guild.channels.staff_lounge: "staff_lounge" +} + class Stats(Cog): """A cog which provides a way to hook onto Discord events and forward to stats.""" @@ -14,12 +24,13 @@ class Stats(Cog): if message.guild is None: return + if message.guild.id != Guild.id: + return + reformatted_name = message.channel.name.replace('-', '_') - if reformatted_name.startswith("ot"): - # Off-topic channels change names, we don't want this for stats. - # This will change 'ot1-lemon-in-the-dishwasher' to just 'ot1' - reformatted_name = reformatted_name[:3] + if CHANNEL_NAME_OVERRIDES.get(message.channel.id): + reformatted_name = CHANNEL_NAME_OVERRIDES.get(message.channel.id) stat_name = f"channels.{reformatted_name}" self.bot.stats.incr(stat_name) @@ -37,22 +48,39 @@ class Stats(Cog): @Cog.listener() async def on_member_join(self, member: Member) -> None: """Update member count stat on member join.""" + if member.guild.id != Guild.id: + return + self.bot.stats.gauge(f"guild.total_members", len(member.guild.members)) @Cog.listener() async def on_member_leave(self, member: Member) -> None: """Update member count stat on member leave.""" + if member.guild.id != Guild.id: + return + self.bot.stats.gauge(f"guild.total_members", len(member.guild.members)) @Cog.listener() async def on_member_update(self, _before: Member, after: Member) -> None: """Update presence estimates on member update.""" - members = after.guild.members + if after.guild.id != Guild.id: + return - online = len([m for m in members if m.status == Status.online]) - idle = len([m for m in members if m.status == Status.idle]) - dnd = len([m for m in members if m.status == Status.do_not_disturb]) - offline = len([m for m in members if m.status == Status.offline]) + online = 0 + idle = 0 + dnd = 0 + offline = 0 + + for member in after.guild.members: + if member.status == Status.online: + online += 1 + elif member.status == Status.dnd: + dnd += 1 + elif member.status == Status.idle: + idle += 1 + else: + offline += 1 self.bot.stats.gauge("guild.status.online", online) self.bot.stats.gauge("guild.status.idle", idle) -- cgit v1.2.3 From 77c07fc2bdad4d7caa9dc654f62a9b1c4dfb63b2 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 19:24:54 +0100 Subject: Address review comments from Mark --- bot.svg | 3795 ----------------------------------------------------- bot/cogs/stats.py | 10 +- 2 files changed, 5 insertions(+), 3800 deletions(-) delete mode 100644 bot.svg diff --git a/bot.svg b/bot.svg deleted file mode 100644 index 97a3914d4..000000000 --- a/bot.svg +++ /dev/null @@ -1,3795 +0,0 @@ - - - - - - -G - - - -bot_cogs_clean - -bot.cogs.clean - - - -requests - -requests - - - -discord_Webhook - -discord. -Webhook - - - -requests->discord_Webhook - - - - - -bot_cogs_doc - -bot.cogs.doc - - - -requests->bot_cogs_doc - - - - - -bot_bot - -bot.bot - - - -bot_bot->bot_cogs_clean - - - - - -bot_cogs_moderation_management - -bot. -cogs. -moderation. -management - - - -bot_bot->bot_cogs_moderation_management - - - - - -bot_cogs_webhook_remover - -bot. -cogs. -webhook_remover - - - -bot_bot->bot_cogs_webhook_remover - - - - - - - -bot_cogs_verification - -bot. -cogs. -verification - - - -bot_bot->bot_cogs_verification - - - - - -bot_cogs_wolfram - -bot. -cogs. -wolfram - - - -bot_bot->bot_cogs_wolfram - - - - - - -bot_cogs_site - -bot.cogs.site - - - -bot_bot->bot_cogs_site - - - - - -bot_cogs_watchchannels_watchchannel - -bot. -cogs. -watchchannels. -watchchannel - - - -bot_bot->bot_cogs_watchchannels_watchchannel - - - - - - -bot_cogs_extensions - -bot. -cogs. -extensions - - - -bot_bot->bot_cogs_extensions - - - - - -bot_cogs_antimalware - -bot. -cogs. -antimalware - - - -bot_bot->bot_cogs_antimalware - - - - - -bot___main__ - -bot.__main__ - - - -bot_bot->bot___main__ - - - - - -bot_cogs_moderation - -bot. -cogs. -moderation - - - -bot_bot->bot_cogs_moderation - - - - - -bot_cogs_duck_pond - -bot. -cogs. -duck_pond - - - -bot_bot->bot_cogs_duck_pond - - - - - - -bot_cogs_antispam - -bot. -cogs. -antispam - - - -bot_bot->bot_cogs_antispam - - - - - -bot_interpreter - -bot. -interpreter - - - -bot_bot->bot_interpreter - - - - - -bot_cogs_moderation_superstarify - -bot. -cogs. -moderation. -superstarify - - - -bot_bot->bot_cogs_moderation_superstarify - - - - -bot_cogs_defcon - -bot. -cogs. -defcon - - - -bot_bot->bot_cogs_defcon - - - - - - -bot_cogs_moderation_ModLog - -bot. -cogs. -moderation. -ModLog - - - -bot_bot->bot_cogs_moderation_ModLog - - - - - -bot_cogs_watchchannels_talentpool - -bot. -cogs. -watchchannels. -talentpool - - - -bot_bot->bot_cogs_watchchannels_talentpool - - - - -bot_cogs_information - -bot. -cogs. -information - - - -bot_bot->bot_cogs_information - - - - - -bot_cogs_off_topic_names - -bot. -cogs. -off_topic_names - - - -bot_bot->bot_cogs_off_topic_names - - - - - - -bot_cogs_alias - -bot.cogs.alias - - - -bot_bot->bot_cogs_alias - - - - - - -bot_cogs_moderation_silence - -bot. -cogs. -moderation. -silence - - - -bot_bot->bot_cogs_moderation_silence - - - - - -bot_cogs_sync_syncers - -bot. -cogs. -sync. -syncers - - - -bot_bot->bot_cogs_sync_syncers - - - - - - -bot_cogs_moderation_scheduler - -bot. -cogs. -moderation. -scheduler - - - -bot_bot->bot_cogs_moderation_scheduler - - - - - - -bot_cogs_eval - -bot.cogs.eval - - - -bot_bot->bot_cogs_eval - - - - - -bot_cogs_help_channels - -bot. -cogs. -help_channels - - - -bot_bot->bot_cogs_help_channels - - - - - -bot_cogs_tags - -bot.cogs.tags - - - -bot_bot->bot_cogs_tags - - - - - - -bot_cogs_reddit - -bot. -cogs. -reddit - - - -bot_bot->bot_cogs_reddit - - - - - - -bot_cogs_utils - -bot.cogs.utils - - - -bot_bot->bot_cogs_utils - - - - - -bot_cogs_sync - -bot.cogs.sync - - - -bot_bot->bot_cogs_sync - - - - - -bot_cogs_help - -bot.cogs.help - - - -bot_bot->bot_cogs_help - - - - - -bot_cogs_bot - -bot.cogs.bot - - - -bot_bot->bot_cogs_bot - - - - - - - - -bot_cogs_watchchannels_bigbrother - -bot. -cogs. -watchchannels. -bigbrother - - - -bot_bot->bot_cogs_watchchannels_bigbrother - - - - -bot_cogs_logging - -bot. -cogs. -logging - - - -bot_bot->bot_cogs_logging - - - - - - -bot_cogs_config_verifier - -bot. -cogs. -config_verifier - - - -bot_bot->bot_cogs_config_verifier - - - - - - -bot_cogs_moderation_modlog - -bot. -cogs. -moderation. -modlog - - - -bot_bot->bot_cogs_moderation_modlog - - - - - - -bot_cogs_sync_cog - -bot. -cogs. -sync. -cog - - - -bot_bot->bot_cogs_sync_cog - - - - - - -bot_cogs_snekbox - -bot. -cogs. -snekbox - - - -bot_bot->bot_cogs_snekbox - - - - - -bot_cogs_moderation_infractions - -bot. -cogs. -moderation. -infractions - - - -bot_bot->bot_cogs_moderation_infractions - - - - - -bot_cogs_watchchannels - -bot. -cogs. -watchchannels - - - -bot_bot->bot_cogs_watchchannels - - - - - -bot_bot->bot_cogs_doc - - - - - -bot_cogs_security - -bot. -cogs. -security - - - -bot_bot->bot_cogs_security - - - - - -bot_cogs_reminders - -bot. -cogs. -reminders - - - -bot_bot->bot_cogs_reminders - - - - - -bot_cogs_token_remover - -bot. -cogs. -token_remover - - - -bot_bot->bot_cogs_token_remover - - - - - - -bot_cogs_filtering - -bot. -cogs. -filtering - - - -bot_bot->bot_cogs_filtering - - - - - -bot_cogs_jams - -bot.cogs.jams - - - -bot_bot->bot_cogs_jams - - - - - -bot_cogs_error_handler - -bot. -cogs. -error_handler - - - -bot_bot->bot_cogs_error_handler - - - - - -bot_rules_attachments - -bot. -rules. -attachments - - - -bot_rules - -bot.rules - - - -bot_rules_attachments->bot_rules - - - - - -bot_cogs_stats - -bot.cogs.stats - - - -bot_api - -bot.api - - - -bot_api->bot_bot - - - - - -bot_api->bot_cogs_watchchannels_watchchannel - - - - -bot_cogs_moderation_utils - -bot. -cogs. -moderation. -utils - - - -bot_api->bot_cogs_moderation_utils - - - - - -bot_api->bot_cogs_watchchannels_talentpool - - - - -bot_api->bot_cogs_off_topic_names - - - - - -bot_api->bot_cogs_sync_syncers - - - - - -bot_api->bot_cogs_moderation_scheduler - - - - -bot_api->bot_cogs_sync_cog - - - - - -bot_api->bot_cogs_error_handler - - - - - - - -bot_cogs_moderation_management->bot_cogs_moderation - - - - - -urllib3_exceptions - -urllib3. -exceptions - - - -urllib3_exceptions->requests - - - - - -urllib3 - -urllib3 - - - -urllib3_exceptions->urllib3 - - - - - -urllib3_exceptions->bot_cogs_doc - - - - - -dateutil_parser - -dateutil. -parser - - - -bot_converters - -bot.converters - - - -dateutil_parser->bot_converters - - - - - -dateutil_parser->bot_cogs_watchchannels_watchchannel - - - - - - -bot_utils_time - -bot.utils.time - - - -dateutil_parser->bot_utils_time - - - - - -dateutil_parser->bot_cogs_moderation_scheduler - - - - - - -dateutil_parser->bot_cogs_reminders - - - - -dateutil_relativedelta - -dateutil. -relativedelta - - - -dateutil_relativedelta->bot_cogs_wolfram - - - - - - -dateutil_relativedelta->bot_converters - - - - - - -dateutil_relativedelta->bot_cogs_moderation_ModLog - - - - - - -dateutil_relativedelta->bot_utils_time - - - - - -dateutil_relativedelta->bot_cogs_utils - - - - - -dateutil_relativedelta->bot_cogs_moderation_modlog - - - - - -dateutil_relativedelta->bot_cogs_reminders - - - - -dateutil_relativedelta->bot_cogs_filtering - - - - - - -bot_rules_chars - -bot. -rules. -chars - - - -bot_rules_chars->bot_rules - - - - - -discord_Guild - -discord.Guild - - - -discord_Guild->bot_cogs_sync_syncers - - - - - -bot_converters->bot_cogs_moderation_management - - - - -bot_converters->bot_cogs_antispam - - - - - -bot_converters->bot_cogs_moderation_superstarify - - - - -bot_converters->bot_cogs_watchchannels_talentpool - - - - - - -bot_converters->bot_cogs_alias - - - - - -bot_converters->bot_cogs_moderation_silence - - - - -bot_converters->bot_cogs_tags - - - - - -bot_converters->bot_cogs_reddit - - - - - -bot_converters->bot_cogs_watchchannels_bigbrother - - - - -bot_converters->bot_cogs_moderation_infractions - - - - - -bot_converters->bot_cogs_doc - - - - - -bot_converters->bot_cogs_reminders - - - - -bot_converters->bot_cogs_error_handler - - - - - -bs4 - -bs4 - - - -bs4->bot_cogs_doc - - - - - -discord_Client - -discord.Client - - - -bot_utils_messages - -bot. -utils. -messages - - - -discord_Client->bot_utils_messages - - - - -bot_rules_burst_shared - -bot. -rules. -burst_shared - - - -bot_rules_burst_shared->bot_rules - - - - - -bot_cogs_watchchannels_watchchannel->bot_cogs_watchchannels_talentpool - - - - - -bot_cogs_watchchannels_watchchannel->bot_cogs_watchchannels_bigbrother - - - - - -aiohttp - -aiohttp - - - -aiohttp->bot_bot - - - - - -aiohttp->bot_api - - - - - - -aiohttp->bot_converters - - - - - -aiohttp->discord_Client - - - - - - -aiohttp->discord_Webhook - - - - - -aiohttp->bot_cogs_reddit - - - - - -bot_cogs_extensions->bot_cogs_alias - - - - - -discord_User - -discord.User - - - -discord_User->bot_cogs_clean - - - - - - -discord_User->bot_cogs_duck_pond - - - - - - - -discord_User->bot_cogs_sync_syncers - - - - - -discord_User->bot_cogs_help - - - - - -discord_User->bot_cogs_sync_cog - - - - - -discord_User->bot_cogs_snekbox - - - - - -bot_rules->bot_cogs_antispam - - - - - -bot_cogs_moderation->bot_cogs_clean - - - - - -bot_cogs_moderation->bot_cogs_webhook_remover - - - - - -bot_cogs_moderation->bot_cogs_verification - - - - - -bot_cogs_moderation->bot_cogs_watchchannels_watchchannel - - - - - -bot_cogs_moderation->bot_cogs_antispam - - - - - -bot_cogs_moderation->bot_cogs_defcon - - - - - -bot_cogs_moderation->bot_cogs_watchchannels_bigbrother - - - - -bot_cogs_moderation->bot_cogs_token_remover - - - - - -bot_cogs_moderation->bot_cogs_filtering - - - - - -discord - -discord - - - -discord->bot_cogs_clean - - - - - -discord->bot_bot - - - - - -discord->bot_rules_attachments - - - - - -discord->bot_cogs_stats - - - - - -discord->bot_cogs_moderation_management - - - - - -discord->bot_cogs_webhook_remover - - - - -discord->bot_cogs_verification - - - - - - -discord->bot_cogs_wolfram - - - - - - - -discord->bot_rules_chars - - - - - -discord->bot_converters - - - - - -discord->bot_cogs_site - - - - - - -discord->bot_rules_burst_shared - - - - - -discord->bot_cogs_watchchannels_watchchannel - - - - - -discord->bot_cogs_extensions - - - - - -discord->bot_cogs_antimalware - - - - - -discord->bot___main__ - - - - - - -discord->bot_cogs_duck_pond - - - - - -discord->bot_cogs_antispam - - - - -discord->bot_interpreter - - - - - -discord->bot_cogs_moderation_superstarify - - - - - -discord->bot_cogs_defcon - - - - - - -bot_pagination - -bot.pagination - - - -discord->bot_pagination - - - - - -discord->bot_cogs_moderation_ModLog - - - - - - -discord->bot_cogs_moderation_utils - - - - - - -bot_rules_newlines - -bot. -rules. -newlines - - - -discord->bot_rules_newlines - - - - - - -discord->bot_cogs_watchchannels_talentpool - - - - - -discord->bot_cogs_information - - - - - -discord->bot_cogs_off_topic_names - - - - - -discord->bot_cogs_alias - - - - - -discord->bot_cogs_moderation_silence - - - - - - -discord->bot_cogs_sync_syncers - - - - - - -discord->bot_cogs_moderation_scheduler - - - - - - -discord->bot_cogs_eval - - - - - - -bot_rules_burst - -bot. -rules. -burst - - - -discord->bot_rules_burst - - - - - -discord->bot_cogs_help_channels - - - - - - -discord->bot_cogs_tags - - - - - -discord->bot_cogs_reddit - - - - - - -discord->bot_cogs_utils - - - - - -bot_rules_discord_emojis - -bot. -rules. -discord_emojis - - - -discord->bot_rules_discord_emojis - - - - - -bot_rules_duplicates - -bot. -rules. -duplicates - - - -discord->bot_rules_duplicates - - - - - -discord->bot_cogs_help - - - - - -discord->bot_cogs_bot - - - - - - - -discord->bot_cogs_logging - - - - - - -bot_rules_mentions - -bot. -rules. -mentions - - - -discord->bot_rules_mentions - - - - - -bot_decorators - -bot.decorators - - - -discord->bot_decorators - - - - - -discord->bot_cogs_moderation_modlog - - - - - -discord->bot_cogs_sync_cog - - - - - -discord->bot_cogs_snekbox - - - - - -discord->bot_cogs_moderation_infractions - - - - - - -bot_rules_links - -bot. -rules. -links - - - -discord->bot_rules_links - - - - - - -discord->bot_cogs_doc - - - - - -discord->bot_utils_messages - - - - - -bot_patches_message_edited_at - -bot. -patches. -message_edited_at - - - -discord->bot_patches_message_edited_at - - - - - - - -discord->bot_cogs_reminders - - - - - -bot_rules_role_mentions - -bot. -rules. -role_mentions - - - -discord->bot_rules_role_mentions - - - - - -discord->bot_cogs_token_remover - - - - - -discord->bot_cogs_filtering - - - - - -discord->bot_cogs_jams - - - - - -bot_constants - -bot.constants - - - -bot_constants->bot_cogs_clean - - - - - -bot_constants->bot_bot - - - - - -bot_constants->bot_api - - - - - -bot_constants->bot_cogs_moderation_management - - - - - -bot_constants->bot_cogs_webhook_remover - - - - -bot_constants->bot_cogs_verification - - - - - -bot_constants->bot_cogs_wolfram - - - - -bot_constants->bot_cogs_site - - - - - - -bot_constants->bot_cogs_watchchannels_watchchannel - - - - - -bot_constants->bot_cogs_extensions - - - - - -bot_constants->bot_cogs_antimalware - - - - - -bot_constants->bot___main__ - - - - - -bot_constants->bot_cogs_duck_pond - - - - - - -bot_constants->bot_cogs_antispam - - - - - -bot_constants->bot_cogs_moderation_superstarify - - - - -bot_constants->bot_cogs_defcon - - - - - -bot_constants->bot_pagination - - - - - - -bot_constants->bot_cogs_moderation_ModLog - - - - - - -bot_constants->bot_cogs_moderation_utils - - - - - -bot_constants->bot_cogs_watchchannels_talentpool - - - - - - -bot_constants->bot_cogs_information - - - - - - -bot_constants->bot_cogs_off_topic_names - - - - - -bot_constants->bot_cogs_moderation_silence - - - - - - -bot_constants->bot_cogs_sync_syncers - - - - - -bot_constants->bot_cogs_moderation_scheduler - - - - - - -bot_constants->bot_cogs_eval - - - - - -bot_constants->bot_cogs_help_channels - - - - - -bot_constants->bot_cogs_tags - - - - - - -bot_constants->bot_cogs_reddit - - - - -bot_constants->bot_cogs_utils - - - - - -bot_constants->bot_cogs_help - - - - - - - -bot_constants->bot_cogs_bot - - - - - -bot_constants->bot_cogs_watchchannels_bigbrother - - - - - - -bot_constants->bot_cogs_logging - - - - - - -bot_constants->bot_cogs_config_verifier - - - - - -bot_constants->bot_decorators - - - - -bot_constants->bot_cogs_moderation_modlog - - - - - - -bot_constants->bot_cogs_sync_cog - - - - - - -bot_constants->bot_cogs_snekbox - - - - - - -bot_constants->bot_cogs_moderation_infractions - - - - - - -bot_constants->bot_cogs_doc - - - - - -bot_constants->bot_utils_messages - - - - - - -bot_constants->bot_cogs_reminders - - - - -bot_constants->bot_cogs_token_remover - - - - - -bot_constants->bot_cogs_filtering - - - - - - - - -bot_constants->bot_cogs_jams - - - - - -bot_constants->bot_cogs_error_handler - - - - -discord_File - -discord.File - - - -discord_File->bot_utils_messages - - - - -discord_Reaction - -discord. -Reaction - - - -discord_Reaction->bot_cogs_sync_syncers - - - - - -discord_Reaction->bot_cogs_help - - - - - -discord_Reaction->bot_cogs_snekbox - - - - - -discord_Reaction->bot_utils_messages - - - - - -bot_interpreter->bot_cogs_eval - - - - - - -bot_cogs_moderation_superstarify->bot_cogs_moderation - - - - - - -more_itertools - -more_itertools - - - -more_itertools->bot_cogs_jams - - - - - -statsd - -statsd - - - -statsd->bot_bot - - - - - -bot_pagination->bot_cogs_moderation_management - - - - - - - -bot_pagination->bot_cogs_wolfram - - - - - -bot_pagination->bot_cogs_site - - - - - -bot_pagination->bot_cogs_watchchannels_watchchannel - - - - - -bot_pagination->bot_cogs_extensions - - - - - -bot_pagination->bot_cogs_watchchannels_talentpool - - - - - -bot_pagination->bot_cogs_information - - - - - -bot_pagination->bot_cogs_off_topic_names - - - - - - -bot_pagination->bot_cogs_alias - - - - - -bot_pagination->bot_cogs_tags - - - - - -bot_pagination->bot_cogs_reddit - - - - - - -bot_pagination->bot_cogs_help - - - - - -bot_pagination->bot_cogs_doc - - - - - -bot_pagination->bot_cogs_reminders - - - - - -bot_utils_checks - -bot. -utils. -checks - - - -bot_utils_checks->bot_cogs_moderation_management - - - - - -bot_utils_checks->bot_cogs_verification - - - - - -bot_utils_checks->bot_cogs_extensions - - - - - - -bot_utils_checks->bot_cogs_moderation_superstarify - - - - - -bot_utils_checks->bot_cogs_information - - - - - -bot_utils_checks->bot_cogs_moderation_silence - - - - - - -bot_utils_checks->bot_decorators - - - - - -bot_utils_checks->bot_cogs_moderation_infractions - - - - - - -bot_utils_checks->bot_cogs_reminders - - - - -bot_cogs_moderation_ModLog->bot_cogs_clean - - - - - - -bot_cogs_moderation_ModLog->bot_cogs_verification - - - - - -bot_cogs_moderation_ModLog->bot_cogs_watchchannels_watchchannel - - - - - -bot_cogs_moderation_ModLog->bot_cogs_antispam - - - - - -bot_cogs_moderation_ModLog->bot_cogs_defcon - - - - - - -bot_cogs_moderation_ModLog->bot_cogs_token_remover - - - - - - - -bot_cogs_moderation_ModLog->bot_cogs_filtering - - - - - - - -discord_Object - -discord.Object - - - -discord_Object->bot_cogs_verification - - - - - -discord_Object->bot_cogs_antispam - - - - - - -bot_cogs_moderation_utils->bot_cogs_moderation_management - - - - - -bot_cogs_moderation_utils->bot_cogs_moderation_superstarify - - - - - - -bot_cogs_moderation_utils->bot_cogs_moderation_scheduler - - - - - -bot_cogs_moderation_utils->bot_cogs_watchchannels_bigbrother - - - - - - - -bot_cogs_moderation_utils->bot_cogs_moderation_infractions - - - - - -discord_Webhook->bot_utils_messages - - - - - - - -bot_rules_newlines->bot_rules - - - - - -bot_cogs_watchchannels_talentpool->bot_cogs_watchchannels - - - - - -discord_utils - -discord.utils - - - -discord_utils->discord_Guild - - - - - -discord_utils->discord_Client - - - - -discord_utils->discord_User - - - - - -discord_utils->discord - - - - - -discord_utils->bot_cogs_moderation_ModLog - - - - - - -discord_utils->discord_Object - - - - - -discord_utils->discord_Webhook - - - - - -discord_utils->bot_cogs_information - - - - - - -discord_Message - -discord. -Message - - - -discord_utils->discord_Message - - - - - -discord_abc - -discord.abc - - - -discord_utils->discord_abc - - - - - -discord_message - -discord. -message - - - -discord_utils->discord_message - - - - - -discord_Role - -discord.Role - - - -discord_utils->discord_Role - - - - - -discord_Member - -discord.Member - - - -discord_utils->discord_Member - - - - - - -discord_utils->bot_cogs_moderation_modlog - - - - - -discord_utils->bot_patches_message_edited_at - - - - - -discord_utils->bot_cogs_token_remover - - - - - -discord_utils->bot_cogs_filtering - - - - - - -discord_utils->bot_cogs_jams - - - - - -bot_cogs_moderation_silence->bot_cogs_moderation - - - - - -bot_utils_time->bot_cogs_moderation_management - - - - - - -bot_utils_time->bot_cogs_wolfram - - - - - -bot_utils_time->bot_cogs_watchchannels_watchchannel - - - - - -bot_utils_time->bot_cogs_moderation_superstarify - - - - - -bot_utils_time->bot_cogs_moderation_ModLog - - - - - -bot_utils_time->bot_cogs_watchchannels_talentpool - - - - -bot_utils_time->bot_cogs_information - - - - - - -bot_utils_time->bot_cogs_moderation_scheduler - - - - - -bot_utils_time->bot_cogs_utils - - - - - -bot_utils_time->bot_cogs_moderation_modlog - - - - - - - -bot_utils_time->bot_cogs_reminders - - - - - -discord_Message->bot_cogs_clean - - - - - -discord_Message->bot_rules_attachments - - - - - -discord_Message->bot_cogs_stats - - - - - -discord_Message->bot_cogs_webhook_remover - - - - - - - -discord_Message->bot_cogs_verification - - - - - -discord_Message->bot_rules_chars - - - - - - -discord_Message->bot_rules_burst_shared - - - - - -discord_Message->bot_cogs_watchchannels_watchchannel - - - - - - -discord_Message->bot_cogs_antimalware - - - - - -discord_Message->bot_cogs_duck_pond - - - - - -discord_Message->bot_cogs_antispam - - - - - -discord_Message->bot_rules_newlines - - - - - - -discord_Message->bot_cogs_information - - - - - -discord_Message->bot_cogs_sync_syncers - - - - - -discord_Message->bot_rules_burst - - - - - - - -discord_Message->bot_cogs_utils - - - - - -discord_Message->bot_rules_discord_emojis - - - - - - -discord_Message->bot_rules_duplicates - - - - - - -discord_Message->bot_cogs_help - - - - - - - - -discord_Message->bot_cogs_bot - - - - - - -discord_Message->bot_rules_mentions - - - - - - -discord_Message->bot_cogs_snekbox - - - - - - - -discord_Message->bot_rules_links - - - - - - -discord_Message->bot_utils_messages - - - - - -discord_Message->bot_rules_role_mentions - - - - - -discord_Message->bot_cogs_token_remover - - - - - - -discord_Message->bot_cogs_filtering - - - - - - - -bot_cogs_sync_syncers->bot_cogs_sync_cog - - - - - -dateutil - -dateutil - - - -dateutil->bot_cogs_wolfram - - - - - -dateutil->bot_converters - - - - - - - -dateutil->bot_cogs_watchchannels_watchchannel - - - - -dateutil->bot_cogs_moderation_ModLog - - - - - -dateutil->bot_utils_time - - - - - -dateutil->bot_cogs_moderation_scheduler - - - - - -dateutil->bot_cogs_utils - - - - - -dateutil->bot_cogs_moderation_modlog - - - - - -dateutil->bot_cogs_reminders - - - - -dateutil->bot_cogs_filtering - - - - - -bot_cogs_moderation_scheduler->bot_cogs_moderation_superstarify - - - - - -bot_cogs_moderation_scheduler->bot_cogs_moderation_infractions - - - - - -bot_rules_burst->bot_rules - - - - - -discord_abc->discord_User - - - - - -discord_abc->discord - - - - - -discord_abc->bot_pagination - - - - - -discord_abc->bot_cogs_moderation_ModLog - - - - - -discord_abc->discord_Member - - - - - -discord_abc->bot_cogs_moderation_modlog - - - - - -discord_abc->bot_utils_messages - - - - -discord_message->discord - - - - - -discord_message->discord_Webhook - - - - - -discord_message->bot_patches_message_edited_at - - - - - - -bot_rules_discord_emojis->bot_rules - - - - - -yaml - -yaml - - - -yaml->bot_constants - - - - - -bot_rules_duplicates->bot_rules - - - - - -urllib3->requests - - - - - -urllib3->bot_cogs_doc - - - - - - -discord_Role->bot_cogs_information - - - - - -discord_Role->bot_cogs_utils - - - - - -discord_Role->bot_cogs_sync_cog - - - - - -discord_Colour - -discord.Colour - - - -discord_Colour->bot_cogs_clean - - - - -discord_Colour->bot_cogs_webhook_remover - - - - - -discord_Colour->bot_cogs_verification - - - - - -discord_Colour->bot_cogs_site - - - - - - -discord_Colour->bot_cogs_extensions - - - - - -discord_Colour->bot_cogs_antispam - - - - - -discord_Colour->bot_cogs_moderation_superstarify - - - - - - -discord_Colour->bot_cogs_defcon - - - - - - -discord_Colour->bot_cogs_moderation_ModLog - - - - - - -discord_Colour->bot_cogs_information - - - - - -discord_Colour->bot_cogs_off_topic_names - - - - - -discord_Colour->bot_cogs_alias - - - - - -discord_Colour->bot_cogs_tags - - - - - -discord_Colour->bot_cogs_reddit - - - - - -discord_Colour->bot_cogs_utils - - - - - -discord_Colour->bot_cogs_help - - - - - -discord_Colour->bot_decorators - - - - - -discord_Colour->bot_cogs_moderation_modlog - - - - - - -discord_Colour->bot_cogs_token_remover - - - - - - -discord_Colour->bot_cogs_filtering - - - - - -bot_cogs_watchchannels_bigbrother->bot_cogs_watchchannels - - - - - -discord_Member->bot_rules_attachments - - - - - - - -discord_Member->bot_cogs_stats - - - - - -discord_Member->bot_rules_chars - - - - - -discord_Member->bot_rules_burst_shared - - - - - -discord_Member->bot_cogs_duck_pond - - - - - - -discord_Member->bot_cogs_antispam - - - - -discord_Member->bot_cogs_moderation_superstarify - - - - - -discord_Member->bot_cogs_defcon - - - - - -discord_Member->bot_rules_newlines - - - - - -discord_Member->bot_cogs_watchchannels_talentpool - - - - - - -discord_Member->bot_cogs_information - - - - - -discord_Member->bot_cogs_sync_syncers - - - - - -discord_Member->bot_rules_burst - - - - - -discord_Member->bot_rules_discord_emojis - - - - - -discord_Member->bot_rules_duplicates - - - - -discord_Member->bot_rules_mentions - - - - -discord_Member->bot_decorators - - - - - -discord_Member->bot_cogs_sync_cog - - - - - -discord_Member->bot_cogs_moderation_infractions - - - - - -discord_Member->bot_rules_links - - - - - -discord_Member->bot_utils_messages - - - - - - -discord_Member->bot_rules_role_mentions - - - - - -discord_Member->bot_cogs_filtering - - - - - - - -discord_Member->bot_cogs_jams - - - - - -bot_rules_mentions->bot_rules - - - - - -bot_decorators->bot_cogs_clean - - - - - - - - -bot_decorators->bot_cogs_verification - - - - -bot_decorators->bot_cogs_defcon - - - - - -bot_decorators->bot_cogs_watchchannels_talentpool - - - - - -bot_decorators->bot_cogs_information - - - - - -bot_decorators->bot_cogs_off_topic_names - - - - - -bot_decorators->bot_cogs_eval - - - - - -bot_decorators->bot_cogs_help_channels - - - - - - -bot_decorators->bot_cogs_reddit - - - - - -bot_decorators->bot_cogs_utils - - - - - -bot_decorators->bot_cogs_help - - - - - -bot_decorators->bot_cogs_bot - - - - -bot_decorators->bot_cogs_watchchannels_bigbrother - - - - - -bot_decorators->bot_cogs_snekbox - - - - - - -bot_decorators->bot_cogs_moderation_infractions - - - - - -bot_decorators->bot_cogs_doc - - - - - -bot_decorators->bot_cogs_jams - - - - - -bot_decorators->bot_cogs_error_handler - - - - - -bot_cogs_moderation_modlog->bot_cogs_moderation_management - - - - - - -bot_cogs_moderation_modlog->bot_cogs_webhook_remover - - - - - -bot_cogs_moderation_modlog->bot_cogs_moderation - - - - -bot_cogs_moderation_modlog->bot_cogs_moderation_scheduler - - - - - -bot_patches - -bot.patches - - - -bot_patches->bot___main__ - - - - - -bot_cogs_sync_cog->bot_cogs_sync - - - - - -bot_utils - -bot.utils - - - -bot_utils->bot_cogs_moderation_management - - - - -bot_utils->bot_cogs_verification - - - - - - -bot_utils->bot_cogs_wolfram - - - - - -bot_utils->bot_cogs_watchchannels_watchchannel - - - - - - -bot_utils->bot_cogs_extensions - - - - - -bot_utils->bot_cogs_duck_pond - - - - -bot_utils->bot_cogs_antispam - - - - - - -bot_utils->bot_cogs_moderation_superstarify - - - - - -bot_utils->bot_cogs_moderation_ModLog - - - - - - -bot_utils->bot_cogs_watchchannels_talentpool - - - - - - -bot_utils->bot_cogs_information - - - - -bot_utils->bot_cogs_moderation_silence - - - - - - - -bot_utils->bot_cogs_moderation_scheduler - - - - - -bot_utils->bot_cogs_help_channels - - - - - -bot_utils->bot_cogs_tags - - - - - -bot_utils->bot_cogs_utils - - - - - -bot_utils->bot_cogs_bot - - - - - - -bot_utils->bot_decorators - - - - - -bot_utils->bot_cogs_moderation_modlog - - - - - -bot_utils->bot_cogs_snekbox - - - - - -bot_utils->bot_cogs_moderation_infractions - - - - - - - -bot_utils->bot_cogs_reminders - - - - - - -bot_cogs_moderation_infractions->bot_cogs_moderation_management - - - - - -bot_cogs_moderation_infractions->bot_cogs_moderation - - - - - -bot_rules_links->bot_rules - - - - - -discord_errors - -discord.errors - - - -discord_errors->discord_Guild - - - - - -discord_errors->discord_Client - - - - - - -discord_errors->bot_cogs_watchchannels_watchchannel - - - - -discord_errors->discord_User - - - - - - -discord_errors->discord - - - - - -discord_errors->bot_cogs_duck_pond - - - - - -discord_errors->discord_Webhook - - - - - -discord_errors->discord_utils - - - - - -discord_errors->discord_Message - - - - - - -discord_errors->discord_abc - - - - - -discord_errors->discord_message - - - - - -discord_errors->discord_Role - - - - - - -discord_errors->bot_decorators - - - - - - -discord_errors->bot_cogs_doc - - - - - - -discord_errors->bot_utils_messages - - - - - -discord_errors->bot_cogs_filtering - - - - - -bot_utils_scheduling - -bot. -utils. -scheduling - - - -bot_utils_scheduling->bot_cogs_moderation_scheduler - - - - -bot_utils_scheduling->bot_cogs_help_channels - - - - - -bot_utils_scheduling->bot_cogs_reminders - - - - - -bs4_element - -bs4.element - - - -bs4_element->bs4 - - - - - -bs4_element->bot_cogs_doc - - - - - -dateutil_tz - -dateutil.tz - - - -dateutil_tz->bot_converters - - - - -bot_utils_messages->bot_cogs_watchchannels_watchchannel - - - - - - - -bot_utils_messages->bot_cogs_duck_pond - - - - - -bot_utils_messages->bot_cogs_antispam - - - - - - -bot_utils_messages->bot_cogs_tags - - - - - -bot_utils_messages->bot_cogs_bot - - - - -bot_utils_messages->bot_cogs_snekbox - - - - -bot_patches_message_edited_at->bot_patches - - - - - -bot_rules_role_mentions->bot_rules - - - - - -bot_cogs_token_remover->bot_cogs_bot - - - - - diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index e963dc312..8fb7d8639 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -1,14 +1,14 @@ from discord import Member, Message, Status from discord.ext.commands import Bot, Cog, Context -from bot.constants import Guild +from bot.constants import Channels, Guild CHANNEL_NAME_OVERRIDES = { - Guild.channels.off_topic_0: "off_topic_0", - Guild.channels.off_topic_1: "off_topic_1", - Guild.channels.off_topic_2: "off_topic_2", - Guild.channels.staff_lounge: "staff_lounge" + Channels.off_topic_0: "off_topic_0", + Channels.off_topic_1: "off_topic_1", + Channels.off_topic_2: "off_topic_2", + Channels.staff_lounge: "staff_lounge" } -- cgit v1.2.3 From bca47a1c2e59fb112b947876cea1836879ac7282 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 21:02:13 +0100 Subject: Implement an AsyncStatsClient to send statsd communications asynchronously --- bot/async_stats.py | 39 +++++++++++++++++++++++++++++++++++++++ bot/bot.py | 13 ++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 bot/async_stats.py diff --git a/bot/async_stats.py b/bot/async_stats.py new file mode 100644 index 000000000..58a80f528 --- /dev/null +++ b/bot/async_stats.py @@ -0,0 +1,39 @@ +import asyncio +import socket + +from statsd.client.base import StatsClientBase + + +class AsyncStatsClient(StatsClientBase): + """An async transport method for statsd communication.""" + + def __init__( + self, + loop: asyncio.AbstractEventLoop, + host: str = 'localhost', + port: int = 8125, + prefix: str = None + ): + """Create a new client.""" + family, _, _, _, addr = socket.getaddrinfo( + host, port, socket.AF_INET, socket.SOCK_DGRAM)[0] + self._addr = addr + self._prefix = prefix + self._loop = loop + self._transport = None + + async def create_socket(self) -> None: + """Use the loop.create_datagram_endpoint method to create a socket.""" + self._transport, _ = await self._loop.create_datagram_endpoint( + asyncio.DatagramProtocol, + family=socket.AF_INET, + remote_addr=self._addr + ) + + def _send(self, data: str) -> None: + """Start an async task to send data to statsd.""" + self._loop.create_task(self._async_send(data)) + + async def _async_send(self, data: str) -> None: + """Send data to the statsd server using the async transport.""" + self._transport.sendto(data.encode('ascii'), self._addr) diff --git a/bot/bot.py b/bot/bot.py index 65081e438..c5d490409 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -6,10 +6,10 @@ from typing import Optional import aiohttp import discord -import statsd from discord.ext import commands from bot import DEBUG_MODE, api, constants +from bot.async_stats import AsyncStatsClient log = logging.getLogger('bot') @@ -41,7 +41,7 @@ class Bot(commands.Bot): # will effectively disable stats. statsd_url = "127.0.0.1" - self.stats = statsd.StatsClient(statsd_url, 8125, prefix="bot") + self.stats = AsyncStatsClient(self.loop, statsd_url, 8125, prefix="bot") def add_cog(self, cog: commands.Cog) -> None: """Adds a "cog" to the bot and logs the operation.""" @@ -60,7 +60,7 @@ class Bot(commands.Bot): super().clear() async def close(self) -> None: - """Close the Discord connection and the aiohttp session, connector, and resolver.""" + """Close the Discord connection and the aiohttp session, connector, statsd client, and resolver.""" await super().close() await self.api_client.close() @@ -74,6 +74,9 @@ class Bot(commands.Bot): if self._resolver: await self._resolver.close() + if self.stats._transport: + await self.stats._transport.close() + async def login(self, *args, **kwargs) -> None: """Re-create the connector and set up sessions before logging into Discord.""" self._recreate() @@ -111,6 +114,10 @@ class Bot(commands.Bot): self.http_session = aiohttp.ClientSession(connector=self._connector) self.api_client.recreate(force=True, connector=self._connector) + async def on_ready(self) -> None: + """Construct an asynchronous transport for the statsd client.""" + await self.stats.create_socket() + async def on_guild_available(self, guild: discord.Guild) -> None: """ Set the internal guild available event when constants.Guild.id becomes available. -- cgit v1.2.3 From 7da559647db7dfd4386f1711e2c053efd9a6c897 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 21:37:49 +0100 Subject: Move create_socket to the login method of the bot --- bot/bot.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index c5d490409..ef4a325dc 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -80,6 +80,7 @@ class Bot(commands.Bot): async def login(self, *args, **kwargs) -> None: """Re-create the connector and set up sessions before logging into Discord.""" self._recreate() + await self.stats.create_socket() await super().login(*args, **kwargs) def _recreate(self) -> None: @@ -114,10 +115,6 @@ class Bot(commands.Bot): self.http_session = aiohttp.ClientSession(connector=self._connector) self.api_client.recreate(force=True, connector=self._connector) - async def on_ready(self) -> None: - """Construct an asynchronous transport for the statsd client.""" - await self.stats.create_socket() - async def on_guild_available(self, guild: discord.Guild) -> None: """ Set the internal guild available event when constants.Guild.id becomes available. -- cgit v1.2.3 From ee5a4df9537b46cdceb35243d887e84601d07795 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 22:11:43 +0100 Subject: Additional statistics --- bot/cogs/defcon.py | 2 ++ bot/cogs/error_handler.py | 14 +++++++++++++- bot/cogs/help_channels.py | 24 ++++++++++++++++++++---- bot/cogs/stats.py | 14 ++++++++++---- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index 06b2f25c6..9197dcca3 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -146,6 +146,8 @@ class Defcon(Cog): await ctx.send(self.build_defcon_msg(action, error)) await self.send_defcon_log(action, ctx.author, error) + self.bot.stats.gauge("defcon.days", days) + @defcon_group.command(name='enable', aliases=('on', 'e')) @with_role(Roles.admins, Roles.owners) async def enable_command(self, ctx: Context) -> None: diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 722376cc6..dae283c6a 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -171,19 +171,25 @@ class ErrorHandler(Cog): if isinstance(e, errors.MissingRequiredArgument): await ctx.send(f"Missing required argument `{e.param.name}`.") await ctx.invoke(*help_command) + self.bot.stats.incr("errors.missing_required_argument") elif isinstance(e, errors.TooManyArguments): await ctx.send(f"Too many arguments provided.") await ctx.invoke(*help_command) + self.bot.stats.incr("errors.too_many_arguments") elif isinstance(e, errors.BadArgument): await ctx.send(f"Bad argument: {e}\n") await ctx.invoke(*help_command) + self.bot.stats.incr("errors.bad_argument") elif isinstance(e, errors.BadUnionArgument): await ctx.send(f"Bad argument: {e}\n```{e.errors[-1]}```") + self.bot.stats.incr("errors.bad_union_argument") elif isinstance(e, errors.ArgumentParsingError): await ctx.send(f"Argument parsing error: {e}") + self.bot.stats.incr("errors.argument_parsing_error") else: await ctx.send("Something about your input seems off. Check the arguments:") await ctx.invoke(*help_command) + self.bot.stats.incr("errors.other_user_input_error") @staticmethod async def handle_check_failure(ctx: Context, e: errors.CheckFailure) -> None: @@ -205,10 +211,12 @@ class ErrorHandler(Cog): ) if isinstance(e, bot_missing_errors): + ctx.bot.stats.incr("errors.bot_permission_error") await ctx.send( f"Sorry, it looks like I don't have the permissions or roles I need to do that." ) elif isinstance(e, (InChannelCheckFailure, errors.NoPrivateMessage)): + ctx.bot.stats.incr("errors.wrong_channel_or_dm_error") await ctx.send(e) @staticmethod @@ -217,16 +225,20 @@ class ErrorHandler(Cog): if e.status == 404: await ctx.send("There does not seem to be anything matching your query.") log.debug(f"API responded with 404 for command {ctx.command}") + ctx.bot.stats.incr("errors.api_error_404") elif e.status == 400: content = await e.response.json() log.debug(f"API responded with 400 for command {ctx.command}: %r.", content) await ctx.send("According to the API, your request is malformed.") + ctx.bot.stats.incr("errors.api_error_400") elif 500 <= e.status < 600: await ctx.send("Sorry, there seems to be an internal issue with the API.") log.warning(f"API responded with {e.status} for command {ctx.command}") + ctx.bot.stats.incr("errors.api_internal_server_error") else: await ctx.send(f"Got an unexpected status code from the API (`{e.status}`).") log.warning(f"Unexpected API response for command {ctx.command}: {e.status}") + ctx.bot.stats.incr(f"errors.api_error_{e.status}") @staticmethod async def handle_unexpected_error(ctx: Context, e: errors.CommandError) -> None: @@ -236,7 +248,7 @@ class ErrorHandler(Cog): f"```{e.__class__.__name__}: {e}```" ) - ctx.bot.stats.incr("errors.commands") + ctx.bot.stats.incr("errors.unexpected") with push_scope() as scope: scope.user = { diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 389a4ad2a..01a77db2b 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -127,6 +127,9 @@ class HelpChannels(Scheduler, commands.Cog): self.on_message_lock = asyncio.Lock() self.init_task = self.bot.loop.create_task(self.init_cog()) + # Stats + self.claim_times = {} + def cog_unload(self) -> None: """Cancel the init task and scheduled tasks when the cog unloads.""" log.trace("Cog unload: cancelling the init_cog task") @@ -195,7 +198,7 @@ class HelpChannels(Scheduler, commands.Cog): if ctx.channel.category == self.in_use_category: self.cancel_task(ctx.channel.id) - await self.move_to_dormant(ctx.channel) + await self.move_to_dormant(ctx.channel, "command") else: log.debug(f"{ctx.author} invoked command 'dormant' outside an in-use help channel") @@ -406,7 +409,7 @@ class HelpChannels(Scheduler, commands.Cog): f"and will be made dormant." ) - await self.move_to_dormant(channel) + await self.move_to_dormant(channel, "auto") else: # Cancel the existing task, if any. if has_task: @@ -446,8 +449,12 @@ class HelpChannels(Scheduler, commands.Cog): await self.ensure_permissions_synchronization(self.available_category) self.report_stats() - async def move_to_dormant(self, channel: discord.TextChannel) -> None: - """Make the `channel` dormant.""" + async def move_to_dormant(self, channel: discord.TextChannel, caller: str) -> None: + """ + Make the `channel` dormant. + + A caller argument is provided for metrics. + """ log.info(f"Moving #{channel} ({channel.id}) to the Dormant category.") await channel.edit( @@ -457,6 +464,13 @@ class HelpChannels(Scheduler, commands.Cog): topic=DORMANT_TOPIC, ) + self.bot.stats.incr(f"help.dormant_calls.{caller}") + + if self.claim_times.get(channel.id): + claimed = self.claim_times[channel.id] + in_use_time = datetime.now() - claimed + self.bot.stats.timer("help.in_use_time", in_use_time) + log.trace(f"Position of #{channel} ({channel.id}) is actually {channel.position}.") log.trace(f"Sending dormant message for #{channel} ({channel.id}).") @@ -560,6 +574,8 @@ class HelpChannels(Scheduler, commands.Cog): self.bot.stats.incr("help.claimed") + self.claim_times[channel.id] = datetime.now() + log.trace(f"Releasing on_message lock for {message.id}.") # Move a dormant channel to the Available category to fill in the gap. diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index 8fb7d8639..772ae2c97 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -1,3 +1,5 @@ +import string + from discord import Member, Message, Status from discord.ext.commands import Bot, Cog, Context @@ -11,6 +13,8 @@ CHANNEL_NAME_OVERRIDES = { Channels.staff_lounge: "staff_lounge" } +ALLOWED_CHARS = string.ascii_letters + string.digits + class Stats(Cog): """A cog which provides a way to hook onto Discord events and forward to stats.""" @@ -32,6 +36,8 @@ class Stats(Cog): if CHANNEL_NAME_OVERRIDES.get(message.channel.id): reformatted_name = CHANNEL_NAME_OVERRIDES.get(message.channel.id) + reformatted_name = "".join([char for char in reformatted_name if char in ALLOWED_CHARS]) + stat_name = f"channels.{reformatted_name}" self.bot.stats.incr(stat_name) @@ -73,13 +79,13 @@ class Stats(Cog): offline = 0 for member in after.guild.members: - if member.status == Status.online: + if member.status is Status.online: online += 1 - elif member.status == Status.dnd: + elif member.status is Status.dnd: dnd += 1 - elif member.status == Status.idle: + elif member.status is Status.idle: idle += 1 - else: + elif member.status is Status.offline: offline += 1 self.bot.stats.gauge("guild.status.online", online) -- cgit v1.2.3 From ea9db39715199ea05eafe124dbf74231d1e7e3d4 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 11 Apr 2020 22:25:44 +0100 Subject: Update bot/cogs/stats.py Co-Authored-By: Mark --- bot/cogs/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index 772ae2c97..c15d0eb1b 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -36,7 +36,7 @@ class Stats(Cog): if CHANNEL_NAME_OVERRIDES.get(message.channel.id): reformatted_name = CHANNEL_NAME_OVERRIDES.get(message.channel.id) - reformatted_name = "".join([char for char in reformatted_name if char in ALLOWED_CHARS]) + reformatted_name = "".join(char for char in reformatted_name if char in ALLOWED_CHARS) stat_name = f"channels.{reformatted_name}" self.bot.stats.incr(stat_name) -- cgit v1.2.3 From 33c40ce2913e2a324647c4eb8f5c511cb26cf8ae Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 22:28:24 +0100 Subject: Use in for membership check as opposed to .get() --- bot/cogs/help_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 01a77db2b..632c78701 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -466,7 +466,7 @@ class HelpChannels(Scheduler, commands.Cog): self.bot.stats.incr(f"help.dormant_calls.{caller}") - if self.claim_times.get(channel.id): + if channel.id in self.claim_times: claimed = self.claim_times[channel.id] in_use_time = datetime.now() - claimed self.bot.stats.timer("help.in_use_time", in_use_time) -- cgit v1.2.3 From 673869062ff49a74a2d2f8338c4d26003bee995e Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sat, 11 Apr 2020 22:44:13 +0100 Subject: Add a metric for tracking how long defcon was active --- bot/cogs/defcon.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index 9197dcca3..7043c7cbb 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -126,6 +126,19 @@ class Defcon(Cog): async def _defcon_action(self, ctx: Context, days: int, action: Action) -> None: """Providing a structured way to do an defcon action.""" + try: + response = await self.bot.api_client.get('bot/bot-settings/defcon') + data = response['data'] + + if "enable_date" in data and action is Action.DISABLED: + enabled = datetime.fromisoformat(data["enable_date"]) + + delta = datetime.now() - enabled + + self.bot.stats.timer("defcon.enabled", delta) + except Exception: + pass + error = None try: await self.bot.api_client.put( @@ -136,6 +149,7 @@ class Defcon(Cog): # TODO: retrieve old days count 'days': days, 'enabled': action is not Action.DISABLED, + 'enable_date': datetime.now().isoformat() } } ) @@ -146,7 +160,7 @@ class Defcon(Cog): await ctx.send(self.build_defcon_msg(action, error)) await self.send_defcon_log(action, ctx.author, error) - self.bot.stats.gauge("defcon.days", days) + self.bot.stats.gauge("defcon.threshold", days) @defcon_group.command(name='enable', aliases=('on', 'e')) @with_role(Roles.admins, Roles.owners) -- cgit v1.2.3 From 4adcb0b0ab2b8768e31043429bdfcbf4dab607e6 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sun, 12 Apr 2020 00:09:53 +0100 Subject: Address aeros' review comment regarding help channel stat reporting --- bot/cogs/help_channels.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 632c78701..d260a6a33 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -374,9 +374,9 @@ class HelpChannels(Scheduler, commands.Cog): def report_stats(self) -> None: """Report the channel count stats.""" - total_in_use = len(list(self.get_category_channels(self.in_use_category))) - total_available = len(list(self.get_category_channels(self.available_category))) - total_dormant = len(list(self.get_category_channels(self.dormant_category))) + total_in_use = sum(1 for _ in self.get_category_channels(self.in_use_category)) + total_available = sum(1 for _ in self.get_category_channels(self.available_category)) + total_dormant = sum(1 for _ in self.get_category_channels(self.dormant_category)) self.bot.stats.gauge("help.total.in_use", total_in_use) self.bot.stats.gauge("help.total.available", total_available) -- cgit v1.2.3 From d5cd996864fe213d1c804911c2a17a6d04b8e170 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sun, 12 Apr 2020 01:00:27 +0100 Subject: Add a timeout to prevent the bot from being overloaded with presence updates --- bot/bot.py | 2 +- bot/cogs/stats.py | 12 ++++++++++-- bot/constants.py | 8 +++++++- config-default.yml | 4 +++- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index ef4a325dc..6dd5ba896 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -33,7 +33,7 @@ class Bot(commands.Bot): self._resolver = None self._guild_available = asyncio.Event() - statsd_url = constants.Bot.statsd_host + statsd_url = constants.Stats.statsd_host if DEBUG_MODE: # Since statsd is UDP, there are no errors for sending to a down port. diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index c15d0eb1b..df4827ba1 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -1,9 +1,10 @@ import string +from datetime import datetime from discord import Member, Message, Status from discord.ext.commands import Bot, Cog, Context -from bot.constants import Channels, Guild +from bot.constants import Channels, Guild, Stats as StatConf CHANNEL_NAME_OVERRIDES = { @@ -13,7 +14,7 @@ CHANNEL_NAME_OVERRIDES = { Channels.staff_lounge: "staff_lounge" } -ALLOWED_CHARS = string.ascii_letters + string.digits +ALLOWED_CHARS = string.ascii_letters + string.digits + "-" class Stats(Cog): @@ -21,6 +22,7 @@ class Stats(Cog): def __init__(self, bot: Bot): self.bot = bot + self.last_presence_update = None @Cog.listener() async def on_message(self, message: Message) -> None: @@ -73,6 +75,12 @@ class Stats(Cog): if after.guild.id != Guild.id: return + if self.last_presence_update: + if (datetime.now() - self.last_presence_update).seconds < StatConf.presence_update_timeout: + return + + self.last_presence_update = datetime.now() + online = 0 idle = 0 dnd = 0 diff --git a/bot/constants.py b/bot/constants.py index 33c1d530d..2add028e7 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -199,7 +199,6 @@ class Bot(metaclass=YAMLGetter): prefix: str token: str sentry_dsn: str - statsd_host: str class Filter(metaclass=YAMLGetter): section = "filter" @@ -351,6 +350,13 @@ class CleanMessages(metaclass=YAMLGetter): message_limit: int +class Stats(metaclass=YAMLGetter): + section = "bot" + subsection = "stats" + + presence_update_timeout: int + statsd_host: str + class Categories(metaclass=YAMLGetter): section = "guild" diff --git a/config-default.yml b/config-default.yml index 567caacbf..4cd61ce10 100644 --- a/config-default.yml +++ b/config-default.yml @@ -3,7 +3,9 @@ bot: token: !ENV "BOT_TOKEN" sentry_dsn: !ENV "BOT_SENTRY_DSN" - statsd_host: "graphite" + stats: + statsd_host: "graphite" + presence_update_timeout: 300 cooldowns: # Per channel, per tag. -- cgit v1.2.3 From a24f3736b37e8de9978931dc415d702d98f46b5c Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sun, 12 Apr 2020 01:18:48 +0100 Subject: Use underscore for metric names instead of dash --- bot/cogs/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/stats.py b/bot/cogs/stats.py index df4827ba1..d253db913 100644 --- a/bot/cogs/stats.py +++ b/bot/cogs/stats.py @@ -14,7 +14,7 @@ CHANNEL_NAME_OVERRIDES = { Channels.staff_lounge: "staff_lounge" } -ALLOWED_CHARS = string.ascii_letters + string.digits + "-" +ALLOWED_CHARS = string.ascii_letters + string.digits + "_" class Stats(Cog): -- cgit v1.2.3 From 540700694c7918058bf66af23a6351438423c155 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sun, 12 Apr 2020 01:48:58 +0100 Subject: timer -> timing for statsd --- bot/cogs/defcon.py | 2 +- bot/cogs/help_channels.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index 7043c7cbb..56fca002a 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -135,7 +135,7 @@ class Defcon(Cog): delta = datetime.now() - enabled - self.bot.stats.timer("defcon.enabled", delta) + self.bot.stats.timing("defcon.enabled", delta) except Exception: pass diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index d260a6a33..12bc4e279 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -469,7 +469,7 @@ class HelpChannels(Scheduler, commands.Cog): if channel.id in self.claim_times: claimed = self.claim_times[channel.id] in_use_time = datetime.now() - claimed - self.bot.stats.timer("help.in_use_time", in_use_time) + self.bot.stats.timing("help.in_use_time", in_use_time) log.trace(f"Position of #{channel} ({channel.id}) is actually {channel.position}.") -- cgit v1.2.3 From 7f1e65f51d6c597214bbdd7002d43afa4617e0c2 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Sun, 12 Apr 2020 11:12:01 +0100 Subject: [stat] Create a statistic for whether dormant was called by the claimant or staff --- bot/cogs/help_channels.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 4dd70d7bf..36ad6a7a3 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -198,10 +198,16 @@ class HelpChannels(Scheduler, commands.Cog): """Return True if the user is the help channel claimant or passes the role check.""" if self.help_channel_claimants.get(ctx.channel) == ctx.author: log.trace(f"{ctx.author} is the help channel claimant, passing the check for dormant.") + self.bot.stats.incr("help.dormant_invoke.claimant") return True log.trace(f"{ctx.author} is not the help channel claimant, checking roles.") - return with_role_check(ctx, *constants.HelpChannels.cmd_whitelist) + role_check = with_role_check(ctx, *constants.HelpChannels.cmd_whitelist) + + if role_check: + self.bot.stats.incr("help.dormant_invoke.staff") + + return role_check @commands.command(name="dormant", aliases=["close"], enabled=False) async def dormant_command(self, ctx: commands.Context) -> None: -- cgit v1.2.3 From 3d25c790a69421e0ed9c7c7a29ca1d5833322169 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Mon, 13 Apr 2020 03:20:43 +0100 Subject: [stat] Tag statistic was using the user input as the series name, not the resolved tag name --- bot/cogs/tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index b81859db1..9ba33d7e0 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -208,7 +208,7 @@ class Tags(Cog): "channel": ctx.channel.id } - self.bot.stats.incr(f"tags.usages.{tag_name.replace('-', '_')}") + self.bot.stats.incr(f"tags.usages.{tag['title'].replace('-', '_')}") await wait_for_deletion( await ctx.send(embed=Embed.from_dict(tag['embed'])), -- cgit v1.2.3 From ea81f3b23192cf0840144cecfb2ca721c89a34fe Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff Date: Tue, 14 Apr 2020 11:04:55 +0200 Subject: Revert deletion of !dormant invocation messages PR #868 introduced the automatic deletion of the message that issued the `!dormant` command. The idea behind this was that moving the channel to the dormant category makes it obvious that a channel has gone dormant and the message would only serve as visual clutter. However, removing the command invocation also means that it's less obvious why a channel was moved to the dormant category. As the message gets deleted almost immediately, you have to be actively watching the channel to know that the command was issued and who issued it. This has already caused some confusion where helping members where left wondering why a channel suddenly went dormant while they felt that the conversation was still ongoing. To improve the user experience, this commit removes the deletions of the command invocation messages. --- bot/cogs/help_channels.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 36ad6a7a3..692bb234c 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -224,10 +224,6 @@ class HelpChannels(Scheduler, commands.Cog): with suppress(KeyError): del self.help_channel_claimants[ctx.channel] - with suppress(discord.errors.NotFound): - log.trace("Deleting dormant invokation message.") - await ctx.message.delete() - with suppress(discord.errors.HTTPException, discord.errors.NotFound): await self.reset_claimant_send_permission(ctx.channel) -- cgit v1.2.3 From b44caed7c5b26f13b4b31d237ffe07f3157aadfb Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff Date: Thu, 16 Apr 2020 16:21:25 +0200 Subject: Remove `.md` from anti-malware whitelist We want our members to use the paste site to share text-based files instead of them sharing the files as attachments on Discord. As `.md`, a file extensions used for plain-text files with markdown formatting, is such a text file, I've removed it from the anti-malware whitelist. --- config-default.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index 4cd61ce10..f2b0bfa9f 100644 --- a/config-default.yml +++ b/config-default.yml @@ -475,7 +475,6 @@ anti_malware: - '.mp3' - '.wav' - '.ogg' - - '.md' reddit: -- cgit v1.2.3 From 27ed536350640fd1ce3fee5c21cfa6b495b4793e Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 17 Apr 2020 10:09:23 -0700 Subject: HelpChannels: ensure `is_in_category` returns a bool Co-Authored-By: kwzrd <44734341+kwzrd@users.noreply.github.com> --- bot/cogs/help_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py index 56caa60af..170812d1b 100644 --- a/bot/cogs/help_channels.py +++ b/bot/cogs/help_channels.py @@ -380,7 +380,7 @@ class HelpChannels(Scheduler, commands.Cog): def is_in_category(channel: discord.TextChannel, category_id: int) -> bool: """Return True if `channel` is within a category with `category_id`.""" actual_category = getattr(channel, "category", None) - return actual_category and actual_category.id == category_id + return actual_category is not None and actual_category.id == category_id async def move_idle_channel(self, channel: discord.TextChannel, has_task: bool = True) -> None: """ -- cgit v1.2.3