Proyecto
This commit is contained in:
parent
abc8b0d061
commit
650b20dda5
|
@ -4,12 +4,20 @@ import threading
|
||||||
|
|
||||||
class MailTab:
|
class MailTab:
|
||||||
def __init__(self, parent, mail_client):
|
def __init__(self, parent, mail_client):
|
||||||
|
"""
|
||||||
|
Inicializa la pestaña de envío de correos en la interfaz.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent (tk.Notebook): Contenedor de pestañas.
|
||||||
|
mail_client (MailClient): Cliente de correo que gestiona el envío de emails.
|
||||||
|
"""
|
||||||
self.mail_client = mail_client
|
self.mail_client = mail_client
|
||||||
|
|
||||||
|
# Crear un nuevo frame dentro del notebook
|
||||||
self.frame = ttk.Frame(parent)
|
self.frame = ttk.Frame(parent)
|
||||||
parent.add(self.frame, text="Correo")
|
parent.add(self.frame, text="Correo")
|
||||||
|
|
||||||
# Campos para ingresar credenciales del usuario
|
# Campos de entrada para el correo, contraseña y destinatario
|
||||||
ttk.Label(self.frame, text="Correo electrónico:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
|
ttk.Label(self.frame, text="Correo electrónico:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
|
||||||
self.entry_email = ttk.Entry(self.frame, width=40)
|
self.entry_email = ttk.Entry(self.frame, width=40)
|
||||||
self.entry_email.grid(row=0, column=1, padx=5, pady=5)
|
self.entry_email.grid(row=0, column=1, padx=5, pady=5)
|
||||||
|
@ -18,17 +26,16 @@ class MailTab:
|
||||||
self.entry_password = ttk.Entry(self.frame, show="*", width=40)
|
self.entry_password = ttk.Entry(self.frame, show="*", width=40)
|
||||||
self.entry_password.grid(row=1, column=1, padx=5, pady=5)
|
self.entry_password.grid(row=1, column=1, padx=5, pady=5)
|
||||||
|
|
||||||
# Campo para ingresar destinatario
|
|
||||||
ttk.Label(self.frame, text="Destinatario:").grid(row=2, column=0, sticky="e", padx=5, pady=5)
|
ttk.Label(self.frame, text="Destinatario:").grid(row=2, column=0, sticky="e", padx=5, pady=5)
|
||||||
self.entry_recipient = ttk.Entry(self.frame, width=40)
|
self.entry_recipient = ttk.Entry(self.frame, width=40)
|
||||||
self.entry_recipient.grid(row=2, column=1, padx=5, pady=5)
|
self.entry_recipient.grid(row=2, column=1, padx=5, pady=5)
|
||||||
|
|
||||||
# Campo para ingresar asunto
|
# Campo para el asunto del correo
|
||||||
ttk.Label(self.frame, text="Asunto:").grid(row=3, column=0, sticky="e", padx=5, pady=5)
|
ttk.Label(self.frame, text="Asunto:").grid(row=3, column=0, sticky="e", padx=5, pady=5)
|
||||||
self.entry_subject = ttk.Entry(self.frame, width=40)
|
self.entry_subject = ttk.Entry(self.frame, width=40)
|
||||||
self.entry_subject.grid(row=3, column=1, padx=5, pady=5)
|
self.entry_subject.grid(row=3, column=1, padx=5, pady=5)
|
||||||
|
|
||||||
# Campo para escribir el mensaje
|
# Área de texto para el mensaje
|
||||||
ttk.Label(self.frame, text="Mensaje:").grid(row=4, column=0, sticky="ne", padx=5, pady=5)
|
ttk.Label(self.frame, text="Mensaje:").grid(row=4, column=0, sticky="ne", padx=5, pady=5)
|
||||||
self.text_body = tk.Text(self.frame, width=50, height=10)
|
self.text_body = tk.Text(self.frame, width=50, height=10)
|
||||||
self.text_body.grid(row=4, column=1, padx=5, pady=5)
|
self.text_body.grid(row=4, column=1, padx=5, pady=5)
|
||||||
|
@ -38,11 +45,11 @@ class MailTab:
|
||||||
self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky="e")
|
self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky="e")
|
||||||
|
|
||||||
def send_email_thread(self):
|
def send_email_thread(self):
|
||||||
"""Ejecuta el envío de correo en un hilo separado para no congelar la interfaz."""
|
"""Ejecuta el envío de correo en un hilo separado para evitar bloquear la interfaz."""
|
||||||
threading.Thread(target=self.send_email).start()
|
threading.Thread(target=self.send_email).start()
|
||||||
|
|
||||||
def send_email(self):
|
def send_email(self):
|
||||||
"""Envía el correo utilizando el cliente de correo."""
|
"""Envía un correo usando el cliente de correos."""
|
||||||
sender_email = self.entry_email.get()
|
sender_email = self.entry_email.get()
|
||||||
sender_password = self.entry_password.get()
|
sender_password = self.entry_password.get()
|
||||||
recipient = self.entry_recipient.get()
|
recipient = self.entry_recipient.get()
|
||||||
|
@ -53,5 +60,6 @@ class MailTab:
|
||||||
messagebox.showerror("Error", "Todos los campos son obligatorios.")
|
messagebox.showerror("Error", "Todos los campos son obligatorios.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Llamada al cliente de correo para enviar el email
|
||||||
result = self.mail_client.send_email(sender_email, sender_password, recipient, subject, body)
|
result = self.mail_client.send_email(sender_email, sender_password, recipient, subject, body)
|
||||||
messagebox.showinfo("Resultado", result)
|
messagebox.showinfo("Resultado", result)
|
||||||
|
|
|
@ -4,11 +4,20 @@ import threading
|
||||||
|
|
||||||
class InboxTab:
|
class InboxTab:
|
||||||
def __init__(self, parent, email_client):
|
def __init__(self, parent, email_client):
|
||||||
|
"""
|
||||||
|
Inicializa la pestaña de la bandeja de entrada.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent (tk.Notebook): Contenedor de pestañas.
|
||||||
|
email_client (MailClient): Cliente de correo para gestionar la recepción y eliminación de correos.
|
||||||
|
"""
|
||||||
self.email_client = email_client
|
self.email_client = email_client
|
||||||
|
|
||||||
|
# Crear un nuevo frame en la pestaña
|
||||||
self.frame = ttk.Frame(parent)
|
self.frame = ttk.Frame(parent)
|
||||||
parent.add(self.frame, text="Bandeja de Entrada")
|
parent.add(self.frame, text="Bandeja de Entrada")
|
||||||
|
|
||||||
|
# Campos de entrada para el correo y contraseña
|
||||||
ttk.Label(self.frame, text="Correo electrónico:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
|
ttk.Label(self.frame, text="Correo electrónico:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
|
||||||
self.entry_email = ttk.Entry(self.frame, width=40)
|
self.entry_email = ttk.Entry(self.frame, width=40)
|
||||||
self.entry_email.grid(row=0, column=1, padx=5, pady=5)
|
self.entry_email.grid(row=0, column=1, padx=5, pady=5)
|
||||||
|
@ -17,25 +26,31 @@ class InboxTab:
|
||||||
self.entry_password = ttk.Entry(self.frame, show="*", width=40)
|
self.entry_password = ttk.Entry(self.frame, show="*", width=40)
|
||||||
self.entry_password.grid(row=1, column=1, padx=5, pady=5)
|
self.entry_password.grid(row=1, column=1, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Botón para cargar correos
|
||||||
self.load_button = ttk.Button(self.frame, text="Cargar Correos", command=self.load_emails_thread)
|
self.load_button = ttk.Button(self.frame, text="Cargar Correos", command=self.load_emails_thread)
|
||||||
self.load_button.grid(row=2, column=1, sticky="e", padx=5, pady=5)
|
self.load_button.grid(row=2, column=1, sticky="e", padx=5, pady=5)
|
||||||
|
|
||||||
|
# Lista para mostrar los correos
|
||||||
self.email_listbox = tk.Listbox(self.frame, width=80, height=20)
|
self.email_listbox = tk.Listbox(self.frame, width=80, height=20)
|
||||||
self.email_listbox.grid(row=3, column=0, columnspan=2, padx=5, pady=5)
|
self.email_listbox.grid(row=3, column=0, columnspan=2, padx=5, pady=5)
|
||||||
self.email_listbox.bind("<Double-Button-1>", self.show_email)
|
self.email_listbox.bind("<Double-Button-1>", self.show_email)
|
||||||
|
|
||||||
|
# Botón para eliminar un correo seleccionado
|
||||||
self.delete_button = ttk.Button(self.frame, text="Eliminar Correo", command=self.delete_email_thread)
|
self.delete_button = ttk.Button(self.frame, text="Eliminar Correo", command=self.delete_email_thread)
|
||||||
self.delete_button.grid(row=4, column=1, sticky="e", padx=5, pady=5)
|
self.delete_button.grid(row=4, column=1, sticky="e", padx=5, pady=5)
|
||||||
|
|
||||||
self.emails = []
|
self.emails = []
|
||||||
|
|
||||||
def load_emails_thread(self):
|
def load_emails_thread(self):
|
||||||
|
"""Carga los correos en un hilo separado."""
|
||||||
threading.Thread(target=self.load_emails).start()
|
threading.Thread(target=self.load_emails).start()
|
||||||
|
|
||||||
def delete_email_thread(self):
|
def delete_email_thread(self):
|
||||||
|
"""Elimina un correo en un hilo separado."""
|
||||||
threading.Thread(target=self.delete_email).start()
|
threading.Thread(target=self.delete_email).start()
|
||||||
|
|
||||||
def load_emails(self):
|
def load_emails(self):
|
||||||
|
"""Obtiene la lista de correos desde el servidor y la muestra en la lista."""
|
||||||
email_address = self.entry_email.get()
|
email_address = self.entry_email.get()
|
||||||
password = self.entry_password.get()
|
password = self.entry_password.get()
|
||||||
|
|
||||||
|
@ -46,6 +61,7 @@ class InboxTab:
|
||||||
self.emails = []
|
self.emails = []
|
||||||
self.email_listbox.delete(0, tk.END)
|
self.email_listbox.delete(0, tk.END)
|
||||||
|
|
||||||
|
# Obtener los correos usando el cliente de correos
|
||||||
emails = self.email_client.fetch_emails(email_address, password)
|
emails = self.email_client.fetch_emails(email_address, password)
|
||||||
|
|
||||||
if isinstance(emails, str):
|
if isinstance(emails, str):
|
||||||
|
@ -57,6 +73,7 @@ class InboxTab:
|
||||||
self.emails.append((email_id, subject, sender))
|
self.emails.append((email_id, subject, sender))
|
||||||
|
|
||||||
def delete_email(self):
|
def delete_email(self):
|
||||||
|
"""Elimina un correo seleccionado en la lista."""
|
||||||
selected_index = self.email_listbox.curselection()
|
selected_index = self.email_listbox.curselection()
|
||||||
if not selected_index:
|
if not selected_index:
|
||||||
messagebox.showwarning("Advertencia", "Seleccione un correo para eliminar.")
|
messagebox.showwarning("Advertencia", "Seleccione un correo para eliminar.")
|
||||||
|
@ -66,6 +83,7 @@ class InboxTab:
|
||||||
email_address = self.entry_email.get()
|
email_address = self.entry_email.get()
|
||||||
password = self.entry_password.get()
|
password = self.entry_password.get()
|
||||||
|
|
||||||
|
# Eliminar el correo usando el cliente
|
||||||
result = self.email_client.delete_email(email_address, password, email_id)
|
result = self.email_client.delete_email(email_address, password, email_id)
|
||||||
|
|
||||||
if "eliminado" in result:
|
if "eliminado" in result:
|
||||||
|
@ -75,6 +93,7 @@ class InboxTab:
|
||||||
messagebox.showerror("Error", result)
|
messagebox.showerror("Error", result)
|
||||||
|
|
||||||
def show_email(self, event):
|
def show_email(self, event):
|
||||||
|
"""Muestra el contenido del correo seleccionado."""
|
||||||
selected_index = self.email_listbox.curselection()
|
selected_index = self.email_listbox.curselection()
|
||||||
if selected_index:
|
if selected_index:
|
||||||
email_id, subject, sender = self.emails[selected_index[0]]
|
email_id, subject, sender = self.emails[selected_index[0]]
|
||||||
|
|
|
@ -6,8 +6,16 @@ from email.mime.text import MIMEText
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.utils import formatdate
|
from email.utils import formatdate
|
||||||
|
|
||||||
|
|
||||||
class MailClient:
|
class MailClient:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inicializa el cliente de correo con la configuración del servidor.
|
||||||
|
|
||||||
|
Atributos:
|
||||||
|
server_ip (str): Dirección del servidor de correo.
|
||||||
|
ports (dict): Diccionario con los puertos para SMTP e IMAP.
|
||||||
|
"""
|
||||||
self.server_ip = "s1.ieslamar.org"
|
self.server_ip = "s1.ieslamar.org"
|
||||||
self.ports = {
|
self.ports = {
|
||||||
"SMTP": 25, # SMTP sin seguridad
|
"SMTP": 25, # SMTP sin seguridad
|
||||||
|
@ -16,7 +24,15 @@ class MailClient:
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_smtp_connection(self, port):
|
def check_smtp_connection(self, port):
|
||||||
"""Verifica si el servidor SMTP responde en el puerto especificado."""
|
"""
|
||||||
|
Verifica si el servidor SMTP responde en un puerto específico.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port (int): Número del puerto a verificar.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: `True` si la conexión es exitosa, `False` en caso contrario.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
with socket.create_connection((self.server_ip, port), timeout=15):
|
with socket.create_connection((self.server_ip, port), timeout=15):
|
||||||
return True
|
return True
|
||||||
|
@ -24,8 +40,21 @@ class MailClient:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def send_email(self, sender_email, sender_password, recipient, subject, body):
|
def send_email(self, sender_email, sender_password, recipient, subject, body):
|
||||||
"""Envía un correo utilizando SMTP en los puertos 587 o 25."""
|
"""
|
||||||
|
Envía un correo utilizando el protocolo SMTP.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sender_email (str): Dirección de correo del remitente.
|
||||||
|
sender_password (str): Contraseña del remitente.
|
||||||
|
recipient (str): Dirección de correo del destinatario.
|
||||||
|
subject (str): Asunto del correo.
|
||||||
|
body (str): Cuerpo del correo.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Mensaje indicando el resultado del envío.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Crear el mensaje de correo
|
||||||
message = MIMEMultipart()
|
message = MIMEMultipart()
|
||||||
message["From"] = sender_email
|
message["From"] = sender_email
|
||||||
message["To"] = recipient
|
message["To"] = recipient
|
||||||
|
@ -33,23 +62,27 @@ class MailClient:
|
||||||
message["Subject"] = subject
|
message["Subject"] = subject
|
||||||
message.attach(MIMEText(body, "plain", "utf-8"))
|
message.attach(MIMEText(body, "plain", "utf-8"))
|
||||||
|
|
||||||
smtp_ports = [587, 25] # Intenta primero 587, luego 25
|
# Lista de puertos disponibles para SMTP
|
||||||
|
smtp_ports = [587, 25] # Prioriza 587 y luego 25
|
||||||
puerto_activo = None
|
puerto_activo = None
|
||||||
|
|
||||||
|
# Verificar qué puerto está disponible
|
||||||
for smtp_port in smtp_ports:
|
for smtp_port in smtp_ports:
|
||||||
if self.check_smtp_connection(smtp_port):
|
if self.check_smtp_connection(smtp_port):
|
||||||
puerto_activo = smtp_port
|
puerto_activo = smtp_port
|
||||||
break # Si encuentra un puerto disponible, lo usa
|
break
|
||||||
|
|
||||||
if not puerto_activo:
|
if not puerto_activo:
|
||||||
return "Error: No se pudo conectar con el servidor SMTP en los puertos (587, 25)."
|
return "Error: No se pudo conectar con el servidor SMTP en los puertos (587, 25)."
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Conectar al servidor SMTP
|
||||||
with smtplib.SMTP(self.server_ip, puerto_activo, timeout=15) as server:
|
with smtplib.SMTP(self.server_ip, puerto_activo, timeout=15) as server:
|
||||||
if puerto_activo == 587:
|
if puerto_activo == 587:
|
||||||
server.starttls() # Activa TLS si está en el puerto 587
|
server.starttls() # Activar TLS si se usa el puerto 587
|
||||||
server.login(sender_email, sender_password)
|
server.login(sender_email, sender_password) # Autenticación
|
||||||
server.sendmail(sender_email, recipient, message.as_string())
|
server.sendmail(sender_email, recipient, message.as_string()) # Enviar el correo
|
||||||
|
|
||||||
return f"Correo enviado correctamente a {recipient} usando el puerto {puerto_activo}"
|
return f"Correo enviado correctamente a {recipient} usando el puerto {puerto_activo}"
|
||||||
|
|
||||||
except smtplib.SMTPAuthenticationError:
|
except smtplib.SMTPAuthenticationError:
|
||||||
|
@ -63,46 +96,76 @@ class MailClient:
|
||||||
return f"Error inesperado al enviar el correo: {str(e)}"
|
return f"Error inesperado al enviar el correo: {str(e)}"
|
||||||
|
|
||||||
def fetch_emails(self, email_address, password):
|
def fetch_emails(self, email_address, password):
|
||||||
"""Recibe correos utilizando IMAP sin SSL."""
|
"""
|
||||||
|
Recupera los correos electrónicos de la bandeja de entrada usando IMAP.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
email_address (str): Dirección de correo del usuario.
|
||||||
|
password (str): Contraseña del usuario.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: Lista de tuplas con los correos (`email_id`, `subject`, `sender`).
|
||||||
|
str: Mensaje de error si la autenticación falla.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Conectar al servidor IMAP
|
||||||
mail = imaplib.IMAP4(self.server_ip, self.ports["IMAP"])
|
mail = imaplib.IMAP4(self.server_ip, self.ports["IMAP"])
|
||||||
mail.login(email_address, password)
|
mail.login(email_address, password)
|
||||||
mail.select("inbox")
|
mail.select("inbox")
|
||||||
|
|
||||||
|
# Buscar todos los correos en la bandeja de entrada
|
||||||
status, messages = mail.search(None, "ALL")
|
status, messages = mail.search(None, "ALL")
|
||||||
email_ids = messages[0].split()
|
email_ids = messages[0].split()
|
||||||
emails = []
|
emails = []
|
||||||
|
|
||||||
for email_id in email_ids[-10:]: # Obtener los últimos 10 correos
|
# Obtener los últimos 10 correos
|
||||||
|
for email_id in email_ids[-10:]:
|
||||||
status, msg_data = mail.fetch(email_id, "(RFC822)")
|
status, msg_data = mail.fetch(email_id, "(RFC822)")
|
||||||
for response_part in msg_data:
|
for response_part in msg_data:
|
||||||
if isinstance(response_part, tuple):
|
if isinstance(response_part, tuple):
|
||||||
msg = email.message_from_bytes(response_part[1])
|
msg = email.message_from_bytes(response_part[1])
|
||||||
|
|
||||||
|
# Decodificar el asunto del correo
|
||||||
subject, encoding = email.header.decode_header(msg["Subject"])[0]
|
subject, encoding = email.header.decode_header(msg["Subject"])[0]
|
||||||
if isinstance(subject, bytes):
|
if isinstance(subject, bytes):
|
||||||
subject = subject.decode(encoding or "utf-8")
|
subject = subject.decode(encoding or "utf-8")
|
||||||
|
|
||||||
sender = msg.get("From")
|
sender = msg.get("From")
|
||||||
emails.append((email_id, subject, sender))
|
emails.append((email_id, subject, sender))
|
||||||
|
|
||||||
mail.logout()
|
mail.logout()
|
||||||
return emails
|
return emails
|
||||||
|
|
||||||
except imaplib.IMAP4.error:
|
except imaplib.IMAP4.error:
|
||||||
return "Error: Fallo de autenticación en IMAP. Verifica tu correo y contraseña."
|
return "Error: Fallo de autenticación en IMAP. Verifica tu correo y contraseña."
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Error al recibir los correos: {str(e)}"
|
return f"Error al recibir los correos: {str(e)}"
|
||||||
|
|
||||||
def delete_email(self, email_address, password, email_id):
|
def delete_email(self, email_address, password, email_id):
|
||||||
"""Elimina un correo utilizando IMAP sin SSL."""
|
"""
|
||||||
|
Elimina un correo electrónico utilizando IMAP.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
email_address (str): Dirección de correo del usuario.
|
||||||
|
password (str): Contraseña del usuario.
|
||||||
|
email_id (bytes): ID del correo a eliminar.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Mensaje indicando si la eliminación fue exitosa o no.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Conectar al servidor IMAP
|
||||||
mail = imaplib.IMAP4(self.server_ip, self.ports["IMAP"])
|
mail = imaplib.IMAP4(self.server_ip, self.ports["IMAP"])
|
||||||
mail.login(email_address, password)
|
mail.login(email_address, password)
|
||||||
mail.select("inbox")
|
mail.select("inbox")
|
||||||
|
|
||||||
mail.store(email_id, "+FLAGS", "\\Deleted") # Marca el correo como eliminado
|
# Marcar el correo como eliminado
|
||||||
mail.expunge() # Borra permanentemente los correos marcados como eliminados
|
mail.store(email_id, "+FLAGS", "\\Deleted")
|
||||||
|
mail.expunge() # Eliminar definitivamente los correos marcados
|
||||||
mail.logout()
|
mail.logout()
|
||||||
|
|
||||||
return f"Correo con ID {email_id} eliminado correctamente."
|
return f"Correo con ID {email_id} eliminado correctamente."
|
||||||
|
|
||||||
except imaplib.IMAP4.error:
|
except imaplib.IMAP4.error:
|
||||||
return "Error: Fallo de autenticación en IMAP. Verifica tu correo y contraseña."
|
return "Error: Fallo de autenticación en IMAP. Verifica tu correo y contraseña."
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
Loading…
Reference in New Issue