{"id":17967886,"url":"https://github.com/simonprickett/bartfbchatbot","last_synced_at":"2025-10-30T13:09:20.706Z","repository":{"id":149218905,"uuid":"56114282","full_name":"simonprickett/bartfbchatbot","owner":"simonprickett","description":"Facebook Messenger Chatbot for BART.","archived":false,"fork":false,"pushed_at":"2017-02-24T06:03:41.000Z","size":389,"stargazers_count":34,"open_issues_count":1,"forks_count":13,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-19T23:52:59.992Z","etag":null,"topics":["bart-api","facebook-platform","messenger-bot","nodejs"],"latest_commit_sha":null,"homepage":"https://simonprickett.dev/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simonprickett.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-13T02:28:39.000Z","updated_at":"2024-03-27T13:29:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"c83fb660-9b68-46c8-98b5-39b4a4391187","html_url":"https://github.com/simonprickett/bartfbchatbot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonprickett%2Fbartfbchatbot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonprickett%2Fbartfbchatbot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonprickett%2Fbartfbchatbot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonprickett%2Fbartfbchatbot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simonprickett","download_url":"https://codeload.github.com/simonprickett/bartfbchatbot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245426296,"owners_count":20613325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bart-api","facebook-platform","messenger-bot","nodejs"],"created_at":"2024-10-29T14:09:57.901Z","updated_at":"2025-10-30T13:09:20.619Z","avatar_url":"https://github.com/simonprickett.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BART Chat Bot for Facebook Messenger\n\nFacebook Messenger chat bot for BART (Bay Area Rapid Transit).\n\nImplemented using Node JS, and can be hosted anywhere that meets the following criteria:\n\n* Accessible from the internet\n* Uses SSL (Facebook requires bots to use SSL)\n\nI have been running this using the AWS Elastic Beanstalk hosting environment, and am using Cloudflare to provide SSL.\n\nCloudflare is set up for the domain that I am hosting the bot on, there's a DNS CNAME pointing:\n\nbartfbsecurechatbot.mydomain.mytld\n\nto the AWS Elastic Beanstalk instance that the bot runs on.\n\nAs Cloudflare sits in front of that DNS CNAME I can use their free SSL, and configure the Facebook platform to see my bot at an SSL protected URL.\n\nCommunications between Cloudflare and Elastic Beanstalk remain via regular HTTP for now, and this isn't something you should do in production where you want SSL enabled hosting.\n\n## Video\n\nThe bot isn't publically available as I haven't submitted it to Facebook for approval, nor scaled the infrastructure to operate as internet scale.  It's more of a coding exercise / demo than something I would put into production long term.\n\nTo see a YouTube video of the bot working, click the screenshot below.\n\n[![Hey BART Bot](bart_fb_bot_screenshot.png)](https://www.youtube.com/watch?v=_zUNHfDCsDk \"Hey BART Bot\")\n\n## BART API\n\nThis project uses my BART JSON API:\n\n* [GitHub Repo](https://github.com/simonprickett/bartnodeapi)\n* [Running Instance used by this project to get data from BART](http://bart.crudworks.org/api)\n\nThis in turn makes calls out to the real BART API, which returns XML.  I decided a while back that I wanted a JSON based API, so wrote my own conversion layer which is what I am talking to from the bot in this project.\n\nRight now I am not using the API call to get the route and price for a journey between two stations, but I aim to add that in future.\n\n## Bot Backend Node JS Application Initial Setup\n\nWe can write the bot's backend in anything that can live on a SSL URL, receive HTTP requests and respond to them.  As all of the requests would be coming from Facebook, we may need to consider making sure our hosting choices scale.  \n\nAWS Lambda would potentially be a good option for this.  As I wanted to learn about Facebook Messenger bots with minimum other distractions, I went with AWS Elastic Beanstalk and Node JS as I am familiar with scaffolding applications quickly there, and don't intend putting my bot into production use.\n\nTo keep things simple, I used the popular [Express](http://expressjs.com/) web framework and [Request](https://www.npmjs.com/package/request) HTTP client for making calls to the BART JSON API endpoints.\n\nWe need to get something basic running in order to register a webhook with the Facebook platform in the next step.\n\nAs part of the initial handshake with the Facebook platform, our application needs to respond to a GET request to `/webhook/`, verify a validation token and reply with a \"challenge\" value that Facebook sends in the request.\n\nPick a validation token - for example \"super_secret_squirrel\", then deploy an application that contains the following route somewhere that it can be accessed at a HTTPS URL:\n\n```javascript\napp.get('/webhook/', function (req, res) {\n  if (req.query['hub.verify_token'] === 'super_secret_squirrel') {\n    res.send(req.query['hub.challenge']);\n  }\n  res.send('Error, wrong validation token');\n})\n```\n\nFacebook documentation on this can be found [here](https://developers.facebook.com/docs/messenger-platform/quickstart).\n\nBefore adding any more logic to the application, we need to do some setup on the Facebook platform.\n\n## Initial Facebook Setup\n\nFor this exercise, we'll need to create a Facebook Page and App for the bot.  I'm assuming you are familiar with the [Facebook Developer Portal](https://developers.facebook.com/) so won't cover this in step by step detail.  \n\nThe Facebook documentation for creating a Messenger bot can be found [here](https://developers.facebook.com/docs/messenger-platform).\n\n### Create a Facebook Page\n\nCreate a new Facebook Page to use with the bot, or use one you already have.\n\nFor testing a bot, this doesn't have to be a published page.  The bot wil use the profile pic from your page as its avatar in Messenger conversations.\n\nIf you're going to run your bot in pre-release / sandbox mode, then you'll want to make your Facebook friends whom you also want to be able to use the bot editors of your unpublished page, as they won't be able to see it otherwise.\n\n### Create a Facebook App\n\nYou will need a new Facebook App for your bot, and you can keep it in sandbox mode.  When creating a new app, add the \"Messenger\" product. Facebook documentation for each of these steps can be found [here](https://developers.facebook.com/docs/messenger-platform/quickstart).\n\n### Set Callback URL and Verify Token\n\nYou will be asked for a Callback URL, set this to the full HTTPS URL for your Node application's webhook route e.g:\n\n```\nhttps://whatever.something.com/webhook\n```\nYou will also need to add your verification token ('super_secret_squirrel' from the webhook code we wrote earlier) into the dialog that appear, and check the boxes to subscribe to:\n\n* `message_deliveries`\n* `messages`\n* `message_optins`\n* `messaging_postbacks`\n\n### Generate Page Token\n\nIn the Messenger properties page for your app, there's a Token Generation section.  Select the page that you created earlier from the \"Page\" drop down.  When the token appears, copy that as it will be needed for the next step.\n\n### Subscribe the App to the Facebook Page\n\nTo associate your app with the Facebook page for the purposes of receiving updates from it, run the following at the command line on your local machine.\n\n```\ncurl -X POST \"https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=\u003cFACEBOOK_PAGE_ACCESS_TOKEN\u003e\"\n```\n\nSubstitute `\u003cFACEBOOK_PAGE_ACCESS_TOKEN\u003e` for the token that was generated in the previous step.\n\nYou should now have things setup so that messages sent to your page from Facebook users are routed to the Node application for processing.\n\n## Adding Logic to the Node Backend\n\nThe Node backend application now needs to be modified to receive and process messages from the Facebook platform, and respond with appropriately formatted JSON replies that Facebook will render as messages to the user from the bot.\n\nThe Facebook platform communicates with the bot using a single `POST` route to `/webhook/`, so we need to add a handler for that to our Node/Express application.\n\nFacebook provides some boilerplate code for this that looks like this:\n\n```javascript\napp.post('/webhook/', function (req, res) {\n  messaging_events = req.body.entry[0].messaging;\n  for (i = 0; i \u003c messaging_events.length; i++) {\n    event = req.body.entry[0].messaging[i];\n    sender = event.sender.id;\n    if (event.message \u0026\u0026 event.message.text) {\n      text = event.message.text;\n      // Handle a text message from this sender\n    }\n  }\n  res.sendStatus(200);\n});\n```\n\nThis will receive a message (or array of - Facebook can batch incoming messages together if traffic on the system is high), then get the text of the message out of the incoming request.  You then need to add your own code to do something with that text (parse it for keywords for example) and return a response JSON object that Facebook will render back to the user.\n\nIn my bot, I'm handling more than just the basic text messages that the Facebook example code caters for: I'm looking for messages with a location attachment (user has sent their location from Messenger to the bot), and postback messages from previous button presses that the user made on calls to action that the user took when dealing with replies from the bot.  So my logic for processing incoming messages from Messenger looks like:\n\n```javascript\napp.post('/webhook/', function (req, res) {\n    var messagingEvents, \n        i = 0,\n        event,\n        sender,\n        text,\n        attachment;\n\n    if (req.body \u0026\u0026 req.body.entry) {\n        messagingEvents = req.body.entry[0].messaging;\n\n        for (; i \u003c messagingEvents.length; i++) {\n            event = messagingEvents[i];\n            sender = event.sender.id;\n            if (event.message \u0026\u0026 event.message.attachments \u0026\u0026 event.message.attachments.length \u003e 0) {\n                attachment = event.message.attachments[0];\n\n                if (attachment.type === 'location') {\n                    processLocation(sender, attachment.payload.coordinates);\n                }\n            } else if (event.postback \u0026\u0026 event.postback.payload) {\n                if (event.postback.payload.indexOf('departures') \u003e -1) {\n                    processMessage(sender, event.postback.payload);\n                }\n            } else {\n                if (event.message \u0026\u0026 event.message.text) {\n                    text = event.message.text;\n                    processMessage(sender, text);\n                }\n            }\n        }\n    }\n\n    res.sendStatus(200);\n});\n```\n\nNote we always send a 200 status back to Facebook as soon as possible - the response message will be sent as a separate `POST` asychronously.\n\nWe'll cover each of the incoming message types in detail, but we're looking for:\n\n* Text message: `event.message.text`\n* Postback action (call to action button pressed): `event.postback.payload`\n* Location sent: `event.message.attachments[0].type === 'location'`\n\nThen we deal with each using their own function.\n\n## Responding to Messages from Users\n\nNow we can read messages from users, we need to do something with them and send an appropriate response back.\n\nThe Messenger platform supports some basic response types, which are:\n\n* A text message (plain text)\n* A URL to an image (not used in this example)\n* An image file (not used in this example)\n* A call to action with postback action buttons or links to external websites\n* A structured message containing one or more \"bubbles\", each containing text, optional image and optional calls to action links.  \"bubbles\" scroll horizontally in the user's view in Messenger\n* A structured message containing a receipt for goods (not used in this example)\n\nThe message types are all quite basic, represented as JSON, and have little to no formatting options.  More information and example JSON schemas for each can be found in the [Send API Reference documentation](https://developers.facebook.com/docs/messenger-platform/send-api-reference).\n\nThe API is not designed for long replies, the currenct limitations are:\n\n* Title field: 45 characters\n* Subtitle field: 80 characters\n* Call to action button title: 20 characters\n* Maximum call to action items per bubble: 3\n* Maximium bubbles per message response: 10 (will scroll horizontally)\n\nText appears to have little to no formatting options (seemingly no way to do an unordered or ordered list.  `\\n` does cause a line break).\n\nIf a message has a text field in it that contains more than the allowed number of characters, Facebook will reject it.\n\n### Responding to Text Messages\n\nWhen we receive a text message (identified by a `POST` to `/webhook/` containing):\n\n```javascript\nevent.message.text\n```\n\neverything that the user typed is delivered as a single string in the above property.\n\nWe can then use any sort of string parsing to try and work out what the user is asking for, and send any type of response message (plain text, one or more bubbles of text / image / link / call to action buttons).\n\nIn this demo, we're using basic string searches to determine the user's query.  Ugly, but effective enough to pick out what we need:\n\n```javascript\nfunction processMessage(sender, reqText) {\n    var respText = 'Sorry I don\\'t understand. Try:\\n\\nstatus\\nelevators\\nstations\\ndepartures \u003ccode\u003e\\n\\nOr send your location for nearest station.',\n        keywordPos = -1,\n        stationCode;\n\n    reqText = reqText.trim().toLowerCase();\n\n    if (reqText.indexOf('help') \u003e -1) {\n        // Deal with sending user help message\n    } else if (reqText.indexOf('stations') \u003e -1) {\n        // Get a list of all station codes and send them to the user\n    } else if (reqText.indexOf('departures') \u003e -1) {\n        // Parse out a station code from:\n        // departures from \u003ccode\u003e\n        // departures for \u003ccode\u003e\n        // departures at \u003ccode\u003e\n        // departures \u003ccode\u003e\n\n        keywordPos = reqText.indexOf('departures at');\n        if (keywordPos \u003e -1 \u0026\u0026 reqText.length \u003e= keywordPos + 18) {\n            stationCode = reqText.substring(keywordPos + 14, keywordPos + 18);\n        } else {\n            keywordPos = reqText.indexOf('departures for');\n            if (keywordPos \u003e -1 \u0026\u0026 reqText.length \u003e= keywordPos + 19) {\n                stationCode = reqText.substring(keywordPos + 15, keywordPos + 19);\n            } else {\n                keywordPos = reqText.indexOf('departures from');\n                if (keywordPos \u003e -1 \u0026\u0026 reqText.length \u003e= keywordPos + 20) {\n                    stationCode = reqText.substring(keywordPos + 16, keywordPos + 20);\n                } else {\n                    keywordPos = reqText.indexOf('departures');\n                    if (reqText.length \u003e= keywordPos + 15) {\n                        stationCode = reqText.substring(keywordPos + 11, keywordPos + 15);\n                    } else {\n                        // Keyword found but no station code\n                        keywordPos = -1;\n                    }\n                }\n            }\n        }\n\n        if (keywordPos \u003e -1) {\n            stationCode = stationCode.trim();\n            // Go get the train departures for the requested\n            // station code and send to the user\n        } else {\n            // Send error message to user\n        }\n    } else if (reqText.indexOf('elevators') \u003e -1) {\n        // Get elevator status and send to the user\n    } else if (reqText.indexOf('status') \u003e -1) {\n        // Get system status and service announcements\n        // and send to the user\n    } else {\n        // Unknown command\n        console.log(respText);\n        sendTextMessage(sender, respText);\n    }\n}\n```\n\nFor more complex text processing, Facebook recommends trying [wit.ai](https://wit.ai/).\n\n#### Sending a Plain Text Response\n\nIn the case where we want to send a basic text reply (for example with the elevator status response that is just a short text message), we simply call a function `sendTextMessage` which expects the sender (from the original webhook call that came from the Facebook platform), and a string for the message to send back to that user:\n\n```javascript\nsendTextMessage(sender, 'Hello there!');\n```\n\nThe implementation of `sendTextMessage` looks like this:\n\n```javascript\nfunction sendTextMessage(sender, text) {\n    var messageData = {\n        text: text\n    };\n\n    httpRequest({\n        url: 'https://graph.facebook.com/v2.6/me/messages',\n        qs: { \n            access_token: FACEBOOK_PAGE_ACCESS_TOKEN \n        },\n        method: 'POST',\n        json: {\n            recipient: { \n                id: sender\n            },\n            message: messageData,\n        }\n    }, function(error, response, body) {\n        if (error) {\n            console.log('Error sending message: ', error);\n        } else if (response.body.error) {\n            console.log('Error: ', response.body.error);\n        }\n    });\n}\n```\n\nThis simple sends a HTTP `POST` to the Facebook Graph API (must be v2.6 or higher) to generate a message back to the user.  The `messageData` object simply wraps the plain text that we want to send.  We authenticate to Facebook using the Page Access Token that was obtained when setting up the bot.\n\nIf the message is over 320 characters, Facebook will reject it.\n\nIn other cases, we have more information than the 320 character limit of a text message allows for, and/or we want to format it to include multiple message \"bubbles\", images, header/sub-headers, links to websites or further calls to action.  For this we need to use Facebook's \"Generic Template\".\n\n#### Sending a Richer Response\n\nStrangely, Facebook uses the term \"Generic Template\" for its richer message template that can include between 1 and 10 \"bubbles\", each containing a title (45 characters), subtitle (80 characters), 3 buttons each having up to a 20 character label and linking to an external web page or a postback to the bot.\n\nAgain, the message bubble(s) are described by way of JavaScript objects and posted to the Facebook Graph API as JSON.\n\nAn example object that sends 3 bubbles each with a title and subtitle looks like this (we use this to return departure times from a given station):\n\n```javascript\nmessageData = {\n    attachment: {\n        type: 'template',\n        payload: {\n            template_type: 'generic',\n            elements: [\n                {\n                    title: '24th Street',\n                    'subtitle': '43 mins, 9 cars. 58 mins, 9 cars. 73 mins, 9 cars.'\n                },\n                {\n                    title: 'Daly City',\n                    'subtitle': '43 mins, 9 cars. 58 mins, 9 cars. 73 mins, 9 cars. 1 min, 9 cars. 4 mins, 9 cars.'\n                },\n                {\n                    title: 'Millbrae',\n                    'subtitle': '8 mins, 4 cars. 23 mins, 4 cars. 38 mins, 4 cars. 13 mins, 5 cars.'\n                }\n            ]\n        }\n    }\n};\n```\n\nEach object in the `elements` array becomes its own \"bubble\" when rendered in Messenger, and these scroll horizontally in the Messenger chat box.\n\nWe'll look at adding images and callback buttons when responding to location messages later.\n\nWhen we have our object ready to send, we call a function `sendGenericMessage` which expects the sender (from the original webhook call that came from the Facebook platform), and an object containing the template for the message bubble(s) to send back to that user:\n\nThe implementation of `sendGenericMessage` looks like this:\n\n```javascript\nfunction sendGenericMessage(sender, messageData) {\n    httpRequest({\n        url: 'https://graph.facebook.com/v2.6/me/messages',\n        qs: { \n            access_token: FACEBOOK_PAGE_ACCESS_TOKEN \n        },\n        method: 'POST',\n        json: {\n            recipient: {\n                id: sender\n            },\n            message: messageData,\n        }\n    }, function(error, response, body) {\n        if (error) {\n            console.log('Error sending message: ', error);\n        } else if (response.body.error) {\n            console.log('Error: ', response.body.error);\n        }\n    });\n}\n```\n\nWe do pretty much the same thing as in `sendTextMessage`, however `sendGenericMessage` expects a `messageData` object rather than a string as its second parameter.\n\n### Responding to Location Messages\n\nWhen we receive a location message (identified by):\n\n```javascript\nevent.message.attachments[0].type === 'location'\n```\n\nWe're interested in the user's lat/long co-ordinates, which can be obtained from the incoming message as:\n\n```javascript\nattachment.payload.coordinates.lat\nattachment.payload.coordinates.long\n```\n\nand then used in any other API calls or further processing to determine what sort of response to send back to the user.\n\nIn our case, the bot asks the BART API for the station closest to the user's location then responds with a \"Generic\" template message containing:\n\n* Name of the closest station\n* Distance in miles to closest station\n* Image containing an Open Street Map tile showing the nearest station's location\n* Call to Action button to open the BART website at the nearest station's page\n* Call to Action button to tell the bot that the user would like to see train departures from that tation\n* Call to Action button to open a Bing maps URL with driving directions to the station if it is more than 2 miles away, otherwise walking directions\n\nThe code for this looks like:\n\n```javascript\nfunction processLocation(sender, coords) {\n    httpRequest({\n        url: BART_API_BASE + '/station/' + coords.lat + '/' + coords.long,\n        method: 'GET'\n    }, function(error, response, body) {\n        var station,\n            messageData,\n            directionsUrl;\n\n        if (! error \u0026\u0026 response.statusCode === 200) {\n            station = JSON.parse(body);\n            directionsUrl = 'http://bing.com/maps/default.aspx?rtop=0~~\u0026rtp=pos.' + coords.lat + '_' + coords.long + '~pos.' + station.gtfs_latitude + '_' + station.gtfs_longitude + '\u0026mode=';\n\n            // Walkable if 2 miles or under\n            directionsUrl += (station.distance \u003c= 2 ? 'W' : 'D');\n\n            messageData = {\n                'attachment': {\n                    'type': 'template',\n                    'payload': {\n                        'template_type': 'generic',\n                        'elements': [{\n                            'title': 'Closest BART: ' + station.name,\n                            'subtitle': station.distance.toFixed(2) + ' miles',\n                            'image_url': 'http://staticmap.openstreetmap.de/staticmap.php?center=' + station.gtfs_latitude + ',' + station.gtfs_longitude + '\u0026zoom=18\u0026size=640x480\u0026maptype=osmarenderer\u0026markers=' + station.gtfs_latitude + ',' + station.gtfs_longitude,\n                            'buttons': [{\n                                'type': 'web_url',\n                                'url': 'http://www.bart.gov/stations/' + station.abbr.toLowerCase(),\n                                'title': 'Station Information'\n                            }, {\n                                'type': 'postback',\n                                'title': 'Departures',\n                                'payload': 'departures ' + station.abbr,\n                            }, {\n                                'type': 'web_url',\n                                'url': directionsUrl,\n                                'title': 'Directions'\n                            }]\n                        }]\n                    }\n                }\n            };\n\n            sendGenericMessage(sender, messageData);\n        } else {\n            console.log(error);\n            sendTextMessage(sender, 'Sorry I was unable to determine your closest BART station.');\n        }\n    });   \n}\n```\n\nThe message to send back to the user is built up in `messageData` and sent with the `sendGeneric` function.\n\nTo include a button that points to a URL we use:\n\n```javascript\n{\n    'type': 'web_url',\n    'url': 'http://google.com',\n    'title': 'Google'\n}\n```\n\nWhen clicked in Messenger, this button will open the URL in the browser, and the a callback to the bot is not made.\n\nTo include a button that posts back a further action to the bot we use:\n\n```javascript\n{\n    'type': 'postback',\n    'title': 'Departures',\n    'payload': 'json to send back to bot',\n}\n```\n\nWhen clicked in Messenger, this will cause a POST to be sent to the bot's `/webhook/` endpoint containing the payload JSON as `event.postback.payload`.  The bot can then use this value to determine what action to take next, and which further response to send.\n\nIf there's an error, we just return an error back to the user using the `sendTextMessage` function.\n\n### Responding to Postback Messages\n\nWhen we receive a postback message (identified by):\n\n```javascript\nevent.postback.payload\n```\n\nexisting, and containing any JSON that was included in the `payload` of the button that was pressed in Messenger.  We then use that to determine what action to take.\n\nIn our example, the only postback payloads look like `departures powl` which is something that the user can also enter themselves in a text message, so in our webhook POST route we just use the same handler as we would for a text message, and pass it the payload text:\n\n```javascript\nif (event.postback.payload.indexOf('departures') \u003e -1) {\n    processMessage(sender, event.postback.payload);\n}\n```\n\n## Additional Facebook Setup\n\nThere's some extra Facebook setup that we can do to improve the user experience a little.\n\n### Set up Welcome Message\n\nThis is optional, but nice to have.  A welcome message is displayed automatically at the start of each new conversation.  To set this up, we will need to send Facebook a post request containing the JSON representation of either a plain text (as used below) or structured message with call to action buttons.  We will also need the access token for our Facebook page.\n\n```\ncurl -H \"Content-Type: application/json\" -X POST -d '{\"setting_type\":\"call_to_actions\",\"thread_state\":\"new_thread\",\"call_to_actions\":[{\"message\":{\"text\":\"Hi, I am BART bot - I can help with your Bay Area travel needs!\"}}]}' https://graph.facebook.com/v2.6/heybartbot/thread_settings?access_token=\u003cFACEBOOK_PAGE_ACCESS_TOKEN\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonprickett%2Fbartfbchatbot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimonprickett%2Fbartfbchatbot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonprickett%2Fbartfbchatbot/lists"}