#!/bin/bash # /usr/local/bin/python-upload-watcher.sh # Watcher para entregas de Programación ASIR1 via SFTP # v7 — Nombre completo en notificaciones ([María Jara] en vez de [jara]) # # - Batching por carpeta: UN mensaje XMPP con listado completo # - Re-entregas: detecta delete+recreate (<120s) # - Windows-safe: ignora Thumbs.db, desktop.ini, .DS_Store, *.tmp, __pycache__, *.pyc WATCH_DIR="/home" # --- Mapa usuario → nombre completo --- fullname() { case "$1" in barja) echo "Alex Barja" ;; barrios) echo "Andrés Barrios" ;; cayo) echo "Jared Cayo" ;; contrera) echo "Luciano Contrera" ;; duque) echo "Jorge Duque" ;; florea) echo "Alejandro Florea" ;; gomes) echo "Gabriel Gomes" ;; izquierdo) echo "Daniel Izquierdo" ;; jara) echo "María Jara" ;; lillo) echo "Fernando Lillo" ;; linares) echo "Christopher Linares" ;; macedo) echo "Eduardo Macedo" ;; martinez) echo "Daniel Martínez" ;; munoz) echo "Jordy Muñoz" ;; olcina) echo "Jorge Olcina" ;; ponce) echo "Francisco Ponce" ;; posada) echo "Santiago Posada" ;; quiroz) echo "Alexander Quiroz" ;; reynoso) echo "Jorge Reynoso" ;; sierra) echo "Leonel Sierra" ;; torrero) echo "Mario Torrero" ;; *) echo "$1" ;; # fallback: username tal cual esac } LOG="/var/log/python-upload-watcher.log" BATCH_DIR="/tmp/python-watcher-batches" DELETE_DIR="/tmp/python-watcher-deletes" QUIET_SECONDS=10 mkdir -p "$BATCH_DIR" "$DELETE_DIR" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG" } notify_xmpp() { /usr/local/bin/xmpp-notify.py "$1" >/dev/null 2>&1 & } is_junk() { case "${1,,}" in thumbs.db|desktop.ini|.ds_store|*.tmp|~\$*|~*.tmp) return 0 ;; __pycache__|*.pyc|*.pyo) return 0 ;; .git|.venv|.env|node_modules|.idea|.vscode) return 0 ;; *) return 1 ;; esac } # Filtrar paths que contengan directorios basura is_junk_path() { case "$1" in */__pycache__/*|*/.git/*|*/.venv/*|*/node_modules/*|*/.idea/*|*/.vscode/*) return 0 ;; *) return 1 ;; esac } file_icon() { case "${1,,}" in *.py) echo "🐍" ;; *.zip|*.rar|*.tar*|*.gz|*.bz2|*.7z) echo "📦" ;; *.md) echo "📝" ;; *.txt) echo "📄" ;; *.pdf) echo "📕" ;; *.docx|*.doc) echo "📘" ;; *.png|*.jpg|*.jpeg|*.gif) echo "🖼️" ;; *) echo "📎" ;; esac } batch_key() { echo "${1}_${2}" | tr '/ ' '__' } flush_batch() { local batchfile="$1" [[ ! -f "$batchfile" ]] && return local user dir tag file_count file_list label msg user=$(sed -n '1p' "$batchfile") dir=$(sed -n '2p' "$batchfile") tag=$(sed -n '3p' "$batchfile") file_count=$(tail -n +4 "$batchfile" | wc -l) [[ "$file_count" -eq 0 ]] && { rm -f "$batchfile"; return; } file_list=$(tail -n +4 "$batchfile") label="Entrega" [[ "$tag" == "re-entrega" ]] && label="♻️ Re-entrega" local display_name display_name=$(fullname "$user") msg="📁 [$display_name] $label ASIR1: $dir/ ($file_count ficheros) $file_list" log "[$user] BATCH FLUSH ($tag): $dir/ → $file_count ficheros" notify_xmpp "$msg" rm -f "$batchfile" } flush_daemon() { while true; do sleep 3 for batchfile in "$BATCH_DIR"/*.batch; do [[ ! -f "$batchfile" ]] && continue local last_mod now age last_mod=$(stat -c%Y "$batchfile" 2>/dev/null) || continue now=$(date +%s) age=$(( now - last_mod )) (( age >= QUIET_SECONDS )) && flush_batch "$batchfile" done done } flush_daemon & FLUSH_PID=$! trap "kill $FLUSH_PID 2>/dev/null" EXIT log "=== Python Upload Watcher v7 iniciado ===" notify_xmpp "🚀 [zzz] Python Upload Watcher v7 iniciado (ASIR1)" # --------------------------------------------------------- # Bucle principal — sin 'local', todo variables globales # --------------------------------------------------------- inotifywait -m -r \ -e close_write -e moved_to -e create -e delete -e moved_from \ --format "%e %w%f" "$WATCH_DIR" 2>/dev/null | \ while IFS= read -r line; do _event="${line%% /*}" _filepath="/${line#* /}" [[ ! "$_filepath" =~ ^/home/([^/]+)/python/(.+)$ ]] && continue _user="${BASH_REMATCH[1]}" _relpath="${BASH_REMATCH[2]}" _filename=$(basename "$_relpath") is_junk "$_filename" && continue is_junk_path "$_relpath" && continue # --- DELETE / MOVED_FROM --- if [[ "$_event" == *DELETE* || "$_event" == *MOVED_FROM* ]]; then if [[ "$_relpath" != */* ]]; then _bk=$(batch_key "$_user" "$_relpath") echo "$(date +%s)" > "$DELETE_DIR/$_bk" rm -f "$BATCH_DIR/${_bk}.batch" log "[$_user] BORRADA: $_relpath/" fi continue fi # --- CREATE directorio --- if [[ "$_event" == *CREATE* && -d "$_filepath" ]]; then # Solo carpetas de primer nivel bajo python/ if [[ "$_relpath" != */* ]]; then _bk=$(batch_key "$_user" "$_relpath") _tag="new" if [[ -f "$DELETE_DIR/$_bk" ]]; then _del_ts=$(cat "$DELETE_DIR/$_bk") _now=$(date +%s) (( _now - _del_ts < 120 )) && _tag="re-entrega" rm -f "$DELETE_DIR/$_bk" fi printf '%s\n%s\n%s\n' "$(fullname "$_user")" "$_relpath" "$_tag" > "$BATCH_DIR/${_bk}.batch" log "[$_user] CARPETA ($_tag): $_relpath/ — batch abierto en $BATCH_DIR/${_bk}.batch" fi continue fi # --- CREATE fichero → skip (close_write viene después) --- [[ "$_event" == *CREATE* ]] && continue # --- close_write / moved_to → fichero completado --- _size=$(stat -c%s "$_filepath" 2>/dev/null || echo "?") _icon=$(file_icon "$_filename") if [[ "$_relpath" == */* ]]; then _topdir="${_relpath%%/*}" _bk=$(batch_key "$_user" "$_topdir") _batchfile="$BATCH_DIR/${_bk}.batch" if [[ -f "$_batchfile" ]]; then _subpath="${_relpath#*/}" echo " $_icon $_subpath ($_size bytes)" >> "$_batchfile" log "[$_user] +BATCH: $_relpath ($_size bytes)" continue fi # Sin batch → individual log "[$_user] Subido: $_relpath ($_size bytes)" notify_xmpp "$_icon [$(fullname "$_user")] Entrega ASIR1: $_relpath ($_size bytes)" else log "[$_user] Subido: $_relpath ($_size bytes)" notify_xmpp "$_icon [$(fullname "$_user")] Entrega ASIR1: $_relpath ($_size bytes)" fi done