Преглед изворни кода

Adding factory method for standard BotMessageReactions. Adding bot check to avoid exceptions. Adding more logging.

tags/1.0.1
Rocketsoup пре 4 година
родитељ
комит
996d943ff8
7 измењених фајлова са 239 додато и 66 уклоњено
  1. 90
    10
      cogs/basecog.py
  2. 15
    24
      cogs/crosspostcog.py
  3. 11
    1
      cogs/generalcog.py
  4. 7
    5
      cogs/joinraidcog.py
  5. 14
    9
      cogs/patterncog.py
  6. 100
    15
      cogs/urlspamcog.py
  7. 2
    2
      rocketbot.py

+ 90
- 10
cogs/basecog.py Прегледај датотеку

2
 from discord.ext import commands
2
 from discord.ext import commands
3
 from datetime import datetime, timedelta
3
 from datetime import datetime, timedelta
4
 
4
 
5
+from abc import ABC, abstractmethod
5
 from config import CONFIG
6
 from config import CONFIG
6
 from rscollections import AgeBoundDict
7
 from rscollections import AgeBoundDict
7
 from storage import ConfigKey, Storage
8
 from storage import ConfigKey, Storage
28
 			other.is_enabled == self.is_enabled and \
29
 			other.is_enabled == self.is_enabled and \
29
 			other.description == self.description
30
 			other.description == self.description
30
 
31
 
32
+	@classmethod
33
+	def standard_set(cls,
34
+			did_delete: bool = None,
35
+			message_count: int = 1,
36
+			did_kick: bool = None,
37
+			did_ban: bool = None,
38
+			user_count: int = 1) -> list:
39
+		"""
40
+		Convenience factory for generating any of the three most common
41
+		commands: delete message(s), kick user(s), and ban user(s). All
42
+		arguments are optional. Resulting list can be passed directly to
43
+		`BotMessage.set_reactions()`.
44
+
45
+		Params
46
+		- did_delete     Whether the message(s) have been deleted. Pass True or
47
+		                 False if this applies, omit to leave out delete action.
48
+		- message_count  How many messages there are. Used for pluralizing
49
+		                 description. Defaults to 1. Omit if n/a.
50
+		- did_kick       Whether the user(s) have been kicked. Pass True or
51
+		                 False if this applies, omit to leave out kick action.
52
+		- did_ban        Whether the user(s) have been banned. Pass True or
53
+		                 False if this applies, omit to leave out ban action.
54
+		- user_count     How many users there are. Used for pluralizing
55
+		                 description. Defaults to 1. Omit if n/a.
56
+		"""
57
+		reactions = []
58
+		if did_delete is not None:
59
+			if did_delete:
60
+				reactions.append(BotMessageReaction(
61
+					CONFIG['trash_emoji'],
62
+					False,
63
+					'Message deleted' if message_count == 1 else 'Messages deleted'))
64
+			else:
65
+				reactions.append(BotMessageReaction(
66
+					CONFIG['trash_emoji'],
67
+					True,
68
+					'Delete message' if message_count == 1 else 'Delete messages'))
69
+		if did_kick is not None:
70
+			if did_ban is not None and did_ban:
71
+				# Don't show kick option at all if we also banned
72
+				pass
73
+			elif did_kick:
74
+				reactions.append(BotMessageReaction(
75
+					CONFIG['kick_emoji'],
76
+					False,
77
+					'User kicked' if user_count == 1 else 'Users kicked'))
78
+			else:
79
+				reactions.append(BotMessageReaction(
80
+					CONFIG['kick_emoji'],
81
+					True,
82
+					'Kick user' if user_count == 1 else 'Kick users'))
83
+		if did_ban is not None:
84
+			if did_ban:
85
+				reactions.append(BotMessageReaction(
86
+					CONFIG['ban_emoji'],
87
+					False,
88
+					'User banned' if user_count == 1 else 'Users banned'))
89
+			else:
90
+				reactions.append(BotMessageReaction(
91
+					CONFIG['ban_emoji'],
92
+					True,
93
+					'Ban user' if user_count == 1 else 'Ban users'))
94
+		return reactions
95
+
31
 class BotMessage:
96
 class BotMessage:
32
 	"""
97
 	"""
33
 	Holds state for a bot-generated message. A message is composed, sent via
98
 	Holds state for a bot-generated message. A message is composed, sent via
