188 lines
6.9 KiB
Python
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)),
|
|
)
|