Saltar al contenido
Chatbots de WhatsApp desde cero
Temario Ir al ejercicio →

Tu primer bot en la consola

  • ⏱ 60 minutos
  • 📶 Nivel: básico
  • 🧩 Incluye ejercicio

💡 🎯 Al terminar esta lección podrás…

  • Crear un bot de consola que lee lo que escribes con readline y responde.
  • Separar el «cerebro» (una función pura responder(texto)) de la entrada y salida (la consola).
  • Implementar saludo, menú de tres opciones y respuestas por palabras clave, ignorando mayúsculas y tildes.
  • Organizar un proyecto con las carpetas src/ y test/ y el archivo consola.js.
  • Escribir y ejecutar la primera prueba con node:test.

1.Por qué empezar por la consola

Conectar un bot a WhatsApp exige cuentas, tokens y un servidor. Antes de eso conviene comprobar lo esencial: ¿el bot entiende y responde bien? Eso se puede probar en tu terminal, sin internet, sin tarjetas y sin esperar aprobaciones.

La consola va a ser tu «WhatsApp de laboratorio». Todo lo que construyas aquí (las reglas, el menú, más adelante el catálogo y el carrito) se conectará a WhatsApp después sin reescribirlo, porque la lógica quedará separada de la forma de entrada. Esa separación es la idea central de esta lección.

Necesitas Node instalado y saber crear un proyecto; si no, repasa la lección 3. El diseño del menú viene de la lección 2.

2.El proyecto: estructura de carpetas

Crea la carpeta del proyecto y su package.json como en la lección anterior:

Terminal
mkdir bot-minimarket
cd bot-minimarket
npm init -y

Abre package.json y déjalo así:

package.json
{
  "name": "bot-minimarket",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node consola.js",
    "test": "node --test"
  }
}

La estructura que vamos a construir es esta:

Texto
bot-minimarket/
├── package.json
├── consola.js          entrada y salida: lee del teclado, imprime
├── src/
│   └── cerebro.js      el cerebro: responder(texto)
└── test/
    └── cerebro.test.js las pruebas

Un archivo, una responsabilidad. consola.js sabe hablar con la terminal; src/cerebro.js sabe qué contestar. Ninguno se mete en el trabajo del otro.

3.Primer paso: un eco con readline

Empecemos con lo mínimo: leer una línea y devolverla. Node incluye readline, que lee lo que escribe el usuario. Crea eco.js (es solo un experimento; luego lo descartas):

eco.js
import readline from "node:readline";

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

rl.question("Tú: ", (texto) => {
  console.log("Bot: dijiste " + texto);
  rl.close();
});

Ejecútalo con node eco.js. Escribes hola bot y pulsas Enter:

Salida esperada
Tú: hola bot
Bot: dijiste hola bot

Qué ocurre: createInterface conecta el teclado (stdin) y la pantalla (stdout). rl.question muestra una pregunta y, cuando el usuario pulsa Enter, llama a tu función con lo escrito. Es un callback: una función que se ejecuta cuando llega el dato. rl.close() termina la lectura.

Ya tienes lo esencial de un bot: entra un texto, sale un texto. Ahora hay que hacerlo inteligente y, sobre todo, ordenado.

4.Segundo paso: separar cerebro y consola

Imagina que escribes las reglas dentro de consola.js, mezcladas con console.log y readline. Cuando en la lección 10 quieras que el mismo bot responda por WhatsApp, tendrás que arrancarlo de nuevo. En cambio, si las reglas viven en una función pura (recibe texto, devuelve texto, no imprime nada), la consola y WhatsApp son solo dos formas distintas de llamarla:

Consolao más adelante WhatsApp
→texto del cliente
responder(texto)el cerebro (función pura)
→texto de respuesta
Consolaimprime la respuesta

Escribe el cerebro más simple posible en src/cerebro.js:

src/cerebro.js
export function responder(texto) {
  return "Dijiste: " + texto;
}

Y la consola, que ahora repite la pregunta después de cada respuesta para mantener la conversación. Crea consola.js:

consola.js
import readline from "node:readline";
import { responder } from "./src/cerebro.js";

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

console.log("Bot del Minimarket La Esquina. Escribe 'salir' para terminar.\n");

function preguntar() {
  rl.question("Tú: ", (linea) => {
    if (linea.trim().toLowerCase() === "salir") {
      console.log("Bot: Hasta pronto.");
      rl.close();
      return;
    }
    console.log("Bot: " + responder(linea) + "\n");
    preguntar();
  });
}

