Quay lại phần Tin tức dành cho nhà phát triển

Creating Surveys with WhatsApp Flows

6 tháng 3, 2024Tác giảGafi G & Iryna Wagner

WhatsApp Flows tối ưu hóa và đơn giản hóa cách doanh nghiệp bạn thu thập dữ liệu khách hàng. Tổ chức của bạn có thể dễ dàng tiếp nhận thông tin có cấu trúc từ các tương tác với khách hàng - những người lần lượt tận hưởng trải nghiệm người dùng tích cực trong WhatsApp. WhatsApp Flows hoạt động hiệu quả trong việc thu thập dữ liệu tìm kiếm khách hàng tiềm năng, thực hiện khảo sát, giúp khách hàng đặt lịch hẹn, gửi câu hỏi cùng với mối quan tâm của khách hàng và hơn thế nữa.

Điều tuyệt vời nhất là bạn có thể cung cấp cho khách hàng của mình tất cả các lựa chọn này mà không cần xây dựng ứng dụng và phần phụ trợ phức tạp: Chỉ cần sử dụng WhatsApp làm giao diện người dùng và sử dụng webhook để ghi lại phản hồi dưới dạng tin nhắn JSON, xử lý thông tin và truy xuất dữ liệu bạn cần.

Thông qua việc minh họa bằng một công ty hư cấu, hướng dẫn này khám phá cách thiết lập cuộc khảo sát khách hàng trên WhatsApp bằng webhook. Cuộc khảo sát sẽ thu thập ý kiến đóng góp như cách khách hàng khám phá công ty và các loại chuyến tham quan ưa thích của họ để công ty có thể phục vụ khách hàng hiện tại và tương lai tốt hơn.

Thực hiện khảo sát bằng WhatsApp Flows

Điều kiện tiên quyết

Để làm theo, hãy đảm bảo bạn có những điều sau:

Quy trình

  1. Tạo ứng dụng Flask.
  2. Viết mã Python để tạo và đăng Quy trình bằng API WhatsApp Flows. Mã Python cũng sẽ gửi Quy trình đã đăng bằng API Đám mây.
  3. Tạo webhook để nghe tin nhắn trong đoạn chat.
  4. Chạy ứng dụng.

Nếu muốn xem trước dự án, bạn có thể xem mã hoàn tất.

Tạo khảo sát bằng API WhatsApp Flows

Có 2 cách để tạo Quy trình: bằng cách sử dụng Giao diện người dùng công cụ tạo quy trình hoặc API Quy trình. Hướng dẫn này sử dụng API Quy trình để thiết lập khảo sát theo chương trình.

Để xây dựng Quy trình sử dụng dữ liệu động từ máy chủ của mình, bạn có thể tạo điểm cuối kết nối cuộc khảo sát với máy chủ của riêng bạn. Bằng cách sử dụng điểm cuối, bạn có thể kiểm soát logic điều hướng giữa các màn hình Quy trình, điền dữ liệu Quy trình từ máy chủ của bạn hoặc hiển thị/ẩn các thành phần trên màn hình dựa trên tương tác của người dùng.

Ví dụ về Quy trình khảo sát sẽ được thảo luận không sử dụng bất kỳ điểm cuối nào vì không có trao đổi dữ liệu động giữa Quy trình đó và máy chủ. Bạn sẽ sử dụng webhook đoạn chat để nắm bắt thông tin từ cuộc khảo sát. Ngoài ra, bạn có thể đính kèm Quy trình vào mẫu tin nhắn trong Trình quản lý WhatsApp.

Tạo ứng dụng Flask

Đầu tiên, hãy tạo ứng dụng Flask để tương tác với API Quy trình. Chạy lệnh sau trong cửa sổ dòng lệnh của bạn để tạo môi trường ảo.

python -m venv venv
        

Sau đó, sử dụng lệnh dưới đây để kích hoạt môi trường.

source venv/bin/activate
        

Tiếp theo, sử dụng lệnh sau đây để cài đặt gói được yêu cầu.

pip install requests flask python-dotenv
        

