Chatbots Used in Singapore

Chatbot Development Tutorial: How to build a fully functional weather bot on Facebook Messenger with API.AI

Posted on

Chatbot Development Tutorial: How to build a fully functional weather bot on Facebook Messenger with API.AI

 

Overview

This is an example on how you can build a Weather Chatbot on Facebook platform, using Ruby on Rails and API.AI

The technology stack we used is

  • Server backend: Ruby on Rails
  • Natural Language Processing platfrom: API.AI
  • Deployment on Facebook Messenger
  • The Singapore National Environment Agency provides a nice API (for free) that gives current weather as well as forecasts

Sample feature:

(1) Able to return the “2 Hour Nowcast” when user asks for the current weather

User: “What’s the weather in Singapore?” Bot: “The weather in Singapore is {weather}”

User: “How’s the weather in Singapore?” Bot: “The weather in Singapore is {weather}”

User: “Is it raining in Singapore” Bot: “The weather in Singapore is {weather}”

You can try out the weather bot here: WBot By Robusttechhouse

Screen Shot 2017-02-07 at 12.03.28 PM

 

Setting Up API.AI

Go to https://console.api.ai and register an account for you. Please note Api.ai cannot support collaboration yet so you may have to create a generic account to share among your team. Read https://docs.api.ai/docs/get-started and follow the steps there. Then, go to the settings in your wit app and get the token id.

 

Integrate Rails app with API.AI

Fortunately we have a nice Ruby SDK to the https://api.ai natural language processing service. You can find out more about this gem at here.

gem 'api-ai-ruby'

Basic Usage

Just pass correct credentials to ApiAiRuby::Client constructor

client = ApiAiRuby::Client.new(
    :client_access_token => 'YOUR_CLIENT_ACCESS_TOKEN'
)

After that you can send text requests to the https://api.ai with command

response = client.text_request 'hello!'

Or try to invocate intent via defined ‘event‘:

response_zero = client.event_request 'MY_CUSTOM_EVENT_NAME';
response_one = client.event_request 'MY_EVENT_WITH_DATA_TO_STORE', {:param1 => 'value'}
response_two = client.event_request 'MY_EVENT_WITH_DATA_TO_STORE', {:some_param => 'some_value'}, :resetContexts =>

Our Chat Service Class

For cleaner coding, I have wraped all the integration between our Rails App and API.AI within a single class. Create a new chat_service.rb file in /services. What we need to do is create a ChatService class and in it’s initializer, where we will set up a API.AI client

class ChatService
  attr_reader :response_message

  def initialize(uid)
    @client = ApiAiRuby::Client.new(
      client_access_token: ENV['API_AI_CLIENT_ACCESS_TOKEN'],
      api_session_id: uid
    )
  end

  def execute(message)
    // Some logic here to get @response_message
    @response_message
  end
end

By doing this, when new message coming from an user, we can simply give user ID and the message to our ChatService class through the execute method which in turn will result in the @response_message

Note that you shouldn’t actually have your API.AI access client access token or any other token like it just lying around in code waiting to be put in version control. You should use the secrets.yml or application.yml (using Figaro) for development and environment variables in production.

Here is how we implement the execute method, please note there are lots of thing we can implement here to customize and improve the chat experience:

def execute(message)
  api_ai_response = @client.text_request(message)
  api_ai_response_message = api_ai_response[:result][:fulfillment][:speech]
  
  if action_incomplete
    @response_message = api_ai_response_message
    return
  end

  case action
  when 'ask.current.weather'
    @response_message = search_forecast(api_ai_response[:result][:parameters][:location])
  when
    ... # More implementation here
  end
end

def search_forecast(location)
  WeatherService.search_forecast(location)
end

Note: WeatherService is a class within my services directory. This class acts as an adapter, connecting to 3rd party weather provider to fetch weather information given a specific location.

 

Integrate Rails app with Facebook Messenger

Set up Facebook App

Head on over to the developer console and press “Add a New App”. After creating one, you can skip the quick start. You’ll end up here.

1*0DZc9c_t2Sr2DuUpIYvO5Q

From here, you’re going to want to press “+Add Product” and add Messenger. After we configure a webhook, Facebook wants us to validate the URL to our application.

 

Set up Rails App

We’ll be using the facebook-messenger gem. It’s arguably the best Ruby client for Facebook Messenger.

Add below initialization code so that our Rails App can loaded the gem

# frozen_string_literal: true
# config/initializers/facebook_messenger.rb

unless Rails.env.production?
  bot_files = Dir[Rails.root.join('app', 'bot', '**', '*.rb')]
  bots_reloader = ActiveSupport::FileUpdateChecker.new(bot_files) do
    bot_files.each { |file| require_dependency file }
  end

  ActionDispatch::Callbacks.to_prepare do
    bots_reloader.execute_if_updated
  end

  bot_files.each { |file| require_dependency file }
end

 

Add initial code for our bot

# frozen_string_literal: true
# app/bot/listen.rb

require 'facebook/messenger'

include Facebook::Messenger

Facebook::Messenger::Subscriptions.subscribe(access_token: ENV['ACCESS_TOKEN'])

Bot.on :message do |message|
  request_message = message.text
  uid = message.sender['id']
  User.find_or_create_by(uid: uid)  

  unless request_message.nil?
    chat_service = ChatService.new(uid)
    chat_service.execute(request_message)
    FacebookMessengerService.deliver(uid, chat_service.response_message)
  end
end

 

I also have a Facebook Messager Service class, which main function is to handle the replying to the sender, wrapped within the deliver method:

# frozen_string_literal: true
# app/services/facebook_messenger_service.rb
class FacebookMessengerService
  class << self
    def deliver(uid, message)
      puts "[debuz] sending '#{message}' to user '#{uid}'"

      message_content = {
        text: message,
        quick_replies: quick_replies ? quick_replies : nil
      }

      begin
        Bot.deliver(
          {
            recipient: {
              id: uid
            },
            message: message_content
          },
          access_token: ENV['ACCESS_TOKEN']
        )
      rescue => e
        puts '[debuz] ' + e.message
      end
    end
  end
end

 

Add to config/application.rb so rails knows about our bot files

# Auto-load /bot and its subdirectories

config.paths.add File.join("app", "bot"), glob: File.join("**","*.rb")
config.autoload_paths += Dir[Rails.root.join("app", "bot", "*")]

 

One last thing is to update routes for /bot

# config/routes.rb

Rails.application.routes.draw do
  mount Facebook::Messenger::Server, at: "bot"
end

 

Also, do not forget to set the env variables for the following

ACCESS_TOKEN=
VERIFY_TOKEN=
APP_SECRET=

 

Wrap up

Now that we have a functional bot, we can play around with the UI elements that Facebook provides. You can check them out here.

With this post, I hope you can build a API.AI chatbot for your own. This is an interesting space as it’s fairly new, good luck!

You can find the example Rails app here: https://github.com/duc4nh/wbot-api-ai.git

 

References

Special thanks to:

 

Brought to you by SingaporeChabots.sg. Want to build a chatbot? Drop us a note !

Leave a Reply

Your email address will not be published. Required fields are marked *