Backup automatico script del 2025-12-01 16:49
This commit is contained in:
110
services/telegram-bot/arome_snow_alert.py
Normal file
110
services/telegram-bot/arome_snow_alert.py
Normal file
@@ -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()
|
||||
@@ -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 (!)"
|
||||
|
||||
Reference in New Issue
Block a user