2025-03-26 18:54:30 +01:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
#set -x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Configuration ============================================
|
|
|
|
|
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
|
|
|
|
BASE_DIR="/usr/local/share/proxmenux"
|
|
|
|
|
UTILS_FILE="$BASE_DIR/utils.sh"
|
|
|
|
|
VENV_PATH="/opt/googletrans-env"
|
|
|
|
|
|
|
|
|
|
if [[ -f "$UTILS_FILE" ]]; then
|
|
|
|
|
source "$UTILS_FILE"
|
|
|
|
|
fi
|
|
|
|
|
load_language
|
|
|
|
|
initialize_cache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CONFIG_FILE="/etc/proxmox-telegram.conf"
|
|
|
|
|
PID_DIR="/var/run/proxmox-telegram"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Crear archivo de configuración si no existe
|
|
|
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
|
|
|
cat <<EOF > "$CONFIG_FILE"
|
|
|
|
|
BOT_TOKEN=""
|
|
|
|
|
CHAT_ID=""
|
|
|
|
|
vm_start=0
|
|
|
|
|
vm_shutdown=0
|
|
|
|
|
vm_restart=0
|
|
|
|
|
vm_fail=0
|
|
|
|
|
update_available=0
|
|
|
|
|
update_complete=0
|
|
|
|
|
system_shutdown=0
|
|
|
|
|
system_problem=0
|
|
|
|
|
system_load_high=0
|
|
|
|
|
kernel_panic=0
|
|
|
|
|
disk_fail=0
|
|
|
|
|
disk_full=0
|
|
|
|
|
disk_io_error=0
|
|
|
|
|
node_disconnect=0
|
|
|
|
|
split_brain=0
|
|
|
|
|
network_down=0
|
|
|
|
|
network_saturation=0
|
|
|
|
|
firewall_issue=0
|
|
|
|
|
backup_complete=0
|
|
|
|
|
backup_fail=0
|
|
|
|
|
snapshot_complete=0
|
|
|
|
|
snapshot_fail=0
|
|
|
|
|
auth_fail=0
|
|
|
|
|
ip_block=0
|
|
|
|
|
user_permission_change=0
|
|
|
|
|
cpu_high=0
|
|
|
|
|
ram_high=0
|
|
|
|
|
temp_high=0
|
|
|
|
|
low_disk_space=0
|
|
|
|
|
EOF
|
|
|
|
|
chmod 600 "$CONFIG_FILE" # Proteger el archivo de configuración
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Leer configuración actual
|
|
|
|
|
source "$CONFIG_FILE"
|
|
|
|
|
|
|
|
|
|
# Función para enviar notificaciones a Telegram (sin logging)
|
|
|
|
|
send_notification() {
|
|
|
|
|
local message="$1"
|
|
|
|
|
|
|
|
|
|
# Si el token o chat ID están vacíos → No enviar nada
|
|
|
|
|
if [[ -z "$BOT_TOKEN" || -z "$CHAT_ID" ]]; then
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Enviar notificación a Telegram (sin output)
|
|
|
|
|
curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
|
|
|
|
|
-d "chat_id=$CHAT_ID" \
|
|
|
|
|
-d "text=$message" > /dev/null 2>&1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Opciones para el menú
|
|
|
|
|
options=(
|
|
|
|
|
"VM y Contenedor|Inicio de VM/Contenedor|vm_start"
|
|
|
|
|
"VM y Contenedor|Apagado de VM/Contenedor|vm_shutdown"
|
|
|
|
|
"VM y Contenedor|Reinicio de VM/Contenedor|vm_restart"
|
|
|
|
|
"VM y Contenedor|Error en inicio de VM/Contenedor|vm_fail"
|
|
|
|
|
"Sistema|Nueva actualización disponible|update_available"
|
|
|
|
|
"Sistema|Actualización completada|update_complete"
|
|
|
|
|
"Sistema|Apagado del sistema|system_shutdown"
|
|
|
|
|
"Sistema|Problema con el sistema|system_problem"
|
|
|
|
|
"Sistema|Carga del sistema alta|system_load_high"
|
|
|
|
|
"Sistema|Kernel Panic|kernel_panic"
|
|
|
|
|
"Almacenamiento|Fallo de disco|disk_fail"
|
|
|
|
|
"Almacenamiento|Almacenamiento lleno|disk_full"
|
|
|
|
|
"Almacenamiento|Problemas de lectura/escritura|disk_io_error"
|
|
|
|
|
"Clúster|Nodo desconectado|node_disconnect"
|
|
|
|
|
"Clúster|Split-brain (conflicto en quorum)|split_brain"
|
|
|
|
|
"Red|Interfaz de red caída|network_down"
|
|
|
|
|
"Red|Saturación de red|network_saturation"
|
|
|
|
|
"Red|Problema con el firewall|firewall_issue"
|
|
|
|
|
"Backup y Snapshot|Backup completado|backup_complete"
|
|
|
|
|
"Backup y Snapshot|Backup fallido|backup_fail"
|
|
|
|
|
"Backup y Snapshot|Snapshot completado|snapshot_complete"
|
|
|
|
|
"Backup y Snapshot|Snapshot fallido|snapshot_fail"
|
|
|
|
|
"Seguridad|Intento de autenticación fallido|auth_fail"
|
|
|
|
|
"Seguridad|Bloqueos automáticos de IP|ip_block"
|
|
|
|
|
"Seguridad|Cambio de permisos de usuario|user_permission_change"
|
|
|
|
|
"Recursos|Uso alto de CPU|cpu_high"
|
|
|
|
|
"Recursos|Uso alto de RAM|ram_high"
|
|
|
|
|
"Recursos|Temperatura alta del sistema|temp_high"
|
|
|
|
|
"Recursos|Bajo espacio en disco|low_disk_space"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Función para obtener el nombre de una VM/CT a partir de su ID
|
|
|
|
|
get_vm_name() {
|
|
|
|
|
local vmid="$1"
|
|
|
|
|
local name=""
|
|
|
|
|
|
|
|
|
|
if [[ -f "/etc/pve/qemu-server/$vmid.conf" ]]; then
|
|
|
|
|
name=$(grep -i "^name:" "/etc/pve/qemu-server/$vmid.conf" | cut -d ' ' -f2-)
|
|
|
|
|
elif [[ -f "/etc/pve/lxc/$vmid.conf" ]]; then
|
|
|
|
|
name=$(grep -i "^hostname:" "/etc/pve/lxc/$vmid.conf" | cut -d ' ' -f2-)
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Siempre devolver el nombre y el ID en el mismo formato
|
|
|
|
|
if [[ -n "$name" ]]; then
|
|
|
|
|
echo "$name ($vmid)"
|
|
|
|
|
else
|
|
|
|
|
echo "$vmid"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función para configurar notificaciones
|
|
|
|
|
configure_notifications() {
|
|
|
|
|
# Ordenar las opciones por categoría y descripción
|
|
|
|
|
IFS=$'\n' sorted_options=($(for option in "${options[@]}"; do
|
|
|
|
|
IFS='|' read -r category description var_name <<< "$option"
|
|
|
|
|
printf "%s|%s|%s\n" "$category" "$description" "$var_name"
|
|
|
|
|
done | sort -t'|' -k1,1 -k2,2))
|
|
|
|
|
unset IFS
|
|
|
|
|
|
|
|
|
|
# Crear un mapeo de índices a nombres de variables
|
|
|
|
|
declare -A index_to_var
|
|
|
|
|
index=1
|
|
|
|
|
|
|
|
|
|
# Preparar las opciones para `whiptail`
|
|
|
|
|
menu_items=()
|
|
|
|
|
for option in "${sorted_options[@]}"; do
|
|
|
|
|
IFS='|' read -r category description var_name <<< "$option"
|
|
|
|
|
|
|
|
|
|
# Guardar el mapeo de índice a nombre de variable
|
|
|
|
|
index_to_var["$index"]="$var_name"
|
|
|
|
|
|
|
|
|
|
# Formatear descripción con espacios para alinear la categoría a la derecha
|
|
|
|
|
formatted_item="$description"
|
|
|
|
|
current_length=${#formatted_item}
|
|
|
|
|
spaces_needed=$((50 - current_length))
|
|
|
|
|
|
|
|
|
|
for ((j = 0; j < spaces_needed; j++)); do
|
|
|
|
|
formatted_item+=" "
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
formatted_item+="$category"
|
|
|
|
|
|
|
|
|
|
# Marcar como ON u OFF según la configuración guardada
|
|
|
|
|
state="OFF"
|
|
|
|
|
[[ "$(eval echo \$$var_name)" -eq 1 ]] && state="ON"
|
|
|
|
|
|
|
|
|
|
menu_items+=("$index" "$formatted_item" "$state")
|
|
|
|
|
((index++))
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Mostrar menú `whiptail`
|
|
|
|
|
selected_indices=$(whiptail --title "$(translate "Configuración de Notificaciones a Telegram")" \
|
|
|
|
|
--checklist --separate-output \
|
|
|
|
|
"\n$(translate "Selecciona los eventos que deseas recibir:")\n" \
|
|
|
|
|
30 100 20 \
|
|
|
|
|
"${menu_items[@]}" \
|
|
|
|
|
3>&1 1>&2 2>&3)
|
|
|
|
|
|
|
|
|
|
local result=$?
|
|
|
|
|
|
|
|
|
|
# Si se ha seleccionado alguna opción, actualizar configuración
|
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
|
|
|
# Hacer una copia de seguridad del archivo de configuración
|
|
|
|
|
cp "$CONFIG_FILE" "${CONFIG_FILE}.bak" 2>/dev/null
|
|
|
|
|
|
|
|
|
|
# Establecer todas las opciones en OFF
|
|
|
|
|
for var_name in "${index_to_var[@]}"; do
|
|
|
|
|
sed -i "s/^$var_name=.*/$var_name=0/" "$CONFIG_FILE"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Activar las opciones seleccionadas
|
|
|
|
|
for selected_index in $selected_indices; do
|
|
|
|
|
var_name="${index_to_var[$selected_index]}"
|
|
|
|
|
sed -i "s/^$var_name=.*/$var_name=1/" "$CONFIG_FILE"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Recargar la configuración
|
|
|
|
|
source "$CONFIG_FILE"
|
|
|
|
|
|
|
|
|
|
whiptail --title "$(translate "Éxito")" \
|
|
|
|
|
--msgbox "$(translate "Configuración actualizada correctamente.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función: captura eventos desde journalctl
|
|
|
|
|
capture_journal_events() {
|
|
|
|
|
# Usar un archivo para almacenar eventos ya procesados
|
|
|
|
|
local processed_events_file="$PID_DIR/processed_events"
|
|
|
|
|
|
|
|
|
|
# Crear directorio si no existe
|
|
|
|
|
mkdir -p "$PID_DIR" 2>/dev/null
|
|
|
|
|
|
|
|
|
|
# Crear el archivo si no existe
|
|
|
|
|
if [[ ! -f "$processed_events_file" ]]; then
|
|
|
|
|
touch "$processed_events_file"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Monitorear continuamente el log
|
|
|
|
|
while true; do
|
|
|
|
|
# Usar tail para el archivo de tareas de Proxmox
|
|
|
|
|
tail -F /var/log/pve/tasks/index 2>/dev/null | while read -r line; do
|
|
|
|
|
# Crear un identificador único para este evento
|
|
|
|
|
event_id=$(echo "$line" | md5sum | cut -d' ' -f1)
|
|
|
|
|
|
|
|
|
|
# Verificar si ya procesamos este evento
|
|
|
|
|
if grep -q "$event_id" "$processed_events_file" 2>/dev/null; then
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Añadir el ID al archivo de eventos procesados
|
|
|
|
|
echo "$event_id" >> "$processed_events_file"
|
|
|
|
|
|
|
|
|
|
# Limitar el tamaño del archivo de eventos procesados
|
|
|
|
|
tail -n 1000 "$processed_events_file" > "${processed_events_file}.tmp" 2>/dev/null && mv "${processed_events_file}.tmp" "$processed_events_file" 2>/dev/null
|
|
|
|
|
|
|
|
|
|
# Variable para controlar si el evento ya fue procesado por algún patrón
|
|
|
|
|
local event_processed=false
|
|
|
|
|
|
|
|
|
|
# ===== EVENTOS CRÍTICOS (INMEDIATOS) =====
|
|
|
|
|
|
|
|
|
|
# Error al iniciar VM (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "Failed to start VM" ]] && [[ "$vm_fail" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'VM \K[0-9]+')
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Error al iniciar la VM:") $NAME"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Errores de I/O de disco (CRÍTICO)
|
|
|
|
|
if [[ "$disk_io_error" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
if [[ "$line" =~ "I/O error" || "$line" =~ "read error" || "$line" =~ "write error" || "$line" =~ "blk_update_request" || "$line" =~ "buffer I/O error" ]]; then
|
|
|
|
|
DISK=$(echo "$line" | grep -oE "/dev/[a-zA-Z0-9]+" || echo "desconocido")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Error de lectura/escritura en disco:") $DISK"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Fallo de disco (CRÍTICO)
|
|
|
|
|
if [[ "$disk_fail" -eq 1 ]] && [[ "$line" =~ "disk failure" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
DISK=$(echo "$line" | grep -oE "/dev/[a-zA-Z0-9]+" || echo "desconocido")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Fallo de disco detectado:") $DISK"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Snapshot fallido (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "snapshot" ]] && [[ "$snapshot_fail" -eq 1 ]] && [[ "$line" =~ "error" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'TASK \K[0-9]+' || echo "")
|
|
|
|
|
if [[ -n "$VM_ID" ]]; then
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Snapshot fallido para:") $NAME"
|
|
|
|
|
else
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Snapshot fallido")"
|
|
|
|
|
fi
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Backup fallido (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "backup" ]] && [[ "$backup_fail" -eq 1 ]] && [[ "$line" =~ "error" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'TASK \K[0-9]+' || echo "")
|
|
|
|
|
if [[ -n "$VM_ID" ]]; then
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Backup fallido para:") $NAME"
|
|
|
|
|
else
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Backup fallido")"
|
|
|
|
|
fi
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Intento de autenticación fallido (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "authentication failure" ]] && [[ "$auth_fail" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
USER=$(echo "$line" | grep -oP 'user=\K[^ ]+' || echo "desconocido")
|
|
|
|
|
IP=$(echo "$line" | grep -oP 'rhost=\K[^ ]+' || echo "desconocida")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Intento de autenticación fallido:") $USER desde $IP"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Problema con el firewall (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "firewall" ]] && [[ "$firewall_issue" -eq 1 ]] && [[ "$line" =~ "error|block|reject" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Problema con el firewall:") $line"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Interfaz de red caída (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "network" ]] && [[ "$network_down" -eq 1 ]] && [[ "$line" =~ "down" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
IFACE=$(echo "$line" | grep -oP 'interface \K[^ ]+' || echo "desconocida")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Interfaz de red caída:") $IFACE"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Split-brain detectado (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "Split-Brain" ]] && [[ "$split_brain" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Split-brain detectado en el clúster")"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Nodo desconectado del clúster (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "quorum" ]] && [[ "$node_disconnect" -eq 1 ]] && [[ "$line" =~ "lost" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
NODE=$(echo "$line" | grep -oP 'node \K[^ ]+' || echo "desconocido")
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Nodo desconectado del clúster:") $NODE"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Kernel panic (CRÍTICO)
|
|
|
|
|
if [[ "$line" =~ "kernel panic" ]] && [[ "$kernel_panic" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Kernel panic detectado")"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ===== EVENTOS NO CRÍTICOS (INMEDIATOS) =====
|
|
|
|
|
|
|
|
|
|
# Inicio de VM (NO CRÍTICO pero inmediato)
|
|
|
|
|
if [[ "$line" =~ "qmstart" ]] && [[ "$vm_start" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'qmstart:\K[0-9]+')
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "✅ $(translate "VM comenzó con éxito:") $NAME"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Apagado de VM (NO CRÍTICO pero inmediato)
|
|
|
|
|
if [[ "$line" =~ "qmstop" ]] && [[ "$vm_shutdown" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'qmstop:\K[0-9]+')
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "✅ $(translate "VM se detuvo con éxito:") $NAME"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Reinicio de VM (NO CRÍTICO pero inmediato)
|
|
|
|
|
if [[ "$line" =~ "qmreset" || "$line" =~ "qmreboot" ]] && [[ "$vm_restart" -eq 1 ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP '(qmreset|qmreboot):\K[0-9]+')
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "✅ $(translate "VM reinició con éxito:") $NAME"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Snapshot completado (NO CRÍTICO pero inmediato)
|
|
|
|
|
if [[ "$line" =~ "snapshot" ]] && [[ "$snapshot_complete" -eq 1 ]] && [[ ! "$line" =~ "error" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'TASK \K[0-9]+' || echo "")
|
|
|
|
|
if [[ -n "$VM_ID" ]]; then
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "✅ $(translate "Snapshot completado para:") $NAME"
|
|
|
|
|
else
|
|
|
|
|
send_notification "✅ $(translate "Snapshot completado")"
|
|
|
|
|
fi
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Backup completado (NO CRÍTICO pero inmediato)
|
|
|
|
|
if [[ "$line" =~ "backup" ]] && [[ "$backup_complete" -eq 1 ]] && [[ "$line" =~ "successful" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
VM_ID=$(echo "$line" | grep -oP 'TASK \K[0-9]+' || echo "")
|
|
|
|
|
if [[ -n "$VM_ID" ]]; then
|
|
|
|
|
NAME=$(get_vm_name "$VM_ID")
|
|
|
|
|
send_notification "✅ $(translate "Backup completado para:") $NAME"
|
|
|
|
|
else
|
|
|
|
|
send_notification "✅ $(translate "Backup completado")"
|
|
|
|
|
fi
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Actualización completada (NO CRÍTICO pero inmediato)
|
|
|
|
|
if [[ "$line" =~ "update" ]] && [[ "$update_complete" -eq 1 ]] && [[ "$line" =~ "complete" ]] && [[ "$event_processed" = false ]]; then
|
|
|
|
|
send_notification "✅ $(translate "Actualización del sistema completada")"
|
|
|
|
|
event_processed=true
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Si llegamos aquí, es porque tail -F terminó inesperadamente
|
|
|
|
|
sleep 5
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función: captura eventos directos del sistema
|
|
|
|
|
capture_direct_events() {
|
|
|
|
|
# Variables para controlar la frecuencia de las notificaciones
|
|
|
|
|
local last_load_notification=0
|
|
|
|
|
local last_temp_notification=0
|
|
|
|
|
local last_disk_space_notification=0
|
|
|
|
|
local last_cpu_notification=0
|
|
|
|
|
local last_ram_notification=0
|
|
|
|
|
local last_update_notification=0
|
|
|
|
|
|
|
|
|
|
# Tiempo mínimo entre notificaciones repetitivas (en segundos)
|
|
|
|
|
local resource_interval=900 # 15 minutos para recursos
|
|
|
|
|
local update_interval=86400 # 24 horas para actualizaciones
|
|
|
|
|
|
|
|
|
|
# Variables para eventos CRÍTICOS (sin intervalo)
|
|
|
|
|
local disk_full_detected=false
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
current_time=$(date +%s)
|
|
|
|
|
|
|
|
|
|
# ===== EVENTOS CRÍTICOS (INMEDIATOS) =====
|
|
|
|
|
|
|
|
|
|
# Disco lleno (CRÍTICO - inmediato)
|
|
|
|
|
if [[ "$disk_full" -eq 1 ]]; then
|
|
|
|
|
full_disks=$(df -h | awk '$5 == "100%" {print $1 " (100% lleno)"}')
|
|
|
|
|
if [[ -n "$full_disks" && "$disk_full_detected" = false ]]; then
|
|
|
|
|
send_notification "🚨 $(translate "CRÍTICO: Almacenamiento completamente lleno:") $full_disks"
|
|
|
|
|
disk_full_detected=true
|
|
|
|
|
elif [[ -z "$full_disks" ]]; then
|
|
|
|
|
disk_full_detected=false
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ===== EVENTOS NO CRÍTICOS (CON INTERVALO) =====
|
|
|
|
|
|
|
|
|
|
# Carga alta del sistema (NO CRÍTICO - con intervalo)
|
|
|
|
|
if [[ "$system_load_high" -eq 1 ]]; then
|
|
|
|
|
load=$(awk '{print $1}' /proc/loadavg)
|
|
|
|
|
if (( $(echo "$load > 5.00" | bc -l) )) && (( current_time - last_load_notification > resource_interval )); then
|
|
|
|
|
send_notification "⚠️ $(translate "Carga alta del sistema detectada:") $load"
|
|
|
|
|
last_load_notification=$current_time
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Actualizaciones disponibles (NO CRÍTICO - con intervalo diario)
|
|
|
|
|
if [[ "$update_available" -eq 1 ]] && (( current_time - last_update_notification > update_interval )); then
|
|
|
|
|
if command -v apt-get &>/dev/null; then
|
|
|
|
|
apt-get update -qq &>/dev/null
|
|
|
|
|
updates=$(apt list --upgradable 2>/dev/null | grep -v "Listing..." | wc -l)
|
|
|
|
|
if [[ $updates -gt 0 ]]; then
|
|
|
|
|
send_notification "ℹ️ $(translate "Actualizaciones disponibles:") $updates"
|
|
|
|
|
last_update_notification=$current_time
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Espacio en disco bajo (NO CRÍTICO - con intervalo)
|
|
|
|
|
if [[ "$low_disk_space" -eq 1 ]] && (( current_time - last_disk_space_notification > resource_interval )); then
|
|
|
|
|
# Comprobar particiones con más del 90% pero menos del 100% de uso
|
|
|
|
|
low_space=$(df -h | awk '$5 ~ /9[0-9]%/ && $5 != "100%" {print $1 " (" $5 " lleno)"}')
|
|
|
|
|
if [[ -n "$low_space" ]]; then
|
|
|
|
|
send_notification "⚠️ $(translate "Espacio en disco bajo:") $low_space"
|
|
|
|
|
last_disk_space_notification=$current_time
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Uso alto de CPU (NO CRÍTICO - con intervalo)
|
|
|
|
|
if [[ "$cpu_high" -eq 1 ]] && (( current_time - last_cpu_notification > resource_interval )); then
|
|
|
|
|
# Usar mpstat si está disponible, si no, usar top
|
|
|
|
|
if command -v mpstat &>/dev/null; then
|
|
|
|
|
cpu_usage=$(mpstat 1 1 | awk '/Average:/ {print 100 - $NF}')
|
|
|
|
|
else
|
|
|
|
|
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if (( $(echo "$cpu_usage > 90" | bc -l) )); then
|
|
|
|
|
send_notification "⚠️ $(translate "Uso alto de CPU:") $cpu_usage%"
|
|
|
|
|
last_cpu_notification=$current_time
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Uso alto de RAM (NO CRÍTICO - con intervalo)
|
|
|
|
|
if [[ "$ram_high" -eq 1 ]] && (( current_time - last_ram_notification > resource_interval )); then
|
|
|
|
|
ram_usage=$(free | awk '/Mem:/ {printf "%.2f", $3/$2 * 100}')
|
|
|
|
|
if (( $(echo "$ram_usage > 90" | bc -l) )); then
|
|
|
|
|
send_notification "⚠️ $(translate "Uso alto de RAM:") $ram_usage%"
|
|
|
|
|
last_ram_notification=$current_time
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Temperatura alta (NO CRÍTICO - con intervalo)
|
|
|
|
|
if [[ "$temp_high" -eq 1 ]] && (( current_time - last_temp_notification > resource_interval )); then
|
|
|
|
|
if command -v sensors &>/dev/null; then
|
|
|
|
|
# Intentar diferentes patrones para detectar la temperatura
|
|
|
|
|
temp=$(sensors | grep -E 'Package id 0:|Core 0:|CPU:' | head -1 | grep -oP '\+\d+\.\d+°C' | grep -oP '\d+\.\d+' || echo "0")
|
|
|
|
|
|
|
|
|
|
if [[ -n "$temp" && "$temp" != "0" ]] && (( $(echo "$temp > 80" | bc -l) )); then
|
|
|
|
|
send_notification "⚠️ $(translate "Temperatura alta del sistema:") $temp°C"
|
|
|
|
|
last_temp_notification=$current_time
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Pausa entre comprobaciones
|
|
|
|
|
sleep 30
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función para configurar Telegram
|
|
|
|
|
configure_telegram() {
|
|
|
|
|
# Cargar configuración existente (si existe)
|
|
|
|
|
[[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE"
|
|
|
|
|
|
|
|
|
|
# Solicitar BOT_TOKEN
|
|
|
|
|
BOT_TOKEN=$(whiptail --title "$(translate "Configuración de Telegram")" \
|
|
|
|
|
--inputbox "$(translate "Introduce tu Token de Bot de Telegram:")" 10 70 "$BOT_TOKEN" 3>&1 1>&2 2>&3)
|
|
|
|
|
|
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Solicitar CHAT_ID
|
|
|
|
|
CHAT_ID=$(whiptail --title "$(translate "Configuración de Telegram")" \
|
|
|
|
|
--inputbox "$(translate "Introduce tu ID de Chat de Telegram:")" 10 70 "$CHAT_ID" 3>&1 1>&2 2>&3)
|
|
|
|
|
|
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Guardar configuración en archivo
|
|
|
|
|
if [[ -n "$BOT_TOKEN" && -n "$CHAT_ID" ]]; then
|
|
|
|
|
# Hacer una copia de seguridad del archivo de configuración
|
|
|
|
|
cp "$CONFIG_FILE" "${CONFIG_FILE}.bak" 2>/dev/null
|
|
|
|
|
|
|
|
|
|
# Verificar si las variables ya existen en el archivo
|
|
|
|
|
if grep -q "^BOT_TOKEN=" "$CONFIG_FILE"; then
|
|
|
|
|
sed -i "s/^BOT_TOKEN=.*/BOT_TOKEN=\"$BOT_TOKEN\"/" "$CONFIG_FILE"
|
|
|
|
|
else
|
|
|
|
|
echo "BOT_TOKEN=\"$BOT_TOKEN\"" >> "$CONFIG_FILE"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if grep -q "^CHAT_ID=" "$CONFIG_FILE"; then
|
|
|
|
|
sed -i "s/^CHAT_ID=.*/CHAT_ID=\"$CHAT_ID\"/" "$CONFIG_FILE"
|
|
|
|
|
else
|
|
|
|
|
echo "CHAT_ID=\"$CHAT_ID\"" >> "$CONFIG_FILE"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Recargar la configuración
|
|
|
|
|
source "$CONFIG_FILE"
|
|
|
|
|
|
|
|
|
|
# Probar la configuración inmediatamente
|
|
|
|
|
response=$(curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
|
|
|
|
|
-d "chat_id=$CHAT_ID" \
|
|
|
|
|
-d "text=$(translate "¡Telegram está funcionando correctamente!")")
|
|
|
|
|
|
|
|
|
|
if [[ "$response" =~ "ok\":true" ]]; then
|
|
|
|
|
whiptail --title "$(translate "Éxito")" \
|
|
|
|
|
--msgbox "$(translate "Configuración de Telegram válida. Las notificaciones serán enviadas.")" 10 70
|
|
|
|
|
else
|
|
|
|
|
whiptail --title "$(translate "Error")" \
|
|
|
|
|
--msgbox "$(translate "Configuración de Telegram inválida. Por favor, verifica el token y el ID de chat.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
whiptail --title "$(translate "Error")" \
|
|
|
|
|
--msgbox "$(translate "Configuración de Telegram incompleta. Por favor, proporciona tanto el token como el ID de chat.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Función para iniciar el servicio de notificaciones
|
|
|
|
|
start_notification_service() {
|
|
|
|
|
if [[ ! -f /etc/systemd/system/proxmox-telegram.service ]]; then
|
|
|
|
|
install_systemd_service
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if systemctl is-active --quiet proxmox-telegram.service; then
|
|
|
|
|
whiptail --title "$(translate "Información")" \
|
|
|
|
|
--msgbox "$(translate "El servicio de notificaciones ya está en ejecución.")" 10 70
|
|
|
|
|
else
|
|
|
|
|
systemctl start proxmox-telegram.service
|
|
|
|
|
if systemctl is-active --quiet proxmox-telegram.service; then
|
|
|
|
|
whiptail --title "$(translate "Iniciado")" \
|
|
|
|
|
--msgbox "$(translate "El servicio se ha iniciado correctamente.")" 10 70
|
|
|
|
|
else
|
|
|
|
|
whiptail --title "$(translate "Error")" \
|
|
|
|
|
--msgbox "$(translate "No se pudo iniciar el servicio de notificaciones.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función para detener el servicio
|
|
|
|
|
stop_notification_service() {
|
|
|
|
|
if [[ -f /etc/systemd/system/proxmox-telegram.service ]]; then
|
|
|
|
|
if systemctl is-active --quiet proxmox-telegram.service; then
|
|
|
|
|
systemctl stop proxmox-telegram.service
|
|
|
|
|
sleep 2
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if ! systemctl is-active --quiet proxmox-telegram.service; then
|
|
|
|
|
whiptail --title "$(translate "Detenido")" \
|
|
|
|
|
--msgbox "$(translate "El servicio ha sido detenido correctamente.")" 10 70
|
|
|
|
|
else
|
|
|
|
|
whiptail --title "$(translate "Error")" \
|
|
|
|
|
--msgbox "$(translate "No se pudo detener el servicio de notificaciones.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
whiptail --title "$(translate "Información")" \
|
|
|
|
|
--msgbox "$(translate "El servicio de notificaciones no está instalado aún.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función para verificar el estado del servicio
|
|
|
|
|
check_service_status() {
|
|
|
|
|
clear
|
|
|
|
|
if [[ -f /etc/systemd/system/proxmox-telegram.service ]]; then
|
|
|
|
|
systemctl status proxmox-telegram.service
|
|
|
|
|
else
|
|
|
|
|
echo "$(translate "El servicio no está instalado.")"
|
|
|
|
|
fi
|
|
|
|
|
echo
|
|
|
|
|
echo "$(translate "Pulsa Enter para volver al menú...")"
|
|
|
|
|
read -r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Función para eliminar el servicio systemd
|
|
|
|
|
remove_systemd_service() {
|
|
|
|
|
if [[ -f /etc/systemd/system/proxmox-telegram.service ]]; then
|
|
|
|
|
if systemctl is-active --quiet proxmox-telegram.service; then
|
|
|
|
|
systemctl stop proxmox-telegram.service
|
|
|
|
|
fi
|
|
|
|
|
systemctl disable proxmox-telegram.service
|
|
|
|
|
rm -f /etc/systemd/system/proxmox-telegram.service
|
|
|
|
|
systemctl daemon-reexec
|
|
|
|
|
whiptail --title "$(translate "Eliminado")" \
|
|
|
|
|
--msgbox "$(translate "El servicio ha sido eliminado correctamente. Puedes reinstalarlo desde el menú si lo deseas.")" 10 70
|
|
|
|
|
else
|
|
|
|
|
whiptail --title "$(translate "Información")" \
|
|
|
|
|
--msgbox "$(translate "El servicio no existe, no se requiere eliminar nada.")" 10 70
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-26 18:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-26 18:54:30 +01:00
|
|
|
|
# Función para instalar el servicio como un servicio systemd
|
|
|
|
|
install_systemd_service() {
|
|
|
|
|
mkdir -p "$PID_DIR"
|
2025-03-26 18:56:51 +01:00
|
|
|
|
|
2025-03-26 18:54:30 +01:00
|
|
|
|
cat > /etc/systemd/system/proxmox-telegram.service <<EOF
|
|
|
|
|
[Unit]
|
|
|
|
|
Description=Proxmox Telegram Notification Service
|
|
|
|
|
After=network.target pve-cluster.service
|
|
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
|
Type=simple
|
2025-03-26 18:56:51 +01:00
|
|
|
|
ExecStart=/bin/bash -c 'bash <(curl -fsSL https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/telegram-notifier.sh) start_silent'
|
|
|
|
|
ExecStop=/bin/bash -c 'bash <(curl -fsSL https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/telegram-notifier.sh) stop_silent'
|
2025-03-26 18:54:30 +01:00
|
|
|
|
Restart=on-failure
|
|
|
|
|
PIDFile=$PID_DIR/service.pid
|
|
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
|
EOF
|
|
|
|
|
|
2025-03-26 18:56:51 +01:00
|
|
|
|
systemctl daemon-reexec
|
2025-03-26 18:54:30 +01:00
|
|
|
|
systemctl enable proxmox-telegram.service
|
|
|
|
|
systemctl start proxmox-telegram.service
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Menú principal
|
|
|
|
|
main_menu() {
|
|
|
|
|
local extra_option=""
|
|
|
|
|
if [[ -f /etc/systemd/system/proxmox-telegram.service ]]; then
|
2025-03-26 18:56:51 +01:00
|
|
|
|
extra_option="8 \"\$(translate \"Eliminar Servicio de Notificaciones\")\""
|
2025-03-26 18:54:30 +01:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
while true; do
|
2025-03-26 18:56:51 +01:00
|
|
|
|
OPTION=$(eval whiptail --title "\"\$(translate \"Configuración de Notificaciones de Proxmox\")\"" \
|
|
|
|
|
--menu "\"\$(translate \"Elige una opción:\")\"" 20 70 10 \
|
|
|
|
|
"1" "\"\$(translate \"Configurar Telegram\")\"" \
|
|
|
|
|
"2" "\"\$(translate \"Configurar Notificaciones\")\"" \
|
|
|
|
|
"3" "\"\$(translate \"Iniciar Servicio de Notificaciones\")\"" \
|
|
|
|
|
"4" "\"\$(translate \"Detener Servicio de Notificaciones\")\"" \
|
|
|
|
|
"5" "\"\$(translate \"Verificar Estado del Servicio\")\"" \
|
|
|
|
|
"7" "\"\$(translate \"Salir\")\"" \
|
2025-03-26 18:54:30 +01:00
|
|
|
|
$extra_option 3>&1 1>&2 2>&3)
|
|
|
|
|
|
|
|
|
|
if [[ $? -ne 0 ]]; then exit 0; fi
|
|
|
|
|
|
|
|
|
|
case "$OPTION" in
|
|
|
|
|
1) configure_telegram ;;
|
|
|
|
|
2) configure_notifications ;;
|
|
|
|
|
3) start_notification_service ;;
|
|
|
|
|
4) stop_notification_service ;;
|
|
|
|
|
5) check_service_status ;;
|
|
|
|
|
7) exit 0 ;;
|
|
|
|
|
8) remove_systemd_service ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-26 19:14:20 +01:00
|
|
|
|
case "$1" in
|
|
|
|
|
start_silent) start_silent ;;
|
|
|
|
|
stop_silent) stop_silent ;;
|
|
|
|
|
*) main_menu ;;
|
|
|
|
|
esac
|