# 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, manteniendo # 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#" f"{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: patron_nombre = r"([A-Za-z\s]+)\s*<" nombre_match = re.search(patron_nombre, 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 Exception: 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 (normaliza 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(), # Solo hasta minutos para permitir pequeñas variaciones self.fecha.strftime("%Y%m%d%H%M"), subject_normalizado, # Usar solo los primeros 500 caracteres del contenido normalizado contenido_hash[:500], ] # 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(" 🔍 Debug Hash:") print(" - Remitente: '" + self.remitente.strip() + "'") print(" - Fecha: '" + self.fecha.strftime("%Y%m%d%H%M") + "'") print(" - Subject: '" + subject_normalizado + "'") preview = contenido_hash[:500] print(" - Contenido (500 chars): '" + preview + "'") print(" - 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 de 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