- Automod cog: 520 -> 100 lines (spam only, no commands) - AI moderation cog: 664 -> 250 lines (images only, full cost controls) - Automod service: 600+ -> 200 lines (spam only) - All cost control measures implemented - NSFW video domain blocking - Rate limiting per guild and per user - Image deduplication - File size limits - Configurable via YAML Next: Update AI providers and models
90 lines
3.2 KiB
Python
90 lines
3.2 KiB
Python
"""Automod cog for automatic spam detection - Minimal Version."""
|
|
|
|
import logging
|
|
|
|
import discord
|
|
from discord.ext import commands
|
|
|
|
from guardden.bot import GuardDen
|
|
from guardden.services.automod import AutomodResult, AutomodService, SpamConfig
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Automod(commands.Cog):
|
|
"""Automatic spam detection (no commands, no banned words)."""
|
|
|
|
def __init__(self, bot: GuardDen) -> None:
|
|
self.bot = bot
|
|
self.automod = AutomodService()
|
|
|
|
def _spam_config(self) -> SpamConfig:
|
|
"""Get spam config from YAML."""
|
|
config_loader = self.bot.config_loader
|
|
|
|
return SpamConfig(
|
|
message_rate_limit=config_loader.get_setting("automod.message_rate_limit", 5),
|
|
message_rate_window=config_loader.get_setting("automod.message_rate_window", 5),
|
|
duplicate_threshold=config_loader.get_setting("automod.duplicate_threshold", 3),
|
|
mention_limit=config_loader.get_setting("automod.mention_limit", 5),
|
|
mention_rate_limit=config_loader.get_setting("automod.mention_rate_limit", 10),
|
|
mention_rate_window=config_loader.get_setting("automod.mention_rate_window", 60),
|
|
)
|
|
|
|
async def _handle_violation(
|
|
self,
|
|
message: discord.Message,
|
|
result: AutomodResult,
|
|
) -> None:
|
|
"""Handle an automod violation by deleting the message."""
|
|
# Delete the message (no logging, no timeout, no DM)
|
|
if result.should_delete:
|
|
try:
|
|
await message.delete()
|
|
logger.info(
|
|
f"Automod deleted message from {message.author} in {message.guild.name}: {result.reason}"
|
|
)
|
|
except discord.Forbidden:
|
|
logger.warning(f"Cannot delete message in {message.guild}: missing permissions")
|
|
except discord.NotFound:
|
|
pass # Already deleted
|
|
|
|
@commands.Cog.listener()
|
|
async def on_message(self, message: discord.Message) -> None:
|
|
"""Check all messages for spam violations."""
|
|
# Skip DMs, bots, and empty messages
|
|
if not message.guild or message.author.bot or not message.content:
|
|
return
|
|
|
|
# Get config from YAML
|
|
config = self.bot.config_loader
|
|
if not config.get_setting("automod.enabled", True):
|
|
return
|
|
|
|
# Check spam ONLY (no banned words, no scam links, no invites)
|
|
if config.get_setting("automod.anti_spam_enabled", True):
|
|
spam_config = self._spam_config()
|
|
result = self.automod.check_spam(
|
|
message,
|
|
anti_spam_enabled=True,
|
|
spam_config=spam_config,
|
|
)
|
|
|
|
if result:
|
|
await self._handle_violation(message, result)
|
|
|
|
@commands.Cog.listener()
|
|
async def on_message_edit(self, before: discord.Message, after: discord.Message) -> None:
|
|
"""Check edited messages for spam violations."""
|
|
# Only check if content changed
|
|
if before.content == after.content:
|
|
return
|
|
|
|
# Reuse on_message logic
|
|
await self.on_message(after)
|
|
|
|
|
|
async def setup(bot: GuardDen) -> None:
|
|
"""Load the Automod cog."""
|
|
await bot.add_cog(Automod(bot))
|