Cách xử lý người dùng ngoại tuyến trong chiến dịch RBM

Một yếu tố chính ảnh hưởng đến thời gian gửi tin nhắn của nhân viên hỗ trợ là liệu người dùng mà bạn đang cố gắng liên hệ có kết nối dữ liệu tại thời điểm nhân viên hỗ trợ gửi tin nhắn hay không. Nếu người dùng không có kết nối mạng, nền tảng RBM sẽ lưu trữ tin nhắn và cố gắng gửi tin nhắn trong tối đa 30 ngày. Nếu đến thời điểm đó mà tin nhắn vẫn không gửi được thì tin nhắn sẽ bị xoá khỏi hệ thống.

Có nhiều lý do và tình huống mà người dùng có thể không có kết nối khi nhân viên hỗ trợ của bạn cố gắng liên hệ với họ. Họ có thể đã tắt dữ liệu để tiết kiệm tiền khi dùng gói di động, có thể đang trên máy bay mà không có kết nối Wi-Fi hoặc có thể đang đi tàu điện ngầm trong đường hầm. Tuỳ thuộc vào mức độ khẩn cấp cần gửi tin nhắn, nhân viên hỗ trợ của bạn cần xử lý linh hoạt người dùng ngoại tuyến bằng cách thu hồi tin nhắn RBM chưa được gửi và định tuyến lại các tin nhắn đó qua một kênh khác.

Trong bài viết này, tôi sẽ hướng dẫn bạn cách sử dụng Google Cloud Datastore để theo dõi tin nhắn bạn gửi và gửi, cách sử dụng cron để revoke tin nhắn chưa được gửi và cách định tuyến lại những tin nhắn đó qua SMS.

Theo dõi thư đã gửi

Mỗi tin nhắn do nhân viên hỗ trợ RBM gửi phải có một mã tin nhắn riêng. Để theo dõi tin nhắn mà nhân viên hỗ trợ gửi, bạn cần lưu mã nhận dạng tin nhắn, số điện thoại và dấu thời gian cho mỗi tin nhắn.

Bạn có thể sử dụng nhiều công nghệ để lưu trữ thông tin này, nhưng trong bài viết này, tôi sẽ sử dụng Google Cloud Datastore. Cloud Datastore là một cơ sở dữ liệu NoSQL có khả năng mở rộng cao, tự động xử lý việc phân đoạn và sao chép. Đây là giải pháp tuyệt vời để lưu trữ dữ liệu không có quan hệ như tin nhắn do nhân viên hỗ trợ gửi. Cloud Datastore phụ thuộc vào việc có một phiên bản Google App Engine đang hoạt động, vì vậy, tôi sử dụng App Engine để lưu trữ tác nhân RBM của mình và định cấu hình công việc định kỳ.

Cloud Datastore có các thư viện ứng dụng hỗ trợ nhiều ngôn ngữ. Trong ví dụ này, tôi sử dụng Node.js và lấy mã tác nhân RBM dựa trên Mẫu Node.js của Tác nhân đầu tiên có trên Trang web dành cho nhà phát triển RBM. Việc đầu tiên tôi làm là cài đặt Cloud Datastore cho dự án Node.js bằng cách chạy lệnh sau:

npm install --save @google-cloud/datastore

Trong mã nguồn tác nhân, tôi thêm tham chiếu chung đến thư viện ứng dụng Cloud Datastore.

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

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

Với đối tượng kho dữ liệu được tạo, tôi sẽ giới thiệu một hàm để lưu trữ trạng thái msisdn, message id, sent timedelivery cho từng thông báo.

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

Với chức năng này, bạn cần gọi phương thức này bất cứ khi nào tác nhân của bạn gửi tin nhắn cho người dùng. Khi thư viện ứng dụng RBM Node.js gửi thông báo RBM, thư viện sẽ cung cấp một đối tượng phản hồi trong phương thức gọi lại chứa messageId cho thông báo đã gửi đến người dùng.

Dưới đây là ví dụ về cách gửi tin nhắn văn bản thuần tuý cho người dùng và ghi lại thông tin tin nhắn sau khi giao tiếp thành công với 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);
});

Sau khi chạy mã trên, bạn có thể kiểm tra Datastore từ Google Cloud Console trong chế độ xem Thực thể Datastore.

Datastore

Cập nhật trạng thái gửi tin nhắn

Giờ đây, nhân viên hỗ trợ của bạn lưu trữ các yêu cầu tin nhắn đã gửi trong Datastore, chúng ta cần cập nhật trạng thái gửi sau khi tin nhắn được gửi thành công đến thiết bị của người dùng.

