Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ data/exports/
.coverage.*
htmlcov/
.pytest_cache/
.pre-commit-cache/
.pre-commit-home/
.ai/*
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ ffpy-pickem-web = "ffpy.pickem_web:main"
ffpy-league-api = "ffpy.league_api:main"
ffpy-web = "ffpy.unified_web:main"
ffpy-sleeper = "ffpy.sleeper_web.main:main"
ffpy-ingest = "ffpy.ingest.cli:main"

[build-system]
requires = ["uv_build>=0.9.18,<0.10.0"]
Expand Down
17 changes: 17 additions & 0 deletions src/ffpy/ingest/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""FFPy league data ingestion CLI.

Usage:
ffpy-ingest espn <league_id> [--season N] [--json|--csv] [--db PATH] [--swid ...] [--s2 ...]
ffpy-ingest yahoo <league_id> [--season N] [--json|--csv] [--db PATH] [--token ...]
ffpy-ingest sleeper <league_id> [--season N] [--json|--csv] [--db PATH]
ffpy-ingest yahoo-auth
ffpy-ingest yahoo-token --code CODE
ffpy-ingest leagues-list [--json|--csv] [--db PATH]
ffpy-ingest leagues-info <id> [--json|--csv] [--db PATH]
ffpy-ingest roster <league_id> <team_id> [--json|--csv] [--db PATH]
ffpy-ingest matchups <league_id> <week> [--json|--csv] [--db PATH]
"""

from . import auth, cli, espn, output, sleeper, yahoo

__all__ = ["auth", "cli", "espn", "output", "sleeper", "yahoo"]
92 changes: 92 additions & 0 deletions src/ffpy/ingest/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Auth helpers for ingest CLI: cookie files, token file I/O, env helpers."""

from __future__ import annotations

import json
import logging
import os
import time
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

TOKEN_DIR = Path.home() / ".ffpy"
TOKEN_FILE = TOKEN_DIR / "yahoo_token.json"
COOKIE_FILE = TOKEN_DIR / "espn_cookies.json"


def _ensure_dir() -> None:
TOKEN_DIR.mkdir(parents=True, exist_ok=True)


# ---------------------------------------------------------------------------
# Yahoo token file
# ---------------------------------------------------------------------------


def load_yahoo_token() -> Optional[dict]:
"""Load Yahoo OAuth token from ~/.ffpy/yahoo_token.json if valid."""
if not TOKEN_FILE.exists():
return None
try:
data = json.loads(TOKEN_FILE.read_text())
except (json.JSONDecodeError, OSError):
logger.warning("Corrupt Yahoo token file: %s", TOKEN_FILE)
return None

expires_at = data.get("expires_at", 0)
if time.time() >= expires_at:
logger.info("Yahoo token expired — caller should refresh")
return data


def save_yahoo_token(token: dict) -> None:
"""Persist Yahoo OAuth token to ~/.ffpy/yahoo_token.json."""
_ensure_dir()
TOKEN_FILE.write_text(json.dumps(token, indent=2))
TOKEN_FILE.chmod(0o600)
logger.info("Yahoo token saved to %s", TOKEN_FILE)
Comment thread
Copilot marked this conversation as resolved.


def delete_yahoo_token() -> None:
"""Remove stored Yahoo token."""
if TOKEN_FILE.exists():
TOKEN_FILE.unlink()
logger.info("Yahoo token deleted")


# ---------------------------------------------------------------------------
# ESPN cookie file
# ---------------------------------------------------------------------------


def load_espn_cookies() -> tuple[str, str]:
"""Load ESPN cookies from ~/.ffpy/espn_cookies.json or env vars."""
swid = os.getenv("ESPN_SWID", "")
s2 = os.getenv("ESPN_S2", "")

if not swid or not s2:
if COOKIE_FILE.exists():
try:
data = json.loads(COOKIE_FILE.read_text())
swid = data.get("swid", swid)
s2 = data.get("espn_s2", s2)
except (json.JSONDecodeError, OSError):
pass

return swid, s2


def save_espn_cookies(swid: str, espn_s2: str) -> None:
"""Persist ESPN cookies to ~/.ffpy/espn_cookies.json."""
_ensure_dir()
COOKIE_FILE.write_text(json.dumps({"swid": swid, "espn_s2": espn_s2}, indent=2))
COOKIE_FILE.chmod(0o600)
logger.info("ESPN cookies saved to %s", COOKIE_FILE)
Comment thread
Copilot marked this conversation as resolved.


def delete_espn_cookies() -> None:
if COOKIE_FILE.exists():
COOKIE_FILE.unlink()
logger.info("ESPN cookies deleted")
Loading