diff --git a/matrixbot/commands.py b/matrixbot/commands.py index 859dfa9..cb31e50 100644 --- a/matrixbot/commands.py +++ b/matrixbot/commands.py @@ -1647,11 +1647,15 @@ def record_wyr_vote(event_id: str, sender: str, key: str) -> None: async def _generate_wyr() -> dict | None: system_msg = ( - "You are a game host generating Would You Rather questions. " + "You are a game host generating Would You Rather questions for a group of adult friends. " "Always respond with ONLY a JSON object — no markdown fences, no explanation. " - 'Format: {"question": "Would you rather...", "option_a": "short option", "option_b": "short option"}' + 'Format: {"question": "Would you rather...", "option_a": "short option", "option_b": "short option"}\n' + "Make the dilemma genuinely difficult — both options should have real downsides so it's actually a hard choice. " + "Be creative and edgy: embarrassing scenarios, weird superpowers, social nightmares, gross but survivable situations, " + "impossible tradeoffs. Avoid boring safe options like 'dance with dolphins' or 'sing karaoke'. " + "Keep each option under 10 words." ) - user_msg = "Generate a fun, creative Would You Rather question. Keep each option under 10 words." + user_msg = "Generate a spicy, genuinely difficult Would You Rather question." try: timeout = aiohttp.ClientTimeout(total=60) async with aiohttp.ClientSession(timeout=timeout) as session: @@ -1774,9 +1778,16 @@ async def _generate_riddle() -> dict | None: ) if _riddle_recent else "" system_msg = ( "You are a riddle generator. Always respond with ONLY a JSON object — no markdown fences, no explanation. " - 'Format: {"riddle": "the riddle text", "answer": "short answer"}' + 'Format: {"riddle": "the riddle text", "answer": "short answer"}\n' + "Rules for a good riddle:\n" + "- The answer must be a specific, unambiguous noun (1-3 words). Avoid abstract answers.\n" + "- The riddle must describe the answer through metaphor or wordplay — NOT by literally describing it.\n" + "- Do NOT include the answer word anywhere in the riddle text.\n" + "- Do NOT end the riddle with 'what am I?' or 'what could it possibly mean?' — the riddle should stand alone.\n" + "- The clues must logically point to ONE specific answer. Test it: would most people agree this answer is correct?\n" + "- Avoid riddles about riddles, shadows, or abstract concepts. Prefer concrete things: candle, mirror, clock, river, echo, stamp, etc." ) - user_msg = f"Generate a clever, original riddle. The answer should be 1-4 words.{avoid_clause}" + user_msg = f"Generate a clever, original riddle with a clear unambiguous answer.{avoid_clause}" try: timeout = aiohttp.ClientTimeout(total=60) async with aiohttp.ClientSession(timeout=timeout) as session: @@ -1856,14 +1867,26 @@ async def cmd_riddle(client: AsyncClient, room_id: str, sender: str, args: str): _RIDDLE_ACTIVE[room_id]["task"] = task +def _riddle_matches(answer: str, body: str) -> bool: + """Fuzzy match: strip articles, allow the core word to appear in the guess or vice versa.""" + def _normalize(s: str) -> str: + s = s.strip().lower() + for art in ("a ", "an ", "the "): + if s.startswith(art): + s = s[len(art):] + return s.strip() + + ans = _normalize(answer) + guess = _normalize(body) + return ans == guess or ans in guess or guess in ans + + async def check_riddle_answer(client: AsyncClient, room_id: str, sender: str, body: str) -> bool: """Check if a room message answers the active riddle. Returns True if correct.""" if room_id not in _RIDDLE_ACTIVE: return False game = _RIDDLE_ACTIVE[room_id] - answer_lower = game["answer"].lower() - body_lower = body.strip().lower() - if answer_lower in body_lower: + if _riddle_matches(game["answer"], body.strip()): task = game.get("task") if task: task.cancel()