Cómo administrar los usuarios sin conexión en tus campañas de RBM

Un factor importante en el tiempo de entrega de los mensajes de tu agente es si el usuario con el que intentas comunicarte tiene una conexión de datos en el momento en que tu agente envía un mensaje. Si el usuario no tiene conexión, la plataforma de RBM almacena el mensaje y, luego, intenta entregarlo por hasta 30 días. Si el mensaje no se puede entregar para entonces, se quita del sistema.

Existen muchos motivos y situaciones en las que un usuario puede no tener conectividad cuando tu agente intenta comunicarse con él. Podrían haber desactivado los datos para ahorrar dinero en su plan de telefonía móvil, podrían estar en un avión sin conexión Wi-Fi o tal vez estar en un metro a través de un túnel. Según la urgencia con la que deban llegar tus mensajes, tu agente debe manejar de forma correcta a los usuarios sin conexión revocando los mensajes de RBM no entregados y redirigiendo esos mensajes a un canal diferente.

En este artículo, te mostraré cómo usar Google Cloud Datastore para realizar un seguimiento de los mensajes que envías y entregas, cómo usar cron para revoke los mensajes no entregados y cómo volver a enrutar esos mensajes mediante SMS.

Hacer un seguimiento de los mensajes enviados

Cada mensaje que envía tu agente de RBM debe incluir un ID de mensaje único. Para realizar un seguimiento de los mensajes que envía tu agente, debes guardar el ID del mensaje, el número de teléfono y la marca de tiempo de cada mensaje.

Puedes utilizar una variedad de tecnologías para almacenar esta información, pero en este artículo utilizo Google Cloud Datastore. Cloud Datastore es una base de datos NoSQL altamente escalable que se encarga de forma automática de la fragmentación y la replicación. Es una solución excelente para almacenar datos no relacionales, como los mensajes que envía un agente. Cloud Datastore depende de tener una instancia activa de Google App Engine, por lo que uso App Engine para alojar mi agente de RBM y configurar un trabajo cron.

Hay bibliotecas cliente para Cloud Datastore disponibles en muchos lenguajes. En este ejemplo, uso Node.js y baso el código de agente de RBM en la muestra del primer agente de Node.js, disponible en el sitio web para desarrolladores de RBM. Lo primero que hago es instalar Cloud Datastore para mi proyecto de Node.js mediante la ejecución del siguiente comando:

npm install --save @google-cloud/datastore

En el código fuente de mi agente, agrego una referencia global a la biblioteca cliente de Cloud Datastore.

// Imports the Google Cloud client library
const Datastore = require('@google-cloud/datastore');

// Creates a client
const datastore = new Datastore({
    projectId: PROJECT_ID,
});

Una vez creado el objeto del almacén de datos, introduzco una función para almacenar los estados msisdn, message id, sent time y delivery de cada mensaje.

/**
 *   Records an entry in the Cloud Datastore to keep track of the
 *   messageIds sent to users and the delivery state.
 *
 *   @property {string} msisdn The user's phone number in E.164 format.
 *   @property {string} messageId The unique message identifier.
 *   @property {boolean} delivered True if message has been delivered.
 */
function saveMessage(msisdn, messageId, delivered) {
    const messageKey = datastore.key(['Message', messageId]);

    const dataForMessage = {
        key: messageKey,
        data: {
            id: messageId,
            msisdn: msisdn,
            lastUpdated: new Date().getTime(),
            delivered: delivered
        },
    };

    // Record that the message was sent.
    datastore
        .save(dataForMessage)
        .then(function() {
            console.log('saved message successfully');
        })
        .catch((err) => {
            console.error('ERROR:', err);
        });
}

Con esta función implementada, debes llamar a este método cada vez que tu agente envíe un mensaje a un usuario. Cuando la biblioteca cliente de Node.js de RBM envía mensajes de RBM, la biblioteca proporciona un objeto de respuesta en el método de devolución de llamada que contiene el messageId para el mensaje que se envió al usuario.

A continuación, se muestra un ejemplo para enviar un mensaje de texto sin formato a un usuario y grabar la información del mensaje después de comunicarse correctamente con la API de RBM.

let params = {
    messageText: 'Hello, World!',
    msisdn:'+12223334444',
};

// Send "Hello, World!" to the user.
rbmApiHelper.sendMessage(params,
function(response) {
    // Extract the message id from the response
    let messageId = response.config.params.messageId;

    // Store the sent state in the Datastore
    saveMessage(phoneNumber, messageId, false);
});

Después de ejecutar el código anterior, puedes inspeccionar Datastore desde Google Cloud Console en la vista Entidades de Datastore.

Datastore

Actualiza el estado de entrega de los mensajes

Ahora que tu agente almacena las solicitudes de mensajes enviados en Datastore, debemos actualizar el estado de entrega una vez que el mensaje se entregue de forma correcta al dispositivo del usuario.

Los dispositivos de los usuarios envían eventos DELIVERED, READ y IS_TYPING a los agentes de RBM a través de Cloud Pub/Sub. En el controlador de Pub/Sub, verifica los eventos entregados y actualiza la configuración de Datastore para la marca entregada a verdadera.

/**
 *   Uses the event received by the Pub/Sub subscription to send a
 *   response to the client's device.
 *   @param {object} userEvent The JSON object of a message
 *   received by the subscription.
 */
function handleMessage(userEvent) {
    if (userEvent.senderPhoneNumber != undefined) {
        let msisdn = userEvent.senderPhoneNumber;
        let messageId = userEvent.messageId;
        let eventType = userEvent.eventType;

        if(eventType === 'DELIVERED') {
            saveMessage(msisdn, messageId, true);
        }

        // TODO: Process message and create RBM response
    }
}

El agente guarda los mensajes salientes en Datastore y actualiza los datos cuando recibe una notificación de entrega. En la siguiente sección, configuramos un trabajo cron en App Engine de Google para que se ejecute cada 10 minutos y supervise los mensajes no entregados.

Cron en App Engine de Google

Puedes usar trabajos cron para programar tareas en diferentes intervalos. En App Engine de Google, un trabajo cron se configura con una descripción, una URL y un intervalo.

En las apps de Node.js, estos se configuran en un archivo cron.yaml, que puedes implementar en App Engine mediante el SDK de Google Cloud. Obtén más información sobre otras configuraciones de lenguaje en Programa trabajos con cron.yaml.

Debido a que la tarea cron necesita una URL, debes agregar un extremo de URL al router de la app exprés para que cron lo llame. Este webhook se encarga de consultar el almacén de datos en busca de mensajes antiguos, borrarlos de la plataforma de RBM y enviarlos al usuario mediante SMS.

router.get('/expireMessages', function(req, res, next) {
    // TOOD: Query the Datastore for undelivered messages,
    // remove them from the RBM platform, and send them over SMS

    res.status(200).send();
});

A continuación, se muestra la configuración del archivo cron.yaml para ejecutar este extremo cada 10 minutos.

cron:
-   description: "Processing expired RBM messages"
  url: /expireMessages
  schedule: every 10 mins

Para implementar las tareas cron en App Engine, ejecuta el siguiente comando:

gcloud app deploy cron.yaml

Después de la implementación, App Engine configura automáticamente la tarea cron, y la tarea se puede ver en App Engine > Trabajos cron.

trabajos cron

Consulta Datastore para ver si hay mensajes no entregados

En el webhook del trabajo cron que configuraste en la sección anterior, debes agregar lógica para buscar todos los mensajes enviados que tengan un estado delivered igual a falso y que tengan un tiempo lastUpdated anterior al tiempo de espera predefinido, que tiene sentido para nuestro caso de uso. En este ejemplo, los mensajes vencen hace más de una hora.

Para admitir una consulta compuesta como esta, Datastore debe tener un índice compuesto que contenga las propiedades delivered y lastUpdated. Para hacerlo, puedes crear un archivo en tu proyecto llamado index.yaml con la siguiente información:

indexes:
-   kind: Message
  properties:
  -   name: delivered
    direction: asc
  -   name: lastUpdated
    direction: desc

De manera similar a la implementación del trabajo cron que definiste antes, usa el SDK de Google Cloud para implementar el índice compuesto que definiste con el siguiente comando:

gcloud datastore create-indexes index.yaml

Después de la implementación, App Engine configura el índice automáticamente, que se puede ver en Datastore > Índices.

Índices de Datastore

Con el índice definido, podemos volver al webhook que creaste para el trabajo cron y completar la lógica de vencimiento del mensaje:

router.get('/expireMessages', function(req, res, next) {
    // Milliseconds in an hour
    const TIMEOUT = 3600000;

    // Threshold is current time minus one hour
    const OLD_MESSAGE_THRESHOLD = new Date().getTime() - TIMEOUT;

    // Create a query to find old undelivered messages
    const query = datastore
        .createQuery('Message')
        .filter('delivered', '=', false)
        .filter('lastUpdated', '<', OLD_MESSAGE_THRESHOLD);

    // Execute the query
    datastore.runQuery(query).then((results) => {
        for(var i = 0; i < results[0].length; i++) {
            let msisdn = results[0][i].msisdn;
            let messageId = results[0][i].id;

            // Stop the message from being sent
            rbmApiHelper.revokeMessage(msisdn, messageId);

            // Remove the message from the Datastore
            datastore.delete(results[0][i][datastore.KEY]);

            // TODO: Send the user the message as SMS
        }
    });

    res.status(200).send();
});

RBM no admite el resguardo de SMS de forma nativa, por lo que deberás implementar la lógica para enviar los mensajes no entregados mediante SMS.

Conclusión y resumen

Para administrar los usuarios sin conexión, puedes compilar una lógica de revocación para los mensajes de RBM que no se entregaron. La cantidad de tiempo que usas antes de que caduque el mensaje depende de qué tan urgente es la información que transmites. En el caso de esta información, recomendamos un tiempo de espera de menos de dos horas.

En este ejemplo, se usa Cloud Datastore y Google App Engine para administrar el proceso de almacenamiento, consulta y revocación, pero cualquier mecanismo de almacenamiento para realizar un seguimiento de tus mensajes enviados y entregados debería funcionar.

¡Buena suerte, y feliz programación!