Files
Mace-AIO-Discord-Bot---With…/bot/utils/automod_settings.py

188 lines
6.9 KiB
Python

# ╔══════════════════════════════════════════════════════════════════╗
# ║ ║
# ║ +-+-+-+-+-+-+-+-+ ║
# ║ |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)),
)