[🐛] error con encadenamiento de addAnswer, fallback con addAction (1.2.3, 1.2.4, 1.2.5, 1.2.7-alpha.5) y conexión con meta (1.2.6)
¿Que versión estas usando?
v2
¿Sobre que afecta?
Flujo de palabras (Flow)
Describe tu problema
Error con el addAnswer
Me pasa algo similar pero sin nisiquiera pasar por un goToFlow, en este caso tome el ejemplo de la documentación oficial: https://builderbot.vercel.app/en/add-functions#message-with-callback y solo le agregué un if para saber si el fallBack funcionaba.
En efecto funciona, pero luego del mensaje del flowDynamic no salta al siguiente addAnswer.
const mainFlow2 = addKeyword('pepe')
.addAnswer('Hi!, Do you know 4+4?', {capture:true}, async (_, {flowDynamic, fallBack}) => {
const sum = 4 + 4
const value = _.body;
if (Number(value) !== sum) {
return fallBack(`No, ${value} no es correcto, intenta de nuevo`);
}
await flowDynamic(`Total: ${sum} pero tu dijiste ${value}`)
})
.addAnswer('¿Quieres saber algo más?', {capture:true}, async (_, {flowDynamic, fallBack}) => {
const value = _.body;
if (value !== 'si' && value !== 'no') {
return fallBack(`No entiendo, por favor responde con "si" o "no"`);
}
await flowDynamic(`Tu respuesta fue ${value}`)
})
.addAction(async (_, {flowDynamic}) => {
await flowDynamic(`Other message`)
})
Este es el resultado, en lugar de saltar a la pregunta '¿Quieres saber algo más?', se finaliza el flujo.
En un ejemplo más complejo se puede ver el mismo comportamiento:
import { addKeyword, EVENTS } from '@builderbot/bot';
import { ApiResponse } from '../services/api/interfaces/api-response.interface';
import { ClientRequestDto, ClientResponseDto } from '../services/api/dto/client.dto';
import { ApiService } from '../services/api/api.service';
const apiService = new ApiService();
const registerFlow = addKeyword(EVENTS.ACTION)
.addAnswer('👋 Hola, parece que eres nuevo por aquí.\n\n¿Quieres registrarte?',
{ capture: true,
buttons: [
{ body: 'Si, quiero'},
{ body: 'No, gracias'}
]
},
async (ctx, ctxFn) => {
if(ctx.body === 'Si, quiero'){
await ctxFn.flowDynamic('✅ ¡Perfecto! Vamos a comenzar con tu registro 📝');
}else if(ctx.body === 'No, gracias'){
return ctxFn.endFlow('❌ El registro fue cancelado, puedes volver a escribirle al bot para registrarte cuando lo desees');
}else{
return ctxFn.fallBack('⚠️ Por favor, escoge una de las opciones');
}
}
)
.addAnswer('📝 ¿Cuál es tu nombre completo?',
{ capture: true },
async (ctx, ctxFn) => {
await ctxFn.flowDynamic(`👤 Gracias ${ctx.body}!`);
await ctxFn.state.update({ firstName: ctx.body });
}
)
.addAnswer('📧 ¿Cuál es tu correo electrónico?',
{ capture: true },
async (ctx, ctxFn) => {
// Validar formato de correo electrónico
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(ctx.body)) {
return ctxFn.fallBack('⚠️ Por favor, ingresa un correo electrónico válido');
}
await ctxFn.state.update({ email: ctx.body });
}
)
.addAnswer('🔢 ¿Cuál es tu número de identificación? solo escribe números',
{ capture: true },
async (ctx, ctxFn) => {
// Validar que sea un número de teléfono (simplificado)
const identificacionRegex = /^\d{7,15}$/;
const identificationNumber = ctx.body.replace(/\D/g, '');
if (!identificacionRegex.test(identificationNumber)) {
return ctxFn.fallBack('⚠️ Por favor, ingresa un número de teléfono válido (solo números)');
}
const state = ctxFn.state.getMyState();
// Preparar objeto para enviar a la API
const clientData: ClientRequestDto = {
VcIdentificationNumber: identificationNumber,
VcFirstName: state.firstName,
VcEmail: state.email,
VcPhone: ctx.body,
VcFirstLastName: ' ',
};
try {
// Llamar a la API para registrar el cliente
const response = await apiService.request<ApiResponse<ClientResponseDto>>(
'/clients',
'POST',
clientData
);
if(response.status === 'success'){
await ctxFn.flowDynamic('🎉 ¡Excelente! Tus datos ya fueron registrados, ya puedes comenzar a utilizar nuestros servicios');
}else{
await ctxFn.flowDynamic('❌ Lo siento, hubo un error al registrar tus datos. Por favor, intenta nuevamente más tarde.');
}
} catch (error) {
console.error('Error al registrar cliente:', error);
await ctxFn.flowDynamic('❌ Lo siento, hubo un error al registrar tus datos. Por favor, intenta nuevamente más tarde.');
}
}
);
export { registerFlow };
Resultado con versiones: 1.2.3, 1.2.4 , 1.2.5 y 1.2.7-alpha.5 En este caso no pasa al siguiente bloque:
Resultado con versiones: 1.2.6 En este caso, aunque se logra conectar al provider de meta, no envia mensajes, no muestra nada en consola por lo que es imposible hacer un debug.
Error con el fallBack con addAction
Buscando alternativas, se intentó usar addAction en lugar de addAnswer pero el comportamiento es igual errático En este caso luego de un goToFlow, el encadenamiento de los mensajes funciona, pero el fallback no realiza las validaciones correctas:
import { addKeyword, EVENTS } from '@builderbot/bot';
import { ApiResponse } from '../services/api/interfaces/api-response.interface';
import { ClientRequestDto, ClientResponseDto } from '../services/api/dto/client.dto';
import { ApiService } from '../services/api/api.service';
const apiService = new ApiService();
const registerFlow = addKeyword(EVENTS.ACTION)
.addAnswer('👋 Hola, parece que eres nuevo por aquí.\n\n¿Quieres registrarte?\n\nResponde *SI* o *NO* (también puedes usar 1 o 0)',
{ capture: true }
)
.addAction(async (ctx, ctxFn) => {
// Convertir respuesta a minúsculas para facilitar validación
const response = ctx.body.toLowerCase();
// Validar que la respuesta sea una de las opciones permitidas
if (response === 'si' || response === '1') {
await ctxFn.flowDynamic('✅ ¡Perfecto! Vamos a comenzar con tu registro 📝');
} else if (response === 'no' || response === '0') {
return ctxFn.endFlow('❌ El registro fue cancelado, puedes volver a escribirle al bot para registrarte cuando lo desees');
} else {
return ctxFn.fallBack('⚠️ Por favor, responde solo con *SI* o *NO* (o 1 o 0)');
}
})
.addAnswer('📝 ¿Cuál es tu nombre completo?',
{ capture: true }
)
.addAction(async (ctx, ctxFn) => {
await ctxFn.flowDynamic(`👤 Gracias ${ctx.body}!`);
await ctxFn.state.update({ firstName: ctx.body });
})
.addAnswer('📧 ¿Cuál es tu correo electrónico?',
{ capture: true }
)
.addAction(async (ctx, ctxFn) => {
// Validar formato de correo electrónico
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(ctx.body)) {
return ctxFn.fallBack('⚠️ Por favor, ingresa un correo electrónico válido');
}
await ctxFn.state.update({ email: ctx.body });
})
.addAnswer('🔢 ¿Cuál es tu número de identificación? solo escribe números',
{ capture: true }
)
.addAction(async (ctx, ctxFn) => {
// Validar que sea un número de teléfono (simplificado)
const identificacionRegex = /^\d{7,15}$/;
const identificationNumber = ctx.body.replace(/\D/g, '');
if (!identificacionRegex.test(identificationNumber)) {
return ctxFn.fallBack('⚠️ Por favor, ingresa un número de teléfono válido (solo números)');
}
const state = ctxFn.state.getMyState();
// Preparar objeto para enviar a la API
const clientData: ClientRequestDto = {
VcIdentificationNumber: identificationNumber,
VcFirstName: state.firstName,
VcEmail: state.email,
VcPhone: ctx.body,
VcFirstLastName: ' ',
};
try {
// Llamar a la API para registrar el cliente
const response = await apiService.request<ApiResponse<ClientResponseDto>>(
'/clients',
'POST',
clientData
);
if(response.status === 'success'){
await ctxFn.flowDynamic('🎉 ¡Excelente! Tus datos ya fueron registrados, ya puedes comenzar a utilizar nuestros servicios');
}else{
await ctxFn.flowDynamic('❌ Lo siento, hubo un error al registrar tus datos. Por favor, intenta nuevamente más tarde.');
}
} catch (error) {
console.error('Error al registrar cliente:', error);
await ctxFn.flowDynamic('❌ Lo siento, hubo un error al registrar tus datos. Por favor, intenta nuevamente más tarde.');
}
});
export { registerFlow };
Este es el resultado:
Reproducir error
No response
Información Adicional
No response
Hola, tambien estoy revisando, usando la version v22.0 de meta, no me responde el bot, tal vez sea por la version o alguna actualización. Solo cree el proyecto usando builderbot por defecto, asigne las credenciales de meta y configure el webhook, todo bien (en consola me sale http server on, y que si esta conectado provider) pero no me responde el bot solo estoy usando el "welcomeFlow" que viene por defecto.
Solo cree el proyecto usando builderbot por defecto, asigne las credenciales de meta y configure el webhook, todo bien (en consola me sale http serv
Me sucedía exactamente lo mismo con la versión 1.2.6. Cuando ví que desde Postman me funcionaba el hello_word, supe que el problema era por una actualización y que la solución era borrar la carpeta node_modules, bajar las versiones a 1.2.2 o 1.2.3 o 1.2.4 ya funciona el provider de Meta de forma correcta; pero igual las interacciones de flujos anidados no me funcionó para ninguna de las versiones que probé y eso que lo hice con ejemplos de la documentación inicial.
Te comparto la conversación de discord: https://discord.com/channels/915193197645402142/1360308708655235273/1370823579327008849
Buenas puedes probar la versión 1.2.7 latest ya corregimos un bug que se tenia con meta que no respondía, ya vuelve a estar funcional ;)
Buenas puedes probar la versión 1.2.7 latest ya corregimos un bug que se tenia con meta que no respondía, ya vuelve a estar funcional ;)
Hola, te puedo confirmar que en la versión 1.2.7 ya no se presenta el error de conexión con el proveedor de Meta. Pero, me falló el goToFlow, mira el ejemplo:
import { addKeyword, EVENTS } from "@builderbot/bot";
import { ApiService } from '../services/api/api.service';
import { ClientResponseDto } from '../services/api/dto/client.dto';
import { ApiResponse } from '../services/api/interfaces/api-response.interface';
import { registerFlow } from "./register.flow";
const apiService = new ApiService();
const mainFlow = addKeyword(EVENTS.WELCOME)
.addAction(
async (ctx, { flowDynamic, gotoFlow }) => {
console.log(JSON.stringify(ctx));
const numero = ctx.from;
try {
const response = await apiService.request<ApiResponse<ClientResponseDto>>(
'/clients/cellphone/:numero',
'GET',
null,
{ numero }
);
if(response.status === 'success'){
const clientData = response?.data;
console.log('Cliente encontrado:', JSON.stringify(clientData));
await flowDynamic(`Hola ${clientData.VcFirstName}, bienvenido de nuevo!`);
}else{
return gotoFlow(registerFlow);
}
} catch (error: any) {
console.error('Cliente no encontrado: ', error.message);
return gotoFlow(registerFlow); <-- AQUI DEBERÍA IRSE AL registerFlow
}
}
);
const registerFlow = addKeyword(EVENTS.ACTION)
.addAnswer('👋 Hola, parece que eres nuevo por aquí.\n\n¿Quieres registrarte?\n\nResponde *SI* o *NO* (también puedes usar 1 o 0)',
{ capture: true }
)
.addAction(async (ctx, ctxFn) => {
// Convertir respuesta a minúsculas para facilitar validación
const response = ctx.body.toLowerCase();
// Validar que la respuesta sea una de las opciones permitidas
if (response === 'si' || response === '1') {
await ctxFn.flowDynamic('✅ ¡Perfecto! Vamos a comenzar con tu registro 📝');
} else if (response === 'no' || response === '0') {
return ctxFn.endFlow('❌ El registro fue cancelado, puedes volver a escribirle al bot para registrarte cuando lo desees');
} else {
return ctxFn.fallBack('⚠️ Por favor, responde solo con *SI* o *NO* (o 1 o 0)');
}
})
En este caso si el API responde que el no está el número creado en la tabla de clientes se va por el catch y lo envía al registerFlow. Pero no dispara el flujo, en el WhatsApp se vé así:
Y en la consula pasa por el console.error, pero no hace nada más:
Message Payload: { body: 'hola', from: '573333333333' }
{"type":"text","from":"573057467347","to":"15551468852","body":"hola","name":"Camilo","pushName":"Camilo","message_id":"wamid.HBgMNTczMDU3NDY3MzQ3FQIAEhgWM0VCMDM5QTJCRTZCMkM2QUEzMTBEMwA=","timestamp":"1747281899","host":"undefined"}
query: SELECT "ClientEntity"."Id" AS "ClientEntity_Id", "ClientEntity"."vc_identification_number" AS "ClientEntity_vc_identification_number", "ClientEntity"."vc_phone" AS "ClientEntity_vc_phone", "ClientEntity"."vc_nick_name" AS "ClientEntity_vc_nick_name", "ClientEntity"."vc_first_name" AS "ClientEntity_vc_first_name", "ClientEntity"."vc_second_name" AS "ClientEntity_vc_second_name", "ClientEntity"."vc_first_last_name" AS "ClientEntity_vc_first_last_name", "ClientEntity"."vc_second_last_name" AS "ClientEntity_vc_second_last_name", "ClientEntity"."vc_email" AS "ClientEntity_vc_email", "ClientEntity"."created_at" AS "ClientEntity_created_at", "ClientEntity"."updated_at" AS "ClientEntity_updated_at" FROM "Client" "ClientEntity" WHERE (("ClientEntity"."vc_phone" = $1)) LIMIT 1 -- PARAMETERS: ["573333333333"]
API Request Error: {
"status": "error",
"message": "No customer found with the phone number",
"errors": [
{
"code": "NUMBER_DOES_NOT_EXIST",
"message": "No customer found with the phone number 573333333333",
"field": "cellphone"
}
]
}
Error during API request to http://localhost:3000/api/v1/clients/cellphone/573333333333: {
status: 'error',
message: 'No customer found with the phone number',
errors: [
{
code: 'NUMBER_DOES_NOT_EXIST',
message: 'No customer found with the phone number 573333333333',
field: 'cellphone'
}
]
}
Cliente no encontrado: No customer found with the phone number
Request failed with status code 400
Lo que me causa curiosidad es que yo en el servicio estoy devolviendo un 409, y en ningún lado devolvemos el mensaje "Request failed with status code 400" Es como si fuera un error interno de la librería que lo propagara y lo hiciera detener, pero lo curioso es que en las versiones anteriores, aunque imprimía el mensaje del servicio y el console.error, se dirigía al registerFlow.
Me podrías confirmar si estoy haciendo algo mal, o es un bug de la versión 1.2.7, mil gracias por la rapidez de la primera respuesta.
¿Alguna novedad sobre esta ISSUE?