Browse Source

Edits and deletions now attachment and embed aware. Edits show diffs. Better startup logging.

master
Rocketsoup 1 year ago
parent
commit
980231d09c
3 changed files with 116 additions and 14 deletions
  1. 4
    3
      bot.py
  2. 13
    4
      rocketbot/cogs/generalcog.py
  3. 99
    7
      rocketbot/cogs/logcog.py

+ 4
- 3
bot.py View File

@@ -22,6 +22,7 @@ from rocketbot.cogs.logcog import LoggingCog
22 22
 from rocketbot.cogs.patterncog import PatternCog
23 23
 from rocketbot.cogs.urlspamcog import URLSpamCog
24 24
 from rocketbot.cogs.usernamecog import UsernamePatternCog
25
+from rocketbot.utils import bot_log
25 26
 
26 27
 CURRENT_CONFIG_VERSION = 3
27 28
 if (CONFIG.get('__config_version') or 0) < CURRENT_CONFIG_VERSION:
@@ -72,8 +73,8 @@ async def start_bot():
72 73
 	intents.message_content = True  # pylint: disable=assigning-non-slot
73 74
 	intents.members = True  # pylint: disable=assigning-non-slot
74 75
 	intents.presences = True
75
-	print(f"Intents are {intents}")
76
-	print(f"Command prefix is {CONFIG['command_prefix']}")
76
+	bot_log(None, None, 'Bot initializing...')
77
+	bot_log(None, None, f"Type {CONFIG['command_prefix']}help in Discord for available commands.")
77 78
 	bot = Rocketbot(command_prefix=CONFIG['command_prefix'], intents=intents)
78 79
 
79 80
 	# Core
@@ -96,4 +97,4 @@ async def start_bot():
96 97
 try:
97 98
 	asyncio.run(start_bot())
98 99
 except KeyboardInterrupt:
99
-	pass
100
+	bot_log(None, None, 'Stopping bot due to Ctrl+C key')

+ 13
- 4
rocketbot/cogs/generalcog.py View File

@@ -21,28 +21,37 @@ class GeneralCog(BaseCog, name='General'):
21 21
 		super().__init__(bot)
22 22
 		self.is_connected = False
23 23
 		self.is_ready = False
24
+		self.is_first_ready = True
25
+		self.is_first_connect = True
24 26
 
25 27
 	@commands.Cog.listener()
26 28
 	async def on_connect(self):
27 29
 		'Event handler'
28
-		print('on_connect')
30
+		if self.is_first_connect:
31
+			self.log(None, 'Connected')
32
+			self.is_first_connect = False
33
+		else:
34
+			self.log(None, 'Reconnected')
29 35
 		self.is_connected = True
30 36
 
31 37
 	@commands.Cog.listener()
32 38
 	async def on_disconnect(self):
33 39
 		'Event handler'
34
-		print('on_disconnect')
40
+		self.log(None, 'Disconnected')
35 41
 
36 42
 	@commands.Cog.listener()
37 43
 	async def on_ready(self):
38 44
 		'Event handler'
39
-		print('on_ready')
45
+		self.log(None, 'Bot done initializing')
40 46
 		self.is_ready = True
47
+		if self.is_first_ready:
48
+			print('----------------------------------------------------------')
49
+			self.is_first_ready = False
41 50
 
42 51
 	@commands.Cog.listener()
43 52
 	async def on_resumed(self):
44 53
 		'Event handler'
45
-		print('on_resumed')
54
+		self.log(None, 'Session resumed')
46 55
 
47 56
 	@commands.command(
48 57
 		brief='Posts a test warning',

+ 99
- 7
rocketbot/cogs/logcog.py View File

@@ -8,7 +8,8 @@ from discord import AuditLogAction, AuditLogEntry, Emoji, Guild, GuildSticker, I
8 8
 from discord.abc import GuildChannel
9 9
 from discord.ext import commands
10 10
 from discord.utils import escape_markdown
11
-from typing import List, Optional, Union
11
+from typing import List, Optional, Tuple, Union
12
+import difflib
12 13
 import traceback
13 14
 
14 15
 from config import CONFIG
@@ -451,12 +452,54 @@ class LoggingCog(BaseCog, name='Logging'):
451 452
 			return
452 453
 		if after.author.id == self.bot.user.id:
453 454
 			return
454
-		if after.content == before.content:
455
-			# Most likely an embed being updated
455
+
456
+		content_changed = (after.content != before.content)
457
+		attachments_changed = (after.attachments != before.attachments)
458
+		embeds_changed = (after.embeds != before.embeds)
459
+		embeds_add_only = len(before.embeds or []) == 0 and len(after.embeds or []) > 0
460
+
461
+		if not content_changed and not attachments_changed and (not embeds_changed or embeds_add_only):
462
+			# Most likely an embed being asynchronously populated by server
456 463
 			return
464
+		if content_changed:
465
+			(before_markdown, after_markdown) = self.__diff(self.__quote_markdown(before.content), \
466
+													  self.__quote_markdown(after.content))
467
+		else:
468
+			before_markdown = self.__quote_markdown(before.content)
469
+			after_markdown = before_markdown if len(before.content.strip()) == 0 else '> _<content unchanged>_'
470
+		if attachments_changed:
471
+			if len(before.attachments or []) > 0:
472
+				for attachment in before.attachments:
473
+					before_markdown += f'\n> * 📎 {attachment.url}'
474
+					if attachment not in after.attachments or []:
475
+						before_markdown += ' (removed)'
476
+			else:
477
+				before_markdown += '\n> * _<no attachments>_'
478
+			if len(after.attachments or []) > 0:
479
+				for attachment in after.attachments:
480
+					after_markdown += f'\n> * 📎 {attachment.url}'
481
+					if attachment not in before.attachments or []:
482
+						after_markdown += ' (added)'
483
+			else:
484
+				after_markdown += '\n> * _<no attachments>_'
485
+		if embeds_changed:
486
+			if len(before.embeds or []) > 0:
487
+				for embed in before.embeds:
488
+					before_markdown += f'\n> * 🔗 {embed.url}'
489
+					if embed not in after.embeds or []:
490
+						before_markdown += ' (removed)'
491
+			else:
492
+				before_markdown += '\n> * _<no embeds>_'
493
+			if len(after.embeds or []) > 0:
494
+				for embed in after.embeds:
495
+					after_markdown += f'\n> * 🔗 {embed.url}'
496
+					if embed not in before.embeds or []:
497
+						after_markdown += ' (added)'
498
+			else:
499
+				after_markdown += '\n> * _<no embeds>_'
457 500
 		text = f'Message {after.jump_url} edited by {self.__describe_user(after.author)}.\n' + \
458
-			f'Original markdown:\n{self.__quote_markdown(before.content)}\n' + \
459
-			f'Updated markdown:\n{self.__quote_markdown(after.content)}'
501
+			f'Original:\n{before_markdown}\n' + \
502
+			f'Updated:\n{after_markdown}'
460 503
 		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG, suppress_embeds=True)
