Añadir email y chat

This commit is contained in:
mireya 2026-02-27 11:05:20 +00:00
parent 22d3fb98ff
commit 83c4994194
1 changed files with 212 additions and 7 deletions

219
main.py
View File

@ -3,8 +3,8 @@ from tkinter import Menu, ttk, messagebox, filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import os import os
import threading import threading
from PIL import Image, ImageTk
# Importaciones de módulos de lógica
import utility_thread as ut import utility_thread as ut
import monitor_logic as ml import monitor_logic as ml
import alarm_logic as al import alarm_logic as al
@ -15,6 +15,8 @@ import camel_game_logic as cgl
import audio_player_logic as apl import audio_player_logic as apl
import external_launcher as el import external_launcher as el
import scraping_logic as sl import scraping_logic as sl
import email_logic as eml
import chat_logic as cl
# --- CLASE ALARMLISTFRAME --- # --- CLASE ALARMLISTFRAME ---
class AlarmListFrame(tk.Frame): class AlarmListFrame(tk.Frame):
@ -88,11 +90,14 @@ class MainApplication(tk.Tk):
self.selected_alarm_is_active = False self.selected_alarm_is_active = False
self.camel_threads = [] self.camel_threads = []
self.winner_name = None self.winner_name = None
# Variables para el scraping
self.scraping_search_term_var = None self.scraping_search_term_var = None
self.scraping_results_text = None self.scraping_results_text = None
self.DEBUG_HTML_FILE = "amazon_debugging_output.html" self.DEBUG_HTML_FILE = "amazon_debugging_output.html"
self.chat_display = None
self.chat_net = cl.ChatNetwork(self.receive_chat_message)
self.chat_net.start_server()
self.configure_layout() self.configure_layout()
self.create_widgets() self.create_widgets()
@ -102,14 +107,16 @@ class MainApplication(tk.Tk):
self.start_alarm_checker() self.start_alarm_checker()
nm.start_network_monitoring_thread(self) nm.start_network_monitoring_thread(self)
# INICIALIZAR EL REPRODUCTOR DE MÚSICA
self.music_player = apl.MusicPlayer(self) self.music_player = apl.MusicPlayer(self)
self.protocol("WM_DELETE_WINDOW", self.on_closing) self.protocol("WM_DELETE_WINDOW", self.on_closing)
def on_closing(self): def on_closing(self):
"""Detiene la música y destruye la ventana al cerrar la app.""" """Detiene la música y destruye la ventana al cerrar la app."""
self.music_player.stop_music() self.music_player.stop_music()
self.destroy() self.destroy()
if hasattr(self, 'chat_net'):
self.chat_net.stop()
def configure_layout(self): def configure_layout(self):
self.columnconfigure(0, weight=0) self.columnconfigure(0, weight=0)
@ -133,9 +140,11 @@ class MainApplication(tk.Tk):
self.create_audio_player_tab() self.create_audio_player_tab()
self.create_external_launcher_tab() self.create_external_launcher_tab()
self.create_scraping_tab() self.create_scraping_tab()
self.create_email_tab()
self.notebook.bind('<<NotebookTabChanged>>', self.on_tab_change) self.notebook.bind('<<NotebookTabChanged>>', self.on_tab_change)
self.update_activity_status("Inicio de la aplicación") self.update_activity_status("Inicio de la aplicación")
self.create_chat_tab()
def create_menu(self): def create_menu(self):
@ -393,7 +402,6 @@ class MainApplication(tk.Tk):
tab_scraping.columnconfigure(0, weight=1) tab_scraping.columnconfigure(0, weight=1)
tab_scraping.rowconfigure(1, weight=1) tab_scraping.rowconfigure(1, weight=1)
# 1. Controles Superiores (Término y Botón)
frame_controls = ttk.Frame(tab_scraping, padding=10) frame_controls = ttk.Frame(tab_scraping, padding=10)
frame_controls.grid(row=0, column=0, sticky='ew', pady=(0, 10)) frame_controls.grid(row=0, column=0, sticky='ew', pady=(0, 10))
frame_controls.columnconfigure(1, weight=1) frame_controls.columnconfigure(1, weight=1)
@ -405,7 +413,6 @@ class MainApplication(tk.Tk):
ttk.Button(frame_controls, text="START SCRAPING", command=self.start_scraping_callback).grid(row=0, column=2, padx=10, sticky='e') ttk.Button(frame_controls, text="START SCRAPING", command=self.start_scraping_callback).grid(row=0, column=2, padx=10, sticky='e')
# 2. Área de Resultados (Texto)
frame_results = ttk.Frame(tab_scraping, relief=tk.SUNKEN, borderwidth=2) frame_results = ttk.Frame(tab_scraping, relief=tk.SUNKEN, borderwidth=2)
frame_results.grid(row=1, column=0, sticky='nsew', padx=5, pady=5) frame_results.grid(row=1, column=0, sticky='nsew', padx=5, pady=5)
frame_results.columnconfigure(0, weight=1) frame_results.columnconfigure(0, weight=1)
@ -440,7 +447,6 @@ class MainApplication(tk.Tk):
self.scraping_results_text.delete("1.0", tk.END) self.scraping_results_text.delete("1.0", tk.END)
self.scraping_results_text.insert("1.0", f"Buscando productos en Amazon para '{search_term}' usando Playwright. Esto puede tardar unos segundos...\n") self.scraping_results_text.insert("1.0", f"Buscando productos en Amazon para '{search_term}' usando Playwright. Esto puede tardar unos segundos...\n")
# LLAMADA CLAVE: Inicia el proceso asíncrono en un hilo de trabajo
sl.start_playwright_scraper(search_term, self) sl.start_playwright_scraper(search_term, self)
def _display_scraping_results(self, results, search_term): def _display_scraping_results(self, results, search_term):
@ -682,6 +688,205 @@ class MainApplication(tk.Tk):
def start_alarm_checker(self): def start_alarm_checker(self):
self.after(100, al.check_alarms, self.root, self.alarm_list_widget, self.show_alarm_popup) self.after(100, al.check_alarms, self.root, self.alarm_list_widget, self.show_alarm_popup)
# --- GESTIÓN DE EMAIL ---
def create_email_tab(self):
"""Inicializa la pestaña de correo."""
self.tab_email = ttk.Frame(self.notebook)
self.notebook.add(self.tab_email, text="Correo ✉️")
self.current_user = ""
self.current_pass = ""
self.show_email_login_screen()
def logout_email(self):
"""Limpia la sesión y vuelve al login."""
self.current_user = ""
self.current_pass = ""
self.show_email_login_screen()
self.update_activity_status("Sesión de correo cerrada.")
def show_email_inbox(self, email_list):
"""Bandeja de entrada responsive."""
for widget in self.tab_email.winfo_children(): widget.destroy()
navbar = tk.Frame(self.tab_email, bg="white", height=50)
navbar.pack(fill="x")
ttk.Button(navbar, text="📝 Redactar", command=self.open_compose_window).pack(side="left", padx=10)
ttk.Button(navbar, text="🔄 Actualizar", command=self.refresh_inbox).pack(side="left")
ttk.Button(navbar, text="🚪 Cerrar Sesión", command=self.logout_email).pack(side="right", padx=10)
container = tk.Frame(self.tab_email, bg="#f0f2f5")
container.pack(fill="both", expand=True)
canvas = tk.Canvas(container, bg="#f0f2f5", highlightthickness=0)
sb = ttk.Scrollbar(container, command=canvas.yview)
self.mail_frame = tk.Frame(canvas, bg="#f0f2f5")
self.mail_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
c_win = canvas.create_window((0,0), window=self.mail_frame, anchor="nw")
canvas.bind("<Configure>", lambda e: canvas.itemconfig(c_win, width=e.width))
canvas.configure(yscrollcommand=sb.set)
canvas.pack(side="left", fill="both", expand=True)
sb.pack(side="right", fill="y")
for m in email_list: self.create_mail_card(m)
def show_email_login_screen(self):
for widget in self.tab_email.winfo_children(): widget.destroy()
container = tk.Frame(self.tab_email, bg="#f5f7fa")
container.place(relx=0, rely=0, relwidth=1, relheight=1)
card = tk.Frame(container, bg="white", padx=40, pady=40, highlightbackground="#d1d9e6", highlightthickness=1)
card.place(relx=0.5, rely=0.5, anchor="center")
tk.Label(card, text="Acceso Correo", font=("Arial", 16, "bold"), bg="white").pack(pady=(0, 20))
tk.Label(card, text="Usuario", bg="white").pack(anchor="w")
self.ent_user = tk.Entry(card, width=30, highlightthickness=1)
self.ent_user.insert(0, "alumno@10.10.0.101")
self.ent_user.pack(pady=5)
tk.Label(card, text="Contraseña", bg="white").pack(anchor="w")
self.ent_pass = tk.Entry(card, show="*", width=30, highlightthickness=1)
self.ent_pass.pack(pady=5)
tk.Button(card, text="ENTRAR", bg="#3498db", fg="white", font=("Arial", 10, "bold"),
command=self.do_email_login, padx=20, pady=10).pack(pady=20, fill="x")
def do_email_login(self):
"""Realiza la conexión inicial."""
u, p = self.ent_user.get(), self.ent_pass.get()
success, data = eml.fetch_emails_logic(u, p)
if success:
self.current_user, self.current_pass = u, p
self.show_email_inbox(data)
else:
messagebox.showerror("Error", "Credenciales incorrectas o error de red")
def refresh_inbox(self):
"""Actualiza la bandeja."""
success, data = eml.fetch_emails_logic(self.current_user, self.current_pass)
if success: self.show_email_inbox(data)
def create_mail_card(self, mail):
"""Tarjetas expandibles de correo."""
card = tk.Frame(self.mail_frame, bg="white", highlightbackground="#ddd", highlightthickness=1)
card.pack(fill="x", pady=5, padx=15)
header = tk.Label(card, text=f"De: {mail['sender']}\nAsunto: {mail['subject']}",
font=("Arial", 10, "bold"), bg="white", justify="left", anchor="w", pady=10, padx=10)
header.pack(fill="x")
body_box = tk.Frame(card, bg="#f9f9f9")
self.img_refs = []
def expand(event):
if body_box.winfo_ismapped(): body_box.pack_forget()
else:
success, data = eml.fetch_email_full_data(self.current_user, self.current_pass, mail['id'])
if success:
for w in body_box.winfo_children(): w.destroy()
txt = tk.Text(body_box, height=8, bg="#f9f9f9", bd=0, padx=10, pady=10)
txt.insert("1.0", data['body'])
txt.config(state="disabled")
txt.pack(fill="x")
for img in data['images']:
try:
photo = ImageTk.PhotoImage(Image.open(img).convert("RGB").resize((300, 300)))
self.img_refs.append(photo)
tk.Label(body_box, image=photo, bg="#f9f9f9").pack(pady=5)
except: pass
for f_path in data['files']:
tk.Button(body_box, text=f"📄 Abrir: {os.path.basename(f_path)}",
fg="blue", command=lambda p=f_path: os.system(f'xdg-open "{p}"'),
bg="#f9f9f9", bd=0).pack(anchor="w", padx=20, pady=5)
body_box.pack(fill="x")
header.bind("<Button-1>", expand)
def open_compose_window(self):
comp = tk.Toplevel(self); comp.title("Redactar"); comp.geometry("400x500")
tk.Label(comp, text="Para:").pack(padx=10, anchor="w")
e_to = tk.Entry(comp); e_to.pack(fill="x", padx=10)
tk.Label(comp, text="Asunto:").pack(padx=10, anchor="w")
e_sub = tk.Entry(comp); e_sub.pack(fill="x", padx=10)
self.attachment_path = None
lbl_file = tk.Label(comp, text="Sin archivo")
def attach():
self.attachment_path = filedialog.askopenfilename()
if self.attachment_path: lbl_file.config(text=os.path.basename(self.attachment_path), fg="blue")
tk.Button(comp, text="📎 Adjuntar", command=attach).pack(pady=5)
lbl_file.pack()
txt = tk.Text(comp, height=10); txt.pack(fill="both", expand=True, padx=10, pady=10)
def send():
s, m = eml.send_email_logic(self.current_user, e_to.get(), e_sub.get(), txt.get("1.0", "end"), self.current_pass, self.attachment_path)
if s: messagebox.showinfo("OK", "Enviado"); comp.destroy()
else: messagebox.showerror("Error", m)
tk.Button(comp, text="ENVIAR 🚀", bg="#2ecc71", fg="white", command=send).pack(pady=10)
# --- GESTIÓN DEL CHAT ---
def create_chat_tab(self):
import os
import tkinter as tk
from tkinter import ttk
tab_chat = ttk.Frame(self.notebook)
self.notebook.add(tab_chat, text="Chat Grupal 💬", padding=15)
tab_chat.columnconfigure(0, weight=1)
tab_chat.rowconfigure(2, weight=1)
mi_pid = os.getpid()
lbl_pid = tk.Label(tab_chat, text=f"📍 TU NÚMERO DE PROCESO (PID) ES: {mi_pid}",
font=("Arial", 14, "bold"), fg="#2c3e50", bg="#ecf0f1", pady=10, relief="groove")
lbl_pid.grid(row=0, column=0, sticky="ew", pady=(0, 10))
tk.Label(tab_chat, text="Sala general (todos los procesos leen y escriben aquí):",
font=("Arial", 10, "bold"), anchor="w").grid(row=1, column=0, sticky="ew")
self.chat_display = tk.Text(tab_chat, state='disabled', height=15, bg="#fdfdfd", font=("Consolas", 11))
self.chat_display.grid(row=2, column=0, sticky="nsew", pady=5)
frame_input = ttk.Frame(tab_chat)
frame_input.grid(row=3, column=0, sticky="ew", pady=(5, 0))
self.chat_entry = ttk.Entry(frame_input, font=("Arial", 11))
self.chat_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
self.chat_entry.bind("<Return>", lambda e: self.send_chat_message())
ttk.Button(frame_input, text="Enviar 🚀", command=self.send_chat_message).pack(side="right")
def send_chat_message(self):
msg = self.chat_entry.get()
if msg.strip():
if self.chat_net.send_message(msg):
self.chat_entry.delete(0, tk.END)
def receive_chat_message(self, message):
import tkinter as tk
def _actualizar_ui():
if self.chat_display and self.chat_display.winfo_exists():
self.chat_display.config(state='normal')
self.chat_display.insert(tk.END, message + "\n")
self.chat_display.see(tk.END)
self.chat_display.config(state='disabled')
self.after(0, _actualizar_ui)
if __name__ == "__main__": if __name__ == "__main__":
app = MainApplication() app = MainApplication()