如何在 RBM 廣告活動中處理離線使用者

代理程式訊息傳送時間的一大因素,就是在代理程式傳送訊息時,您嘗試連線的使用者是否具備數據連線。如果使用者處於離線狀態,RBM 平台會儲存訊息並嘗試在 30 天內傳送。如果屆時訊息無法傳送,就會從系統中移除。

代理程式嘗試聯絡使用者時,有許多原因及情況可能都沒有連線。例如關閉行動數據方案以節省行動資費方案費用、位於沒有 Wi-Fi 連線的飛機上,或是在隧道中搭乘地鐵。視訊息送達的緊急程度而定,您的代理程式必須撤銷未傳送的 RBM 訊息,並透過其他管道重新轉送這些訊息,妥善處理離線使用者。

本文將說明如何使用 Google Cloud Datastore 追蹤您收發的訊息、如何使用 Cron revoke未傳送的訊息,以及如何透過簡訊重新轉送這些訊息。

追蹤已傳送的郵件

RBM 代理程式傳送的每則訊息都必須包含專屬的訊息 ID。如要追蹤代理程式傳送的訊息,您需要儲存每則訊息的訊息 ID、電話號碼及時間戳記。

您可以使用各種技術儲存這項資訊,但在本文中,我使用的是 Google Cloud Datastore。Cloud Datastore 是可自動處理資料分割和複製作業的 高擴充性 NoSQL 資料庫。非常適合儲存非關聯的資料,例如代理程式傳送的訊息。Cloud Datastore 仰賴有效的 Google App Engine 執行個體,所以我使用 App Engine 託管 RBM 代理程式並設定 Cron 工作。

Cloud Datastore 有許多程式語言的用戶端程式庫。在這個範例中,我使用 Node.js,並以 RBM 開發人員網站提供的第一個代理程式 Node.js 範例為基礎來建構 RBM 代理程式程式碼。我會先執行下列指令,為 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);
});

執行上述程式碼後,您可以在 Datastore 實體檢視畫面中的 Google Cloud 控制台檢查 Datastore。

Datastore

更新訊息的傳送狀態

現在代理程式會將已傳送的訊息要求儲存在 Datastore 中,因此在訊息成功傳送至使用者裝置後,我們需要更新傳送狀態。

使用者的裝置會透過 Cloud Pub/Sub,將 DELIVEREDREADIS_TYPING 事件傳送至 RBM 代理程式。在 Pub/Sub 的處理常式中,檢查已送出的事件,並將 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,並在收到傳遞通知時更新資料。在下一節中,我們在 Google App Engine 上設定一項 Cron 工作,並每 10 分鐘執行一次,以監控未傳送的訊息。

Google App Engine 上的 Cron

您可以使用 Cron 工作,安排不同的間隔工作。在 Google App Engine 中,Cron 工作已設定說明、網址和時間間隔。

在 Node.js 應用程式中,您可以在 cron.yaml 檔案中設定這些項目,以便使用 Google Cloud SDK 部署至 App Engine。如要瞭解其他語言設定,請參閱使用 cron.yaml 排定工作一文。

由於 Cron 工作需要網址,因此您必須在 Express 應用程式路由器中新增網址端點,以便 Cron 呼叫。這個 Webhook 負責查詢 Datastore 中的舊訊息、從 RBM 平台刪除舊訊息,然後透過簡訊傳送訊息給使用者。

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」>「CronJob」查看任務。

Cron 工作

查詢資料儲存庫中是否有未傳送的訊息

您在上一節設定的 Cron 工作 Webhook 中,需要加入邏輯來查詢所有已傳送訊息,且 delivered 狀態等於 false,且 lastUpdated 時間比預先滿足這個用途的預先定義逾時時間。在這個範例中,超過 1 小時的訊息將失效。

為了支援這類的複合式查詢,Datastore 必須具有同時包含 deliveredlastUpdated 屬性的複合式索引。如要這麼做,您可以使用下列資訊,在專案中建立名為 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 會自動設定索引,然後您可在「Datastore」>「Indexes」(索引) 下查看索引。

Datastore 索引

定義索引後,我們即可返回您為 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 本身不提供簡訊備用支援,因此您必須實作邏輯,才能透過簡訊傳送未傳送的訊息。

總結與重點摘要

如要處理離線使用者,您可以針對未傳送的 RBM 訊息建構撤銷邏輯。將訊息到期前的持續時間取決於您所傳輸資訊的時效性。由於提供具時效性的資訊,建議您將逾時時間設為兩小時以內。

這個範例使用 Cloud Datastore 和 Google App Engine 來管理儲存、查詢和撤銷程序,但任何用於追蹤已傳送和已傳遞訊息的儲存機制應該都能正常運作。

祝您旗開得勝,寫程式愉快!