Skip to content

Instantly share code, notes, and snippets.

@lizettepreiss
Last active October 30, 2018 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lizettepreiss/efd90b50abeef4c768f727eabce2d8d3 to your computer and use it in GitHub Desktop.
Save lizettepreiss/efd90b50abeef4c768f727eabce2d8d3 to your computer and use it in GitHub Desktop.
FacebookMessengerWebhookExample. Note that it is a requirement to have an SSL certificate for the webhook. For dev, I did not have a certificate, nor a public IP. I got around that by registering an account at NGROK and letting NGROK handle the SSL aspect for me. Note that you need to recreate your access tokens each time you restart NGROK as th…
package preiss.facebookmessenger;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
public class ButtonsRequest {
public String getButtonsJson() {
String json = "";
try (FileInputStream inputStream = new FileInputStream(
"C:/<yourpath>/ButtonsTemplateExample.json")) {
json = IOUtils.toString(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return json;
}
}
{
"recipient":{
"id":"<PSID>"
},
"message":{
"attachment":{
"type":"template",
"payload":{
"template_type":"button",
"text":"What do you want to do next?",
"buttons":[
{
"type":"web_url",
"url":"https://www.messenger.com",
"title":"One"
},
{
"type":"web_url",
"url":"https://www.property24.com",
"title":"Two"
},
{ "type":"web_url",
"url":"http://www.holidogz.co.za",
"title":"Three"
}
]
}
}
}
}
package preiss.facebookmessenger;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
/**
* @author preissl
*
*/
public class HTTPFacebookMessengerJSONWebhook implements HttpHandler {
private String challenge = "";
/*
* I got the following information from Stackoverflow.
*
* GET PERMANENT ACCESS TOKEN AS FOLLOWS
*
* 1. Get User Short-Lived Access Token Go to the Graph API Explorer (https://developers.facebook.com/tools/explorer) Select the application you
* want to get the access token for (in the "Application" drop-down menu, not the "My Apps" menu). Click "Get Token" "Get User Access Token". In
* the pop-up, under the "Extended Permissions" tab, check "manage_pages". Click "Get Access Token". Grant access from a Facebook account that has
* access to manage the target page. Note that if this user loses access the final, never-expiring access token will likely stop working. The
* token that appears in the "Access Token" field is your short-lived access token.
*
* 2. Generate Long-Lived Access Token Following these instructions from the Facebook docs, make a GET request to
*
* https://graph.facebook.com/v2.10/oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={app_secret}&fb_exchange_token
* ={short_lived_token}
*
* entering in your app's ID and secret and the short-lived token generated in the previous step.
*
* You cannot use the Graph API Explorer. For some reason it gets stuck on this request. I think it's because the response isn't JSON, but a query
* string. Since it's a GET request, you can just go to the URL in your browser.
*
* The response should look like this:
*
* {"access_token":"ABC123","token_type":"bearer","expires_in":5183791}
*
* "ABC123" will be your long-lived access token. You can put it into the Access Token Debugger to verify. Under "Expires" it should have
* something like "2 months".
*
* 3. Get User ID Using the long-lived access token, make a GET request to
*
* https://graph.facebook.com/v2.10/me?access_token={long_lived_access_token}
*
* The id field is your account ID. You'll need it for the next step.
*
* 4. Get Permanent Page Access Token Make a GET request to
*
* https://graph.facebook.com/v2.10/{account_id}/accounts?access_token={long_lived_access_token}
*
* The JSON response should have a data field under which is an array of items the user has access to. Find the item for the page you want the
* permanent access token from. The access_token field should have your permanent access token. Copy it and test it in the Access Token Debugger.
* Under "Expires" it should say "Never".
*/
private static String yourFBPageID = "XXXXXXXXXXX";
private static String yourTestAppId = "YYYYYYYYYYY";
private static final String yourTestAppSecret = "ZZZZZZZZZZZZZZZZZ";
private static final String PSID = "AAAAAAAAAAAAAAAAA";
private static final String ASID = "BBBBBBBBBBBBBBBBBBBBB";
private static final String usrAccTok = "";
/*
* Get short term Page Access Tokens here: https://developers.facebook.com/tools/explorer/ Click "Application" and choose your app. Click Get
* Token and choose Page Access Token. Then choose your page and it auto populates the "Access Token" field. Use the string in the field.
*/
private static String shortTermPageAccTok = "asdfasdfasdfasdfasdfasfdasfdas";
private static String longLivedAccTok = "zxcvzxcvxzcvzxcvzxvzxcvzxv";
private static String permPageAccTok = "fghjfghjfgjhfghjfghhjfghjfghjfg";
private static String urlTempMe = "https://graph.facebook.com/v2.6/me/messages?access_token=" + shortTermPageAccTok;
private static String urlTempPage = "https://graph.facebook.com/v2.6/" + yourFBPageID + "/messages?access_token="
+ shortTermPageAccTok;
private static String urlPermMe = "https://graph.facebook.com/v2.6/me/messages?access_token=" + permPageAccTok;
private static String urlPermPage = "https://graph.facebook.com/v2.6/" + yourFBPageID + "/messages?access_token="
+ permPageAccTok;
private static String url = urlTempMe;
// CONSTANT
private static final int PORT = 4025;
private static final String CONTEXT = "/webhookcontext";
private static final boolean VERIFY_WEBHOOK = false;
private static final String HUB_CHALLENGE = "hub.challenge";
// private static final int ASYNC_REPLY = RequestMessageFactory.GREETING;
// private static final int ASYNC_REPLY = RequestMessageFactory.PICS;
private static final int ASYNC_REPLY = RequestMessages.LIST;
// private static final int ASYNC_REPLY = RequestMessageFactory.BUTTONS;
/**
* main
*
* @param args
*/
public static void main(String[] args) {
new HTTPFacebookJSONListener().run();
}
/**
*
*/
public void run() {
// get page access token
HttpServer server = null;
try {
server = HttpServer.create(new InetSocketAddress(PORT), 10);
server.setExecutor(Executors.newFixedThreadPool(10));
server.start();
server.createContext(CONTEXT, this);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
/**
*
*/
public void handle(HttpExchange httpExch) throws IOException {
String query = httpExch.getRequestURI().getQuery();
if (query != null) {
Map<String, String> params = queryToMap(query);
challenge = params.get(HUB_CHALLENGE);
}
InputStream inputStream = httpExch.getRequestBody();
StringBuilder sb = new StringBuilder();
int i;
while ((i = inputStream.read()) != -1) {
sb.append((char) i);
}
inputStream.close();
String incomingJSONContent = sb.toString();
(new JsonParser()).parseJson(incomingJSONContent);
replyOK(httpExch); // SYNCHRONOUS REPLY TO FB
if (!VERIFY_WEBHOOK && !incomingJSONContent.contains("delivery") && !incomingJSONContent.contains("read")) {
// get recipient ID from incoming message
asyncReply(httpExch);
}
}
/**
* This method sends an asynchronous message to Facebook (See the Facebook Messenger SendAPI specification).
* @see <a href="https://developers.facebook.com/docs/messenger-platform/reference/send-api"</a>
*
* @param httpExch
*/
private void asyncReply(HttpExchange httpExch) {
// Get this from the incoming message in due course after POC
String recipientId = PSID;
String message = (new RequestMessages(recipientId)).getMessage(ASYNC_REPLY);
HttpURLConnection conn;
URL oURL;
InputStream in = null;
try {
oURL = new URL(url);
conn = (HttpURLConnection) oURL.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-type", ("application/json"));
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.forEach(conn::setRequestProperty);
try (OutputStream out = conn.getOutputStream()) {
out.write(message.getBytes());
}
int httpStatusCode = conn.getResponseCode();
StringBuilder response = new StringBuilder();
if (httpStatusCode >= 200 && httpStatusCode < 300) {
in = conn.getInputStream();
} else {
in = conn.getErrorStream();
}
if (in == null) {
response.append("No Response");
} else {
int i;
while ((i = in.read()) != -1) {
response.append((char) i);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
in = null;
}
}
}
/**
* Sending synchronous 200 OK response as required by FB API Spec for the webhook.
* This method is also used in order to send the challenge back when verifying a webhook.
* @see <a href="https://developers.facebook.com/docs/messenger-platform/getting-started/webhook-setup"</a>
* @param httpExch
*/
private void replyOK(HttpExchange httpExch) {
try {
Headers responseHeaders = httpExch.getResponseHeaders();
responseHeaders.add("Content-Type", ("application/json"));
httpExch.sendResponseHeaders(200, challenge.length());
try (OutputStream os = httpExch.getResponseBody()) {
if (!challenge.isEmpty()) {
os.write(challenge.getBytes());
}
}
} catch (IOException e1) {
System.out.println(e1.getMessage());
}
}
/**
* Utility method to get the FB Webhook Challenge when it is sent.
* @param query
* @return
*/
private Map<String, String> queryToMap(String query) {
Map<String, String> result = new HashMap<>();
for (String param : query.split("&")) {
String[] entry = param.split("=");
if (entry.length > 1) {
result.put(entry[0], entry[1]);
} else {
result.put(entry[0], "");
}
}
return result;
}
}
package preiss.facebookmessenger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class JsonParser {
protected void parseJson(String json) {
try {
JSONObject obj = new JSONObject(json);
JSONArray arr = obj.getJSONArray("entry");
sysout("id", "" + arr.getJSONObject(0).getString("id"));
sysout("time","" + arr.getJSONObject(0).getLong("time"));
sysout("messaging.0.sender.id","" + arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("sender").getString("id"));
sysout("messaging.0.recipient.id", "" + arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("recipient").getString("id"));
sysout("messaging.0.timestamp","" + arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getLong("timestamp"));
sysout("messaging.0.message.text", arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("message").getString("text"));
sysout("messaging.0.seq","" + arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("message").getLong("seq"));
sysout("messaging.0.mid", arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("message").getString("mid"));
sysout("messaging.0.message.nlp.entities.sentiment.0.value", arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("message").getJSONObject("nlp").getJSONObject("entities").getJSONArray("sentiment").getJSONObject(0).getString("value"));
sysout("messaging.0.message.nlp.entities.sentiment.0.value", arr.getJSONObject(0).getJSONArray("messaging").getJSONObject(0).getJSONObject("message").getJSONObject("quick_reply").getJSONObject("entities").getString("payload"));
} catch (JSONException e) {
System.out.println(e.getMessage());
}
}
private void sysout(String a, String b) {
System.out.println(a + ": " + b );
}
}
package preiss.facebookmessenger;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
public class ListRequest {
public String getListJson() {
String json = "";
try (FileInputStream inputStream = new FileInputStream(
"C:/2_OWN_TECH/Projects-Java/SANDBOX/src/preiss/facebookmessenger/ListTemplateExample.json")) {
json = IOUtils.toString(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return json;
}
}
{
"recipient":{
"id":"<PSID>"
},
"message": {
"attachment": {
"type": "template",
"payload": {
"template_type": "list",
"top_element_style": "compact",
"elements": [
{
"title": "Classic T-Shirt Collection",
"subtitle": "See all our colors",
"image_url": "https://placeholder.com/25x75",
"buttons": [
{
"title": "View",
"type": "web_url",
"url": "https://placeholder.com/",
"messenger_extensions": true,
"webview_height_ratio": "tall",
"fallback_url": "https://placeholder.com/"
}
]
},
{
"title": "Classic White T-Shirt",
"subtitle": "See all our colors",
"default_action": {
"type": "web_url",
"url": "https://placeholder.com/",
"messenger_extensions": false,
"webview_height_ratio": "tall"
}
},
{
"title": "Classic Blue T-Shirt",
"image_url": "https://placeholder.com/50x50",
"subtitle": "100% Cotton, 200% Comfortable",
"default_action": {
"type": "web_url",
"url": "https://placeholder.com/",
"messenger_extensions": true,
"webview_height_ratio": "tall",
"fallback_url": "https://placeholder.com/"
},
"buttons": [
{
"title": "Shop Now",
"type": "web_url",
"url": "https://placeholder.com/",
"messenger_extensions": true,
"webview_height_ratio": "tall",
"fallback_url": "https://placeholder.com/"
}
]
}
],
"buttons": [
{
"title": "View More",
"type": "postback",
"payload": "VIEW MORE PLEASE"
}
]
}
}
}
}
package preiss.facebookmessenger;
import java.util.HashMap;
import java.util.Map;
/**
* Horrible cobbled together class to return a variety of JSON messages that implement the Facebook SendAPI
* to send to Facebook.
*
* @author PreissL
*
*/
public class RequestMessages {
private String imgUrl = "http://yourWhitelistedUrl.co.za/images/image.png";
private Map<Integer, String> jsonMessageMap = new HashMap<>();
// STATIC
public static int GREETING = 1;
public static int PICS = 3;
public static int LIST = 4;
public static int BUTTONS = 2;
private String getGreeting(String rId) {
String greeting = "{ 'messaging_type': 'RESPONSE', 'recipient': {" + " 'id': '" + rId + "' },'message': {"
+ " 'text': 'greetings1!'}}";
return greeting;
}
private String getPayload(String rId) {
String s = "{ " + " 'recipient':{ 'id':'" + rId + "' " + " }, 'message':{ "
+ " 'text': 'Here is a quick reply!', " + " 'quick_replies':[ { "
+ " 'content_type':'text', 'title':'Send Your Location?', " // This is just a dumb clickable title
+ " 'payload':'<POSTBACK_PAYLOAD>', " + " 'image_url':'" + this.imgUrl + "' }, " // the tiny icon image in the title
+ " { 'content_type':'location' } " // This identifies the type of message
+ " ] } } ";
return s;
}
private String getListJson(String rId) {
String showList = new ListRequest().getListJson();
showList = showList.replace("<PSID>", rId);
return showList;
}
private String getButtonsJson(String rId) {
String showList = new ButtonsRequest().getButtonsJson();
showList = showList.replace("<PSID>", rId);
return showList;
}
public RequestMessages(String rId) {
jsonMessageMap = new HashMap<>();
jsonMessageMap.put(GREETING, getGreeting(rId));
jsonMessageMap.put(BUTTONS, getButtonsJson(rId));
jsonMessageMap.put(PICS, getPayload(rId));
jsonMessageMap.put(LIST, getListJson(rId));
}
public String getMessage(Integer type) {
return jsonMessageMap.get(type);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment