Come gestire gli utenti offline nelle campagne RBM

Un fattore importante nei tempi di consegna dei messaggi dell'agente è il fatto che l'utente che stai tentando di raggiungere disponga di una connessione dati nel momento in cui l'agente invia un messaggio. Se l'utente è offline, la piattaforma RBM archivia il messaggio e tenta di consegnarlo per un massimo di 30 giorni. Se il messaggio non può essere recapitato entro questo periodo, viene rimosso dal sistema.

Esistono molti motivi e situazioni in cui un utente potrebbe non avere connettività quando l'agente tenta di contattarlo. Potrebbero aver disattivato i dati per risparmiare sul piano tariffario, essere su un aereo senza connessione Wi-Fi o magari su una metropolitana in un tunnel. A seconda dell'urgenza con cui devono arrivare i messaggi, il tuo agente deve gestire agevolmente gli utenti offline revocando i messaggi RBM non recapitati e reindirizzandoli su un canale diverso.

In questo articolo, ti mostrerò come utilizzare Google Cloud Datastore per tenere traccia dei messaggi inviati e recapitati, come utilizzare cron per revoke i messaggi non recapitati e reindirizzare questi messaggi tramite SMS.

Tenere traccia dei messaggi inviati

Ogni messaggio inviato dall'agente RBM deve includere un ID messaggio univoco. Per tenere traccia dei messaggi inviati dall'agente, devi salvare l'ID messaggio, il numero di telefono e il timestamp di ogni messaggio.

Puoi utilizzare una varietà di tecnologie per archiviare queste informazioni, ma in questo articolo utilizzo un datastore Google Cloud. Cloud Datastore è un database NoSQL a scalabilità elevata che gestisce automaticamente lo sharding e la replica. È un'ottima soluzione per l'archiviazione di dati non relazionali come i messaggi inviati da un agente. Cloud Datastore dipende dalla presenza di un'istanza Google App Engine attiva, quindi utilizzo App Engine per ospitare l'agente RBM e configurare un cron job.

Esistono librerie client per Cloud Datastore disponibili in molti linguaggi. Per questo esempio, utilizzo Node.js e basiamo il codice dell'agente RBM sull'esempio Node.js per il primo agente disponibile nel sito web per sviluppatori RBM. La prima cosa che faccio è installare Cloud Datastore per il mio progetto Node.js eseguendo questo comando:

npm install --save @google-cloud/datastore

Nel codice sorgente dell'agente, aggiungo un riferimento globale alla libreria client di 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 volta creato l'oggetto Datastore, introduco una funzione per archiviare lo stato msisdn, message id, sent time e delivery per ogni messaggio.

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

Una volta attivata questa funzione, devi chiamare questo metodo ogni volta che l'agente invia un messaggio a un utente. Quando la libreria client Node.js RBM invia messaggi RBM, la libreria fornisce un oggetto risposta nel metodo di callback che contiene messageId per il messaggio inviato all'utente.

Di seguito è riportato un esempio di invio di un messaggio di testo normale a un utente e registrazione delle informazioni del messaggio dopo aver comunicato correttamente con l'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);
});

Dopo aver eseguito il codice riportato sopra, puoi esaminare Datastore dalla console Google Cloud all'interno della vista Entità Datastore.

Datastore

Aggiornamento dello stato di recapito dei messaggi

Ora che l'agente archivia le richieste di messaggi inviate in Datastore, dobbiamo aggiornare lo stato di recapito una volta che il messaggio è stato recapitato correttamente al dispositivo dell'utente.

I dispositivi degli utenti inviano gli eventi DELIVERED, READ e IS_TYPING agli agenti RBM tramite Cloud Pub/Sub. Nel gestore per Pub/Sub, controlla gli eventi pubblicati e aggiorna l'impostazione Datastore in modo che il flag pubblicato sia impostato su true.

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

L'agente salva i messaggi in uscita in Datastore e aggiorna i dati quando riceve una notifica di consegna. Nella sezione successiva, abbiamo configurato un cron job su App Engine di Google da eseguire ogni 10 minuti per monitorare i messaggi non recapitati.

Cron su App Engine di Google

Puoi utilizzare i cron job per pianificare attività a intervalli diversi. In App Engine di Google, viene configurato un cron job con una descrizione, un URL e un intervallo.

Nelle app Node.js, devi configurarle in un file cron.yaml, di cui puoi eseguire il deployment in App Engine utilizzando l'SDK Google Cloud. Scopri di più su altre configurazioni di configurazione dei linguaggi in Pianificazione dei job con cron.yaml.

Poiché l'attività cron richiede un URL, devi aggiungere un endpoint URL al router dell'app express per essere chiamato da cron. Questo webhook è responsabile dell'esecuzione di query in datastore per i messaggi precedenti, dell'eliminazione dalla piattaforma RBM e dell'invio all'utente tramite 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();
});

Di seguito è riportata la configurazione del file cron.yaml per eseguire questo endpoint ogni 10 minuti.

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

Per eseguire il deployment delle attività cron in App Engine, esegui questo comando:

gcloud app deploy cron.yaml

Dopo il deployment, App Engine configura automaticamente l'attività cron, che è visualizzabile in App Engine > Cron job.

cron job

Esecuzione di query su Datastore per i messaggi non recapitati

Nel webhook per il cron job configurato nella sezione precedente, devi aggiungere la logica per cercare tutti i messaggi inviati con uno stato delivered uguale a false e che hanno un valore di lastUpdated (tempo precedente a un timeout predefinito) adatto al nostro caso d'uso. In questo esempio, scadono i messaggi più vecchi di un'ora.

Per supportare una query composita come questa, Datastore deve avere un indice composto contenente entrambe le proprietà delivered e lastUpdated. Per farlo, puoi creare un file nel tuo progetto denominato index.yaml con le seguenti informazioni:

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

Come per il deployment del cron job definito in precedenza, utilizza l'SDK Google Cloud per eseguire il deployment dell'indice composto definito con il seguente comando:

gcloud datastore create-indexes index.yaml

Dopo il deployment, App Engine configura automaticamente l'indice, che è visibile in Datastore > Indici.

Indici Datastore

Una volta definito l'indice, possiamo tornare al webhook che hai creato per il cron job e completare la logica di scadenza del messaggio:

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 non supporta gli SMS di riserva, quindi dovrai implementare la logica per inviare i messaggi non recapitati tramite SMS.

Riepilogo e TL;DR

Per gestire gli utenti offline, puoi creare una logica di revoca per i messaggi RBM non recapitati. La quantità di tempo da utilizzare prima della scadenza del messaggio dipende dal grado di tempestività delle informazioni che trasmetti. Nel caso di informazioni sensibili al fattore tempo, consigliamo un timeout inferiore a due ore.

Questo esempio utilizza Cloud Datastore e Google App Engine per gestire il processo di archiviazione, query e revoca, ma qualsiasi meccanismo di archiviazione per tenere traccia dei messaggi inviati e recapitati dovrebbe funzionare.

Buona fortuna e buona programmazione.