55
 		self.type = type
120
 		self.type = type
56
 		self.context = context
121
 		self.context = context
57
 		self.quote = None
122
 		self.quote = None
123
+		self.source_cog = None  # Set by `BaseCog.post_message()`
58
 		self.__posted_text = None  # last text posted, to test for changes
124
 		self.__posted_text = None  # last text posted, to test for changes
59
 		self.__posted_emoji = set()
125
 		self.__posted_emoji = set()
60
 		self.__message = None  # Message
126
 		self.__message = None  # Message
160
 			else:
226
 			else:
161
 				channel_id = Storage.get_config_value(self.guild, ConfigKey.WARNING_CHANNEL_ID)
227
 				channel_id = Storage.get_config_value(self.guild, ConfigKey.WARNING_CHANNEL_ID)
162
 				if channel_id is None:
228
 				if channel_id is None:
163
-					BaseCog.guild_trace(self.guild, 'No warning channel set! No warning issued.')
229
+					BaseCog.log(self.guild, '\u0007No warning channel set! No warning issued.')
164
 					return
230
 					return
165
 				channel: TextChannel = self.guild.get_channel(channel_id)
231
 				channel: TextChannel = self.guild.get_channel(channel_id)
166
 				if channel is None:
232
 				if channel is None:
167
-					BaseCog.guild_trace(self.guild, 'Configured warning channel does not exist!')
233
+					BaseCog.log(self.guild, '\u0007Configured warning channel does not exist!')
168
 					return
234
 					return
169
 				self.__message = await channel.send(content=content)
235
 				self.__message = await channel.send(content=content)
170
 				self.__posted_text = content
236
 				self.__posted_text = content
205
 			s += '\n\nAvailable actions:'
271
 			s += '\n\nAvailable actions:'
206
 			for reaction in self.__reactions:
272
 			for reaction in self.__reactions:
207
 				if reaction.is_enabled:
273
 				if reaction.is_enabled:
208
-					s += f'\n   {reaction.emoji} {reaction.description}'
274
+					s += f'\n     {reaction.emoji} {reaction.description}'
209
 				else:
275
 				else:
210
-					s += f'\n   {reaction.description}'
276
+					s += f'\n     {reaction.description}'
211
 
277
 
212
 		return s
278
 		return s
213
 
279
 
241
 			Storage.set_state_value(guild, 'bot_messages', bm)
307
 			Storage.set_state_value(guild, 'bot_messages', bm)
242
 		return bm
308
 		return bm
243
 
309
 
244
-	@classmethod
245
-	async def post_message(cls, message: BotMessage) -> bool:
310
+	async def post_message(self, message: BotMessage) -> bool:
311
+		message.source_cog = self
246
 		await message._update()
312
 		await message._update()
247
-		guild_messages = cls.__bot_messages(message.guild)
313
+		guild_messages = self.__bot_messages(message.guild)
248
 		if message.is_sent():
314
 		if message.is_sent():
249
 			guild_messages[message.message_id()] = message
315
 			guild_messages[message.message_id()] = message
250
 			return True
316
 			return True
279
 		if bot_message is None:
345
 		if bot_message is None:
280
 			# Unknown message (expired or was never tracked)
346
 			# Unknown message (expired or was never tracked)
281
 			return
347
 			return
348
+		if self is not bot_message.source_cog:
349
+			# Belongs to a different cog
350
+			return
282
 		reaction = bot_message.reaction_for_emoji(payload.emoji)
351
 		reaction = bot_message.reaction_for_emoji(payload.emoji)
283
 		if reaction is None or not reaction.is_enabled:
352
 		if reaction is None or not reaction.is_enabled:
284
 			# Can't use this reaction with this message
353
 			# Can't use this reaction with this message
298
 		"""
367
 		"""
299
 		pass
368
 		pass
300
 
369
 
370
+	# Helpers
371
+
301
 	@classmethod
372
 	@classmethod
