# models/mensaje_email.py import re import hashlib from datetime import datetime from email.utils import parseaddr, parsedate_to_datetime class MensajeEmail: def __init__(self, remitente, fecha, contenido, subject=None, adjuntos=None): self.remitente = self._estandarizar_remitente(remitente) self.fecha = self._estandarizar_fecha(fecha) self.subject = subject if subject else "Sin Asunto" self.contenido = self._limpiar_contenido(contenido) self.adjuntos = adjuntos if adjuntos else [] self.hash = self._generar_hash() def _formatear_subject_para_link(self, subject): """ Formatea el subject para usarlo como ancla en links de Obsidian Remueve caracteres especiales y espacios múltiples """ if not subject: return "Sin-Asunto" # Eliminar caracteres especiales y reemplazar espacios con guiones formatted = re.sub(r"[^\w\s-]", "", subject) formatted = re.sub(r"\s+", "-", formatted.strip()) return formatted def _limpiar_contenido(self, contenido): if not contenido: return "" # Eliminar líneas de metadatos lines = contenido.split("\n") cleaned_lines = [] for line in lines: # Skip metadata lines if line.strip().startswith( ("Da: ", "Inviato: ", "A: ", "From: ", "Sent: ", "To: ") ) or line.strip().startswith("Oggetto: "): continue # Limpiar espacios múltiples dentro de cada línea, pero mantener la línea completa cleaned_line = re.sub(r" +", " ", line) cleaned_lines.append(cleaned_line) # Unir las líneas preservando los saltos de línea text = "\n".join(cleaned_lines) # Limpiar la combinación específica de CRLF+NBSP+CRLF text = re.sub(r"\r?\n\xa0\r?\n", "\n", text) # Reemplazar CRLF por LF text = text.replace("\r\n", "\n") # Reemplazar CR por LF text = text.replace("\r", "\n") # Reemplazar 3 o más saltos de línea por dos text = re.sub(r"\n{3,}", "\n\n", text) # Eliminar espacios al inicio y final del texto completo return text.strip() def to_markdown(self): # Hash con caracteres no título hash_line = f"+ {self.hash}\n\n" # Subject como título subject_line = f"### {self.subject if self.subject else 'Sin Asunto'}\n\n" # Fecha en formato legible fecha_formato = self.fecha.strftime("%d-%m-%Y") fecha_line = f"- {fecha_formato}\n\n" # Contenido del mensaje md = f"{hash_line}{subject_line}{fecha_line}" md += self.contenido + "\n\n" # Adjuntos si existen if self.adjuntos: md += "### Adjuntos\n" for adj in self.adjuntos: md += f"- [[{adj}]]\n" md += "---\n\n" return md def get_index_entry(self): """ Genera una entrada de lista para el índice """ fecha_formato = self.fecha.strftime("%d-%m-%Y") subject_link = self._formatear_subject_para_link(self.subject) return f"- {fecha_formato} - {self.remitente} - [[cronologia#{self.subject}|{subject_link}]]" def _estandarizar_remitente(self, remitente): if "Da:" in remitente: remitente = remitente.split("Da:")[1].split("Inviato:")[0] elif "From:" in remitente: remitente = remitente.split("From:")[1].split("Sent:")[0] nombre, email = parseaddr(remitente) if not nombre and email: nombre = email.split("@")[0] elif not nombre and not email: nombre_match = re.search(r"([A-Za-z\s]+)\s*<", remitente) if nombre_match: nombre = nombre_match.group(1) else: return "Remitente Desconocido" nombre = re.sub(r'[<>:"/\\|?*]', "", nombre.strip()) nombre = nombre.encode("ascii", "ignore").decode("ascii") return nombre def _estandarizar_fecha(self, fecha): if isinstance(fecha, str): try: return parsedate_to_datetime(fecha) except: return datetime.now() return fecha def _generar_hash(self): """ Genera un hash único para el mensaje basado en una combinación de campos que identifican únicamente el mensaje """ # Limpiar y normalizar el contenido para el hash # Para el hash, sí normalizamos completamente los espacios contenido_hash = re.sub(r"\s+", " ", self.contenido).strip() # Normalizar el subject subject_normalizado = re.sub( r"\s+", " ", self.subject if self.subject else "" ).strip() # Crear una cadena con los elementos clave del mensaje elementos_hash = [ self.remitente.strip(), self.fecha.strftime( "%Y%m%d%H%M" ), # Solo hasta minutos para permitir pequeñas variaciones subject_normalizado, contenido_hash[ :500 ], # Usar solo los primeros 500 caracteres del contenido normalizado ] # Unir todos los elementos con un separador único texto_hash = "|".join(elementos_hash) # Mostrar información de debug para el hash (solo si está habilitado) if hasattr(self, "_debug_hash") and self._debug_hash: print(f" 🔍 Debug Hash:") print(f" - Remitente: '{self.remitente.strip()}'") print(f" - Fecha: '{self.fecha.strftime('%Y%m%d%H%M')}'") print(f" - Subject: '{subject_normalizado}'") print(f" - Contenido (500 chars): '{contenido_hash[:500]}'") print(f" - Texto completo hash: '{texto_hash[:100]}...'") # Generar el hash hash_resultado = hashlib.md5(texto_hash.encode()).hexdigest() return hash_resultado def debug_hash_info(self): """ Muestra información detallada sobre cómo se genera el hash de este mensaje """ self._debug_hash = True hash_result = self._generar_hash() delattr(self, "_debug_hash") return hash_result