Bạn sẽ sử dụng Flask để tạo tuyến và tương tác với API Quy trình, yêu cầu để gửi yêu cầu HTTP và python-dotenv để tải biến môi trường.

Lúc này, hãy tạo file môi trường với tên .env và dán vào thông tin sau đây.

VERIFY_TOKEN =
ACCESS_TOKEN =
WHATSAPP_BUSINESS_ACCOUNT_ID =
PHONE_NUMBER_ID =
        

Chỉ định giá trị dựa trên thông tin tài khoản nhà phát triển của bạn. Bạn có thể sử dụng bất kỳ chuỗi nào cho VERIFY_TOKEN. Biến WHATSAPP_BUSINESS_ACCOUNT_IDPHONE_NUMBER_ID là mã định dạng duy nhất của tài khoản bạn do Meta tự động tạo. The ACCESS_TOKEN dùng để xác thực và ủy quyền yêu cầu API.

Để truy cập thông tin này từ bảng điều khiển của ứng dụng trên Meta, nhấp vào WhatsApp > Thiết lập API trên bảng điều hướng bên trái như trong ảnh chụp màn hình dưới đây.

Xem trước thiết lập API

Cuối cùng, trong cùng thư mục, hãy tạo một file có tên main.py để chứa logic Python để tạo Quy trình và webhook.

Xây dựng Quy trình

Để xây dựng Quy trình, trước hết, hãy thêm gói sau vào main.py.

import os
import uuid
import requests
from dotenv import load_dotenv
from flask import Flask, request, make_response, json
        

Tiếp theo, thêm đoạn mã sau vào main.py để khởi tạo biến. Đoạn mã cũng sẽ khởi tạo Flask và gọi phương thức load_dotenv() để hỗ trợ tải biến.

app = Flask(__name__)

load_dotenv()
PHONE_NUMBER_ID = os.getenv('PHONE_NUMBER_ID')
VERIFY_TOKEN = os.getenv('VERIFY_TOKEN')
ACCESS_TOKEN = os.getenv('ACCESS_TOKEN')
WHATSAPP_BUSINESS_ACCOUNT_ID = os.getenv('WHATSAPP_BUSINESS_ACCOUNT_ID')
created_flow_id = ""
messaging_url = f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages"

auth_header = {"Authorization": f"Bearer {ACCESS_TOKEN}"}

messaging_headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {ACCESS_TOKEN}",
}
        

Sau đó, thêm tuyến sau đây để tạo Quy trình.

@app.route("/create-flow", methods=["POST"])
def create_flow():
    flow_base_url = (
        f"https://graph.facebook.com/v18.0/{WHATSAPP_BUSINESS_ACCOUNT_ID}/flows"
    )
    flow_creation_payload = {"name": "<FLOW-NAME>", "categories": '["SURVEY"]'}
    flow_create_response = requests.request(
        "POST", flow_base_url, headers=auth_header, data=flow_creation_payload
    )

    try:
        global created_flow_id
        created_flow_id = flow_create_response.json()["id"]
        graph_assets_url = f"https://graph.facebook.com/v18.0/{created_flow_id}/assets"

        upload_flow_json(graph_assets_url)
        publish_flow(created_flow_id)

        print("FLOW CREATED!")
        return make_response("FLOW CREATED", 200)
    except:
        return make_response("ERROR", 500)

Hàm gọi điểm cuối Quy trình (flow_base_url) trong khi chuyển phần tải dữ liệu (flow_creation_payload) chứa tên và hạng mục Quy trình. Giá trị có thể có cho hạng mục là: SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY hoặc OTHER.

Thay thế <FLOW-NAME> bằng tên bạn muốn, chẳng hạn như survey_flow.

Sau khi mã tạo Quy trình, mã sẽ trích xuất created_flow_id để tải lên nội dung JSON của mã.

Tải lên thành phần JSON của Quy trình

Tạo file survey.jsonnhững nội dung này. JSON chứa cấu trúc của Quy trình.

Sau đó, dán mã sau đây vào file main.py.

