├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── INSTALL.md ├── README.md ├── package.json └── src └── fb.coffee /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # ========================= 31 | # Operating System Files 32 | # ========================= 33 | 34 | # OSX 35 | # ========================= 36 | 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | 52 | # Directories potentially created on remote AFP share 53 | .AppleDB 54 | .AppleDesktop 55 | Network Trash Folder 56 | Temporary Items 57 | .apdisk 58 | 59 | # Windows 60 | # ========================= 61 | 62 | # Windows image file caches 63 | Thumbs.db 64 | ehthumbs.db 65 | 66 | # Folder config file 67 | Desktop.ini 68 | 69 | # Recycle Bin used on file shares 70 | $RECYCLE.BIN/ 71 | 72 | # Windows Installer files 73 | *.cab 74 | *.msi 75 | *.msm 76 | *.msp 77 | 78 | # Windows shortcuts 79 | *.lnk 80 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## Major Changes: 3 | ### 5.0.0: 4 | - New: hubot-fb now queues message submission, which enforces displaying messages in the order they are sent. Thanks [@nsand](https://github.com/nsand)! 5 | 6 | ### 3.0.0: 7 | - __BREAKING:__ `FB_ROUTE_URL`'s new default is `/hubot/fb`, to reduce the chance of routing conflicts. 8 | - __BREAKING:__ hubot-fb now automatically sets up webhooks! Manually specifying `FB_VERIFY_TOKEN` is no longer necessary, but you now need to specify `FB_PAGE_ID`, `FB_APP_ID`, `FB_APP_SECRET`, and `FB_WEBHOOK`. For simplicity's sake, this is required even if you previously have manually set up webhooks. Thanks [@kengz](https://github.com/kengz)! 9 | - New: Setting `FB_AUTOHEAR` to `true` allows hubot-fb to treat all direct messages (which is all the API currently supports) as messages that hubot will listen to. E.g., for a robot named "hubot", both "ping" and "@hubot ping" will be passed as "@hubot ping", when `FB_AUTOHEAR` is enabled. 10 | - New: Emit [`fb_optin` and `fb_authentication`](https://developers.facebook.com/docs/messenger-platform/webhook-reference#auth) events. Thanks [@andjosh](https://github.com/andjosh)! 11 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # hubot-fb Detailed Installation Instructions 2 | ### Setup Hubot and install hubot-fb 3 | - For setting up a Hubot instance, [see here](https://hubot.github.com/docs/) 4 | - Install hubot-fb into your Hubot instance using by running `npm install -save hubot-fb` in your Hubot's root. 5 | 6 | ### Setup Facebook Page + App, and configure hubot-fb 7 | Now we'll create the Facebook Page your bot will send and receive as, and the Facebook App that will be used to manage it from Facebook's end. 8 | - Create a Facebook Page [here](https://www.facebook.com/pages/create/) 9 | - Set your `FB_PAGE_ID` from `https://www.facebook.com//info?tab=page_info`. 10 | - Create a Facebook App [here](https://developers.facebook.com/quickstarts/?platform=web). 11 | - After you create an app ID and enter you email, press 12 | ![Skip Quick Start](https://cloud.githubusercontent.com/assets/1904031/14837112/f635ca32-0c15-11e6-8fb2-3bd2185a3cd7.png) 13 | - Go to your app dashboard, and set your `FB_APP_ID` and `FB_APP_SECRET` from there. 14 | - Click "Messenger" on the App Dashboard sidebar. It should look something like this: 15 | 16 | ![image](https://cloud.githubusercontent.com/assets/1904031/14604183/71017e6e-0572-11e6-888e-1cea71ca34e0.png) 17 | 18 | - Under "Token Generation", select a page, and copy the page access token that is generated: 19 | 20 | ![image](https://cloud.githubusercontent.com/assets/1904031/14604243/da3d106e-0572-11e6-822e-ac15322bf94b.png) 21 | 22 | - Set your `FB_PAGE_TOKEN` environment variable as the page access token you copied. This will allow your bot to send as your page. 23 | - Pick an alphanumeric string and set it as your `FB_VERIFY_TOKEN`. 24 | 25 | ### Launch hubot-fb 26 | - Launch your hubot instance using hubot-fb by running `bin/hubot -a fb` (edit your Procfile to do the same on a Heroku-hosted instance) 27 | - You're now set up to send and receive messages to your hubot instance from Facebook Messenger. 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hubot-fb 2 | [![NPM Version](https://badge.fury.io/js/hubot-fb.svg)](https://badge.fury.io/js/hubot-fb) 3 | [![Dependency Status](https://david-dm.org/chen-ye/hubot-fb.svg)](https://david-dm.org/chen-ye/hubot-fb) 4 | 5 | A [Hubot](https://hubot.github.com) adapter for the [Facebook Messenger Platform](https://messengerplatform.fb.com/). This adapter fully supports everything the v2.6 Messenger platform API is capable of: 6 | - Token validation and botside autosetup 7 | - Resolving user profiles (name and profile pictures from ids) 8 | - Send and receive text messages 9 | - Send templates and images (jpgs; pngs; animated gifs) 10 | - Receive images, location, and other attachments 11 | - Template postbacks 12 | 13 | [Changelog](/CHANGELOG.md) 14 | 15 | ## Installation 16 | [See detailed installation instructions here.](/INSTALL.md) 17 | - For setting up a Hubot instance, [see here](https://hubot.github.com/docs/) 18 | - Create a [Facebook page](https://www.facebook.com/pages/create/) and [App](https://developers.facebook.com/quickstarts/?platform=web) (you can skip Quick Start after you create an App ID and enter your email), or use existing ones. 19 | - Install hubot-fb into your Hubot instance using by running ```npm install -save hubot-fb``` in your Hubot's root. 20 | - [Configure](#configuration) hubot-fb. Setting up webhooks and subscribing to pages will be done automatically by hubot-fb. 21 | - Set hubot-fb as your adapter by launching with ```bin/hubot -a fb```. (Edit your Procfile to do the same on Heroku.) 22 | 23 | 24 | ## Warnings 25 | This adapter will truncate messages longer than 320 characters (the maximum allowed by Facebook's API). For alternate behavor, use a script like [hubot-chunkify](https://github.com/chen-ye/hubot-chunkify) or [hubot-longtext](https://github.com/ClaudeBot/hubot-longtext) 26 | 27 | If you update a webhook, allow up to 10 minutes for Facebook to propagate your webhook, then it will start posting to the new webhook url. 28 | 29 | 30 | ## Configuration 31 | Required variables are in **bold**. 32 | 33 | | config variable | type | default | description | 34 | |---------------------------|---------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 35 | | **`FB_PAGE_ID`** | string | - | Your Facebook Page ID. You can find it at `https://www.facebook.com//info?tab=page_info`. | 36 | | **`FB_APP_ID`, `FB_APP_SECRET`** | string | - | Your App ID and App Secret. You can find them at `https://developers.facebook.com/apps/`. | 37 | | **`FB_WEBHOOK_BASE`** | string | - | The base url for a Facebook webhook subscription. This will be joined with `FB_ROUTE_URL`, e.g. `FB_WEBHOOK_BASE=https://mybot.com` and `FB_ROUTE_URL=/hubot/fb` will be passed to Facebook as `https://mybot.com/hubot/fb`. Note that the URL must use `https`. | 38 | | `FB_ROUTE_URL` | string | `/hubot/fb` | The webhook route path hubot-fb monitors for new message events. | 39 | | **`FB_PAGE_TOKEN`** | string | - | Your [page access token](https://developers.facebook.com/docs/messenger-platform/implementation#page_access_token). You can get one at `https://developers.facebook.com/apps//messenger/`. | 40 | | `FB_VERIFY_TOKEN` | string | - | Your [verification token](https://developers.facebook.com/docs/graph-api/webhooks#setup). This is the string your app expects when you modify a webhook subscription at `https://developers.facebook.com/apps//webhooks/`. One will be automatically set for you if you do not specify a token. | 41 | | `FB_AUTOHEAR` | boolean | `false` | Prepend a `@` to all dirrect messages to your robot, so that it'll respond to a direct message even if not explicitly invoked. E.g., for a robot named "hubot", both "ping" and "@hubot ping" will be passed as "@hubot ping" | 42 | | `FB_SEND_IMAGES` | boolean | `true` | Whether or not hubot-fb should automatically convert compatible urls into image attachments | 43 | 44 | ## Use 45 | ### Sending Rich Messages (Templates, Images) 46 | _Note: If you just want to send images, you can also send a standard image url in your message text with `FB_SEND_IMAGES` set to `true`._ 47 | To send rich messages, include in your envelope 48 | ``` 49 | envelope = 50 | { 51 | fb: { 52 | richMsg: [RICH_MESSAGE] 53 | }, 54 | user[...] 55 | } 56 | ``` 57 | 58 | In a response, this would look something like: 59 | 60 | ``` 61 | robot.hear /getting chilly/i, (res) -> 62 | res.envelope.fb = { 63 | richMsg: { 64 | attachment: { 65 | type: "template", 66 | payload: { 67 | template_type: "button", 68 | text: "Do you wanna build a snowman?", 69 | buttons: [ 70 | { 71 | type: "web_url", 72 | url: "http://www.dailymotion.com/video/x1fa7w8_frozen-do-you-wanna-build-the-snowman-1080p-official-hd-music-video_music", 73 | title: "Yes" 74 | }, 75 | { 76 | type: "web_url", 77 | title: "No", 78 | url: "http://wallpaper.ultradownloads.com.br/275633_Papel-de-Parede-Meme-Okay-Face_1600x1200.jpg" 79 | } 80 | ] 81 | } 82 | } 83 | } 84 | } 85 | res.send() 86 | ``` 87 | 88 | 89 | See Facebook's API reference [here](https://developers.facebook.com/docs/messenger-platform/send-api-reference#guidelines) for further examples of rich messages. 90 | 91 | ### Events 92 | Events allow you react to input that Hubot doesn't natively support. This adapter emits `fb_postback`, `fb_delivery`, `fb_richMsg`, and `fb_richMsg_[ATTACHMENT_TYPE]` events. 93 | 94 | Register a listener using `robot.on [EVENT_NAME] [CALLBACK]`. 95 | 96 | | event name | callback object | description | 97 | |--------------------------------|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 98 | | `fb_postback` | ``` { event: msgevent, user: hubot.user, room: string, payload: string } ``` | Emitted when a postback is triggered. | 99 | | `fb_delivery` | ```{ event: msgevent, user: hubot.user, room: string }``` | Emitted when a delivery confirmation is sent. | 100 | | `fb_richMsg` | ```{ event: msgevent, user: hubot.user, room: string, attachments: array[msgevent.message.attachment]}``` | Emitted when a message with an attachment is sent. Contains all attachments within that message. | 101 | | `fb_richMsg_[ATTACHMENT.TYPE]` | ```{ event: msgevent, user: hubot.user, room: string, attachment: msgevent.message.attachment}``` | Emitted when a message with an attachment is sent. Contains a single attachment of type [ATTACHMENT.TYPE], and multiple are emitted in messages with multiple attachments. | 102 | | `fb_optin` or `fb_authentication` | ``` { event: msgevent, user: hubot.user, room: string, ref: string } ``` | Emitted when an [authentication event](https://developers.facebook.com/docs/messenger-platform/plugin-reference#send_to_messenger) is triggered 103 | 104 | #### `fb_postback` example 105 | 106 | Responding to an event is a bit more manual—here's an example. 107 | 108 | ``` 109 | # You need this to manually compose a Response 110 | {Response} = require 'hubot' 111 | 112 | module.exports = (robot) -> 113 | 114 | # This can exist alongside your other hooks 115 | robot.on "fb_postback", (envelope) -> 116 | res = new Response robot, envelope, undefined 117 | if envelope.payload is "send_ok_face" 118 | res.send "http://wallpaper.ultradownloads.com.br/275633_Papel-de-Parede-Meme-Okay-Face_1600x1200.jpg" 119 | ``` 120 | 121 | Of course, postbacks can do anything in your application—not just trigger responses. 122 | 123 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hubot-fb", 3 | "version": "5.0.0", 4 | "description": "A Hubot adapter for Facebook Messenger", 5 | "keywords": [ 6 | "facebook messenger", 7 | "hubot", 8 | "hubot-scripts", 9 | "hubot-adapters" 10 | ], 11 | "homepage": "https://github.com/chen-ye/hubot-fb", 12 | "bugs": { 13 | "url": "https://github.com/chen-ye/hubot-fb/issues" 14 | }, 15 | "author": "Chen Ye (cye.me)", 16 | "contributors": [], 17 | "main": "src/fb.coffee", 18 | "scripts": { 19 | "test": "echo \"Error: no test specified\" && exit 1" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/chen-ye/hubot-fb.git" 24 | }, 25 | "license": "ISC", 26 | "dependencies": { 27 | "mime": "^1.3.4", 28 | "parent-require": "^1.0.0" 29 | }, 30 | "peerDependencies": { 31 | "hubot": ">=2.0" 32 | }, 33 | "devDependencies": { 34 | "coffee-script": ">=1.2.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/fb.coffee: -------------------------------------------------------------------------------- 1 | try 2 | {Robot,Adapter,TextMessage,User} = require 'hubot' 3 | catch 4 | prequire = require('parent-require') 5 | {Robot,Adapter,TextMessage,User} = prequire 'hubot' 6 | 7 | Mime = require 'mime' 8 | crypto = require 'crypto' 9 | inspect = require('util').inspect 10 | 11 | 12 | class FBMessenger extends Adapter 13 | 14 | constructor: -> 15 | super 16 | 17 | @page_id = process.env['FB_PAGE_ID'] 18 | @app_id = process.env['FB_APP_ID'] 19 | @app_secret = process.env['FB_APP_SECRET'] 20 | 21 | @token = process.env['FB_PAGE_TOKEN'] 22 | @vtoken = process.env['FB_VERIFY_TOKEN'] or crypto.randomBytes(16).toString('hex') 23 | 24 | @routeURL = process.env['FB_ROUTE_URL'] or '/hubot/fb' 25 | @webhookURL = process.env['FB_WEBHOOK_BASE'] + @routeURL 26 | 27 | _sendImages = process.env['FB_SEND_IMAGES'] 28 | if _sendImages is undefined 29 | @sendImages = true 30 | else 31 | @sendImages = _sendImages is 'true' 32 | 33 | @autoHear = process.env['FB_AUTOHEAR'] is 'true' 34 | 35 | @apiURL = 'https://graph.facebook.com/v2.6' 36 | @pageURL = @apiURL + '/'+ @page_id 37 | @messageEndpoint = @pageURL + '/messages?access_token=' + @token 38 | @subscriptionEndpoint = @pageURL + '/subscribed_apps?access_token=' + @token 39 | @appAccessTokenEndpoint = 'https://graph.facebook.com/oauth/access_token?client_id=' + @app_id + '&client_secret=' + @app_secret + '&grant_type=client_credentials' 40 | @setWebhookEndpoint = @pageURL + '/subscriptions' 41 | 42 | @msg_maxlength = 320 43 | 44 | @_dataQueue = [] 45 | 46 | send: (envelope, strings...) -> 47 | @_sendText envelope.user.id, msg for msg in strings 48 | if envelope.fb?.richMsg? 49 | @_sendRich envelope.user.id, envelope.fb.richMsg 50 | 51 | _sendText: (user, msg) -> 52 | data = { 53 | recipient: {id: user}, 54 | message: {} 55 | } 56 | 57 | if @sendImages 58 | mime = Mime.lookup(msg) 59 | 60 | if mime is "image/jpeg" or mime is "image/png" or mime is "image/gif" 61 | data.message.attachment = { type: "image", payload: { url: msg }} 62 | else 63 | data.message.text = msg.substring(0,@msg_maxlength) 64 | else 65 | data.message.text = msg 66 | 67 | @_sendAPI data 68 | 69 | _sendRich: (user, richMsg) -> 70 | data = { 71 | recipient: {id: user}, 72 | message: richMsg 73 | } 74 | @_sendAPI data 75 | 76 | _sendAPI: (data) -> 77 | @_dataQueue.push data 78 | if @_dataQueue.length == 1 79 | # Nothing else is queued up, so initiate the API request 80 | @_sendData() 81 | 82 | _sendData: () -> 83 | self = @ 84 | data = @_dataQueue[0] 85 | return unless data 86 | 87 | @robot.http(@messageEndpoint) 88 | .query({access_token:self.token}) 89 | .header('Content-Type', 'application/json') 90 | .post(JSON.stringify(data)) (error, response, body) -> 91 | self._dataQueue.shift() 92 | # If there are other items in the queue, send them 93 | if self._dataQueue.length > 0 94 | self._sendData() 95 | if error 96 | self.robot.logger.error 'Error sending message: #{error}' 97 | return 98 | unless response.statusCode in [200, 201] 99 | self.robot.logger.error "Send request returned status " + 100 | "#{response.statusCode}. data='#{data}'" 101 | self.robot.logger.error body 102 | return 103 | 104 | reply: (envelope, strings...) -> 105 | @send envelope, strings... 106 | 107 | _receiveAPI: (event) -> 108 | self = @ 109 | 110 | user = @robot.brain.data.users[event.sender.id] 111 | unless user? 112 | self.robot.logger.debug "User doesn't exist, creating" 113 | @_getUser event.sender.id, event.recipient.id, (user) -> 114 | self._dispatch event, user 115 | else 116 | self.robot.logger.debug "User exists" 117 | self._dispatch event, user 118 | 119 | _dispatch: (event, user) -> 120 | envelope = { 121 | event: event, 122 | user: user, 123 | room: event.recipient.id 124 | } 125 | 126 | if event.message? 127 | @_processMessage event, envelope 128 | else if event.postback? 129 | @_processPostback event, envelope 130 | else if event.delivery? 131 | @_processDelivery event, envelope 132 | else if event.optin? 133 | @_processOptin event, envelope 134 | 135 | _processMessage: (event, envelope) -> 136 | @robot.logger.debug inspect event.message 137 | if event.message.attachments? 138 | envelope.attachments = event.message.attachments 139 | @robot.emit "fb_richMsg", envelope 140 | @_processAttachment event, envelope, attachment for attachment in envelope.attachments 141 | if event.message.text? 142 | text = if @autoHear then @_autoHear event.message.text, envelope.room else event.message.text 143 | msg = new TextMessage envelope.user, text, event.message.mid 144 | @receive msg 145 | @robot.logger.info "Reply message to room/message: " + envelope.user.name + "/" + event.message.mid 146 | 147 | _autoHear: (text, chat_id) -> 148 | # If it is a private chat, automatically prepend the bot name if it does not exist already. 149 | if (chat_id > 0) 150 | # Strip out the stuff we don't need. 151 | text = text.replace(new RegExp('^@?' + @robot.name.toLowerCase(), 'gi'), ''); 152 | text = text.replace(new RegExp('^@?' + @robot.alias.toLowerCase(), 'gi'), '') if @robot.alias 153 | text = @robot.name + ' ' + text 154 | 155 | return text 156 | 157 | _processAttachment: (event, envelope, attachment) -> 158 | unique_envelope = { 159 | event: event, 160 | user: envelope.user, 161 | room: envelope.room, 162 | attachment: attachment 163 | } 164 | @robot.emit "fb_richMsg_#{attachment.type}", unique_envelope 165 | 166 | _processPostback: (event, envelope) -> 167 | envelope.payload = event.postback.payload 168 | @robot.emit "fb_postback", envelope 169 | 170 | _processDelivery: (event, envelope) -> 171 | @robot.emit "fb_delivery", envelope 172 | 173 | _processOptin: (event, envelope) -> 174 | envelope.ref = event.optin.ref 175 | @robot.emit "fb_optin", envelope 176 | @robot.emit "fb_authentication", envelope 177 | 178 | _getUser: (userId, page, callback) -> 179 | self = @ 180 | 181 | @robot.http(@apiURL + '/' + userId) 182 | .query({fields:"first_name,last_name,profile_pic",access_token:self.token}) 183 | .get() (error, response, body) -> 184 | if error 185 | self.robot.logger.error 'Error getting user profile: #{error}' 186 | return 187 | unless response.statusCode is 200 188 | self.robot.logger.error "Get user profile request returned status " + 189 | "#{response.statusCode}. data='#{body}'" 190 | self.robot.logger.error body 191 | return 192 | userData = JSON.parse body 193 | 194 | userData.name = userData.first_name 195 | userData.room = page 196 | 197 | user = new User userId, userData 198 | self.robot.brain.data.users[userId] = user 199 | 200 | callback user 201 | 202 | 203 | run: -> 204 | self = @ 205 | 206 | unless @token 207 | @emit 'error', new Error 'The environment variable "FB_PAGE_TOKEN" is required. See https://github.com/chen-ye/hubot-fb/blob/master/README.md for details.' 208 | 209 | unless @page_id 210 | @emit 'error', new Error 'The environment variable "FB_PAGE_ID" is required. See https://github.com/chen-ye/hubot-fb/blob/master/README.md for details.' 211 | 212 | unless @app_id 213 | @emit 'error', new Error 'The environment variable "FB_APP_ID" is required. See https://github.com/chen-ye/hubot-fb/blob/master/README.md for details.' 214 | 215 | unless @app_secret 216 | @emit 'error', new Error 'The environment variable "FB_APP_SECRET" is required. See https://github.com/chen-ye/hubot-fb/blob/master/README.md for details.' 217 | 218 | unless process.env['FB_WEBHOOK_BASE'] 219 | @emit 'error', new Error 'The environment variable "FB_WEBHOOK_BASE" is required. See https://github.com/chen-ye/hubot-fb/blob/master/README.md for details.' 220 | 221 | @robot.http(@subscriptionEndpoint) 222 | .query({access_token:self.token}) 223 | .post() (error, response, body) -> 224 | self.robot.logger.info "subscribed app to page: " + body 225 | 226 | @robot.router.get [@routeURL], (req, res) -> 227 | if req.param('hub.mode') == 'subscribe' and req.param('hub.verify_token') == self.vtoken 228 | res.send req.param('hub.challenge') 229 | self.robot.logger.info "successful webhook verification" 230 | else 231 | res.send 400 232 | 233 | @robot.router.post [@routeURL], (req, res) -> 234 | self.robot.logger.debug "Received payload: " + JSON.stringify(req.body) 235 | messaging_events = req.body.entry[0].messaging 236 | self._receiveAPI event for event in messaging_events 237 | res.send 200 238 | 239 | @robot.http(@appAccessTokenEndpoint) 240 | .get() (error, response, body) -> 241 | self.app_access_token = body.split("=").pop() 242 | self.robot.http(self.setWebhookEndpoint) 243 | .query( 244 | object: 'page', 245 | callback_url: self.webhookURL 246 | fields: 'messaging_optins, messages, message_deliveries, messaging_postbacks' 247 | verify_token: self.vtoken 248 | access_token: self.app_access_token 249 | ) 250 | .post() (error2, response2, body2) -> 251 | self.robot.logger.info "FB webhook set/updated: " + body2 252 | 253 | @robot.logger.info "FB-adapter initialized" 254 | @emit "connected" 255 | 256 | 257 | exports.use = (robot) -> 258 | new FBMessenger robot 259 | --------------------------------------------------------------------------------