import asyncio from datetime import datetime, timezone from typing import Dict from services.runner import run_cmd def container_uptime(started_at: str) -> str: """ started_at: 2026-02-06T21:14:33.123456789Z """ try: start = datetime.fromisoformat( started_at.replace("Z", "+00:00") ).astimezone(timezone.utc) delta = datetime.now(timezone.utc) - start days = delta.days hours = delta.seconds // 3600 minutes = (delta.seconds % 3600) // 60 if days > 0: return f"{days}d {hours}h" if hours > 0: return f"{hours}h {minutes}m" return f"{minutes}m" except Exception: return "unknown" async def build_docker_map(cfg) -> Dict[str, str]: docker_cfg = cfg.get("docker", {}) result: Dict[str, str] = {} # 1. autodiscovery if docker_cfg.get("autodiscovery"): rc, raw = await run_cmd( ["sudo", "docker", "ps", "--format", "{{.Names}}"], timeout=20 ) if rc == 0: names = raw.splitlines() patterns = docker_cfg.get("match", []) for name in names: if any(p in name for p in patterns): result[name] = name # 2. aliases override aliases = docker_cfg.get("aliases", {}) for alias, real in aliases.items(): result[alias] = real return result async def discover_containers(cfg) -> Dict[str, str]: """ returns: alias -> real container name """ docker_cfg = cfg.get("docker", {}) result: Dict[str, str] = {} # --- autodiscovery --- if docker_cfg.get("autodiscovery"): rc, raw = await run_cmd( ["sudo", "docker", "ps", "--format", "{{.Names}}"], timeout=20 ) if rc == 0: found = raw.splitlines() label = docker_cfg.get("label") patterns = docker_cfg.get("match", []) for name in found: # label-based discovery if label: key, val = label.split("=", 1) rc2, lbl = await run_cmd([ "sudo", "docker", "inspect", "-f", f"{{{{ index .Config.Labels \"{key}\" }}}}", name ]) if rc2 == 0 and lbl.strip() == val: result[name] = name continue # name-pattern discovery if any(p in name for p in patterns): result[name] = name # --- manual aliases ALWAYS override --- aliases = docker_cfg.get("aliases", {}) for alias, real in aliases.items(): result[alias] = real return result async def docker_watchdog(container_map, notify, bot, chat_id): last = {} while True: for alias, real in container_map.items(): rc, state = await run_cmd( ["docker", "inspect", "-f", "{{.State.Status}}", real], timeout=10 ) if rc != 0: state = "error" state = state.strip() if last.get(alias) != state: await notify(bot, chat_id, f"🐳 {alias}: {state}") last[alias] = state await asyncio.sleep(120)