def upload_flow_json(graph_assets_url):
    flow_asset_payload = {"name": "flow.json", "asset_type": "FLOW_JSON"}
    files = [("file", ("survey.json", open("survey.json", "rb"), "application/json"))]

    res = requests.request(
        "POST",
        graph_assets_url,
        headers=auth_header,
        data=flow_asset_payload,
        files=files,
    )
    print(res.json())

Hàm đó tải dữ liệu JSON lên từ survey.json đến điểm cuối tài sản Quy trình.

Trong đoạn mã dưới đây, khi người dùng kích hoạt hành động nhấp, hệ thống sẽ khởi tạo on-click-action, thu thập dữ liệu trong phần tải dữ liệu. Trường "name": "complete" cho biết Quy trình chưa hoàn tất. Hệ thống sẽ đóng Quy trình và sẽ gửi phần tải dữ liệu đến máy chủ webhook của bạn.

...
"on-click-action": {
    "name": "complete",
    "payload": {
        "source": "${form.source}",
        "tour_type": "${form.tour_type}",
        "tour_quality": "${form.tour_quality}",
        "decision_influencer": "${form.decision_influencer}",
        "tour_guides": "${form.tour_guides}",
        "aspects_enjoyed": "${form.aspects_enjoyed}",
        "improvements": "${form.improvements}",
        "recommend": "${form.recommend}",
        "return_booking": "${form.return_booking}"
    }
}

...
        

Giá trị bên trong đối tượng phần tải dữ liệu có thể phản ứng với thành phần Quy trình (giống như tên thành phần trong mẫu HTML) hoặc đối tượng dữ liệu. Các khóa liên kết với các giá trị phần tải dữ liệu này được gọi là tên, tương tự như cách bạn chỉ định các biến trong ngôn ngữ lập trình.

Thành phần data-source cũng chứa ID đóng vai trò như khóa cho giá trị. Mã gửi những ID này cho lựa chọn. Ví dụ: Nếu người dùng chọn Likely cho data-source dưới đây thì mã sẽ gửi 1. Sau khi nhận dữ liệu, bạn có thể so khớp nguồn dữ liệu.

...
{
    "type": "RadioButtonsGroup",
    "required": true,
    "name": "return_booking",
    "data-source": [
        {
            "id": "0",
            "title": "Very likely"
        },
        {
            "id": "1",
            "title": "Likely"
        },
        {
            "id": "2",
            "title": "Undecided"
        },
        {
            "id": "3",
            "title": "Unlikely"
        },
        {
            "id": "4",
            "title": "Very likely"
        }
    ]
}
...
        

Quy trình có một màn hình chứa 9 câu hỏi trắc nghiệm như ở dưới đây.

Quy trình khảo sát

Bạn có thể khám phá các chi tiết của thành phần JSON trong tài liệu dành cho nhà phát triển.

Đăng Quy trình

Tiếp theo, dán hàm dưới đây vào file main.py để thêm logic để đăng Quy trình. Quy trình đã đăng là sản phẩm đã sẵn sàng nên bạn sẽ không thể thực hiện thêm thay đổi nào nữa.

def publish_flow(flow_id):
    flow_publish_url = f"https://graph.facebook.com/v18.0/{flow_id}/publish"
    requests.request("POST", flow_publish_url, headers=auth_header)
        

Hàm gọi điểm cuối đăng trong khi chuyển trong ID Quy trình.

Gửi Quy trình

Dán hàm sau đây vào file main.py để gửi Quy trình đến người dùng WhatsApp. Hàm gọi điểm cuối tin nhắn của API Đám mây trong khi chuyển trong phần tải dữ liệu của Quy trình.

def send_flow(flow_id, recipient_phone_number):
    # Generate a random UUID for the flow token
    flow_token = str(uuid.uuid4())

    flow_payload = json.dumps(
        {
            "type": "flow",
            "header": {"type": "text", "text": "Survey"},
            "body": {
                "text": "Your insights are invaluable to us – please take a moment to share your feedback in our survey."
            },
            "footer": {"text": "Click the button below to proceed"},
            "action": {
                "name": "flow",
                "parameters": {
                    "flow_message_version": "3",
                    "flow_token": flow_token,
                    "flow_id": flow_id,
                    "flow_cta": "Proceed",
                    "flow_action": "navigate",
                    "flow_action_payload": {"screen": "SURVEY_SCREEN"},
                },
            },
        }
    )

    payload = json.dumps(
        {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": str(recipient_phone_number),
            "type": "interactive",
            "interactive": json.loads(flow_payload),
        }
    )

    requests.request("POST", messaging_url, headers=messaging_headers, data=payload)
    print("MESSAGE SENT")
        

