¿Alguna vez has explicado un proceso completo por WhatsApp enviando capturas de pantalla, notas de voz y mensajes de texto… y pensaste: “Ojalá pudiera convertir esto en un tutorial sin tener que redactarlo todo desde cero”? 🤔💡

Cuando exportas un chat con archivos multimedia, WhatsApp te entrega un archivo .zip desordenado: un archivo .txt por un lado, carpetas llenas de fotos sueltas por otro, y audios en formato .opus que son imposibles de leer en un documento.

¡Hoy vamos a solucionar eso! 🚀 Veremos cómo transformar ese .zip en un PDF interactivo y visual con:

  • 📸 Imágenes incrustadas en el lugar exacto donde se enviaron.
  • 🎙️ Audios transcritos automáticamente a texto usando Inteligencia Artificial local.
  • 📄 Diseño limpio estilo WhatsApp listo para que una IA (como Gemini o ChatGPT) lo lea y redacte un tutorial profesional en segundos.

🛠️ ¿Qué necesitamos?

Solo necesitas tu computadora (preferiblemente con Linux o terminal) y unas pocas herramientas gratuitas:

  1. Python 3 instalado.
  2. FFmpeg (para procesar los archivos de audio).
  3. faster-whisper (un modelo de IA local ultrarrápido que convierte voz a texto sin enviar tus audios a internet).

🚀 Paso 1: Preparar el entorno

Para evitar conflictos de librerías en sistemas modernos, abrimos la terminal y preparamos nuestro entorno virtual en un par de comandos:

Bash

# 1. Instalar herramientas base
sudo apt update && sudo apt install -y ffmpeg python3-pip python3-venv

# 2. Crear y activar un entorno virtual
python3 -m venv env_whatsapp
source env_whatsapp/bin/activate

# 3. Instalar la IA de transcripción
pip install faster-whisper

🔒 Dato clave de privacidad: faster-whisper corre 100% en tu procesador. Tus conversaciones privadas nunca salen de tu máquina.

🐍 Paso 2: El script mágico

Creamos un archivo llamado exportar_chat_completo.py y pegamos un script que haga tres tareas automáticas:

  1. Leer el archivo .txt y armar las burbujas de diálogo.
  2. Detectar las imágenes .jpg o .png y convertirlas a Base64 para que queden incrustadas directamente dentro del HTML.
  3. Detectar las notas de voz .opus, transcribirlas con Whisper e insertar el texto dentro de la conversación.

Código del script:

Python

#!/usr/bin/env python3
import sys, os, re, zipfile, base64, html, tempfile
from faster_whisper import WhisperModel

if len(sys.argv) < 2:
    print("Uso: python exportar_chat_completo.py <archivo.zip>")
    sys.exit(1)

zip_path = sys.argv[1]
output_html = "chat_completo.html"

print("🧠 Cargando modelo de transcripción...")
whisper_model = WhisperModel("base", device="cpu", compute_type="int8")

