Keeping up with the conversation A less obscure cultural reference In the of this tutorial we set up our page on and hooked it to a server running on our machine through . Then we introduced the gem by the guys at . We also taught our bot to use in order to lookup GPS coordinates for any given address. This is what our bot will look like after we meddle with it some more: Part 1 Facebook Sinatra ngrok facebook-messenger Hyperoslo Google Geocoding API Chatting with the bot on mobile Talk to me OK, let’s get first things about Messenger bots straight. : They adhere to Victorian etiquette for little kids “Do not speak unless spoken to” This is a good rule, because it helps to avoid unsolicited messages aka spam. But how do we provide solicited messages? In the of our bot we just assumed that the user will give us something we can send directly to Google Geocoding API, wait for response and display the result. That assumes user’s prior knowledge about our bot’s intent, which is a very bad way to go about UX. Also, we did not provide any fallbacks in case user types anything that doesn’t have GPS coordinates (surprisingly, ‘Hello’ will yield a result, as Google will just assume we’re looking for ) — in that case, our bot will completely lose its marbles. We need some kind of . first minimalistic version Hella, Iceland conversational loop by Reza Farazmand is licensed under a Poorly Drawn Lines CC BY-NC 3.0 First thing I learned while trying to implement it with gem: call can be made only , otherwise we face all sorts of unexpected behaviour ( ). ! Fine, let’s define separate methods to handle different ways our conversation can go, starting with the most obvious one. Inside delete your Bot.on method that takes a block and replace it with this: facebook-messenger Bot.on :message { |…| block } once per scope correct me in replies if I’m wrong So we need to create more scopes bot.rb Now we need to add a method referenced in our . Put this into your : process_coordinates wait_for_user_input bot.rb Now at the bottom of call our waiting method. It will launch the conversation loop: bot.rb wait_for_user_input From now on we will only engage user if he mentions “coordinates” (“coord” as a shortcut) or “gps” in any context. Then we will ask user to provide a destination for look-up, then we will call Google and display the result. If no result is found, we will notify user and wait for another command. At this stage, our looks like . Hit in your tab with running and restart the server to test it in Messenger. code.rb this Ctrl-C rackup OK, it seems to work. Let’s add features! If we dig inside a we get back from Google, we’ll see that there’s a key called that will return a full postal address for any destination Google can find. Let’s add this as a feature to our bot. JSON formatted_address First, add another clause to our method: when wait_for_user_input Note that that we thought of user misspelling “address”: if he/she types “I need a full adress” (a type of mistake I’d make) our will still match and the function will be called. Put it inside too: regexp show_full_address bot.rb Finally, add this helper method: Great. This is your . Restart the server and test. code at this point Time to refactor! Our bot surely works and can handle two different commands, but its code could certainly use some refactoring: and methods are 80% identical, that means . process_coordinates show_full_address we can do better Refactoring can sure get messy We know that methods can . It seems we may benefit from this language feature to make our code -er. Let’s define another method that will abstract out most of API handling functionality: Ruby accept blocks DRY Then we can change and like so: both process_coordinates show_full_address Great. Now our methods only do stuff that is actually specific to their purpose, the rest is handled by our generic method that handles API-related logic. I know, the concept of Ruby with arguments can be tricky to wrap your head around, especially if you are a beginner coder like me, but I like to think of it as , and you can pass variables back and forth. yield time-space warp that connects two methods This is how I imagine Ruby that takes an argument inside a method (Almost) yield While we are at it, let’s also create an constant, so we can have all our bot’s phrases in one place. Add it on top of your : IDIOMS bot.rb IDIOMS = {not_found: 'There were no resutls. Ask me again, please',ask_location: 'Enter destination'} Now replace your hard coded replies with a reference to like so: IDIOMS message.reply(text: IDIOMS[:ask_location]) This is your . Restart your server and . code at this stage test test test Not just talk Great, out bot still works and we used some clever Ruby, but it is . How the user is supposed to know what to do? Can we guide him? The promise of conversational AI did not really fulfil itself yet, and for now we are stuck with . is . Facebook Messenger has currently three (that I know of) ways to show “menus” to the user: still not the best user experience mixed interfaces Everyone doing it A persistent menu that can always be called from the icon next to text entry field. A “Button” template that let’s you tie action buttons to a message bubble. A set of “quick replies”. Three Facebook-approved ways of presenting button menus to user We will pick as I find them more user-friendly. They are also treated as regular messages by the API, while other buttons implement call to your webhook, which is a bit different concept, as we will see later. quick replies “postback” First, create constant to keep your quick replies in one place: “Payload” sounds confusing, but this is really just another identifier you can assign to user interaction. Think of it as a constant, so we use for our payloads. CAPITAL_LETTERS Okay, now we are going to write a little method that will help us a lot in the future to adopt more approach in the way we code bot’s messages. Remember I told you bots only speak when they are spoken to? Well, that’s true up to a point. A user must that will . Like, sending first message or using a link to your Facebook’s page address (where your bot lives and handles its Messenger interaction). declarative do something expose his ID So, in addition to that we saw earlier, gem creators also gave us method that lets us send messages to user out of the blue once we have his Facebook ID. We are going to wrap it into our own helper: Bot.on :message { |…| block } facebook-messenger Bot.deliver say It will take a user’s id, a text for the message we want to send and optional argument for an array of quick replies. If an argument is supplied, message will be sent with quick replies attached to it, if not — it will be just a text. From this in we know that most users strike a conversation with a bot with some form of ‘ ’ or ‘ ’. We want our bot to present what’s on the menu ASAP, without any unnecessary meet & greet, so we will cheat a little: we’ll introduce the method that will present our array of quick replies to whatever user sends us, be it ‘ ’, ‘ ’ or ‘ ’. Add it to : great article Chatbot’s Life hi hello wait_for_any_input hi bonjour привет bot.rb is how we get user’s ID after he opened the conversation. message.sender[‘id’] Also, let’s change the name of our old method to to avoid any ambiguity. Make sure to change the definition and all possible calls. wait_for_user_input wait_for_command Also, make sure that at the very end of our you now call : bot.rb wait_for_any_input # launch the loopwait_for_any_input Let’s update our : IDIOMS IDIOMS = {not_found: 'There were no resutls. Ask me again, please',ask_location: 'Enter destination',unknown_command: 'Sorry, I did not recognize your command',menu_greeting: 'What do you want to look up?'} Then let’s define our method: show_replies_menu It uses our helper to send user the menu. Then it calls our to handle whatever user does next. Note that we picked the wording of our quick replies to match all possible regexp in . At the same time, user is not obliged to pick any of quick replies. He can shout at us something like “ ” and his command will still be handled, as our regexp is case-insensitive. Now let’s add an clause to our switch statement to handle the case where none of the commands known to us have been matched. Here is our new and improved : say wait_for_command wait_for_command GIVE ME COORDINATES YOU STUPID BOT! else wait_for_command Up until now, when the API request failed, user had to type something like “ ” or “ ” yet again to restart the whole process. That is not very cool, so let’s update our to retry the last command if API did not return any results. For that, we will use some ( ). Look: coordinates full address handle_api_request metaprogramming one of my favourite things about Ruby! By the thread By now your bot should be testable. At last, we will adjust some , so our user is properly greeted and presented with “Get Started” button that allows to strike the conversation. We will also add a persistent menu that will give user constant access two both features, even if he’s in the middle of something else. Here’s how it will look like: thread settings Making our bot nicer This will require us to use ’s functionality. First, go to your Facebook developer console, under click and enable . facebook-messenger Bot.on :postback { |..| block } “Webhooks” “Edit events“ messaging_postbacks Add this to your : bot.rb Now add some logic to handle postback events: And that’s all for Part 2! We have a fully functional (though may be not particularly sexy in order to attract VC millions) bot that we wrote in under 200 lines of pure Ruby code. part of this tutorial to see how we add some final bells and whistles and deploy to Heroku, so our can finally go live. Follow the third and the last Smooth Coordinator All the code for our bot at this stage of its development can be found on my Github . You can find the code for a complete project (still WIP on the time of this writing) in the same repo, but in the main branch. Feel free to fork it, clone it and play with it. GO TO PART 3 : About me I’m a beginner web developer, ex senior international TV correspondent, and young father who is looking for a first proper programmer job. Aside from obsessing about coding style and trying to pump my head full of technical literature, 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 also own this domain . I am currently available for hire, be it an inspiring internship, freelance or even full-time work. 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.