From 730318e11343bc7e07be5fbf6ee1db3fb6512272 Mon Sep 17 00:00:00 2001 From: michaeld Date: Sat, 4 Mar 2017 16:00:03 +0200 Subject: [PATCH] - support version 1.4 of messenger api - new `setPersistentMenu` API aligned with v1.4 - added `setGreetingText`, `setAccountLinkingURL`, `setTargetAudience` API - aligned all thread settings to the new profile API - added support for filedata upload in the `sendAttachment` - added support for the new upload attachment API, - support for new image_aspect_ratio in generic template --- README.md | 115 ++++++++++++++++++++--- example/app.js | 206 +++++++++++++++++++++++++---------------- lib/Botly.js | 126 +++++++++++++------------ package.json | 2 +- test/botly_test.js | 224 +++++++++++++++++++++++++++++++++++++++------ 5 files changed, 491 insertions(+), 182 deletions(-) diff --git a/README.md b/README.md index e5f01c9..9b17196 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [Example](#example) - [API](#api) - [send (options[, callback])](#send-options-callback) + - [upload (options[, callback])](#upload-options-callback) - [sendText (options[, callback])](#sendtext-options-callback) - [sendAttachment (options[, callback])](#sendattachment-options-callback) - [sendImage (options[, callback])](#sendimage-options-callback) @@ -25,6 +26,8 @@ - [sendAction (options[, callback])](#sendaction-options-callback) - [sendReceipt (options[, callback])](#sendreceipt-options-callback) - [setGetStarted (options[, callback])](#setgetstarted-options-callback) + - [setGreetingText (options[, callback])](#setgreetingtext-options-callback) + - [setTargetAudience (options[, callback])](#settargetaudience-options-callback) - [setWhitelist (options[, callback])](#setwhitelist-options-callback) - [setPersistentMenu (options[, callback])](#setpersistentmenue-options-callback) - [getUserProfile (userId[, callback])](#getuserprofile-userid-callback) @@ -37,7 +40,7 @@ - [createShareLocation ()](#createsharelocation-) - [createListElement (options)](#createlistelement-options) - [createButtonTemplate (text, buttons)](#createbuttontemplate-text-buttons) - - [createGenericTemplate (elements)](#creategenerictemplate-elements) + - [createGenericTemplate (elements[, aspectRatio])](#creategenerictemplate-elements-aspectratio) - [createListTemplate (options)](#createlisttemplate-options) - [handleMessage (req)](#handlemessage-req) - [Events](#events) @@ -85,6 +88,16 @@ botly.send({ }); ``` +#### upload (options[, callback]) +```javascript +botly.upload({ + type: Botly.CONST.ATTACHMENT_TYPE.IMAGE, + payload: {url: "http://example.com/image.png"} +}, (err, data) => { + //save data.attachment_id +}); +``` + #### sendText (options[, callback]) ```javascript botly.sendText({id: userId, text: "Hi There!"}, function (err, data) { @@ -93,19 +106,20 @@ botly.sendText({id: userId, text: "Hi There!"}, function (err, data) { ``` #### sendAttachment (options[, callback]) +Also supports `options.filedata = '@/tmp/receipt.pdf'`. ```javascript botly.sendAttachment({ id: userId, type: Botly.CONST.ATTACHMENT_TYPE.IMAGE, payload: {url: "http://example.com/image.png"} -}, function (err, data) { +}, (err, data) => { //log it }); ``` #### sendImage (options[, callback]) ```javascript -botly.sendImage({id: userId, url: "http://example.com/image.png"}, function (err, data) { +botly.sendImage({id: userId, url: "http://example.com/image.png"}, (err, data) => { //log it }); ``` @@ -116,7 +130,7 @@ let buttons = []; buttons.push(botly.createWebURLButton("Go to Askrround", "http://askrround.com")); buttons.push(botly.createPostbackButton("Continue", "continue")); botly.sendButtons({id: userId, text: "What do you want to do next?", buttons: buttons} - , function (err, data) { + , (err, data) => { //log it }); ``` @@ -133,7 +147,7 @@ let element = { subtitle: "Choose now!", buttons: buttons } -botly.sendGeneric({id: userId, elements: element}, function (err, data) { +botly.sendGeneric({id: userId, elements: element, aspectRatio: Botly.CONST.IMAGE_ASPECT_RATIO.HORIZONTAL}, (err, data) => { console.log("send generic cb:", err, data); }); ``` @@ -151,14 +165,14 @@ let element = botly.createListElement({ "url": "https://peterssendreceiveapp.ngrok.io/shop_collection", } }); -botly.sendList({id: userId, elements: element, buttons: buttons}, function (err, data) { +botly.sendList({id: userId, elements: element, buttons: buttons}, (err, data) => { console.log("send generic cb:", err, data); }); ``` #### sendAction (options[, callback]) ```javascript -botly.sendAction({id: userId, action: Botly.CONST.ACTION_TYPES.TYPING_ON}, function (err, data) { +botly.sendAction({id: userId, action: Botly.CONST.ACTION_TYPES.TYPING_ON}, (err, data) => { //log it }); ``` @@ -222,21 +236,90 @@ botly.sendReceipt({id: sender, payload: payload}, function (err, data) { #### setGetStarted (options[, callback]) ```javascript -botly.setGetStarted({pageId: "myPage", payload: "GET_STARTED_CLICKED"}, function (err, body) { +botly.setGetStarted({pageId: "myPage", payload: "GET_STARTED_CLICKED"}, (err, body) => { + //log it +}); +``` + +#### setGreetingText (options[, callback]) +```javascript +botly.setGreetingText({ + pageId: "myPage", + greeting: [{ + "locale":"default", + "text":"Hello!" + }, { + "locale":"en_US", + "text":"Timeless apparel for the masses." + }]}, (err, body) => { + //log it +}); +``` + +#### setTargetAudience (options[, callback]) +```javascript +botly.setTargetAudience({ + pageId: "myPage", + audience: { + "audience_type":"custom", + "countries":{ + "whitelist":["US", "CA"] + } + }}, (err, body) => { //log it }); ``` #### setWhitelist (options[, callback]) ```javascript -botly.setWhitelist({whiteList: ["https://askhaley.com"], actionType: "add" /*default*/}, function (err, body) { +botly.setWhitelist({whiteList: ["https://askhaley.com"]}, (err, body) => { //log it }); ``` #### setPersistentMenu (options[, callback]) ```javascript -botly.setPersistentMenu({pageId: "myPage", buttons: [botly.createPostbackButton('reset', 'reset_me')]}, function (err, body) { +botly.setPersistentMenu({ + pageId: "myPage", + menu: [ + { + "locale":"default", + "composer_input_disabled":true, + "call_to_actions":[ + { + "title":"My Account", + "type":"nested", + "call_to_actions":[ + { + "title":"Pay Bill", + "type":"postback", + "payload":"PAYBILL_PAYLOAD" + }, + { + "title":"History", + "type":"postback", + "payload":"HISTORY_PAYLOAD" + }, + { + "title":"Contact Info", + "type":"postback", + "payload":"CONTACT_INFO_PAYLOAD" + } + ] + }, + { + "type":"web_url", + "title":"Latest News", + "url":"http://petershats.parseapp.com/hat-news", + "webview_height_ratio":"full" + } + ] + }, + { + "locale":"zh_CN", + "composer_input_disabled":false + } + ]}, (err, body) => { //log it }); ``` @@ -280,8 +363,9 @@ Will create a list element. `default_action` will be added `web_url` type, and w #### createButtonTemplate (text, buttons) Where `buttons` can be a single button or an array of buttons. -#### createGenericTemplate (elements) +#### createGenericTemplate (elements[, aspectRatio]) Where `elements` can be a single element or an array of elements. +and `aspectRatio` defaults to `horizontal` #### createListTemplate (options) Where `options` has `bottons` and `elements` - an array will be created automatically if a single item was passed. @@ -355,6 +439,15 @@ botly.on("referral", (sender, message, ref) => { ### Change Log +### version 1.4.0 +- support version 1.4 of messenger api +- new `setPersistentMenu` API aligned with v1.4 +- added `setGreetingText`, `setAccountLinkingURL`, `setTargetAudience` API +- aligned all thread settings to the new profile API +- added support for filedata upload in the `sendAttachment` +- added support for the new upload attachment API, +- support for new image_aspect_ratio in generic template + ### version 1.3.0 - support version 1.3 of messenger including the new list template - support for referral params on m.me links diff --git a/example/app.js b/example/app.js index 13d73fe..047c1b2 100644 --- a/example/app.js +++ b/example/app.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -"use strict"; +'use strict'; const express = require('express'); const path = require('path'); const bodyParser = require('body-parser'); @@ -8,9 +8,9 @@ const http = require('http'); const port = '8080'; -const Botly = require("../index"); +const Botly = require('../index'); const botly = new Botly({ - verifyToken: "this_is_a_token", + verifyToken: 'this_is_a_token', accessToken: process.env.ACCESS_TOKEN }); @@ -22,23 +22,23 @@ botly.on('message', (sender, message, data) => { let text = `echo: ${data.text}`; if (users[sender]) { - if (data && data.text && data.text.indexOf("image") !== -1) { - botly.sendImage({id: sender, url:"https://upload.wikimedia.org/wikipedia/en/9/93/Tanooki_Mario.jpg"}, function (err, whatever) { + if (data && data.text && data.text.indexOf('image') !== -1) { + botly.sendImage({id: sender, url:'https://upload.wikimedia.org/wikipedia/en/9/93/Tanooki_Mario.jpg'}, function (err, whatever) { console.log(err); }); } - else if (data && data.text &&data.text.indexOf("buttons") !== -1) { + else if (data && data.text &&data.text.indexOf('buttons') !== -1) { let buttons = []; - buttons.push(botly.createWebURLButton("Go to Askrround", "http://askrround.com")); - buttons.push(botly.createPostbackButton("Continue", "continue")); - botly.sendButtons({id: sender, text: "What do you want to do next?", buttons: buttons}, function (err, data) { - console.log("send buttons cb:", err, data); + buttons.push(botly.createWebURLButton('Go to Askrround', 'http://askrround.com')); + buttons.push(botly.createPostbackButton('Continue', 'continue')); + botly.sendButtons({id: sender, text: 'What do you want to do next?', buttons: buttons}, function (err, data) { + console.log('send buttons cb:', err, data); }); } - else if (data && data.text && data.text.indexOf("generic") !== -1) { + else if (data && data.text && data.text.indexOf('generic') !== -1) { let buttons = []; - buttons.push(botly.createWebURLButton("Go to Askrround", "http://askrround.com")); - buttons.push(botly.createPostbackButton("Continue", "continue")); + buttons.push(botly.createWebURLButton('Go to Askrround', 'http://askrround.com')); + buttons.push(botly.createPostbackButton('Continue', 'continue')); let element = { title: 'What do you want to do next?', item_url: 'https://upload.wikimedia.org/wikipedia/en/9/93/Tanooki_Mario.jpg', @@ -46,102 +46,102 @@ botly.on('message', (sender, message, data) => { subtitle: 'Choose now!', buttons: [botly.createWebURLButton('Go to Askrround', 'http://askrround.com')] }; - botly.sendGeneric({id: sender, elements:element}, function (err, data) { - console.log("send generic cb:", err, data); + botly.sendGeneric({id: sender, elements:element, aspectRatio: Botly.CONST.IMAGE_ASPECT_RATIO.SQUARE}, function (err, data) { + console.log('send generic cb:', err, data); }); } - else if (data && data.text && data.text.indexOf("list") !== -1) { + else if (data && data.text && data.text.indexOf('list') !== -1) { let element = botly.createListElement({ - title: "Classic T-Shirt Collection", - image_url: "https://peterssendreceiveapp.ngrok.io/img/collection.png", - subtitle: "See all our colors", + title: 'Classic T-Shirt Collection', + image_url: 'https://peterssendreceiveapp.ngrok.io/img/collection.png', + subtitle: 'See all our colors', buttons: [ - {title: "DO WORK", payload: "DO_WORK"}, + {title: 'DO WORK', payload: 'DO_WORK'}, ], default_action: { - "url": "https://peterssendreceiveapp.ngrok.io/shop_collection", + 'url': 'https://peterssendreceiveapp.ngrok.io/shop_collection', } }); let element2 = botly.createListElement({ - title: "Number 2", - image_url: "https://peterssendreceiveapp.ngrok.io/img/collection.png", - subtitle: "See all our colors", + title: 'Number 2', + image_url: 'https://peterssendreceiveapp.ngrok.io/img/collection.png', + subtitle: 'See all our colors', buttons: [ - {title: "Go to Askrround", url: "http://askrround.com"}, + {title: 'Go to Askrround', url: 'http://askrround.com'}, ], default_action: { - "url": "https://peterssendreceiveapp.ngrok.io/shop_collection", + 'url': 'https://peterssendreceiveapp.ngrok.io/shop_collection', } }); - botly.sendList({id: sender, elements: [element, element2], buttons: botly.createPostbackButton("Continue", "continue"), top_element_style: Botly.CONST.TOP_ELEMENT_STYLE.LARGE},function (err, data) { - console.log("send list cb:", err, data); + botly.sendList({id: sender, elements: [element, element2], buttons: botly.createPostbackButton('Continue', 'continue'), top_element_style: Botly.CONST.TOP_ELEMENT_STYLE.LARGE},function (err, data) { + console.log('send list cb:', err, data); }); } - else if (data && data.text && data.text.indexOf("quick") !== -1) { - botly.sendText({id: sender, text:"some question?", quick_replies: [botly.createQuickReply('option1', 'option_1')]}, function (err, data) { - console.log("send generic cb:", err, data); + else if (data && data.text && data.text.indexOf('quick') !== -1) { + botly.sendText({id: sender, text:'some question?', quick_replies: [botly.createQuickReply('option1', 'option_1')]}, function (err, data) { + console.log('send generic cb:', err, data); }); } - else if (data && data.text && data.text.indexOf("receipt") !== -1) { + else if (data && data.text && data.text.indexOf('receipt') !== -1) { let payload = { - "recipient_name": "Stephane Crozatier", - "order_number": "12345678902", - "currency": "USD", - "payment_method": "Visa 2345", - "order_url": "http://petersapparel.parseapp.com/order?order_id=123456", - "timestamp": "1428444852", - "elements": [ + 'recipient_name': 'Stephane Crozatier', + 'order_number': '12345678902', + 'currency': 'USD', + 'payment_method': 'Visa 2345', + 'order_url': 'http://petersapparel.parseapp.com/order?order_id=123456', + 'timestamp': '1428444852', + 'elements': [ { - "title": "Classic White T-Shirt", - "subtitle": "100% Soft and Luxurious Cotton", - "quantity": 2, - "price": 50, - "currency": "USD", - "image_url": "http://petersapparel.parseapp.com/img/whiteshirt.png" + 'title': 'Classic White T-Shirt', + 'subtitle': '100% Soft and Luxurious Cotton', + 'quantity': 2, + 'price': 50, + 'currency': 'USD', + 'image_url': 'http://petersapparel.parseapp.com/img/whiteshirt.png' }, { - "title": "Classic Gray T-Shirt", - "subtitle": "100% Soft and Luxurious Cotton", - "quantity": 1, - "price": 25, - "currency": "USD", - "image_url": "http://petersapparel.parseapp.com/img/grayshirt.png" + 'title': 'Classic Gray T-Shirt', + 'subtitle': '100% Soft and Luxurious Cotton', + 'quantity': 1, + 'price': 25, + 'currency': 'USD', + 'image_url': 'http://petersapparel.parseapp.com/img/grayshirt.png' } ], - "address": { - "street_1": "1 Hacker Way", - "street_2": "", - "city": "Menlo Park", - "postal_code": "94025", - "state": "CA", - "country": "US" + 'address': { + 'street_1': '1 Hacker Way', + 'street_2': '', + 'city': 'Menlo Park', + 'postal_code': '94025', + 'state': 'CA', + 'country': 'US' }, - "summary": { - "subtotal": 75.00, - "shipping_cost": 4.95, - "total_tax": 6.19, - "total_cost": 56.14 + 'summary': { + 'subtotal': 75.00, + 'shipping_cost': 4.95, + 'total_tax': 6.19, + 'total_cost': 56.14 }, - "adjustments": [ + 'adjustments': [ { - "name": "New Customer Discount", - "amount": 20 + 'name': 'New Customer Discount', + 'amount': 20 }, { - "name": "$10 Off Coupon", - "amount": 10 + 'name': '$10 Off Coupon', + 'amount': 10 } ] }; botly.sendReceipt({id: sender, payload: payload}, function (err, data) { - console.log("send generic cb:", err, data); + console.log('send generic cb:', err, data); }); } else { botly.send({id: sender, message: { text: `${users[sender].last_name}, try sending 'list'/'generic'/'receipt'/'quick'/'image'/'buttons' to try out the different types of messages` }}, function (err, data) { - console.log("regular send cb:", err, data); + console.log('regular send cb:', err, data); }); } } @@ -150,35 +150,83 @@ botly.on('message', (sender, message, data) => { users[sender] = info; botly.sendText({id: sender, text: `${text} ${users[sender].first_name}`}, function (err, data) { - console.log("send text cb:", err, data); + console.log('send text cb:', err, data); }); }); } }); botly.on('postback', (sender, message, postback) => { - console.log("postback:", sender, message, postback); + console.log('postback:', sender, message, postback); }); botly.on('delivery', (sender, message, mids) => { - console.log("delivery:", sender, message, mids); + console.log('delivery:', sender, message, mids); }); botly.on('optin', (sender, message, optin) => { - console.log("optin:", sender, message, optin); + console.log('optin:', sender, message, optin); }); botly.on('error', (ex) => { - console.log("error:", ex); + console.log('error:', ex); }); if (process.env.PAGE_ID) { botly.setGetStarted({pageId: process.env.PAGE_ID, payload: 'GET_STARTED_CLICKED'}, function (err, body) { - console.log("welcome cb:", err, body); + console.log('welcome cb:', err, body); + }); + botly.setPersistentMenu({pageId: process.env.PAGE_ID, menu: [ + { + 'locale':'default', + 'composer_input_disabled':true, + 'call_to_actions':[ + { + 'title':'My Account', + 'type':'nested', + 'call_to_actions':[ + { + 'title':'Pay Bill', + 'type':'postback', + 'payload':'PAYBILL_PAYLOAD' + }, + { + 'title':'History', + 'type':'postback', + 'payload':'HISTORY_PAYLOAD' + }, + { + 'title':'Contact Info', + 'type':'postback', + 'payload':'CONTACT_INFO_PAYLOAD' + } + ] + }, + { + 'type':'web_url', + 'title':'Latest News', + 'url':'http://petershats.parseapp.com/hat-news', + 'webview_height_ratio':'full' + } + ] + }, + { + 'locale':'zh_CN', + 'composer_input_disabled':false + } + ]}, (err, body) => { + console.log('persistent menu cb:', err, body); + }); + botly.setTargetAudience({ + pageId: process.env.PAGE_ID, + audience: { + 'audience_type':'custom', + 'countries':{ + 'whitelist':['US', 'CA'] + } + }}, (err, body) => { + console.log('set target audience', err, body); }); - botly.setPersistentMenu({pageId: process.env.PAGE_ID, buttons: [botly.createPostbackButton('reset', 'reset_me')]}, function (err, body) { - console.log("persistent menu cb:", err, body); - }) } app.use(bodyParser.json()); diff --git a/lib/Botly.js b/lib/Botly.js index 50bc717..e534d45 100644 --- a/lib/Botly.js +++ b/lib/Botly.js @@ -58,6 +58,11 @@ const TOP_ELEMENT_STYLE = { COMPACT: 'compact' }; +const IMAGE_ASPECT_RATIO = { + HORIZONTAL: 'horizontal', + SQUARE: 'square' +}; + function Botly(options) { if (!(this instanceof Botly)) { return new Botly(options); @@ -148,58 +153,51 @@ Botly.prototype.getUserProfile = function (options, callback) { }; Botly.prototype.setGetStarted = function (options, callback) { - const PAGE_URL = `${FB_URL}${options.pageId}/thread_settings`; + options.body = { + get_started: { + payload: options.payload + } + }; + this.setProfile(options, callback); +}; - request.post( - { - url: PAGE_URL, - json: true, - qs: { - access_token: this.accessToken || options.accessToken - }, - body: { - setting_type: 'call_to_actions', - thread_state: 'new_thread', - call_to_actions: [ - { - payload: options.payload - } - ] - } +Botly.prototype.setGreetingText = function (options, callback) { + options.body = { + greeting: options.greeting + }; + this.setProfile(options, callback); +}; - }, (err, res, body) => { - if (callback) { - callback(err, body); - } - }); +Botly.prototype.setPersistentMenu = function (options, callback) { + options.body = { + persistent_menu: options.menu + }; + this.setProfile(options, callback); }; -Botly.prototype.setGreetingText = function (options, callback) { - const PAGE_URL = `${FB_URL}${options.pageId}/thread_settings`; +Botly.prototype.setWhiteList = function (options, callback) { + options.body = { + whitelisted_domains: options.whiteList, + }; + this.setProfile(options, callback); +}; - request.post( - { - url: PAGE_URL, - json: true, - qs: { - access_token: this.accessToken || options.accessToken - }, - body: { - setting_type: 'greeting', - "greeting":{ - "text": options.text - } - } +Botly.prototype.setTargetAudience = function (options, callback) { + options.body = { + target_audience: options.audience, + }; + this.setProfile(options, callback); +}; - }, (err, res, body) => { - if (callback) { - callback(err, body); - } - }); +Botly.prototype.setAccountLinkingURL = function (options, callback) { + options.body = { + account_linking_url: options.url, + }; + this.setProfile(options, callback); }; -Botly.prototype.setPersistentMenu = function (options, callback) { - const PAGE_URL = `${FB_URL}${options.pageId}/thread_settings`; +Botly.prototype.setProfile = function (options, callback) { + const PAGE_URL = `${FB_URL}${options.pageId}/messenger_profile`; request.post( { @@ -208,11 +206,7 @@ Botly.prototype.setPersistentMenu = function (options, callback) { qs: { access_token: this.accessToken || options.accessToken }, - body: { - setting_type: 'call_to_actions', - thread_state: 'existing_thread', - call_to_actions: options.buttons - } + body: options.body }, (err, res, body) => { if (callback) { @@ -221,9 +215,17 @@ Botly.prototype.setPersistentMenu = function (options, callback) { }); }; -Botly.prototype.setWhiteList = function (options, callback) { - const PAGE_URL = `${FB_URL}${options.pageId}/thread_settings`; - +Botly.prototype.upload = function (options, callback) { + const PAGE_URL = `${FB_URL}${options.pageId}/message_attachments`; + options.message = options.message || { + attachment: { + type: options.type, + payload: options.payload + } + }; + if (options.filedata) { + options.message.filedata = options.filedata; + } request.post( { url: PAGE_URL, @@ -232,11 +234,8 @@ Botly.prototype.setWhiteList = function (options, callback) { access_token: this.accessToken || options.accessToken }, body: { - setting_type: 'domain_whitelisting', - whitelisted_domains: options.whiteList, - domain_action_type: options.actionType || 'add' + message: options.message } - }, (err, res, body) => { if (callback) { callback(err, body); @@ -294,6 +293,9 @@ Botly.prototype.sendAttachment = function (options, callback) { payload: options.payload } }; + if (options.filedata) { + options.message.filedata = options.filedata; + } if (options.quick_replies) { options.message.quick_replies = options.quick_replies; } @@ -328,7 +330,7 @@ Botly.prototype.sendButtons = function (options, callback) { }; Botly.prototype.sendGeneric = function (options, callback) { - options.payload = this.createGenericTemplate(options.elements); + options.payload = this.createGenericTemplate(options.elements, options.aspectRatio); options.type = ATTACHMENT_TYPE.TEMPLATE; this.sendAttachment(options, callback); }; @@ -426,7 +428,7 @@ Botly.prototype.createListElement = function (options) { }; let buttons = options.buttons; if (buttons) { - if(!Array.isArray(buttons)) { + if (!Array.isArray(buttons)) { buttons = [buttons]; } buttons = buttons.map(button => { @@ -454,11 +456,12 @@ Botly.prototype.createListElement = function (options) { return element; }; -Botly.prototype.createGenericTemplate = function (elements) { +Botly.prototype.createGenericTemplate = function (elements, aspectRatio) { if (!Array.isArray(elements)) { elements = [elements]; } return { + image_aspect_ratio: aspectRatio || IMAGE_ASPECT_RATIO.HORIZONTAL, template_type: TEMPLATE_TYPE.GENERIC, elements: elements }; @@ -476,7 +479,7 @@ Botly.prototype.createListTemplate = function (options) { elements: elements }; if (buttons) { - if(!Array.isArray(buttons)) { + if (!Array.isArray(buttons)) { buttons = [buttons]; } template.buttons = buttons; @@ -577,7 +580,8 @@ Botly.CONST = { ACTION_TYPES: ACTION_TYPES, CONTENT_TYPE: CONTENT_TYPE, WEBVIEW_HEIGHT_RATIO: WEBVIEW_HEIGHT_RATIO, - TOP_ELEMENT_STYLE: TOP_ELEMENT_STYLE + TOP_ELEMENT_STYLE: TOP_ELEMENT_STYLE, + IMAGE_ASPECT_RATIO: IMAGE_ASPECT_RATIO }; module.exports = Botly; diff --git a/package.json b/package.json index c001e25..363f3d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "botly", - "version": "1.3.5", + "version": "1.4.0", "description": "Simple Facebook Messenger Bot API", "main": "index.js", "scripts": { diff --git a/test/botly_test.js b/test/botly_test.js index 3971ef7..7c8934f 100644 --- a/test/botly_test.js +++ b/test/botly_test.js @@ -80,7 +80,7 @@ describe('Botly Tests', function () { expect(response._getData()).to.equal('42'); }); - + it('should provide an express router and reject correct verify_token if hub.mode is not "subscribe"', () => { var botly = new Botly({ @@ -321,8 +321,8 @@ describe('Botly Tests', function () { it('should handle account linking messages', done => { var linkContent = { - "status":"linked", - "authorization_code":"PASS_THROUGH_AUTHORIZATION_CODE" + "status": "linked", + "authorization_code": "PASS_THROUGH_AUTHORIZATION_CODE" }; var botly = new Botly({ accessToken: 'myToken', @@ -537,7 +537,7 @@ describe('Botly Tests', function () { notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH }); - botly.sendText({id: USER_ID, text: 'hi'}, ()=> { + botly.sendText({id: USER_ID, text: 'hi'}, () => { }); expect(request.post.calledOnce).to.be.true; @@ -562,7 +562,7 @@ describe('Botly Tests', function () { notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH }); - botly.sendAction({id: USER_ID, action: Botly.CONST.ACTION_TYPES.TYPING_ON}, ()=> { + botly.sendAction({id: USER_ID, action: Botly.CONST.ACTION_TYPES.TYPING_ON}, () => { }); expect(request.post.calledOnce).to.be.true; @@ -584,7 +584,11 @@ describe('Botly Tests', function () { notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH }); - botly.sendText({id: USER_ID, text: 'hi', quick_replies: [botly.createQuickReply('option1', 'option_1', 'http://google.com/someimage.png'), botly.createShareLocation()]}, ()=> { + botly.sendText({ + id: USER_ID, + text: 'hi', + quick_replies: [botly.createQuickReply('option1', 'option_1', 'http://google.com/someimage.png'), botly.createShareLocation()] + }, () => { }); expect(request.post.calledOnce).to.be.true; @@ -640,6 +644,31 @@ describe('Botly Tests', function () { }); + it('should upload attachment', () => { + var botly = new Botly({ + accessToken: 'myToken', + verifyToken: 'myVerifyToken', + webHookPath: '/webhook', + notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH + }); + + botly.upload({type: Botly.CONST.ATTACHMENT_TYPE.IMAGE, payload: {url: 'http://image.com', is_reusable: true}}); + + expect(request.post.calledOnce).to.be.true; + expect(request.post.args[0][0].body).to.eql({ + 'message': { + 'attachment': { + 'payload': { + 'url': 'http://image.com', + 'is_reusable': true + }, + 'type': 'image' + } + } + }); + + }); + it('should send button messages', () => { var botly = new Botly({ accessToken: 'myToken', @@ -697,7 +726,7 @@ describe('Botly Tests', function () { subtitle: 'Choose now!', buttons: [botly.createWebURLButton('Go to Askrround', 'http://askrround.com'), botly.createAccountLinkButton('http://askrround.com/login'), botly.createShareButton()] }; - botly.sendGeneric({id: USER_ID, elements: element}); + botly.sendGeneric({id: USER_ID, elements: element, aspectRatio: Botly.CONST.IMAGE_ASPECT_RATIO.HORIZONTAL}); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ @@ -726,6 +755,7 @@ describe('Botly Tests', function () { 'title': 'What do you want to do next?' } ], + 'image_aspect_ratio': 'horizontal', 'template_type': 'generic' }, 'type': 'template' @@ -760,7 +790,12 @@ describe('Botly Tests', function () { "url": "https://peterssendreceiveapp.ngrok.io/shop_collection", } }); - botly.sendList({id: USER_ID, elements: element, buttons: botly.createPostbackButton("Continue", "continue"), top_element_style: Botly.CONST.TOP_ELEMENT_STYLE.LARGE}); + botly.sendList({ + id: USER_ID, + elements: element, + buttons: botly.createPostbackButton("Continue", "continue"), + top_element_style: Botly.CONST.TOP_ELEMENT_STYLE.LARGE + }); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ @@ -854,6 +889,7 @@ describe('Botly Tests', function () { 'title': 'What do you want to do next?' } ], + 'image_aspect_ratio': 'horizontal', 'template_type': 'generic' }, 'type': 'template' @@ -888,7 +924,7 @@ describe('Botly Tests', function () { fallbackURL: 'http://askrround.com' })] }; - botly.sendGeneric({id: USER_ID, elements: element}); + botly.sendGeneric({id: USER_ID, elements: element, aspectRatio: Botly.CONST.IMAGE_ASPECT_RATIO.SQUARE}); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ @@ -913,6 +949,7 @@ describe('Botly Tests', function () { 'title': 'What do you want to do next?' } ], + 'image_aspect_ratio': 'square', 'template_type': 'generic' }, 'type': 'template' @@ -985,7 +1022,7 @@ describe('Botly Tests', function () { } ] }; - botly.sendReceipt({id: USER_ID, payload: payload, notificationType:Botly.CONST.NOTIFICATION_TYPE.REGULAR}); + botly.sendReceipt({id: USER_ID, payload: payload, notificationType: Botly.CONST.NOTIFICATION_TYPE.REGULAR}); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ @@ -1158,23 +1195,53 @@ describe('Botly Tests', function () { notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH }); - botly.setGetStarted({pageId: PAGE_ID, payload: 'GET_STARTED_CLICKED'}, ()=> { + botly.setGetStarted({pageId: PAGE_ID, payload: 'GET_STARTED_CLICKED'}, () => { }); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ - 'call_to_actions': [ + 'get_started': { + 'payload': 'GET_STARTED_CLICKED' + } + }); + + }); + + it('should set greeting text', () => { + request.post.yields(null, {}); + var botly = new Botly({ + accessToken: 'myToken', + verifyToken: 'myVerifyToken', + webHookPath: '/webhook', + notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH + }); + + botly.setGreetingText({pageId: PAGE_ID, greeting: [ + { + "locale":"default", + "text":"Hello!" + }, { + "locale":"en_US", + "text":"Timeless apparel for the masses." + } + ]}, () => { + }); + + expect(request.post.calledOnce).to.be.true; + expect(request.post.args[0][0].body).to.eql({ + 'greeting': [ { - 'payload': 'GET_STARTED_CLICKED' + "locale":"default", + "text":"Hello!" + }, { + "locale":"en_US", + "text":"Timeless apparel for the masses." } - ], - 'setting_type': 'call_to_actions', - 'thread_state': 'new_thread' + ] }); }); - it('should setwhitelist', () => { request.post.yields(null, {}); var botly = new Botly({ @@ -1184,14 +1251,41 @@ describe('Botly Tests', function () { notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH }); - botly.setWhiteList({whiteList: ["https://askhaley.com"], actionType: 'add'}, ()=> { + botly.setWhiteList({whiteList: ["https://askhaley.com"]}, () => { }); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ - 'whitelisted_domains': ["https://askhaley.com"], - 'setting_type': 'domain_whitelisting', - 'domain_action_type': 'add' + 'whitelisted_domains': ["https://askhaley.com"] + }); + + }); + + it('should set target audience', () => { + request.post.yields(null, {}); + var botly = new Botly({ + accessToken: 'myToken', + verifyToken: 'myVerifyToken', + webHookPath: '/webhook', + notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH + }); + + botly.setTargetAudience({audience: { + "audience_type":"custom", + "countries":{ + "whitelist":["US", "CA"] + } + }}, () => { + }); + + expect(request.post.calledOnce).to.be.true; + expect(request.post.args[0][0].body).to.eql({ + 'target_audience': { + "audience_type":"custom", + "countries":{ + "whitelist":["US", "CA"] + } + } }); }); @@ -1205,23 +1299,93 @@ describe('Botly Tests', function () { notificationType: Botly.CONST.NOTIFICATION_TYPE.NO_PUSH }); - botly.setPersistentMenu({pageId: PAGE_ID, buttons: [botly.createPostbackButton('reset', 'reset_me')]}, ()=> { + botly.setPersistentMenu({ + pageId: PAGE_ID, menu: [ + { + "locale": "default", + "composer_input_disabled": true, + "call_to_actions": [ + { + "title": "My Account", + "type": "nested", + "call_to_actions": [ + { + "title": "Pay Bill", + "type": "postback", + "payload": "PAYBILL_PAYLOAD" + }, + { + "title": "History", + "type": "postback", + "payload": "HISTORY_PAYLOAD" + }, + { + "title": "Contact Info", + "type": "postback", + "payload": "CONTACT_INFO_PAYLOAD" + } + ] + }, + { + "type": "web_url", + "title": "Latest News", + "url": "http://petershats.parseapp.com/hat-news", + "webview_height_ratio": "full" + } + ] + }, + { + "locale": "zh_CN", + "composer_input_disabled": false + } + ] + }, () => { }); expect(request.post.calledOnce).to.be.true; expect(request.post.args[0][0].body).to.eql({ - 'call_to_actions': [ + 'persistent_menu': [ + { + "locale": "default", + "composer_input_disabled": true, + "call_to_actions": [ + { + "title": "My Account", + "type": "nested", + "call_to_actions": [ + { + "title": "Pay Bill", + "type": "postback", + "payload": "PAYBILL_PAYLOAD" + }, + { + "title": "History", + "type": "postback", + "payload": "HISTORY_PAYLOAD" + }, + { + "title": "Contact Info", + "type": "postback", + "payload": "CONTACT_INFO_PAYLOAD" + } + ] + }, + { + "type": "web_url", + "title": "Latest News", + "url": "http://petershats.parseapp.com/hat-news", + "webview_height_ratio": "full" + } + ] + }, { - type: 'postback', - title: 'reset', - payload: 'reset_me' + "locale": "zh_CN", + "composer_input_disabled": false } - ], - 'setting_type': 'call_to_actions', - 'thread_state': 'existing_thread' + ] }); }); -}); \ No newline at end of file +});