RBM キャンペーンでオフライン ユーザーに対応する方法

エージェントのメッセージの配信時間に影響を与える主な要因は、エージェントがメッセージを送信したときに、到達しようとしているユーザーがデータ接続を持っているかどうかです。ユーザーがオフラインの場合、RBM プラットフォームはメッセージを保存して最大 30 日間配信を試みます。その日までにメッセージを配信できない場合、そのメッセージはシステムから削除されます。

エージェントがユーザーに連絡しようとしたときにユーザーが接続できない理由はさまざまです。たとえば、モバイルプランの費用を抑えるためにデータをオフにしている、Wi-Fi 接続がない飛行機内、トンネル内で地下鉄に乗っている、などが考えられます。メッセージの到着の緊急度に応じて、エージェントは、未配信の RBM メッセージを取り消して別のチャネル経由でそれらのメッセージを再ルーティングすることで、オフライン ユーザーを適切に処理する必要があります。

この記事では、Google Cloud Datastore を使用して送受信するメッセージを管理する方法、cron を使用して未配信メッセージをrevoke、SMS 経由でそれらのメッセージを再ルーティングする方法について説明します。

送信済みメッセージの追跡

RBM エージェントによって送信されるすべてのメッセージには、一意のメッセージ ID が含まれている必要があります。エージェントが送信したメッセージを追跡するには、各メッセージのメッセージ ID、電話番号、タイムスタンプを保存する必要があります。

この情報の保存にはさまざまなテクノロジーを使用できますが、この記事では Google Cloud Datastore を使用します。Cloud Datastore はスケーラビリティの高い NoSQL データベースで、シャーディングとレプリケーションを自動的に処理します。これは、エージェントから送信されたメッセージなどの非リレーショナル データを保存する優れたソリューションです。Cloud Datastore はアクティブな Google App Engine インスタンスに依存しているため、App Engine を使用して RBM エージェントをホストし、cron ジョブを構成します。

Cloud Datastore には、多くの言語で利用可能なクライアント ライブラリがあります。この例では、Node.js を使用します。RBM エージェント コードは、RBM デベロッパー ウェブサイトで入手可能な最初のエージェントの Node.js サンプルに基づいています。まず、次のコマンドを実行して、Node.js プロジェクト用に Cloud Datastore をインストールします。

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

作成したデータストア オブジェクトに対して、各メッセージの msisdnmessage idsent timedelivery の状態を保存する関数を導入します。

/**
 *   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 を含むレスポンス オブジェクトを提供します。

以下は、RBM API と正常に通信した後、ユーザーに書式なしテキスト メッセージを送信し、メッセージ情報を記録する例です。

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 コンソールの [Datastore エンティティ] ビュー内で Datastore を検査できます。

Datastore

メールの配信状態を更新する

送信したメッセージ リクエストをエージェントが Datastore に保存するようになったので、メッセージがユーザーのデバイスに正常に配信されたら、配信ステータスを更新する必要があります。

ユーザーのデバイスは、DELIVEREDREADIS_TYPING イベントを Cloud Pub/Sub 経由で RBM エージェントに送信します。Pub/Sub のハンドラで配信されたイベントを確認し、Delivered フラグの Datastore 設定を 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
    }
}

エージェントは送信メッセージを Datastore に保存し、配信通知を受信するとデータを更新します。次のセクションでは、10 分ごとに実行して未配信メッセージをモニタリングするように、Google の App Engine で cron ジョブを設定します。

Google の App Engine の cron

cron ジョブを使用して、さまざまな間隔でタスクをスケジュールできます。Google の App Engine では、cron ジョブは、説明、URL、間隔で構成されます。

Node.js アプリでは、これらを cron.yaml ファイルで構成します。このファイルは、Google Cloud SDK を使用して App Engine にデプロイできます。その他の言語構成の設定については、cron.yaml を使用したジョブのスケジューリングをご覧ください。

cron タスクには URL が必要なため、cron によって呼び出されるように、Express アプリのルーターに URL エンドポイントを追加する必要があります。この Webhook は、Datastore に古いメッセージをクエリし、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();
});

以下は、このエンドポイントを 10 分ごとに実行するための cron.yaml ファイルの構成です。

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 ジョブ

Datastore に対して未配信メッセージをクエリする

前のセクションで設定した cron ジョブの Webhook には、delivered 状態が false に等しい送信メッセージをすべて検索するロジックを追加し、このユースケースに理にかなった事前定義のタイムアウトより lastUpdated 時間が経過したメッセージを検索する必要があります。この例では、1 時間以上経過したメッセージを期限切れにします。

このような複合クエリをサポートするには、Datastore に 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

デプロイ後、App Engine によって自動的にインデックスが構成されます。インデックスは [データストア] > [インデックス] で確認できます。

データストア インデックス

インデックスが定義されたので、cron ジョブ用に作成した Webhook に戻り、メッセージの有効期限ロジックを完了できます。

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 経由で未配信のメッセージを送信するロジックを実装する必要があります。

まとめと要約

オフライン ユーザーを処理するには、配信されなかった RBM メッセージの取り消しロジックを構築します。メッセージを期限切れにするまでの時間は、送信する情報の時間的制約によって異なります。時間的制約のある情報では、タイムアウトを 2 時間未満にすることをおすすめします。

この例では、Cloud Datastore と Google App Engine を使用してストレージ、クエリ、取り消しプロセスを管理しますが、送受信されたメッセージを追跡するためのストレージ メカニズムは正常に動作します。

この方法がお役に立てば幸いです。