Proyecto
This commit is contained in:
parent
c80721e33e
commit
c6bfeaeca1
14
app/main.py
14
app/main.py
|
@ -5,6 +5,7 @@ import datetime
|
|||
from tkinter import Menu # Importar el widget Menu
|
||||
from tkinter import ttk # Importar el widget ttk
|
||||
from correo_server.EmailTab import MailTab
|
||||
from correo_server.InboxTab import InboxTab
|
||||
from correo_server.MailClient import MailClient
|
||||
from hilos.ChatWidget import ChatWidget
|
||||
from hilos.MusicPlayer import MusicPlayer
|
||||
|
@ -19,16 +20,12 @@ from solapas.TicTacToe import TicTacToe
|
|||
from solapas.WebScraperToDB import WebScraperToDB
|
||||
|
||||
|
||||
# Configuración del servidor de correos
|
||||
SMTP_SERVER = "192.168.120.103"
|
||||
SMTP_PORT = 587 # Usar SSL para mayor seguridad
|
||||
# Crear instancia del cliente de correo con configuración de puertos
|
||||
email_client = MailClient()
|
||||
|
||||
# Clave de API de OpenWeatherMap
|
||||
API_KEY = "1fa8fd05b650773bbc3f2130657e808a"
|
||||
|
||||
# Crear cliente de correo
|
||||
mail_client = MailClient(SMTP_SERVER, SMTP_PORT)
|
||||
|
||||
def update_time(status_bar):
|
||||
"""Función que actualiza la hora y el día de la semana en un label"""
|
||||
while True:
|
||||
|
@ -184,8 +181,9 @@ notebook.add(tab5, text="Web Scraper", padding=4)
|
|||
# Añadir el widget de Web Scraper a la Solapa 5
|
||||
web_scraper = WebScraperToDB(tab5)
|
||||
|
||||
# Crear e inicializar la pestaña de correos
|
||||
email_tab = MailTab(notebook, mail_client)
|
||||
# Crear pestañas de correo
|
||||
MailTab(notebook, email_client)
|
||||
InboxTab(notebook, email_client)
|
||||
|
||||
# Barra de estado
|
||||
# Dividir la barra de estado en 4 labels
|
||||
|
|
|
@ -6,11 +6,10 @@ class MailTab:
|
|||
def __init__(self, parent, mail_client):
|
||||
self.mail_client = mail_client
|
||||
|
||||
# Crear el frame de la pestaña
|
||||
self.frame = ttk.Frame(parent)
|
||||
parent.add(self.frame, text="Correo")
|
||||
|
||||
# Campos de entrada para enviar correo
|
||||
# Campos para ingresar credenciales del usuario
|
||||
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.grid(row=0, column=1, padx=5, pady=5)
|
||||
|
@ -19,30 +18,40 @@ class MailTab:
|
|||
self.entry_password = ttk.Entry(self.frame, show="*", width=40)
|
||||
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)
|
||||
self.recipient_entry = ttk.Entry(self.frame, width=40)
|
||||
self.recipient_entry.grid(row=2, column=1, padx=5, pady=5)
|
||||
self.entry_recipient = ttk.Entry(self.frame, width=40)
|
||||
self.entry_recipient.grid(row=2, column=1, padx=5, pady=5)
|
||||
|
||||
# Campo para ingresar asunto
|
||||
ttk.Label(self.frame, text="Asunto:").grid(row=3, column=0, sticky="e", padx=5, pady=5)
|
||||
self.subject_entry = ttk.Entry(self.frame, width=40)
|
||||
self.subject_entry.grid(row=3, column=1, padx=5, pady=5)
|
||||
self.entry_subject = ttk.Entry(self.frame, width=40)
|
||||
self.entry_subject.grid(row=3, column=1, padx=5, pady=5)
|
||||
|
||||
# Campo para escribir el mensaje
|
||||
ttk.Label(self.frame, text="Mensaje:").grid(row=4, column=0, sticky="ne", padx=5, pady=5)
|
||||
self.body_text = tk.Text(self.frame, width=50, height=10)
|
||||
self.body_text.grid(row=4, column=1, padx=5, pady=5)
|
||||
self.text_body = tk.Text(self.frame, width=50, height=10)
|
||||
self.text_body.grid(row=4, column=1, padx=5, pady=5)
|
||||
|
||||
# Botón para enviar el correo
|
||||
self.send_button = ttk.Button(self.frame, text="Enviar", command=self.send_email_thread)
|
||||
self.send_button.grid(row=5, column=1, padx=5, pady=5, sticky="e")
|
||||
|
||||
def send_email_thread(self):
|
||||
"""Ejecuta el envío de correo en un hilo separado para no congelar la interfaz."""
|
||||
threading.Thread(target=self.send_email).start()
|
||||
|
||||
def send_email(self):
|
||||
"""Envía el correo utilizando el cliente de correo."""
|
||||
sender_email = self.entry_email.get()
|
||||
sender_password = self.entry_password.get()
|
||||
recipient = self.recipient_entry.get()
|
||||
subject = self.subject_entry.get()
|
||||
body = self.body_text.get("1.0", tk.END).strip()
|
||||
recipient = self.entry_recipient.get()
|
||||
subject = self.entry_subject.get()
|
||||
body = self.text_body.get("1.0", tk.END).strip()
|
||||
|
||||
if not sender_email or not sender_password or not recipient or not subject or not body:
|
||||
messagebox.showerror("Error", "Todos los campos son obligatorios.")
|
||||
return
|
||||
|
||||
result = self.mail_client.send_email(sender_email, sender_password, recipient, subject, body)
|
||||
messagebox.showinfo("Resultado", result)
|
||||
|
|
|
@ -1,17 +1,109 @@
|
|||
import smtplib
|
||||
import socket
|
||||
import imaplib
|
||||
import email
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.utils import formatdate
|
||||
|
||||
class MailClient:
|
||||
def __init__(self, smtp_server, smtp_port):
|
||||
self.smtp_server = smtp_server
|
||||
self.smtp_port = smtp_port
|
||||
def __init__(self):
|
||||
self.server_ip = "s1.ieslamar.org"
|
||||
self.ports = {
|
||||
"SMTP": 25, # SMTP sin seguridad
|
||||
"SMTP-SUBMISSION": 587, # SMTP autenticado sin SSL
|
||||
"IMAP": 143 # IMAP sin SSL
|
||||
}
|
||||
|
||||
def check_smtp_connection(self, port):
|
||||
"""Verifica si el servidor SMTP responde en el puerto especificado."""
|
||||
try:
|
||||
with socket.create_connection((self.server_ip, port), timeout=15):
|
||||
return True
|
||||
except (socket.timeout, ConnectionRefusedError):
|
||||
return False
|
||||
|
||||
def send_email(self, sender_email, sender_password, recipient, subject, body):
|
||||
"""Envía un correo utilizando el servidor SMTP con SSL."""
|
||||
"""Envía un correo utilizando SMTP en los puertos 587 o 25."""
|
||||
try:
|
||||
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
|
||||
message = MIMEMultipart()
|
||||
message["From"] = sender_email
|
||||
message["To"] = recipient
|
||||
message["Date"] = formatdate(localtime=True)
|
||||
message["Subject"] = subject
|
||||
message.attach(MIMEText(body, "plain", "utf-8"))
|
||||
|
||||
smtp_ports = [587, 25] # Intenta primero 587, luego 25
|
||||
puerto_activo = None
|
||||
|
||||
for smtp_port in smtp_ports:
|
||||
if self.check_smtp_connection(smtp_port):
|
||||
puerto_activo = smtp_port
|
||||
break # Si encuentra un puerto disponible, lo usa
|
||||
|
||||
if not puerto_activo:
|
||||
return "Error: No se pudo conectar con el servidor SMTP en los puertos (587, 25)."
|
||||
|
||||
try:
|
||||
with smtplib.SMTP(self.server_ip, puerto_activo, timeout=15) as server:
|
||||
if puerto_activo == 587:
|
||||
server.starttls() # Activa TLS si está en el puerto 587
|
||||
server.login(sender_email, sender_password)
|
||||
message = f"Subject: {subject}\n\n{body}"
|
||||
server.sendmail(sender_email, recipient, message)
|
||||
return "Correo enviado correctamente"
|
||||
server.sendmail(sender_email, recipient, message.as_string())
|
||||
return f"Correo enviado correctamente a {recipient} usando el puerto {puerto_activo}"
|
||||
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
return "Error: Autenticación fallida. Verifica tu correo y contraseña."
|
||||
except smtplib.SMTPConnectError:
|
||||
return f"Error: No se pudo conectar al servidor en el puerto {puerto_activo}."
|
||||
except smtplib.SMTPException as e:
|
||||
return f"Error SMTP en el puerto {puerto_activo}: {str(e)}"
|
||||
|
||||
except Exception as e:
|
||||
return f"Error al enviar el correo: {str(e)}"
|
||||
return f"Error inesperado al enviar el correo: {str(e)}"
|
||||
|
||||
def fetch_emails(self, email_address, password):
|
||||
"""Recibe correos utilizando IMAP sin SSL."""
|
||||
try:
|
||||
mail = imaplib.IMAP4(self.server_ip, self.ports["IMAP"])
|
||||
mail.login(email_address, password)
|
||||
mail.select("inbox")
|
||||
|
||||
status, messages = mail.search(None, "ALL")
|
||||
email_ids = messages[0].split()
|
||||
emails = []
|
||||
|
||||
for email_id in email_ids[-10:]: # Obtener los últimos 10 correos
|
||||
status, msg_data = mail.fetch(email_id, "(RFC822)")
|
||||
for response_part in msg_data:
|
||||
if isinstance(response_part, tuple):
|
||||
msg = email.message_from_bytes(response_part[1])
|
||||
subject, encoding = email.header.decode_header(msg["Subject"])[0]
|
||||
if isinstance(subject, bytes):
|
||||
subject = subject.decode(encoding or "utf-8")
|
||||
sender = msg.get("From")
|
||||
emails.append((email_id, subject, sender))
|
||||
|
||||
mail.logout()
|
||||
return emails
|
||||
except imaplib.IMAP4.error:
|
||||
return "Error: Fallo de autenticación en IMAP. Verifica tu correo y contraseña."
|
||||
except Exception as e:
|
||||
return f"Error al recibir los correos: {str(e)}"
|
||||
|
||||
def delete_email(self, email_address, password, email_id):
|
||||
"""Elimina un correo utilizando IMAP sin SSL."""
|
||||
try:
|
||||
mail = imaplib.IMAP4(self.server_ip, self.ports["IMAP"])
|
||||
mail.login(email_address, password)
|
||||
mail.select("inbox")
|
||||
|
||||
mail.store(email_id, "+FLAGS", "\\Deleted") # Marca el correo como eliminado
|
||||
mail.expunge() # Borra permanentemente los correos marcados como eliminados
|
||||
mail.logout()
|
||||
|
||||
return f"Correo con ID {email_id} eliminado correctamente."
|
||||
except imaplib.IMAP4.error:
|
||||
return "Error: Fallo de autenticación en IMAP. Verifica tu correo y contraseña."
|
||||
except Exception as e:
|
||||
return f"Error al eliminar el correo: {str(e)}"
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
import threading
|
||||
import pygame # Necesitas instalar pygame: pip install pygame
|
||||
import pygame
|
||||
|
||||
# Definir la carpeta donde se guardarán los archivos descargados
|
||||
DOWNLOAD_FOLDER = "musicplayer"
|
||||
|
||||
class MusicPlayer:
|
||||
def __init__(self, parent):
|
||||
|
@ -22,19 +25,24 @@ class MusicPlayer:
|
|||
)
|
||||
self.title_label.pack(pady=5)
|
||||
|
||||
# Botón para seleccionar archivo
|
||||
self.select_button = tk.Button(
|
||||
self.frame, text="Seleccionar Archivo", command=self.select_file, width=20
|
||||
)
|
||||
self.select_button.pack(pady=5)
|
||||
# Lista de canciones descargadas
|
||||
self.song_listbox = tk.Listbox(self.frame, width=50, height=10)
|
||||
self.song_listbox.pack(pady=5)
|
||||
self.load_songs()
|
||||
|
||||
# Crear un marco para los botones de control
|
||||
self.controls_frame = tk.Frame(self.frame, bg="lightgreen")
|
||||
self.controls_frame.pack(pady=10)
|
||||
|
||||
# Botones de control (centrados)
|
||||
# Botón para seleccionar un archivo manualmente
|
||||
self.select_button = tk.Button(
|
||||
self.frame, text="Seleccionar Archivo", command=self.select_file, width=20
|
||||
)
|
||||
self.select_button.pack(pady=5)
|
||||
|
||||
# Botones de control
|
||||
self.play_button = tk.Button(
|
||||
self.controls_frame, text="▶ Reproducir", command=self.play_music, width=12
|
||||
self.controls_frame, text="▶ Reproducir", command=self.play_selected_music, width=12
|
||||
)
|
||||
self.play_button.grid(row=0, column=0, padx=5)
|
||||
|
||||
|
@ -43,17 +51,34 @@ class MusicPlayer:
|
|||
)
|
||||
self.stop_button.grid(row=0, column=1, padx=5)
|
||||
|
||||
def load_songs(self):
|
||||
"""Carga la lista de canciones descargadas en la carpeta 'musicplayer/'."""
|
||||
if not os.path.exists(DOWNLOAD_FOLDER):
|
||||
os.makedirs(DOWNLOAD_FOLDER)
|
||||
|
||||
self.song_listbox.delete(0, tk.END) # Limpiar lista antes de recargar
|
||||
|
||||
for file in os.listdir(DOWNLOAD_FOLDER):
|
||||
if file.endswith(".mp3"):
|
||||
self.song_listbox.insert(tk.END, file)
|
||||
|
||||
def select_file(self):
|
||||
"""Abrir el selector de archivos para elegir un archivo de música."""
|
||||
"""Abrir el selector de archivos para elegir un archivo de música manualmente."""
|
||||
self.music_file = filedialog.askopenfilename(
|
||||
filetypes=[("Archivos de audio", "*.mp3 *.wav"), ("Todos los archivos", "*.*")]
|
||||
)
|
||||
if self.music_file:
|
||||
self.title_label.config(text=f"Archivo: {self.music_file.split('/')[-1]}")
|
||||
self.title_label.config(text=f"Archivo: {os.path.basename(self.music_file)}")
|
||||
|
||||
def play_selected_music(self):
|
||||
"""Reproducir la canción seleccionada desde la lista de descargas."""
|
||||
selected_index = self.song_listbox.curselection()
|
||||
if not selected_index:
|
||||
return
|
||||
|
||||
selected_song = self.song_listbox.get(selected_index)
|
||||
self.music_file = os.path.join(DOWNLOAD_FOLDER, selected_song)
|
||||
|
||||
def play_music(self):
|
||||
"""Iniciar la reproducción de música."""
|
||||
if hasattr(self, "music_file"):
|
||||
self.is_playing = True
|
||||
self.play_button.config(state="disabled")
|
||||
self.stop_button.config(state="normal")
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import threading
|
||||
from pytube import YouTube
|
||||
import yt_dlp
|
||||
|
||||
# Definir la carpeta donde se guardarán los archivos descargados
|
||||
DOWNLOAD_FOLDER = "musicplayer"
|
||||
|
||||
class MusicDownloader:
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Inicializa la interfaz para descargar música de YouTube en MP3.
|
||||
|
||||
Args:
|
||||
parent (tk.Frame): Frame donde se colocará el downloader.
|
||||
"""
|
||||
self.parent = parent
|
||||
|
||||
# Crear carpeta de descargas si no existe
|
||||
if not os.path.exists(DOWNLOAD_FOLDER):
|
||||
os.makedirs(DOWNLOAD_FOLDER)
|
||||
|
||||
# Etiqueta de título
|
||||
title = tk.Label(self.parent, text="Descargar Música MP3", font=("Helvetica", 14, "bold"))
|
||||
title.pack(pady=10)
|
||||
|
@ -46,20 +50,27 @@ class MusicDownloader:
|
|||
threading.Thread(target=self.download_music, args=(url,), daemon=True).start()
|
||||
|
||||
def download_music(self, url):
|
||||
"""Descarga el audio de YouTube como MP3."""
|
||||
"""Descarga el audio de YouTube en MP3 usando yt-dlp."""
|
||||
try:
|
||||
# Inicializa la descarga
|
||||
yt = YouTube(url, on_progress_callback=self.update_progress)
|
||||
stream = yt.streams.filter(only_audio=True).first()
|
||||
self.status_label.config(text="Descargando...", fg="blue")
|
||||
output_template = os.path.join(DOWNLOAD_FOLDER, "%(title)s.%(ext)s")
|
||||
|
||||
ydl_opts = {
|
||||
'format': 'bestaudio/best',
|
||||
'outtmpl': output_template,
|
||||
'postprocessors': [{
|
||||
'key': 'FFmpegExtractAudio',
|
||||
'preferredcodec': 'mp3',
|
||||
'preferredquality': '192',
|
||||
}],
|
||||
}
|
||||
|
||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||
ydl.download([url])
|
||||
|
||||
# Descargar archivo
|
||||
self.status_label.config(text="Descargando...")
|
||||
self.progress["value"] = 0
|
||||
stream.download(filename=f"{yt.title}.mp3")
|
||||
self.status_label.config(text="¡Descarga completada!", fg="green")
|
||||
except Exception as e:
|
||||
self.status_label.config(text=f"Error: {str(e)}", fg="red")
|
||||
|
||||
def update_progress(self, stream, chunk, bytes_remaining):
|
||||
"""Actualiza la barra de progreso durante la descarga."""
|
||||
total_size = stream.filesize
|
||||
|
|
Loading…
Reference in New Issue