Thiết bị của người dùng gửi các sự kiện DELIVERED, READIS_TYPING cho nhân viên hỗ trợ RBM qua Cloud Pub/Sub. Trong trình xử lý cho Pub/Sub, hãy kiểm tra các sự kiện đã phân phối và cập nhật chế độ cài đặt Datastore cho cờ đã phân phối thành 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
    }
}

Nhân viên hỗ trợ lưu các tin nhắn gửi đi vào Datastore và cập nhật dữ liệu khi nhận được thông báo gửi. Trong phần tiếp theo, chúng ta sẽ thiết lập một công việc định kỳ (cron job) trên App Engine của Google để chạy 10 phút một lần nhằm theo dõi các thông báo chưa được gửi.

Trình duyệt trên App Engine của Google

Bạn có thể sử dụng tính năng cron job để lên lịch các tác vụ vào những khoảng thời gian khác nhau. Trong App Engine của Google, công việc chạy theo thời gian (cron job) được định cấu hình bằng một nội dung mô tả, URL và một khoảng thời gian.

Trong ứng dụng Node.js, bạn định cấu hình các tệp này trong tệp cron.yaml. Bạn có thể triển khai tệp này cho App Engine bằng cách sử dụng Google Cloud SDK. Đọc về các cách thiết lập cấu hình ngôn ngữ khác trong phần Lên lịch công việc bằng cron.yaml.

Vì tác vụ cron cần URL, nên bạn cần thêm điểm cuối URL vào bộ định tuyến ứng dụng nhanh để được gọi bằng cron. Webhook này chịu trách nhiệm truy vấn Datastore về các tin nhắn cũ, xoá tin nhắn cũ khỏi nền tảng RBM và gửi chúng cho người dùng qua 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();
});

Dưới đây là cấu hình tệp cron.yaml để thực thi điểm cuối này 10 phút một lần.

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

Để triển khai các tác vụ cron cho App Engine, hãy chạy lệnh sau:

gcloud app deploy cron.yaml

Sau khi triển khai, App Engine sẽ tự động định cấu hình tác vụ cron và bạn có thể xem tác vụ đó trong App Engine > Cron job (Ứng dụng > Công việc chạy ngầm).

cron job

Truy vấn Datastore để tìm thông báo chưa được gửi

Trong webhook cho công việc định kỳ mà bạn thiết lập ở phần trước, bạn cần thêm logic để tìm kiếm tất cả thông báo đã gửi có trạng thái delivered bằng false và có thời gian lastUpdated cũ hơn thời gian chờ xác định trước. Điều này phù hợp với trường hợp sử dụng của chúng ta. Trong ví dụ này, thư sẽ hết hạn cũ hơn một giờ.

Để hỗ trợ một truy vấn tổng hợp như vậy, Datastore cần có một chỉ mục tổng hợp chứa cả thuộc tính deliveredlastUpdated. Để thực hiện việc này, bạn có thể tạo một tệp trong dự án có tên là index.yaml với thông tin sau:

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

Tương tự như việc triển khai công việc định kỳ mà bạn đã xác định trước đó, hãy sử dụng Google Cloud SDK để triển khai chỉ mục tổng hợp mà bạn đã xác định bằng lệnh sau:

gcloud datastore create-indexes index.yaml

Sau khi triển khai, App Engine sẽ tự động định cấu hình chỉ mục và bạn có thể xem chỉ mục đó trong Datastore > Indexes (Kho dữ liệu > Chỉ mục).

Chỉ mục kho dữ liệu

Với chỉ mục đã xác định, chúng ta có thể quay lại webhook mà bạn đã tạo cho lệnh in cron và hoàn tất logic hết hạn thông báo:

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 vốn không hỗ trợ tính năng dự phòng SMS, vì vậy, bạn sẽ cần triển khai logic để gửi các tin nhắn chưa được gửi qua SMS.

Tổng kết và tóm tắt

Để xử lý người dùng ngoại tuyến, bạn có thể xây dựng logic thu hồi cho các thông báo RBM chưa được gửi. Khoảng thời gian bạn dùng trước khi thông báo hết hạn tuỳ thuộc vào mức độ nhạy cảm của thông tin mà bạn truyền đi. Đối với thông tin có giới hạn về thời gian, bạn nên dùng thời gian chờ dưới 2 giờ.

Ví dụ này sử dụng Cloud Datastore và Google App Engine để quản lý quy trình lưu trữ, truy vấn và thu hồi, nhưng mọi cơ chế lưu trữ để theo dõi thông báo đã gửi và gửi sẽ hoạt động.

Chúc bạn may mắn và thành công lập trình vui vẻ!