How do I write a simple bot in Ruby?

How do I write a simple bot in Ruby?

What is a bot?

Internet bots, also known as web robots, WWW robots or simply bots, are software applications that run automated tasks over the Internet. Typically, bots perform tasks that are both simple and structurally repetitive, at a much higher rate than would be possible for a human alone.1

Why do I need a bot?

I travel often to various cities and would like to know the current temperature and alerts if any for the city I am in. My bot should send me an email every day with this information.

What do I need to do?

To get the weather information, I am going to make use of the Forecast API. Let us register for a new account. On registering, you get an API Key. We will use it to access the necessary information.

Let’s say that I am planning to go to “1600 Amphitheatre Parkway, Mountain View, CA” tomorrow for a few days and I would like my bot to give me the weather forecast for this area everyday. My bot needs to know the latitude and longitude of this area. There are many tools to get this information; I use my Ruby gem to get the same, as latitude 37.423021 and longitude -122.083739.

Please read the Forecast API thoroughly. The forecast URL that our bot needs, would look like:

https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE

APIKEY is your Dark Sky API key. LATITUDE and LONGITUDE is the geographic coordinates of a location in decimal degrees. The response is a JSON-formatted object with many properties. Our bot shall use the following properties:

  • currently: A data point containing the current weather conditions at the requested location. A data point object represents the various weather phenomena occurring at a specific instant of time, and has many varied properties like:
    • summary which is a human-readable text summary of this data point.
    • temperature which is a numerical value (float) representing the temperature at the given time in degrees Fahrenheit.
  • alerts an array of alert objects, which, if present, contains any severe weather alerts, issued by a governmental weather authority, pertinent to the requested location. Such objects contain many properties like:
    • title a short text summary of the alert.

Note: Just about every object property in the response (see above) is optional. In fact, a request with no data to return will return a nearly empty object, rather than a failure. Robust code will check the presence of required parameters before using them, and will fail gracefully if they are not present (or return an error if a minimum functional set of data is not available).

A note on Module

Read this if you are new to Ruby; others can skip this part.

Modules provide a structure to collect Ruby classes, methods, and constants into a single, separately named and defined unit. This is useful so you can avoid clashes with existing classes, methods, and constants.

Modules for Namespaces

One common feature used in Ruby is the ability to include code situated in other files into the current program. When including other files, you can quickly run into conflicts. You cannot guarantee that no file that’s included (or one that’s included in a long chain of includes) will clash with code you’ve already written or processed. Class names can clash similarly, and you could end up with two classes mixed into one by accident. Modules help to solve these conflicts by providing namespaces that can contain any number of classes, methods, and constants, and allow you to address them directly.

Folder structure for our bot

We will create one top-level module called Forecast, and then place our class Extract inside this module. The full name of our class will be Forecast::Extract

Because class Extract is inside the module Forecast, it makes sense to put the corresponding file, extract.rb, inside a directory named forecast/ in the lib/ directory . This helps people who read your code in the future; when they see a name like A::B::C, they know to look for c.rb in the b/ directory in the a/ directory of your library. Here’s the directory structure:

forecast/
  lib/
    forecast/
      extract.rb
  bin/
    weather_bot.rb

extract.rb will contain our Forecast module. weather_bot.rb will contain the executable code of our bot.

Using Git and Heroku

Read this if you are new to Ruby; others can skip this part.

We shall make use of Git and Heroku to host our bot on the cloud.

Heroku is a cloud application platform. You can write apps in the programming language of your choice. You manage your app using the Heroku command-line tool and you deploy code using the Git revision control system, all running on the Heroku infrastructure.2

Sign up at Heroku

Please ensure that you are connected to the internet and then create an account on Heroku (obviously do this only once) if you don’t have one – https://id.heroku.com/signup/devcenter.

Install the Heroku Toolbelt

Install the Heroku Toolbelt for your development operating system.

The toolbelt contains the Heroku client, a command-line tool for creating and managing Heroku apps; Foreman, an easy option for running your apps locally; and Git, the revision control system needed for pushing applications to Heroku.

The heroku command-line tool is an interface to the Heroku Web API and includes support for things like creating/renaming apps, running one-off dynos, taking backups, and configuring add-ons.

Install bundler

Open a Git Bash shell for the folder c:\forecast. Type:

$ gem install bundler

Login

After installing the Toolbelt, you’ll have access to the heroku command from your command shell. Authenticate using the email address and password you used when creating your Heroku account:

$ heroku login
Enter your Heroku credentials.
Email: satish@rubylearning.org
Password: 
Could not find an existing public key.
Would you like to generate one? [Yn] 
Generating new SSH public key.
Uploading ssh public key /Users/adam/.ssh/id_rsa.pub

Press enter at the prompt to upload your existing ssh key or create a new one, used for pushing code later on.

Note: If you have previously uploaded a key to Heroku, we assume you will keep using it and do not prompt you about creating a new one during login. If you would prefer to create and upload a new key after login, simply run heroku keys:add

Let us write our bot

extract.rb

require 'rest-client'
require 'json'
require 'mail'

