Cree Una Aplicación de Mensajería Móvil Inteligente con Twilio Frontline

March 28, 2022
Revisado por
Ben Datlen
Twilion

Limitación de nuevas ventas de Frontline

A partir del 9 de febrero de 2023, el acceso está limitado solo a los clientes existentes de Twilio Frontline. Consulte el artículo de nuestro centro de ayuda para obtener detalles completos sobre la disponibilidad del servicio.

 

 

Interactuar con clientes usando su canal de preferencia siempre es difícil. Las empresas a menudo necesitan soluciones técnicas para mediar entre los empleados y los canales privados de sus clientes. Twilio Frontline es una solución de software configurable que permite que cualquier persona tenga acceso a la mensajería de sus clientes (SMS. WhatsApp) en dispositivos móviles.

Cuando un cliente quiere comunicarse con una empresa, el primer paso es encontrar a la persona adecuada. Con Frontline, podemos integrar una lógica dada si nuestro back-end ya sabe con quién habla normalmente esta persona, pero ¿no sería más agradable dejar que la aplicación primero pregunte con quién se quiere hablar?  

Este blog explica cómo integrar un bot que preguntará a los clientes con qué experto se necesita conectar. Mejoraremos la configuración inicial de Frontline ( Figura 1) con un bot que administrará los mensajes como parte de una conversación independientemente a la aplicación Frontline ( Figura 2)

Smart clienteling with Twilio Frontline
Figure 1
Smart clienteling bot with Twilio Functions and Frontline
Figure 2

Requisitos previos

  1. Necesitará una cuenta de Twilio. Si aún no tiene una, rellena este formulario para obtener una cuenta gratis.
  2. Aprovecharemos Frontline  como aplicación de clientela sólo para la parte de mensajería.
  3. Desarrollaremos las integraciones de Frontline con la base de datos y con un bot utilizando el CLI de Twilio. Puede instalarlo desde aquí.
  4. Una base de datos de CRM. Para nuestro ejemplo, usaremos la plataforma Heroku y usaremos CMS Strapi para crear una base de datos de contactos básica.

 

Esta aplicación se configurará para admitir sólo mensajería con SMS o WhatsApp como canales. Es sencillo adaptarlo para admitir Chat y Voz: siga los pasos aquí para ello.

Configurar Strapi para la base de datos de contactos

Frontline se puede configurar para acceder a una base de datos de contactos que contendrá los detalles de los clientes que ya hemos registrado, junto con sus preferencias de comunicación y canales de contacto (por ejemplo, SMS, correo electrónico). Usaremos Strapi como nuestra base de datos fácil de editar, pero podría usar cualquier otra base de datos de CRM.

Configuración de Strapi

Siga los pasos de este blog para crear una instancia de Strapi en producción que actuará como su base de datos de CRM. En nuestro blog, la aplicación Heroku se llama expert-hub y, por lo tanto, está alojada en la siguiente URL https://expert-hub.herokuapp.com

Para agregar clientes a esta base de datos, deberá ejecutar Strapi en el entorno de desarrollo localmente para crear las Tablas que albergarán los perfiles de clientes. Necesitará las siguientes tablas:

  • Customers: contendrá la información que Frontline mostrará sobre sus clientes siguiendo esta estructura JSON.
  • Permission and Role:  que deberían venir con Strapi y ayudar a administrar el acceso del usuario al CMS de Strapi.
  • Token: que crearemos para almacenar tokens de acceso para la cuenta de servicio que Frontline utilizará para acceder a los datos del Cliente.
  • User: que contendrá la cuenta de servicio que usará Frontline.

Los pasos a seguir son:

