From 7e1272446118a0398a87923498487e123bf54159 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 6 Jun 2025 23:59:58 +0000 Subject: [PATCH 1/6] Update system prompt to skip AI porn moderation --- cogs/aimod_cog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cogs/aimod_cog.py b/cogs/aimod_cog.py index 0ecf9af..7068d89 100644 --- a/cogs/aimod_cog.py +++ b/cogs/aimod_cog.py @@ -1058,6 +1058,7 @@ Instructions: The only rule regarding NSFW content is that **real-life pornography is strictly prohibited**. Full-on pornographic images are permitted in designated NSFW channels. Stickers and emojis are NOT considered "full-on pornographic images" and are allowed in any channel. + - Do NOT attempt to moderate AI-generated pornography. You are unlikely to know what it looks like. - For general disrespectful behavior, harassment, or bullying (Rule 2 & 3): Only flag a violation if the intent appears **genuinely malicious, targeted, or serious, even after considering conversational history and replies.** Lighthearted insults or "wild" statements within an ongoing banter are generally permissible. - For **explicit slurs or severe discriminatory language** (Rule 3): These are violations **regardless of joking intent if they are used in a targeted or hateful manner**. Context from replies and history is still important to assess targeting. After considering the above, pay EXTREME attention to rules 5 (Pedophilia) and 5A (IRL Porn) – these are always severe. Rule 4 (AI Porn) is also critical. Prioritize these severe violations. From f3ec18a6a5feb120221abeb0cc7cacbd6de380a3 Mon Sep 17 00:00:00 2001 From: Slipstream Date: Fri, 6 Jun 2025 18:06:03 -0600 Subject: [PATCH 2/6] fix: update reasoning for attachment violation to specify IRL pornography --- cogs/aimod_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/aimod_cog.py b/cogs/aimod_cog.py index 7068d89..6b07a4f 100644 --- a/cogs/aimod_cog.py +++ b/cogs/aimod_cog.py @@ -1108,7 +1108,7 @@ Example Response (Image Violation): Example Response (Multiple Attachments Violation): {{ - "reasoning": "While the text content is fine, attachment #3 contains AI-generated pornography, violating rule 4.", + "reasoning": "While the text content is fine, attachment #3 contains IRL pornography, violating rule 4.", "violation": true, "rule_violated": "4", "action": "WARN" From 1ee79a957699dd0a4af10a4b47acd8b91f63f030 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 7 Jun 2025 00:13:50 +0000 Subject: [PATCH 3/6] feat(aimod): process direct link attachments --- cogs/aimod_cog.py | 80 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/cogs/aimod_cog.py b/cogs/aimod_cog.py index 6b07a4f..b66a40b 100644 --- a/cogs/aimod_cog.py +++ b/cogs/aimod_cog.py @@ -18,6 +18,7 @@ import shutil # For backing up files from typing import Optional, List, Dict, Any, Tuple # For type hinting import asyncio import aiofiles +import re # Google Generative AI Imports (using Vertex AI backend) from google import genai @@ -446,6 +447,55 @@ class AIModerationCog(commands.Cog): print(f"Error processing video: {e}") return None, None + async def process_url_attachment(self, url: str) -> tuple[str, bytes, str, str]: + """Fetch an attachment from a direct link.""" + import aiohttp + + try: + cleaned_url = url.strip("<>") + filename = cleaned_url.split("/")[-1].split("?")[0] + _, ext = os.path.splitext(filename.lower()) + if ext in self.image_extensions: + attachment_type = "image" + elif ext in self.gif_extensions: + attachment_type = "gif" + elif ext in self.video_extensions: + attachment_type = "video" + else: + return None, None, None, None + + async with aiohttp.ClientSession() as session: + async with session.get(cleaned_url) as resp: + if resp.status != 200: + print( + f"Failed to fetch URL attachment {cleaned_url}: {resp.status}" + ) + return None, None, None, None + data = await resp.read() + mime_type = resp.headers.get( + "Content-Type", f"image/{ext.lstrip('.')}" + ) + return mime_type, data, attachment_type, filename + except Exception as e: + print(f"Error processing URL attachment {url}: {e}") + return None, None, None, None + + def extract_direct_attachment_urls(self, text: str) -> List[str]: + """Return a list of direct image/video URLs found in the text.""" + + urls = re.findall(r"https?://\S+", text or "") + allowed_exts = ( + self.image_extensions + self.gif_extensions + self.video_extensions + ) + results = [] + for u in urls: + cleaned = u.strip("<>") + path = cleaned.split("?")[0] + _, ext = os.path.splitext(path.lower()) + if ext in allowed_exts: + results.append(cleaned) + return results + # --- AI Moderation Command Group --- aimod_group = app_commands.Group( name="aimod", description="AI Moderation commands." @@ -2077,8 +2127,13 @@ CRITICAL: Do NOT output anything other than the required JSON response. if message.author.bot: print(f"Ignoring message {message.id} from bot.") return - # Ignore messages without content or attachments - if not message.content and not message.attachments: + link_urls = ( + self.extract_direct_attachment_urls(message.content) + if message.content + else [] + ) + # Ignore messages without content, attachments, or direct attachment links + if not message.content and not message.attachments and not link_urls: print(f"Ignoring message {message.id} with no content or attachments.") return # Ignore DMs @@ -2125,6 +2180,27 @@ CRITICAL: Do NOT output anything other than the required JSON response. f"Processed {len(image_data_list)} attachments for message {message.id}" ) + # Check for direct link attachments in the message content + if link_urls: + processed_links = 0 + for url in link_urls: + mime_type, image_bytes, attachment_type, filename = ( + await self.process_url_attachment(url) + ) + if mime_type and image_bytes and attachment_type: + image_data_list.append( + (mime_type, image_bytes, attachment_type, filename) + ) + processed_links += 1 + print( + f"Processed linked attachment: {filename} as {attachment_type}" + ) + + if processed_links > 0: + print( + f"Processed {processed_links} linked attachments for message {message.id}" + ) + # Only proceed with AI analysis if there's text to analyze or attachments if not message_content and not image_data_list: print( From da5a4eddaa0123b2567e6341092a1652003e2f2c Mon Sep 17 00:00:00 2001 From: Slipstream Date: Sat, 7 Jun 2025 00:32:31 +0000 Subject: [PATCH 4/6] Applying previous commit. --- cogs/aimod_cog.py | 80 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/cogs/aimod_cog.py b/cogs/aimod_cog.py index 6b07a4f..b66a40b 100644 --- a/cogs/aimod_cog.py +++ b/cogs/aimod_cog.py @@ -18,6 +18,7 @@ import shutil # For backing up files from typing import Optional, List, Dict, Any, Tuple # For type hinting import asyncio import aiofiles +import re # Google Generative AI Imports (using Vertex AI backend) from google import genai @@ -446,6 +447,55 @@ class AIModerationCog(commands.Cog): print(f"Error processing video: {e}") return None, None + async def process_url_attachment(self, url: str) -> tuple[str, bytes, str, str]: + """Fetch an attachment from a direct link.""" + import aiohttp + + try: + cleaned_url = url.strip("<>") + filename = cleaned_url.split("/")[-1].split("?")[0] + _, ext = os.path.splitext(filename.lower()) + if ext in self.image_extensions: + attachment_type = "image" + elif ext in self.gif_extensions: + attachment_type = "gif" + elif ext in self.video_extensions: + attachment_type = "video" + else: + return None, None, None, None + + async with aiohttp.ClientSession() as session: + async with session.get(cleaned_url) as resp: + if resp.status != 200: + print( + f"Failed to fetch URL attachment {cleaned_url}: {resp.status}" + ) + return None, None, None, None + data = await resp.read() + mime_type = resp.headers.get( + "Content-Type", f"image/{ext.lstrip('.')}" + ) + return mime_type, data, attachment_type, filename + except Exception as e: + print(f"Error processing URL attachment {url}: {e}") + return None, None, None, None + + def extract_direct_attachment_urls(self, text: str) -> List[str]: + """Return a list of direct image/video URLs found in the text.""" + + urls = re.findall(r"https?://\S+", text or "") + allowed_exts = ( + self.image_extensions + self.gif_extensions + self.video_extensions + ) + results = [] + for u in urls: + cleaned = u.strip("<>") + path = cleaned.split("?")[0] + _, ext = os.path.splitext(path.lower()) + if ext in allowed_exts: + results.append(cleaned) + return results + # --- AI Moderation Command Group --- aimod_group = app_commands.Group( name="aimod", description="AI Moderation commands." @@ -2077,8 +2127,13 @@ CRITICAL: Do NOT output anything other than the required JSON response. if message.author.bot: print(f"Ignoring message {message.id} from bot.") return - # Ignore messages without content or attachments - if not message.content and not message.attachments: + link_urls = ( + self.extract_direct_attachment_urls(message.content) + if message.content + else [] + ) + # Ignore messages without content, attachments, or direct attachment links + if not message.content and not message.attachments and not link_urls: print(f"Ignoring message {message.id} with no content or attachments.") return # Ignore DMs @@ -2125,6 +2180,27 @@ CRITICAL: Do NOT output anything other than the required JSON response. f"Processed {len(image_data_list)} attachments for message {message.id}" ) + # Check for direct link attachments in the message content + if link_urls: + processed_links = 0 + for url in link_urls: + mime_type, image_bytes, attachment_type, filename = ( + await self.process_url_attachment(url) + ) + if mime_type and image_bytes and attachment_type: + image_data_list.append( + (mime_type, image_bytes, attachment_type, filename) + ) + processed_links += 1 + print( + f"Processed linked attachment: {filename} as {attachment_type}" + ) + + if processed_links > 0: + print( + f"Processed {processed_links} linked attachments for message {message.id}" + ) + # Only proceed with AI analysis if there's text to analyze or attachments if not message_content and not image_data_list: print( From b4be9d8cd02dff830e304243587e0317dece469f Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 7 Jun 2025 00:33:17 +0000 Subject: [PATCH 5/6] feat: parse attachments from embed URLs --- cogs/aimod_cog.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cogs/aimod_cog.py b/cogs/aimod_cog.py index b66a40b..66492b3 100644 --- a/cogs/aimod_cog.py +++ b/cogs/aimod_cog.py @@ -2127,9 +2127,10 @@ CRITICAL: Do NOT output anything other than the required JSON response. if message.author.bot: print(f"Ignoring message {message.id} from bot.") return + embed_urls = [embed.url for embed in message.embeds if embed.url] link_urls = ( - self.extract_direct_attachment_urls(message.content) - if message.content + self.extract_direct_attachment_urls(" ".join(embed_urls)) + if embed_urls else [] ) # Ignore messages without content, attachments, or direct attachment links From d452e6620ee4bd37b5cdcad6a2bf9ca12cc95d53 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 7 Jun 2025 01:08:47 +0000 Subject: [PATCH 6/6] Update aimod prompt and pedophilia rule --- cogs/aimod_cog.py | 1 + cogs/aimod_config.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cogs/aimod_cog.py b/cogs/aimod_cog.py index 66492b3..df22242 100644 --- a/cogs/aimod_cog.py +++ b/cogs/aimod_cog.py @@ -1102,6 +1102,7 @@ Instructions: - **"Replied-to Message" and "Recent Channel History" are vital for understanding banter, jokes, and ongoing discussions. A statement that seems offensive in isolation might be acceptable within the flow of conversation or as a direct reply.** - If images, GIFs, or videos are attached, analyze ALL of them for rule violations. - Pay special attention to images that may contain NSFW content, pornography, gore, or other prohibited visual content. + - **Do not attempt to guess or estimate the ages of characters or people in images.** Only act on explicit textual context indicating they are minors. - If multiple attachments are present, a violation in ANY of them should be flagged. 2. Determine if ANY rule is violated. When evaluating, consider the server's culture where **extremely edgy, dark, and sexual humor, including potentially offensive jokes (e.g., rape jokes, saying you want to be raped), are common and generally permissible IF THEY ARE CLEARLY JOKES, part of an established banter, or a direct non-malicious reply, and not targeted harassment or explicit rule violations.** * **NSFW Content:** diff --git a/cogs/aimod_config.py b/cogs/aimod_config.py index c7af766..c701157 100644 --- a/cogs/aimod_config.py +++ b/cogs/aimod_config.py @@ -140,7 +140,7 @@ Stickers and emojis are NOT considered "full-on pornographic images" and are all * **AI-Generated Pornography:** Do not post AI-generated pornography. -* **Zero Tolerance for Pedophilia:** Any form of pedophilia, including lolicon and shotacon content, is strictly forbidden and will result in an immediate ban. +* **Pedophilia and Underage Content:** Depicting minors in sexual situations, including lolicon or shotacon, is not allowed and may result in severe moderation. * **Channel Usage:** Please use channels for their intended purposes. Bot commands should primarily be used in `#bot-commands`, unless they are part of a bot-based game or event happening in another specific channel.