understanding commited on
Commit
69a35bd
·
verified ·
1 Parent(s): 65e4817

Update bot/core/speedtest.py

Browse files
Files changed (1) hide show
  1. bot/core/speedtest.py +56 -56
bot/core/speedtest.py CHANGED
@@ -1,82 +1,82 @@
1
  # PATH: bot/core/speedtest.py
 
2
  import os
3
  import time
4
- import shutil
5
- import secrets
6
  import httpx
7
 
8
- # Defaults (can be overridden via env)
9
- # Cloudflare speed test endpoints (no "HF" mention anywhere)
10
- DL_URL = os.environ.get("SPEEDTEST_DL_URL", "https://speed.cloudflare.com/__down")
11
- UP_URL = os.environ.get("SPEEDTEST_UP_URL", "https://speed.cloudflare.com/__up")
12
- PING_URL = os.environ.get("SPEEDTEST_PING_URL", "https://www.google.com/generate_204")
13
 
14
- DL_BYTES_DEFAULT = int(float(os.environ.get("SPEEDTEST_DL_BYTES", "8000000"))) # ~8MB
15
- UP_BYTES_DEFAULT = int(float(os.environ.get("SPEEDTEST_UP_BYTES", "3000000"))) # ~3MB
16
- TIMEOUT_SEC = float(os.environ.get("SPEEDTEST_TIMEOUT_SEC", "30"))
17
 
18
- def bytes_to_mb(x: float) -> float:
19
- # "real mb/s" => treat 1 MB = 1024*1024 bytes
20
- return float(x) / (1024.0 * 1024.0)
21
 
22
  def bytes_per_sec_to_mb_s(bps: float) -> float:
23
- return bytes_to_mb(bps)
 
24
 
25
  def disk_total_free(path: str = "/") -> dict:
26
- du = shutil.disk_usage(path)
27
- return {"total": du.total, "free": du.free, "used": du.used}
 
 
28
 
29
- async def ping_ms() -> float | None:
 
 
 
 
 
30
  t0 = time.perf_counter()
31
  try:
32
- async with httpx.AsyncClient(timeout=TIMEOUT_SEC, follow_redirects=True) as c:
33
- r = await c.get(PING_URL, headers={"cache-control": "no-cache"})
34
- # Even if status not 204, RTT still valid for "ping-like"
35
- _ = r.status_code
 
 
 
 
36
  return (time.perf_counter() - t0) * 1000.0
37
  except Exception:
38
  return None
39
 
40
- async def net_download_test(bytes_to_get: int | None = None) -> dict:
41
- n = int(bytes_to_get or DL_BYTES_DEFAULT)
 
 
 
 
42
  t0 = time.perf_counter()
43
  got = 0
44
 
45
- try:
46
- async with httpx.AsyncClient(timeout=TIMEOUT_SEC, follow_redirects=True) as c:
47
- # Cloudflare accepts ?bytes=...
48
- url = f"{DL_URL}?bytes={n}"
49
- async with c.stream("GET", url, headers={"cache-control": "no-cache"}) as r:
50
- r.raise_for_status()
51
- async for chunk in r.aiter_bytes():
52
- got += len(chunk)
53
- if got >= n:
54
- break
55
- except Exception:
56
- # return partial if any
57
- pass
58
 
59
- sec = max(0.001, time.perf_counter() - t0)
60
- bps = float(got) / sec
61
- return {"bytes": got, "seconds": sec, "bps": bps}
62
 
63
- async def net_upload_test(bytes_to_send: int | None = None) -> dict:
64
- n = int(bytes_to_send or UP_BYTES_DEFAULT)
65
- payload = secrets.token_bytes(n)
66
 
 
 
 
 
 
67
  t0 = time.perf_counter()
68
- sent = 0
69
- try:
70
- async with httpx.AsyncClient(timeout=TIMEOUT_SEC, follow_redirects=True) as c:
71
- # Cloudflare accepts ?bytes=... (and body upload)
72
- url = f"{UP_URL}?bytes={n}"
73
- r = await c.post(url, content=payload, headers={"content-type": "application/octet-stream"})
74
- # If server returns error, still count attempt
75
- _ = r.status_code
76
- sent = n
77
- except Exception:
78
- sent = 0
79
 