$ npm run develop
  1. Abra su navegador en http://localhost:1337/admin.
  2. Inicie sesión como administrador (cree el usuario si aún no lo ha creado).
  3. Haga clic en "Content-Type Builder" y luego en "+ Create new component" llame al componente Channels. Agregue dos campos de texto: type y value.
  4. Ahora haga clic en "+ Create a collection type" y llámelo Customers. Agregue los siguientes campos:

    display_name (Text)
    customer_id (Number)
    avatar (Text)
    channels 

    Strapi no expondrá la lista de clientes bajo el end-point de la API /customers. Para acceder a la API de forma segura, implementaremos la autenticación básica (usuario + token) siguiendo la guía de Strapi.

    Una vez en producción, deberá crear las cuentas de usuario y desarrollar la autenticación siguiendo esta guía. Profundizaremos en los detalles cuando configuremos la aplicación Frontline más adelante.
  5. Suba su aplicación Strapi a producción:

    git push heroku  (si ha seguido la guía de Heroku)
  6. Abra https://expert-hub.herokuapp.com/admin, cree un usuario administrador y agregue las entradas que desea que sean sus Customer Collection Types. Agregaremos las tres entradas que se ven en el  bloque de código siguiente y usaremos la entrada de María Sánchez para ingresar nuestro propio número de teléfono (para ayudar a probar el enrutamiento más adelante).
const clients = [
   {
     display_name: "Maria Sanchez",
     customer_id: "1",
     canales: [{ type: "sms", value:"+336795*****" }],
     avatar: "https ://cinnabar-albatross-5407.twil.io/assets/face2.jpeg"
   },
   {
     display_name: "Jo Anne",
     customer_id: "2",
     canales: [{ tipo: "sms", valor:"+447491 *****" }],
     avatar: "https://cinnabar-albatross-5407.twil.io/assets/face3.jpeg"
   },
   {
     display_name: "Susan Smith",
     customer_id: "3",
     canales: [{ tipo: "whatsapp", valor:"+55219895*****" }],
     avatar: "https://cinnabar-albatross-5407.twil.io/assets/face4.jpeg"
   },
 ];

Frontline

Configuración

Comience integrando Frontline con su proveedor de identidad para habilitar el inicio de sesión en la aplicación móvil.

Luego, descargue la aplicación Twilio Frontline desde AppStore o Google Play.

Seleccione "Enrutamiento personalizado" como una forma de dirigir los mensajes que ingresan. Crearemos el servicio que manejará el enrutamiento más tarde (puede dejarlo vacío o agregar una URL sin valor por ahora).

Inbound conversations routing

Usaremos el  CRM callback y el Outgoing Conversations Callback. La siguiente sección explicará cómo implementaremos estos servicios usando Twilio Serverless Functions.

Agregue números habilitados para SMS al servicio de mensajería de Frontline

Para el servicio de mensajería de Frontline, deberá comprar números de SMS y agregarlos en la consola.

Luego, seleccione el "Default Conversations Service" en "Messaging Services" y agregue los números comprados dentro del "Sender Pool".

Necesitará diferentes números dependiendo de múltiples factores. Si tiene usuarios de SMS en diferentes países, es mejor seleccionar un número por país. Si necesita que un usuario de SMS discuta en paralelo con dos o más trabajadores, necesitará tantos números como discusiones paralelas activas desee admitir.

Edit the sending pool in conversations

Los servicios que coordinan las comunicaciones de Frontline

Ahora, es el momento de desarrollar todas las funciones de back-end necesarias para nuestra aplicación.

Si no quiere esperar: el código de esta aplicación está disponible en https://github.com/teresan/twilio-frontline-expert-hub.

Este repositorio tiene un servicio frontline en la carpeta serverless/. Explicaremos paso a paso cómo crear cada una de esas funciones, pero si lo prefiere, puede reutilizar el código completo disponible en el repositorio.

$ npm install twilio-cli -g

Instale el Twilio Serverless Toolkit

# Desinstale el Serverless Toolkit existente
twilio plugins:remove @twilio-labs/plugin-serverless

# Instale la última versión de los complementos de Twilio de Serverless Toolkit
twilio plugins:install @twilio-labs/plugin-serverless@latest

Asegúrese de que su Twilio CLI se conecta a la cuenta de Twilio Frontline agregando y/o usando el perfil correcto y las credenciales correctas:

twilio profiles:list
 
# Inicializar un nuevo proyecto con el nombre frontline-service
twilio serverless:init frontline-service

# Cambiar a ese nuevo directorio de proyectos
cd frontline-service

Esto creará un proyecto con una estructura predefinida (ver documentación para más detalles). Elimine todos los archivos en la carpeta  "assets" y en la carpeta "functions".

