エージェントのメッセージの配信時間に影響を与える主な要因は、エージェントがメッセージを送信したときに、到達しようとしているユーザーがデータ接続を持っているかどうかです。ユーザーがオフラインの場合、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,
});
作成したデータストア オブジェクトに対して、各メッセージの 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
を含むレスポンス オブジェクトを提供します。
以下は、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 に保存するようになったので、メッセージがユーザーのデバイスに正常に配信されたら、配信ステータスを更新する必要があります。
ユーザーのデバイスは、DELIVERED
、READ
、IS_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 ジョブ] に表示されます。
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 を使用してストレージ、クエリ、取り消しプロセスを管理しますが、送受信されたメッセージを追跡するためのストレージ メカニズムは正常に動作します。
この方法がお役に立てば幸いです。