Going live In Part 1 of this tutorial we set up our page on Facebook and hooked it to a Sinatra server running on our machine through ngrok . We introduced the facebook-messenger gem from Hyperoslo and we hooked our bot to Google Geocoding API . In Part 2 we added new features to our bot and saw the complexity of our code increase, so we had to refactor. We added two different sets of menus to make interaction with our bot easier. T we’ll teach our bot to use . We will also admit some design mistakes we made previously, refactor yet again, and finally set our bot free from the tethers of your computer by deploying it to . You will be also guided through , so your own bot can one day go live. his time our current location Heroku Facebook verification process If you did not follow steps from last two parts, you can pick up the code to begin with . here Here’s what we are building today: Our bot by the end of this tutorial Keeping secrets Remember how we had to type in our like or by hand each time we opened a new terminal window to launch our bot on localhost? Well, some readers pointed out there is a gem for that and it will work with any Ruby project (doesn’t have to be Rails, as is the case with the popular gem). Meet . First, install it on your system: environment variables ACCESS_TOKEN VERIFY_TOKEN figaro dotenv gem install dotenv Then require it on top of and files both app.rb bot.rb require 'dotenv/load' Create a file named in your project folder and put exactly the same environment variables declaration that you used to set in your terminal. ( ) .env Reminder: both tokens can be found in your Facebook developer console, see Part 1 for details VERIFY_TOKEN=here_comes_YOUR_verify_tokenACCESS_TOKEN=here_comes_YOUR_access_token Now add another file to your project folder, called . Put inside (yes, just the name of .env file) so that your tokens are not pushed to Github for the whole world to see. I assume you track your project with Git, that would be essential for our deployment to Heroku. .gitignore .env Great, now you every time your launch your local server with a command your environment variables will be set automatically, no need to worry about closing and reopening your terminal window. rackup Where am I? You have to admit, the of our bot was not very useful: you had to manually type in an address in order to lookup GPS coordinates. Nice to learn random things like the latitude of an Eiffel Tower, but not really helpful when you need to answer the question, old as humanity itself, . last iteration “Where am I?” The question everyone asks oneself sometimes: “Where the hell am I?” Your phone has GPS, right? Well, Facebook lets us use it. Here’s there own example of how to enable location sharing. Messenger API curl -X POST -H "Content-Type: application/json" -d '{"recipient":{"id":"USER_ID"},"message":{"text":"Please share your location:","quick_replies":[{"content_type":"location",}]}}' "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN" But we are not accessing Facebook JSON endpoints directly, all this work is done behind the scenes by the gem that we use. However, we are able to reason deductively and what do we see by looking at this raw JSON? We see that the part enabling user to send his/her location is in the set of quick replies. That means, we need to send any message to the user first and attach a location query to it. facebook-messenger “content_type”:”location” You will also need to enable a webhook under tab in your Facebook developer console. location Webhooks We already have a wrapper over the JSON request in a form of helper that we introduced in a . Here’s how it looked like: say previous part As you can see, this method takes an optional argument that is an array of replies. We used it construct our “menu” of quick replies to show basic commands to our user. Meaning, we can pass (that’s right, Hash inside an Array) to this method and voilà, we’ll send exactly the same JSON we saw above to Facebook API. That’s code reuse! quick_replies [{ content_type: ‘location’ }] Also, remember that we can examine any messages our bot sends us with . Try adding this line to the top of your method inside , right before line: message.inspect wait_for_any_input bot.rb sender_id = message.sender[‘id’] puts "Received '#{message.inspect}' from #{message.sender}" Now, if you open Messenger on your mobile phone, connect to your bot and send your location instead of text, then by examining your logs you can see how the location message looks like to our Santa’s little helper. You should see something like this: rackup facebook-messenger Received '#<Facebook::Messenger::Incoming::Message:0x007fad3890be00 @messaging={"sender"=>{"id"=>"1295486190524658"}, "recipient"=>{"id"=>"1614495692193246"}, "timestamp"=>1484597226465, "message"=>{"mid"=>"mid.1484597226465:b335687852", "seq"=>59617, "attachments"=>[{"title"=>"Andy's Location", "url"=>"https://www.facebook.com/l.php?u=https%3A%2F%2Fwww.bing.com%2Fmaps%2Fdefault.aspx%3Fv%3D2%26pc%3DFACEBK%26mid%3D8100%26where1%3D55.772721087%252C%2B37.683347132677%26FORM%3DFBKPL1%26mkt%3Den-US&h=ATNEWzpLzUVPzSBK4oIF9rpOfG1IDaYzKtbNpDNcLAG9729MvTMvXETHmOTMdhnrQEw5Af2Pg8Ct2uTwXnQoTRI4DbmXWMH-p1ajjeAXhcvMLmMjvsyhCFgMDMM9xkHrFPVTTeg&s=1&enc=AZOftxHdpeOTr32oSlm8KEimjwBgrzLEPiUtBmE5a0pwZZ2BTsgizwnV6p3b8D3HvtE7dmT-hQuRh7Vn8x-vWNPe", "type"=>"location", "payload"=>{"coordinates"=>{"lat"=>55.772721087, "long"=>37.683347132677}}}]}}>' from {"id"=>"1295486190524658"} If we dig in deep enough, we can get coordinates, we don’t even have to make a call to Geocoding anymore! By trial and error we discover that key containing a hash with latitude and longitude values can be accessed from our program like so: Google “coordinates” Ruby message.attachments.first['payload']['coordinates'] Great! Let’s write some new methods. Add this to : bot.rb As you can see, we use helper method to add attach ‘Send Location’ label to your message. In the interface it will look like so: say We will also have to make sure that whatever reply we get in is indeed a location and not a plain old text in order to process it further. lookup_location(sender_id) Add this helper method to : bot.rb Now let’s get down to business and put our main logic in another method: Nice, now our bot is able to (in theory, we haven’t yet hooked this methods up to any bot conversation logic) extract your current GPS coordinates from the location you’ve sent. Let’s take it one step further. What if we could also guess full address based on location? Sure, we can! also lets us do something called . From the sound of it — exactly what we’re looking for! In order to use it we need to send a GET request to a different URL, so let’s create another constant on top of our , next to where we declared . I’ll call it for consistency, but I admit it’s not the best naming. Google Geocoding API reverse geocoding bot.rb API_URL REVERSE_API_URL REVERSE_API_URL = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' Note: Remember we’re only using Geocoding API without an API key for development and testing. For production you should get it as instructed . Now, let’s update our method to reverse-geocode our coordinates. Note we are reusing and methods that we created earlier. handle_user_location(message) get_parsed_response extract_full_address Great, we have all we need for the feature, now let’s add it to the interface. The simplest place to add it is a persistent menu, defined in your thread settings on top of . Update it: bot.rb Now add a postback to payload that will call our new functionality. Update your block. LOCATION Bot.on :postback Also, add this constant somewhere on top of your file to avoid retyping everywhere we need it: [{ content_type: ‘location’ }] TYPE_LOCATION = [{ content_type: 'location' }] Now whenever a user picks ‘Location lookup’ from the persistent menu, we ask for a location and invite him/her to send it to us. We were also interacting with the user through the set of quick replies. Update it to include a reference to location command: We handle quick replies like any other user command from inside the method. Let’s account for location (and change previous naming while doing so): wait_for_command Notice we start adding to all method calls. That’s because we want to add ‘Send Location’ label to all of our bot’s functions. After all, for a postal address lookup user may want to type partial address in, or just send his/hers current location. Same goes for manual GPS lookup. Now we have to handle location inside and methods. We will have to revisit what we’ve already done. TYPE_LOCATION say show_coordinates show_full_addres Keeping it simple An important principle in code (and in life) OK. It’s time to admit sometimes you can your code. As some of my more experienced readers pointed out to me, introduction of with argument inside the method in was atrocious, because even if it helps us DRY-out 8 or so repeating lines, it makes our code . Also, if at some point we want our features to develop more separate functionality, we want to be able to modify them independent of each other. Let’s work on our mistakes: get rid of method and rewrite two features. over-refactor yield handle_api_request Part 2 very difficult to reason about handle_api_request Overthinking is a form of stupidity. Take-away: Here, we keep all our conditional logic inside our block, we also add a separate method for each lookup in order to follow the and reduce nesting. Bot.on :message SRP That was a lot of code! Whenever you feel lost, go to to see the latest good version of the file. this tutorial’s Github bot.rb Going international You must have noticed the method that we haven’t yet introduced. As of , our bot would just take whatever a user texted and append it to API URL, assuming all is good. But, if you try using international characters in your address (“Красная площадь“ or 東京都), your bot will throw this: encode_ascii Part 2 ERROR URI::InvalidURIError: URI must be ascii only "https://maps.googleapis.com/maps/api/geocode/json?address=\u{424}\u{440}. \u{42d}\u{43d}\u{433}\u{435}\u{43b}\u{44c}\u{441}\u{430}, \u{414}. 23" This error comes from Ruby standard library module that is used by (the gem we use to make HTTP requests). We need a way to convert a string that comes from a user into the URL-friendly ASCII format. uri HTTParty For that, we will use the gem. It has a method to perform on an URL, but you can also do it on any string. Add line to your and in . Then add method, it’s a one-liner. addressable percent-encoding gem ‘addressable’ Gemfile require ‘addressable/uri’ bot.rb ensode_ascii(s) Now a string will turn into . Google API is smart enough to decode it back once you make a request. “Красная площадь, д.1” “%D0%9A%D1%80%D0%B0%D1%81%D0%BD%D0%B0%D1%8F%20%D0%BF%D0%BB%D0%BE%D1%89%D0%B0%D0%B4%D1%8C,%20%D0%B4.1” The alternative is to use gem that will try to any string so will turn into , which is close enough, but will turn into , which has nothing to do with Tokyo (for which that three Japanese characters stood in the first place). Actually, for the transliterated version of Google API will return some address in Florida. That’s probably not what you want. unidecoder transliterate “Красная площадь” “Krasnaia ploshchad’” “東京都” “Dong Jing Du“ “東京都” Robot abuse You always have to assume that your , either because of sheer curiosity, or just by chance, because you did not account for some behaviour. Currently, if you ask user for an address and he/she sends you an image or a video, instead of text, as a reply — your bot is broken. To account for that, you’ll need to introduce a tiny new method: users will do everything they can to break your bot Then perform this check in every action, like so: Why humans have to be so cruel? Spring cleaning Okay, now our bot is well over 200 lines long. That makes our code difficult to maintain. It’s time to put some of common functionality into separate files. We can use classes or modules to break our program into pieces. We will use a simple class wrapper with a class method to invoke part of code ( ). The idea is to do something like this: that’s debatable, and I’d like to hear more about the actual best practice in comments And then call at the top of your . I leave the details to you, in I created two files: and to collect all actions. Greetings.enable bot.rb my project greetings.rb persistent_menu.rb Facebook::Messenger::Thread.set Commencing countdown, engines on Our bot goes to Heroku cloud Now after we tested all we could on localhost ( ) we are ready to send our bot into the world. We will use for deployment. Go to Heroku’s and . yes, I know bot testing is kind of hell Heroku website sign up for free Then, install if you haven’t done it before by following . Also, make sure your project folder is tracked with Git. If not, run: Heroku CLI these instructions git initgit add .git commit -m "First commit" Then authenticate your machine with and create your heroku app by running this command: heroku login heroku create YOUR_BOT_NAME It will create a web address in the form of and add heroku as a remote Git repository. Check if all went well by running: YOUR_BOT_NAME.herokuapp.com git remote -v You should see that two new references (fetch and push) to your . heroku remote Now, and : line both in and . gem does not work with Heroku ( ). Then in your pick your newly created app, go to its and scroll to . Reveal them and type in your and like so: this is important comment out require ‘dotenv/load’ bot.rb app.rb dotenv there’s a fork that does, you can look it up, but I found it easier to set variables directly in the dashboard Heroku dashboard Settings Config variables ACCESS_TOKEN VERIFY_TOKEN Now, the moment of truth. If you followed all the steps then the contents of file should be enough to successfully deploy. Run: config.ru git push heroku master …and behold the magic happening as Heroku creates a functional web app from your code. All you have to do now is to go to Facebook developer’s console and change the address for a webhook from your ngrok URL to your new Heroku app, something like: https://your-bot-name.herokuapp.com/webhook If the webhook verification goes well — ! you have successfully deployed your bot Hooray! For now you can test your bot yourself or add other Facebook users as testers under tab in your Facebook developer console. If you are truly ready to make your bot available to the whole world, you need to go through the . Meaning, actual live people at Facebook HQ will look at your bot (though, briefly) and check if it doesn’t do anything nasty. Go to tab and make your bot “live”. Then go to tab and under add to submission. Follow the provided checklist. Roles verification process App Review Messenger App Review for Messenger pages_messaging You just need to review your pages_messaging in our case You will need a privacy policy for your app that can be easily generated on numerous websites. I found to be most straightforward. Also, you need to supply an icon for your app and it must be exactly 1024x1024 in resolution. this one Now cross fingers and wait. In my case, Facebook reviewers cleared my bot in (I did it on Friday morning, Eastern time). Sometimes reviewers may ask you to record a of your bot, which is done easily with QuickTime. less than 15 minutes screencast Ahoy, Captain! Of course, our Coordinator bot does not provide the best user experience and some people may find it’s interface confusing. The conversation flow is rather rigid and if you make a mistake, you have to start over. However, I feel that some features, like a postal address lookup or finding the name of the street you’re on just by a single message can prove useful (at least, I find myself using them from time to time). You can play with the latest version of this tutorial’s bot by following . this link This project started with a contrived example to play around with the gem as a personal exercise in Ruby. Later I realised it may help and started working on this tutorial. facebook-messenger beginners like me My job was, more than anything, to stimulate your imagination and inspire you to build your own things. So I was happy to learn that one of my readers used this very tutorial to create a bot that . helps navigate Paris subway bot, inspired by this very tutorial Captain Metro If you live in Paris, you can test Captain Metro here . : About me I’m a beginner programmer and ex senior international TV correspondent. I am looking for my first proper job as a developer . Currently, I am having hard time getting employed in Russia, where engineers are extremely good, and it seems no one wants to hire beginners . If you want a motivated coder with well-developed human language skills for your team, I’ll be glad to join, even remotely. You can also invite me for an internship. I recently graduated top of the class from an excellent Le Wagon coding bootcamp in Paris and co-authored HALFWAY.ninja as a graduate project there. It’s an airfare comparator for couples. Go check it out! You can find me on Github , Twitter and Instagram . I speak English, Russian, French, Ruby, JavaScript, Python and Swift and I am currently based in Moscow, Russian Federation. Feel free to write me directly with any offers. P.S. I also write poetry (Russian knowledge required).