Edite el archivo .env y agregue lo siguiente, asegurándose de editar los valores para que coincidan con los de su entorno:

DB_URL=https://expert-hub.herokuapp.com
DB_ID=frontline_app
DB_PASS=XXXX 
FRONTLINE_PROXY_NUMBER=+4478285 **** 
FRONTLINE_PROXY_NUMBER_UK=+4478285**** 
FRONTLINE_PROXY_NUMBER_FR=+337575**** 
FRONTLINE_MESSAGING_SERVICE=MGaabc851414764527f5cd13542xxxxxx

DB_URL es el URI de su base de datos de Strapi que contiene la lista de clientes.

DB_IDes el ID de la base de datos que creó en Strapi y

DB_PASSes la contraseña para conectarse a la base de datos.

FRONTLINE_MESSAGING_SERVICE contiene el SID del servicio de mensajería que contiene sus números.

Cada FRONTLINE_PROXY_NUMBER_XX contiene cada uno de los remitentes de su servicio de mensajes de conversación utilizados en nuestro código: uno por país.

Cree el CRM callback para recopilar información de cliente desde Strapi.

La aplicación Frontline necesita recopilar datos del cliente para completar la pestaña “My Customers”. Crearemos una función que consultará estos datos en Strapi y los pondrá a disposición de Frontline.

Cree una función llamada crm.js en frontline-service, en la carpeta functions. Crearemos una función interna llamada fetchAll que usará axios para conectarse a Strapi y listar todos los clientes de la base de datos. Esta función accederá a las variables de entorno DB_URL, que contiene la URL de la base de datos de Strapi, DB_ID con el identificador de la base de datos y DB_PASS que contiene la contraseña para acceder a la base de datos.

async function fetchAll(context) {
 const axios = require("axios").create({
   baseURL: context.DB_URL
 });
   const token = await axios.post('/auth/local', {
     identifier: context.DB_ID,
     password: context.DB_PASS,
   });
   const response = await axios.get('/customers', {
     headers: {
       Authorization:
         'Bearer ' + token.data.jwt,
     },
    
   });
   return response.data;
}

La función fetchAll será llamada por la función handler , que será llamada por Frontline cada vez que la aplicación necesite cargar datos de clientes. Cuando se llama al handler , la respuesta contendrá un atributo llamado location que contendrá uno de los siguientes valores, indicando qué datos se deben devolver:

  • GetCustomerDetailsByCustomerId: esto significa que Frontline está tratando de obtener los detalles de un cliente individual a través de su customer_id;
  • GetCustomersList: en este caso, Frontline necesita obtener la lista de todos los clientes.

Así es como se ve la función handler :

