most fouras commands are now using discord slash commands

This commit is contained in:
Anselme FRANÇOIS 2026-07-10 16:03:59 +02:00
parent 400dcecf13
commit 343eecb4d8
3 changed files with 158 additions and 153 deletions

288
fouras.py
View File

@ -1,20 +1,16 @@
# Remplacez les imports globaux et initialisations # Remplacez les imports globaux et initialisations
import json import json
import os
import random import random
import re import re
from typing import Any, Dict from typing import Any, Dict, Optional
import discord import discord
from discord import app_commands
from unidecode import unidecode from unidecode import unidecode
import database import database
# Supprimez ces lignes globales :
# ongoing_riddles = {}
# riddles = []
# answers = []
# Gardez seulement le chargement
riddles = [] riddles = []
answers = [] answers = []
@ -67,7 +63,7 @@ def new_riddle_state(index: int) -> Dict[str, Any]:
} }
async def finish_riddle(channel, client) -> None: async def finish_riddle(channel, client: discord.Client) -> None:
"""Supprimer l'énigme active de la DB et supprimer le suivi en mémoire.""" """Supprimer l'énigme active de la DB et supprimer le suivi en mémoire."""
removed = database.remove_active_riddle(str(channel.id)) removed = database.remove_active_riddle(str(channel.id))
@ -108,7 +104,6 @@ def format_riddle_message(
nb_clues = current_riddle.get("nbClues", -1) nb_clues = current_riddle.get("nbClues", -1)
answer = current_riddle.get("answer", "") answer = current_riddle.get("answer", "")
# Si l'énigme est résolue, on récupère le solver depuis l'ID
solver = None solver = None
if solved and solver_mention: if solved and solver_mention:
solver = type("obj", (object,), {"mention": solver_mention})() solver = type("obj", (object,), {"mention": solver_mention})()
@ -138,7 +133,7 @@ def format_riddle_message(
) )
async def get_channel_name(channel, client) -> str: async def get_channel_name(channel, client: discord.Client) -> str:
if isinstance(channel, discord.DMChannel): if isinstance(channel, discord.DMChannel):
dm_channel = await client.fetch_channel(channel.id) dm_channel = await client.fetch_channel(channel.id)
return "[DM={0}]".format(dm_channel.recipient.name) return "[DM={0}]".format(dm_channel.recipient.name)
@ -146,7 +141,7 @@ async def get_channel_name(channel, client) -> str:
return "[Server={0}] => [Channel={1}]".format(channel.guild.name, channel.name) return "[Server={0}] => [Channel={1}]".format(channel.guild.name, channel.name)
async def handle_debug_commands(message, client) -> bool: async def handle_debug_commands(message, client: discord.Client) -> bool:
"""Handle debug commands (debug, save, load, broadcast). Returns True if handled.""" """Handle debug commands (debug, save, load, broadcast). Returns True if handled."""
message_content = message.content.lower() message_content = message.content.lower()
@ -209,88 +204,31 @@ async def handle_debug_commands(message, client) -> bool:
return False return False
async def handle_riddle_commands(message, client) -> bool: def get_current_riddle(channel_id):
"""Handle /fouras commands. Returns True if command was processed.""" riddle_data = database.get_active_riddle(str(channel_id))
message_content = message.content.lower()
fouras_match = re.match(r"^fouras\s+(\d+)$", message_content)
if fouras_match:
index = int(fouras_match.group(1)) - 1
if 0 <= index < len(riddles):
if random.random() <= 0.03:
await message.channel.send("Non")
else:
riddle_state = new_riddle_state(index)
msg = await message.channel.send(
format_riddle_message({**riddle_state, "index": index})
)
database.save_active_riddle(
channel_id=str(message.channel.id),
riddle_index=index,
nb_clues=-1,
message_id=msg.id,
solver_id=None,
)
else:
await message.channel.send(INVALID_ID.format(len=len(riddles)))
return True
if message_content == "fouras":
if random.random() <= 0.03:
await message.channel.send("Non")
elif len(riddles) > 0:
index = random.randint(0, len(riddles) - 1)
riddle_state = new_riddle_state(index)
msg = await message.channel.send(
format_riddle_message({**riddle_state, "index": index})
)
database.save_active_riddle(
channel_id=str(message.channel.id),
riddle_index=index,
nb_clues=-1,
message_id=msg.id,
solver_id=None,
)
else:
print(f'riddles : "{len(riddles)}"')
return True
if message_content == "about fouras":
author_user = await client.fetch_user(MAINTAINER_ID)
await message.channel.send(ABOUT.format(user=author_user.mention, url=API_URL))
return True
return False
async def handle_riddle_solving(message, client) -> bool:
"""Handle riddle solving logic. Returns True if riddle was solved or modified."""
# Vérification via la DB au lieu de la variable globale
riddle_data = database.get_active_riddle(str(message.channel.id))
if not riddle_data: if not riddle_data:
return False return None
# Reconstruire l'état de l'énigme # Reconstruire l'état de l'énigme
if 0 <= riddle_data["riddle_index"] < len(riddles): if 0 <= riddle_data["riddle_index"] < len(riddles):
current_riddle = { return {
"index": riddle_data["riddle_index"], "index": riddle_data["riddle_index"],
"nbClues": riddle_data["nb_clues"], "nbClues": riddle_data["nb_clues"],
"riddle": riddles[riddle_data["riddle_index"]].strip(), "riddle": riddles[riddle_data["riddle_index"]].strip(),
"answer": answers[riddle_data["riddle_index"]], "answer": answers[riddle_data["riddle_index"]],
"data": riddle_data
} }
else: else:
return False return None
# Si aucun message n'est associé, on ne peut pas éditer
if not riddle_data["message_id"]:
return False
async def handle_riddle_solving(message, client: discord.Client) -> bool:
current_riddle = get_current_riddle(message.channel.id)
if(not current_riddle):
return False
answer = current_riddle["answer"] answer = current_riddle["answer"]
riddle_data = current_riddle["data"]
# Check if message contains the answer # Check if message contains the answer
if unidecode(answer.lower()) in unidecode(message.content.lower()): if unidecode(answer.lower()) in unidecode(message.content.lower()):
@ -326,65 +264,10 @@ async def handle_riddle_solving(message, client) -> bool:
await finish_riddle(message.channel, client) await finish_riddle(message.channel, client)
return True return True
# Repeat riddle command
if message.content.lower() in ["repete", "répète", "repeat"]:
try:
original_msg = await message.channel.fetch_message(
riddle_data["message_id"]
)
# msg = await message.channel.send(format_riddle_message(current_riddle))
# database.save_active_riddle(
# channel_id=str(message.channel.id),
# riddle_index=index,
# nb_clues=-1,
# message_id=msg.id,
# solver_id=None,
# )
except discord.errors.NotFound:
pass
return True
# Clue command
if message.content.lower() in ["indice", "aide", "help", "clue"]:
nb_clues = current_riddle["nbClues"] + 1
current_riddle["nbClues"] = nb_clues
if nb_clues >= len(answer):
await message.channel.send(
"Perdu ! La réponse était : `{0}`".format(answer)
)
# Mark as unsolved/revealed in DB
database.save_active_riddle(
channel_id=str(message.channel.id),
riddle_index=riddle_data["riddle_index"],
nb_clues=nb_clues,
message_id=riddle_data["message_id"],
solver_id="revealed", # Marqueur spécial
)
await finish_riddle(message.channel, client)
else:
# Mise à jour du nombre d'indices en DB
database.save_active_riddle(
channel_id=str(message.channel.id),
riddle_index=riddle_data["riddle_index"],
nb_clues=nb_clues,
message_id=riddle_data["message_id"],
solver_id=riddle_data["solver_id"],
)
try:
original_msg = await message.channel.fetch_message(
riddle_data["message_id"]
)
await original_msg.edit(content=format_riddle_message(current_riddle))
except discord.errors.NotFound:
pass
return True
return False return False
async def handle_bug_report(message, client) -> bool: async def handle_bug_report(message, client: discord.Client) -> bool:
"""Handle bug report command. Returns True if bug report was sent.""" """Handle bug report command. Returns True if bug report was sent."""
if not message.content.lower().startswith("bug"): if not message.content.lower().startswith("bug"):
return False return False
@ -424,7 +307,7 @@ async def handle_bug_report(message, client) -> bool:
return True return True
async def handle_message(message, client) -> bool: async def handle_message(message, client: discord.Client) -> bool:
"""Main entry point for message handling.""" """Main entry point for message handling."""
database.ensure_db() database.ensure_db()
@ -432,10 +315,6 @@ async def handle_message(message, client) -> bool:
if await handle_debug_commands(message, client): if await handle_debug_commands(message, client):
return True return True
# Handle riddle commands
if await handle_riddle_commands(message, client):
return True
# Handle bug reports # Handle bug reports
if await handle_bug_report(message, client): if await handle_bug_report(message, client):
return True return True
@ -445,3 +324,132 @@ async def handle_message(message, client) -> bool:
return True return True
return False return False
async def new_riddle(interaction, index: int) -> None:
if random.random() <= 0.03:
await interaction.response.send_message("Non")
else:
riddle_state = new_riddle_state(index)
await interaction.response.send_message("Nouvelle énigme !")
msg = await interaction.channel.send(
format_riddle_message({**riddle_state, "index": index})
)
database.save_active_riddle(
channel_id=str(interaction.channel.id),
riddle_index=index,
nb_clues=-1,
message_id=msg.id,
solver_id=None
)
async def update_riddle_message(channel, current_riddle):
try:
original_msg = await channel.fetch_message(current_riddle["data"]["message_id"])
await original_msg.edit(content=format_riddle_message(current_riddle))
return True
except discord.errors.NotFound:
return False
def setup_commands(client: discord.Client, tree) -> None:
@tree.command(
name="fouras",
description="Demander une énigme au Père Fouras"
)
async def fouras(
interaction: discord.Interaction,
):
if len(riddles) == 0:
await interaction.response.send_message("Désolé, mais j'ai oublié mes énigmes", ephemeral=True)
else:
index = random.randint(0, len(riddles) - 1)
await new_riddle(interaction, index)
@tree.command(
name="fouras_requete",
description="Demander une énigme spécifique au Père Fouras"
)
@app_commands.describe(index="Numéro de l'énigme")
async def fouras_requete(
interaction: discord.Interaction,
index: int,
):
index = index - 1
if not (0 <= index < len(riddles)):
await interaction.response.send_message(INVALID_ID.format(len=len(riddles)+1), ephemeral=True)
return
await new_riddle(interaction, index)
@tree.command(
name="repete",
description="Demander au Père Fouras de répéter l'énigme en cours"
)
async def repete(
interaction: discord.Interaction,
):
current_riddle = get_current_riddle(interaction.channel.id)
if(not current_riddle):
await interaction.response.send_message("Aucune énigme en cours", ephemeral=True)
return
riddle_data = current_riddle["data"]
await interaction.response.send_message("Ok, je vais répéter l'énigme en cours", ephemeral=True)
msg = await interaction.channel.send(format_riddle_message(current_riddle))
database.save_active_riddle(
channel_id=str(interaction.channel.id),
riddle_index=riddle_data["riddle_index"],
nb_clues=current_riddle["nbClues"],
message_id=msg.id,
solver_id=riddle_data["solver_id"],
)
@tree.command(
name="about",
description="Qui est le Père Fouras ?"
)
async def about(
interaction: discord.Interaction,
):
author_user = await client.fetch_user(MAINTAINER_ID)
await interaction.response.send_message(ABOUT.format(user=author_user.mention, url=API_URL), ephemeral=True)
@tree.command(
name="indice",
description="Demander un indice pour l'énigme en cours"
)
async def indice(
interaction: discord.Interaction,
):
current_riddle = get_current_riddle(interaction.channel.id)
if(not current_riddle):
await interaction.response.send_message("Aucune énigme en cours", ephemeral=True)
return
answer = current_riddle["answer"]
riddle_data = current_riddle["data"]
nb_clues = current_riddle["nbClues"] + 1
current_riddle["nbClues"] = nb_clues
if nb_clues >= len(answer):
await interaction.response.send_message(
"Perdu ! La réponse était : `{0}`".format(answer)
)
if(not await update_riddle_message(interaction.channel, current_riddle)):
return
await finish_riddle(interaction.channel, client)
else:
# Update nb clues in DB
database.save_active_riddle(
channel_id=str(interaction.channel.id),
riddle_index=riddle_data["riddle_index"],
nb_clues=nb_clues,
message_id=riddle_data["message_id"],
solver_id=riddle_data["solver_id"],
)
if(not await update_riddle_message(interaction.channel, current_riddle)):
return
await interaction.response.send_message("Nouvel indice : `{0}`".format(clue_string(answer, nb_clues)))

