perefouras/database.py
2026-07-17 19:45:11 +02:00

205 lines
6.7 KiB
Python

# database.py
import sqlite3
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Tuple
DB_FILE = "data/database.db"
def ensure_db() -> None:
"""Initialize SQLite database and create tables if needed."""
Path(DB_FILE).parent.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(DB_FILE) as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS guild_state (
guild_id TEXT PRIMARY KEY,
guild_name TEXT NOT NULL DEFAULT '',
cooldown_until TEXT NOT NULL DEFAULT '1970-01-01T00:00:00',
cooldown_ratio REAL NOT NULL DEFAULT 1.0,
self_control REAL NOT NULL DEFAULT 1.0,
last_updated TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS active_riddles (
channel_id TEXT PRIMARY KEY,
riddle_index INTEGER NOT NULL,
nb_clues INTEGER NOT NULL DEFAULT -1,
message_id INTEGER NOT NULL,
solver_id TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.commit()
def get_connection() -> sqlite3.Connection:
"""Return SQLite connection with row factory for named column access."""
conn = sqlite3.connect(DB_FILE)
conn.row_factory = sqlite3.Row
return conn
def get_guild_state(guild_id: str) -> Dict[str, Any]:
"""Retrieve guild state from database."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"""
SELECT guild_id, guild_name, cooldown_until, cooldown_ratio, self_control, last_updated
FROM guild_state WHERE guild_id = ?
""",
(guild_id,),
)
row = cursor.fetchone()
if row:
return {
"guild_id": row["guild_id"],
"guild_name": row["guild_name"],
"cooldown_until": row["cooldown_until"],
"cooldown_ratio": row["cooldown_ratio"],
"self_control": row["self_control"],
"last_updated": row["last_updated"],
}
else:
return {
"guild_id": guild_id,
"guild_name": "",
"cooldown_until": "1970-01-01T00:00:00",
"cooldown_ratio": 1.0,
"self_control": 1.0,
"last_updated": datetime.now().isoformat(),
}
def update_guild_state(
guild_id: str, guild_name: str, cooldown_until: str, cooldown_ratio: float, self_control: float
) -> None:
"""Update guild state in database."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"""
INSERT OR REPLACE INTO guild_state (guild_id, guild_name, cooldown_until, cooldown_ratio, self_control, last_updated)
VALUES (?, ?, ?, ?, ?, ?)
""",
(
guild_id,
guild_name,
cooldown_until,
cooldown_ratio,
self_control,
datetime.now().isoformat(),
),
)
conn.commit()
def delete_guild_state(guild_id: str) -> bool:
"""Delete guild state from database."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM guild_state WHERE guild_id = ?", (guild_id,))
conn.commit()
return cursor.rowcount > 0
def get_all_guild_states() -> Dict[str, Dict[str, Any]]:
"""Retrieve all guild states (for debug purposes)."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT guild_id, guild_name, cooldown_until, cooldown_ratio, self_control, last_updated FROM guild_state"
)
return {
row["guild_id"]: {
"guild_name": row["guild_name"],
"cooldown_until": row["cooldown_until"],
"cooldown_ratio": row["cooldown_ratio"],
"self_control": row["self_control"],
"last_updated": row["last_updated"],
}
for row in cursor.fetchall()
}
def get_active_riddle(channel_id: str):
"""Récupérer une énigme active pour un canal spécifique."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM active_riddles WHERE channel_id = ?", (str(channel_id),)
)
row = cursor.fetchone()
if row:
return {
"channel_id": row["channel_id"],
"riddle_index": row["riddle_index"],
"nb_clues": row["nb_clues"],
"message_id": row["message_id"],
"solver_id": row["solver_id"],
"created_at": row["created_at"],
"updated_at": row["updated_at"],
}
return None
def save_active_riddle(
channel_id: str,
riddle_index: int,
nb_clues: int,
message_id: int,
solver_id: str = None,
):
"""Sauvegarder ou mettre à jour une énigme active."""
with get_connection() as conn:
cursor = conn.cursor()
# Si solver_id existe, on considère que c'est résolu et on garde l'entrée pour historique
# Sinon on met à jour l'énigme en cours
cursor.execute(
"""
INSERT OR REPLACE INTO active_riddles
(channel_id, riddle_index, nb_clues, message_id, solver_id, updated_at)
VALUES (?, ?, ?, ?, ?, datetime('now'))
""",
(str(channel_id), riddle_index, nb_clues, message_id, solver_id),
)
conn.commit()
def remove_active_riddle(channel_id: str) -> bool:
"""Supprimer une énigme active du suivi."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"DELETE FROM active_riddles WHERE channel_id = ?", (str(channel_id),)
)
conn.commit()
return cursor.rowcount > 0
def get_all_active_riddles():
"""Récupérer toutes les énigmes actives (pour debug)."""
with get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM active_riddles")
return [
{
"channel_id": row["channel_id"],
"riddle_index": row["riddle_index"],
"nb_clues": row["nb_clues"],
"message_id": row["message_id"],
"solver_id": row["solver_id"],
"created_at": row["created_at"],
"updated_at": row["updated_at"],
}
for row in cursor.fetchall()
]