Платформа WhatsApp Flows позволяет компаниям оптимизировать и упростить сбор данных о клиентах. Вы сможете легко собирать структурированную информацию о клиентах, а они будут получать положительные впечатления от взаимодействия с вашей организацией в WhatsApp. Используйте платформу WhatsApp Flows, чтобы собирать данные для генерации лидов, проводить опросы, помогать клиентам оформлять бронирования, реагировать на их вопросы и жалобы и т. д.
Чтобы предложить все эти услуги клиентам, не нужно создавать отдельное приложение и заниматься бэкенд-разработкой. Просто используйте WhatsApp в качестве фронтенд-платформы и задействуйте Webhook для сбора ответов в качестве JSON-сообщений, обработки информации и извлечения необходимых данных.
Давайте рассмотрим на примере выдуманной компании, как создать опрос для клиентов в WhatsApp с помощью Webhook. В ходе опроса, кроме прочего, можно выяснить, как клиенты узнали о компании и какие типы туров они предпочитают. Так компания сможет оптимизировать обслуживание текущих и будущих клиентов.
Чтобы приступить к работе, убедитесь, что:
Вы также можете просмотреть полный код проекта.
Есть два способа создать сценарий: с помощью Flow Builder UI или Flows API. В этой статье описано, как создавать опрос с помощью программирования и Flows API.
Если вы хотите, чтобы сценарий использовал динамические данные с вашего сервера, создайте конечную точку, которая свяжет опрос с вашим сервером. С помощью конечной точки вы сможете контролировать логику навигации между экранами сценария, извлекать данные сценария со своего сервера, а также показывать и скрывать компоненты на экранах в зависимости от действий пользователя.
В рассматриваемом примере не используется конечная точка, поскольку сценария для опроса и сервер не обмениваются динамическими данными. Для сбора информации из опроса вы будете использовать Webhook чата. Вы также можете прикрепить сценарий к шаблону сообщения в WhatsApp Manager.
Сначала создайте приложение Flask для взаимодействия с Flows API. Выполните в терминале указанную ниже команду, чтобы создать виртуальную среду.
python -m venv venv
Выполните команду ниже, чтобы активировать среду.
source venv/bin/activate
Выполните команду ниже, чтобы установить необходимые пакеты.
pip install requests flask python-dotenv
Вы будете использовать Flask для создания маршрутов и взаимодействия с Flows API, запросы для отправки HTTP-запросов и пакет python-dotenv для загрузки переменных среды.
Теперь создайте файл среды с названием .env и вставьте указанную ниже информацию.
VERIFY_TOKEN = ACCESS_TOKEN = WHATSAPP_BUSINESS_ACCOUNT_ID = PHONE_NUMBER_ID =
Укажите значения на основе информации своего аккаунта разработчика. Для VERIFY_TOKEN
можно использовать любую строку. Переменные WHATSAPP_BUSINESS_ACCOUNT_ID
и PHONE_NUMBER_ID
— это уникальные идентификаторы вашего аккаунта, автоматически сгенерированные Meta. The ACCESS_TOKEN
служит для аутентификации и авторизации запросов API.
Чтобы получить доступ к информации с панели вашего приложения Meta, нажмите WhatsApp > Настройка API на панели навигации в левой части экрана, как показано на скриншоте ниже.
Наконец, в том же каталоге создайте файл main.py. Он будет содержать логику Python для создания сценариев и Webhook.
Чтобы создать сценарий, сначала добавьте в файл main.py
указанные ниже пакеты.
import os import uuid import requests from dotenv import load_dotenv from flask import Flask, request, make_response, json
Далее добавьте указанный ниже фрагмент кода в файл main.py
, чтобы инициализировать переменные. Этот фрагмент также инициализирует Flask и вызывает пакет load_dotenv()
, чтобы загрузить переменные.
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}", }
Добавьте указанный ниже маршрут в обработчик, который создаст сценарий.
@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)
Функция вызывает конечную точку сценария (flow_base_url)
в то время, как передает полезные данные (flow_creation_payload)
с названием и категорией сценария. Возможные значения категории: SIGN_UP
, SIGN_IN
, APPOINTMENT_BOOKING
, LEAD_GENERATION
, CONTACT_US
, CUSTOMER_SUPPORT
, SURVEY
или OTHER
.
Замените <FLOW-NAME>
нужным названием, например survey_flow.
После того как код создаст сценарий, он извлечет идентификатор сценария created_flow_id
для загрузки его основного текста JSON.
Создайте файл survey.json
с этим содержанием. JSON содержит структуру сценария.
Далее вставьте следующий код в файл 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())
Эта функция загрузит данные JSON из файла survey.json
в конечные точки объектов сценария.
Во фрагменте кода ниже действие нажатия вызывает функцию on-click-action
. Выполняется сбор полезных данных. Поле "name": "complete"
указывает на то, что сценарий готов. Он закроется, а полезные данные будут отправлены на сервер вашего Webhook.
... "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}" } } ...
Значения в объектах полезных данных могут соответствовать компонентам сценария (напоминая названия элементов в HTML-формах) или объектам данных. Ключи, связанные с этими значениями полезных данных, называются именами, как при присвоении переменных в языках программирования.
Элементы data-source
также содержат ID, которые выступают в роли ключей к значениям. Код отправляет эти ID в зависимости от выбора пользователя. Например, если пользователь выбирает Likely
для элемента data-source
ниже, то код отправляет значение 1
. Вы можете сопоставить полученную информацию с источниками данных.
... { "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" } ] } ...
В сценарии есть один экран с девятью вопросами и несколькими вариантами ответа (как показано ниже).
Вы можете узнать больше об элементах JSON в документации для разработчиков.
Далее вставьте функцию ниже в файл main.py
, чтобы добавить логику публикации в сценарий. Опубликованный сценарий готов к использованию, поэтому вам не требуется вносить изменения.
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)
Эта функция вызовет конечную точку публикации, передавая при этом ID сценария.
Вставьте указанную ниже функцию в файл main.py
, чтобы отправить сценарий пользователю WhatsApp. Эта функция вызовет конечную точку сообщений Cloud API, передавая при этом полезные данные сценария:
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")
В полезных данных сценария содержатся сведения о нем. Поле action.parameters.flow_token
позволяет передать уникальный идентификатор для сообщения сценария, которое отправится от клиента в ваш Webhook, как только клиент пройдет сценарий. В рассматриваемом примере мы будем использовать случайный ID (uuid). Код устанавливает экран action.parameters.flow_action_payload.screen
в качестве экрана SURVEY_SCREEN
. Это ID экрана, который будет появляться, когда пользователь нажмет action.parameters.flow_cta
.
Логика Webhook довольно проста. В ней есть две функции: webhook_get
и webhook_post
, которые управляют запросами GET
и POST
соответственно. Код использует запрос GET
, когда Webhook добавляется в ваше приложение Meta. Если запрос успешный, он возвращает hub.challenge
запроса. Запрос POST
записывает полезные данные сообщения в терминал.
@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)
Запрос POST
извлекает и обрабатывает полезные данные сообщения. Поскольку код работает только с полезными данными сообщения, он возвращает ошибку, если получает другие полезные данные. В связи с этим нужно использовать утверждение if
, чтобы проверить наличие основного текста сообщения (messages
). После проверки наличия основного текста JSON в сообщении (messages
) выполняется другая проверка для извлечения номера телефона отправителя. Это происходит, только если в полезных данных сообщения (messages
) есть основной текст (text
).
@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)
Также следует использовать вспомогательную функцию flow_reply_processor
, чтобы извлечь ответ из сценария и отправить его назад пользователю. Поскольку ответ на вопрос сценария содержит ID выбранного варианта при сборе данных из RadioButtonsGroups
, функция сопоставляет ID с соответствующими значениями строк.
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")
Функция отправляет ответ с помощью конечной точки сообщения Cloud API.
Прежде чем настраивать Webhook на консоли Meta for Developers, запустите приложение. Используйте в терминале команду flask --app main run --port 5000
, чтобы запустить код. Если всё настроено правильно, в терминале появится сообщение * Running on http://127.0.0.1:5000
.
Выполните в терминале команду ngrok
http 5000
, чтобы получить URL, связанный с вашим приложением. Скопируйте эту ссылку.
На консоли Meta for Developers под пунктом WhatsApp на панели навигации в левой части экрана нажмите Конфигурация.
На карточке Webhook нажмите Редактировать. В диалоговом окне в поле URL обратного вызова добавьте скопированный URL и добавьте /webhook
. В поле Маркер подтверждения добавьте маркер из переменной TOKEN
файла .env
.
Когда всё будет готово, нажмите Подтвердить и сохранить. Диалоговое окно закроется. Нажмите Управлять и проверьте поле messages. Информация должна быть похожей на данные на изображении ниже: поле URL обратного вызова, скрытая информация под пунктом Маркер подтверждения и значение messages под пунктом Поля Webhook.
Настройка Webhook завершена.
В новом экземпляре терминала выполните команду cURL (как указано ниже), чтобы создать сценарий.
curl --location --request POST 'http://127.0.0.1:5000/create-flow'
В экземпляре терминала, который показывает вывод данных Webhook, появится сообщение, похожее на приведенное ниже.
Когда пользователь отправляет сообщение на ваш номер WhatsApp, он получает сценарий "Опрос" в качестве ответа, как показано на скриншоте ниже.
После прохождения опроса пользователь получит сообщение со своими ответами.
Любое сообщение, которое отправит пользователь на ваш номер, вызовет сценарий опроса. В вашем случае нужно кастомизировать код так, чтобы опрос отправлялся только в определенных ситуациях, например после завершения беседы между клиентом и компанией.
WhatsApp Flows дает возможность использовать интерактивные интерфейсы для сбора информации пользователей — например, через опрос вымышленной туристической компании. Этот процесс улучшает впечатление клиентов от взаимодействия с вашей компанией. Компании просто нужно создать приложение Flask, внедрить код Python для генерации и развертывания сценария опроса через WhatsApp Flows API, настроить Webhook для прослушивания входящих сообщений и запустить приложение для сбора ответов на опрос.
WhatsApp Flows позволяет организации быстро и просто собирать данные, что может повысить показатель завершения взаимодействий с клиентами. Подобным образом можно создать собственный опрос, предлагать поддержку клиентам, помогать им совершать бронирования и многое другое.
Продолжайте исследовать возможности WhatsApp Flows. Скорее попробуйте этот инструмент.