preguntar();

La función preguntar se llama a sí misma al terminar cada respuesta: eso crea el bucle de conversación. La única salida es escribir salir. Este consola.js no cambiará más en toda la lección: lo que mejore será el cerebro.

5.Tercer paso: saludo, menú y palabras clave

Ahora el cerebro de verdad. Tiene tres piezas.

1. Normalizar el texto. Los clientes escriben «HOLA», «Hola», «¿Dónde están?», «donde estan». Antes de comparar, convertimos todo a minúsculas y quitamos las tildes. normalize("NFD") separa cada letra de su tilde y la expresión regular elimina las tildes sueltas:

JavaScript
export function normalizar(texto) {
  return String(texto ?? "")
    .toLowerCase()
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .trim();
}

El String(texto ?? "") hace que null o undefined no rompan la función.

2. Reglas por palabras clave. Una regla es un objeto con las palabras que la activan y una función que da la respuesta. Las guardamos en un array y recorremos hasta encontrar la primera que encaje. Así, añadir una regla es añadir un objeto, sin tocar la lógica.

3. Respuesta por defecto. Si ninguna regla encaja, el bot dice que no entendió y ofrece el menú (recuerda la lección 2: todo error termina en una opción).

Este es el src/cerebro.js completo:

src/cerebro.js
// Cerebro del bot: recibe texto, devuelve texto. No imprime nada ni lee nada.

export function normalizar(texto) {
  return String(texto ?? "")
    .toLowerCase()
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .trim();
}

const MENU = [
  "Elige una opción escribiendo el número o la palabra:",
  "1. Horario",
  "2. Dirección",
  "3. Ayuda",
].join("\n");

const REGLAS = [
  { palabras: ["hola", "buenas", "buenos dias", "buenas tardes"], respuesta: () => "Hola, bienvenido a Minimarket La Esquina.\n" + MENU },
  { palabras: ["1", "horario", "hora", "abren", "cierran"], respuesta: () => "Atendemos de lunes a sábado de 8:00 a 21:00 y domingos de 9:00 a 14:00." },
  { palabras: ["2", "direccion", "donde", "ubicacion"], respuesta: () => "Estamos en Av. Los Olivos 123 (ejemplo), frente al parque." },
  { palabras: ["3", "ayuda", "menu", "opciones"], respuesta: () => MENU },
];

export function responder(texto) {
  const t = normalizar(texto);
  if (t === "") return "Escríbeme algo y te ayudo. Con 'ayuda' ves las opciones.";
  for (const regla of REGLAS) {
    if (regla.palabras.some((p) => t === p || t.includes(p) && p.length > 1)) {
      return regla.respuesta();
    }
  }
  return "No entendí tu mensaje. Escribe 'ayuda' para ver las opciones.";
}

Léelo con calma. La línea clave es regla.palabras.some(...): la regla se activa si el texto es exactamente la palabra o contiene una palabra de más de un carácter. La condición p.length > 1 evita que el número "1" se active con cualquier mensaje que lo lleve (por ejemplo «quiero 10 panes»): los números solo valen si el cliente escribe solo el número.

El horario y la dirección son datos de ejemplo del negocio ficticio. En un negocio real, vendrían de su configuración.

6.Ejecutarlo

Con los dos archivos listos, arranca el bot con npm start (o node consola.js) y conversa:

Terminal
npm start
Salida esperada
Bot del Minimarket La Esquina. Escribe 'salir' para terminar.

Tú: hola
Bot: Hola, bienvenido a Minimarket La Esquina.
Elige una opción escribiendo el número o la palabra:
1. Horario
2. Dirección
3. Ayuda

Tú: ¿a qué hora abren?
Bot: Atendemos de lunes a sábado de 8:00 a 21:00 y domingos de 9:00 a 14:00.

Tú: 2
Bot: Estamos en Av. Los Olivos 123 (ejemplo), frente al parque.

Tú: quiero pizza
Bot: No entendí tu mensaje. Escribe 'ayuda' para ver las opciones.

Tú: salir
Bot: Hasta pronto.

Tienes un bot que saluda, ofrece un menú de tres opciones, entiende por palabras clave o por número y admite que no entendió. Es poco, pero es un bot de verdad: la misma estructura crecerá hasta manejar pedidos completos.

📌 Nota · Escribir con tildes

