# rhymes.py import json import random from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, Tuple import discord from discord import Interaction, app_commands import database RHYMES_FILE = "resources/rhymes.json" RHYME_LOG_FILE = "data/rhyme_log.csv" loaded_rhymes = {} def ensure_log_file() -> None: """Create CSV log file if it doesn't exist.""" Path(RHYME_LOG_FILE).parent.mkdir(parents=True, exist_ok=True) if not Path(RHYME_LOG_FILE).exists(): with open(RHYME_LOG_FILE, "w", encoding="utf-8") as f: f.write("timestamp,last_word,rhyme_triggered\n") def load_rhymes() -> Tuple[bool, str]: global loaded_rhymes """Load rhymes from JSON file. Returns (success, message).""" try: with open(RHYMES_FILE, "r", encoding="utf-8") as f: loaded_rhymes = json.load(f) return True, f'Loaded rhymes file "{RHYMES_FILE}"' except FileNotFoundError: return False, f'No rhymes file found at "{RHYMES_FILE}"' except json.JSONDecodeError as e: return False, f'Invalid JSON in "{RHYMES_FILE}": {e}' def log_rhyme(last_word: str, rhyme_triggered: str) -> None: """Log rhyme trigger to CSV file.""" timestamp = datetime.now().isoformat() with open(RHYME_LOG_FILE, "a", encoding="utf-8") as f: safe_rhyme = rhyme_triggered.replace(",", ";") f.write(f"{timestamp},{last_word},{safe_rhyme}\n") def get_last_word(text: str) -> str: """Extract last alphabetic word from text.""" truncated = text while True: if len(truncated) < 2 or truncated[-1].isnumeric(): return "" if truncated[-1].isalpha() and truncated[-2].isalpha(): break truncated = truncated[:-1] truncated = truncated.split(" ")[-1] return truncated if truncated.isalpha() else "" def find_rhyme(word: str) -> str: global loaded_rhymes """Find matching rhyme for given word.""" for rhyme in loaded_rhymes: if word in rhyme["blacklist"]: return "" if word.endswith(tuple(rhyme["keys"])): log_rhyme(word, rhyme["sound"]) return random.choice(rhyme["rhymes"]) return "" async def get_guild_name(guildId, client) -> str: guild = await client.fetch_guild(guildId) return "[Server={0}]".format(guild.name) async def handle_rhyme_logic(message, client) -> bool: """Main rhyme detection logic. Returns True if rhyme was triggered.""" message_content = message.content.lower() last_word = get_last_word(message_content) if message.author != client.user and message.guild and last_word: rhyme = find_rhyme(last_word) guild_id = str(message.guild.id) if rhyme: guild_state = database.get_guild_state(guild_id) cooldown_ratio = guild_state["cooldown_ratio"] if cooldown_ratio >= 0: # Check cooldown cooldown_dt = datetime.fromisoformat(guild_state["cooldown_until"]) now_dt = datetime.now() if now_dt >= cooldown_dt: self_control = guild_state["self_control"] # Probability check if random.random() < self_control: database.rhyme_damage_self_control(guild_id) return False database.rhyme_reset_cooldown(guild_id, cooldown_ratio) await message.channel.send(rhyme) return True return False async def handle_message(message, client) -> bool: """Main entry point for message handling.""" # Initialize database and log file on first run database.ensure_db() ensure_log_file() # Process rhyme logic return await handle_rhyme_logic(message, client) def setup_rhymes_commands(client: discord.Client, tree: discord.app_commands.CommandTree) -> None: @tree.command( name="config", description="Permet de configurer à quel point le Père Fouras est relou" ) @app_commands.default_permissions(manage_guild=True) @app_commands.checks.has_permissions(manage_guild=True) @app_commands.describe(ratio="ratio de cooldown (0 = )") async def config( interaction: discord.Interaction, ratio: float ): guild_id: str = str(interaction.guild_id) guild_state = database.get_guild_state(guild_id) database.rhyme_configure_cooldown_ratio(guild_id, ratio) msg = f"Le ratio de cooldown passe de {guild_state["cooldown_ratio"]:.2f} à {ratio:.2f} pour le serveur {interaction.guild.name}" if ratio < 0: await interaction.response.send_message(f"{msg}\nLes \"poil au\" sont désormais désactivés.", ephemeral=True) else: await interaction.response.send_message(f"{msg}\nLes \"poil au\" ont désormais un cooldown entre 0 et {int(180*ratio)} minutes.", ephemeral=True)