Adhearsion is...

Adhearsion is a new way to write voice-enabled applications. It's not just an API or library — it's a fully-featured framework , the first of its kind, designed for maximal code reuse and intuitiveness. The name "Adhearsion" is a combination of "adhesion" and "hear" because Adhearsion shines best when integrating technologies with voice.

It's modern

Adhearsion uses the high-level Ruby programming language and has a comprehensive regression test suite. In the age of the social web, what other technologies are more social than voice? Adhearsion aims to help merge this so-called "Web 2.0" world with the previously untamable telephony world.

It's intuitive

Voice applications don't have to be hard. Adhearsion abstracts much of the complicated telephony domain, allowing you to focus on what's really important: the business domain. Take a look at the Examples to see what we mean.

It's open-source

We take open-source software seriously. The telecom industry, above all, needs fresh innovative talent and Adhearsion's here to foster it. This is one of the final major frontiers for open-source to truly make a transformative impact and now is an exciting time to be a part of it.

We think it's fun

Many hackers' faces light up the first time they run code which makes the cell phone in their pocket ring. Whether you're a veteran telephony engineer or a web developer wanting a new experiment to do over the weekend, we think Adhearsion is enough of a departure from classical telephony to tickle your fancy.

Dialplan Examples

The "dialplan" is the main place where you control phone calls with Adhearsion. When you create a new Adhearsion app, you'll immediately see the dialplan.rb file in which you can begin writing your custom telephony app's realtime call control logic. The examples should help you get started.

Play a sound file

When using Adhearsion with Asterisk, you have hundreds of sound files to use right out of the box.

These range from the useful everyday sound files to pure novelties such as monkeys screaming and practical jokes.

To play a sound file, use the play method like below.

play "hello-world"

You can play multiple files in sequence too.

play "please-enter-your", "extension"

If this is your first time seeing Ruby, play is actually a method. Ruby does not require you wrap a method's arguments with parenthesis.

Sharing variables between contexts

Adhearsion creates a number of variables for you when it runs your dialplan which are available in any context you may jump around to.

For example, the following dialplan will speak back whatever extension the user dials.

adhearsion {
  play extension
}

The extension variable in this case is actually not an variable. It's a method that, because of Ruby's cleanliness, simply look like local variable. This may be a little confusing to you at first. Consider the following example:

adhearsion {
  extension = extension + 1
  play extension
  +player
}
player {
  play extension
}

When the adhearsion context jumps to the player context, you may think both contexts will speak the same number. It actually won't for a slightly technical reason:

When extension is defined with the = sign, Ruby will make that into a local variable and "shadow" anything else in a higher scope that may have that same name. Because the method extension is in a higher scope, the local variable will take precedence, but only in the local scope The player context has its own, separate scope and, therefore, the extension instance method is used.

To share information between contexts, simply use Ruby instance variables. We can rewrite the previous example properly as follows:

adhearsion {
  @extension += 1
  play @extension
  +player
}
player {
  play @extension
}

All contexts will run within the same Execution Environment object and therefore all instance variables will remain in scope no matter how many times you jump around.

Receiving keypad input

Taking input via phone keypad input is extremely simple.

desired_extension = input

The example above shows a Ruby principle at work: convention over configuration All of input parameters are optional. The code above simply receives any number of digits with no timeout until the user presses the "#" key. The result is then bound to the desired_extension variable with the delimiting "#" removed.

Or you can explicitly receive a number of digits with a timeout.

desired_extension = input 3, :timeout => 1.minute

This takes in three digits, waiting up to one minute for each successive key entry. Though a one minute timeout is a bit contrived, this example shows an interesting feature of Ruby: everything, even numbers, is an object. Objects have methods, therefore a minute method makes perfect sense.

Joining a conference bridge

Want the user to join a conference bridge? Just tell them to join something.

join "sales"

You can make the conference name virtually anything. For example, if you create a web-based tool to manage your company's conferences, you can make the conference name the unique ID of that conference's database record.

The join method uses the Asterisk MeetMe application. Alternatively, you may use the add-on application Conference using the execute method as follows:

execute "Conference", "room/S"

Using dialplan contexts

The Adhearsion dialplan.rb mirrors the Asterisk concept of a context. Within the extensions.conf file of Asterisk you may have these contexts

