Initial commit - Excommunicado Discord bot (XCOM stealth isolation)

This commit is contained in:
Subinacls
2026-06-08 20:47:32 -04:00
commit fc9845772d
9 changed files with 925 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
"""Utility modules for Excommunicado bot."""
+102
View File
@@ -0,0 +1,102 @@
"""Configuration and persistence for the Excommunicado bot."""
import json
from pathlib import Path
from typing import Any, Dict, Optional
DATA_DIR = Path(__file__).parent.parent / "data"
CONFIG_FILE = DATA_DIR / "xcom_config.json"
def _ensure_data_dir() -> None:
DATA_DIR.mkdir(parents=True, exist_ok=True)
def load_config() -> Dict[str, Any]:
"""Load the entire config dict."""
_ensure_data_dir()
if not CONFIG_FILE.exists():
save_config({})
return {}
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_config(data: Dict[str, Any]) -> None:
"""Save the config dict."""
_ensure_data_dir()
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
def get_guild_data(guild_id: int) -> Dict[str, Any]:
"""Get or initialize data for a specific guild."""
config = load_config()
gid = str(guild_id)
if gid not in config:
config[gid] = {
"xcom_role_id": None,
"xcom_channel_id": None,
"isolated_users": {} # user_id -> {"reason": str, "timestamp": str}
}
save_config(config)
return config[gid]
def update_guild_data(guild_id: int, key: str, value: Any) -> None:
"""Update a top-level key for the guild."""
config = load_config()
gid = str(guild_id)
if gid not in config:
config[gid] = {
"xcom_role_id": None,
"xcom_channel_id": None,
"isolated_users": {}
}
config[gid][key] = value
save_config(config)
def add_isolated_user(guild_id: int, user_id: int, reason: str, timestamp: str) -> None:
"""Record a newly isolated user (shared xcom-lounge channel)."""
config = load_config()
gid = str(guild_id)
if gid not in config:
config[gid] = {"xcom_role_id": None, "xcom_channel_id": None, "isolated_users": {}}
config[gid]["isolated_users"][str(user_id)] = {
"reason": reason,
"timestamp": timestamp,
"absolution_points": 0,
"marked": True
}
save_config(config)
def increment_absolution_points(guild_id: int, user_id: int, amount: int = 1) -> int:
"""Increase absolution points for a user and return the new total."""
config = load_config()
gid = str(guild_id)
uid = str(user_id)
if gid in config and uid in config[gid].get("isolated_users", {}):
current = config[gid]["isolated_users"][uid].get("absolution_points", 0)
new_total = current + amount
config[gid]["isolated_users"][uid]["absolution_points"] = new_total
save_config(config)
return new_total
return 0
def remove_isolated_user(guild_id: int, user_id: int) -> Optional[Dict]:
"""Remove user from isolation record and return previous data or None."""
config = load_config()
gid = str(guild_id)
if gid in config and str(user_id) in config[gid].get("isolated_users", {}):
data = config[gid]["isolated_users"].pop(str(user_id))
save_config(config)
return data
return None
def get_isolated_users(guild_id: int) -> Dict[str, Dict]:
"""Return all isolated users for guild."""
data = get_guild_data(guild_id)
return data.get("isolated_users", {})