Saltar al contenido
Chatbots de WhatsApp desde cero
Temario

Ejercicio: Marketing responsable por WhatsApp

  • ⏱ 45 minutos
  • 📶 Nivel: intermedio

Objetivo

Construir las tres piezas que protegen a tu minimarket del spam: registrar el opt-in, reconocer la baja y decidir si un cliente puede recibir una promoción hoy.

Cómo se trabaja

La parte A es de diseño: piensas tus textos reales. La parte B son funciones de JavaScript puro que se comprueban aquí. Las horas son números en milisegundos y un cliente es un objeto como este:

Texto
{ telefono: "51999000111", optIn: { canal: "qr", fecha: 1000 } | null, baja: null | 5000, promosEnviadas: [1000, 2000] }

Considera un día como 24 * 60 * 60 * 1000 ms y una semana como 7 días. Un envío cuenta para el límite semanal si pasó menos de 7 días.

💡 Reto extra

Añade registrarEnvio(cliente, ahora), que devuelve un cliente nuevo con ahora añadido a promosEnviadas y descarta los envíos de hace más de 30 días para que el registro no crezca sin límite. Pensando en la lección 24, es una forma sencilla de aplicar retención.

▶ Prueba tu código aquí (opcional)
Consola

Pasos

Marca cada paso a medida que lo completes. Tu avance se guarda en este navegador.

💡 Pista 1: registrarOptIn sin mutar

Usa { ...cliente, optIn: { canal, fecha: ahora }, baja: null }. Antes, comprueba el canal con un array y includes.

💡 Pista 2: Normalizar el texto

String(texto ?? "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9 ]/g, " ").replace(/\s+/g, " ").trim() deja " Stop. " como "stop". Luego compara con un array de palabras usando includes.

💡 Pista 3: Contar envíos recientes

Filtra cliente.promosEnviadas (puede no existir: usa ?? []) con ahora - t < 7 * DIA y compara la longitud con maxPorSemana.

🔎 Comprueba tu trabajo

Pega tu código y pulsa Comprobar. La revisión ocurre en tu navegador; nada se envía a ningún sitio. Cada punto en rojo incluye una pista. Pega solo el JavaScript, sin las etiquetas <script>.

    ✅ Ver una solución posible

    Intenta resolverlo antes de mirar. Tu versión puede ser distinta y también correcta.

    promos.js
    const DIA = 24 * 60 * 60 * 1000;
    const CANALES = ["casilla", "palabra-clave", "qr", "formulario"];
    
    function registrarOptIn(cliente, canal, ahora) {
      if (!CANALES.includes(canal)) throw new Error("Canal de opt-in desconocido: " + canal);
      return { ...cliente, optIn: { canal, fecha: ahora }, baja: null };
    }
    
    function normalizar(texto) {
      return String(texto ?? "")
        .normalize("NFD").replace(/[̀-ͯ]/g, "")
        .toLowerCase().replace(/[^a-z0-9 ]/g, " ").replace(/\s+/g, " ").trim();
    }
    
    function procesarBaja(texto) {
      return ["baja", "stop", "cancelar", "no mas"].includes(normalizar(texto));
    }
    
    function puedeRecibirPromo(cliente, ahora, maxPorSemana = 2) {
      if (!cliente.optIn || cliente.baja) return false;
      const recientes = (cliente.promosEnviadas ?? []).filter(t => ahora - t < 7 * DIA);
      return recientes.length < maxPorSemana;
    }