modificando mensajes

This commit is contained in:
Daniel Fernandez Sotolongo
2025-09-27 12:31:54 -04:00
parent 514c655746
commit 89cae3d090
2 changed files with 255 additions and 252 deletions

View File

@ -10,6 +10,8 @@ services:
environment: environment:
- TZ=America/Havana - TZ=America/Havana
- PYTHONUNBUFFERED=1 - PYTHONUNBUFFERED=1
# Pasa el nombre del host al contenedor del notificador
- WATCHTOWER_NOTIFICATIONS_HOSTNAME=Servidor-Easypanel
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
depends_on: depends_on:

View File

@ -1,252 +1,253 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#watchtower_telegram_notifier.py """
""" Watchtower Telegram Notifier - Notificaciones personalizadas en español
Watchtower Telegram Notifier - Notificaciones personalizadas en español Monitorea los logs de Watchtower y envía notificaciones detalladas a Telegram
Monitorea los logs de Watchtower y envía notificaciones detalladas a Telegram """
"""
import docker
import docker import requests
import requests import time
import time import re
import re import os
from datetime import datetime from datetime import datetime
from threading import Thread from threading import Thread
import logging import logging
# Configuración # Configuración
TELEGRAM_BOT_TOKEN = "7676713419:AAG-tfwzgrA9JaU5xNjvG5iBlFeZpz2ahiY" TELEGRAM_BOT_TOKEN = "7676713419:AAG-tfwzgrA9JaU5xNjvG5iBlFeZpz2ahiY"
TELEGRAM_CHAT_ID = "7940222048" TELEGRAM_CHAT_ID = "7940222048"
WATCHTOWER_CONTAINER_NAME = "watchtower" WATCHTOWER_CONTAINER_NAME = "watchtower"
HOSTNAME = "Servidor-Easypanel" # Obtener el nombre del host de una variable de entorno, con un valor por defecto
HOSTNAME = os.environ.get("WATCHTOWER_NOTIFICATIONS_HOSTNAME", "Servidor-Easypanel")
# Configurar logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # Configurar logging
logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class WatchtowerTelegramNotifier:
def __init__(self): class WatchtowerTelegramNotifier:
self.client = docker.from_env() def __init__(self):
self.telegram_url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" self.client = docker.from_env()
self.container_updates = {} self.telegram_url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
self.scan_in_progress = False self.container_updates = {}
self.scan_in_progress = False
def send_telegram_message(self, message):
"""Envía mensaje a Telegram""" def send_telegram_message(self, message):
try: """Envía mensaje a Telegram"""
payload = { try:
'chat_id': TELEGRAM_CHAT_ID, payload = {
'text': message, 'chat_id': TELEGRAM_CHAT_ID,
'parse_mode': 'HTML' 'text': message,
} 'parse_mode': 'HTML'
response = requests.post(self.telegram_url, data=payload, timeout=10) }
if response.status_code == 200: response = requests.post(self.telegram_url, data=payload, timeout=10)
logger.info("✅ Mensaje enviado a Telegram exitosamente") if response.status_code == 200:
return True logger.info("✅ Mensaje enviado a Telegram exitosamente")
else: return True
logger.error(f"❌ Error enviando mensaje: {response.status_code}") else:
return False logger.error(f"❌ Error enviando mensaje: {response.status_code}")
except Exception as e: return False
logger.error(f"❌ Error conectando a Telegram: {e}") except Exception as e:
return False logger.error(f"❌ Error conectando a Telegram: {e}")
return False
def format_startup_message(self):
"""Mensaje de inicio del sistema""" def format_startup_message(self):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") """Mensaje de inicio del sistema"""
message = f"""🔄 <b>WATCHTOWER INICIADO</b> now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
message = f"""🔄 <b>WATCHTOWER INICIADO</b>
🏠 <b>Servidor:</b> {HOSTNAME}
📅 <b>Fecha:</b> {now} 🏠 <b>Servidor:</b> {HOSTNAME}
⚙️ <b>Estado:</b> Monitoreo activo 📅 <b>Fecha:</b> {now}
🕐 <b>Intervalo:</b> Cada 24 horas ⚙️ <b>Estado:</b> Monitoreo activo
🕐 <b>Intervalo:</b> Cada 24 horas
📋 <b>Contenedores monitoreados:</b>"""
📋 <b>Contenedores monitoreados:</b>"""
try:
containers = self.client.containers.list() try:
for container in containers: containers = self.client.containers.list()
if container.name != WATCHTOWER_CONTAINER_NAME: for container in containers:
status_emoji = "🟢" if container.status == "running" else "🔴" if container.name != WATCHTOWER_CONTAINER_NAME:
message += f"\n{status_emoji} {container.name}" status_emoji = "🟢" if container.status == "running" else "🔴"
except Exception as e: message += f"\n{status_emoji} {container.name}"
logger.error(f"Error obteniendo contenedores: {e}") except Exception as e:
logger.error(f"Error obteniendo contenedores: {e}")
message += "\n\n✅ <b>Sistema listo para detectar actualizaciones</b>"
return message message += "\n\n✅ <b>Sistema listo para detectar actualizaciones</b>"
return message
def format_scan_start_message(self):
"""Mensaje cuando inicia un escaneo""" def format_scan_start_message(self):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") """Mensaje cuando inicia un escaneo"""
return f"""🔍 <b>ESCANEO DE ACTUALIZACIONES INICIADO</b> now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return f"""🔍 <b>ESCANEO DE ACTUALIZACIONES INICIADO</b>
🏠 <b>Servidor:</b> {HOSTNAME}
📅 <b>Fecha:</b> {now} 🏠 <b>Servidor:</b> {HOSTNAME}
⏳ <b>Estado:</b> Verificando imágenes... 📅 <b>Fecha:</b> {now}
⏳ <b>Estado:</b> Verificando imágenes...
<i>Buscando actualizaciones disponibles...</i>"""
<i>Buscando actualizaciones disponibles...</i>"""
def format_update_report(self, updated_containers, checked_containers):
"""Formato del reporte final de actualizaciones""" def format_update_report(self, updated_containers, checked_containers):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") """Formato del reporte final de actualizaciones"""
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if updated_containers:
message = f"""🎉 <b>ACTUALIZACIONES COMPLETADAS</b> if updated_containers:
message = f"""🎉 <b>ACTUALIZACIONES COMPLETADAS</b>
🏠 <b>Servidor:</b> {HOSTNAME}
📅 <b>Fecha:</b> {now} 🏠 <b>Servidor:</b> {HOSTNAME}
📦 <b>Contenedores verificados:</b> {len(checked_containers)} 📅 <b>Fecha:</b> {now}
📦 <b>Contenedores verificados:</b> {len(checked_containers)}
<b>📥 ACTUALIZACIONES REALIZADAS:</b>"""
<b>📥 ACTUALIZACIONES REALIZADAS:</b>"""
for container_info in updated_containers:
message += f"\n✅ <b>{container_info['name']}</b>" for container_info in updated_containers:
message += f"\n 📋 Imagen: <code>{container_info['image']}</code>" message += f"\n✅ <b>{container_info['name']}</b>"
if container_info.get('old_image'): message += f"\n   📋 Imagen: <code>{container_info['image']}</code>"
message += f"\n 🔄 Desde: <code>{container_info['old_image']}</code>" if container_info.get('old_image'):
message += f"\n   🔄 Desde: <code>{container_info['old_image']}</code>"
unchanged = [c for c in checked_containers if c not in [u['name'] for u in updated_containers]]
if unchanged: unchanged = [c for c in checked_containers if c not in [u['name'] for u in updated_containers]]
message += f"\n\n<b>ℹ️ SIN CAMBIOS:</b>" if unchanged:
for container in unchanged: message += f"\n\n<b> SIN CAMBIOS:</b>"
message += f"\n🔘 {container}" for container in unchanged:
message += f"\n🔘 {container}"
message += f"\n\n🎯 <b>Total actualizados:</b> {len(updated_containers)}"
message += "\n✨ <b>¡Todos los servicios están actualizados!</b>" message += f"\n\n🎯 <b>Total actualizados:</b> {len(updated_containers)}"
message += "\n✨ <b>¡Todos los servicios están actualizados!</b>"
else:
message = f"""ℹ️ <b>VERIFICACIÓN COMPLETADA</b> else:
message = f""" <b>VERIFICACIÓN COMPLETADA</b>
🏠 <b>Servidor:</b> {HOSTNAME}
📅 <b>Fecha:</b> {now} 🏠 <b>Servidor:</b> {HOSTNAME}
📦 <b>Contenedores verificados:</b> {len(checked_containers)} 📅 <b>Fecha:</b> {now}
📦 <b>Contenedores verificados:</b> {len(checked_containers)}
<b>📋 ESTADO:</b>"""
<b>📋 ESTADO:</b>"""
for container in checked_containers:
message += f"\n🔘 <b>{container}</b> - Sin actualizaciones" for container in checked_containers:
message += f"\n🔘 <b>{container}</b> - Sin actualizaciones"
message += "\n\n✅ <b>Todos los contenedores están al día</b>"
message += "\n\n✅ <b>Todos los contenedores están al día</b>"
return message
return message
def parse_watchtower_logs(self, log_line):
"""Analiza líneas de log de Watchtower""" def parse_watchtower_logs(self, log_line):
log_line = log_line.strip() """Analiza líneas de log de Watchtower"""
log_line = log_line.strip()
# Detectar inicio de escaneo
if "Checking all containers" in log_line and not self.scan_in_progress: # Detectar inicio de escaneo
self.scan_in_progress = True if "Checking all containers" in log_line and not self.scan_in_progress:
self.container_updates.clear() self.scan_in_progress = True
self.send_telegram_message(self.format_scan_start_message()) self.container_updates.clear()
return self.send_telegram_message(self.format_scan_start_message())
return
# Detectar actualizaciones
update_patterns = [ # Detectar actualizaciones
r'Found new.*image for (.+)', update_patterns = [
r'Updating (.+?) with.*', r'Found new.*image for (.+)',
r'Updated (.+?) \(.*\)' r'Updating (.+?) with.*',
] r'Updated (.+?) \(.*\)'
]
for pattern in update_patterns:
match = re.search(pattern, log_line, re.IGNORECASE) for pattern in update_patterns:
if match: match = re.search(pattern, log_line, re.IGNORECASE)
container_name = match.group(1) if match:
if container_name not in self.container_updates: container_name = match.group(1)
self.container_updates[container_name] = { if container_name not in self.container_updates:
'name': container_name, self.container_updates[container_name] = {
'image': 'Detectando...', 'name': container_name,
'updated': True 'image': 'Detectando...',
} 'updated': True
logger.info(f"📦 Actualización detectada: {container_name}") }
break logger.info(f"📦 Actualización detectada: {container_name}")
break
# Detectar fin de proceso
if any(phrase in log_line.lower() for phrase in ["session done", "watchtower will check again", "sleeping"]): # Detectar fin de proceso
if self.scan_in_progress: if any(phrase in log_line.lower() for phrase in ["session done", "watchtower will check again", "sleeping"]):
self.send_update_report() if self.scan_in_progress:
self.scan_in_progress = False self.send_update_report()
self.scan_in_progress = False
def send_update_report(self):
"""Envía el reporte final de actualizaciones""" def send_update_report(self):
try: """Envía el reporte final de actualizaciones"""
# Obtener lista actual de contenedores try:
containers = self.client.containers.list(all=True) # Obtener lista actual de contenedores
container_names = [c.name for c in containers if c.name != WATCHTOWER_CONTAINER_NAME] containers = self.client.containers.list(all=True)
container_names = [c.name for c in containers if c.name != WATCHTOWER_CONTAINER_NAME]
# Completar información de contenedores actualizados
updated_list = [] # Completar información de contenedores actualizados
for container_name, info in self.container_updates.items(): updated_list = []
try: for container_name, info in self.container_updates.items():
container = self.client.containers.get(container_name) try:
info['image'] = container.image.tags[0] if container.image.tags else "Sin tag" container = self.client.containers.get(container_name)
except: info['image'] = container.image.tags[0] if container.image.tags else "Sin tag"
info['image'] = "Imagen no encontrada" except:
updated_list.append(info) info['image'] = "Imagen no encontrada"
updated_list.append(info)
# Enviar reporte
report = self.format_update_report(updated_list, container_names) # Enviar reporte
self.send_telegram_message(report) report = self.format_update_report(updated_list, container_names)
self.send_telegram_message(report)
except Exception as e:
logger.error(f"Error generando reporte: {e}") except Exception as e:
error_msg = f"❌ <b>Error generando reporte de actualizaciones</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Error:</b> {str(e)}" logger.error(f"Error generando reporte: {e}")
self.send_telegram_message(error_msg) error_msg = f"❌ <b>Error generando reporte de actualizaciones</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Error:</b> {str(e)}"
self.send_telegram_message(error_msg)
def monitor_watchtower(self):
"""Monitorea logs de Watchtower en tiempo real""" def monitor_watchtower(self):
logger.info("🔍 Iniciando monitoreo de Watchtower...") """Monitorea logs de Watchtower en tiempo real"""
logger.info("🔍 Iniciando monitoreo de Watchtower...")
try:
container = self.client.containers.get(WATCHTOWER_CONTAINER_NAME) try:
logger.info(f"✅ Contenedor Watchtower encontrado: {container.id[:12]}") container = self.client.containers.get(WATCHTOWER_CONTAINER_NAME)
logger.info(f"✅ Contenedor Watchtower encontrado: {container.id[:12]}")
# Enviar mensaje de inicio
self.send_telegram_message(self.format_startup_message()) # Enviar mensaje de inicio
self.send_telegram_message(self.format_startup_message())
# Monitorear logs en tiempo real
for log in container.logs(stream=True, follow=True, tail=10): # Monitorear logs en tiempo real
try: for log in container.logs(stream=True, follow=True, tail=10):
log_line = log.decode('utf-8').strip() try:
if log_line: log_line = log.decode('utf-8').strip()
logger.debug(f"Log: {log_line}") if log_line:
self.parse_watchtower_logs(log_line) logger.debug(f"Log: {log_line}")
except Exception as e: self.parse_watchtower_logs(log_line)
logger.error(f"Error procesando log: {e}") except Exception as e:
continue logger.error(f"Error procesando log: {e}")
continue
except docker.errors.NotFound:
error_msg = f"❌ <b>ERROR: Contenedor Watchtower no encontrado</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Nombre esperado:</b> {WATCHTOWER_CONTAINER_NAME}" except docker.errors.NotFound:
self.send_telegram_message(error_msg) error_msg = f"❌ <b>ERROR: Contenedor Watchtower no encontrado</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Nombre esperado:</b> {WATCHTOWER_CONTAINER_NAME}"
logger.error("❌ Contenedor Watchtower no encontrado") self.send_telegram_message(error_msg)
except Exception as e: logger.error("❌ Contenedor Watchtower no encontrado")
error_msg = f"❌ <b>ERROR EN MONITOREO</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Error:</b> {str(e)}" except Exception as e:
self.send_telegram_message(error_msg) error_msg = f"❌ <b>ERROR EN MONITOREO</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Error:</b> {str(e)}"
logger.error(f"❌ Error en monitoreo: {e}") self.send_telegram_message(error_msg)
logger.error(f"❌ Error en monitoreo: {e}")
def main():
logger.info("🚀 Iniciando Watchtower Telegram Notifier...") def main():
logger.info("🚀 Iniciando Watchtower Telegram Notifier...")
notifier = WatchtowerTelegramNotifier()
notifier = WatchtowerTelegramNotifier()
# Verificar conexión a Telegram
test_message = f"🤖 <b>Bot de notificaciones iniciado</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n📅 <b>Fecha:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n✅ <b>Conexión establecida correctamente</b>" # Verificar conexión a Telegram
test_message = f"🤖 <b>Bot de notificaciones iniciado</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n📅 <b>Fecha:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n✅ <b>Conexión establecida correctamente</b>"
if notifier.send_telegram_message(test_message):
logger.info("✅ Conexión a Telegram verificada") if notifier.send_telegram_message(test_message):
else: logger.info("✅ Conexión a Telegram verificada")
logger.error("❌ Error de conexión a Telegram") else:
return logger.error("❌ Error de conexión a Telegram")
return
# Iniciar monitoreo
try: # Iniciar monitoreo
notifier.monitor_watchtower() try:
except KeyboardInterrupt: notifier.monitor_watchtower()
logger.info("🛑 Monitoreo interrumpido por el usuario") except KeyboardInterrupt:
farewell_msg = f"👋 <b>Monitoreo detenido</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n📅 <b>Fecha:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n⚠️ <b>Bot desconectado</b>" logger.info("🛑 Monitoreo interrumpido por el usuario")
notifier.send_telegram_message(farewell_msg) farewell_msg = f"👋 <b>Monitoreo detenido</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n📅 <b>Fecha:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n⚠️ <b>Bot desconectado</b>"
except Exception as e: notifier.send_telegram_message(farewell_msg)
logger.error(f"❌ Error fatal: {e}") except Exception as e:
error_msg = f"💥 <b>ERROR FATAL EN BOT</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Error:</b> {str(e)}\n\n🔄 <b>Reinicia el script</b>" logger.error(f"❌ Error fatal: {e}")
notifier.send_telegram_message(error_msg) error_msg = f"💥 <b>ERROR FATAL EN BOT</b>\n\n🏠 <b>Servidor:</b> {HOSTNAME}\n⚠️ <b>Error:</b> {str(e)}\n\n🔄 <b>Reinicia el script</b>"
notifier.send_telegram_message(error_msg)
if __name__ == "__main__":
main() if __name__ == "__main__":
main()