diff --git a/Main.py b/Main.py index 0bf156b..cfabf3a 100644 --- a/Main.py +++ b/Main.py @@ -1,117 +1,10 @@ -import poplib -import email -import pymongo -from email.utils import parsedate_to_datetime +import tkinter as tk +from controlador import CorreoControlador +from vista import CorreoVista -# Configuración del servidor POP3 (Sin SSL) -POP3_SERVER = "192.168.120.103" -POP3_PORT = 110 # Puerto POP3 estándar sin SSL -EMAIL_USER = "kevin@psp.ieslamar.org" -EMAIL_PASS = "1234" - -# Configuración de la base de datos MongoDB -MONGO_CLIENT = "mongodb://localhost:27017/" -DB_NAME = "correo_db" -COLLECTION_NAME = "correos" - -# Conectar a MongoDB -client = pymongo.MongoClient(MONGO_CLIENT) -db = client[DB_NAME] -collection = db[COLLECTION_NAME] - -def correo_existe(remitente, asunto, fecha): - """ Verifica si un correo ya existe en la base de datos. """ - return collection.find_one({"remitente": remitente, "asunto": asunto, "fecha": fecha}) is not None - -def guardar_correo(remitente, asunto, fecha, cuerpo): - """ Guarda un correo en la base de datos si no existe. """ - if correo_existe(remitente, asunto, fecha): - print("⚠️ Correo ya guardado, se omite.") - return - - correo = { - "remitente": remitente, - "asunto": asunto, - "fecha": fecha, - "cuerpo": cuerpo - } - collection.insert_one(correo) - print("✅ Correo guardado en la base de datos.") - -def descargar_correos(): - """ Descarga correos desde el servidor y solo guarda los nuevos. """ - try: - print("📡 Conectando al servidor POP3 para descargar correos...\n") - mail = poplib.POP3(POP3_SERVER, POP3_PORT) - mail.user(EMAIL_USER) - mail.pass_(EMAIL_PASS) - - num_mensajes = len(mail.list()[1]) - print(f"📩 Se encontraron {num_mensajes} correos en la bandeja de entrada.\n") - - for i in range(1, num_mensajes + 1): - response, lines, octets = mail.retr(i) - raw_email = b"\n".join(lines) - msg = email.message_from_bytes(raw_email) - - remitente = msg["From"] - asunto = msg["Subject"] - fecha = msg["Date"] - - if fecha: - try: - fecha = parsedate_to_datetime(fecha).strftime("%Y-%m-%d %H:%M:%S") - except Exception: - pass - - cuerpo = "" - if msg.is_multipart(): - for part in msg.walk(): - if part.get_content_type() == "text/plain": - cuerpo = part.get_payload(decode=True).decode(errors="ignore") - break - else: - cuerpo = msg.get_payload(decode=True).decode(errors="ignore") - - guardar_correo(remitente, asunto, fecha, cuerpo.strip()) - - mail.quit() - print("✅ Descarga de correos completada.\n") - - except Exception as e: - print(f"❌ Error al descargar correos: {e}") - -def mostrar_correos(): - """ Muestra todos los correos almacenados en MongoDB. """ - print("📂 Mostrando correos almacenados en la base de datos...\n") - correos = collection.find() - - for correo in correos: - print(f"📅 Fecha: {correo['fecha']}") - print(f"🔹 Remitente: {correo['remitente']}") - print(f"📌 Asunto: {correo['asunto']}") - print(f"📝 Mensaje:\n{correo['cuerpo']}") - print("-" * 40) - -def menu(): - """ Menú interactivo para ejecutar las opciones del programa. """ - while True: - print("\n📬 MENÚ:") - print("1. Descargar correos nuevos") - print("2. Mostrar correos almacenados") - print("3. Salir") - - opcion = input("Seleccione una opción: ") - - if opcion == "1": - descargar_correos() - elif opcion == "2": - mostrar_correos() - elif opcion == "3": - print("👋 Saliendo...") - break - else: - print("❌ Opción no válida, intente de nuevo.") - -# Ejecutar el menú interactivo -menu() +if __name__ == "__main__": + root = tk.Tk() + controlador = CorreoControlador() + vista = CorreoVista(root, controlador) + controlador.vista = vista + root.mainloop() diff --git a/__pycache__/controlador.cpython-313.pyc b/__pycache__/controlador.cpython-313.pyc new file mode 100644 index 0000000..8fecb21 Binary files /dev/null and b/__pycache__/controlador.cpython-313.pyc differ diff --git a/__pycache__/modelo.cpython-313.pyc b/__pycache__/modelo.cpython-313.pyc new file mode 100644 index 0000000..1db8443 Binary files /dev/null and b/__pycache__/modelo.cpython-313.pyc differ diff --git a/__pycache__/vista.cpython-313.pyc b/__pycache__/vista.cpython-313.pyc new file mode 100644 index 0000000..5cd3023 Binary files /dev/null and b/__pycache__/vista.cpython-313.pyc differ diff --git a/controlador.py b/controlador.py new file mode 100644 index 0000000..f25d05e --- /dev/null +++ b/controlador.py @@ -0,0 +1,17 @@ +from modelo import CorreoModelo +from tkinter import messagebox + +class CorreoControlador: + def __init__(self): + self.modelo = CorreoModelo() + + def descargar_correos(self): + resultado = self.modelo.descargar_correos() + if resultado is True: + messagebox.showinfo("Éxito", "Correos descargados correctamente") + else: + messagebox.showerror("Error", f"Error al descargar correos: {resultado}") + self.vista.actualizar_lista() + + def obtener_correos(self): + return self.modelo.obtener_correos() diff --git a/modelo.py b/modelo.py new file mode 100644 index 0000000..55e1524 --- /dev/null +++ b/modelo.py @@ -0,0 +1,110 @@ +import poplib +import email +import pymongo +from email.utils import parsedate_to_datetime + +class CorreoModelo: + POP3_SERVER = "192.168.120.103" + POP3_PORT = 110 + EMAIL_USER = "kevin@psp.ieslamar.org" + EMAIL_PASS = "1234" + + MONGO_CLIENT = "mongodb://localhost:27017/" + DB_NAME = "correo_db" + COLLECTION_NAME = "correos" + + def __init__(self): + self.client = pymongo.MongoClient(self.MONGO_CLIENT) + self.db = self.client[self.DB_NAME] + self.collection = self.db[self.COLLECTION_NAME] + + def correo_existe(self, remitente, asunto, fecha): + return self.collection.find_one({"remitente": remitente, "asunto": asunto, "fecha": fecha}) is not None + + def guardar_correo(self, remitente, asunto, fecha, cuerpo): + if self.correo_existe(remitente, asunto, fecha): + return + correo = {"remitente": remitente, "asunto": asunto, "fecha": fecha, "cuerpo": cuerpo} + self.collection.insert_one(correo) + + def descargar_correos(self): + try: + mail = poplib.POP3(self.POP3_SERVER, self.POP3_PORT) + mail.user(self.EMAIL_USER) + mail.pass_(self.EMAIL_PASS) + + num_mensajes = len(mail.list()[1]) + mensajes_nuevos = False + + for i in range(1, num_mensajes + 1): + response, lines, octets = mail.retr(i) + raw_email = b"\n".join(lines) + msg = email.message_from_bytes(raw_email) + + remitente = msg["From"] + asunto = msg["Subject"] + fecha = msg["Date"] + + if fecha: + try: + fecha = parsedate_to_datetime(fecha).strftime("%Y-%m-%d %H:%M:%S") + except Exception: + pass + + cuerpo = "" + if msg.is_multipart(): + for part in msg.walk(): + if part.get_content_type() == "text/plain": + cuerpo = part.get_payload(decode=True).decode(errors="ignore") + break + else: + cuerpo = msg.get_payload(decode=True).decode(errors="ignore") + + if not self.correo_existe(remitente, asunto, fecha): + self.guardar_correo(remitente, asunto, fecha, cuerpo.strip()) + mensajes_nuevos = True + + mail.quit() + return True + except Exception as e: + return str(e) + + def obtener_correos(self): + return list(self.collection.find()) + + def hay_mensajes_nuevos(self): + """ + Verifica si hay correos nuevos en el servidor POP3 que no estén en la base de datos. + """ + try: + mail = poplib.POP3(self.POP3_SERVER, self.POP3_PORT) + mail.user(self.EMAIL_USER) + mail.pass_(self.EMAIL_PASS) + + num_mensajes = len(mail.list()[1]) + + for i in range(1, num_mensajes + 1): + response, lines, octets = mail.retr(i) + raw_email = b"\n".join(lines) + msg = email.message_from_bytes(raw_email) + + remitente = msg["From"] + asunto = msg["Subject"] + fecha = msg["Date"] + + if fecha: + try: + fecha = parsedate_to_datetime(fecha).strftime("%Y-%m-%d %H:%M:%S") + except Exception: + pass + + if not self.correo_existe(remitente, asunto, fecha): + mail.quit() + return True # Hay al menos un mensaje nuevo + + mail.quit() + return False # No hay mensajes nuevos + + except Exception as e: + return False # En caso de error, asumimos que no hay nuevos mensajes + diff --git a/vista.py b/vista.py new file mode 100644 index 0000000..713effb --- /dev/null +++ b/vista.py @@ -0,0 +1,126 @@ +import tkinter as tk +from tkinter import ttk, messagebox, scrolledtext +from controlador import CorreoControlador +import threading +import time + +class CorreoVista: + def __init__(self, root, controlador): + self.root = root + self.controlador = controlador + self.root.title("📬 Gestor de Correos") + self.root.geometry("900x600") + self.root.configure(bg="#f4f4f4") + + # Frame del menú izquierdo + self.menu_frame = tk.Frame(self.root, bg="#333333", width=200) + self.menu_frame.pack(side=tk.LEFT, fill=tk.Y) + + # Botones del menú + self.btn_f1 = tk.Button(self.menu_frame, text="📋 Listado", font=("Arial", 12), bg="#555555", fg="white", relief=tk.FLAT, command=self.mostrar_frame_f1) + self.btn_f1.pack(fill=tk.X, pady=5, padx=5) + + self.btn_f2 = tk.Button(self.menu_frame, text="⚙️ Configuración", font=("Arial", 12), bg="#555555", fg="white", relief=tk.FLAT, command=self.mostrar_frame_f2) + self.btn_f2.pack(fill=tk.X, pady=5, padx=5) + + # Frame principal derecho + self.main_frame = tk.Frame(self.root, bg="#f4f4f4") + self.main_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) + + # Barra inferior + self.footer_frame = tk.Frame(self.root, bg="#1E90FF", height=30) + self.footer_frame.place(relx=0, rely=1.0, relwidth=1, anchor="sw") + + self.footer_label = tk.Label(self.footer_frame, text="Gestor de Correos - 2025", bg="#1E90FF", fg="white", font=("Arial", 10)) + self.footer_label.pack(side=tk.RIGHT, padx=10) + + # Frames individuales para cada sección + self.frame_f1 = tk.Frame(self.main_frame, bg="#f4f4f4") + self.frame_f2 = tk.Frame(self.main_frame, bg="#f4f4f4") + + self.crear_frame_f1() + self.crear_frame_f2() + + # Mostrar el primer frame por defecto + self.frame_f1.pack(fill=tk.BOTH, expand=True) + + # Iniciar el hilo de actualización de la barra + self.ejecutar_hilo_actualizacion_barra() + + def mostrar_frame_f1(self): + self.frame_f2.pack_forget() + self.frame_f1.pack(fill=tk.BOTH, expand=True) + + def mostrar_frame_f2(self): + self.frame_f1.pack_forget() + self.frame_f2.pack(fill=tk.BOTH, expand=True) + + def crear_frame_f1(self): + frame_top = tk.Frame(self.frame_f1, bg="#f4f4f4") + frame_top.pack(pady=10, fill=tk.X) + + btn_descargar = ttk.Button(frame_top, text="📥 Descargar Correos", command=self.controlador.descargar_correos) + btn_descargar.pack(side=tk.LEFT, padx=10) + + btn_mostrar = ttk.Button(frame_top, text="🔍 Mostrar Correo", command=self.mostrar_correo) + btn_mostrar.pack(side=tk.LEFT, padx=10) + + frame_list = tk.Frame(self.frame_f1, bg="#ffffff", bd=2, relief=tk.GROOVE) + frame_list.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + self.tree = ttk.Treeview(frame_list, columns=("Remitente", "Asunto", "Fecha", "Cuerpo"), show="headings") + self.tree.heading("Remitente", text="✉️ Remitente") + self.tree.heading("Asunto", text="📌 Asunto") + self.tree.heading("Fecha", text="📅 Fecha") + self.tree.heading("Cuerpo", text="📝 Cuerpo") + self.tree.pack(fill=tk.BOTH, expand=True) + + frame_details = tk.Frame(self.frame_f1, bg="#ffffff", bd=2, relief=tk.GROOVE) + frame_details.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + self.detalle_text = scrolledtext.ScrolledText(frame_details, wrap=tk.WORD, height=10, font=("Arial", 10)) + self.detalle_text.pack(fill=tk.BOTH, expand=True) + + def crear_frame_f2(self): + label_config = tk.Label(self.frame_f2, text="⚙️ Configuración", font=("Arial", 16), bg="#f4f4f4") + label_config.pack(pady=20) + + def mostrar_correo(self): + seleccionado = self.tree.selection() + if not seleccionado: + messagebox.showwarning("⚠️ Advertencia", "Seleccione un correo") + return + + correo_id = seleccionado[0] + correo = self.tree.item(correo_id, "values") + + self.detalle_text.delete("1.0", tk.END) + self.detalle_text.insert(tk.END, f"📧 Remitente: {correo[0]}\n") + self.detalle_text.insert(tk.END, f"📌 Asunto: {correo[1]}\n") + self.detalle_text.insert(tk.END, f"📅 Fecha: {correo[2]}\n\n") + self.detalle_text.insert(tk.END, f"📝 Mensaje:\n{correo[3]}") + + def actualizar_lista(self): + self.tree.delete(*self.tree.get_children()) + for correo in self.controlador.obtener_correos(): + self.tree.insert("", "end", values=(correo["remitente"], correo["asunto"], correo["fecha"], correo["cuerpo"])) + + def ejecutar_hilo_actualizacion_barra(self): + """ Inicia un hilo para actualizar el color de la barra inferior cada 5 segundos. """ + hilo = threading.Thread(target=self.actualizar_barra_periodicamente, daemon=True) + hilo.start() + + def actualizar_barra_periodicamente(self): + """ Cambia el color de la barra inferior cada 5 segundos según si hay nuevos correos. """ + while True: + hay_nuevos = self.controlador.modelo.hay_mensajes_nuevos() + + # Cambiar color de la barra en el hilo principal de Tkinter + self.root.after(0, self.cambiar_color_barra, "#FF0000" if hay_nuevos else "#1E90FF") + + time.sleep(5) # Esperar 5 segundos antes de verificar nuevamente + + def cambiar_color_barra(self, color): + """ Cambia el color de la barra inferior. """ + self.footer_frame.configure(bg=color) + self.footer_label.configure(bg=color)