Comment gérer les utilisateurs hors connexion dans vos campagnes RBM

Un facteur important dans le délai de distribution des messages de votre agent est de savoir si l'utilisateur que vous tentez de joindre dispose d'une connexion de données au moment où votre agent envoie un message. Si l'utilisateur est hors connexion, la plate-forme RBM stocke le message et tente de le distribuer pendant 30 jours maximum. Si le message ne peut pas être distribué d’ici là, il est supprimé du système.

Il existe de nombreuses raisons et situations où un utilisateur peut ne pas être connecté à Internet lorsque votre agent tente de le contacter. Ils ont peut-être désactivé les données pour économiser de l'argent sur leur forfait mobile, être en avion sans connexion Wi-Fi ou se trouver dans le métro dans un tunnel. En fonction de l'urgence avec laquelle vos messages doivent arriver, votre agent doit gérer de façon optimale les utilisateurs hors connexion en révoquant les messages RBM non distribués et en les réacheminant via un autre canal.

Dans cet article, je vais vous expliquer comment utiliser Google Cloud Datastore pour suivre les messages que vous envoyez et distribuez, comment utiliser Cron pour revoke les messages non distribués et comment réacheminer ces messages par SMS.

Suivre les messages envoyés

Chaque message envoyé par votre agent RBM doit inclure un ID de message unique. Pour suivre les messages que votre agent envoie, vous devez enregistrer l'ID, le numéro de téléphone et l'horodatage de chaque message.

Vous pouvez utiliser diverses technologies pour stocker ces informations, mais dans cet article, j'utilise Google Cloud Datastore. Cloud Datastore est une base de données NoSQL hautement évolutive qui gère automatiquement le partitionnement et la réplication. Il s'agit d'une excellente solution pour stocker des données non relationnelles telles que les messages envoyés par un agent. Cloud Datastore nécessite une instance Google App Engine active. J'utilise donc App Engine pour héberger mon agent RBM et configurer une tâche Cron.

Des bibliothèques clientes pour Cloud Datastore sont disponibles dans de nombreux langages. Pour cet exemple, j'utilise Node.js et je base le code de l'agent RBM sur le premier exemple d'agent Node.js, disponible sur le site Web pour les développeurs RBM. Tout d'abord, j'installe Cloud Datastore pour mon projet Node.js en exécutant la commande suivante:

npm install --save @google-cloud/datastore

Dans le code source de mon agent, j'ajoute une référence globale à la bibliothèque cliente Cloud Datastore.

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

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

Une fois l'objet datastore créé, j'introduis une fonction permettant de stocker l'état msisdn, message id, sent time et delivery de chaque message.

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

Une fois cette fonction en place, vous devez l'appeler chaque fois que votre agent envoie un message à un utilisateur. Lorsque la bibliothèque cliente Node.js RBM envoie des messages RBM, elle fournit un objet de réponse dans la méthode de rappel, qui contient le messageId pour le message envoyé à l'utilisateur.

Vous trouverez ci-dessous un exemple d'envoi d'un message en texte brut à un utilisateur et d'enregistrement des informations du message après la communication réussie avec 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);
});

Après avoir exécuté le code ci-dessus, vous pouvez inspecter le datastore à partir de la console Google Cloud dans la vue Entités Datastore.

Datastore

Modifier l'état de distribution des messages

Maintenant que votre agent stocke les requêtes de message envoyées dans le datastore, nous devons mettre à jour l'état de distribution une fois le message distribué sur l'appareil de l'utilisateur.

Les appareils des utilisateurs envoient les événements DELIVERED, READ et IS_TYPING aux agents RBM via Cloud Pub/Sub. Dans le gestionnaire de Pub/Sub, recherchez les événements diffusés et définissez le paramètre Datastore de l'indicateur distribué sur "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'agent enregistre les messages sortants dans le datastore et met à jour les données lorsqu'il reçoit une notification de distribution. Dans la section suivante, nous allons configurer une tâche Cron sur App Engine de Google pour qu'elle s'exécute toutes les 10 minutes afin de surveiller les messages non distribués.

Cron sur Google App Engine

Vous pouvez utiliser des tâches Cron pour planifier des tâches à différents intervalles. Dans Google App Engine, une tâche Cron est configurée avec une description, une URL et un intervalle.

Dans les applications Node.js, vous les configurez dans un fichier cron.yaml, que vous pouvez déployer sur App Engine à l'aide du SDK Google Cloud. Pour en savoir plus sur les autres configurations de configuration de langage, consultez la page Planifier des tâches avec cron.yaml.

Étant donné que la tâche Cron nécessite une URL, vous devez ajouter un point de terminaison d'URL au routeur d'application express pour qu'il soit appelé par Cron. Ce webhook est chargé d'interroger le datastore pour récupérer les anciens messages, de les supprimer de la plate-forme RBM et de les envoyer à l'utilisateur par 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();
});

Vous trouverez ci-dessous la configuration du fichier cron.yaml permettant d'exécuter ce point de terminaison toutes les 10 minutes.

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

Pour déployer les tâches Cron sur App Engine, exécutez la commande suivante:

gcloud app deploy cron.yaml

Après le déploiement, App Engine configure automatiquement la tâche Cron et celle-ci est visible sous App Engine > Jobs Cron.

tâches Cron

Interroger le datastore pour rechercher les messages non distribués

Dans le webhook de la tâche Cron que vous avez configurée dans la section précédente, vous devez ajouter une logique pour rechercher tous les messages envoyés dont l'état delivered est égal à "false" et dont l'état lastUpdated est antérieur à un délai d'expiration prédéfini qui est pertinent pour notre cas d'utilisation. Dans cet exemple, les messages de plus d'une heure font expirer.

Pour accepter une requête composite de ce type, le datastore doit disposer d'un index composite contenant les propriétés delivered et lastUpdated. Pour ce faire, vous pouvez créer dans votre projet un fichier nommé index.yaml contenant les informations suivantes:

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

Comme pour le déploiement de la tâche Cron que vous avez définie précédemment, utilisez le SDK Google Cloud pour déployer l'index composite que vous avez défini à l'aide de la commande suivante:

gcloud datastore create-indexes index.yaml

Après le déploiement, App Engine configure automatiquement l'index, qui peut être affiché sous Datastore > Indexes (Datastore > Index).

Index Datastore

Une fois l'index défini, nous pouvons revenir au webhook que vous avez créé pour la tâche Cron et terminer la logique d'expiration du message:

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 n'est pas compatible de manière native avec les SMS de remplacement. Vous devez donc implémenter la logique pour envoyer vos messages non distribués par SMS.

Conclusion et résumé

Pour gérer les utilisateurs hors connexion, vous pouvez créer une logique de révocation pour les messages RBM non distribués. Le délai d'expiration du message dépend du degré de sensibilité des informations que vous transmettez. Pour les informations urgentes, nous vous recommandons de définir un délai avant expiration inférieur à deux heures.

Cet exemple utilise Cloud Datastore et Google App Engine pour gérer le processus de stockage, de requête et de révocation, mais tout mécanisme de stockage permettant d'effectuer le suivi des messages envoyés et distribués devrait fonctionner.

Bonne chance et bon codage !