diff --git a/services/telegram-bot/bot.py b/services/telegram-bot/bot.py index 3d0e0c1..d12f600 100644 --- a/services/telegram-bot/bot.py +++ b/services/telegram-bot/bot.py @@ -18,7 +18,6 @@ NAS_USER = "daniely" MASTER_IP = "192.168.128.80" # --- LISTE DISPOSITIVI --- -# Core: Dispositivi intelligenti (SSH, CPU, RAM) CORE_DEVICES = [ {"name": "๐Ÿ“ Pi-1 (Master)", "ip": MASTER_IP, "type": "pi", "user": SSH_USER}, {"name": "๐Ÿ“ Pi-2 (Backup)", "ip": "127.0.0.1", "type": "local", "user": ""}, @@ -26,7 +25,6 @@ CORE_DEVICES = [ {"name": "๐Ÿ—„๏ธ NAS 214", "ip": "192.168.128.90", "type": "nas", "user": NAS_USER} ] -# Infra: Solo Ping e Riavvio (Switch, WiFi) INFRA_DEVICES = [ {"name": "๐Ÿ“ก Router", "ip": "192.168.128.1"}, {"name": "๐Ÿ“ถ WiFi Sala", "ip": "192.168.128.101"}, @@ -47,27 +45,41 @@ logger = logging.getLogger(__name__) def run_cmd(command, ip=None, user=None): try: if ip == "127.0.0.1" or ip is None: - return subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip() + return subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT, timeout=5).decode('utf-8').strip() else: safe_cmd = command.replace("'", "'\\''") - full_cmd = f"ssh -o LogLevel=ERROR -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=4 {user}@{ip} '{safe_cmd}'" - return subprocess.check_output(full_cmd, shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip() + full_cmd = f"ssh -o LogLevel=ERROR -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=3 {user}@{ip} '{safe_cmd}'" + return subprocess.check_output(full_cmd, shell=True, stderr=subprocess.STDOUT, timeout=8).decode('utf-8').strip() except Exception: return "Err" def get_ping_icon(ip): + print(f"DEBUG: Pinging {ip}...") # LOG PER CAPIRE DOVE SI BLOCCA try: - # Ping ultra-rapido (Timeout 1s) - subprocess.check_call(["ping", "-c", "1", "-W", "1", ip], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # Timeout aggressivo: 0.5 secondi (-W 1 รจ il minimo di ping standard, ma Python taglia a 0.8) + subprocess.run( + ["ping", "-c", "1", "-W", "1", ip], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + timeout=0.8, # Timeout Python brutale + check=True + ) return "โœ…" - except: return "๐Ÿ”ด" + except subprocess.TimeoutExpired: + return "๐Ÿ”ด" # Timeout Python + except subprocess.CalledProcessError: + return "๐Ÿ”ด" # Risposta "Host Unreachable" + except Exception as e: + print(f"Errore Ping {ip}: {e}") + return "โ“" def get_device_stats(device): ip, user, dtype = device['ip'], device['user'], device['type'] uptime_raw = run_cmd("uptime -p 2>/dev/null || uptime", ip, user) if not uptime_raw or "Err" in uptime_raw: return "๐Ÿ”ด **OFFLINE**" - uptime = uptime_raw.replace("up ", "").split(", load")[0].split(", ")[0] + uptime = uptime_raw.replace("up ", "").split(", load")[0].split(", ")[0] temp = "N/A" + if dtype in ["pi", "local"]: t = run_cmd("cat /sys/class/thermal/thermal_zone0/temp", ip, user) if t.isdigit(): temp = f"{int(t)/1000:.1f}ยฐC" @@ -78,6 +90,7 @@ def get_device_stats(device): if dtype == "nas": ram_cmd = "free | grep Mem | awk '{printf \"%.0f%%\", $3*100/$2}'" else: ram_cmd = "free -m | awk 'NR==2{if ($2>0) printf \"%.0f%%\", $3*100/$2; else print \"0%\"}'" + disk_path = "/" if dtype != "nas" else "/volume1" disk_cmd = f"df -h {disk_path} | awk 'NR==2{{print $5}}'" @@ -92,7 +105,7 @@ def run_speedtest(): try: return subprocess.check_output("speedtest-cli --simple", shell=True, timeout=50).decode('utf-8') except: return "Errore Speedtest" -# --- GESTIONE MENU --- +# --- BOT HANDLERS --- def restricted(func): @wraps(func) async def wrapped(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs): @@ -102,18 +115,16 @@ def restricted(func): @restricted async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - # MENU PRINCIPALE keyboard = [ [InlineKeyboardButton("๐Ÿ–ฅ๏ธ Core Server", callback_data="menu_core"), InlineKeyboardButton("๐Ÿ” Scan LAN", callback_data="menu_lan")], [InlineKeyboardButton("๐Ÿ›ก๏ธ Pi-hole", callback_data="menu_pihole"), InlineKeyboardButton("๐ŸŒ Rete", callback_data="menu_net")], [InlineKeyboardButton("๐Ÿ“œ Logs", callback_data="menu_logs")] ] - text = "๐ŸŽ› **Loogle Control Center**\nSeleziona un pannello:" + text = "๐ŸŽ› **Loogle Control Center v6.2**\nSeleziona un pannello:" if update.message: await update.message.reply_text(text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode="Markdown") else: - # Se chiamato da un pulsante "Indietro", modifica il messaggio esistente await update.callback_query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode="Markdown") @restricted @@ -124,7 +135,6 @@ async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> if data == "main_menu": await start(update, context) - # --- MENU CORE --- elif data == "menu_core": keyboard = [] for i, dev in enumerate(CORE_DEVICES): keyboard.append([InlineKeyboardButton(dev['name'], callback_data=f"stat_{i}")]) @@ -143,42 +153,43 @@ async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> await query.edit_message_text(f"โณ Controllo {dev['name']}...", parse_mode="Markdown") await query.edit_message_text(f"๐Ÿ”น **{dev['name']}**\n\n{get_device_stats(dev)}", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("โฌ…๏ธ Indietro", callback_data="menu_core")]]), parse_mode="Markdown") - # --- MENU LAN (DIAGNOSTICA) --- elif data == "menu_lan": - await query.edit_message_text("โณ **Ping test in corso...**", parse_mode="Markdown") - + await query.edit_message_text("โณ **Scansione LAN rapida...**", parse_mode="Markdown") report = "๐Ÿ” **DIAGNOSTICA LAN**\n\n" - # Genera tabella compatta - for dev in INFRA_DEVICES: - status = get_ping_icon(dev['ip']) - # Aggiunge riga: Icona Nome (IP) - report += f"{status} **{dev['name']}**\n`{dev['ip']}`\n" + try: + # Core Devices + for dev in CORE_DEVICES: + report += f"{get_ping_icon(dev['ip'])} `{dev['ip']}` - {dev['name']}\n" + report += "\n" + + # Infra Devices + for dev in INFRA_DEVICES: + report += f"{get_ping_icon(dev['ip'])} `{dev['ip']}` - {dev['name']}\n" + + except Exception as e: + report += f"\nโš ๏ธ Errore imprevisto durante scansione: {e}" + keyboard = [ - [InlineKeyboardButton("โšก Menu Riavvio Dispositivi", callback_data="menu_reboot")], - [InlineKeyboardButton("๐Ÿ”„ Aggiorna", callback_data="menu_lan")], - [InlineKeyboardButton("โฌ…๏ธ Indietro", callback_data="main_menu")] + [InlineKeyboardButton("โšก Menu Riavvio", callback_data="menu_reboot")], + [InlineKeyboardButton("๐Ÿ”„ Aggiorna", callback_data="menu_lan"), InlineKeyboardButton("โฌ…๏ธ Indietro", callback_data="main_menu")] ] await query.edit_message_text(report, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode="Markdown") - # --- MENU RIAVVIO --- elif data == "menu_reboot": keyboard = [] for i, dev in enumerate(INFRA_DEVICES): - if "Router" not in dev['name']: # Protezione router - keyboard.append([InlineKeyboardButton(f"โšก {dev['name']}", callback_data=f"reboot_{i}")]) + if "Router" not in dev['name']: keyboard.append([InlineKeyboardButton(f"โšก {dev['name']}", callback_data=f"reboot_{i}")]) keyboard.append([InlineKeyboardButton("โฌ…๏ธ Indietro", callback_data="menu_lan")]) - await query.edit_message_text("โš ๏ธ **RIAVVIO REMOTO**\nRichiede SSH configurato sul dispositivo target (admin).", reply_markup=InlineKeyboardMarkup(keyboard), parse_mode="Markdown") + await query.edit_message_text("โš ๏ธ **RIAVVIO REMOTO**\nFunziona solo se il dispositivo supporta SSH e hai le chiavi.", reply_markup=InlineKeyboardMarkup(keyboard), parse_mode="Markdown") elif data.startswith("reboot_"): dev = INFRA_DEVICES[int(data.split("_")[1])] - # Tenta riavvio con utente 'admin' (standard per Zyxel/AP) res = run_cmd("reboot", dev['ip'], "admin") - await query.edit_message_text(f"โšก Comando inviato a {dev['name']}...\n\nRisposta:\n`{res}`", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("โฌ…๏ธ Indietro", callback_data="menu_reboot")]]), parse_mode="Markdown") - # --- ALTRI MENU (Pi-hole, Net, Logs) --- + # --- ALTRI MENU STANDARD --- elif data == "menu_pihole": status_raw = run_cmd("sudo pihole status", MASTER_IP, SSH_USER) icon = "โœ…" if "Enabled" in status_raw or "enabled" in status_raw else "๐Ÿ”ด" @@ -198,7 +209,7 @@ async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> await query.edit_message_text(f"๐ŸŒ **Rete**\n๐ŸŒ IP: `{ip}`", reply_markup=InlineKeyboardMarkup(keyboard), parse_mode="Markdown") elif data == "net_speedtest": - await query.edit_message_text("๐Ÿš€ **Speedtest...**", parse_mode="Markdown") + await query.edit_message_text("๐Ÿš€ **Speedtest in corso...**", parse_mode="Markdown") res = run_speedtest() await query.edit_message_text(f"๐Ÿš€ **Risultato:**\n\n`{res}`", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("โฌ…๏ธ Indietro", callback_data="menu_net")]]), parse_mode="Markdown")