module Forecast  
  class Extract  
    # Default API endpoint
    DEFAULT_FORECAST_IO_API_ENDPOINT = 'https://api.forecast.io'
    
    Mail.defaults do
      delivery_method :smtp, {
        :port                 => 587,
        :address              => "smtp.sendgrid.net",
        :user_name            => ENV['SENDGRID_USERNAME'], # has been set on Heroku
        :password             => ENV['SENDGRID_PASSWORD'], # has been set on Heroku
        :domain               => 'heroku.com',
        :authentication       => :plain,
        :enable_starttls_auto => true
      }
    end
   
    def initialize
      # ENV['API_KEY']    has been set on Heroku
      # ENV['LATITUDE']   has been set on Heroku
      # ENV['LONGITUDE']  has been set on Heroku
      @forecast_url = "#{DEFAULT_FORECAST_IO_API_ENDPOINT}/forecast/#{ENV['API_KEY']}/#{ENV['LATITUDE']},#{ENV['LONGITUDE']}"
    end
    
    def get_weather_forecast
      parsed = JSON.parse(RestClient.get(URI.encode(@forecast_url)))
      msg = "The current weather summary = #{parsed["currently"]["summary"]} and the temperature in F is #{parsed["currently"]["temperature"].to_s}. "
      if !(parsed["alerts"].nil?)
        msg += "An alert has been issued: #{parsed["alerts"][0]["title"]}"
      end
      send_email(msg)
    end
    
    private
    def send_email(msg)
      mail = Mail.deliver do
        to      'satish.talim@gmail.com'
        from    'Satish Talim ' # Your from name and email address
        subject 'Your weather report!'

        text_part do
          body msg
        end
      end
    end
  end  
end 

weather_bot.rb

require_relative '../lib/forecast/extract'

forecast = Forecast::Extract.new
forecast.get_weather_forecast

We shall talk in detail about our code soon.

Declare dependencies with a Gemfile

Heroku recognizes an app as Ruby by the existence of a Gemfile. Even if your app has no gem dependencies, you should still create an empty Gemfile in order that it appear as a Ruby app.

To create the Gemfile, in the already open Bash shell type:

$ bundle init

This creates a Gemfile file as follows:

# A sample Gemfile
source "https://rubygems.org"
# gem "rails"

Edit the created Gemfile with your preferred text editor to contain the following:

source 'https://rubygems.org'
ruby '2.0.0'
gem 'rest-client'
gem 'json'
gem 'mail'

Now we need to tell Bundler to check if we’re missing the gems our application depends on, if so, tell it to install them. In your open Bash shell type:

$ bundle check

It outputs:

Resolving dependencies...
The Gemfile's dependencies are satisfied

Finally in the open Bash shell, type:

$ bundle install

This will ensure all gems specified on Gemfile, together with their dependencies, are available for your application. Running bundle install will also generate a Gemfile.lock file. The Gemfile.lock ensures that your deployed versions of gems on Heroku match the version installed locally on your development machine.

Declare process types with Procfile

Use a Procfile, a text file in the root directory of your application, to explicitly declare what command should be executed to start a web dyno.

Here’s a Procfile for our bot:

web: bundle exec ruby bin/weather_bot.rb -p $PORT

This declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type will be attached to the HTTP routing stack of Heroku, and receive web traffic when deployed.

Note: A Procfile is not necessary to deploy apps written in most languages supported by Heroku. The platform automatically detects the language, and creates a default web process type to boot the application server.

Store your app in Git

Next we set up our local app to use Git. Type:

$ git init
$ git add .
$ git commit -m "Ruby bot app first commit"

Deploy to Heroku

Let’s create our bot app, call it forecast-extract-bot on Heroku. Type:

$ heroku apps:create forecast-extract-bot
Creating forecast-extract-bot... done, stack is cedar
http://forecast-extract-bot.herokuapp.com/ | git@heroku.com:forecast-extract-bot.git
Git remote heroku added

The command’s output shows that the app will be available at http://forecast-extract-bot.herokuapp.com/. The second URL, git@heroku.com:forecast-extract-bot.git, is the remote git repository URL; by default, the heroku create command automatically adds a git remote named “heroku” pointing at this URL.

Do note that the output from the create command will be different for each one of you.

Now let us deploy our code to Heroku. Type:

$ git push heroku master

Although our bot has been deployed to Heroku, our bot has no web interface and http://forecast-extract-bot.herokuapp.com/ will give a Runtime Error. After our code has been deployed to Heroku, log in to your Heroku account on the web. Go to our app forecast-extract-bot and click on Get Add-ons. From the screen that loads, go to the Email and SMS section and select SendGrid add-on Starter pack, which is free. Please do note that to use this pack, you will have to give your credit-card details.

Some details of our Ruby code

Do refer to the code of extract.rb

In the already open Bash shell, type:

$ heroku config

This command displays the SENDGRID_USERNAME and SENDGRID_PASSWORD which we use in extract.rb

Next, let us set the following on Heroku: API_KEY, LATITUDE and LONGITUDE For example, type:

$ heroku config:set LATITUDE=37.423021

We are using the above in our extract.rb program.

SendGrid now starts sending me emails as shown:

The latitude and longitude that I have used for my bot is for the address 1600 Amphitheatre Parkway, Mountain View, CA. The latitude and longitude are 37.423021, -122.083739. The email that I get is:

The Email
The Email

You can check out the source code here.

Thanks to Victor Goff, our mentor at RubyLearning.org for his invaluable inputs.

That’s it! Have fun with Ruby!

is an author and founder of RubyLearning.com and RubyLearning.org where 1000′s of participants, since 2005, have learnt Ruby programming from across the globe.

Posted by Satish Talim

Follow me on Twitter to communicate and stay connected

{ 2 comments… read them below or add one }

Luis July 7, 2013 at 4:12 am

Hi Satish Talim, good tutorial. I just made the bot. I have a question: How can I program the bot so as to visit the weather API at a certain time, in Heroku (like a Cron Job).

Reply

Satish Talim July 7, 2013 at 6:20 am

You can use Heroku Scheduler – https://devcenter.heroku.com/articles/scheduler

Reply

Leave a Comment

{ 1 trackback }