diff --git a/Main2.py b/Main2.py deleted file mode 100644 index f2aae06..0000000 --- a/Main2.py +++ /dev/null @@ -1,46 +0,0 @@ -import smtplib -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from datetime import datetime - -# Configuración del servidor SMTP (Sin SSL) -SMTP_SERVER = "192.168.120.103" -SMTP_PORT = 25 # También puedes probar 587 si 25 no funciona -EMAIL_USER = "pruebas@psp.ieslamar.org" -EMAIL_PASS = "1234" - -def enviar_correo(destinatario, asunto, mensaje): - try: - # Obtener la fecha y hora actual - fecha_envio = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - # Crear mensaje con la fecha incluida - msg = MIMEMultipart() - msg["From"] = EMAIL_USER - msg["To"] = destinatario - msg["Subject"] = asunto - msg["Date"] = fecha_envio # Agregar la fecha en la cabecera del correo - - # Formato del mensaje con la fecha en el cuerpo - mensaje_completo = f""" - asdasdsad - - {mensaje} - """ - - msg.attach(MIMEText(mensaje_completo, "plain")) # Mensaje en texto plano - - # Conectar al servidor SMTP SIN SSL - server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) - server.ehlo() - server.login(EMAIL_USER, EMAIL_PASS) # Iniciar sesión - server.sendmail(EMAIL_USER, destinatario, msg.as_string()) - server.quit() - - print(f"Correo enviado a {destinatario} el {fecha_envio}") - - except Exception as e: - print(f"Error enviando correo: {e}") - -# Uso del script -enviar_correo("kevin@psp.ieslamar.org", "Prueba de Correo", "Este es un mensaje de prueba sin SSL.") diff --git a/Main3.py b/Main3.py deleted file mode 100644 index 0bf156b..0000000 --- a/Main3.py +++ /dev/null @@ -1,117 +0,0 @@ -import poplib -import email -import pymongo -from email.utils import parsedate_to_datetime - -# 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() diff --git a/__pycache__/controlador.cpython-313.pyc b/__pycache__/controlador.cpython-313.pyc index 8fecb21..2efa4c8 100644 Binary files a/__pycache__/controlador.cpython-313.pyc and b/__pycache__/controlador.cpython-313.pyc differ diff --git a/__pycache__/modelo.cpython-313.pyc b/__pycache__/modelo.cpython-313.pyc index 1db8443..9a44d12 100644 Binary files a/__pycache__/modelo.cpython-313.pyc and b/__pycache__/modelo.cpython-313.pyc differ diff --git a/__pycache__/vista.cpython-313.pyc b/__pycache__/vista.cpython-313.pyc index 5cd3023..11f572a 100644 Binary files a/__pycache__/vista.cpython-313.pyc and b/__pycache__/vista.cpython-313.pyc differ diff --git a/controlador.py b/controlador.py index f25d05e..a4bb3cb 100644 --- a/controlador.py +++ b/controlador.py @@ -1,17 +1,37 @@ from modelo import CorreoModelo from tkinter import messagebox +import threading class CorreoControlador: def __init__(self): self.modelo = CorreoModelo() def descargar_correos(self): + self.vista.actualizar_footer("📥 Descargando correos...") + hilo = threading.Thread(target=self._descargar_correos) + hilo.start() + + 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() + self.vista.actualizar_footer("Gestor de Correos - 2025") def obtener_correos(self): return self.modelo.obtener_correos() + + def enviar_correo(self, destinatario, asunto, mensaje): + hilo = threading.Thread(target=self._enviar_correo(destinatario,asunto,mensaje)) + hilo.start(); + + def _enviar_correo(self, destinatario, asunto, mensaje): + self.vista.actualizar_footer("📨 Enviando correo...") + resultado, mensaje_respuesta = CorreoModelo.enviar_correo(destinatario, asunto, mensaje) + if resultado: + messagebox.showinfo("Éxito", mensaje_respuesta) + else: + messagebox.showerror("Error", mensaje_respuesta) + self.vista.actualizar_footer("Gestor de Correos - 2025") diff --git a/modelo.py b/modelo.py index 55e1524..61c598d 100644 --- a/modelo.py +++ b/modelo.py @@ -2,16 +2,25 @@ import poplib import email import pymongo from email.utils import parsedate_to_datetime +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from datetime import datetime +import re class CorreoModelo: - POP3_SERVER = "192.168.120.103" - POP3_PORT = 110 - EMAIL_USER = "kevin@psp.ieslamar.org" + POP3_SERVER = "s1.ieslamar.org" #192.168.120.103 + POP3_PORT = 110 #110 + + SMTP_SERVER = "s1.ieslamar.org" #s1.ieslamar.org + SMTP_PORT = 25 #25 + + EMAIL_USER = "kevin@fp.ieslamar.org" EMAIL_PASS = "1234" MONGO_CLIENT = "mongodb://localhost:27017/" - DB_NAME = "correo_db" - COLLECTION_NAME = "correos" + DB_NAME = "correo_db" + COLLECTION_NAME = "correoc" #*s def __init__(self): self.client = pymongo.MongoClient(self.MONGO_CLIENT) @@ -73,9 +82,6 @@ class CorreoModelo: 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) @@ -100,11 +106,45 @@ class CorreoModelo: if not self.correo_existe(remitente, asunto, fecha): mail.quit() - return True # Hay al menos un mensaje nuevo + return True mail.quit() - return False # No hay mensajes nuevos + return False except Exception as e: - return False # En caso de error, asumimos que no hay nuevos mensajes + return False + @staticmethod + def enviar_correo(destinatario, asunto, mensaje): + try: + if not CorreoModelo.validar_correo(destinatario): + return False, "Dirección de correo inválida" + + fecha_envio = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + msg = MIMEMultipart() + msg["From"] = CorreoModelo.EMAIL_USER + msg["To"] = destinatario + msg["Subject"] = asunto + msg["Date"] = fecha_envio + + mensaje_completo = f""" + {mensaje} + """ + msg.attach(MIMEText(mensaje_completo, "plain")) + + server = smtplib.SMTP(CorreoModelo.SMTP_SERVER, CorreoModelo.SMTP_PORT) + server.ehlo() + server.login(CorreoModelo.EMAIL_USER, CorreoModelo.EMAIL_PASS) + server.sendmail(CorreoModelo.EMAIL_USER, destinatario, msg.as_string()) + server.quit() + + return True, f"Correo enviado a {destinatario} el {fecha_envio}" + + except Exception as e: + return False, f"Error enviando correo: {e}" + + @staticmethod + def validar_correo(destinatario): + patron = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$" + return re.match(patron, destinatario) is not None diff --git a/vista.py b/vista.py index 713effb..b7a834c 100644 --- a/vista.py +++ b/vista.py @@ -11,6 +11,7 @@ class CorreoVista: self.root.title("📬 Gestor de Correos") self.root.geometry("900x600") self.root.configure(bg="#f4f4f4") + self.archivos_adjuntos = [] # Frame del menú izquierdo self.menu_frame = tk.Frame(self.root, bg="#333333", width=200) @@ -22,6 +23,9 @@ class CorreoVista: 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) + + self.btn_f3 = tk.Button(self.menu_frame, text="📤 Enviar Correo", font=("Arial", 12), bg="#555555", fg="white", relief=tk.FLAT, command=self.mostrar_frame_f3) + self.btn_f3.pack(fill=tk.X, pady=5, padx=5) # Frame principal derecho self.main_frame = tk.Frame(self.root, bg="#f4f4f4") @@ -37,9 +41,11 @@ class CorreoVista: # 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.frame_f3 = tk.Frame(self.main_frame, bg="#f4f4f4") self.crear_frame_f1() self.crear_frame_f2() + self.crear_frame_f3() # Mostrar el primer frame por defecto self.frame_f1.pack(fill=tk.BOTH, expand=True) @@ -49,11 +55,18 @@ class CorreoVista: def mostrar_frame_f1(self): self.frame_f2.pack_forget() + self.frame_f3.pack_forget() self.frame_f1.pack(fill=tk.BOTH, expand=True) def mostrar_frame_f2(self): self.frame_f1.pack_forget() + self.frame_f3.pack_forget() self.frame_f2.pack(fill=tk.BOTH, expand=True) + + def mostrar_frame_f3(self): + self.frame_f1.pack_forget() + self.frame_f2.pack_forget() + self.frame_f3.pack(fill=tk.BOTH, expand=True) def crear_frame_f1(self): frame_top = tk.Frame(self.frame_f1, bg="#f4f4f4") @@ -82,9 +95,54 @@ class CorreoVista: 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 = tk.Label(self.frame_f2, text="⚙️ Configuración de Usuario", font=("Arial", 16), bg="#f4f4f4") label_config.pack(pady=20) + tk.Label(self.frame_f2, text="Usuario (correo):", font=("Arial", 12), bg="#f4f4f4").pack() + self.entry_usuario = tk.Entry(self.frame_f2, font=("Arial", 12), width=50) + self.entry_usuario.pack(pady=5) + + tk.Label(self.frame_f2, text="Contraseña:", font=("Arial", 12), bg="#f4f4f4").pack() + self.entry_contraseña = tk.Entry(self.frame_f2, font=("Arial", 12), width=50, show="*") + self.entry_contraseña.pack(pady=5) + + btn_cambiar = ttk.Button(self.frame_f2, text="🔄 Cambiar", command=self.cambiar_credenciales) + btn_cambiar.pack(pady=10) + + + def crear_frame_f3(self): + label_envio = tk.Label(self.frame_f3, text="📤 Enviar Correo", font=("Arial", 16), bg="#f4f4f4") + label_envio.pack(pady=20) + + frame_inputs = tk.Frame(self.frame_f3, bg="#f4f4f4") + frame_inputs.pack(pady=10, padx=10, fill=tk.X) + + tk.Label(frame_inputs, text="Para:", font=("Arial", 12), bg="#f4f4f4").grid(row=0, column=0, sticky="w", padx=5) + self.entry_destinatario = tk.Entry(frame_inputs, font=("Arial", 12), width=50) + self.entry_destinatario.grid(row=0, column=1, pady=5) + + tk.Label(frame_inputs, text="Asunto:", font=("Arial", 12), bg="#f4f4f4").grid(row=1, column=0, sticky="w", padx=5) + self.entry_asunto = tk.Entry(frame_inputs, font=("Arial", 12), width=50) + self.entry_asunto.grid(row=1, column=1, pady=5) + + tk.Label(self.frame_f3, text="Mensaje:", font=("Arial", 12), bg="#f4f4f4").pack(anchor="w", padx=10) + self.text_mensaje = scrolledtext.ScrolledText(self.frame_f3, wrap=tk.WORD, height=10, font=("Arial", 12)) + self.text_mensaje.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + btn_enviar = ttk.Button(self.frame_f3, text="📨 Enviar", command=self.enviar_correo) + btn_enviar.pack(pady=10) + + def enviar_correo(self): + destinatario = self.entry_destinatario.get().strip() + asunto = self.entry_asunto.get().strip() + mensaje = self.text_mensaje.get("1.0", tk.END).strip() + + if not destinatario or not asunto or not mensaje: + messagebox.showwarning("⚠️ Advertencia", "Todos los campos son obligatorios.") + return + + self.controlador.enviar_correo(destinatario, asunto, mensaje) + def mostrar_correo(self): seleccionado = self.tree.selection() if not seleccionado: @@ -118,9 +176,24 @@ class CorreoVista: # 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 + time.sleep(2) # 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) + + def actualizar_footer(self, mensaje): + """ Cambia el texto de la barra inferior """ + self.footer_label.config(text=mensaje) + + def cambiar_credenciales(self): + nuevo_usuario = self.entry_usuario.get().strip() + nueva_contraseña = self.entry_contraseña.get().strip() + + if not nuevo_usuario or not nueva_contraseña: + messagebox.showwarning("⚠️ Advertencia", "Usuario y contraseña no pueden estar vacíos.") + return + + self.controlador.cambiar_credenciales(nuevo_usuario, nueva_contraseña) + messagebox.showinfo("✅ Éxito", "Credenciales actualizadas correctamente.") \ No newline at end of file