Phần tải dữ liệu của Quy trình chứa chi tiết Quy trình. Thông qua trường action.parameters.flow_token, bạn có thể chuyển một mã định danh duy nhất cho tin nhắn Quy trình sẽ truyền từ máy khách đến webhook của bạn sau khi Quy trình hoàn tất. Đối với hướng dẫn này, bạn sẽ sử dụng ID ngẫu nhiên (uuid). Mã đặt action.parameters.flow_action_payload.screen dưới dạng SURVEY_SCREEN. Đây là ID màn hình bạn muốn hiển thị khi người dùng nhấp vào action.parameters.flow_cta.

Cài đặt webhook

Logic webhook khá dễ hiểu. Logic webhook có 2 hàm là webhook_getwebhook_post dùng để xử lý các yêu cầu GETPOST tương ứng. Mã sử dụng yêu cầu GET khi thêm webhook vào ứng dụng Meta. Mã sẽ trả về hub.challenge của yêu cầu khi thành công. Yêu cầu POST in phần tải dữ liệu của tin nhắn vào cửa sổ dòng lệnh.

@app.route("/webhook", methods=["GET"])
def webhook_get():
    if (
        request.args.get("hub.mode") == "subscribe"
        and request.args.get("hub.verify_token") == VERIFY_TOKEN
    ):
        return make_response(request.args.get("hub.challenge"), 200)
    else:
        return make_response("Success", 403)
        

Yêu cầu POST trích xuất và xử lý phần tải dữ liệu tin nhắn. Vì mã chỉ phục vụ cho phần tải dữ liệu tin nhắn nên sẽ gây lỗi khi thu thập bất kỳ phần tải dữ liệu nào khác. Vì lý do này, bạn sử dụng lệnh if để kiểm tra xem liệu có nội dung messages hay không. Sau khi kiểm tra rằng nội dung JSON messages đã hiện diện, hệ thống sẽ thực hiện một quy trình kiểm tra khác để trích xuất số điện thoại của người gửi chỉ khi có nội dung text trong phần tải dữ liệu messages.

@app.route("/webhook", methods=["POST"])
def webhook_post():
    # checking if there is a messages body in the payload
    if (
        json.loads(request.get_data())["entry"][0]["changes"][0]["value"].get(
            "messages"
        )
    ) is not None:
        """
        checking if there is a text body in the messages payload so that the sender's phone number can be extracted from the message
        """
        if (
            json.loads(request.get_data())["entry"][0]["changes"][0]["value"][
                "messages"
            ][0].get("text")
        ) is not None:
            user_phone_number = json.loads(request.get_data())["entry"][0]["changes"][
                0
            ]["value"]["contacts"][0]["wa_id"]
            send_flow(created_flow_id, user_phone_number)
        else:
            flow_reply_processor(request)

    return make_response("PROCESSED", 200)
        

Ngoài ra, bạn nên sử dụng hàm trình trợ giúp sau với tên flow_reply_processor để trích xuất phản hồi từ Quy trình và gửi lại Quy trình cho người dùng. Vì phản hồi Quy trình chứa ID lựa chọn đã chọn khi thu thập dữ liệu từ RadioButtonsGroups, hàm này khớp các ID với các giá trị chuỗi tương ứng.

