Como lidar com usuários off-line nas suas campanhas do RBM

Um fator importante no tempo de entrega das mensagens do agente é se o usuário que você está tentando entrar em contato tem uma conexão de dados no momento em que o agente envia uma mensagem. Se o usuário estiver off-line, a plataforma do RBM armazenará a mensagem e tentará entregar por até 30 dias. Se a mensagem não puder ser entregue até essa data, ela será removida do sistema.

Há muitos motivos e situações em que um usuário pode não ter conectividade quando o agente está tentando entrar em contato com ele. Ele poderia ter desativado os dados para economizar dinheiro no plano móvel, pode estar em um avião sem conexão Wi-Fi ou talvez em um metrô em um túnel. Dependendo da urgência com que as mensagens precisam chegar, seu agente precisa lidar com os usuários off-line revogando mensagens RBM não entregues e redirecionando essas mensagens por um canal diferente.

Neste artigo, mostrarei como usar o Google Cloud Datastore para controlar as mensagens enviadas e entregues, como usar o cron para revoke mensagens não entregues e como redirecionar essas mensagens por SMS.

Como manter o controle das mensagens enviadas

Todas as mensagens enviadas pelo seu agente do RBM precisam incluir um ID de mensagem exclusivo. Para acompanhar as mensagens enviadas pelo agente, salve o ID das mensagens, o número de telefone e o carimbo de data/hora de cada mensagem.

Várias tecnologias podem ser usadas para armazenar essas informações, mas neste artigo uso um Google Cloud Datastore. O Cloud Datastore é um banco de dados NoSQL altamente escalonável, que processa automaticamente a fragmentação e a replicação. É uma ótima solução para armazenar dados não relacionais, como as mensagens enviadas por um agente. O Cloud Datastore depende de uma instância ativa do Google App Engine. Por isso, uso o App Engine para hospedar meu agente RBM e configurar um cron job.

Há bibliotecas de cliente para o Cloud Datastore disponíveis em várias linguagens. Para este exemplo, eu uso o Node.js e baseo o código do agente do RBM na Amostra do primeiro agente Node.js, disponível no site do desenvolvedor do RBM. A primeira coisa que faço é instalar o Cloud Datastore para meu projeto Node.js executando o seguinte comando:

npm install --save @google-cloud/datastore

No código-fonte do agente, adiciono uma referência global à biblioteca de cliente do Cloud Datastore.

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

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

Com o objeto do armazenamento de dados criado, apresento uma função para armazenar o estado msisdn, message id, sent time e delivery para cada mensagem.

/**
 *   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);
        });
}

Com essa função, é preciso chamar o método sempre que o agente enviar uma mensagem a um usuário. Quando a biblioteca de cliente RBM Node.js envia mensagens RBM, ela fornece um objeto de resposta no método de callback que contém o messageId para a mensagem enviada ao usuário.

Veja abaixo um exemplo de envio de uma mensagem de texto simples a um usuário e gravação das informações da mensagem após a comunicação com a API 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);
});

Depois de executar o código acima, você pode inspecionar o Datastore no Console do Google Cloud na visualização Entidades do Datastore.

Datastore

Como atualizar o estado de entrega de mensagens

Agora que o agente armazena solicitações de mensagens enviadas no Datastore, precisamos atualizar o status de entrega quando a mensagem é entregue ao dispositivo do usuário.

Os dispositivos dos usuários enviam eventos DELIVERED, READ e IS_TYPING para agentes do RBM pelo Cloud Pub/Sub. No gerenciador do Pub/Sub, verifique os eventos entregues e atualize a configuração do Datastore para a sinalização entregue como verdadeira.

/**
 *   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
    }
}

O agente salva as mensagens enviadas no Datastore e atualiza os dados ao receber uma notificação de entrega. Na próxima seção, configuraremos um cron job no App Engine do Google para ser executado a cada 10 minutos para monitorar mensagens não entregues.

Cron no Google App Engine

É possível usar cron jobs para programar tarefas em intervalos diferentes. No App Engine do Google, um cron job é configurado com uma descrição, um URL e um intervalo.

Em aplicativos Node.js, você a configura em um arquivo cron.yaml, que pode ser implantado no App Engine usando o SDK Google Cloud. Saiba mais sobre outras definições de configuração da linguagem em Como programar jobs com cron.yaml.

Como a tarefa cron precisa de um URL, você precisa adicionar um endpoint de URL ao roteador do app expresso para ser chamado pelo cron. Esse webhook é responsável por consultar o Datastore em busca de mensagens antigas, excluí-las da plataforma RBM e enviá-las ao usuário por 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();
});

Veja abaixo a configuração do arquivo cron.yaml para executar esse endpoint a cada 10 minutos.

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

Para implantar as tarefas cron no App Engine, execute o seguinte comando:

gcloud app deploy cron.yaml

Após a implantação, o App Engine configura automaticamente a tarefa cron, e ela fica visível em App Engine > Cron jobs.

cron jobs

Como consultar o Datastore em busca de mensagens não entregues

No webhook do cron job configurado na seção anterior, é necessário adicionar lógica para procurar todas as mensagens enviadas com estado delivered igual a falso e lastUpdated vez mais antigo que um tempo limite predefinido que faça sentido para nosso caso de uso. Neste exemplo, expiram as mensagens com mais de uma hora.

Para aceitar uma consulta composta como essa, o Datastore precisa ter um índice composto que contenha as propriedades delivered e lastUpdated. Para isso, crie um arquivo no projeto chamado index.yaml com as seguintes informações:

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

Semelhante à implantação do cron job definido anteriormente, use o SDK do Google Cloud para implantar o índice composto definido com o seguinte comando:

gcloud datastore create-indexes index.yaml

Após a implantação, o App Engine configura o índice automaticamente e ele fica visível em Datastore > Índices.

Índices do armazenamento de dados

Com o índice definido, podemos voltar ao webhook que você criou para o cron job e concluir a lógica de expiração da mensagem:

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();
});

O RBM não oferece suporte nativo ao substituto de SMS. Por isso, você precisará implementar a lógica para enviar mensagens não entregues por SMS.

Conclusão e resumo

Para processar usuários off-line, crie uma lógica de revogação para mensagens RBM não entregues. A quantidade de tempo usada antes de a mensagem expirar depende do tempo necessário para transmitir as informações. Para informações urgentes, recomendamos um tempo limite de menos de duas horas.

Neste exemplo, usamos o Cloud Datastore e o Google App Engine para gerenciar o processo de armazenamento, a consulta e a revogação, mas qualquer mecanismo de armazenamento que controle as mensagens enviadas e entregues funcionará.

Boa sorte e boa programação!