Ejercicio: Costos y métricas de tu bot
Objetivo
Construir una calculadora de costos con tarifas parametrizables y las funciones que resumen las métricas del bot, para poder presentarle al dueño del minimarket cuánto cuesta y qué resultados da.
Cómo se trabaja
La parte A es de investigación con la documentación oficial. La parte B son tres funciones de JavaScript puro. Las tarifas del ejercicio son de ejemplo, no las de Meta.
Un mensaje del mes se describe así: { categoria: "marketing" | "utilidad" | "autenticacion" | "servicio", cantidad: 80, enPuntoDeEntrada: true | false }. Las tarifas son un objeto como { marketing: 0.06, utilidad: 0.02, autenticacion: 0.02, servicio: 0.01, servicioGratis: 1000 }. Una conversación es { estado: "resuelta" | "derivada" | "abandonada" }.
💡 Reto extra
Añade costoPorPedido(costoMeta, costoInfra, pedidos) que devuelva cuánto cuesta cada pedido recibido por el bot (0 si no hubo pedidos). Es la cifra que más le gusta ver a un dueño: «cada pedido por WhatsApp me cuesta S/ 0.20».
Pasos
Marca cada paso a medida que lo completes. Tu avance se guarda en este navegador.
💡 Pista 1: Cobrar o no cobrar
Calcula el precio de cada mensaje con tarifas[m.categoria] ?? 0 y salta los mensajes enPuntoDeEntrada y, para servicio, guarda en una variable cuántos mensajes gratis quedan y réstala entrada tras entrada.
💡 Pista 2: Redondear
Math.round(x * 100) / 100 redondea a 2 decimales y Math.round(x * 1000) / 1000 a 3. Los errores de coma flotante son normales; el redondeo final los arregla.
💡 Pista 3: Contar eventos
Una función auxiliar n = tipo => eventos.filter(e => e.tipo === tipo).length evita repetir código. Para el tiempo medio, filtra los eventos respuesta y promedia su ms.
🔎 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 costoMensual(mensajes, tarifas) {
let total = 0;
let gratisServicio = tarifas.servicioGratis ?? 0;
for (const m of mensajes) {
if (m.enPuntoDeEntrada) continue;
let cobrables = m.cantidad;
if (m.categoria === "servicio") {
cobrables = Math.max(0, m.cantidad - gratisServicio);
gratisServicio = Math.max(0, gratisServicio - m.cantidad);
}
total += (tarifas[m.categoria] ?? 0) * cobrables;
}
return Math.round(total * 100) / 100;
}
function tasaDeResolucion(conversaciones) {
if (conversaciones.length === 0) return 0;
const resueltas = conversaciones.filter(c => c.estado === "resuelta").length;
return Math.round((resueltas / conversaciones.length) * 1000) / 1000;
}
function resumenMetricas(eventos) {
const n = tipo => eventos.filter(e => e.tipo === tipo).length;
const resp = eventos.filter(e => e.tipo === "respuesta");
const conversaciones = n("conversacion_iniciada");
const enviadas = n("plantilla_enviada");
const redondear = (x, d) => Math.round(x * 10 ** d) / 10 ** d;
return {
conversaciones,
tasaResolucion: conversaciones ? redondear(n("conversacion_resuelta") / conversaciones, 3) : 0,
pedidosPorConversacion: conversaciones ? redondear(n("pedido") / conversaciones, 3) : 0,
tiempoRespuestaMedioMs: resp.length ? Math.round(resp.reduce((s, e) => s + e.ms, 0) / resp.length) : 0,
tasaLectura: enviadas ? redondear(n("plantilla_leida") / enviadas, 3) : 0,
bajas: n("baja"),
};
}