exports.handler = async function (context, event, callback) {
 let customers = await fetchAll(context);
 switch (event.Location) {
   case 'GetCustomerDetailsByCustomerId':
     callback(null, { objects: { customer: customers[event.CustomerId - 1] } });
     break;
   default:
     callback(null, { objects: { customers: customers } });
     break;
 }

En crm.js, además, necesitamos implementar una función de soporte que será utilizada más adelante por uno de los webhooks de Conversations. Esta función consultará los datos del cliente por número de teléfono. Esto es útil cuando, por ejemplo, se crea una conversación nueva y necesitamos agregar datos de CRM a un participante en la conversación. Llamaremos a esta función fetchByPhoneNumber y esta función también usará fetchAll.

exports.fetchByPhoneNumber = async (phoneNumber, context) => {
   const customers = await fetchAll(context);
   return customers.filter(e => e.channels[0].value == phoneNumber)[0];
}

En la función anterior, devolveremos el primer cliente encontrado en el CRM con el número de teléfono proporcionado como parámetro.

Cree las funciones que coordinarán el enrutamiento y el bot

Cada conversación en Frontline es proporcionada por la API de conversaciones de Twilio; esto significa que la API REST de Conversations se puede usar para administrar las conversaciones de Frontline. Frontline también aprovecha los webhooks de Conversations para monitorear eventos en conversaciones y ejecutar cualquier acción cuando ocurren estos eventos.  

En este ejemplo, un usuario final enviará un SMS a un número asociado a Frontline y interactuará con un bot antes de conectarse con un trabajador de Frontline.

Implementaremos la interacción del bot mediante las Twilio functions y luego configuraremos otra función como un webhook de conversaciones que invocará al bot.

El Servicio del bot

Cree una función llamada speakToBot.js en el proyecto frontline-service. Tenga en cuenta que speakToBot.js debe estar en la carpeta functions

El nuestro es un bot muy simple que pregunta si el usuario desea conectarse con un trabajador de Frontline ya conocido proporcionando su correo electrónico o con cualquier otro agente disponible. Este bot se puede mejorar conectándose a una plataforma conversacional de IA, por ejemplo, Google DialogFlow, IBM Watson Assistant o Microsoft LUIS.

exports.speakToBot = async (message) => {
 if (message.includes('@')) {
   return {message: `Bear with us while we connect you to ${email}`, route: message.toLowerCase()} ;
 } else if (message.toLowerCase().includes('agent')) {
   return {message: `Bear with us while we connect you to someone`, route: 'agent'};
 } else {
   return {
     message: `If you know the email of the agent you want to speak, please write:\n`
       + `person@email.com\n`
       + `Otherwise write:\n`
       + `AGENT\n`
       + `and we will connect you with an expert.`,
     route: 'no'} ;
 }
}

La función devuelve un JSON compuesto por un atributo de mensaje y un atributo de ruta. Esto se utilizará más adelante para tomar decisiones de enrutamiento.

Pre-event webhook

Cree otra función llamada pre-event.js para que sea el webhook previo al evento de Conversaciones que reacciona al evento onMessageAdd . La idea es que después de que un usuario final envíe el primer SMS, el webhook invocará al bot.

Primero, verificamos el EventType y la fuente del mensaje, que, en nuestro ejemplo, será SMS. En caso de que los usuarios finales se unan a una conversación a través del chat, esta condición debe ser diferente. En caso de que estas dos condiciones no se cumplan, este webhook debería regresar con el callback()

exports.handler = async function (context, event, callback) {

 const twilio = context.getTwilioClient();

 if (event['EventType'] === 'onMessageAdd' && event['Source'] != 'SDK') {

 } else {
   callback(null, 'All good. Let the conversation flow');
 }

Para obtener una lista completa de los parámetros que se envían al pre-event webhook en Conversations, consulte esta página en la documentación de Twilio.

Para asegurarnos de que este sea el comienzo de la conversación, usamos la API de Conversations para verificar si el participante es el único agregado a la conversación en este punto. Si ese no es el caso, finalizamos la ejecución de la función llamando a callback() para enviar una respuesta.

   let participants = await twilio
     .conversations
     .conversations(event['ConversationSid'])
     .participants
     .list();

   if (participants.length > 1) {
     callback(null, 'All participants added');
   } else {
     //call the bot service here

   }
}

Ahora, si sólo tenemos el primer participante en la conversación, es hora de activar el bot dentro de la declaración else en el código anterior. El bot es simplemente otra función de Twilio en el mismo servicio que creamos en el paso anterior de este blog. El bot recibe como parámetro el mensaje enviado por el usuario final: el atributo Body en el evento. Luego, la respuesta del bot se envía a la misma conversación y, por lo tanto, al usuario final, a través de la API de Conversations.

     const botService = require(Runtime.getFunctions()['speakToBot'].path);
     let reply = await botService.bot(event['Body']);

     await twilio
       .conversations
       .conversations(event['ConversationSid'])
       .messages
       .create({ author: 'system', body: reply.message });

La respuesta del bot es un JSON que también incluye el atributo route que se utilizará para las decisiones de enrutamiento.

El formato de respuesta del bot:

{
 message: "agent|an email worker@email.com|any string",
 route: "agent|email|no"
}

Después de responder al usuario, el atributo route se usa para decidir cuál es el siguiente paso a realizar en esta conversación:

  • route es no: no se debe enrutar, el bot está interactuando con el usuario final
  • route es email: indica que el usuario quiere hablar con un trabajador en específico
  • route es AGENT: el usuario final quiere chatear con cualquier trabajador disponible. AGENT; aquí se distingue entre mayúsculas y minúsculas.

Cuando route es no, no hay nada que hacer en el webhook previo al evento.

     if (reply.route == 'no') {
       callback(null, 'No routing. Bot interacting with participant');
     } else if (reply.route.includes('@')) {
        // connect to a worker here

     }

Cuando  route es email, el  message  en la respuesta del bot contiene el correo electrónico del trabajador al que el usuario le gustaría estar conectado. Todo lo que tenemos que hacer es agregar este correo electrónico como la identidad de un participante y agregarlo a la conversación. Para conectarse con un trabajador de Frontline, asegúrese de que el correo electrónico proporcionado coincida exactamente con el correo electrónico de un trabajador de Frontline; la comparación distingue entre mayúsculas y minúsculas.

   } else if (reply.route.includes('@')) {
selectedWorker = reply.route;
await twilio.conversations
             .conversations(event['ConversationSid'])
             .participants
             .create({ identity: selectedWorker });
   }

Cuando route es agent, necesitaremos encontrar un trabajador de Frontline disponible para conectarnos con el usuario final y, para eso, necesitamos usar TaskRouter.

Integración con TaskRouter

Frontline utiliza Twilio TaskRouter como motor para administrar trabajadores - o workers - y reglas de enrutamiento. Los Frontline workers son workers de TaskRouter. Cuando instale Frontline en su cuenta de Twilio, se creará un nuevo espacio de trabajo de TaskRouter denominado "Frontline" . Para encontrar un worker para nuestra Frontline conversations usaremos la API de TaskRouter para consultar los workers disponibles.

Primero, cree dos variables de entorno en el archivo .env con datos de TaskRouter:

FRONTLINE_WORKSPACE=<agregue aquí su SID de Frontline Workspace>
FRONTLINE_DEFAULT_POOL_QUEUE=<agregue aquí su SID de cola predeterminado>

Luego, cree una función: puede ser un archivo nuevo o, para hacerlo más fácil, simplemente agregue un nuevo método al final pre-event.js – para enumerar todos los trabajadores disponibles en la cola:

let getLongestIdleAvailableWorker = async (context) => {
   const availableWorkers = await context.getTwilioClient().taskrouter
     .workspaces(context.FRONTLINE_WORKSPACE)
     .workers
     .list({taskQueueSid: context.DEFAULT_POOL_QUEUE_SID, available: true});

Esto le genera una lista de workers disponibles, y desde aquí puede seleccionar cualquiera de ellos para conectarse a la conversación de Frontline. Si desea obtener el worker inactivo más largo, deberá ordenar availableWorkers por lastAcceptedTaskTime.

 availableWorkers.sort((a, b) => (
   JSON.parse(a.attributes).lastAcceptedTaskTime
   > JSON.parse(b.attributes).lastAcceptedTaskTime)
   ? 1 : -1);
 const longestIdle = availableWorkers[0];

longestIdle es su worker seleccionado. Para mantener la coherencia, debemos actualizar el lastAcceptedTaskTime para la hora actual antes de devolver el trabajador actualizado al método principal en el pre-event webhook.

   let attr = JSON.parse(longestIdle.attributes);
   attr.lastAcceptedTaskTime = new Date().getTime();
   const updatedWorker = await context.getTwilioClient().taskrouter
     .workspaces(context.FRONTLINE_WORKSPACE)
     .workers(longestIdle.sid)
     .update({attributes: JSON.stringify(attr)});
  return updatedWorker;
}

Con un worker seleccionado, todo lo que necesita hacer es agregar este trabajador como participante en la conversación de Frontline. Volviendo a la implementación de pre-event.js, esto es lo que tenemos hasta ahora:

     if (reply.route == 'no') {
       callback(null, 'No routing. Bot interacting with participant');
     } else if (reply.route.includes('@')) {
       selectedWorker = reply.route;
       await twilio.conversations
         .conversations(event['ConversationSid'])
         .participants
         .create({ identity: selectedWorker });
     }

Agregue una declaración else a la condición anterior y todo lo que necesita hacer es llamar a nuestra función getLongestIdleAvailableWorker para recuperar un worker y luego agregar el worker a la conversación dentro de la declaración else anterior.

} else if (reply.route == 'AGENT') {
  selectedWorker = await getLongestIdleAvailableWorker(context);
  await twilio.conversations
         .conversations(event['ConversationSid'])
         .participants
         .create({ identity: selectedWorker });
}

