# ╔══════════════════════════════════════════════════════════════════╗ # ║ ║ # ║ +-+-+-+-+-+-+-+-+ ║ # ║ |H|e|x|a|H|o|s|t| ║ # ║ +-+-+-+-+-+-+-+-+ ║ # ║ ║ # ║ © 2026 HexaHost — All Rights Reserved ║ # ║ ║ # ║ discord ── https://discord.gg/hexahost ║ # ║ github ── https://github.com/theoneandonlymace ║ # ║ ║ # ╚══════════════════════════════════════════════════════════════════╝ """Shared AutoMod settings: event IDs, punishments, thresholds.""" from __future__ import annotations from typing import Any import aiosqlite from utils.db_paths import db_path # API id -> bot DB event name EVENT_ID_TO_DB: dict[str, str] = { "anti_spam": "Anti spam", "anti_caps": "Anti caps", "anti_link": "Anti link", "anti_invites": "Anti invites", "anti_mass_mention": "Anti mass mention", "anti_emoji_spam": "Anti emoji spam", "anti_nsfw_link": "Anti NSFW link", } # Also accept legacy UI ids EVENT_ID_ALIASES: dict[str, str] = { "anti_links": "anti_link", "anti_mentions": "anti_mass_mention", } DB_EVENT_TO_ID: dict[str, str] = {v: k for k, v in EVENT_ID_TO_DB.items()} VALID_PUNISHMENTS = frozenset({"Mute", "Kick", "Ban"}) PUNISHMENT_ALIASES: dict[str, str] = { "mute": "Mute", "kick": "Kick", "ban": "Ban", "Mute": "Mute", "Kick": "Kick", "Ban": "Ban", "delete": "Mute", # legacy dashboard — map to Mute "warn": "Mute", } MAX_IGNORED = 10 NSFW_KEYWORDS = [ "porn", "xxx", "adult", "sex", "nsfw", "xnxx", "onlyfans", "brazzers", "xhamster", "xvideos", "pornhub", "redtube", "livejasmin", "youporn", "tube8", "pornhat", "swxvid", "ixxx", ] DEFAULT_THRESHOLDS: dict[str, dict[str, float]] = { "anti_spam": {"max_messages": 5, "window_seconds": 10, "mute_minutes": 12}, "anti_caps": {"percent": 70, "min_length": 45, "mute_minutes": 1}, "anti_link": {"mute_minutes": 7}, "anti_invites": {"mute_minutes": 12}, "anti_mass_mention": {"max_mentions": 5, "mute_minutes": 3}, "anti_emoji_spam": {"max_emojis": 5, "mute_minutes": 1}, "anti_nsfw_link": {}, } THRESHOLD_META: dict[str, list[dict[str, Any]]] = { "anti_spam": [ {"key": "max_messages", "label": "Max messages", "min": 2, "max": 20, "step": 1}, {"key": "window_seconds", "label": "Window (seconds)", "min": 3, "max": 60, "step": 1}, {"key": "mute_minutes", "label": "Mute minutes", "min": 1, "max": 1440, "step": 1}, ], "anti_caps": [ {"key": "percent", "label": "Caps percent", "min": 40, "max": 100, "step": 1}, {"key": "min_length", "label": "Min message length", "min": 10, "max": 200, "step": 1}, {"key": "mute_minutes", "label": "Mute minutes", "min": 1, "max": 1440, "step": 1}, ], "anti_link": [ {"key": "mute_minutes", "label": "Mute minutes", "min": 1, "max": 1440, "step": 1}, ], "anti_invites": [ {"key": "mute_minutes", "label": "Mute minutes", "min": 1, "max": 1440, "step": 1}, ], "anti_mass_mention": [ {"key": "max_mentions", "label": "Max mentions", "min": 2, "max": 20, "step": 1}, {"key": "mute_minutes", "label": "Mute minutes", "min": 1, "max": 1440, "step": 1}, ], "anti_emoji_spam": [ {"key": "max_emojis", "label": "Max emojis", "min": 2, "max": 30, "step": 1}, {"key": "mute_minutes", "label": "Mute minutes", "min": 1, "max": 1440, "step": 1}, ], } def normalize_event_id(raw: str) -> str | None: if raw in EVENT_ID_TO_DB: return raw if raw in EVENT_ID_ALIASES: return EVENT_ID_ALIASES[raw] if raw in DB_EVENT_TO_ID: return DB_EVENT_TO_ID[raw] return None def normalize_punishment(raw: str | None) -> str | None: if raw is None: return None return PUNISHMENT_ALIASES.get(raw) or PUNISHMENT_ALIASES.get(raw.strip()) async def ensure_thresholds_table(db: aiosqlite.Connection) -> None: await db.execute( """ CREATE TABLE IF NOT EXISTS automod_thresholds ( guild_id INTEGER, event TEXT, key TEXT, value REAL, PRIMARY KEY (guild_id, event, key) ) """ ) async def get_thresholds(guild_id: int, event_id: str) -> dict[str, float]: """Return merged defaults + DB overrides for an API event id.""" defaults = dict(DEFAULT_THRESHOLDS.get(event_id, {})) db_event = EVENT_ID_TO_DB.get(event_id) if not db_event: return defaults async with aiosqlite.connect(db_path("automod.db")) as db: await ensure_thresholds_table(db) cursor = await db.execute( "SELECT key, value FROM automod_thresholds WHERE guild_id = ? AND event = ?", (guild_id, db_event), ) for key, value in await cursor.fetchall(): defaults[str(key)] = float(value) return defaults async def get_all_thresholds(guild_id: int) -> dict[str, dict[str, float]]: out: dict[str, dict[str, float]] = { eid: dict(vals) for eid, vals in DEFAULT_THRESHOLDS.items() } async with aiosqlite.connect(db_path("automod.db")) as db: await ensure_thresholds_table(db) cursor = await db.execute( "SELECT event, key, value FROM automod_thresholds WHERE guild_id = ?", (guild_id,), ) for event, key, value in await cursor.fetchall(): eid = DB_EVENT_TO_ID.get(event) if not eid: continue out.setdefault(eid, {})[str(key)] = float(value) return out async def set_thresholds( db: aiosqlite.Connection, guild_id: int, thresholds: dict[str, dict[str, float]], ) -> None: await ensure_thresholds_table(db) for event_id, pairs in thresholds.items(): eid = normalize_event_id(event_id) if not eid or eid == "anti_nsfw_link": continue db_event = EVENT_ID_TO_DB[eid] allowed = DEFAULT_THRESHOLDS.get(eid, {}) for key, value in pairs.items(): if key not in allowed: continue await db.execute( """ INSERT OR REPLACE INTO automod_thresholds (guild_id, event, key, value) VALUES (?, ?, ?, ?) """, (guild_id, db_event, key, float(value)), )