205 lines
6.7 KiB
Python
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()
|
|
]
|