[from_dallas]
exten => _X.,1,AGI(agi://localhost)
exten => _X.,n,Hangup

[from_chicago]
exten => _X.,1,AGI(agi://localhost)
exten => _X.,n,Hangup

You would then have these corresponding contexts in the Adhearsion dialplan.rb:

from_texas {
  play 'hello', 'texas'
  # do more here
}

from_illinois {
  play 'hello', 'illinois'
  # do more here
}

Further, you may have additional contexts in the Adhearsion dialplan.rb that may be mixed in as follows:

from_omaha {
  play 'hello', 'omaha', 'and'
  +from_nebraska
}

from_nebraska {
  play 'hello', 'nebraska'
  # do more here
}

With this feature you may have reusable code in contexts within dialplan.rb.

Simple menu example

The menu command solves the problem of building enormous input-fetching state machines in Ruby without first-class message passing facilities or an external DSL. In otherwords, build complex menus to collect actionable user input via DTMF

Here is an example dialplan which uses the menu command effectively:

from_pstn {
  menu 'welcome', 'for-spanish-press-8', 'main-ivr',
       :timeout => 8.seconds, :tries => 3 do |link|
    link.shipment_status  1
    link.ordering         2
    link.representative   4
    link.spanish          8
    link.employee         900..999

    link.on_invalid { play 'invalid' }

    link.on_premature_timeout do |str|
      play 'sorry'
    end

    link.on_failure do
      play 'goodbye'
      hangup
    end
  end
}

shipment_status {
  # Fetch a tracking number and pass it to a web service.
}

ordering {
  # Enter another menu that lets them enter credit card
  # information and place their order over the phone.
}

representative {
  # Place the caller into a queue
}

spanish {
  # Special options for the spanish menu.
}

employee {
  dial "SIP/#{extension}" # Overly simplistic
}

Executing arbitrary Asterisk commands

While Adhearsion provides an extensive set of methods to dialplan.rb files, there may be times you want to execute an Asterisk command (known as an "application" in Asterisk parlance) that has not been exposed by Adhearsion.

The execute method accepts as its first argument the name of the Asterisk command followed by an arbitrarily long sequence of arguments.

execute "DISA", 1234, "disa_context"

Voip-Info.org has an extensive list of the available Asterisk commands here

Note: Asterisk itself has no dialplan-level concept of arguments. The example above is equivalent to DISA(1234|disa_context) in extensions.conf When Asterisk runs the DISA command, it passes the arguments between the parenthesis as a raw string and it's up to the application to parse it. For this reason, you may need to pass a String as the second argument to execute to manually format a finicky command's arguments.

Relational database examples

These examples show basic usage of ActiveRecord, an Object Relational Mapper library for accessing relational data from popular relational databases such as MySQL, PostgreSQl, or sqlite3 as if they were Ruby objects.

Writing an ActiveRecord "model"

ActiveRecord is an object-relational mapper (ORM) for Ruby which makes persistent database data look exactly like Ruby objects. Because ActiveRecord handles all of the SQL for you, it will work on virtually any SQL server for which it has adapters.

If you are not using Rails, then you may still want to access a database with your own models, which is possible with Adhearsion. When building a model, you are simply creating a class that represents the table definition as the class (ie - the columns) while each row becomes an instantiated object of that class.

One thing to keep in mind is that ActiveRecord works with conventions so you will want to do some research on the best way to design your database (don't worry, AR may do legacy stuff too).

With this in mind, if you have two tables named 'customers' and 'orders' you may create their models as follows:

class Customer < ActiveRecord::Base
end

class Order < ActiveRecord::Base
end

As long as you have followed conventions, that is it really. Then you may reference the table rows as follows:

Customer.all.each do |customer|
  puts "#{customer.first_name} #{customer.last_name}"
end

Iterating over all records in a table

With Adhearsion it is easy to access an RDBMS either through Rails or directly with ActiveRecord Once you have your models setup, you may fetch information in a database and then iterate over it in your dialplan.rb.

In this example we will show pulling information from a database to build a text to speech (TTS) audio file to playback to the caller.

# parking_lot.rb
class ParkingLot < ActiveRecord::Base
end

# dialplan.rb
parking_spaces_available {
  messages =

  # Fetch the parking lot details from the database
  parking_lots = ParkingLot.all

  # Then iterate over the results to build a menu
  messages = ["Welcome to the parking info service."] + parking_lots.collect do |parking_lot|
    "#{parking_lot.name} has #{parking_lot.spaces} spaces available."
  end

  # Then if you have Festival TTS installed you may
  # build your TTS file and play it back to the caller
  filename = "parking_spaces_available.ulaw"
  system "echo #{messages.join ' '} | text2wave -o #{filename} -otype ulaw"
  play filename
}

Finding the first record by a column value

In this example we will show finding one record from the database and using information from that row.

# parking_lot.rb
class ParkingLot < ActiveRecord::Base
end

# dialplan.rb
parking_lot_menu {
  menu 'welcome', 'for-bush-st-press1', 'for-hyde-press2',
       :timeout => 8.seconds, :tries => 3 do |link|
    link.bush_st 1
    link.hyde_st 2
  end
}

bush_st {
  # Here you may see we find the parking lot name
  # based on the column 'name'
  parking_lot = ParkingLot.where(:name => "Bush St").first

  play "bush-st-has", parking_lot.spaces, "spaces-available"
}

hyde_st {
  # Do the same thing here
}

Working with Components

Adhearsion's component system is a robust way of developing plugins to the framework. Trading telephony functionality in the past has been prohibitively difficult because telephony platforms are closed or monolithic. The component system allows the industry to take a step forward and start trading open-source telephony functionality. Functionality that you might trade with components varies quite a bit. You may want to trade something as simple as a single new dialplan method or something as complex as a full embedded web GUI. The examples below should help

Creating a new component

Creating a new component is easy with the built-in Adhearsion generators. From within your ~/ahn_project/ directory type:

ahn create component my_shiny_new_component

With this you get a directory and files as follows:

~/ahn_project/components/my_shiny_new_component/my_shiny_new_component.rb
~/ahn_project/components/my_shiny_new_component/my_shiny_new_component.yml

You may then disable the component as follows:

ahn disable component my_shiny_new_component

And enable as follows:

ahn enable component my_shiny_new_component

Adding a new dialplan method

Now that you have created your component, you may begin to create methods accessible to the different subsystems of Adhearsion. To add a method in your component that becomes available to your dialplan.rb, simply do the following:

# In your my_shiny_new_component.rb file
methods_for :dialplan do
  # Totally useless method to count to 10
  def count_to_ten
    cnt = 0
    while cnt < 11
      cnt += 1
    end
    cnt
  end
end

# Then in your dialplan.rb the method is available
inbound {
  ten = count_to_ten
}

Defining a globally-recognized method

Any component methods defined within the :global scope will be available anywhere in the Ruby process.

methods_for :global do
  def open_door
    RestClient.get "http://front-door/control/open"
  end
end

When methods are defined in the global scope, you should make the assumption that you will only have access to other globally-available methods. Don't use dialplan methods such a play in your global methods; this would work when your dialplan calls the global method but blow up when another part of the framework calls it.

Events examples

An Adhearsion application may have much more going on than just call handling. What if you want to do something when a call hangup happens? What if you want to do something when an instant message is received? The asynchronous Adhearsion events subsystem lets you handle these actions concurrently.

Handling a telephony platform event with a particular name

The events.rb subsystem provides all events that the Asterisk manager.conf specifies for that user. Therefore you may want to deal with only certain events to trigger various actions.

In the events.rb you may do something like this:

events.asterisk.manager_interface.each do |event|

  # I always downcase so I do not have to remember too much
  case event.name.downcase.to_sym
  when :newstate
    # If the call was answered
    if event.headers["State"] == "Up"
      # do something
    end
  when :hangup
    # do something
  end

end

Run code after Adhearsion fully initializes

In your component, just register a callback for the :after_initialized namespace.

  initialization do
    Events.register_callback :after_initialized do
      ahn_log "Do stuff here!"
    end
  end

Other web framework examples

These examples show how to build web interfaces for Adhearsion with frameworks other than Ruby on Rails.

Talking to your Adhearsion app from PHP

Below is a sample PHP class you can use to talk with the Adhearsion restful_rpc component. The restful_rpc component comes disabled by default in all newly-generated applications.

  <?php

  // Define an Adhearsion PHP class for a REST HTTP connection
  class Adhearsion {

    public $url;
    public $username;
    public $password;

    function __construct($url, $username, $password) {
      $this->url = $url;
      $this->username = $username;
      $this->password = $password;
    }

    function invoke($method_name) {
      $json = json_encode(array_slice(func_get_args(), 1));

      $url = "$this->url/$method_name";
      return json_decode(http_post_data($url,
                                        $json,
                                        array("httpauth" => "$this->username:$this->password")));
    }

  }

  // Connect to the REST API of Adhearsion
  $ahn = new Adhearsion("localhost:5000", "jicksta", "roflcopterz");

  //Build an array of the options for calling
  $call_options = array(array ('channel' => 'SIP/303',
                         'context' => 'inbound',
                         'exten' => '1000',
                         'priority' => '1',
                         'async' => 'true',
                         'variable' => 'destination=304');

  // Invoke the Adhearsion originate method via an HTTP POST of a JSON object
  $ahn->invoke("call_into_context", $call_options);
  ?>

In this example, it does an "origination" to asynchronously call out to a SIP extension (103 in this case). When the callee answers, it will start executing the inbound Adhearsion context. In dialplan example below, we're extracting a variable set in the origination and using it to dial out again.

inbound {
  dial "SIP/#{get_variable 'destination'}"
}

Using web services

Adhearsion helps you integrate voice into your business and web services which are the lingua franca of most businesses' internal systems.

Consuming RESTful resources

Adhearsion has a great RPC interface out of the box that may be accessed via REST You may then access Adhearsion objects and methods from virtually any modern lanugage. In this case we will show Ruby consuming the web services (keep in mind Adhearsion uses JSON

require 'json'
require 'rest_client'

# First we create the class that handles the connection
class RESTfulAdhearsion

  DEFAULT_OPTIONS = {
    # Note: :user and :password are non-existent by default
    :host         => "localhost",
    :port         => "5000",
    :path_nesting => "/"
  }

  def initialize(options = {})
    @options = DEFAULT_OPTIONS.merge options

    @path_nesting = @options.delete :path_nesting
    @host = @options.delete :host
    @port = @options.delete :port

    @url_beginning = "http://#{@host}:#{@port}#{@path_nesting}"
  end

  def method_missing(method_name, *args)
    JSON.parse RestClient::Resource.new(@url_beginning + method_name.to_s, @options).post(args.to_json)
  end

end

#Create our Adhearsion object connected to the RESTful API of Adhearsion
Adhearsion = RESTfulAdhearsion.new(:host     => "localhost",
                                      :port     => 5000,
                                      :user     => "jicksta",
                                      :password => "roflcopterz")

# Then make a call to it to place a phone call
Adhearsion.originate(:channel => "SIP/3000",
                      :context  => "outbound",
                      :priority => "1",
                      :exten    => "1000",
                      :async    => "true")

Consuming the Yahoo Query Language

Example consuming the Yahoo Query Language The dialplan below will ask the user for their zipcode, fetch the weather based on zipcode and then play the temperature back to the user.

sandbox {

  play 'welcome'

  response = input 5,
             :play    => ['please-enter-your', 'zip-code'],
             :timeout => 10,
             :retries => 3

  query = "SELECT * FROM weather.forecast WHERE location = #{response}"

  url = URI.encode "http://query.yahooapis.com/v1/public/yql?format=json&q=#{query}"

  json_data = open(url).read
  weather_data = JSON.parse json_data

  play 'temperature', weather_data["query"]["results"]["channel"]["item"]["condition"]["temp"]

}

Using Adhearsion with JRuby

Adhearsion fully supports running on JRuby This section will show you how to run and leverage JRuby to get the most of of running on the Java Virtual Machine

Starting Adhearsion with JRuby

JRuby has its own, separate gem repository and requires gems be installed using its own gem executable. If you have the normal Ruby interpreter installed on your system you would be accustomed to using the 'gem' command to install new gems. For JRuby you may simply use 'jgem'.

Adhearsion has a separate jahn executable with a shebang line of #!/usr/bin/env jruby. If your jruby is available in your path, you can simply execute jahn directly as you would the ahn command.

  jahn start /path/to/my/app

Full Component Examples

We have created several fully working component examples that you may download and use.

Click to call

Example component for Adhearsion showing a browser-based click to call application. Also shows how to use the RESTful API of Adhearsion.

You may access the component here

An Adhearsion Stress Tester

The Call Tester is used to generate live inbound call load on an Asterisk or any other telephony system. The Call Tester also provides a facility to play DTMF tones at the beginning of a call in order to traverse an IVR menu and reach various inbound call routes. The intent is use this facility in both engineering QA as well as a tool for operations in the field to load test each and every Asterisk or voice platform implementation.

You may download the component here

Web scraping - Ann Arbor Parking

Provides an example Adhearsion component that exposes the Ann Arbor, MI parking lot space availability via an IVR. This example was inspired by the folks @ VoIP Tech Chat and their Perl example.

You may download the component here

Phone Surveys

Provides an example Adhearsion component that conducts call surveys. The component also provides a Rails GUI.

You may download the component here