"""Empêche le conteneur de s'endormir en pingant /api/health en boucle. Un Space Hugging Face du tier gratuit se met en veille après une période sans trafic HTTP ; le réveil suivant coûte plusieurs dizaines de secondes. Ce script génère ce trafic minimal depuis une machine qui, elle, reste allumée. Usage : python scripts/keep_awake.py https://samdnx-msg-motoc.hf.space python scripts/keep_awake.py http://127.0.0.1:7860 --interval 60 Options : --interval N secondes entre deux pings (défaut : 600) --timeout N délai d'attente d'une réponse (défaut : 15) --once un seul ping, puis sortie — pratique pour cron Code de sortie : 0 si le dernier ping a réussi, 1 sinon. Stdlib uniquement : aucune dépendance à installer côté machine appelante. """ from __future__ import annotations import argparse import sys import time import urllib.error import urllib.request from datetime import datetime, timezone USER_AGENT = "msg-motoc-keepalive/1" def log(message: str) -> None: stamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%SZ") print(f"[{stamp}] {message}", flush=True) def ping(url: str, timeout: float) -> bool: request = urllib.request.Request(url, headers={"User-Agent": USER_AGENT}) started = time.monotonic() try: with urllib.request.urlopen(request, timeout=timeout) as response: elapsed = (time.monotonic() - started) * 1000 log(f"{response.status} en {elapsed:.0f} ms") return response.status == 200 except urllib.error.HTTPError as exc: log(f"HTTP {exc.code} — {exc.reason}") except (urllib.error.URLError, TimeoutError, OSError) as exc: log(f"injoignable — {exc}") return False def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("base_url", help="racine du service, ex. https://x.hf.space") parser.add_argument("--interval", type=float, default=600.0) parser.add_argument("--timeout", type=float, default=15.0) parser.add_argument("--once", action="store_true") args = parser.parse_args() url = args.base_url.rstrip("/") + "/api/health" log(f"ping de {url} toutes les {args.interval:.0f} s") ok = ping(url, args.timeout) if args.once: return 0 if ok else 1 # Boucle infinie : on ne s'arrête jamais sur un échec, le service peut être # en cours de redémarrage ou de réveil (le ping raté est ce qui le réveille). while True: try: time.sleep(args.interval) except KeyboardInterrupt: log("arrêt") return 0 ok = ping(url, args.timeout) if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: sys.exit(0)