Bladeren bron

Adding more log cog options, better excludes certain users and channels from events

main
Rocketsoup 6 dagen geleden
bovenliggende
commit
f055154d10
3 gewijzigde bestanden met toevoegingen van 153 en 88 verwijderingen
  1. 1
    0
      .gitignore
  2. 2
    1
      rocketbot/cogs/helpcog.py
  3. 150
    87
      rocketbot/cogs/logcog.py

+ 1
- 0
.gitignore Bestand weergeven

@@ -120,6 +120,7 @@ dmypy.json
120 120
 # Rocketbot stuff
121 121
 /config.py
122 122
 /config/**
123
+kickstarter.sqlite3
123 124
 
124 125
 # PyCharm
125 126
 .idea

+ 2
- 1
rocketbot/cogs/helpcog.py Bestand weergeven

@@ -367,7 +367,8 @@ class HelpCog(BaseCog, name='Help'):
367 367
 				if not param.required:
368 368
 					text += '['
369 369
 					optional_nesting += 1
370
-				text += f'_{param.name}_'
370
+				escaped_param_name = param.name.replace('_', '\\_')
371
+				text += f'_{escaped_param_name}_'
371 372
 			if optional_nesting > 0:
372 373
 				text += ']' * optional_nesting
373 374
 		text += f'\n\n{command_or_group.description}'

+ 150
- 87
rocketbot/cogs/logcog.py Bestand weergeven

@@ -3,16 +3,14 @@ Cog for detecting large numbers of guild joins in a short period of time.
3 3
 """
4 4
 import difflib
5 5
 import re
6
-from collections.abc import Callable, Sequence
6
+from collections.abc import Callable
7 7
 from datetime import datetime, timedelta, timezone
8 8
 from typing import Any
9 9
 
10 10
 from discord import (
11 11
 	AuditLogAction,
12 12
 	AuditLogEntry,
13
-	Emoji,
14 13
 	Guild,
15
-	GuildSticker,
16 14
 	Invite,
17 15
 	Member,
18 16
 	Message,
@@ -20,7 +18,6 @@ from discord import (
20 18
 	RawMessageDeleteEvent,
21 19
 	RawMessageUpdateEvent,
22 20
 	Role,
23
-	Thread,
24 21
 	User,
25 22
 )
26 23
 from discord.abc import GuildChannel
@@ -31,7 +28,7 @@ from discord.utils import escape_markdown
31 28
 
32 29
 from config import CONFIG
33 30
 from rocketbot.cogs.basecog import BaseCog, BotMessage, CogSetting
34
-from rocketbot.storage import Storage
31
+from rocketbot.storage import ConfigKey, Storage
35 32
 from rocketbot.utils import dump_stacktrace, norm_datetime
36 33
 
37 34
 
@@ -62,6 +59,41 @@ class LoggingCog(BaseCog, name='Logging'):
62 59
 		brief='logging',
63 60
 		description='Whether this module is enabled for a guild.',
64 61
 	)
62
+	SETTING_EDITS_ENABLED = CogSetting(
63
+		'edits',
64
+		bool,
65
+		default_value=True,
66
+		brief='whether to log message edit events',
67
+		description='Whether to log member message edits.'
68
+	)
69
+	SETTING_DELETES_ENABLED = CogSetting(
70
+		'deletes',
71
+		bool,
72
+		default_value=True,
73
+		brief='whether to log message deletion events',
74
+		description='Whether to log member message deletions.'
75
+	)
76
+	SETTING_JOIN_ENABLED = CogSetting(
77
+		'joins',
78
+		bool,
79
+		default_value=True,
80
+		brief='whether to log members joining the server',
81
+		description='Whether to log members joining the server.'
82
+	)
83
+	SETTING_LEAVE_ENABLED = CogSetting(
84
+		'leaves',
85
+		bool,
86
+		default_value=True,
87
+		brief='whether to log members leaving the server',
88
+		description='Whether to log members leaving the server.'
89
+	)
90
+	SETTING_MEMBER_UPDATES_ENABLED = CogSetting(
91
+		'member_updates',
92
+		bool,
93
+		default_value=True,
94
+		brief='whether to log changes to member details',
95
+		description='Whether to log changes to member roles, flags, display names, etc.'
96
+	)
65 97
 
66 98
 	STATE_EVENT_BUFFER = 'LoggingCog.eventBuffer'
67 99
 
@@ -71,13 +103,48 @@ class LoggingCog(BaseCog, name='Logging'):
71 103
 			config_prefix='logging',
72 104
 			short_description='Manages event logging.',
73 105
 		)
74
-		self.add_setting(LoggingCog.SETTING_ENABLED)
106
+		self.add_setting(Self.SETTING_ENABLED)
107
+		self.add_setting(Self.SETTING_EDITS_ENABLED)
108
+		self.add_setting(Self.SETTING_DELETES_ENABLED)
109
+		self.add_setting(Self.SETTING_JOIN_ENABLED)
110
+		self.add_setting(Self.SETTING_LEAVE_ENABLED)
111
+		self.add_setting(Self.SETTING_MEMBER_UPDATES_ENABLED)
75 112
 		self.flush_buffers.start()
76 113
 		self.buffered_guilds: set[Guild] = set()
77 114
 
115
+	# @overload
78 116
 	def cog_unload(self) -> None:
79 117
 		self.flush_buffers.cancel()
80 118
 
119
+	def __is_cog_disabled(self, guild: Guild | None) -> bool:
120
+		"""Whether this cog is disabled for a guild."""
121
+		return guild is None or not self.get_guild_setting(guild, Self.SETTING_ENABLED)
122
+	def __is_edits_disabled(self, guild: Guild | None) -> bool:
123
+		"""Whether logging message edit events is disabled for a guild."""
124
+		return guild is None or self.__is_cog_disabled(guild) or not self.get_guild_setting(guild, Self.SETTING_EDITS_ENABLED)
125
+	def __is_deletes_disabled(self, guild: Guild | None) -> bool:
126
+		"""Whether logging message delete events is disabled for a guild."""
127
+		return guild is None or self.__is_cog_disabled(guild) or not self.get_guild_setting(guild, Self.SETTING_DELETES_ENABLED)
128
+	def __is_joins_disabled(self, guild: Guild | None) -> bool:
129
+		"""Whether logging member join events is disabled for a guild."""
130
+		return guild is None or self.__is_cog_disabled(guild) or not self.get_guild_setting(guild, Self.SETTING_JOIN_ENABLED)
131
+	def __is_leaves_disabled(self, guild: Guild | None) -> bool:
132
+		"""Whether logging member departure events is disabled for a guild."""
133
+		return guild is None or self.__is_cog_disabled(guild) or not self.get_guild_setting(guild, Self.SETTING_LEAVE_ENABLED)
134
+	def __is_member_updates_disabled(self, guild: Guild | None) -> bool:
135
+		"""Whether logging member profile updates is disabled for a guild."""
136
+		return guild is None or self.__is_cog_disabled(guild) or not self.get_guild_setting(guild, Self.SETTING_MEMBER_UPDATES_ENABLED)
137
+	def __is_excluded_user(self, user: User | None) -> bool:
138
+		"""Whether member events should be ignored for the given user."""
139
+		return user is None or \
140
+			user.bot or \
141
+			user.id == self.bot.user.id
142
+	def __is_excluded_channel(self, channel: GuildChannel | None) -> bool:
143
+		"""Whether message events should be ignored in the given channel."""
144
+		if channel is None: return True
145
+		warning_channel_id = Storage.get_config_value(channel.guild, ConfigKey.WARNING_CHANNEL_ID)
146
+		return channel.id == warning_channel_id
147
+
81 148
 	# Events - Channels
82 149
 
83 150
 	@Cog.listener()
@@ -90,8 +157,8 @@ class LoggingCog(BaseCog, name='Logging'):
90 157
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_guild_channel_delete
91 158
 		"""
92 159
 		guild = channel.guild
93
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
94
-			return
160
+		if self.__is_cog_disabled(guild): return
161
+
95 162
 		text = f'Channel **{channel.name}** deleted.'
96 163
 		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
97 164
 		await bot_message.update()
@@ -106,8 +173,8 @@ class LoggingCog(BaseCog, name='Logging'):
106 173
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_guild_channel_create
107 174
 		"""
108 175
 		guild = channel.guild
109
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
110
-			return
176
+		if self.__is_cog_disabled(guild): return
177
+
111 178
 		text = f'Channel **{channel.name}** created. {channel.mention}'
112 179
 		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
113 180
 		await bot_message.update()
@@ -121,8 +188,8 @@ class LoggingCog(BaseCog, name='Logging'):
121 188
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_guild_channel_update
122 189
 		"""
123 190
 		guild = after.guild
124
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
125
-			return
191
+		if self.__is_cog_disabled(guild): return
192
+
126 193
 		changes = []
127 194
 		if after.name != before.name:
128 195
 			changes.append(f'Name: `{before.name}` -> `{after.name}`')
@@ -145,25 +212,25 @@ class LoggingCog(BaseCog, name='Logging'):
145 212
 
146 213
 	# Events - Guilds
147 214
 
148
-	@Cog.listener()
149
-	async def on_guild_available(self, guild: Guild) -> None:
150
-		pass
215
+	# @Cog.listener()
216
+	# async def on_guild_available(self, guild: Guild) -> None:
217
+	# 	pass
151 218
 
152
-	@Cog.listener()
153
-	async def on_guild_unavailable(self, guild: Guild) -> None:
154
-		pass
219
+	# @Cog.listener()
220
+	# async def on_guild_unavailable(self, guild: Guild) -> None:
221
+	# 	pass
155 222
 
156
-	@Cog.listener()
157
-	async def on_guild_update(self, before: Guild, after: Guild) -> None:
158
-		pass
223
+	# @Cog.listener()
224
+	# async def on_guild_update(self, before: Guild, after: Guild) -> None:
225
+	# 	pass
159 226
 
160
-	@Cog.listener()
161
-	async def on_guild_emojis_update(self, guild: Guild, before: Sequence[Emoji], after: Sequence[Emoji]) -> None:
162
-		pass
227
+	# @Cog.listener()
228
+	# async def on_guild_emojis_update(self, guild: Guild, before: Sequence[Emoji], after: Sequence[Emoji]) -> None:
229
+	# 	pass
163 230
 
164
-	@Cog.listener()
165
-	async def on_guild_stickers_update(self, guild: Guild, before: Sequence[GuildSticker], after: Sequence[GuildSticker]) -> None:
166
-		pass
231
+	# @Cog.listener()
232
+	# async def on_guild_stickers_update(self, guild: Guild, before: Sequence[GuildSticker], after: Sequence[GuildSticker]) -> None:
233
+	# 	pass
167 234
 
168 235
 	@Cog.listener()
169 236
 	async def on_invite_create(self, invite: Invite) -> None:
@@ -173,8 +240,8 @@ class LoggingCog(BaseCog, name='Logging'):
173 240
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_invite_create
174 241
 		"""
175 242
 		guild = invite.guild
176
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
177
-			return
243
+		if self.__is_cog_disabled(guild): return
244
+
178 245
 		text = f'Invite code `{invite.code}` created by {self.__describe_user(invite.inviter)}. '
179 246
 		if invite.max_age == 0:
180 247
 			text += "Doesn't expire."
@@ -191,8 +258,8 @@ class LoggingCog(BaseCog, name='Logging'):
191 258
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_invite_delete
192 259
 		"""
193 260
 		guild = invite.guild
194
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
195
-			return
261
+		if self.__is_cog_disabled(guild): return
262
+
196 263
 		if invite.inviter:
197 264
 			text = f'Invite code `{invite.code}` deleted. Originally created by {self.__describe_user(invite.inviter)}.'
198 265
 		else:
@@ -212,11 +279,10 @@ class LoggingCog(BaseCog, name='Logging'):
212 279
 		Requires Members privileged intent.
213 280
 		"""
214 281
 		guild = member.guild
215
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
216
-			return
282
+		if self.__is_joins_disabled(guild): return
283
+
217 284
 		flags = []
218 285
 		noteworthy = False
219
-
220 286
 		badge_info_link = 'https://support.discord.com/hc/en-us/articles/360035962891-Profile-Badges-101'
221 287
 
222 288
 		# Highest priority - should flag mods to be aware
@@ -306,8 +372,8 @@ class LoggingCog(BaseCog, name='Logging'):
306 372
 		Requires Members privileged intent.
307 373
 		"""
308 374
 		guild = member.guild
309
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
310
-			return
375
+		if self.__is_cog_disabled(guild): return
376
+
311 377
 		is_kick = False
312 378
 		kicker = None
313 379
 		kick_reason = None
@@ -323,6 +389,7 @@ class LoggingCog(BaseCog, name='Logging'):
323 389
 			else:
324 390
 				text = f'Member kicked from the server: {self.__describe_user(member)}'
325 391
 		else:
392
+			if self.__is_leaves_disabled(guild): return
326 393
 			text = f'Member left server: {self.__describe_user(member)}'
327 394
 		if kick_reason:
328 395
 			text += f'\nReason: "{kick_reason}"'
@@ -347,8 +414,9 @@ class LoggingCog(BaseCog, name='Logging'):
347 414
 		Requires Members privileged intent.
348 415
 		"""
349 416
 		guild = after.guild
350
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
351
-			return
417
+		if self.__is_member_updates_disabled(guild): return
418
+		if self.__is_excluded_user(after): return
419
+
352 420
 		changes = []
353 421
 		noteworthy = False
354 422
 		if after.nick != before.nick:
@@ -427,12 +495,11 @@ class LoggingCog(BaseCog, name='Logging'):
427 495
 
428 496
 		Requires Members privileged intent.
429 497
 		"""
430
-		if hasattr(after, 'guild'):
431
-			guild = after.guild
432
-		else:
433
-			return
434
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
435
-			return
498
+		if not hasattr(after, 'guild'): return
499
+		guild = after.guild
500
+		if self.__is_member_updates_disabled(guild): return
501
+		if self.__is_excluded_user(after): return
502
+
436 503
 		changes = []
437 504
 		before_avatar_url = before.avatar.url if before.avatar else None
438 505
 		after_avatar_url = after.avatar.url if after.avatar else None
@@ -454,8 +521,8 @@ class LoggingCog(BaseCog, name='Logging'):
454 521
 
455 522
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_ban
456 523
 		"""
457
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
458
-			return
524
+		if self.__is_cog_disabled(guild): return
525
+
459 526
 		banner = None
460 527
 		ban_reason = None
461 528
 		entry = await self.__find_audit_entry(user, AuditLogAction.ban)
@@ -495,8 +562,8 @@ class LoggingCog(BaseCog, name='Logging'):
495 562
 
496 563
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_unban
497 564
 		"""
498
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
499
-			return
565
+		if self.__is_cog_disabled(guild): return
566
+
500 567
 		text = f'Member {self.__describe_user(user)} unbanned'
501 568
 		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
502 569
 		await bot_message.update()
@@ -550,6 +617,10 @@ class LoggingCog(BaseCog, name='Logging'):
550 617
 
551 618
 		Content fields blank without MessageContent privileged intent.
552 619
 		"""
620
+		# if self.__is_cog_disabled(message.guild): return
621
+		# if self.__is_excluded_channel(message.channel): return
622
+		# if self.__is_excluded_user(message.author): return
623
+
553 624
 		# print(f"on_message:"
554 625
 		# 	f"\n\tid: {message.id}"
555 626
 		# 	f"\n\tauthor: {message.author.name}"
@@ -584,11 +655,10 @@ class LoggingCog(BaseCog, name='Logging'):
584 655
 		Content fields blank without MessageContent privileged intent.
585 656
 		"""
586 657
 		guild = after.guild
587
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
588
-			return
589
-		if after.author.id == self.bot.user.id:
590
-			return
658
+		if self.__is_edits_disabled(guild): return
659
+		if self.__is_excluded_user(after.author): return
591 660
 		channel = after.channel
661
+		if self.__is_excluded_channel(channel): return
592 662
 
593 663
 		self.__buffer_event(guild, 'edit', BufferedMessageEditEvent(guild, channel, before, after))
594 664
 
@@ -618,16 +688,11 @@ class LoggingCog(BaseCog, name='Logging'):
618 688
 
619 689
 		Content fields blank without MessageContent privileged intent.
620 690
 		"""
621
-		if payload.cached_message:
622
-			return  # already handled by on_message_edit
691
+		if payload.cached_message: return  # already handled by on_message_edit
623 692
 		guild = self.bot.get_guild(payload.guild_id) or await self.bot.fetch_guild(payload.guild_id)
624
-		if not guild:
625
-			return
626
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
627
-			return
693
+		if not guild or self.__is_edits_disabled(guild): return
628 694
 		channel = guild.get_channel(payload.channel_id) or await guild.fetch_channel(payload.channel_id)
629
-		if not channel:
630
-			return
695
+		if not channel or self.__is_excluded_channel(channel): return
631 696
 
632 697
 		self.__buffer_event(guild, 'edit', BufferedMessageEditEvent(
633 698
 			guild, channel, None, payload.message, payload.data))
@@ -728,20 +793,16 @@ class LoggingCog(BaseCog, name='Logging'):
728 793
 		Content fields blank without MessageContent privileged intent.
729 794
 		"""
730 795
 		message = payload.cached_message
731
-		if message and message.author.id == self.bot.user.id:
732
-			return
796
+		if message and self.__is_excluded_user(message.author): return
733 797
 		guild = (message.guild if message else None) or \
734 798
 				self.bot.get_guild(payload.guild_id) or \
735 799
 				await self.bot.fetch_guild(payload.guild_id)
736
-		if guild is None:
737
-			return
738
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
739
-			return
800
+		if guild is None or self.__is_deletes_disabled(guild): return
740 801
 		channel = (message.channel if message else None) or \
741 802
 				  self.bot.get_channel(payload.channel_id) or \
742 803
 				  await guild.fetch_channel(payload.channel_id)
743
-		if channel is None:
744
-			return
804
+		if channel is None or self.__is_excluded_channel(channel): return
805
+
745 806
 		self.__buffer_event(guild, 'delete', BufferedMessageDeleteEvent(guild, channel, payload.message_id, message))
746 807
 
747 808
 	@Cog.listener()
@@ -759,11 +820,11 @@ class LoggingCog(BaseCog, name='Logging'):
759 820
 		Content fields blank without MessageContent privileged intent.
760 821
 		"""
761 822
 		guild = self.bot.get_guild(payload.guild_id) or await self.bot.fetch_guild(payload.guild_id)
762
-		if not guild:
763
-			return
764
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
765
-			return
823
+		if not guild: return
824
+		if self.__is_deletes_disabled(guild): return
766 825
 		channel = guild.get_channel(payload.channel_id) or await guild.fetch_channel(payload.channel_id)
826
+		if self.__is_excluded_channel(channel): return
827
+
767 828
 		for message_id in payload.message_ids:
768 829
 			message = None
769 830
 			for cached_message in payload.cached_messages:
@@ -836,8 +897,8 @@ class LoggingCog(BaseCog, name='Logging'):
836 897
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_guild_role_create
837 898
 		"""
838 899
 		guild = role.guild
839
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
840
-			return
900
+		if self.__is_cog_disabled(guild): return
901
+
841 902
 		text = f'Role created: **{role.name}**'
842 903
 		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
843 904
 		await bot_message.update()
@@ -852,8 +913,8 @@ class LoggingCog(BaseCog, name='Logging'):
852 913
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_guild_role_delete
853 914
 		"""
854 915
 		guild = role.guild
855
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
856
-			return
916
+		if self.__is_cog_disabled(guild): return
917
+
857 918
 		text = f'Role removed: **{role.name}**'
858 919
 		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
859 920
 		await bot_message.update()
@@ -866,8 +927,8 @@ class LoggingCog(BaseCog, name='Logging'):
866 927
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_guild_role_update
867 928
 		"""
868 929
 		guild = after.guild
869
-		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
870
-			return
930
+		if self.__is_cog_disabled(guild): return
931
+
871 932
 		changes = []
872 933
 		if after.name != before.name:
873 934
 			changes.append(f'Name: `{before.name}` -> `{after.name}`')
@@ -901,17 +962,17 @@ class LoggingCog(BaseCog, name='Logging'):
901 962
 
902 963
 	# Events - Threads
903 964
 
904
-	@Cog.listener()
905
-	async def on_thread_create(self, thread: Thread) -> None:
906
-		pass
965
+	# @Cog.listener()
966
+	# async def on_thread_create(self, thread: Thread) -> None:
967
+	# 	pass
907 968
 
908
-	@Cog.listener()
909
-	async def on_thread_update(self, before: Thread, after: Thread) -> None:
910
-		pass
969
+	# @Cog.listener()
970
+	# async def on_thread_update(self, before: Thread, after: Thread) -> None:
971
+	# 	pass
911 972
 
912
-	@Cog.listener()
913
-	async def on_thread_delete(self, thread: Thread) -> None:
914
-		pass
973
+	# @Cog.listener()
974
+	# async def on_thread_delete(self, thread: Thread) -> None:
975
+	# 	pass
915 976
 
916 977
 
917 978
 	# ------------------------------------------------------------------------
@@ -1000,3 +1061,5 @@ class LoggingCog(BaseCog, name='Logging'):
1000 1061
 			else:
1001 1062
 				d[key] = [elem]
1002 1063
 		return d
1064
+
1065
+Self = LoggingCog

Laden…
Annuleren
Opslaan