Просмотр исходного кода

Progressive functionality based on enabled privileged intents

main
Rocketsoup 1 месяц назад
Родитель
Сommit
c1485f052e

+ 7
- 1
config.sample.py Просмотреть файл

@@ -1,6 +1,6 @@
1 1
 # Copy this file to config.py and fill in "<REQUIRED>" values
2 2
 CONFIG = {
3
-	'__config_version': 4,
3
+	'__config_version': 5,
4 4
 
5 5
     # -----------------------------------------------------------------------
6 6
     # General
@@ -14,6 +14,12 @@ CONFIG = {
14 14
 	'max_members_per_message': 20,
15 15
 	# Minimum seconds between warnings about the same member.
16 16
 	'squelch_warning_seconds': 300,
17
+	# Whether the Discord app has Message Content intent enabled. Enables more
18
+	# features but requires applying for access for bots serving 10k or more users.
19
+	'has_message_content_intent': False,
20
+	# Whether the Discord app has Server Members intent enabled. Enables more
21
+	# features but requires applying for access for bots serving 10k or more users.
22
+	'has_members_intent': False,
17 23
 
18 24
     # -----------------------------------------------------------------------
19 25
     # Emojis used for performing actions on flagged activities. The bot will

+ 1
- 1
main.py Просмотреть файл

@@ -19,7 +19,7 @@ from config import CONFIG  # noqa: E402
19 19
 from rocketbot.bot import start_bot  # noqa: E402
20 20
 from rocketbot.utils import bot_log  # noqa: E402
21 21
 
22
-CURRENT_CONFIG_VERSION = 4
22
+CURRENT_CONFIG_VERSION = 5
23 23
 if (CONFIG.get('__config_version') or 0) < CURRENT_CONFIG_VERSION:
24 24
 	# If you're getting this error, it means something changed in config.py's
25 25
 	# format. Consult config.sample.py and compare it to your own config.py.

+ 25
- 13
rocketbot/bot.py Просмотреть файл

@@ -83,8 +83,9 @@ def __create_bot():
83 83
 		return
84 84
 	bot_log(None, None, 'Creating bot...')
85 85
 	intents = Intents.default()
86
-	intents.message_content = True
87
-	intents.members = True
86
+	# Set privileged intents. Must agree with configuration in Discord app.
87
+	intents.message_content = CONFIG.get('has_message_content_intent', False)
88
+	intents.members = CONFIG.get('has_members_intent', False)
88 89
 	intents.presences = False
89 90
 	rocketbot = Rocketbot(intents=intents)
90 91
 __create_bot()
@@ -111,17 +112,28 @@ async def start_bot():
111 112
 	await rocketbot.add_cog(ConfigCog(rocketbot))
112 113
 
113 114
 	# Optional
114
-	await rocketbot.add_cog(AutoKickCog(rocketbot))
115
-	await rocketbot.add_cog(BangCommandCog(rocketbot))
116
-	await rocketbot.add_cog(CrossPostCog(rocketbot))
117
-	await rocketbot.add_cog(GamesCog(rocketbot))
118
-	await rocketbot.add_cog(HelpCog(rocketbot))
119
-	await rocketbot.add_cog(JoinRaidCog(rocketbot))
120
-	await rocketbot.add_cog(LoggingCog(rocketbot))
121
-	await rocketbot.add_cog(PatternCog(rocketbot))
122
-	await rocketbot.add_cog(URLSpamCog(rocketbot))
123
-	await rocketbot.add_cog(UsernamePatternCog(rocketbot))
124
-	await rocketbot.add_cog(VideoPreviewCog(rocketbot))
115
+	if AutoKickCog.supports_intents(rocketbot.intents):
116
+		await rocketbot.add_cog(AutoKickCog(rocketbot))
117
+	if BangCommandCog.supports_intents(rocketbot.intents):
118
+		await rocketbot.add_cog(BangCommandCog(rocketbot))
119
+	if CrossPostCog.supports_intents(rocketbot.intents):
120
+		await rocketbot.add_cog(CrossPostCog(rocketbot))
121
+	if GamesCog.supports_intents(rocketbot.intents):
122
+		await rocketbot.add_cog(GamesCog(rocketbot))
123
+	if HelpCog.supports_intents(rocketbot.intents):
124
+		await rocketbot.add_cog(HelpCog(rocketbot))
125
+	if JoinRaidCog.supports_intents(rocketbot.intents):
126
+		await rocketbot.add_cog(JoinRaidCog(rocketbot))
127
+	if LoggingCog.supports_intents(rocketbot.intents):
128
+		await rocketbot.add_cog(LoggingCog(rocketbot))
129
+	if PatternCog.supports_intents(rocketbot.intents):
130
+		await rocketbot.add_cog(PatternCog(rocketbot))
131
+	if URLSpamCog.supports_intents(rocketbot.intents):
132
+		await rocketbot.add_cog(URLSpamCog(rocketbot))
133
+	if UsernamePatternCog.supports_intents(rocketbot.intents):
134
+		await rocketbot.add_cog(UsernamePatternCog(rocketbot))
135
+	if VideoPreviewCog.supports_intents(rocketbot.intents):
136
+		await rocketbot.add_cog(VideoPreviewCog(rocketbot))
125 137
 
126 138
 	await rocketbot.start(CONFIG['client_token'], reconnect=True)
127 139
 	print('\nBot aborted')

+ 5
- 1
rocketbot/cogs/autokickcog.py Просмотреть файл

@@ -1,6 +1,6 @@
1 1
 from datetime import datetime, timedelta, timezone
2 2
 
3
-from discord import Guild, Member, Status
3
+from discord import Guild, Intents, Member, Status
4 4
 from discord.ext import tasks
5 5
 from discord.ext.commands import Cog
6 6
 from discord.ext.tasks import Loop
@@ -158,3 +158,7 @@ class AutoKickCog(BaseCog, name='Auto Kick'):
158 158
 			if val % 10 == 3:
159 159
 				return f'{val}rd'
160 160
 		return f'{val}th'
161
+
162
+	@classmethod
163
+	def supports_intents(cls, intents: Intents) -> bool:
164
+		return intents.members

+ 13
- 1
rocketbot/cogs/bangcommandcog.py Просмотреть файл

@@ -1,7 +1,15 @@
1 1
 import re
2 2
 from typing import Optional, TypedDict
3 3
 
4
-from discord import Guild, Interaction, Message, SelectOption, TextChannel, TextStyle
4
+from discord import (
5
+	Guild,
6
+	Intents,
7
+	Interaction,
8
+	Message,
9
+	SelectOption,
10
+	TextChannel,
11
+	TextStyle,
12
+)
5 13
 from discord.app_commands import Choice, Group, autocomplete
6 14
 from discord.ext.commands import Cog
7 15
 from discord.ui import Label, Modal, Select, TextInput
@@ -270,6 +278,10 @@ class BangCommandCog(BaseCog, name='Bang Commands'):
270 278
 		match = re.match(r'^!((?:[a-z]+)(?:[_-][a-z]+)*)\b.*$', name)
271 279
 		return BangCommandCog._normalize_name(match.group(1)) if match else None
272 280
 
281
+	@classmethod
282
+	def supports_intents(cls, intents: Intents) -> bool:
283
+		return intents.message_content
284
+
273 285
 class _EditModal(Modal, title='Edit Command'):
274 286
 	name_label = Label(
275 287
 		text='Command name',

+ 7
- 0
rocketbot/cogs/basecog.py Просмотреть файл

@@ -7,6 +7,7 @@ from typing import Optional
7 7
 import discord
8 8
 from discord import (
9 9
 	Guild,
10
+	Intents,
10 11
 	Interaction,
11 12
 	Member,
12 13
 	Message,
@@ -302,3 +303,9 @@ class BaseCog(Cog):
302 303
 		Writes a message to the console. Intended for significant events only.
303 304
 		"""
304 305
 		bot_log(guild, cls, message)
306
+
307
+	# Meta
308
+
309
+	@classmethod
310
+	def supports_intents(cls, intents: Intents) -> bool:
311
+		return True

+ 11
- 7
rocketbot/cogs/crosspostcog.py Просмотреть файл

@@ -141,13 +141,17 @@ class CrossPostCog(BaseCog, name='Crosspost Detection'):
141 141
 			self.__trace(f"User {message.author.name} exempt from crosspost checks")
142 142
 			return
143 143
 		def compute_message_hash(m: Message) -> int:
144
-			to_hash = m.content
145
-			# URLs sometimes differ per spam message, so simplify them
146
-			url_regex = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
147
-			to_hash = re.sub(url_regex, '<url>', to_hash)
148
-			# Add attachment metadata
149
-			for attachment in m.attachments:
150
-				to_hash += f'\n[[ATT: ct={attachment.content_type} s={attachment.size} w={attachment.width} h={attachment.height}]]'
144
+			if self.bot.intents.message_content:
145
+				to_hash = m.content
146
+				# URLs sometimes differ per spam message, so simplify them
147
+				url_regex = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
148
+				to_hash = re.sub(url_regex, '<url>', to_hash)
149
+				# Add attachment metadata
150
+				for attachment in m.attachments:
151
+					to_hash += f'\n[[ATT: ct={attachment.content_type} s={attachment.size} w={attachment.width} h={attachment.height}]]'
152
+			else:
153
+				# Without content, treat every message as unique
154
+				to_hash = str(m.id)
151 155
 			h = hash(to_hash)
152 156
 			self.__trace(f"Hash for message #{m.id} by {m.author.name} is {h}\n\thash content: \"{to_hash}\"")
153 157
 			return h

+ 5
- 1
rocketbot/cogs/joinraidcog.py Просмотреть файл

@@ -5,7 +5,7 @@ import weakref
5 5
 from datetime import datetime, timedelta
6 6
 from typing import Optional
7 7
 
8
-from discord import Guild, Member
8
+from discord import Guild, Intents, Member
9 9
 from discord.ext.commands import Cog
10 10
 
11 11
 from config import CONFIG
@@ -189,3 +189,7 @@ class JoinRaidCog(BaseCog, name='Join Raids'):
189 189
 			did_kick=kick_count >= member_count,
190 190
 			did_ban=ban_count >= member_count,
191 191
 			user_count=member_count))
192
+
193
+	@classmethod
194
+	def supports_intents(cls, intents: Intents) -> bool:
195
+		return intents.members

+ 18
- 0
rocketbot/cogs/logcog.py Просмотреть файл

@@ -206,6 +206,8 @@ class LoggingCog(BaseCog, name='Logging'):
206 206
 		Called when a Member joins a Guild.
207 207
 
208 208
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_join
209
+
210
+		Requires Members privileged intent.
209 211
 		"""
210 212
 		guild = member.guild
211 213
 		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
@@ -273,6 +275,8 @@ class LoggingCog(BaseCog, name='Logging'):
273 275
 		will not be called, you may use on_raw_member_remove() instead.
274 276
 
275 277
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_remove
278
+
279
+		Requires Members privileged intent.
276 280
 		"""
277 281
 		guild = member.guild
278 282
 		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
@@ -312,6 +316,8 @@ class LoggingCog(BaseCog, name='Logging'):
312 316
 		* flags
313 317
 
314 318
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_update
319
+
320
+		Requires Members privileged intent.
315 321
 		"""
316 322
 		guild = after.guild
317 323
 		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
@@ -371,6 +377,8 @@ class LoggingCog(BaseCog, name='Logging'):
371 377
 		* discriminator
372 378
 
373 379
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_user_update
380
+
381
+		Requires Members privileged intent.
374 382
 		"""
375 383
 		if hasattr(after, 'guild'):
376 384
 			guild = after.guild
@@ -492,6 +500,8 @@ class LoggingCog(BaseCog, name='Logging'):
492 500
 		Called when a Message is created and sent.
493 501
 
494 502
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_message
503
+
504
+		Content fields blank without MessageContent privileged intent.
495 505
 		"""
496 506
 		# print(f"on_message:"
497 507
 		# 	f"\n\tid: {message.id}"
@@ -523,6 +533,8 @@ class LoggingCog(BaseCog, name='Logging'):
523 533
 		* A call message has received an update to its participants or ending time.
524 534
 
525 535
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_message_edit
536
+
537
+		Content fields blank without MessageContent privileged intent.
526 538
 		"""
527 539
 		guild = after.guild
528 540
 		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
@@ -556,6 +568,8 @@ class LoggingCog(BaseCog, name='Logging'):
556 568
 		Discord embed server.
557 569
 
558 570
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_raw_message_edit
571
+
572
+		Content fields blank without MessageContent privileged intent.
559 573
 		"""
560 574
 		if payload.cached_message:
561 575
 			return  # already handled by on_message_edit
@@ -663,6 +677,8 @@ class LoggingCog(BaseCog, name='Logging'):
663 677
 		RawMessageDeleteEvent.cached_message
664 678
 
665 679
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_raw_message_delete
680
+
681
+		Content fields blank without MessageContent privileged intent.
666 682
 		"""
667 683
 		message = payload.cached_message
668 684
 		if message and message.author.id == self.bot.user.id:
@@ -692,6 +708,8 @@ class LoggingCog(BaseCog, name='Logging'):
692 708
 		RawBulkMessageDeleteEvent.cached_messages
693 709
 
694 710
 		https://discordpy.readthedocs.io/en/stable/api.html#discord.on_raw_bulk_message_delete
711
+
712
+		Content fields blank without MessageContent privileged intent.
695 713
 		"""
696 714
 		guild = self.bot.get_guild(payload.guild_id) or await self.bot.fetch_guild(payload.guild_id)
697 715
 		if not guild:

+ 5
- 1
rocketbot/cogs/patterncog.py Просмотреть файл

@@ -6,7 +6,7 @@ import re
6 6
 from datetime import datetime
7 7
 from typing import Optional
8 8
 
9
-from discord import Guild, Interaction, Member, Message
9
+from discord import Guild, Intents, Interaction, Member, Message
10 10
 from discord import utils as discordutils
11 11
 from discord.app_commands import Choice, Group, autocomplete
12 12
 from discord.ext.commands import Cog
@@ -435,3 +435,7 @@ class PatternCog(BaseCog, name='Pattern Matching'):
435 435
 				f'updated to `{priority}`.',
436 436
 			ephemeral=True,
437 437
 		)
438
+
439
+	@classmethod
440
+	def supports_intents(cls, intents: Intents) -> bool:
441
+		return intents.message_content

+ 5
- 1
rocketbot/cogs/urlspamcog.py Просмотреть файл

@@ -5,7 +5,7 @@ import re
5 5
 from datetime import timedelta
6 6
 from typing import Literal
7 7
 
8
-from discord import Guild, Member, Message
8
+from discord import Guild, Intents, Member, Message
9 9
 from discord import utils as discordutils
10 10
 from discord.ext.commands import Cog
11 11
 from discord.utils import escape_markdown
@@ -358,3 +358,7 @@ class URLSpamCog(BaseCog, name='URL Spam'):
358 358
 	def __find_urls(cls, text: str) -> list[str]:
359 359
 		p = re.compile(r'https?://\S+')
360 360
 		return re.findall(p, text)
361
+
362
+	@classmethod
363
+	def supports_intents(cls, intents: Intents) -> bool:
364
+		return intents.message_content

+ 5
- 1
rocketbot/cogs/usernamecog.py Просмотреть файл

@@ -3,7 +3,7 @@ Cog for detecting username patterns.
3 3
 """
4 4
 from typing import Optional
5 5
 
6
-from discord import Guild, Interaction, Member
6
+from discord import Guild, Intents, Interaction, Member
7 7
 from discord.app_commands import Group
8 8
 from discord.ext.commands import Cog
9 9
 
@@ -239,3 +239,7 @@ class UsernamePatternCog(BaseCog, name='Username Pattern'):
239 239
 			context.ignored_by = reacted_by
240 240
 			self.log(context.member.guild, f'Warning ignored by {reacted_by.name}')
241 241
 			await bot_message.set_reactions(context.reactions())
242
+
243
+	@classmethod
244
+	def supports_intents(cls, intents: Intents) -> bool:
245
+		return intents.members

+ 5
- 1
rocketbot/cogs/videopreviewcog.py Просмотреть файл

@@ -4,7 +4,7 @@ import re
4 4
 import subprocess
5 5
 from datetime import timedelta
6 6
 
7
-from discord import Message
7
+from discord import Intents, Message
8 8
 from discord.ext.commands import Cog
9 9
 
10 10
 from rocketbot.cogs.basecog import BaseCog
@@ -219,3 +219,7 @@ class VideoPreviewCog(BaseCog, name='Video Link Previews'):
219 219
 			content,
220 220
 			mention_author=False
221 221
 		)
222
+
223
+	@classmethod
224
+	def supports_intents(cls, intents: Intents) -> bool:
225
+		return intents.message_content

Загрузка…
Отмена
Сохранить