1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
#!/usr/bin/env python3
import chess
import chess.pgn
import io
import json
import os
import time
import urllib.request
OUT = "/var/www/aryanmustafa/api/dailypuzzle"
INTERVAL = 3600 # check every hour; Lichess updates once per day
os.makedirs(os.path.dirname(OUT), exist_ok=True)
while True:
try:
req = urllib.request.Request(
"https://lichess.org/api/puzzle/daily",
headers={"Accept": "application/json"},
)
with urllib.request.urlopen(req, timeout=10) as r:
raw = json.loads(r.read())
puzzle = raw["puzzle"]
game = raw["game"]
# Lichess returns the full game PGN; the puzzle starts at initialPly.
# Replay up to that ply with python-chess to get the exact FEN.
pgn_text = game["pgn"]
initial_ply = puzzle["initialPly"]
parsed = chess.pgn.read_game(io.StringIO(pgn_text))
board = parsed.board()
for i, move in enumerate(parsed.mainline_moves()):
board.push(move)
if i >= initial_ply:
break
result = {
"id": puzzle["id"],
"fen": board.fen(),
"side": "white" if board.turn == chess.WHITE else "black",
"solution": puzzle["solution"], # UCI list e.g. ["e2e4","d7d5"]
"rating": puzzle.get("rating"),
"themes": puzzle.get("themes", []),
"url": f"https://lichess.org/training/{puzzle['id']}",
}
tmp = OUT + ".tmp"
with open(tmp, "w") as f:
json.dump(result, f)
os.replace(tmp, OUT) # atomic write
print(f"updated: puzzle {result['id']}", flush=True)
except Exception as e:
print(f"error: {e}", flush=True)
time.sleep(INTERVAL)
|