from __future__ import annotations import asyncio import random import re from pathlib import Path from typing import Dict, List from snekbot.bot import Bot from ai import AI WORD_LIST_PATH = Path(__file__).with_suffix(".wordlist") if WORD_LIST_PATH.exists(): with WORD_LIST_PATH.open() as _f: DICTIONARY: List[str] = [w.strip().lower() for w in _f if len(w.strip()) == 5] else: DICTIONARY: List[str] = [] def squares(secret: str, guess: str) -> str: """Return 🟩🟨⬜ pattern for *guess* vs *secret*.""" res = ["⬜"] * 5 s_left = list(secret) g_left = list(guess) for i, (g, s) in enumerate(zip(g_left, s_left)): if g == s: res[i] = "🟩" s_left[i] = None g_left[i] = None for i, g in enumerate(g_left): if g and g in s_left: res[i] = "🟨" s_left[s_left.index(g)] = None return "".join(res) def _try_ai(system_prompt: str, **kwargs): try: kwargs["use_cache"] = False return AI(timeout=4).prompt(system_prompt, **kwargs) except Exception: return None def determine_action(msg: str) -> Dict[str, str]: sys = ( "You are WordleBot's intent detector.\n" "Reply ONLY with JSON: {'action':'new_game'} | {'action':'guess','guess':'xxxxx'} | {'action':'unknown'}.\n" "If the user clearly explictely wants a new game choose new_game, else extract the first 5‑letter word as guess. Chose unknown for anything else.\n" f"Message: {msg}" ) out = _try_ai(sys, json=True) if isinstance(out, dict) and out.get("action") != "unknown": print(msg, out) return out if re.search(r"\b(new game|reset|restart|start)\b", msg.lower()): return {"action": "new_game"} m = re.search(r"\b[a-z]{5}\b", msg.lower()) return {"action": "guess", "guess": m.group(0) if m else ""} def say(ctx: str) -> str: sys = ( "You are WordleBot, an upbeat, game‑show‑style host! Use emojis and exclamation marks, max 140 chars.\n" "Respond ONLY with JSON: {'text':'...'}\n" f"Context: {ctx}" ) out = _try_ai(sys, json=True) if isinstance(out, dict) and "text" in out: return out["text"] return ctx def imagine_word() -> str: with open("wordle.wordlist", "r") as f: words = [word.strip() for word in f.read().split("\n") if word.strip()] return random.choice(words) class WordleBot(Bot): MAX_TRIES = 6 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.games: Dict[str, Dict] = {} def _start_game(self, chan: str): self.games[chan] = {"word": imagine_word(), "guesses": {}, "lost": set()} def _add_guess(self, chan: str, user: str, word: str): self.games[chan]["guesses"].setdefault(user, []).append(word) def _log_state(self, chan: str): g = self.games.get(chan) if not g: print(f"[CHAN {chan}] no active game") return parts = ( [f"word={g['word']}"] + [f"{u}:{v}" for u, v in g["guesses"].items()] + [f"lost:{g['lost']}"] ) print(f"[CHAN {chan}] " + " | ".join(parts)) async def on_mention( self, username: str, user_nick: str | None, channel_uid: str, message: str ): intent = determine_action(message) if intent["action"] == "new_game": self._start_game(channel_uid) self._log_state(channel_uid) return await self.send_message( channel_uid, say( "🎉 New round! A fresh 5‑letter mystery awaits – fire your first shot!" ), ) guess = intent.get("guess", "").lower() if len(guess) != 5 or not guess.isalpha(): self._log_state(channel_uid) return await self.send_message( channel_uid, say("⛔ Oops! I need a real 5‑letter word – try again!") ) if channel_uid not in self.games: self._start_game(channel_uid) game = self.games[channel_uid] self._add_guess(channel_uid, username, guess) patt = squares(game["word"], guess) used = len(game["guesses"][username]) left = self.MAX_TRIES - used if guess == game["word"]: msg = say( f"🏆 {guess.upper()} {patt} – spotlight on {user_nick or username}! Fancy another round?" ) del self.games[channel_uid] self._log_state(channel_uid) return await self.send_message(channel_uid, msg) if left == 0: game["lost"].add(username) everyone = set(game["guesses"].keys()) if game["lost"] == everyone and everyone: ans = game["word"].upper() msg = say( f"🛑 All challengers down! The word was {ans}. Shall we spin up a new puzzle?" ) del self.games[channel_uid] self._log_state(channel_uid) return await self.send_message(channel_uid, msg) bust_msg = say( f"💀 {guess.upper()} {patt}. You're out of turns – cheer on the rest!" ) self._log_state(channel_uid) return await self.send_message(channel_uid, bust_msg) feedback = f"{patt} ({left} left)" self._log_state(channel_uid) return await self.send_message(channel_uid, feedback) if __name__ == "__main__": bot = WordleBot(username="wordlebot", password="thetopsecretpassword") asyncio.run(bot.run())