authenticate outgoing lichess.org requests to fix silent 404s on games/user
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 12s

Lichess started rejecting anonymous requests to games/user and
user/activity with a 404 (confirmed live: the same request with a valid
bearer token got a normal 429 instead), which our code silently read as
"user has no games" for every single tracked player, making periodic
checks report zero activity across the board. Add LICHESS_APP_TOKEN and
send it as a Bearer token on these two calls; it just needs to be any
valid token; it does not need to belong to the tracked player.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
vrubelroman 2026-07-25 23:01:35 +00:00
parent 08f97d413a
commit 7ef5875f58
4 changed files with 27 additions and 3 deletions

View file

@ -4,3 +4,9 @@ COMPOSE_PROJECT_NAME=lichess
# Telegram Bot Tokens (Test)
TELEGRAM_BOT_TOKEN=7903295042:AAGBO2k8pfBDy4RoLRFsknwE7z0N-thAPI8
ADMINPANEL_TELEGRAM_BOT_TOKEN=8588876086:AAHoZncfhTCbul1BblpvnZMzvz7jAYVFmcw
# Lichess personal API token used to authenticate outgoing requests to
# lichess.org (games/user, user/activity). Lichess started rejecting
# anonymous requests to these endpoints with a 404; any valid token works,
# it does not need to belong to a tracked player.
LICHESS_APP_TOKEN=lip_xxxxxxxxxxxxxxxxxxxx

View file

@ -14,6 +14,7 @@ Lichess Statistics API - Клиент для работы с Lichess API
"""
import httpx
import os
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import logging
@ -44,6 +45,15 @@ class LichessClient:
self.base_url = "https://lichess.org/api" # Базовый URL Lichess API
self.client = httpx.AsyncClient(timeout=30.0) # HTTP клиент с таймаутом
self.rate_limiter = get_rate_limiter()
# Lichess начал возвращать 404 (вместо честного 401/429) на анонимные
# запросы к games/user и user/activity — тот же валидный токен, отправленный
# для другого юзера, сразу превращает 404 в 429, то есть эндпоинт жив, просто
# больше не пускает анонимных. Токен не должен принадлежать конкретному
# отслеживаемому игроку — он только авторизует запрос, к его данным доступа
# не даёт (партии и так публичные).
self.app_token = os.getenv("LICHESS_APP_TOKEN")
if not self.app_token:
logger.warning("LICHESS_APP_TOKEN не задан — анонимные запросы к games/user и user/activity будут получать 404 от Lichess")
async def get_user_activity(self, username: str) -> Optional[List[Dict[str, Any]]]:
"""
@ -68,8 +78,10 @@ class LichessClient:
url = f"{self.base_url}/user/{username}/activity"
logger.info(f"🔍 Making request to Lichess API: {url}")
headers = {'Authorization': f'Bearer {self.app_token}'} if self.app_token else {}
# Выполняем HTTP GET запрос
response = await self.client.get(url)
response = await self.client.get(url, headers=headers)
logger.info(f"🔍 Lichess API response status: {response.status_code} for {username}")
response.raise_for_status() # Проверяем статус ответа
@ -135,6 +147,8 @@ class LichessClient:
headers = {
'Accept': 'application/x-ndjson' # Запрашиваем NDJSON формат
}
if self.app_token:
headers['Authorization'] = f'Bearer {self.app_token}'
logger.info(f"Запрос игр для {username} с {since_ms} по {until_ms}")

View file

@ -4,6 +4,8 @@ services:
container_name: lichess-api
ports:
- "8002:8000"
env_file:
- .env
environment:
- PYTHONUNBUFFERED=1
restart: always

View file

@ -6,6 +6,8 @@ services:
container_name: lichess-api
ports:
- "8002:8000"
env_file:
- .env
environment:
- PYTHONUNBUFFERED=1
volumes: