Cómo agregar mensajes directos de Twitter a Twilio Flex

August 06, 2021
Redactado por
Revisado por

Twilio Flex x Twitter Header

La propia investigación de Twitter descubrió que el 64 % de los clientes de la plataforma preferirían enviar un mensaje a un asistente de soporte que llamar a una empresa, y el 75 % esperaba recibir una respuesta en 15 minutos. En esta publicación, veremos cómo puede incorporar las solicitudes de asistencia de Twitter a Twilio Flex, para satisfacer las demandas de los clientes y para aprovechar las funciones interactivas de la API de mensajes directos de Twitter, como las “respuestas rápidas” y los “botones”.

twilio-twitter-flex-view.png

Cómo empezar

Antes de entrar en la implementación, asegúrese de haber hecho lo siguiente.

Orquestación de chats

Si bien Flex no proporciona un canal de Twitter de forma inmediata, ofrece a los desarrolladores la posibilidad de crear y agregar canales personalizados. Para crear una línea de conversación abierta entre un cliente en Twitter y un agente en Flex, necesitamos una aplicación que escuche las actualizaciones de cada lado y reenvíe los mensajes en consecuencia para que tanto el cliente como el agente se mantengan sincronizados. La aplicación debería:

  1. Escuchar un evento en el que se envíe un mensaje directo a nuestra cuenta de Twitter y reenviarlo a Flex a través de un canal de chat de Flex. Si el cliente ya interactúa en un chat con un agente, el mensaje se enviará a través de un canal de chat existente, de lo contrario se creará uno nuevo. Nos referiremos a esto como el endpoint [POST] /fromTwitter.
  2. Escuchar un evento en el que un agente de Flex envíe un mensaje y reenviarlo a Twitter como un mensaje directo para el cliente. Nos referiremos a esto como el endpoint [POST] /fromFlex.
  3. Responder a las comprobaciones de respuesta a desafíos de Twitter para que los webhook registrados sigan siendo válidos. Este es un requisito previo para completar el resto de la configuración de los webhook de Twitter que inició anteriormente. Nos referiremos a esto como el endpoint [GET]/fromTwitter.

El siguiente diagrama de secuencia muestra el flujo de mensajes de ida y vuelta.

Flujo entre Twitter y Flex

[GET] /fromTwitter Endpoint

Twitter hace referencia a ejemplos de código para crear tokens de respuesta cifrados que satisfagan las comprobaciones de respuesta de desafío en PythonJavaScript y Ruby. Después, se muestra un ejemplo de Express-Node.js Endpoint:

Una vez que este Endpoint esté en funcionamiento, puede completar los pasos 3 y 4 de la guía Empezar a utilizar los webhook de Twitter, donde registrará la aplicación para recibir los webhook y suscribirá la cuenta de Twitter a las actualizaciones. El equipo de relaciones con los desarrolladores de Twitter también proporciona una aplicación web que puede utilizar para acelerar el proceso.

app.get('/fromTwitter', (req, res) => {
  const crcToken = req.query.crc_token;
  const hmac = createHmac('sha256', process.env.TWITTER_CONSUMER_SECRET)
    .update(crcToken)
    .digest('base64');
  const resToken = `sha256=${hmac}`;
  res.status(200).json({ response_token: resToken });
});

[POST] /fromTwitter Endpoint

Ahora estamos listos para empezar a recibir eventos de mensajes directos de Twitter. El canal de chat que utilizaremos para reenviar los mensajes a Flex requiere una propiedad identity, un identificador único para el cliente en el chat. En este caso, el identificador lógico es el nombre de usuario de Twitter del cliente, que se proporciona como parte del evento del mensaje. En primer lugar, extraeremos el nombre de usuario de Twitter y el identificador de la solicitud, junto con el cuerpo del mensaje, y los pasaremos a una función llamada sendMessageToFlex().

app.post('/fromTwitter', (req, res) => {
  if (req.body.direct_message_events) {
    const users = req.body.users;
    const customer = users[Object.keys(users)[0]];
    const twitterHandle = customer.screen_name;
    const twitterId = customer.id;
    // Check to make sure this is a message sent from a customer rather than a Direct
    // Message we sent from our application
    if (twitterHandle !== process.env.TWITTER_CO_HANDLE) {
      const msg =
        req.body.direct_message_events[0].message_create.message_data.text;
      sendMessageToFlex(twilioClient, msg, twitterHandle, twitterId);
    }
  }
  res.sendStatus(200);
});

sendMessageToFlex() se divide en dos funciones más:

  • getChannel() devuelve el canal de chat asociado al cliente.
  • sendMessageToFlex() envía el mensaje a Flex a través del canal de chat.

 getChannel()  devuelve un canal de chat para el cliente, ya sea mediante la búsqueda de un canal activo y preexistente, o mediante la creación de uno nuevo. En esta etapa también se configura un webhook para que el Endpoint [POST] /fromFlex reciba actualizaciones cuando un agente responda.

Para crear un nuevo canal de chat de Flex, debe proporcionarse un flujo de Flex que indique a Twilio cosas como el servicio de chat que deseamos utilizar, también la forma en que deben tratarse los mensajes entrantes. Visite esta publicación y trabaje a través de la sección “Crear un nuevo flujo de Flex”, y tome nota de su Flex Flow SIDFlex Chat Service SID para utilizar en el código de ejemplo a continuación. El resto de la publicación proporciona información valiosa sobre cómo se orquestan los mensajes en Flex, también una guía para agregar canales de chat personalizados, en los que se basará gran parte de nuestra implementación.

const getChannel = async (
  twilioClient,
  flexFlowSid,
  flexChatServiceSid,
  twitterHandle,
  twitterId
) => {
  const channelExists = await hasOpenChannel(twilioClient, twitterHandle);
  // If we try and create a new channel that already exists, Twilio will just return the 
  // existing channel SID which we need anyway
  const flexChannel = await twilioClient.flexApi.channel.create({
    // Use newly created Flex Flow SID
    flexFlowSid,
    identity: twitterHandle,
    chatUserFriendlyName: `Twitter with @${twitterHandle}`,
    chatFriendlyName: twitterId,
    target: `@${twitterHandle}`,
  });
  // Duplicating webhooks would result in duplicate flows between Twitter and Flex
  // which we need to avoid so we apply the channelExists check here
  if (!channelExists) {
    await twilioClient.chat
      .services(flexChatServiceSid)
      .channels(flexChannel.sid)
      .webhooks.create({
        type: 'webhook',
        configuration: {
          method: 'POST',
          url: `${process.env.NGROK_URL}/fromFlex`,
          filters: ['onMessageSent'],
        },
      });
  }
  return flexChannel
};

const hasOpenChannel = async (twilioClient, twitterHandle) => {
  const channels = await twilioClient.chat
    .services(process.env.FLEX_CHAT_SERVICE)
    .channels.list();
  const openChannelExists =
    channels.filter((c) => {
      const { from, status } = JSON.parse(c.attributes);
      // Channels are automatically set to INACTIVE when they are closed by a Flex Agent
      return from.includes(twitterHandle) && status !== 'INACTIVE';
    }).length > 0;
  return openChannelExists;
};

sendChatMessage() maneja el envío final del mensaje a Flex.

const sendChatMessage = async (flexChatServiceSid, flexChannelSid, twitterHandle, msg) => {
  // Source: https://www.twilio.com/blog/add-custom-chat-channel-twilio-flex
  const params = new URLSearchParams();
  params.append('Body', msg);
  params.append('From', twitterHandle);
  const res = await fetch(
    `https://chat.twilio.com/v2/Services/${flexChatServiceSid}/Channels/${flexChannelSid}/Messages`,
    {
      method: 'post',
      body: params,
      headers: {
        'X-Twilio-Webhook-Enabled': 'true',
        Authorization: `Basic ${base64.encode(
          `${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}`
        )}`,
      },
    }
  );
  return res;
};

En este punto, debería ser capaz de enviar un mensaje directo a la cuenta de Twitter y verlo en Flex. ¡Haga una prueba!

[POST] /fromFlex Endpoint

En el endpoint [POST] /fromTwitter, nos suscribimos a los eventos de mensajes de Flex a través de un webhook que se configuró para el canal de chat. Además del cuerpo del mensaje incluido en estos eventos, necesitamos extraer el identificador de Twitter del cliente del recurso del canal de chat asociado. Este identificador es el identificador de usuario clave que Twitter requiere para enviar un mensaje a través de su API de mensajes directos y su recuperación se gestiona en la función getUserFromChannel() siguiente.

app.post('/fromFlex', async (req, res) => {
  // Source will be 'API' for Twitter customer side, 'SDK' for Flex agent side
  if (req.body.Source === 'SDK') {
    const channelId = req.body.ChannelSid;
    const twitterId = await getUserFromChannel(
      twilioClient,
      channelId
    );
    const msg = req.body.Body;
    await sendMessageToTwitter(twitterClient, msg, twitterId);
  }
  res.sendStatus(200);
});

const getUserFromChannel = async (twilioClient, channelId) => {
  const chat = await twilioClient.chat
    .services(process.env.FLEX_CHAT_SERVICE)
    .channels(channelId)
    .fetch();
  const twitterId = chat.friendlyName;
  return twitterId;
};

Para finalizar el flujo, se envía una respuesta al cliente dentro de la función sendMessageToTwitter() mediante la creación de un nuevo evento de mensaje directo. Tenga en cuenta que, en este ejemplo, utilizamos twit como un cliente de Twitter para manejar la solicitud de la API, pero el objeto event de abajo es el formato estándar que espera Twitter.

const sendMessageToTwitter = async (twitterClient, msg, twitterId) => {
  twitterClient.post(
    'direct_messages/events/new',
    {
      event: {
        type: 'message_create',
        message_create: {
          target: {
            recipient_id: twitterId,
          },
          message_data: {
            text: msg,
          },
        },
      },
    },
  );
};

Ahora puede realizar la prueba del proceso de principio a fin y enviar mensajes de ida y vuelta entre Twitter y Flex. Puede probar a abrir y cerrar conversaciones, también procesar mensajes de varias cuentas de Twitter para asegurarse de que las cosas funcionan como se espera.

Funciones de chat interactivo

Se pueden desarrollar experiencias de chat enriquecidas con la ayuda de las respuestas rápidas y los botones de Twitter. Estas características se realizan a través de algunas alteraciones menores en el objeto event que utilizamos para enviar un mensaje directo en el Endpoint [POST] /fromFlex.

twilio-twitter-flex-quick-replies.png

Para que Twitter muestre las respuestas rápidas en un mensaje, la propiedad message_data debe contener un objeto quick_reply. Después, puede ver un ejemplo de cómo debería ser el objeto event

  "event": {
    "type": "message_create",
    "message_create": {
      "target": {
        "recipient_id": "844385345234",
      },
      "message_data": {
        "text": "Ok, this weeks 2 boxes are:",
        "quick_reply": {
          "type": "options",
          "options": [
            {
              "label": "Missed a delivery 📦",
              "description": "Reschedule a date and time",
            },
            {
              "label": "General information 🍓",
              "description": "Available fruit boxes and what's in season"
            },
          ],
        },
      },
    },
  }

Para esta implementación, el agente activa las respuestas rápidas con una palabra clave “Options” (Opciones) seguida de una lista de opciones. Una función de análisis, getTwitterMessageData(), divide el mensaje del agente en la palabra clave y empaqueta el resto del mensaje en una serie de opciones con labels y descriptions. El objeto message_data devuelto por la función se envía entonces como parte del nuevo evento de mensaje directo. Este es solo uno de los muchos enfoques posibles que podrían adoptarse para dotar a los agentes de respuestas rápidas dentro de la interfaz de usuario de Flex.

const getTwitterMessageData = (msg) => {
  let formattedMsg = msg;
  let optionsObj = {};
  if (msg.includes('Options')) {
    const msgSplit = msg.split('Options');
    const optionsSplit = msgSplit[1].split(',');
    formattedMsg = msgSplit[0];
    const options = optionsSplit.map((op) => {
      const optionDescSplit = op.split('-');
      const option = {
        label: optionDescSplit[0],
      };
      if (optionDescSplit.length > 1) {
        option.description = optionDescSplit[1];
      }
      return option;
    });
    // Package the Quick Reply object
    optionsObj = {
      quick_reply: {
        type: 'options',
        options,
      },
    };
  }
  const messageData = { text: formattedMsg, ...optionsObj, };
  return messageData;
};

Pasos siguientes para su integración con Twitter

El código para esta prueba de concepto, junto con una implementación de botones interactivos, está disponible en este repositorio. Es posible que desee explorar otras incorporaciones para la integración, como la recopilación de las puntuaciones de satisfacción de la asistencia mediante las tarjetas de comentarios de Twitter o el desarrollo de un plugin de Flex que muestre el logotipo y los colores de Twitter para que los agentes sepan cono facilidad que se están comunicando con un cliente en Twitter.

Mark Marshall es un ingeniero de soluciones sénior en Twilio. Puede comunicarse con él en mmarshall [en] twilio.com.

Este artículo ha sido traducido del original "How to add Twitter Direct Messages into Twilio Flex". Mientras continuamos con los procesos de traducción, nos encantaría recibir sus comentarios en help@twilio.com - Buenas contribuciones pueden generar regalos de Twilio.