También puede usar el webhook pre-event para establecer un nombre descriptivo para la conversación que será visible para el trabajador en la aplicación Frontline y la notificación de alerta. En el siguiente ejemplo, buscamos el nombre del participante usando fetchByPhoneNumber en crm.jsy lo usamos como el nombre descriptivo de la conversación.

  const crm = require(Runtime.getFunctions()['crm'].path);  
  let participant = await crm.fetchByPhoneNumber(event['Author'], context);

  if (participant.display_name) {
    await twilio
      .conversations
      .conversations(event['ConversationSid'])
      .update({
        friendlyName: `${participant.display_name}`
      });
  }

Aquí está la implementación completa de su pre-event.js:

exports.handler = async function (context, event, callback) {
 const twilio = context.getTwilioClient();

 if (event['EventType'] === 'onMessageAdd' && event['Source'] != 'SDK') {

   let participants = await twilio
     .conversations
     .conversations(event['ConversationSid'])
     .participants
     .list();

   if (participants.length > 1) {
     callback(null, 'All participants added');

   } else {
     const bot = require(Runtime.getFunctions()['speakToBot'].path);
     let reply = await bot.speakToBot(event['Body']);

     await twilio
       .conversations
       .conversations(event['ConversationSid'])
       .messages
       .create({ author: 'system', body: reply.message });

     if (reply.route == 'no') {
       callback(null, 'No routing. Bot interacting with participant');
     } else if (reply.route.includes('@')) {
       selectedWorker = reply.route;
       await twilio.conversations
         .conversations(event['ConversationSid'])
         .participants
         .create({ identity: selectedWorker });
     } else {
       selectedWorker = await getLongestIdleAvailableWorker(context);

       await twilio.conversations
         .conversations(event['ConversationSid'])
         .participants
         .create({ identity: selectedWorker })
     }

     const crm = require(Runtime.getFunctions()['crm'].path);
     let participant = await crm.fetch(event['Author'], context);
     if (participant.display_name) {
       await twilio
         .conversations
         .conversations(event['ConversationSid'])
         .update({
           friendlyName: `${participant.display_name}`
         });
     }
     callback(null, event['ConversationSid']);
   }
 } else {
   callback(null, 'All good. Let the conversation flow');
 }
}

Webhook post-event

Usaremos un webhook posterior al evento, en el archivo post-event.js, para enriquecer la experiencia del participante final con datos del CRM, lo cual sólo puede hacerse en el evento de conversaciones onParticipantAdded en conversaciones entrantes, y para participantes que son usuarios finales (y no Frontline workers).

Para verificar si el participante que se agrega es un Frontline worker, verificamos el atributo 'Identity'. Tenga en cuenta que para los usuarios de chat, este atributo estará presente tanto para los usuarios de Frontline como para los usuarios externos y es posible que deba implementar una condición diferente. Como, en nuestro ejemplo, el usuario final se comunicará principalmente por SMS, bastará con comprobar este atributo.

Para filtrar las conversaciones entrantes, buscamos el atributo 'attributes' en el cuerpo del 'event', que está vacío para las conversaciones salientes.

exports.handler = async function (context, event, callback) {

 const response = new Twilio.Response();
 response.setStatusCode(200);

 if (event.Identity) {
     response.setBody('No need to query customer attributes: frontline user added');
     callback(null, response);
 }

 let emptyAttributes = !event.Attributes || ('{}' == event.Attributes);
 if (!emptyAttributes) { //attributes are loaded in outbound conversations
     response.setBody('No need to query customer attributes: outbound conversation');
     callback(null, response);
 }

Luego, para el evento onParticipantAdded , llamaremos al fetchByPhoneNumber dentro de crm.js que está disponible en el mismo servicio. Necesitamos recopilar el número de teléfono del cliente que está disponible en el atributo MessagingBinding.Address de este evento para pasarlo como parámetro a fetchByPhoneNumber.

 if ('onParticipantAdded' == event.EventType) { 
     const customerNumber = event['MessagingBinding.Address'];
     const crm = require(Runtime.getFunctions()['crm'].path);
     const crmCustomer = await crm.fetchByPhoneNumber(customerNumber, context);

     if (!crmCustomer) callback(`No crm record for ${customerNumber}`);

     //fetch conversation participant and update from crm
  }
    callback(null, response);
  }

