diff --git a/src/daemon_boyfriend/cogs/ai_chat.py b/src/daemon_boyfriend/cogs/ai_chat.py index 1072e85..01a27a5 100644 --- a/src/daemon_boyfriend/cogs/ai_chat.py +++ b/src/daemon_boyfriend/cogs/ai_chat.py @@ -105,18 +105,99 @@ class AIChatCog(commands.Cog): try: response_text = await self._generate_response(message, content) - # Split and send response - chunks = split_message(response_text) - await message.reply(chunks[0]) + # Extract image URLs and clean response text + text_content, image_urls = self._extract_image_urls(response_text) + # Split and send response + chunks = split_message(text_content) if text_content.strip() else [] + + # Send first chunk as reply (or just images if no text) + if chunks: + first_embed = self._create_image_embed(image_urls[0]) if image_urls else None + await message.reply(chunks[0], embed=first_embed) + remaining_images = image_urls[1:] if image_urls else [] + elif image_urls: + # Only images, no text + await message.reply(embed=self._create_image_embed(image_urls[0])) + remaining_images = image_urls[1:] + else: + await message.reply("I don't have a response for that.") + return + + # Send remaining text chunks for chunk in chunks[1:]: await message.channel.send(chunk) + # Send remaining images as separate embeds + for img_url in remaining_images: + await message.channel.send(embed=self._create_image_embed(img_url)) + except Exception as e: logger.error(f"Mention response error: {e}", exc_info=True) error_message = self._get_error_message(e) await message.reply(error_message) + def _extract_image_urls(self, text: str) -> tuple[str, list[str]]: + """Extract image URLs from text and return cleaned text with URLs. + + Args: + text: The response text that may contain image URLs + + Returns: + Tuple of (cleaned text, list of image URLs) + """ + # Pattern to match image URLs (common formats) + image_extensions = r"\.(png|jpg|jpeg|gif|webp|bmp)" + url_pattern = rf"(https?://[^\s<>\"\')]+{image_extensions}(?:\?[^\s<>\"\')]*)?)" + + # Find all image URLs + image_urls = re.findall(url_pattern, text, re.IGNORECASE) + # The findall returns tuples when there are groups, extract full URLs + image_urls = re.findall( + rf"https?://[^\s<>\"\')]+{image_extensions}(?:\?[^\s<>\"\')]*)?", + text, + re.IGNORECASE, + ) + + # Also check for markdown image syntax ![alt](url) + markdown_images = re.findall(r"!\[[^\]]*\]\(([^)]+)\)", text) + for url in markdown_images: + if url not in image_urls: + # Check if it looks like an image URL + if re.search(image_extensions, url, re.IGNORECASE) or "image" in url.lower(): + image_urls.append(url) + + # Clean the text by removing standalone image URLs (but keep them if part of markdown links) + cleaned_text = text + for url in image_urls: + # Remove standalone URLs (not part of markdown) + cleaned_text = re.sub( + rf"(? discord.Embed: + """Create a Discord embed with an image. + + Args: + image_url: The URL of the image + + Returns: + Discord Embed object with the image + """ + embed = discord.Embed() + embed.set_image(url=image_url) + return embed + def _get_error_message(self, error: Exception) -> str: """Get a user-friendly error message based on the exception type.