File size: 2,796 Bytes
f7fa177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""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)