From 4fa7d5ca502d18192781907b0a08631da5f24e3a Mon Sep 17 00:00:00 2001 From: esefxdz Date: Wed, 23 Sep 2026 16:27:26 +0300 Subject: [PATCH] website patch --- firebase_website/__init__.py | 2 +- firebase_website/sysinfo_sync.py | 44 ++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/firebase_website/__init__.py b/firebase_website/__init__.py index 94c3845..ef4811e 100644 --- a/firebase_website/__init__.py +++ b/firebase_website/__init__.py @@ -32,4 +32,4 @@ async def setup(bot: commands.Bot) -> None: async def teardown(bot: commands.Bot) -> None: global _sync if _sync is not None: - _sync.stop() + await _sync.stop() diff --git a/firebase_website/sysinfo_sync.py b/firebase_website/sysinfo_sync.py index cfaf39a..1097f22 100644 --- a/firebase_website/sysinfo_sync.py +++ b/firebase_website/sysinfo_sync.py @@ -6,6 +6,7 @@ import re import time import psutil +from google.cloud import firestore from .config import get_db, SYSINFO_INTERVAL @@ -13,6 +14,8 @@ log = logging.getLogger(__name__) FASTFETCH_CACHE_SECS = 300 # refresh fastfetch every 5 minutes +VIRTUAL_NIC_PREFIXES = ("lo", "docker", "br-", "veth", "tailscale", "tun", "wg", "virbr") + def _fmt_size(b: int) -> str: """Bytes → human-readable string (auto-scales B → TB).""" @@ -23,6 +26,13 @@ def _fmt_size(b: int) -> str: return f"{b:.1f} PB" +def _fmt_duration(seconds: int) -> str: + days, r = divmod(seconds, 86400) + hours, r = divmod(r, 3600) + minutes, _ = divmod(r, 60) + return f"{days}d {hours}h {minutes}m" if days else f"{hours}h {minutes}m" + + class SysInfoSync: """Background task: collect system stats and push to Firestore.""" @@ -39,12 +49,19 @@ class SysInfoSync: self._task = asyncio.create_task(self._run()) log.info("SysInfo → Firestore sync started (every %ds)", SYSINFO_INTERVAL) - def stop(self) -> None: + async def stop(self) -> None: if self._task is None: return self._task.cancel() self._task = None log.info("SysInfo → Firestore sync stopped") + # Clean shutdown → flip the website dot to offline. Crashes can't do + # this; the website also treats a stale `updatedAt` as offline. + try: + await get_db().collection("sysinfo").document("server").set( + {"online": False, "updatedAt": firestore.SERVER_TIMESTAMP}, merge=True) + except Exception: + log.warning("Could not mark sysinfo offline", exc_info=True) # ── loop ──────────────────────────────────────────────────────────── @@ -77,12 +94,15 @@ class SysInfoSync: "swapUsed": stats["swap_used"], "swapTotal": stats["swap_total"], "uptime": stats["uptime"], + "botUptime": stats["bot_uptime"], + "processes": stats["processes"], "load1": stats["load1"], "load5": stats["load5"], "load15": stats["load15"], "netSent": stats["net_sent"], "netRecv": stats["net_recv"], "fetch": fetch, + "updatedAt": firestore.SERVER_TIMESTAMP, } db = get_db() @@ -97,13 +117,13 @@ class SysInfoSync: disk = psutil.disk_usage("/") swap = psutil.swap_memory() load1, load5, load15 = psutil.getloadavg() - net = psutil.net_io_counters() - uptime_seconds = int(time.time() - psutil.boot_time()) - - days, r = divmod(uptime_seconds, 86400) - hours, r = divmod(r, 3600) - minutes, _ = divmod(r, 60) - uptime_str = f"{days}d {hours}h {minutes}m" if days else f"{hours}h {minutes}m" + nics = psutil.net_io_counters(pernic=True) + physical = [c for name, c in nics.items() if not name.startswith(VIRTUAL_NIC_PREFIXES)] + net_sent = sum(c.bytes_sent for c in physical) + net_recv = sum(c.bytes_recv for c in physical) + now = time.time() + uptime_seconds = int(now - psutil.boot_time()) + bot_uptime_seconds = int(now - psutil.Process().create_time()) return { "cpu": round(cpu, 1), @@ -116,12 +136,14 @@ class SysInfoSync: "swap": round(swap.percent, 1), "swap_used": _fmt_size(swap.used), "swap_total": _fmt_size(swap.total), - "uptime": uptime_str, + "uptime": _fmt_duration(uptime_seconds), + "bot_uptime": _fmt_duration(bot_uptime_seconds), + "processes": len(psutil.pids()), "load1": round(load1, 1), "load5": round(load5, 1), "load15": round(load15, 1), - "net_sent": _fmt_size(net.bytes_sent), - "net_recv": _fmt_size(net.bytes_recv), + "net_sent": _fmt_size(net_sent), + "net_recv": _fmt_size(net_recv), } async def _get_fastfetch(self) -> str: