Как работать с офлайн-пользователями в ваших кампаниях RBM

Основным фактором, влияющим на время доставки сообщений вашего агента, является наличие у пользователя, с которым вы пытаетесь связаться, подключения для передачи данных в момент отправки сообщения вашим агентом. Если пользователь не в сети, платформа RBM сохраняет сообщение и пытается доставить его в течение 30 дней. Если сообщение не может быть доставлено к этому времени, оно удаляется из системы.

Существует множество причин и ситуаций, когда у пользователя может отсутствовать соединение, когда ваш агент пытается связаться с ним. Они могли отключить передачу данных, чтобы сэкономить на своем мобильном тарифе, могли находиться в самолете без подключения к Wi-Fi или, возможно, находиться в метро в туннеле. В зависимости от срочности, с которой должны поступать ваши сообщения, вашему агенту необходимо корректно обрабатывать автономных пользователей, отзывая недоставленные сообщения RBM и перенаправляя эти сообщения через другой канал.

В этой статье я покажу вам, как использовать облачное хранилище данных Google для отслеживания отправляемых и доставляемых вами сообщений, как использовать cron для отзыва недоставленных сообщений и как перенаправить эти сообщения через SMS.

Отслеживание отправленных сообщений

Каждое сообщение, отправленное вашим агентом RBM, должно содержать уникальный идентификатор сообщения . Чтобы отслеживать сообщения, отправляемые вашим агентом, вам необходимо сохранять идентификатор сообщения, номер телефона и метку времени для каждого сообщения.

Для хранения этой информации можно использовать различные технологии, но в этой статье я использую облачное хранилище данных Google. Cloud Datastore — это масштабируемая база данных NoSQL, которая автоматически выполняет сегментирование и репликацию. Это отличное решение для хранения нереляционных данных, таких как сообщения, отправленные агентом. Cloud Datastore зависит от наличия активного экземпляра Google App Engine , поэтому я использую App Engine для размещения своего агента RBM и настройки задания cron.

Клиентские библиотеки для Cloud Datastore доступны на многих языках. В этом примере я использую Node.js и основываю код агента RBM на примере первого агента Node.js , доступном на веб-сайте разработчика RBM . Первое, что я делаю, — это устанавливаю Cloud Datastore для своего проекта Node.js, выполнив следующую команду:

npm install --save @google-cloud/datastore

В исходный код моего агента я добавляю глобальную ссылку на клиентскую библиотеку Cloud Datastore.

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

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

Создав объект хранилища данных, я представляю функцию для хранения msisdn , message id , sent time и состояния delivery для каждого сообщения.

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

Имея эту функцию, вам нужно будет вызывать этот метод всякий раз, когда ваш агент отправляет сообщение пользователю. Когда клиентская библиотека RBM Node.js отправляет сообщения RBM, библиотека предоставляет объект ответа в методе обратного вызова, который содержит messageId для сообщения, отправленного пользователю.

Ниже приведен пример отправки пользователю обычного текстового сообщения и записи информации сообщения после успешного взаимодействия с 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);
});

После запуска приведенного выше кода вы можете проверить хранилище данных из Google Cloud Console в представлении «Объекты хранилища данных» .

Хранилище данных

Обновление состояния доставки сообщений

Теперь, когда ваш агент хранит отправленные запросы сообщений в хранилище данных, нам необходимо обновить статус доставки, как только сообщение будет успешно доставлено на устройство пользователя.

Устройства пользователей отправляют события DELIVERED , READ и IS_TYPING агентам RBM через Cloud Pub/Sub. В обработчике Pub/Sub проверьте наличие доставленных событий и обновите параметр хранилища данных для флага доставки до значения 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
    }
}

Агент сохраняет исходящие сообщения в хранилище данных и обновляет данные при получении уведомления о доставке. В следующем разделе мы настроим задание cron в Google App Engine, которое будет запускаться каждые 10 минут для отслеживания недоставленных сообщений.

Cron в Google App Engine

Вы можете использовать задания cron для планирования задач с разными интервалами. В Google App Engine задание cron настраивается с использованием описания, URL-адреса и интервала.

В приложениях Node.js вы настраиваете их в файле cron.yaml , который можно развернуть в App Engine с помощью Google Cloud SDK . Прочтите о других настройках языковой конфигурации в разделе «Планирование заданий с помощью cron.yaml» .

Поскольку для задачи cron требуется URL-адрес, вам необходимо добавить конечную точку URL-адреса к маршрутизатору экспресс-приложений, который будет вызываться cron. Этот вебхук отвечает за запрос старых сообщений в хранилище данных, их удаление с платформы RBM и отправку их пользователю по 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();
});

Ниже приведена конфигурация файла cron.yaml для выполнения этой конечной точки каждые 10 минут.

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

Чтобы развернуть задачи cron в App Engine, выполните следующую команду:

gcloud app deploy cron.yaml

После развертывания App Engine автоматически настраивает задачу cron, и ее можно просмотреть в разделе App Engine > Задания Cron .

задания cron

Запрос к хранилищу данных на наличие недоставленных сообщений

В веб-перехватчик для задания cron, который вы настроили в предыдущем разделе, вам необходимо добавить логику для поиска всех отправленных сообщений, состояние delivered которых равно false и время lastUpdated которых превышает заранее определенное время ожидания, что имеет смысл для нашего вариант использования. В этом примере срок действия сообщений старше часа истекает.

Чтобы поддерживать такой составной запрос, хранилище данных должно иметь составной индекс, содержащий как delivered свойства, так и свойства lastUpdated . Для этого вы можете создать в своем проекте файл index.yaml со следующей информацией:

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

Аналогично развертыванию задания cron, которое вы определили ранее, используйте Google Cloud SDK для развертывания составного индекса, определенного вами с помощью следующей команды:

gcloud datastore create-indexes index.yaml

После развертывания механизм приложений автоматически настраивает индекс, и его можно просмотреть в разделе «Хранилище данных» > «Индексы» .

Индексы хранилища данных

Определив индекс, мы можем вернуться к веб-перехватчику, который вы создали для задания cron, и завершить логику истечения срока действия сообщения:

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 изначально не поддерживает резервный вариант SMS, поэтому вам потребуется реализовать логику для отправки недоставленных сообщений через SMS.

Подведение итогов и TL;DR

Чтобы обрабатывать автономных пользователей, вы можете создать логику отзыва для недоставленных сообщений RBM. Количество времени, которое вы используете до истечения срока действия сообщения, зависит от того, насколько чувствительна ко времени передаваемая вами информация. При использовании информации, зависящей от времени, мы рекомендуем тайм-аут менее двух часов.

В этом примере используется облачное хранилище данных и Google App Engine для управления процессом хранения, запроса и отзыва, но любой механизм хранения для отслеживания отправленных и доставленных сообщений должен работать.

Удачи и приятного кодирования!