From 876c7d26d402e1c75c9ee9503f315e2c176f703d Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Tue, 21 Apr 2026 23:53:57 -0400 Subject: [PATCH] trivia: add 8 new categories + per-category dedup cache New categories: anime, sports, food, history, geography, nature, mythology, tv (14 total). Add _trivia_recent dict that tracks the last 20 questions per category and injects them into the LLM prompt as a avoid list, preventing duplicate questions within a session. Co-Authored-By: Claude Sonnet 4.6 --- matrixbot/commands.py | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/matrixbot/commands.py b/matrixbot/commands.py index e5dc769..dd35928 100644 --- a/matrixbot/commands.py +++ b/matrixbot/commands.py @@ -798,12 +798,20 @@ async def cmd_agent(client: AsyncClient, room_id: str, sender: str, args: str): _TRIVIA_CATEGORIES = { - "gaming": "video games, gaming history, game mechanics, esports", - "tech": "technology, programming, computers, the internet, software", - "general": "general knowledge, world facts, history, science, geography", - "movies": "movies, film history, actors, directors, pop culture", - "music": "music, bands, songs, music history, artists", - "science": "science, biology, physics, chemistry, space", + "gaming": "video games, gaming history, game mechanics, esports, retro gaming, game franchises", + "tech": "technology, programming, computers, the internet, software, hardware, open source, networking", + "general": "general knowledge, world facts, history, science, geography, politics, culture", + "movies": "movies, film history, actors, directors, pop culture, Oscar winners, franchises", + "music": "music, bands, songs, music history, artists, albums, genres", + "science": "science, biology, physics, chemistry, space, astronomy, mathematics, medicine", + "anime": "anime, manga, Japanese animation, Studio Ghibli, shonen, seinen, classic and modern series", + "sports": "sports, athletics, Olympic history, world records, famous athletes, major leagues", + "food": "food, cooking, cuisine, world dishes, ingredients, culinary history, chefs", + "history": "world history, ancient civilizations, wars, empires, historical figures, timelines", + "geography": "world geography, countries, capitals, rivers, mountains, flags, continents", + "nature": "nature, animals, wildlife, ecosystems, plants, oceans, weather, environment", + "mythology": "mythology, folklore, gods and goddesses, legends, Greek, Norse, Egyptian, world myths", + "tv": "television, TV shows, sitcoms, dramas, streaming originals, characters, actors", } _TRIVIA_FALLBACKS = [ @@ -830,12 +838,24 @@ _TRIVIA_FALLBACKS = [ ] +# Per-category cache of recently asked question texts (avoids duplicates) +_trivia_recent: dict[str, list[str]] = {} +_TRIVIA_RECENT_MAX = 20 + + async def _generate_trivia_question(category: str) -> dict | None: """Ask the LLM to generate a trivia question. Returns None on failure.""" topic = _TRIVIA_CATEGORIES.get(category, _TRIVIA_CATEGORIES["general"]) + recent = _trivia_recent.get(category, []) + avoid_clause = ( + " Do NOT ask any of these questions that were recently used: " + + "; ".join(f'"{q}"' for q in recent[-10:]) + + "." + ) if recent else "" prompt = ( - f"Generate a trivia question about {topic}. " - "Respond with ONLY a JSON object, no markdown, no explanation. " + f"Generate a trivia question about {topic}." + + avoid_clause + + " Respond with ONLY a JSON object, no markdown, no explanation. " 'Format: {"q": "question text", "options": ["A text", "B text", "C text", "D text"], "answer": 0} ' "where answer is the 0-based index of the correct option. " "The question should be clear, factual, and have exactly one correct answer." @@ -873,13 +893,18 @@ async def _generate_trivia_question(category: str) -> dict | None: and isinstance(parsed.get("answer"), int) and 0 <= parsed["answer"] <= 3 ): + # Record in recent cache to avoid future duplicates + bucket = _trivia_recent.setdefault(category, []) + bucket.append(parsed["q"]) + if len(bucket) > _TRIVIA_RECENT_MAX: + bucket.pop(0) return parsed except Exception: pass return None -@command("trivia", "Play a trivia game (!trivia [gaming|tech|general|movies|music|science])") +@command("trivia", "Play a trivia game (!trivia [category] — gaming, tech, science, movies, music, anime, sports, food, history, geography, nature, mythology, tv, general)") async def cmd_trivia(client: AsyncClient, room_id: str, sender: str, args: str): category = args.strip().lower() if args.strip().lower() in _TRIVIA_CATEGORIES else "general" if args.strip() and args.strip().lower() not in _TRIVIA_CATEGORIES: