diff options
| -rw-r--r-- | bot/cogs/moderation/modlog.py | 93 | ||||
| -rw-r--r-- | bot/constants.py | 7 | ||||
| -rw-r--r-- | config-default.yml | 9 |
3 files changed, 101 insertions, 8 deletions
diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 35ef6cbcc..7f24654d8 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -26,6 +26,12 @@ CHANNEL_CHANGES_SUPPRESSED = ("_overwrites", "position") MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status", "nick") ROLE_CHANGES_UNSUPPORTED = ("colour", "permissions") +VOICE_STATE_ATTRIBUTES = { + "channel.name": "Channel", + "self_stream": "Streaming", + "self_video": "Broadcasting", +} + class ModLog(Cog, name="ModLog"): """Logging for server events and staff actions.""" @@ -206,7 +212,7 @@ class ModLog(Cog, name="ModLog"): new = value["new_value"] old = value["old_value"] - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) @@ -284,7 +290,7 @@ class ModLog(Cog, name="ModLog"): new = value["new_value"] old = value["old_value"] - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) @@ -334,7 +340,7 @@ class ModLog(Cog, name="ModLog"): new = value["new_value"] old = value["old_value"] - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) @@ -487,23 +493,23 @@ class ModLog(Cog, name="ModLog"): old = value.get("old_value") if new and old: - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) if before.name != after.name: changes.append( - f"**Username:** `{before.name}` **->** `{after.name}`" + f"**Username:** `{before.name}` **→** `{after.name}`" ) if before.discriminator != after.discriminator: changes.append( - f"**Discriminator:** `{before.discriminator}` **->** `{after.discriminator}`" + f"**Discriminator:** `{before.discriminator}` **→** `{after.discriminator}`" ) if before.display_name != after.display_name: changes.append( - f"**Display name:** `{before.display_name}` **->** `{after.display_name}`" + f"**Display name:** `{before.display_name}` **→** `{after.display_name}`" ) if not changes: @@ -749,3 +755,76 @@ class ModLog(Cog, name="ModLog"): Icons.message_edit, Colour.blurple(), "Message edited (After)", after_response, channel_id=Channels.message_log ) + + @Cog.listener() + async def on_voice_state_update( + self, + member: discord.Member, + before: discord.VoiceState, + after: discord.VoiceState + ) -> None: + """Log member voice state changes to the voice log channel.""" + if ( + member.guild.id != GuildConstant.id + or (before.channel and before.channel.id in GuildConstant.ignored) + ): + return + + if member.id in self._ignored[Event.voice_state_update]: + self._ignored[Event.voice_state_update].remove(member.id) + return + + # Exclude all channel attributes except the name. + diff = DeepDiff( + before, + after, + exclude_paths=("root.session_id", "root.afk"), + exclude_regex_paths=r"root\.channel\.(?!name)", + ) + + # A type change seems to always take precedent over a value change. Furthermore, it will + # include the value change along with the type change anyway. Therefore, it's OK to + # "overwrite" values_changed; in practice there will never even be anything to overwrite. + diff_values = {**diff.get("values_changed", {}), **diff.get("type_changes", {})} + + icon = Icons.voice_state_blue + colour = Colour.blurple() + changes = [] + + for attr, values in diff_values.items(): + if not attr: # Not sure why, but it happens. + continue + + old = values["old_value"] + new = values["new_value"] + + attr = attr[5:] # Remove "root." prefix. + attr = VOICE_STATE_ATTRIBUTES.get(attr, attr.replace("_", " ").capitalize()) + + changes.append(f"**{attr}:** `{old}` **→** `{new}`") + + # Set the embed icon and colour depending on which attribute changed. + if any(name in attr for name in ("Channel", "deaf", "mute")): + if new is None or new is True: + # Left a channel or was muted/deafened. + icon = Icons.voice_state_red + colour = Colours.soft_red + elif old is None or old is True: + # Joined a channel or was unmuted/undeafened. + icon = Icons.voice_state_green + colour = Colours.soft_green + + if not changes: + return + + message = "\n".join(f"{Emojis.bullet} {item}" for item in sorted(changes)) + message = f"**{member}** (`{member.id}`)\n{message}" + + await self.send_log_message( + icon_url=icon, + colour=colour, + title="Voice state updated", + text=message, + thumbnail=member.avatar_url_as(static_format="png"), + channel_id=Channels.voice_log + ) diff --git a/bot/constants.py b/bot/constants.py index 2c0e3b10b..25c7856ba 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -333,6 +333,10 @@ class Icons(metaclass=YAMLGetter): superstarify: str unsuperstarify: str + voice_state_blue: str + voice_state_green: str + voice_state_red: str + class CleanMessages(metaclass=YAMLGetter): section = "bot" @@ -387,6 +391,7 @@ class Channels(metaclass=YAMLGetter): userlog: int user_event_a: int verification: int + voice_log: int class Webhooks(metaclass=YAMLGetter): @@ -553,6 +558,8 @@ class Event(Enum): message_delete = "message_delete" message_edit = "message_edit" + voice_state_update = "voice_state_update" + # Debug mode DEBUG_MODE = True if 'local' in os.environ.get("SITE_URL", "local") else False diff --git a/config-default.yml b/config-default.yml index f66ba8794..f842cf606 100644 --- a/config-default.yml +++ b/config-default.yml @@ -100,6 +100,10 @@ style: superstarify: "https://cdn.discordapp.com/emojis/636288153044516874.png" unsuperstarify: "https://cdn.discordapp.com/emojis/636288201258172446.png" + voice_state_blue: "https://cdn.discordapp.com/emojis/656899769662439456.png" + voice_state_green: "https://cdn.discordapp.com/emojis/656899770094452754.png" + voice_state_red: "https://cdn.discordapp.com/emojis/656899769905709076.png" + guild: id: 267624335836053506 @@ -109,6 +113,7 @@ guild: channels: admins: &ADMINS 365960823622991872 admin_spam: &ADMIN_SPAM 563594791770914816 + admins_voice: &ADMINS_VOICE 500734494840717332 announcements: 354619224620138496 big_brother_logs: &BBLOGS 468507907357409333 bot: 267659945086812160 @@ -139,13 +144,15 @@ guild: python: 267624335836053506 reddit: 458224812528238616 staff_lounge: &STAFF_LOUNGE 464905259261755392 + staff_voice: &STAFF_VOICE 412375055910043655 talent_pool: &TALENT_POOL 534321732593647616 userlog: 528976905546760203 user_event_a: &USER_EVENT_A 592000283102674944 verification: 352442727016693763 + voice_log: 640292421988646961 staff_channels: [*ADMINS, *ADMIN_SPAM, *MOD_SPAM, *MODS, *HELPERS, *ORGANISATION, *DEFCON] - ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG] + ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG, *ADMINS_VOICE, *STAFF_VOICE] roles: admin: &ADMIN_ROLE 267628507062992896 |