Tutorial: Using the Messenger Webview to create richer bot-to-user interactions

Scope

  • Whitelist the domains that serve your webview.
  • Handle incoming messages.
  • Create a URL button that calls a webview.
  • Create a webview.
  • Use the Messenger Extensions SDK to access platform features in your webview.
  • Create an alternative webview that doesn’t use the Messenger Extensions SDK.
  • Process data from the webview.

What you need

Messenger Features

  • Webhook: The Messenger Platform sends events to your webhook to notify your bot when interactions or events happen.
  • Webview: Allows you to open a standard webview, where you can load web pages inside Messenger and offer an experience that is harder with standard Messenger features.
  • A URL Button: Allows you to open a web page in Messenger.
  • The Messenger Extensions SDK: Brings Messenger-specific features to any website or web app opened in the Messenger webview.

Prerequisites

  • Complete the general set up:
  • Facebook Page created: Used as the identity of your bot.
  • Facebook for Developers account: Required to create new apps, which are the core of any Facebook integration.
  • Webhook setup: The Messenger Platform sends events to your webhook to notify your bot when a variety of interactions or events happen, including when a person sends a message.
  • Facebook app setup: contains the settings for your Messenger bot, including access tokens.
  • Build a bot for Messenger. You can do so quickly using the Quick Start guide.
  • Have a server with Node.js installed.

Repository with working code

What does it look like?

The bot overview

How does it work? Step-by-step

STEP 1: Download the starter project

STEP 2: Install dependencies

  • Express to set up a web server for the webhook.
  • body-parser to parse the JSON body of the incoming POST requests.
  • request to send HTTP requests to the Messenger Platform.
  • dotenv to make setting environment variables easier.
npm install

STEP 3: Whitelist domains

curl -X POST -H "Content-Type: application/json" -d '{
"whitelisted_domains":[
"https://<DEVELOPMENT_URL>",
"https://<PRODUCTION_URL>"
]
}' "https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>"

STEP 4: Handle incoming messages

let response;if (received_message.text) {
// Code to follow
} else {
response = {
"text": `Sorry, I don't understand what you mean.`
}
}
callSendAPI(sender_psid, response);
if (received_message.text) {
switch (received_message.text.replace(/[^\w\s]/gi, '').trim().toLowerCase()) {
case "room preferences":
response = setRoomPreferences(sender_psid);
break;
default:
response = {
"text": `You sent the message: "${received_message.text}".`
};
break;
}
} else {
response = {
"text": `Sorry, I don't understand what you mean.`
}
}

STEP 5: Display the URL button

let response = {
attachment: {
type: "template",
payload: {
template_type: "button",
text: "OK, let's set your room preferences so I won't need to ask for them in the future.",
buttons: [{
type: "web_url",
url: SERVER_URL + "/options",
title: "Set preferences",
webview_height_ratio: "compact",
messenger_extensions: false
}]
}
}
};
return response;
Webview size presets

STEP 6: Create the webview page

<form action="/optionspostback" method="get">
<input type="hidden" name="psid" id="psid">
<h3>Pillows</h3>
<input type="radio" name="pillows" value="soft" checked>Soft<br>
<input type="radio" name="pillows" value="hard">Hard<br>
<h3>Bed</h3>
<input type="radio" name="bed" value="single" checked>Single<br>
<input type="radio" name="bed" value="double">Double<br>
<input type="radio" name="bed" value="twin">Twin<br>
<h3>View</h3>
<input type="radio" name="view" value="sea" checked>Sea<br>
<input type="radio" name="view" value="street">Street<br>
<input type="submit" value="Submit" id="submitButton">
</form>

STEP 7: Initialize the SDK

(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.com/en_US/messenger.Extensions.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'Messenger'));
window.extAsyncInit = function () {
// SDK loaded, code to follow
};

STEP 8: Access SDK features

MessengerExtensions.getSupportedFeatures(function success(result) {
let features = result.supported_features;
if (features.indexOf("context") != -1) {
MessengerExtensions.getContext('<YOUR_APP_ID>',
function success(thread_context) {
// success
document.getElementById("psid").value = thread_context.psid;
// More code to follow
},
function error(err) {
console.log(err);
}
);
}
}, function error(err) {
console.log(err);
});
document.getElementById('submitButton').addEventListener('click', function () {
MessengerExtensions.requestCloseBrowser(function success() {
console.log("Webview closing");
}, function error(err) {
console.log(err);
});
});

STEP 9: Allow Messenger to serve the webview

app.get('/options', (req, res) => {});
let referer = req.get('Referer');
if (referer) {
if (referer.indexOf('www.messenger.com') >= 0) {
res.setHeader('X-Frame-Options', 'ALLOW-FROM https://www.messenger.com/');
} else if (referer.indexOf('www.facebook.com') >= 0) {
res.setHeader('X-Frame-Options', 'ALLOW-FROM https://www.facebook.com/');
}
res.sendFile('public/options.html', {root: __dirname});
}

STEP 10: Process the webview data

app.get('/optionspostback', (req, res) => {});
let body = req.query;
let response = {
"text": `Great, I will book you a ${body.bed} bed, with ${body.pillows} pillows and a ${body.view} view.`
};
res.status(200).send('Please close this window to return to the conversation thread.');
callSendAPI(body.psid, response);

Conclusion

--

--

--

I explain cool tech to the World. I am a Technical Writer and blogger. I have crazy projects in progress and will speak to anyone who listens.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

【CI/CD】gulp 搭配 GitHub Actions

Streaming Electron for fun and profit

Software Engineering — Post Campus

Using Puppeteer on Netlify

359⁰ of API Design

Two Fun Programming Problems, in Python

Tools to Improve the Workflow of a Junior Web Developer

Explaining headless commerce in layman’s terms

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Chris Chinchilla

Chris Chinchilla

I explain cool tech to the World. I am a Technical Writer and blogger. I have crazy projects in progress and will speak to anyone who listens.

More from Medium

Building Uptime Monitor with Google Sheets

The Number 1 Challenge for Online Startups in 2022: #Cyberthreats.

Ethereum Game Development Company | Antier Solutions

SaaS Product Designing with Ruby on Rails Framework