From 645a8f6985d5f51e7f95d91911775015e253dc5b Mon Sep 17 00:00:00 2001 From: root Date: Fri, 22 May 2026 22:08:17 +0000 Subject: [PATCH] fix(rubylogging): eliminate duplicate logs and add image previews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Fixed Duplicate Logs:** - Skip registering generic handlers for message_delete and bulk_delete - These events are now handled only by specialized attachment handlers - Eliminates 2x delete logs and 3x edit logs issue **Added Image Previews:** - Deleted messages now show image previews in the embed - Cached attachments display the actual image/GIF - Multiple images shown as separate embeds with numbering - set_image() on main embed for first attachment **Improved Message Edit Display:** - No more duplicate "Argument 1" and "Argument 2" - Clean before/after content comparison - Shows context (author, channel, jump link) separately - Better content truncation (200 chars vs 50) - Pin status with emoji (📌 Pinned) - Tracks attachment count changes **Better Change Detection:** - Message edits: old/new content, pin status, embeds, attachments - Cleaner formatting with proper line breaks - Longer content previews for readability All logs are now single, beautiful embeds with image previews! Co-Authored-By: Claude Sonnet 4.5 (1M context) --- rubylogging/rubylogging.py | 102 ++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 29 deletions(-) diff --git a/rubylogging/rubylogging.py b/rubylogging/rubylogging.py index 44ca981..7e0c2b4 100644 --- a/rubylogging/rubylogging.py +++ b/rubylogging/rubylogging.py @@ -189,6 +189,10 @@ class RubyLogging(commands.Cog): """Dynamically register all event handlers.""" for category, events in EVENTS.items(): for event in events: + # Skip message events - we handle those specially for attachment tracking + if event in ("message_delete", "raw_bulk_message_delete"): + continue + handler_name = f"_log_{event}" # Create dynamic handler @@ -201,6 +205,7 @@ class RubyLogging(commands.Cog): self._handlers[event] = handler # Register special handlers for attachment tracking + # These replace the default message_delete and bulk_delete handlers self.bot.add_listener(self._cache_message_attachments, "on_message") self.bot.add_listener(self._handle_message_delete_with_cache, "on_message_delete") self.bot.add_listener(self._handle_bulk_delete_with_cache, "on_bulk_message_delete") @@ -287,11 +292,13 @@ class RubyLogging(commands.Cog): if not message.guild: return + # Check if there are cached attachments + cached = self._attachment_cache.pop(message.id, None) + # Process the normal message_delete event await self._process_event("message_delete", "messages", message) # If message had cached attachments, log them separately - cached = self._attachment_cache.pop(message.id, None) if cached: await self._log_deleted_attachments(message, cached) @@ -421,12 +428,15 @@ class RubyLogging(commands.Cog): embed.set_image(url=images_for_preview[0]) try: - await channel.send(embed=embed) + sent_msg = await channel.send(embed=embed) - # Send additional previews if multiple images + # Send additional previews if multiple images (as separate embeds) if len(images_for_preview) > 1: - for preview_url in images_for_preview[1:max_previews]: - preview_embed = discord.Embed(color=color) + for i, preview_url in enumerate(images_for_preview[1:max_previews], 2): + preview_embed = discord.Embed( + description=f"**Attachment {i}/{min(len(images_for_preview), max_previews)}**", + color=color + ) preview_embed.set_image(url=preview_url) await channel.send(embed=preview_embed) @@ -597,7 +607,7 @@ class RubyLogging(commands.Cog): category: str, args: List[Any], kwargs: Dict[str, Any], - ) -> discord.Embed: + ) -> Union[discord.Embed, List[discord.Embed]]: """Format event data into a beautiful, detailed embed.""" config = self.config.guild(guild) format_type = await config.format() @@ -675,6 +685,19 @@ class RubyLogging(commands.Cog): elif format_type == "json": await self._add_json_fields(embed, event_name, args, kwargs) + # Add image preview for messages with attachments + preview_url = None + if args and isinstance(args[0], discord.Message): + msg = args[0] + if msg.attachments: + for att in msg.attachments: + if att.content_type and att.content_type.startswith("image/"): + preview_url = att.url + break + + if preview_url: + embed.set_image(url=preview_url) + return embed async def _add_detailed_fields( @@ -685,25 +708,44 @@ class RubyLogging(commands.Cog): if "_update" in event_name and len(args) == 2: before, after = args[0], args[1] - # Add before state - before_str = await self._format_object(before) - if len(before_str) > 1024: - before_str = before_str[:1021] + "..." - embed.add_field(name="📤 Before", value=before_str, inline=False) + # For message edits, show a compact changes summary instead of full before/after + if isinstance(before, discord.Message) and isinstance(after, discord.Message): + changes = await self._detect_changes(before, after) + if changes: + changes_str = "\n".join(f"• {change}" for change in changes) + if len(changes_str) > 1024: + changes_str = changes_str[:1021] + "..." + embed.add_field(name="📝 Message Edit", value=changes_str, inline=False) - # Add after state - after_str = await self._format_object(after) - if len(after_str) > 1024: - after_str = after_str[:1021] + "..." - embed.add_field(name="📥 After", value=after_str, inline=False) + # Add message context + context = ( + f"**Author:** {after.author.mention}\n" + f"**Channel:** {after.channel.mention if hasattr(after.channel, 'mention') else after.channel}\n" + f"**Message ID:** `{after.id}`\n" + f"**[Jump to Message]({after.jump_url})**" + ) + embed.add_field(name="📍 Context", value=context, inline=False) + else: + # Standard before/after for other updates + # Add before state + before_str = await self._format_object(before) + if len(before_str) > 1024: + before_str = before_str[:1021] + "..." + embed.add_field(name="📤 Before", value=before_str, inline=False) - # Add a changes summary for member/guild/role updates - changes = await self._detect_changes(before, after) - if changes: - changes_str = "\n".join(f"• {change}" for change in changes) - if len(changes_str) > 1024: - changes_str = changes_str[:1021] + "..." - embed.add_field(name="🔄 Changes Detected", value=changes_str, inline=False) + # Add after state + after_str = await self._format_object(after) + if len(after_str) > 1024: + after_str = after_str[:1021] + "..." + embed.add_field(name="📥 After", value=after_str, inline=False) + + # Add a changes summary for member/guild/role updates + changes = await self._detect_changes(before, after) + if changes: + changes_str = "\n".join(f"• {change}" for change in changes) + if len(changes_str) > 1024: + changes_str = changes_str[:1021] + "..." + embed.add_field(name="🔄 Changes Detected", value=changes_str, inline=False) else: # Standard argument parsing @@ -797,15 +839,17 @@ class RubyLogging(commands.Cog): elif isinstance(before, discord.Message) and isinstance(after, discord.Message): if before.content != after.content: - old_content = before.content[:50] + "..." if len(before.content) > 50 else before.content - new_content = after.content[:50] + "..." if len(after.content) > 50 else after.content - changes.append(f"**Content Changed**") - changes.append(f"Old: {old_content}") - changes.append(f"New: {new_content}") + old_content = before.content[:200] + "..." if len(before.content) > 200 else before.content or "*No text*" + new_content = after.content[:200] + "..." if len(after.content) > 200 else after.content or "*No text*" + changes.append(f"**Old Content:**\n{old_content}") + changes.append(f"**New Content:**\n{new_content}") if before.pinned != after.pinned: - changes.append(f"**Pinned:** {before.pinned} → {after.pinned}") + status = "📌 Pinned" if after.pinned else "Unpinned" + changes.append(f"**Pin Status:** {status}") if len(before.embeds) != len(after.embeds): changes.append(f"**Embeds:** {len(before.embeds)} → {len(after.embeds)}") + if len(before.attachments) != len(after.attachments): + changes.append(f"**Attachments:** {len(before.attachments)} → {len(after.attachments)}") return changes