def flow_reply_processor(request):
    flow_response = json.loads(request.get_data())["entry"][0]["changes"][0]["value"][
        "messages"
    ][0]["interactive"]["nfm_reply"]["response_json"]

    flow_data = json.loads(flow_response)
    source_id = flow_data["source"]
    tour_type_id = flow_data["tour_type"]
    tour_quality_id = flow_data["tour_quality"]
    decision_influencer_id = flow_data["decision_influencer"]
    tour_guides_id = flow_data["tour_guides"]
    aspects_enjoyed_id = flow_data["aspects_enjoyed"]
    improvements_id = flow_data["improvements"]
    recommend_id = flow_data["recommend"]
    return_booking_id = flow_data["return_booking"]

    match source_id:
        case "0":
            source = "Online search"
        case "1":
            source = "Social media"
        case "2":
            source = "Referral from a friend/family"
        case "3":
            source = "Advertisement"
        case "4":
            source = "Others"

    match tour_type_id:
        case "0":
            tour_type = "Cultural tour"
        case "1":
            tour_type = "Adventure tour"
        case "2":
            tour_type = "Historical tour"
        case "3":
            tour_type = "Wildlife tour"

    match tour_quality_id:
        case "0":
            tour_quality = "1 - Poor"
        case "1":
            tour_quality = "2 - Below Average"
        case "2":
            tour_quality = "3 - Average"
        case "3":
            tour_quality = "4 - Good"
        case "4":
            tour_quality = "5 - Excellent"

    match decision_influencer_id:
        case "0":
            decision_influencer = "Positive reviews"
        case "1":
            decision_influencer = "Pricing"
        case "2":
            decision_influencer = "Tour destinations offered"
        case "3":
            decision_influencer = "Reputation"

    match tour_guides_id:
        case "0":
            tour_guides = "Knowledgeable and friendly"
        case "1":
            tour_guides = "Knowledgeable but not friendly"
        case "2":
            tour_guides = "Friendly but not knowledgeable"
        case "3":
            tour_guides = "Neither of the two"
        case "4":
            tour_guides = "I didn’t interact with them"

    match aspects_enjoyed_id:
        case "0":
            aspects_enjoyed = "Tourist attractions visited"
        case "1":
            aspects_enjoyed = "Tour guide's commentary"
        case "2":
            aspects_enjoyed = "Group dynamics/interaction"
        case "3":
            aspects_enjoyed = "Activities offered"

    match improvements_id:
        case "0":
            improvements = "Tour itinerary"
        case "1":
            improvements = "Communication before the tour"
        case "2":
            improvements = "Transportation arrangements"
        case "3":
            improvements = "Advertisement"
        case "4":
            improvements = "Accommodation quality"

    match recommend_id:
        case "0":
            recommend = "Yes, definitely"
        case "1":
            recommend = "Yes, but with reservations"
        case "2":
            recommend = "No, I would not"

    match return_booking_id:
        case "0":
            return_booking = "Very likely"
        case "1":
            return_booking = "Likely"
        case "2":
            return_booking = "Undecided"
        case "3":
            return_booking = "Unlikely"

    reply = (
        f"Thanks for taking the survey! Your response has been recorded. This is what we received:\n\n"
        f"*How did you hear about our tour company?*\n{source}\n\n"
        f"*Which type of tour did you recently experience with us?*\n{tour_type}\n\n"
        f"*On a scale of 1 to 5, how would you rate the overall quality of the tour?*\n{tour_quality}\n\n"
        f"*What influenced your decision to choose our tour company?*\n{decision_influencer}\n\n"
        f"*How knowledgeable and friendly were our tour guides?*\n{tour_guides}\n\n"
        f"*What aspects of the tour did you find most enjoyable?*\n{aspects_enjoyed}\n\n"
        f"*Were there any aspects of the tour that could be improved?*\n{improvements}\n\n"
        f"*Would you recommend our tour company to a friend or family member?*\n{recommend}\n\n"
        f"*How likely are you to book another tour with us in the future?*\n{return_booking}"
    )

    user_phone_number = json.loads(request.get_data())["entry"][0]["changes"][0][
        "value"
    ]["contacts"][0]["wa_id"]
    send_message(reply, user_phone_number)

After the extraction, the following send_message function sends the responses to the sender.

def send_message(message, phone_number):
    payload = json.dumps(
        {
            "messaging_product": "whatsapp",
            "to": str(phone_number),
            "type": "text",
            "text": {"preview_url": False, "body": message},
        }
    )

    requests.request("POST", messaging_url, headers=messaging_headers, data=payload)
    print("MESSAGE SENT")
        

