Use NYT Wordle API for daily word instead of local list

Fetches today's word from https://www.nytimes.com/svc/wordle/v2/YYYY-MM-DD.json
so puzzle numbers and solutions match the official NYT Wordle. Results are
cached per day; falls back to the local answer list if the API is unreachable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 10:21:30 -05:00
parent d0fd260336
commit 1219b73492

View File

@@ -11,6 +11,7 @@ from dataclasses import dataclass, field
from datetime import date
from pathlib import Path
import aiohttp
from nio import AsyncClient
from utils import send_text, send_html, get_or_create_dm
@@ -27,6 +28,9 @@ logger = logging.getLogger("matrixbot")
_WORDLE_EPOCH = date(2021, 6, 19)
# Cache: date string -> (word, puzzle_number)
_nyt_cache: dict[str, tuple[str, int]] = {}
# Build lookup sets at import time
_ANSWER_LIST = [w.upper() for w in ANSWERS]
_VALID_SET = frozenset(w.upper() for w in VALID_GUESSES) | frozenset(_ANSWER_LIST)
@@ -152,9 +156,36 @@ def _record_game_result(player_id: str, game: WordleGame):
# Core algorithms
# ---------------------------------------------------------------------------
def get_daily_word() -> tuple[str, int]:
"""Return (word, puzzle_number) for today's daily puzzle."""
async def get_daily_word() -> tuple[str, int]:
"""Return (word, puzzle_number) for today's daily puzzle.
Fetches from the NYT Wordle API. Falls back to the local word list
if the request fails.
"""
today = date.today()
date_str = today.strftime("%Y-%m-%d")
if date_str in _nyt_cache:
return _nyt_cache[date_str]
try:
url = f"https://www.nytimes.com/svc/wordle/v2/{date_str}.json"
timeout = aiohttp.ClientTimeout(total=5)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(url) as resp:
if resp.status == 200:
data = await resp.json(content_type=None)
word = data["solution"].upper()
puzzle_number = int(data["id"])
_nyt_cache[date_str] = (word, puzzle_number)
logger.info("NYT Wordle #%d: %s", puzzle_number, word)
return word, puzzle_number
else:
logger.warning("NYT Wordle API returned %d, falling back to local list", resp.status)
except Exception as e:
logger.warning("Failed to fetch NYT Wordle word: %s — falling back to local list", e)
# Fallback: use local answer list
puzzle_number = (today - _WORDLE_EPOCH).days
word = _ANSWER_LIST[puzzle_number % len(_ANSWER_LIST)]
return word, puzzle_number
@@ -455,7 +486,7 @@ async def wordle_start_or_status(client: AsyncClient, room_id: str, sender: str,
return
# Check if already completed today's puzzle
word, puzzle_number = get_daily_word()
word, puzzle_number = await get_daily_word()
stats = _get_player_stats(sender)
if stats["last_daily"] == puzzle_number: