Files
onever_drive/windows-agent/agent_tray.py
T

141 lines
4.7 KiB
Python

import os
import sys
import threading
import time
from pathlib import Path
from PIL import Image
import pystray
from pystray import MenuItem as item
# Add agent root to sys.path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from agent.config import load_config, AgentConfig
from agent.service import AgentDaemon
from agent.uploader import ChunkUploader
from create_icons import generate_app_icons
class WindowsTrayAgent:
"""Windows System Tray (Área de Notificaciones) Application for OnEver Drive."""
def __init__(self):
self.config = load_config()
self.daemon = AgentDaemon(self.config)
self.icon = None
self.is_paused = False
self.icon_image = self._load_icon()
def _load_icon(self) -> Image.Image:
assets_dir = Path(__file__).resolve().parent / "assets"
png_path = assets_dir / "icon.png"
if not png_path.exists():
_, png_path = generate_app_icons()
return Image.open(png_path)
def _get_status_text(self) -> str:
if not self.config.client_code:
return "Estado: Sin Registrar"
if self.is_paused:
return "Estado: Pausado"
return f"Estado: Conectado ({self.config.client_code})"
def _toggle_pause(self, icon, item_obj):
self.is_paused = not self.is_paused
if self.is_paused:
self.daemon.stop()
self.notify("Sincronización en pausa", "El servicio de backup ha sido pausado.")
else:
self.daemon.start()
self.notify("Sincronización activa", "El servicio de backup se ha reanudado.")
def _open_gui(self, icon=None, item_obj=None):
def run_gui():
from agent_gui import launch_gui
launch_gui()
threading.Thread(target=run_gui, daemon=True).start()
def _manual_backup(self, icon=None, item_obj=None):
import tkinter as tk
from tkinter import filedialog, messagebox
def run_picker():
root = tk.Tk()
root.withdraw()
filepath = filedialog.askopenfilename(
title="Seleccionar archivo para backup",
filetypes=[("Archivos SQL / Datos", "*.bak;*.mdf;*.*")]
)
if not filepath:
root.destroy()
return
self.notify("Iniciando Backup", f"Preparando transferencia por chunks: {Path(filepath).name}")
def upload_worker():
try:
uploader = ChunkUploader(self.config)
res = uploader.upload_file(Path(filepath))
self.notify("Backup Completado ✓", f"{Path(filepath).name} verificado con éxito en el servidor.")
except Exception as ex:
self.notify("Error en Backup ✗", f"Fallo al subir {Path(filepath).name}: {str(ex)}")
threading.Thread(target=upload_worker, daemon=True).start()
root.destroy()
threading.Thread(target=run_picker, daemon=True).start()
def notify(self, title: str, message: str):
"""Displays a native Windows Notification Balloon."""
if self.icon:
try:
self.icon.notify(message, title)
except Exception:
pass
def _on_exit(self, icon, item_obj):
self.daemon.stop()
icon.stop()
def build_menu(self):
client_title = f"OnEver Drive ({self.config.client_name or 'Agente'})"
return pystray.Menu(
item(client_title, lambda: None, enabled=False),
item(lambda text: self._get_status_text(), lambda: None, enabled=False),
pystray.Menu.SEPARATOR,
item("Abrir Panel de Control...", self._open_gui, default=True),
item("Hacer Backup Manual...", self._manual_backup),
item(lambda text: "Reanudar Servicio" if self.is_paused else "Pausar Servicio", self._toggle_pause),
pystray.Menu.SEPARATOR,
item("Salir", self._on_exit)
)
def run(self):
# Start background daemon worker
if self.config.device_id:
self.daemon.start()
# Create tray icon
self.icon = pystray.Icon(
name="OnEverDrive",
icon=self.icon_image,
title="OnEver Drive — Agente de Backup",
menu=self.build_menu()
)
# Notify on startup
if self.config.client_code:
self.notify("OnEver Drive Activo", f"Agente en ejecución ({self.config.client_code})")
else:
self.notify("OnEver Drive", "Agente iniciado. Requiere registro en el servidor.")
# Run system tray event loop
self.icon.run()
def main():
agent = WindowsTrayAgent()
agent.run()
if __name__ == "__main__":
main()