Si tu terminal muestra caracteres raros con las tildes en Windows, ejecuta antes chcp 65001 en el Símbolo del sistema, o usa Windows Terminal, que suele funcionar sin ajustes.

7.Cuarto paso: la primera prueba automática

Cada vez que cambies una regla querrás saber que no rompiste otra. En vez de conversar con el bot a mano, escribes pruebas. Como responder es una función pura, probarla es trivial: le das un texto y miras la respuesta. Crea test/cerebro.test.js:

test/cerebro.test.js
import test from "node:test";
import assert from "node:assert/strict";
import { responder, normalizar } from "../src/cerebro.js";

test("normalizar quita tildes y mayúsculas", () => {
  assert.equal(normalizar("  DIRECCIÓN "), "direccion");
});

test("saluda y muestra el menú", () => {
  assert.match(responder("Hola"), /bienvenido/);
});

test("responde el horario por palabra clave y por número", () => {
  assert.match(responder("¿A qué hora abren?"), /8:00/);
  assert.match(responder("1"), /8:00/);
});

test("si no entiende, lo dice", () => {
  assert.match(responder("asdfgh"), /No entendí/);
});

assert.equal compara dos valores exactos; assert.match comprueba que un texto cumpla una expresión regular (útil cuando no quieres repetir el mensaje completo). Ejecuta:

Terminal
npm test
Salida esperada
✔ normalizar quita tildes y mayúsculas (0.9399ms)
✔ saluda y muestra el menú (0.3564ms)
✔ responde el horario por palabra clave y por número (0.155ms)
✔ si no entiende, lo dice (0.1547ms)
ℹ tests 4
ℹ pass 4
ℹ fail 0

Los milisegundos de tu pantalla serán otros; lo importante son los cuatro ✔. Desde ahora, cada vez que añadas una regla, añade su prueba. Es el hábito que separa un experimento de un producto.

8.Una trampa: palabras dentro de otras

Prueba esto en el bot: escribe ahora.

Salida esperada
Tú: ahora
Bot: Atendemos de lunes a sábado de 8:00 a 21:00 y domingos de 9:00 a 14:00.

El bot respondió el horario porque la palabra hora está dentro de «ahora». Es el error clásico del includes. Para un bot de reglas sencillo es un compromiso aceptable (los clientes suelen escribir mensajes cortos), pero en la lección 6 aprenderás a separar el mensaje en palabras completas y a puntuar coincidencias para reducir esos falsos positivos. Por ahora, tenlo presente y coloca las palabras más específicas en las reglas más importantes.

9.Qué sigue

Tu bot tiene una limitación grande: no recuerda nada. Si el cliente escribe «1» después de «quiero pedir», el bot no sabe en qué punto de la conversación estaba. Responder solo según el último mensaje funciona para preguntas sueltas, pero no para un pedido de varios pasos. La solución es el estado: guardar en qué punto va cada cliente, tal como lo dibujaste en el mapa de la lección 2. Eso es la lección 5.

10.Errores frecuentes

11.Apuntes para llevar

12.Glosario

Término Significado
readline Módulo de Node que lee líneas de texto desde el teclado.
stdin / stdout Entrada y salida estándar: el teclado y la pantalla de la terminal.
Callback Función que se ejecuta cuando llega un dato o termina una operación.
Cerebro Función que decide qué responder; no imprime ni lee nada.
Normalizar Dejar el texto en una forma uniforme (minúsculas, sin tildes) para compararlo.
Palabra clave Palabra que, al aparecer en el mensaje, activa una regla.
Falso positivo Regla que se activa cuando no debía (por ejemplo hora dentro de ahora).
Prueba automática Código que verifica que otra función da el resultado esperado.

🧠 Comprueba lo aprendido

Elige una respuesta en cada pregunta. Verás al instante si acertaste y por qué.

¿Por qué se separa responder(texto) del archivo consola.js?

¿Qué hace rl.question("Tú: ", callback)?

¿Para qué sirve normalizar el texto antes de comparar?

El bot responde el horario cuando el cliente escribe «ahora». ¿Por qué?

¿Qué comando ejecuta todas las pruebas del proyecto?

📚 Para profundizar (fuentes oficiales)

Este material lo hace un estudiante con ayuda de IA: ante cualquier duda, manda la documentación oficial.

Tu progreso se guarda en este navegador y se refleja en el índice del curso.

💡 🚀 ¡Ahora te toca!

La práctica fija lo aprendido. Pasa al ejercicio de esta lección.

Ir al ejercicio