View File

@ -25,15 +25,18 @@ async def on_ready():
global client, tree global client, tree
"""Initialize bot state and sync commands.""" """Initialize bot state and sync commands."""
# Import initialization functions # Import initialization functions
from fouras import load_riddles from fouras import load_riddles, setup_commands
from rhymes import _ensure_log_file, load_rhymes from rhymes import ensure_log_file, load_rhymes
# Ensure directories exist # Ensure directories exist
_ensure_log_file() ensure_log_file()
# Load riddles and answers # Load riddles and answers
load_riddles() load_riddles()
# register fouras commands
setup_commands(client, tree)
# Load rhymes # Load rhymes
success, msg = load_rhymes() success, msg = load_rhymes()
print(msg) print(msg)

View File

@ -1,7 +1,7 @@
# rhymes.py # rhymes.py
import json import json
import random import random
from datetime import datetime from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Tuple from typing import Any, Dict, Tuple
@ -13,7 +13,7 @@ RHYME_LOG_FILE = "data/rhyme_log.csv"
loaded_rhymes = {} loaded_rhymes = {}
def _ensure_log_file() -> None: def ensure_log_file() -> None:
"""Create CSV log file if it doesn't exist.""" """Create CSV log file if it doesn't exist."""
Path(RHYME_LOG_FILE).parent.mkdir(parents=True, exist_ok=True) Path(RHYME_LOG_FILE).parent.mkdir(parents=True, exist_ok=True)
@ -182,13 +182,7 @@ async def handle_rhyme_logic(message, client) -> bool:
if bool(random.getrandbits(1)): if bool(random.getrandbits(1)):
wait_time = random.randint(900, 10800) wait_time = random.randint(900, 10800)
new_cooldown_dt = now_dt.replace(second=0, microsecond=0) new_cooldown_dt = now_dt.replace(second=0, microsecond=0) + timedelta(seconds=wait_time)
new_cooldown_dt = new_cooldown_dt.replace(
minute=new_cooldown_dt.minute + wait_time // 60
)
new_cooldown_dt = new_cooldown_dt.replace(
hour=new_cooldown_dt.hour + wait_time // 3600
)
database.update_guild_state( database.update_guild_state(
guild_id, guild_id,
@ -207,7 +201,7 @@ async def handle_message(message, client) -> bool:
"""Main entry point for message handling.""" """Main entry point for message handling."""
# Initialize database and log file on first run # Initialize database and log file on first run
database.ensure_db() database.ensure_db()
_ensure_log_file() ensure_log_file()
# Handle debug commands first # Handle debug commands first
if await handle_debug_commands(message, client): if await handle_debug_commands(message, client):