302
 	async def validate_param(cls, context: commands.Context, param_name: str, value,
373
 	async def validate_param(cls, context: commands.Context, param_name: str, value,
303
 		allowed_types: tuple = None,
374
 		allowed_types: tuple = None,
330
 	@classmethod
401
 	@classmethod
331
 	async def warn(cls, guild: Guild, message: str) -> Message:
402
 	async def warn(cls, guild: Guild, message: str) -> Message:
332
 		"""
403
 		"""
404
+		DEPRECATED. Use post_message.
405
+
333
 		Sends a warning message to the configured warning channel for the
406
 		Sends a warning message to the configured warning channel for the
334
 		given guild. If no warning channel is configured no action is taken.
407
 		given guild. If no warning channel is configured no action is taken.
335
 		Returns the Message if successful or None if not.
408
 		Returns the Message if successful or None if not.
336
 		"""
409
 		"""
337
 		channel_id = Storage.get_config_value(guild, ConfigKey.WARNING_CHANNEL_ID)
410
 		channel_id = Storage.get_config_value(guild, ConfigKey.WARNING_CHANNEL_ID)
338
 		if channel_id is None:
411
 		if channel_id is None:
339
-			cls.guild_trace(guild, 'No warning channel set! No warning issued.')
412
+			cls.log(guild, '\u0007No warning channel set! No warning issued.')
340
 			return None
413
 			return None
341
 		channel: TextChannel = guild.get_channel(channel_id)
414
 		channel: TextChannel = guild.get_channel(channel_id)
342
 		if channel is None:
415
 		if channel is None:
343
-			cls.guild_trace(guild, 'Configured warning channel does not exist!')
416
+			cls.log(guild, '\u0007Configured warning channel does not exist!')
344
 			return None
417
 			return None
345
 		mention: str = Storage.get_config_value(guild, ConfigKey.WARNING_MENTION)
418
 		mention: str = Storage.get_config_value(guild, ConfigKey.WARNING_MENTION)
346
 		text: str = message
419
 		text: str = message
352
 	@classmethod
425
 	@classmethod
353
 	async def update_warn(cls, warn_message: Message, new_text: str) -> None:
426
 	async def update_warn(cls, warn_message: Message, new_text: str) -> None:
354
 		"""
427
 		"""
428
+		DEPRECATED. Use post_message.
429
+
355
 		Updates the text of a previously posted `warn`. Includes configured
430
 		Updates the text of a previously posted `warn`. Includes configured
356
 		mentions if necessary.
431
 		mentions if necessary.
357
 		"""
432
 		"""
364
 		await warn_message.edit(content=text)
439
 		await warn_message.edit(content=text)
365
 
440
 
366
 	@classmethod
441
 	@classmethod
367
-	def guild_trace(cls, guild: Guild, message: str) -> None:
442
+	def log(cls, guild: Guild, message) -> None:
443
+		now = datetime.now()
444
+		print(f'[{now.strftime("%Y-%m-%dT%H:%M:%S")}|{cls.__name__}|{guild.name}] {message}')
445
+
446
+	@classmethod
447
+	def deprecated_guild_trace(cls, guild: Guild, message: str) -> None:
368
 		print(f'[guild {guild.id}|{guild.name}] {message}')
448
 		print(f'[guild {guild.id}|{guild.name}] {message}')

+ 15
- 24
cogs/crosspostcog.py Прегледај датотеку

114
 				context.is_banned = True
114
 				context.is_banned = True
115
 				context.is_autobanned = True
115
 				context.is_autobanned = True
116
 				context.deleted_messages |= context.spam_messages
116
 				context.deleted_messages |= context.spam_messages
117
+				self.log(context.member.guild, f'Bot autobanned {context.member.name} ({context.member.id}) for spamming')
117
 			else:
118
 			else:
118
 				# Already banned. Nothing to update in the message.
119
 				# Already banned. Nothing to update in the message.
119
 				return
120
 				return
137
 		else:
138
 		else:
138
 			await message.set_text(f'User {context.member.mention} posted ' +
139
 			await message.set_text(f'User {context.member.mention} posted ' +
139
 				f'the same message {spam_count} times.')
140
 				f'the same message {spam_count} times.')
140
-			can_delete = spam_count > deleted_count
141
-			can_kick = not context.is_kicked
142
-			can_ban = not context.is_banned
143
-			await message.add_reaction(
144
-				BotMessageReaction(
145
-					CONFIG['trash_emoji'],
146
-					can_delete,
147
-					'Delete messages' if can_delete else f'Deleted {deleted_count} messages'))
148
-			if can_ban:
149
-				# Only show kick info if they can also be banned. Otherwise we say
150
-				# dumb stuff like "user was kicked, user was banned".
151
-				await message.add_reaction(
152
-					BotMessageReaction(
153
-						CONFIG['kick_emoji'],
154
-						can_kick,
155
-						'Kick user' if can_kick else 'User kicked'))
156
-			else:
157
-				await message.remove_reaction(CONFIG['kick_emoji'])
158
-			await message.add_reaction(
159
-				BotMessageReaction(
160
-					CONFIG['ban_emoji'], 
161
-					can_ban,
162
-					'Ban user' if can_ban else 'User banned'))
141
+			await message.set_reactions(BotMessageReaction.standard_set(
142
+				did_delete = deleted_count >= spam_count,
143
+				message_count = spam_count,
144
+				did_kick = context.is_kicked,
145
+				did_ban = context.is_banned))
163
 		if context.bot_message is None:
146
 		if context.bot_message is None:
164
 			await self.post_message(message)
147
 			await self.post_message(message)
165
 			context.bot_message = message
148
 			context.bot_message = message
169
 			await message.delete()
152
 			await message.delete()
170
 			context.deleted_messages.add(message)
153
 			context.deleted_messages.add(message)
171
 		await self.__update_from_context(context)
154
 		await self.__update_from_context(context)
155
+		self.log(context.member.guild, f'Mod deleted messages from {context.member.name} ({context.member.id})')
172
 
156
 
173
 	async def __kick(self, context: SpamContext) -> None:
157
 	async def __kick(self, context: SpamContext) -> None:
174
 		await context.member.kick(reason='Posting same message repeatedly')
158
 		await context.member.kick(reason='Posting same message repeatedly')
175
 		context.is_kicked = True
159
 		context.is_kicked = True
176
 		await self.__update_from_context(context)
160
 		await self.__update_from_context(context)
161
+		self.log(context.member.guild, f'Mod kicked user {context.member.name} ({context.member.id})')
177
 
162
 
178
 	async def __ban(self, context: SpamContext) -> None:
163
 	async def __ban(self, context: SpamContext) -> None:
179
 		await context.member.ban(reason='Posting same message repeatedly', delete_message_days=1)
164
 		await context.member.ban(reason='Posting same message repeatedly', delete_message_days=1)
181
 		context.is_kicked = True
166
 		context.is_kicked = True
182
 		context.is_banned = True
167
 		context.is_banned = True
183
 		await self.__update_from_context(context)
168
 		await self.__update_from_context(context)
169
+		self.log(context.member.guild, f'Mod banned user {context.member.name} ({context.member.id})')
184
 
170
 
185
 	async def on_mod_react(self,
171
 	async def on_mod_react(self,
186
 			bot_message: BotMessage,
172
 			bot_message: BotMessage,
199
 
185
 
200
 	@commands.Cog.listener()
186
 	@commands.Cog.listener()
201
 	async def on_message(self, message: Message):
187
 	async def on_message(self, message: Message):
202
-		if message.author is None or message.channel is None or message.guild is None:
188
+		if message.author is None or \
189
+				message.author.bot or \
190
+				message.channel is None or \
191
+				message.guild is None or \
192
+				message.content is None or \
193
+				message.content == '':
203
 			return
194
 			return
204
 		await self.__record_message(message)
195
 		await self.__record_message(message)
205
 
196
 

+ 11
- 1
cogs/generalcog.py Прегледај датотеку

32
 			await self.warn(context.guild,
32
 			await self.warn(context.guild,
33
 				f'Test warning message (requested by {context.author.name})')
33
 				f'Test warning message (requested by {context.author.name})')
34
 
34
 
35
-	@commands.command()
35
+	@commands.command(
36
+		brief='Simple test reply',
37
+	)
36
 	async def hello(self, context):
38
 	async def hello(self, context):
37
 		await context.message.reply(
39
 		await context.message.reply(
38
 			f'Hey, {context.author.name}!',
40
 			f'Hey, {context.author.name}!',
39
 		 	mention_author=False)
41
 		 	mention_author=False)
42
+
43
+	@commands.command(
44
+		brief='Shuts down the bot (admin only)',
45
+	)
46
+	@commands.has_permissions(administrator=True)
47
+	@commands.guild_only()
48
+	async def shutdown(self, context):
49
+		await self.bot.close()

+ 7
- 5
cogs/joinraidcog.py Прегледај датотеку

97
 			if self.phase == RaidPhase.NONE:
97
 			if self.phase == RaidPhase.NONE:
98
 				self.phase = RaidPhase.JUST_STARTED
98
 				self.phase = RaidPhase.JUST_STARTED
99
 				self.raid_start_time = now
99
 				self.raid_start_time = now
100
-				print('\u0007Join raid started!')
101
 			elif self.phase == RaidPhase.JUST_STARTED:
100
 			elif self.phase == RaidPhase.JUST_STARTED:
102
 				self.phase = RaidPhase.CONTINUING
101
 				self.phase = RaidPhase.CONTINUING
103
-				print(f'\u0007Join raid up to {recent_count} people')
104
 		elif self.phase == self.phase in (RaidPhase.JUST_STARTED, RaidPhase.CONTINUING):
102
 		elif self.phase == self.phase in (RaidPhase.JUST_STARTED, RaidPhase.CONTINUING):
105
 			self.phase = RaidPhase.ENDED
103
 			self.phase = RaidPhase.ENDED
106
-			print('Previous join raid ended')
107
 
104
 
108
 		# Undo join add if the raid is over
105
 		# Undo join add if the raid is over
109
 		if self.phase == RaidPhase.ENDED and len(self.joins) > 0:
106
 		if self.phase == RaidPhase.ENDED and len(self.joins) > 0:
117
 		who were newly kicked.
114
 		who were newly kicked.
118
 		"""
115
 		"""
119
 		kicks = []
116
 		kicks = []
117
+		guild = None
120
 		for join in self.joins:
118
 		for join in self.joins:
119
+			guild = join.member.guild
121
 			if join.is_kicked or join.is_banned:
120
 			if join.is_kicked or join.is_banned:
122
 				continue
121
 				continue
123
 			await join.member.kick(reason=reason)
122
 			await join.member.kick(reason=reason)
125
 			kicks.append(join.member)
124
 			kicks.append(join.member)
126
 		self.phase = RaidPhase.ENDED
125
 		self.phase = RaidPhase.ENDED
127
 		if len(kicks) > 0:
126
 		if len(kicks) > 0:
128
-			print(f'Kicked {len(kicks)} people')
127
+			self.log(guild, f'Mod kicked {len(kicks)} people')
129
 		return kicks
128
 		return kicks
130
 
129
 
131
 	async def ban_all(self,
130
 	async def ban_all(self,
137
 		still be banned. Returns a List of Members who were newly banned.
136
 		still be banned. Returns a List of Members who were newly banned.
138
 		"""
137
 		"""
139
 		bans = []
138
 		bans = []
139
+		guild = None
140
 		for join in self.joins:
140
 		for join in self.joins:
141
+			guild = join.member.guild
141
 			if join.is_banned:
142
 			if join.is_banned:
142
 				continue
143
 				continue
143
 			await join.member.ban(
144
 			await join.member.ban(
147
 			bans.append(join.member)
148
 			bans.append(join.member)
148
 		self.phase = RaidPhase.ENDED
149
 		self.phase = RaidPhase.ENDED
149
 		if len(bans) > 0:
150
 		if len(bans) > 0:
150
-			print(f'Banned {len(bans)} people')
151
+			self.log(guild, f'Mod banned {len(bans)} people')
151
 		return bans
152
 		return bans
152
 
153
 
153
 class GuildContext:
154
 class GuildContext:
452
 			await raid.warning_message.add_reaction(CONFIG['kick_emoji'])
453
 			await raid.warning_message.add_reaction(CONFIG['kick_emoji'])
453
 		if can_ban:
454
 		if can_ban:
454
 			await raid.warning_message.add_reaction(CONFIG['ban_emoji'])
455
 			await raid.warning_message.add_reaction(CONFIG['ban_emoji'])
456
+		self.log(guild, f'New join raid detected!')
455
 
457
 
456
 	async def __update_raid_warning(self, guild: Guild, raid: JoinRaidRecord) -> None:
458
 	async def __update_raid_warning(self, guild: Guild, raid: JoinRaidRecord) -> None:
457
 		"""
459
 		"""

+ 14
- 9
cogs/patterncog.py Прегледај датотеку

72
 
72
 
73
 	@commands.Cog.listener()
73
 	@commands.Cog.listener()
74
 	async def on_message(self, message: Message) -> None:
74
 	async def on_message(self, message: Message) -> None:
75
-		if message.guild is None or message.content is None or message.channel is None:
76
-			return
77
-		if message.author.id == self.bot.user.id:
78
-			# Ignore self
75
+		if message.author is None or \
76
+				message.author.bot or \
77
+				message.channel is None or \
78
+				message.guild is None or \
79
+				message.content is None or \
80
+				message.content == '':
79
 			return
81
 			return
80
 		if message.author.permissions_in(message.channel).ban_members:
82
 		if message.author.permissions_in(message.channel).ban_members:
81
 			# Ignore mods
83
 			# Ignore mods
83
 		patterns = self.__patterns(message.guild)
85
 		patterns = self.__patterns(message.guild)
84
 		for pattern in patterns:
86
 		for pattern in patterns:
85
 			if pattern.matches(message):
87
 			if pattern.matches(message):
86
-				msg = None
88
+				text = None
87
 				if pattern.action == 'delete':
89
 				if pattern.action == 'delete':
88
 					await message.delete()
90
 					await message.delete()
89
-					msg = f'Message from {message.author.mention} matched ' + \
91
+					text = f'Message from {message.author.mention} matched ' + \
90
 						'banned pattern. Deleted.'
92
 						'banned pattern. Deleted.'
93
+					self.log(message.guild, 'Message matched pattern. Deleted.')
91
 				elif pattern.action == 'kick':
94
 				elif pattern.action == 'kick':
92
 					await message.delete()
95
 					await message.delete()
93
 					await message.author.kick(reason='Rocketbot: Message matched banned pattern')
96
 					await message.author.kick(reason='Rocketbot: Message matched banned pattern')
94
-					msg = f'Message from {message.author.mention} matched ' + \
97
+					text = f'Message from {message.author.mention} matched ' + \
95
 						'banned pattern. Message deleted and user kicked.'
98
 						'banned pattern. Message deleted and user kicked.'
99
+					self.log(message.guild, 'Message matched pattern. Kicked user.')
96
 				elif pattern.action == 'ban':
100
 				elif pattern.action == 'ban':
97
 					await message.delete()
101
 					await message.delete()
98
 					await message.author.ban(reason='Rocketbot: Message matched banned pattern')
102
 					await message.author.ban(reason='Rocketbot: Message matched banned pattern')
99
-					msg = f'Message from {message.author.mention} matched ' + \
103
+					text = f'Message from {message.author.mention} matched ' + \
100
 						'banned pattern. Message deleted and user banned.'
104
 						'banned pattern. Message deleted and user banned.'
101
-				if msg:
105
+					self.log(message.guild, 'Message matched pattern. Banned user.')
106
+				if text:
102
 					m = BotMessage(message.guild,
107
 					m = BotMessage(message.guild,
103
 						text = msg,
108
 						text = msg,
104
 						type = BotMessage.TYPE_MOD_WARNING)
109
 						type = BotMessage.TYPE_MOD_WARNING)

+ 100
- 15
cogs/urlspamcog.py Прегледај датотеку

1
-from discord import Guild, Message
1
+from discord import Guild, Member, Message
2
 from discord.ext import commands
2
 from discord.ext import commands
3
 import re
3
 import re
4
 from datetime import timedelta
4
 from datetime import timedelta
5
 
5
 
6
-from cogs.basecog import BaseCog
6
+from cogs.basecog import BaseCog, BotMessage, BotMessageReaction
7
 from config import CONFIG
7
 from config import CONFIG
8
 from storage import Storage
8
 from storage import Storage
9
 
9
 
10
+class URLSpamContext:
11
+	def __init__(self, spam_message: Message):
12
+		self.spam_message = spam_message
13
+		self.is_deleted = False
14
+		self.is_kicked = False
15
+		self.is_banned = False
16
+
10
 class URLSpamCog(BaseCog):
17
 class URLSpamCog(BaseCog):
11
 	CONFIG_KEY_EARLY_URL_TIMEOUT = "urlspam_early_url_timeout"
18
 	CONFIG_KEY_EARLY_URL_TIMEOUT = "urlspam_early_url_timeout"
12
 	CONFIG_KEY_EARLY_URL_ACTION = "urlspam_early_url_action"
19
 	CONFIG_KEY_EARLY_URL_ACTION = "urlspam_early_url_action"
24
 
31
 
25
 	@commands.Cog.listener()
32
 	@commands.Cog.listener()
26
 	async def on_message(self, message: Message):
33
 	async def on_message(self, message: Message):
27
-		if message.guild is None or message.channel is None:
28
-			# DM or something
34
+		if message.author is None or \
35
+				message.author.bot or \
36
+				message.guild is None or \
37
+				message.channel is None or \
38
+				message.content is None:
29
 			return
39
 			return
40
+
30
 		action = self.__early_url_action(message.guild)
41
 		action = self.__early_url_action(message.guild)
31
 		if action == 'nothing':
42
 		if action == 'nothing':
32
 			return
43
 			return
33
-		if message.author.permissions_in(message.channel).ban_members:
34
-			# Mods are exempt
35
-			return
36
 		if not self.__contains_url(message.content):
44
 		if not self.__contains_url(message.content):
37
 			return
45
 			return
38
 		join_age = message.created_at - message.author.joined_at
46
 		join_age = message.created_at - message.author.joined_at
39
 		join_age_str = self.__format_timedelta(join_age)
47
 		join_age_str = self.__format_timedelta(join_age)
40
 		if join_age.total_seconds() < self.__early_url_timeout(message.guild):
48
 		if join_age.total_seconds() < self.__early_url_timeout(message.guild):
41
 			if action == 'modwarn':
49
 			if action == 'modwarn':
42
-				await self.warn(message.guild, f'User {message.author.mention} ' +
43
-					f'posted a URL {join_age_str} after joining.\n\n> {message.content}')
44
-				# TODO: Emoji actions
50
+				bm = BotMessage(
51
+					message.guild,
52
+					f'User {message.author.mention} posted a URL ' + \
53
+					f'{join_age_str} after joining.',
54
+					type = BotMessage.TYPE_MOD_WARNING,
55
+					context = URLSpamContext(message))
56
+				bm.quote = message.content
57
+				await bm.add_reaction(BotMessageReaction(CONFIG['trash_emoji'], True, 'Delete message'))
58
+				await bm.add_reaction(BotMessageReaction(CONFIG['kick_emoji'], True, 'Kick user'))
59
+				await bm.add_reaction(BotMessageReaction(CONFIG['ban_emoji'], True, 'Ban user'))
60
+				await self.post_message(bm)
61
+				self.log(message.guild, f'New user {message.author.name} ' + \
62
+					f'({message.author.id}) posted URL. Mods alerted.')
45
 			elif action == 'delete':
63
 			elif action == 'delete':
46
 				await message.delete()
64
 				await message.delete()
47
-				# TODO: Info to mods
65
+				bm = BotMessage(
66
+					message.guild,
67
+					f'User {message.author.mention} posted a URL ' + \
68
+					f'{join_age_str} after joining. Message deleted.',
69
+					type = BotMessage.TYPE_INFO,
70
+					context = URLSpamContext(message))
71
+				bm.quote = message.content
72
+				await bm.add_reaction(BotMessageReaction(CONFIG['kick_emoji'], True, 'Kick user'))
73
+				await bm.add_reaction(BotMessageReaction(CONFIG['ban_emoji'], True, 'Ban user'))
74
+				await self.post_message(bm)
75
+				self.log(message.guild, f'New user {message.author.name} ' + \
76
+					f'({message.author.id}) posted URL. Message deleted.')
48
 			elif action == 'kick':
77
 			elif action == 'kick':
49
-				await message.author.kick(reason=f'User posted a link {join_age_str} after joining')
50
-				# TODO: Info to mods
78
+				await message.author.kick(reason='User posted a link ' + \
79
+					f'{join_age_str} after joining')
80
+				bm = BotMessage(
81
+					message.guild,
82
+					f'User {message.author.mention} posted a URL ' + \
83
+					f'{join_age_str} after joining. Kicked by bot.',
84
+					type = BotMessage.TYPE_INFO,
85
+					context = URLSpamContext(message))
86
+				bm.quote = message.content
87
+				await bm.add_reaction(BotMessageReaction(CONFIG['ban_emoji'], True, 'Ban user'))
88
+				await self.post_message(bm)
89
+				self.log(message.guild, f'New user {message.author.name} ' + \
90
+					f'({message.author.id}) posted URL. User kicked.')
51
 			elif action == 'ban':
91
 			elif action == 'ban':
52
-				await message.author.ban(reason=f'User posted a link {join_age_str} after joining', delete_message_days=1)
53
-				# TODO: Info to mods
92
+				await message.author.ban(reason='User posted a link ' + \
93
+					f'{join_age_str} after joining', delete_message_days=1)
94
+				bm = BotMessage(
95
+					message.guild,
96
+					f'User {message.author.mention} posted a URL ' + \
97
+					f'{join_age_str} after joining. Banned by bot.',
98
+					type = BotMessage.TYPE_INFO,
99
+					context = URLSpamContext(message))
100
+				bm.quote = message.content
101
+				await self.post_message(bm)
102
+				self.log(message.guild, f'New user {message.author.name} ' + \
103
+					f'({message.author.id}) posted URL. User banned.')
104
+
105
+	async def on_mod_react(self,
106
+			bot_message: BotMessage,
107
+			reaction: BotMessageReaction,
108
+			reacted_by: Member) -> None:
109
+		context: URLSpamContext = bot_message.context
110
+		if context is None:
111
+			return
112
+		sm: Message = context.spam_message
113
+		if reaction.emoji == CONFIG['trash_emoji']:
114
+			if not context.is_deleted:
115
+				await sm.delete()
116
+				context.is_deleted = True
117
+				self.log(sm.guild, f'URL spam by {sm.author.name} deleted by {reacted_by.name}')
118
+		elif reaction.emoji == CONFIG['kick_emoji']:
119
+			if not context.is_deleted:
120
+				await sm.delete()
121
+				context.is_deleted = True
122
+			if not context.is_kicked:
123
+				await sm.author.kick(reason=f'Rocketbot: Kicked for URL spam by {reacted_by.name}')
124
+				context.is_kicked = True
125
+				self.log(sm.guild, f'URL spammer {sm.author.name} kicked by {reacted_by.name}')
126
+		elif reaction.emoji == CONFIG['ban_emoji']:
127
+			if not context.is_banned:
128
+				await sm.author.ban(reason=f'Rocketbot: Banned for URL spam by {reacted_by.name}', delete_message_days=1)
129
+				context.is_deleted = True
130
+				context.is_kicked = True
131
+				context.is_banned = True
132
+				self.log(sm.guild, f'URL spammer {sm.author.name} banned by {reacted_by.name}')
133
+		else:
134
+			return
135
+		await bot_message.set_reactions(BotMessageReaction.standard_set(
136
+			did_delete=context.is_deleted,
137
+			did_kick=context.is_kicked,
138
+			did_ban=context.is_banned))
54
 
139
 
55
 	def __contains_url(self, text: str) -> bool:
140
 	def __contains_url(self, text: str) -> bool:
56
 		p = re.compile(r'http(?:s)?://[^\s]+')
141
 		p = re.compile(r'http(?:s)?://[^\s]+')

+ 2
- 2
rocketbot.py Прегледај датотеку

14
 from cogs.generalcog import GeneralCog
14
 from cogs.generalcog import GeneralCog
15
 from cogs.joinraidcog import JoinRaidCog
15
 from cogs.joinraidcog import JoinRaidCog
16
 from cogs.patterncog import PatternCog
16
 from cogs.patterncog import PatternCog
17
-# from cogs.urlspamcog import URLSpamCog
17
+from cogs.urlspamcog import URLSpamCog
18
 
18
 
19
 CURRENT_CONFIG_VERSION = 1
19
 CURRENT_CONFIG_VERSION = 1
20
 if (CONFIG.get('__config_version') or 0) < CURRENT_CONFIG_VERSION:
20
 if (CONFIG.get('__config_version') or 0) < CURRENT_CONFIG_VERSION:
35
 bot.add_cog(JoinRaidCog(bot))
35
 bot.add_cog(JoinRaidCog(bot))
36
 bot.add_cog(CrossPostCog(bot))
36
 bot.add_cog(CrossPostCog(bot))
37
 bot.add_cog(PatternCog(bot))
37
 bot.add_cog(PatternCog(bot))
38
-# bot.add_cog(URLSpamCog(bot))
38
+bot.add_cog(URLSpamCog(bot))
39
 bot.run(CONFIG['client_token'], bot=True, reconnect=True)
39
 bot.run(CONFIG['client_token'], bot=True, reconnect=True)
40
 print('\nBot aborted')
40
 print('\nBot aborted')

Loading…
Откажи
Сачувај