Hugo Espinosa Callejas
Oscar Barajas Tavares
Team PlatziGracias por la aclaración.
Sabina de la Serna Espinosa
ya lo cambie y sigue igual no entiendo0 nada
Personalizar el saludo de un bot de WhatsApp marca la diferencia entre una réplica fría y una conversación que se siente humana. Aquí aprendes cómo recibir al usuario por su nombre, validar saludos con un array de strings y disparar un mensaje de bienvenida usando una arquitectura de servicios reutilizable en Node.js. Es ideal si estás construyendo un bot conversacional para atención al cliente.
Cómo identifico al usuario que escribe al bot
Cuando llega un mensaje desde la API de WhatsApp, recibes un objeto con mucha información. Dentro de ese objeto vive un campo llamado contacts que trae dos datos clave: el ID del usuario (compuesto por la lada del país y el número de WhatsApp) y el nombre que la persona configuró en su perfil [02:15].
Para extraer esa información, dentro del controller creas una constante senderInfo que apunta a value.contacts[0]. Ese índice cero es donde se guarda el contacto que escribe.
js const senderInfo = value.contacts[0];
Luego pasas ese senderInfo como segundo argumento al messageHandler, junto con el mensaje. Así, la lógica de respuesta tiene acceso al nombre completo del usuario.
¿Qué es senderInfo en un bot de WhatsApp? Es el objeto que contiene los datos de quien envía el mensaje: número con lada y nombre de perfil. Lo extraes desde
contacts[0]en la respuesta del webhook.
Una nota práctica: WhatsApp devuelve el nombre tal como lo configuró el usuario, lo que incluye emojis o caracteres especiales. Puedes aplicar un regex para limpiarlo, pero ten presente que no toda configuración encaja en una regla única.
Cómo valido si un mensaje es un saludo en mi bot
Antes de responder, el bot necesita saber si el texto recibido es un disparador válido. Para eso creas una función llamada isGreeting dentro del message handler [05:40].
La función recibe un mensaje y compara contra un array de strings con los saludos posibles. Estos son los triggers que inicializan el flujo de bienvenida:
js isGreeting(message) { const greetings = ["hola", "hello", "hi", "buenas tardes"]; return greetings.includes(message); }
El método includes retorna un booleano: true si el mensaje coincide con alguno de los saludos, false si no. Con eso decides si ejecutas el flujo de bienvenida o caes en otra lógica.
Por qué normalizo el texto a minúsculas y sin espacios
Los celulares suelen capitalizar la primera letra automáticamente, y a veces el usuario añade un espacio sin querer. Si filtras solo contra "hola" exacto, un "Hola " con mayúscula y espacio nunca pasaría el filtro.
La solución es normalizar el texto antes de validarlo:
js const incomingMessage = message.text.body.toLowerCase().trim();
Con toLowerCase fuerzas todo a minúsculas y con trim eliminas espacios al inicio y al final. Así, " Hola " se convierte en "hola" y pasa la validación sin problema [12:30].
¿Por qué usar toLowerCase y trim juntos? Porque garantizan que el filtro funcione sin importar cómo el usuario escriba el saludo. Sin esta normalización, perderías coincidencias por mayúsculas o espacios accidentales.
Cómo construyo y envío el mensaje de bienvenida
Con la validación lista, defines una función asíncrona sendWelcomeMessage que recibe dos parámetros: to (el ID del usuario) y messageId (el identificador del mensaje original) [09:10].
Dentro armas una constante welcomeMessage con el texto que quieres enviar. En el ejemplo de la veterinaria queda así:
js async sendWelcomeMessage(to, messageId) { const welcomeMessage = "Hola, bienvenido a nuestro servicio de veterinaria online. ¿En qué puedo ayudarte hoy?"; await whatsappService.sendMessage(to, welcomeMessage, messageId); }
Fíjate que llamas a whatsappService.sendMessage, el mismo servicio que ya construiste antes. Esa es la ventaja de la arquitectura por capas: reutilizas la lógica de envío sin copiar y pegar código cada vez que necesitas mandar un mensaje.
Cómo conecto la validación con el envío en handleIncomingMessage
Dentro de handleIncomingMessage, la estructura final queda con un if-else limpio:
js if (this.isGreeting(incomingMessage)) { await this.sendWelcomeMessage(message.from, message.id); } else { await whatsappService.sendMessage(message.from, message.text.body, message.id); } await whatsappService.markAsRead(message.id);
Si el mensaje es un saludo, dispara la bienvenida. Si no, replica el mensaje (la lógica anterior). Al final, marca el mensaje como leído para que aparezca el check azul en el chat del usuario.
Cómo configuro el comando start con nodemon
Antes de probar, ajusta el package.json para que la aplicación se reinicie sola cuando cambies código. Agregas un script nuevo:
"scripts": { "start": "nodemon src/app.js" }
También actualizas la ruta del main a src/app.js. Con eso, nodemon escucha los cambios en tiempo real y reinicia el servidor automáticamente [15:50].
Al probar en WhatsApp, los saludos como hola, hi o buenas tardes disparan el mensaje de bienvenida. Cualquier otro texto, como ayuda, cae en la lógica de réplica porque aún no hay flujo definido para esa palabra.
Ten en cuenta que puedes crear tantos flujos como necesites: uno para agendar citas, otro para consultas frecuentes, otro para emergencias. El bot se comporta distinto según el caso de uso que estés modelando.
¿Cómo mejorarías el saludo de una veterinaria conversacional? Déjalo en los comentarios.
Hugo Espinosa Callejas
Oscar Barajas Tavares
Team PlatziGracias por la aclaración.
Sabina de la Serna Espinosa
ya lo cambie y sigue igual no entiendo0 nada
Mayra Alejandra Soto Nieto
me sale un error al volver a ejecutar el comando de npm run start
Mayra Alejandra Soto Nieto
Cristian Acalo
Tenía el mismo error. Se solucionó cambiando la extensión del archivo nodemon.js a nodemon.json
Sebastián Mejía Ríos
No sé si solo me pasa a mí pero esta clase es la 12 y debería ser a 11. Me había perdido en la configuración de la app.
Guillermo Leonardo Montaño Medina
Tuve el mismo problema como por 2 semanas, me marcaba error y los aventaba medio raro
Marcelo J. Gracia
Miguel Angel Ramirez Jimenez
Hola Tengo este mismo problema y no se como se soluciona, alguien sabes como se debe adicionar la parte de send to whatsapp?
Daniel Sandoval
Para que no puedan ejecutar el servidor, solo ejecuten en la terminal > node src/app.js
Miguel Enrique Sánchez
Gracias!
Genere un nuevo token y ejecute node src/app.js
y el server conecto con el puerto 300
Pablo Andrés Ramírez
Todos mis problemas se solucionaron cuando cambié la extención del nodemon.js a nodemon.json
Jose Alberto Gomez Macias
Siento que este video tuvo que haber estado antes, no?
Oscar Zacaula
Tuve problemas con el número de Mexico igual que Hugo el 1 después del 52 es el problema.
Lo que hice fue en messageHandler.js agregue: const fromNumber = this.formatNumber(message.from);```js const fromNumber = this.formatNumber(message.from);
```js formatNumber(number) { // Si el número empieza con "521", remueve el '1' para que quede "52" seguido del número if (number.startsWith("521")) { return "52" + number.slice(3); } return number; // Retorna el número sin cambios si no empieza con "521" } } ``` formatNumber(number) { // Si el número empieza con "521", remueve el '1' para que quede "52" seguido del número if (number.startsWith("521")) { return "52" + number.slice(3); } return number; // Retorna el número sin cambios si no empieza con "521" }} con esto me quedo funcionando!!
Daniel Stiven Lopez Carmona
Muchas gracias, con ayuda de este código pude resolver el código mío ;)
Darlinson Felipe Polania Camacho
Mi problema era que realizaba la inscripción del webhook correctamente, mi server publico en local, todo correcto hasta aqui, el problema radicaba que si yo enviaba un mensaje de texto, meta no me enviaba de regreso nada, no llegaba a mi codigo, no es inscribir la empresa como muchos decian, al igual que me daban buenas pistas y preguntandole a la IA , resulta que hay un paso adicional que debemos hacer, no debemos condundir el phone numer ID con el WABA ID son dos numeross distintos lo que falto es suscribir el WABA ID que se encuentra con nuestra cuenta suite alli administramos varios numeros (pantallazo)
con ese numero es la que debemos relaizar la suscripcion para que pueda recibir eventos nuestro server. Ojo no hay que incribir como empresa.
En la terminal pones
Invoke-RestMethod `
-Uri " WABA ID /subscribed_apps" `
-Method POST `
-Headers @{ Authorization = "Bearer $token" }
listo tienes ya acceso a tu codigo meta, ya te envia los eventos.!Puede ser una imagen de texto!Puede ser una imagen de texto!Puede ser una imagen de texto
Saul Juarez
def clean_phone_number(phone: str) -> str:
"""
Clean and format a phone number with country code.
Args:
phone: Raw phone number string (may contain non-digit characters)
Returns:
str: Formatted phone number as +{country_code}{phone_number}
Example:
clean_phone_number("34612345678") -> "+3412345678"
clean_phone_number("+34 612 345 678") -> "+3412345678"
"""
# Extract only digits
digits_only = ''.join(filter(str.isdigit, phone))
# Last 10 digits are the phone number
phone_number = digits_only[-10:]
# Remaining digits minus the last digit are the country code
country_code = digits_only[:-11] if len(digits_only) > 10 else ""
return f"+{country_code}{phone_number}"
Asi se puede resolver en Python para el mismo del numero adicional
Nicolas Eduardo Rios Plaza
Soy el unico al que le aparece el video con estos caracteres? <img height="303" width="535" src="https://static.platzi.com/media/user_upload/upload-802946b4-d76e-46da-a1b3-f7130082a73e.png" />
Yesenia Chavarriaga Acevedo
Al decirle por ejemplo "hola como estas" me envía el echo, no debería al tomar el hola ignorar lo otro y enviar el mensaje de saludo?
Mario Alejandro Crespo Reyes
segun la logica de la funcion que escribio oscar, y en el caso que propones de que te escriban "Hola como estas", como puedes ver es un solo string que contiene todas las palabras por lo que luego de pasar por la funcion, el texto quedaria
"holacomoestas"
convierte todo el texto a minusculas y quita los espacios, y este texto no esta incluido en el array de saludos por lo que no se reconoce como un saludo, espero que te sirva esta información.
Luis Fernando Farji
Por mas que intento, recibo un mensaje 403 forbidden el .env esta configurado bien
Waleska Deyanira Gutierrez Arteaga
Hola, agregue unos logs para validar mi mensaje en consola.
isGreeting(message){
const greetingsEN = ['hi', 'hello', 'hey', 'greetings', 'good morning', 'good afternoon', 'good evening'];
const greetingsES = ['hola', 'buenos días', 'buenas tardes', 'buenas noches', 'saludos', 'qué tal', 'buen día', 'buenas'];
const greetingsITA = ['ciao', 'salve', 'buongiorno', 'buonasera', 'saluti'];
const greetings = greetingsEN.concat(greetingsES);
greetings.push(...greetingsITA);
console.log('Checking message for greetings:', message);
return greetings.includes(message);
}
andres arturo rios
Nota para todos: Es necesario actualizar la configuración del webhook. De lo contrario, los mensajes no se recibirán correctamente en el puerto 3000 (actual) y seguirán intentando llegar al 3001 (anterior).
Magdalena Fernandez
andres arturo rios
no puedo ver el error
Xara Beltran
Hola compañeros no sé si les sirva el dato, pero a mí no me funcionaba por nada del mundo jaja tuve que modificar el archivo service.js de esta forma y ahí si me funcionó.```js import express from "express"; import 'dotenv/config'; import webhookController from "./src/controllers/webhookController.js";
const app = express(); app.use(express.json());
const PORT = process.env.PORT || 3000;
// Conectar tu lógica real app.post("/webhook", webhookController.handleIncoming); app.get("/webhook", webhookController.verifyWebhook);
app.get("/", (req, res) => {
res.send(<pre>API de WhatsApp activa. Usa /webhook para mensajes.</pre>);
});
app.listen(PORT, () => {
console.log(✅ Servidor escuchando en el puerto ${PORT});
});
Fredy Yecid Castro Agray
Que cursos me sugieren de JS en platzi previos a este curso?
Jose Luis Bedoya
los cursos de express! ya que es la tecnologïa que estamos usando para el curso
Para el caso de México hay un detalle ya que le pone un 1 después de código ejemplo: 521XXXXXXXXXX si quitas ese 1 funciona
async handleIncomingMessage(message) { if (message?.type === "text") { const incomingMessage = message.text.body.toLowerCase().trim(); // Remove 1 from the position 2 in the from property const fromNumber = message.from.slice(0, 2) + message.from.slice(3); if (this.isGreeting(incomingMessage)) { await this.sendWelcomeMessage(fromNumber, message.id); } else { const response = `Echo: ${message.text.body}`; await whatsappService.sendMessage(fromNumber, response, message.id); } await whatsappService.markAsRead(message.id); } }
Estoy teniendo un error status 400 em el método sendToWhatsApp.
Luego de enviar un msg: "Hi", o cuaquier otro msg..
me marca como leido pero no responde..
const sendToWhatsApp = async (data) => { const baseUrl = `${config.BASE_URL}/${config.API_VERSION}/${config.BUSINESS_PHONE}/messages`; const headers = { Authorization: `Bearer ${config.API_TOKEN}` }; try { const response = await axios({ method: 'POST', url: baseUrl, headers: headers, data, }) return response.data; } catch (error) { console.error(error) } }; ```no se donde puede estar el error, no lo encuentro!