with zipfile.ZipFile(zip_path, 'r') as z:
    file_list = z.namelist()
    txt_name = [f for f in file_list if f.endswith('.txt')][0]
    chat_content = z.read(txt_name).decode('utf-8-sig', errors='replace')
    
    # Regex para extraer mensajes
    msg_pattern = re.compile(r"^\[?(\d{1,2}/\d{1,2}/\d{2,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s+[ap]\.?\s?m\.?)?)\]?\s*(?:-\s*)?([^:]+):\s*(.*)$", re.IGNORECASE)
    
    messages, senders, current_msg = [], set(), None
    for line in chat_content.splitlines():
        match = msg_pattern.match(line)
        if match:
            if current_msg: messages.append(current_msg)
            dt, sender, text = match.groups()
            senders.add(sender.strip())
            current_msg = {'time': dt.strip(), 'sender': sender.strip(), 'text': text.strip()}
        elif current_msg:
            current_msg['text'] += "\n" + line
    if current_msg: messages.append(current_msg)

    primary_sender = list(senders)[0] if senders else ""
    img_pattern = re.compile(r"([\w\-\.]+\.(?:jpg|jpeg|png|webp))", re.IGNORECASE)
    audio_pattern = re.compile(r"([\w\-\.]+\.(?:opus|m4a|mp3|ogg))", re.IGNORECASE)
    
    bubbles = []
    print(f"📦 Procesando {len(messages)} mensajes...")

    for idx, msg in enumerate(messages, start=1):
        is_me = (msg['sender'] == primary_sender)
        bubble_cls = "bubble outgoing" if is_me else "bubble incoming"
        raw_text = msg['text']

        # Incrustar fotos en Base64
        imgs_html = ""
        for img_name in img_pattern.findall(raw_text):
            target = [f for f in file_list if f.endswith(img_name)]
            if target:
                data = base64.b64encode(z.read(target[0])).decode('utf-8')
                imgs_html += f'<div class="img-box"><img src="data:image/jpeg;base64,{data}" /></div>'

        # Transcribir audios con IA
        audios_html = ""
        for aud_name in audio_pattern.findall(raw_text):
            target = [f for f in file_list if f.endswith(aud_name)]
            if target:
                print(f"🎙️ Transcribiendo audio: {aud_name}...")
                with tempfile.NamedTemporaryFile(suffix=os.path.splitext(aud_name)[1], delete=False) as tmp:
                    tmp.write(z.read(target[0]))
                    tmp_p = tmp.name
                try:
                    segs, _ = whisper_model.transcribe(tmp_p, language="es")
                    txt = " ".join([s.text for s in segs]).strip() or "(Audio sin voz clara)"
                except:
                    txt = "(Error en transcripción)"
                finally:
                    if os.path.exists(tmp_p): os.remove(tmp_p)
                audios_html += f'<div class="audio-box"><strong>🎙️ Transcripción:</strong> "{html.escape(txt)}"</div>'

        clean_text = html.escape(raw_text).replace('\n', '<br>')
        bubbles.append(f"""
        <div class="{bubble_cls}">
            <div class="sender">{html.escape(msg['sender'])}</div>
            {imgs_html}
            {audios_html}
            <div class="txt">{clean_text}</div>
            <div class="time">{html.escape(msg['time'])}</div>
        </div>
        """)

# Estilos CSS listos para imprimir a PDF
html_doc = f"""<!DOCTYPE html><html><head><meta charset="utf-8">
<style>
  body {{ background: #efeae2; font-family: sans-serif; display: flex; justify-content: center; padding: 20px; }}
  .chat {{ width: 100%; max-width: 780px; display: flex; flex-direction: column; gap: 8px; }}
  .bubble {{ max-width: 75%; padding: 8px 12px; border-radius: 8px; font-size: 14px; display: flex; flex-direction: column; }}
  .incoming {{ background: #fff; align-self: flex-start; }}
  .outgoing {{ background: #d9fdd3; align-self: flex-end; }}
  .sender {{ font-weight: bold; font-size: 11px; color: #128c7e; margin-bottom: 3px; }}
  .outgoing .sender {{ display: none; }}
  .img-box img {{ max-width: 100%; border-radius: 6px; margin: 5px 0; }}
  .audio-box {{ background: rgba(0,0,0,0.05); border-left: 3px solid #00a884; padding: 6px 10px; margin: 5px 0; font-style: italic; }}
  .time {{ font-size: 10px; color: #667781; align-self: flex-end; margin-top: 4px; }}
  @media print {{ body {{ background: white; }} .bubble {{ page-break-inside: avoid; border: 1px solid #ccc; }} }}
</style></head><body><div class="chat">{''.join(bubbles)}</div></body></html>"""

with open(output_html, "w", encoding="utf-8") as f:
    f.write(html_doc)
print(f"🎉 ¡Listo! Archivo '{output_html}' generado con éxito.")

⚡ Paso 3: Ejecutar y exportar a PDF

  1. Corre el script con tu chat:Bashpython exportar_chat_completo.py "MiChatExportado.zip"
  2. Abre el archivo generado (chat_completo.html) en tu navegador favorito.
  3. Presiona Ctrl + P (Imprimir).
  4. Elige Guardar como PDF y asegúrate de marcar la casilla “Gráficos de fondo” para que conserve los colores originales.

🧠 Paso 4: La magia de la IA para armar tu tutorial

¡Ahora viene lo mejor! Sube tu nuevo archivo PDF a una IA multimodal y dale esta instrucción:

“Actúa como redactor técnico y diseñador instruccional. Analiza este documento PDF que contiene capturas, mensajes y transcripciones de notas de voz. Descarta las charlas casuales y crea un tutorial paso a paso numerado, explicando cada acción y detallando qué se observa en cada captura de pantalla para guiar al usuario.”

En cuestión de segundos tendrás un manual técnico o una guía de uso impecable, estructurada y sin haber pasado horas transcribiendo audios ni acomodando capturas en Word. 🎯👏

¿Qué te parece este flujo de trabajo? ¡Pruébalo con tu próxima explicación técnica y cuéntanos en los comentarios cómo te fue! 👇💬