461 504
 		await bot_message.update()
462 505
 
@@ -523,6 +566,10 @@ class LoggingCog(BaseCog, name='Logging'):
523 566
 				return
524 567
 			text = f'Message by {self.__describe_user(message.author)} deleted from {message.channel.mention}. ' + \
525 568
 				f'Markdown:\n{self.__quote_markdown(message.content)}'
569
+			for attachment in message.attachments or []:
570
+				text += f'\n> * 📎 {attachment.url}'
571
+			for embed in message.embeds or []:
572
+				text += f'\n> * 🔗 {embed.url}'
526 573
 			bot_message = BotMessage(message.guild, text, BotMessage.TYPE_LOG, suppress_embeds=True)
527 574
 			await bot_message.update()
528 575
 		else:
@@ -564,13 +611,17 @@ class LoggingCog(BaseCog, name='Logging'):
564 611
 			text += f' No cached content available for any of them.'
565 612
 		elif uncached_count > 0:
566 613
 			text += f' No cached content available for {uncached_count} of them.'
567
-		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
614
+		bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG, suppress_embeds=True)
568 615
 		await bot_message.update()
569 616
 
570 617
 		for message in payload.cached_messages:
571 618
 			text = f'Message by {self.__describe_user(message.author)} bulk deleted from {message.channel.mention}. ' + \
572 619
 				f'Markdown:\n{self.__quote_markdown(message.content)}'
573
-			bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG)
620
+			for attachment in message.attachments or []:
621
+				text += f'\n> * 📎 {attachment.url}'
622
+			for embed in message.embeds or []:
623
+				text += f'\n> * 🔗 {embed.url}'
624
+			bot_message = BotMessage(guild, text, BotMessage.TYPE_LOG, suppress_embeds=True)
574 625
 			await bot_message.update()
575 626
 
576 627
 	# Events - Roles
@@ -665,6 +716,8 @@ class LoggingCog(BaseCog, name='Logging'):
665 716
 
666 717
 	# ------------------------------------------------------------------------
667 718
 	def __quote_markdown(self, s: str) -> str:
719
+		if len(s.strip()) == 0:
720
+			return '> _<no content>_'
668 721
 		return '> ' + escape_markdown(s).replace('\n', '\n> ')
669 722
 
670 723
 	def __describe_user(self, user: Union[User, Member]) -> str:
@@ -672,3 +725,42 @@ class LoggingCog(BaseCog, name='Logging'):
672 725
 		Standardized markdown describing a user or member.
673 726
 		"""
674 727
 		return f'**{user.name}** ({user.display_name} {user.id})'
728
+
729
+	def __diff(self, a: str, b: str) -> Tuple[str, str]:
730
+		deletion_start = '~~'
731
+		deletion_end = '~~'
732
+		addition_start = '**'
733
+		addition_end = '**'
734
+		markdown_a = ''
735
+		markdown_b = ''
736
+		a_open = False
737
+		b_open = False
738
+		# FIXME: Handle URLs better. They get mangled.
739
+		# URL regex: http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+
740
+		for i, s in enumerate(difflib.ndiff(a, b)):
741
+			operation = s[0]
742
+			content = s[2:]
743
+			if operation != '-' and a_open:
744
+				markdown_a += deletion_end
745
+				a_open = False
746
+			if operation != '+' and b_open:
747
+				markdown_b += addition_end
748
+				b_open = False
749
+			if operation == ' ':
750
+				markdown_a += content
751
+				markdown_b += content
752
+			elif operation == '-':
753
+				if not a_open:
754
+					markdown_a += deletion_start
755
+					a_open = True
756
+				markdown_a += content
757
+			elif operation == '+':
758
+				if not b_open:
759
+					markdown_b += addition_start
760
+					b_open = True
761
+				markdown_b += content
762
+		if a_open:
763
+			markdown_a += deletion_end
764
+		if b_open:
765
+			markdown_b += addition_end
766
+		return (markdown_a, markdown_b)

Loading…
Cancel
Save