From 9c97a6474f6378826339d6aae3c747cc55aaeff9 Mon Sep 17 00:00:00 2001 From: minguezsanzjuanjose Date: Mon, 3 Aug 2026 00:31:02 +0200 Subject: [PATCH] vigilancia: panorama con autoborrado en Telegram a los 10min (proceso separado) - PANORAMA_DELETE_AFTER --- .obsidian/workspace.json | 2 + vigilancia/panorama.py | 110 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 vigilancia/panorama.py diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index af1cec4..73ab640 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -183,6 +183,8 @@ }, "active": "72506070fed1a695", "lastOpenFiles": [ + "vigilancia/__pycache__/panorama.cpython-313.pyc", + "vigilancia/panorama.py", "vigilancia/__pycache__/limpiar.cpython-313.pyc", "vigilancia/limpiar.py" ] diff --git a/vigilancia/panorama.py b/vigilancia/panorama.py new file mode 100644 index 0000000..0685589 --- /dev/null +++ b/vigilancia/panorama.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import sys +from datetime import datetime + +import requests + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import config + +# Segundos tras los que se borran las fotos del panorama en Telegram (default 10 min) +DELETE_AFTER = int(os.getenv("PANORAMA_DELETE_AFTER", "600")) + +if not config.CAMARAS: + print("No hay cámaras configuradas") + sys.exit(1) + +def programar_borrado(message_ids): + """Borra los mensajes (fotos del panorama) tras DELETE_AFTER segundos. + Se lanza en un proceso separado para que sobreviva al cierre de este script.""" + codigo = ( + "import time, requests\n" + f"time.sleep({DELETE_AFTER})\n" + f"chat={config.TELEGRAM_CHAT_ID!r}\n" + f"ids={list(message_ids)!r}\n" + f"tok={config.TELEGRAM_TOKEN!r}\n" + "for mid in ids:\n" + " try:\n" + " requests.post('https://api.telegram.org/bot'+tok+'/deleteMessage'," + " data={'chat_id': chat, 'message_id': mid}, timeout=10)\n" + " print('borrado', mid)\n" + " except Exception as e:\n" + " print('err', mid, e)\n" + ) + subprocess.Popen(["python3", "-c", codigo], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + start_new_session=True, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0)) + +def capturar_snapshot(): + os.makedirs(config.SNAPSHOT_DIR, exist_ok=True) + resultados = [] + for cam in config.CAMARAS: + ts = datetime.now().strftime("%Y%m%d_%H%M%S") + ruta = os.path.join(config.SNAPSHOT_DIR, f"panorama_{cam['nombre']}_{ts}.jpg") + cmd = ["ffmpeg", "-i", cam["rtsp"], "-frames:v", "1", "-q:v", "2", + "-y", "-hide_banner", "-loglevel", "error", ruta] + try: + subprocess.run(cmd, timeout=15, check=True) + print(f"[{cam['nombre']}] Snapshot: {ruta}") + resultados.append((cam["nombre"], ruta)) + except Exception as e: + print(f"[{cam['nombre']}] Error: {e}") + return resultados + +def enviar_album(fotos): + if not fotos: + print("No hay fotos para enviar") + return + if len(fotos) == 1: + nombre, ruta = fotos[0] + url = f"https://api.telegram.org/bot{config.TELEGRAM_TOKEN}/sendPhoto" + try: + with open(ruta, "rb") as f: + r = requests.post(url, + data={"chat_id": config.TELEGRAM_CHAT_ID, + "caption": f"📷 {nombre} — {datetime.now().strftime('%H:%M:%S')}"}, + files={"photo": f}, timeout=30) + print(f"Foto enviada: HTTP {r.status_code}") + if r.status_code == 200: + programar_borrado([r.json()["result"]["message_id"]]) + except Exception as e: + print(f"Error: {e}") + else: + url = f"https://api.telegram.org/bot{config.TELEGRAM_TOKEN}/sendMediaGroup" + media = [] + files_dict = {} + for i, (nombre, ruta) in enumerate(fotos): + media.append({ + "type": "photo", + "media": f"attach://{i}", + "caption": f"📷 {nombre} — {datetime.now().strftime('%H:%M:%S')}" + }) + files_dict[str(i)] = open(ruta, "rb") + try: + r = requests.post(url, data={"chat_id": config.TELEGRAM_CHAT_ID, + "media": json.dumps(media)}, files=files_dict, timeout=30) + print(f"Album enviado: HTTP {r.status_code}") + if r.status_code == 200: + ids = [m["message_id"] for m in r.json()["result"]] + print(f"Borrado programado dentro de {DELETE_AFTER}s para {len(ids)} foto(s)") + programar_borrado(ids) + else: + print(f"Error: {r.text[:300]}") + except Exception as e: + print(f"Error: {e}") + finally: + for f in files_dict.values(): + f.close() + +if __name__ == "__main__": + print("Capturando snapshots...") + fotos = capturar_snapshot() + if fotos: + print(f"Enviando {len(fotos)} foto(s) a IDD...") + enviar_album(fotos) + else: + print("No se capturaron fotos")