Ahora, todo lo que tenemos que hacer es buscar al participante en la conversación usando la API de Conversations y actualizar este participante con los datos de nuestra consulta disponible en la variable crmCustomer.

Mire el participante de la conversación, usando ConversationSid y ParticipantSid, disponibles en el cuerpo del evento:

     const participant = await twilio.conversations
         .conversations(event.ConversationSid)
         .participants
         .get(event.ParticipantSid)
         .fetch()
         .catch(error => callback(error));

Capture los atributos JSON del participante actual y actualícelo con los datos en crmCustomer:

 const participantAttributes = JSON.parse(participant.attributes);
 const customerProperties = {
     attributes: JSON.stringify({
         ...participantAttributes,
         avatar: participantAttributes.avatar || crmCustomer.avatar,
         customer_id: participantAttributes.customer_id || crmCustomer.customer_id,
         display_name: participantAttributes.display_name || crmCustomer.display_name
     })
 };

Finalmente, use la API de conversations para dar al participante con los atributos recién actualizados y, al final de su función, responder.

  if (participant.attributes !== customerProperties.attributes) {
    await participant
      .update(customerProperties)
      .catch(error => callback(error));
   }

Enrutamiento

Frontline proporciona diferentes métodos de enrutamiento para administrar las conversaciones entrantes. El método de enrutamiento seleccionado se activa con cualquier conversación entrante inmediatamente después de que se cree el recurso de conversación. El backend de Frontline utiliza el enrutamiento para decidir si enrutar la conversación a un worker y cómo hacerlo. Puede encontrar más información sobre el enrutamiento de Frontline en estos documentos.

Para conectar un bot a una conversación entrante antes de que se enrute a un trabajador, debemos ignorar el enrutamiento estándar y solo enrutar a un trabajador específico una vez que finaliza la conversación con el bot. Esta es la razón por la que se implementó la lógica de enrutamiento en el webhook previo al evento.

Todavía necesita configurar el enrutamiento para Frontline, pero la implementación del enrutamiento no necesita incluir ninguna lógica de enrutamiento.

Cree un archivo routing.js en el proyecto frontline-service. Nuestra función de enrutamiento básicamente enviará un mensaje de bienvenida al usuario cuando reciba un mensaje.

exports.handler = async function (context, event, callback) {

 //no routing here, only bot welcome
 await context.getTwilioClient()
   .conversations
   .conversations(event['ConversationSid'])
   .messages
   .create({ author: 'system', body: 'Welcome to the Expert Hub. How can I help you?' });

 return callback(null, 200);
}

El autor del mensaje de bienvenida se establece como system solo para diferenciar a este usuario de un worker real una vez agregado a la conversación.

Implemente las funciones en producción

El siguiente paso es implementar todas las funciones creadas en su cuenta de Twilio, de modo que estén disponibles para la configuración de Frontline (lo hará más adelante en este documento).

Hasta ahora, hemos creado las siguientes funciones:

  • crm.js
  • speakToBot.js
  • pre-event.js
  • post-event.js
  • routing.js

Para simplificar, no creamos una función aquí para controlar las conversaciones salientes, pero puede encontrar nuestro código fuente completo para una función saliente, incluido el código completo para todas las funciones enumeradas anteriormente en este repositorio: https://github.com/teresan/twilio-frontline-expert-hub

En este proyecto estamos usando un paquete externo, axios, que debe agregarse a package.json para que su instancia implementada lo instale. Puede agregar esta dependencia a package.json ejecutando npm install en el frontline-service .

Para implementar todas las funciones en la cuenta de Twilio, ejecute:

twilio serverless :deploy –force

Este comando implementará frontline-service y todas sus funciones en su cuenta de Twilio. Tome nota de las urls de sus funciones, enumeradas en la salida de este comando, ya que las necesitará en el siguiente paso. El indicador –force fuerza la implementación sobre otro servicio con el mismo nombre si es necesario.

