diff --git a/services/telegram-bot/arome_snow_alert.py b/services/telegram-bot/arome_snow_alert.py new file mode 100644 index 0000000..72eed29 --- /dev/null +++ b/services/telegram-bot/arome_snow_alert.py @@ -0,0 +1,110 @@ +import requests +import datetime +from dateutil import parser +from zoneinfo import ZoneInfo + +# --- CONFIGURAZIONE UTENTE --- +TELEGRAM_BOT_TOKEN = "8155587974:AAF9OekvBpixtk8ZH6KoIc0L8edbhdXt7A4" +TELEGRAM_CHAT_ID = "64463169" + +# Coordinate San Marino +LATITUDE = 43.9356 +LONGITUDE = 12.4296 + +# Soglia minima per notifica (es. 0.0 per qualsiasi nevicata) +SOGLIA_NOTIFICA = 0.0 + +def send_telegram_message(message): + if not TELEGRAM_BOT_TOKEN or "INSERISCI" in TELEGRAM_BOT_TOKEN: + print(f"[TEST - NO TELEGRAM] {message}") + return + + url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" + payload = { + "chat_id": TELEGRAM_CHAT_ID, + "text": message, + "parse_mode": "Markdown" + } + try: + requests.post(url, json=payload, timeout=10) + except Exception as e: + print(f"Errore invio Telegram: {e}") + +def get_arome_forecast(): + url = "https://api.open-meteo.com/v1/forecast" + params = { + "latitude": LATITUDE, + "longitude": LONGITUDE, + "hourly": "snowfall", + "models": "arome_france_hd", + "timezone": "Europe/Rome", + "forecast_days": 2 + } + + try: + response = requests.get(url, params=params, timeout=10) + response.raise_for_status() + return response.json() + except Exception as e: + print(f"Errore richiesta API: {e}") + return None + +def analyze_snow(): + data = get_arome_forecast() + if not data: + return + + hourly_data = data.get("hourly", {}) + times = hourly_data.get("time", []) + snowfall = hourly_data.get("snowfall", []) + + if not times or not snowfall: + print("Dati meteo incompleti.") + return + + # Otteniamo l'ora corrente + now = datetime.datetime.now(ZoneInfo("Europe/Rome")) + + # Sincronizzazione orario + start_index = -1 + for i, t_str in enumerate(times): + try: + t_obj = parser.isoparse(t_str).replace(tzinfo=ZoneInfo("Europe/Rome")) + if t_obj >= now.replace(minute=0, second=0, microsecond=0): + start_index = i + break + except Exception: + continue + + if start_index == -1: + print("Impossibile sincronizzare l'orario dei dati.") + return + + # Slice dati 12h e 24h + end_12h = min(start_index + 12, len(snowfall)) + end_24h = min(start_index + 24, len(snowfall)) + + snow_next_12h = snowfall[start_index:end_12h] + snow_next_24h = snowfall[start_index:end_24h] + + # --- CORREZIONE ERRORE --- + # Convertiamo eventuali None in 0.0 prima di sommare + sum_12h = sum(x if x is not None else 0.0 for x in snow_next_12h) + sum_24h = sum(x if x is not None else 0.0 for x in snow_next_24h) + + print(f"Check ore {now.strftime('%H:%M')}: 12h={sum_12h}cm, 24h={sum_24h}cm") + + if sum_24h > SOGLIA_NOTIFICA: + msg = ( + f"⚠️ **ALLERTA NEVE SAN MARINO** ⚠️\n" + f"Modello: AROME-HD (1.3km)\n\n" + f"❄️ **Prossime 12 ore:** {sum_12h:.1f} cm\n" + f"🌨️ **Prossime 24 ore:** {sum_24h:.1f} cm\n\n" + f"📅 _Previsto a partire dalle {now.strftime('%H:%M')}_" + ) + send_telegram_message(msg) + else: + print("Nessuna allerta necessaria (sotto soglia).") + +if __name__ == "__main__": + analyze_snow() diff --git a/services/telegram-bot/daily_report.py b/services/telegram-bot/daily_report.py index c427578..0eab385 100644 --- a/services/telegram-bot/daily_report.py +++ b/services/telegram-bot/daily_report.py @@ -41,9 +41,8 @@ def generate_report(): try: conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() - # Calcola l'orario UTC di 24h fa (perché il DB è in UTC) + # Timezone UTC per la query yesterday = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=24) - query = "SELECT download, upload, ping, created_at FROM results WHERE created_at > ? AND status = 'completed' ORDER BY created_at ASC" cursor.execute(query, (yesterday,)) rows = cursor.fetchall() @@ -57,23 +56,24 @@ def generate_report(): print("ℹ️ Nessun test trovato.") return None - # Variabili per statistiche + # Variabili total_down = 0 total_up = 0 count = 0 issues = 0 - # Intestazione Messaggio - # Usiamo l'ora locale del sistema per l'intestazione + # Intestazione now_local = datetime.datetime.now() msg = f"📊 **REPORT VELOCITÀ 24H**\n📅 {now_local.strftime('%d/%m/%Y')}\n\n" + # Inizio Tabella Monospazio + # Allarghiamo le colonne per farci stare "Mb" + # ORA (5) | DOWN (6) | UP (5) msg += "```text\n" - msg += "ORA | DOWN | UP \n" - msg += "------+------+-----\n" + msg += "ORA | DOWN | UP \n" + msg += "------+--------+------\n" for row in rows: - # Conversione Bit -> Mbps d_val = (int(row[0]) * 8) / 1000000 u_val = (int(row[1]) * 8) / 1000000 @@ -86,21 +86,22 @@ def generate_report(): issues += 1 marker = "!" - # Parsing Data e CONVERSIONE TIMEZONE try: d_str = row[3].split(".")[0].replace("T", " ") - # 1. Creiamo l'oggetto datetime e gli diciamo "Tu sei UTC" dt_utc = datetime.datetime.strptime(d_str, '%Y-%m-%d %H:%M:%S').replace(tzinfo=datetime.timezone.utc) - - # 2. Convertiamo nell'orario locale del sistema (definito da TZ nel docker-compose) dt_local = dt_utc.astimezone() - time_str = dt_local.strftime('%H:%M') except: time_str = "--:--" - # Formattazione tabella - row_str = f"{time_str} | {d_val:>4.0f} | {u_val:>3.0f} {marker}" + # FORMATTAZIONE CON UNITÀ + # Creiamo stringhe tipo "850Mb" + d_text = f"{int(d_val)}Mb" + u_text = f"{int(u_val)}Mb" + + # Allineamento: + # :>6 significa "occupa 6 spazi allineato a destra" + row_str = f"{time_str} | {d_text:>6} | {u_text:>5} {marker}" msg += f"{row_str}\n" msg += "```\n" @@ -112,7 +113,8 @@ def generate_report(): icon_d = "✅" if avg_d >= WARN_DOWN else "⚠️" icon_u = "✅" if avg_u >= WARN_UP else "⚠️" - msg += f"📈 **MEDIA:**\n⬇️ {icon_d} `{avg_d:.0f}` Mbps\n⬆️ {icon_u} `{avg_u:.0f}` Mbps" + # Media su una riga sola con "Mb" + msg += f"📈 **MEDIA:** ⬇️{icon_d}`{avg_d:.0f}Mb` ⬆️{icon_u}`{avg_u:.0f}Mb`" if issues > 0: msg += f"\n\n⚠️ **{issues}** test sotto soglia (!)"