Hàm gửi phản hồi sử dụng điểm cuối tin nhắn văn bản API Đám mây.

Bạn nên chạy ứng dụng trước khi cấu hình webhook trong bảng điều khiển Meta for Developers. Vì vậy, hãy sử dụng lệnh flask --app main run --port 5000 trong cửa sổ dòng lệnh để chạy mã. Bạn sẽ nhìn thấy thông báo * Running on http://127.0.0.1:5000 trong cửa sổ dòng lệnh nếu mọi thứ được thiết lập đúng cách.

Tiếp theo, chạy lệnh ngrokhttp 5000 trong cửa sổ dòng lệnh để lấy URL ánh xạ đến ứng dụng của bạn. Sao chép liên kết đó.

Trong bảng điều khiển Meta for Developers, tại mục WhatsApp trong bảng điều hướng bên trái, nhấp vào Cấu hình.

Cấu hình WhatsApp

Trong thẻ Webhook, nhấp vào Chỉnh sửa. Sau đó, trong hộp thoại trong trường URL gọi lại, thêm URL đã sao chép và bổ sung /webhook. Trong trường Xác minh mã, thêm mã từ biến TOKEN của file .env.

Khi hoàn tất, nhấp vào Xác minh và lưu. Hộp thoại sẽ đóng lại. Nhấp vào Quản lý và đánh dấu vào trường tinh nhắn. Thông tin nên hiển thị tương tự với hình ảnh dưới đây cùng với URL gọi lại, thông tin ẩn trong phần Xác minh mãtin nhắn được liệt kê trong phần Trường webhook.

Cấu hình WhatsApp

Lúc này, webhook đã sẵn sàng.

Chạy ứng dụng

Trong phiên bản cửa sổ dòng lệnh mới, chạy lệnh cURL dưới đây để tạo Quy trình.

curl --location --request POST 'http://127.0.0.1:5000/create-flow'
        

Trong phiên bản cửa sổ dòng lệnh hiển thị thông tin đầu ra của webhook, bạn sẽ nhìn thấy tin nhắn tương tự với tin nhắn hiển thị dưới đây.

Chạy ứng dụng

Khi người dùng gửi tin nhắn đến số WhatsApp của bạn, họ sẽ nhận Quy trình dưới dạng câu trả lời như trong ảnh chụp màn hình dưới đây.

Ví dụ gợi ý khảo sát Quy trình

Họ nhận được phản hồi có câu trả lời của họ sau khi hoàn tất cuộc khảo sát.

Ví dụ phản hồi khảo sát Quy trình

Mọi tin nhắn người dùng gửi đến số của bạn sẽ kích hoạt Quy trình. Đối với trường hợp sử dụng của bạn, tùy chỉnh mã của bạn chỉ để gửi Quy trình khảo sát trong một số trường hợp cụ thể, chẳng hạn như sau khi người dùng đã chat với doanh nghiệp bạn.

Kết luận

WhatsApp Flows nâng cao trải nghiệm người dùng thông qua giao diện tương tác để thu thập dữ liệu có cấu trúc, chẳng hạn như phản hồi khảo sát cho một công ty du lịch giả định. Doanh nghiệp chỉ cần tạo một ứng dụng Flask, triển khai mã Python để tạo và triển khai Quy trình thông qua API WhatsApp Flows, thiết lập webhook để nghe tin nhắn đến và chạy ứng dụng để cho phép thu thập phản hồi khảo sát.

Với WhatsApp Flows, tổ chức của bạn có thể thu thập dữ liệu một cách nhanh chóng và đơn giản, qua đó góp phần cải thiện tỷ lệ hoàn tất cho các tương tác của khách hàng. Sử dụng quy trình tương tự để thiết lập khảo sát của riêng bạn, cung cấp dịch vụ hỗ trợ khách hàng, trợ giúp họ đặt lịch hẹn và hơn thế nữa.

Tiếp tục khám phá tiềm năng của WhatsApp Flows. Thử ngay.