Saltar al contenido
Chatbots de WhatsApp desde cero
Temario

Ejercicio: El estado de la conversación

  • ⏱ 45 minutos
  • 📶 Nivel: intermedio

Objetivo

Construir con tus propias manos una mini máquina de estados: una tabla de transiciones, una sesión y una función pura procesar que nunca modifica lo que recibe. Es una versión reducida del motor.js real del curso (sin catálogo ni textos), pero con las mismas reglas de fondo.

Cómo se trabaja

La parte A se hace en tu terminal con el código de referencia del curso: te sirve para ver al motor real cambiar de estado. La parte B son tres funciones de JavaScript puro que se comprueban en esta página. Los eventos son palabras simples ("hola", "pedir", "cerrar"...) en lugar de mensajes de WhatsApp: así te concentras en el estado.

La tabla de transiciones que debes implementar (cada fila: estado actual, evento y estado siguiente):

Estado Evento Siguiente estado
INICIO hola MENU
INICIO, MENU pedir ELIGIENDO
ELIGIENDO cerrar TIPO_ENTREGA
TIPO_ENTREGA delivery DIRECCION
TIPO_ENTREGA recojo PAGO
DIRECCION direccion PAGO
PAGO pago CONFIRMAR
CONFIRMAR confirmar FIN
cualquiera cancelar FIN
cualquiera humano ESPERANDO_HUMANO
cualquiera menu MENU

Reglas extra: en ESPERANDO_HUMANO solo funcionan menu y cancelar; cualquier otro evento se ignora sin contarlo como fallo (el bot calla). Si el estado es FIN, el siguiente evento empieza una sesión nueva.

💡 Reto extra

Añade un evento volver que regrese al estado anterior. Para lograrlo la sesión necesita recordar el historial (por ejemplo un arreglo historial de estados). Piensa: ¿qué debería pasar con volver en INICIO? Es el tipo de decisión de diseño que aparece al crecer una máquina de estados.

▶ 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: Una tabla en vez de muchos if

Un objeto de objetos como { INICIO: { hola: "MENU" } } te deja escribir TRANSICIONES[estado]?.[evento]. Guarda aparte los eventos globales (cancelar, humano, menu) y consúltalos si la tabla del estado no tiene el evento.

💡 Pista 2: ESPERANDO_HUMANO es un caso especial

Trátalo antes que nada: si el estado es ESPERANDO_HUMANO, solo menu y cancelar devuelven un estado nuevo; para todo lo demás devuelve null. En procesar, ese null no debe sumar fallos en este estado.

💡 Pista 3: No mutar la sesión

Crea la sesión nueva con { ...base } y modifica solo la copia. Para el caso FIN, usa como base crearSesion(sesion.telefono, ahora).

🔎 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.

    estado.js
    const TRANSICIONES = {
      INICIO: { hola: "MENU", pedir: "ELIGIENDO" },
      MENU: { pedir: "ELIGIENDO" },
      ELIGIENDO: { cerrar: "TIPO_ENTREGA" },
      TIPO_ENTREGA: { delivery: "DIRECCION", recojo: "PAGO" },
      DIRECCION: { direccion: "PAGO" },
      PAGO: { pago: "CONFIRMAR" },
      CONFIRMAR: { confirmar: "FIN" },
    };
    
    const GLOBALES = { cancelar: "FIN", humano: "ESPERANDO_HUMANO", menu: "MENU" };
    
    function crearSesion(telefono, ahora = 0) {
      return { telefono, estado: "INICIO", fallos: 0, ultimoMensajeCliente: ahora };
    }
    
    function siguienteEstado(estado, evento) {
      if (estado === "ESPERANDO_HUMANO") {
        return evento === "menu" || evento === "cancelar" ? GLOBALES[evento] : null;
      }
      return TRANSICIONES[estado]?.[evento] ?? GLOBALES[evento] ?? null;
    }
    
    function procesar(sesionPrevia, evento, ahora = 0) {
      const base = sesionPrevia.estado === "FIN"
        ? crearSesion(sesionPrevia.telefono, ahora)
        : sesionPrevia;
      const sesion = { ...base, ultimoMensajeCliente: ahora };
      const siguiente = siguienteEstado(sesion.estado, evento);
      if (siguiente) {
        sesion.estado = siguiente;
        sesion.fallos = 0;
      } else if (sesion.estado !== "ESPERANDO_HUMANO") {
        sesion.fallos += 1;
      }
      return sesion;
    }