Configuración del callback

Vaya a la pestaña de Frontline en la consola de Twilio y seleccione “Manage” -> “Callbacks”. Añada la URL de callback de CRM  Frontline CRM con la url a la función /crm implementada . Siga esta guía si necesita más detalles.

En la configuración de enrutamiento (en  “Manage” -> “Routing”), seleccione "Custom routing"  y actualice la URL de callback (del "Custom routing") con la dirección de la función implementada; /routing . Siga esta guía si necesita más detalles.

Ahora, diríjase a la pestaña de Conversations en la consola para configurar los webhooks de Conversations. Seleccione “Manage” -> “Global webhooks” y proporcione las urls de

/pre-event y /post-event a los campos URL anterior al evento y URL posterior al evento, respectivamente. Siga esta guía para más información.

En la misma página, en el  Webhook Filtering, debemos seleccionar los tipos de eventos que escucharán los webhooks previos y posteriores al evento. Seleccione los siguientes eventos:

  • Pre-webhooks: onConversationAdd, onMessageAdd
  • Post-webhooks: onParticipantAdded

Y con esto, ¡ya está todo listo!

Probando la experiencia final 

¡Es hora de probar su bot integrado a Frontline! Usando su teléfono, envíe un SMS a uno de los números de proxy de Twilio, el correspondiente a su país, y comience a interactuar con el bot.

Chat experience with a Frontline bot

Después de proporcionar el correo electrónico del usuario Frontline  con el que desea conectarse, será enrutado y podrá continuar la conversación directamente con el trabajador (y ya no con el bot).

Connecting a bot to an expert

Cuando el worker Frontline recibe la conversación, tiene acceso al historial de la conversación con el bot. El trabajador también puede ver los datos de CRM que se agregaron al participante en el webhook posterior al evento (en este caso, la imagen del avatar y el nombre para mostrar) y tener una experiencia más personalizada.

Connecting a customer to an expert using a bot and Twilio Frontline

En caso de que escoja que le gustaría hablar con cualquier agente, se conectará con el worker Frontline disponible e inactivo por más tiempo y continuará la conversación de la misma manera.

Conclusión

¡Esto es sólo el principio! Puede mejorar y agregar más inteligencia a su bot mediante el uso de una plataforma de conversación de IA o crear una experiencia más personalizada conectando a un usuario con el último trabajador Frontline con el que habló en un chat anterior. También puede expandir sus canales agregando soporte de voz.

Otra forma de mejorar esta experiencia es redirigir la conversación desde Frontline a un centro de contacto Twilio Flex. Para hacerlo, necesitará dos cuentas de Twilio: la cuenta de Frontline y una cuenta Flex (a tiempo de esta publicación, Frontline no se puede instalar en una cuenta Flex). En la cuenta de Frontline, implemente una función de Twilio que cree una tarea en la cuenta Flex mediante la API de TaskRouter. En esta función, puede agregar a la tarea toda la información necesaria para dar contexto al agente, incluido el historial de la conversación en Frontline, y configurar esta función para que se active ante eventos onMessageAdd, de modo que reaccione cada vez que el usuario pida en un mensaje que necesita conectarse con el contact center. Una vez que la conversación se "redirige" desde la cuenta de Frontline, asegúrese de administrar correctamente el estado de la conversación para mantener la coherencia.

Comience desde hoy a ofrecer a sus clientes experiencias personalizadas con Frontline. ¡Estamos ansiosos por ver lo que crea!

Ana Andrés es Ingeniera de Telecomunicación y Doctora en Informática. Ella es parte del equipo de Ingeniería de Soluciones en Twilio. Actúa como asesora para clientes en Europa en todo lo relacionado con la tecnología y los productos de Twilio. Puede comunicarse con Ana por correo electrónico aandresdelvalle@twilio.com

Teresa Nascimento es ingeniera de soluciones en Twilio y ayuda a los clientes en Europa a brindar experiencias increíbles a los clientes mediante el uso de las API de Twilio. Teresa es Ingeniera de Software desde hace mucho tiempo, defensora de las mujeres en tecnología y apasionada por la creación de soluciones con impacto social. Teresa puede ser contactada en tnascimento@twilio.com.