Ejercicio: Recibir mensajes reales
Objetivo
Conectar tu webhook con Meta, ver llegar un mensaje real de tu celular y practicar la lógica del webhook con tres funciones de JavaScript puro: responder la verificación, resumir un evento y descartar duplicados.
Cómo se trabaja
La parte A es práctica real: túnel o despliegue, registro del webhook en Meta y una prueba desde tu celular. La parte B son tres funciones que se comprueban en esta página. Son versiones simplificadas de lo que ya hace src/worker.js y src/whatsapp.js (sin firma ni red), para que entiendas la lógica. No pegues tokens reales aquí ni en capturas.
💡 Reto extra
Añade a resumirEvento el nombre del contacto (value.contacts[].profile.name) cuando exista, con el formato mensaje de Cliente Ejemplo (51999000111): .... Compara tu versión con parsearMensaje de src/whatsapp.js.
Pasos
Marca cada paso a medida que lo completes. Tu avance se guarda en este navegador.
💡 Pista 1: Verificación
Compara con === los tres datos y exige que verifyToken sea un texto no vacío. Recuerda que el reto se devuelve tal cual, sin modificarlo.
💡 Pista 2: Resumen del evento
Usa payload.entry ?? [], luego entrada.changes ?? [], y para cada value recorre value.messages ?? [] y value.statuses ?? []. Un arreglo vacío es un buen valor por defecto para todo lo que falte.
💡 Pista 3: Duplicados
Set tiene has y add. Primero pregunta si existe; si no, agrégalo antes de devolver false.
🔎 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.
function responderVerificacion(query, verifyToken) {
const modo = query["hub.mode"];
const token = query["hub.verify_token"];
const reto = query["hub.challenge"];
if (modo === "subscribe" && verifyToken && token === verifyToken && reto) {
return { status: 200, body: reto };
}
return { status: 403, body: "Prohibido" };
}
function resumirEvento(payload) {
const lineas = [];
if (!payload || payload.object !== "whatsapp_business_account" || !Array.isArray(payload.entry)) return lineas;
for (const entrada of payload.entry) {
for (const cambio of entrada?.changes ?? []) {
const valor = cambio?.value ?? {};
for (const msg of valor.messages ?? []) {
if (msg.type === "text") lineas.push(`mensaje de ${msg.from}: ${msg.text?.body ?? ""}`);
else lineas.push(`mensaje de ${msg.from}: no soportado (${msg.type})`);
}
for (const st of valor.statuses ?? []) {
lineas.push(`estado ${st.status} de ${st.id}`);
}
}
}
return lineas;
}
function esDuplicado(vistos, id) {
if (vistos.has(id)) return true;
vistos.add(id);
return false;
}