80
- sec = max(0.001, time.perf_counter() - t0)
81
- bps = float(sent) / sec
82
- return {"bytes": sent, "seconds": sec, "bps": bps}
 
 
 
 
 
 
1
  # PATH: bot/core/speedtest.py
2
+ import asyncio
3
  import os
4
  import time
5
+ from typing import Optional
6
+
7
  import httpx
8
 
9
+ CF_DOWN = "https://speed.cloudflare.com/__down"
10
+ CF_UP = "https://speed.cloudflare.com/__up"
11
+
 
 
12
 
13
+ def bytes_to_mb(n: float) -> float:
14
+ return float(n) / (1024.0 * 1024.0)
 
15
 
 
 
 
16
 
17
  def bytes_per_sec_to_mb_s(bps: float) -> float:
18
+ return float(bps) / (1024.0 * 1024.0)
19
+
20
 
21
  def disk_total_free(path: str = "/") -> dict:
22
+ st = os.statvfs(path)
23
+ total = st.f_frsize * st.f_blocks
24
+ free = st.f_frsize * st.f_bavail
25
+ return {"total": int(total), "free": int(free)}
26
 
27
+
28
+ async def ping_ms(host: str = "1.1.1.1", port: int = 443, timeout: float = 3.0) -> Optional[float]:
29
+ """
30
+ "Ping" approximation via TCP connect time (no ICMP).
31
+ Returns ms or None.
32
+ """
33
  t0 = time.perf_counter()
34
  try:
35
+ fut = asyncio.open_connection(host, port)
36
+ reader, writer = await asyncio.wait_for(fut, timeout=timeout)
37
+ try:
38
+ writer.close()
39
+ if hasattr(writer, "wait_closed"):
40
+ await writer.wait_closed()
41
+ except Exception:
42
+ pass
43
  return (time.perf_counter() - t0) * 1000.0
44
  except Exception:
45
  return None
46
 
47
+
48
+ async def net_download_test(bytes_target: int = 8 * 1024 * 1024, timeout: float = 15.0) -> dict:
49
+ """
50
+ Downloads bytes_target from Cloudflare endpoint and measures throughput.
51
+ """
52
+ url = f"{CF_DOWN}?bytes={int(bytes_target)}"
53
  t0 = time.perf_counter()
54
  got = 0
55
 
56
+ limits = httpx.Limits(max_connections=10, max_keepalive_connections=5)
57
+ async with httpx.AsyncClient(timeout=timeout, limits=limits, follow_redirects=True) as client:
58
+ async with client.stream("GET", url) as r:
59
+ r.raise_for_status()
60
+ async for chunk in r.aiter_bytes():
61
+ got += len(chunk)
 
 
 
 
 
 
 
62
 
63
+ sec = max(1e-6, time.perf_counter() - t0)
64
+ bps = got / sec
65
+ return {"bytes": int(got), "seconds": float(sec), "bps": float(bps)}
66
 
 
 
 
67
 
68
+ async def net_upload_test(bytes_target: int = 3 * 1024 * 1024, timeout: float = 20.0) -> dict:
69
+ """
70
+ Uploads bytes_target to Cloudflare endpoint and measures throughput.
71
+ """
72
+ payload = os.urandom(int(bytes_target))
73
  t0 = time.perf_counter()
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ limits = httpx.Limits(max_connections=10, max_keepalive_connections=5)
76
+ async with httpx.AsyncClient(timeout=timeout, limits=limits, follow_redirects=True) as client:
77
+ r = await client.post(CF_UP, content=payload, headers={"content-type": "application/octet-stream"})
78
+ r.raise_for_status()
79
+
80
+ sec = max(1e-6, time.perf_counter() - t0)
81
+ bps = bytes_target / sec
82
+ return {"bytes": int(bytes_target), "seconds": float(sec), "bps": float(bps)}