Home > larynx

larynx

Larynx is a project mainly written in Ruby, based on the MIT license.

An evented application framework for the FreeSWITCH telephony platform. Its currently Alpha.

= Larynx

A framework to develop IVR applications in Ruby for the FreeSWITCH (FS) telephony platform. It is used with the FS event socket module to easily develop IVR applications in an asynchronous fashion.

It offer some useful functions and classes on top the default FreeSWITCH dialplan commands to make application development easier.

Larynx currently implements an 'outbound' socket listener for incoming calls to be handled. An 'inbound' module will probably follow soon enough.

== Install

On Rubygems.org:

sudo gem install larynx

You will need to have the FreeSWITCH server installed somewhere you can control.

== Example

Simplest possible

Larynx.answer {|call| call.speak 'Hello world! Or whoever you are.' }

Using the bare Application class, below is a guessing game.

class Guess < Larynx::Application def run @number = rand(9) + 1 @guess = '' @guesses = 0 get_guess end

def get_guess
  if @guesses < 3
    speak(guess_prompt) { @guesses += 1 }
  else
    speak "Sorry you didn't guess it. It was #{@number}. Try again soon.", :bargein => false
    hangup
  end
end

def guess_prompt
  @guesses == 0 ? 'Guess a number between 1 and 9.' : 'Have another guess.'
end

def check_guess
  if @guess.to_i == @number
    speak "You got it! It was #{@guess}. It took you #{@guesses} guesses.", :bargein => false
    speak "Thanks for playing."
    hangup
  else
    speak "No it's not #{@guess}."
    get_guess
  end
end

def dtmf_received(input)
  @guess = input
  check_guess
end

end

Larynx.answer {|call| Guess.run(call) }

A more sophisticated example using the Form class

class Guess < Larynx::Form field(:guess, :attempts => 3, :length => 1) do prompt :speak => 'Guess a number between 1 and 9.', :interdigit_timeout => 6 reprompt :speak => 'Have another guess.', :interdigit_timeout => 6

  setup do
    @number = rand(9) + 1
    @guesses = 0
  end

  validate do
    @guesses += 1 if guess.size > 0
    @number == guess.to_i
  end

  invalid do
    if guess.size > 0
      speak "No, it's not #{guess}.", :bargein => false
    end
  end

  success do
    speak "You got it! It was #{guess}. It took you #{@guesses} guesses.", :bargein => false
    hangup
  end

  failure do
    speak "Sorry you didn't guess it. It was #{@number}. Try again soon.", :bargein => false
    hangup
  end
end

end

Larynx.answer {|call| Guess.run(call) }

The Form class wraps up many handy conventions into a pleasant DSL in which allows you to control the caller interaction more easily.

Save your app into file and run larynx comand to start the app server ready to receive calls.

$ larynx app.rb

Now make a call to extension 2000 with a SIP phone. Your app should start.

== Configure FreeSWTICH

To set up a dialplan which connects to your app read http://wiki.freeswitch.org/wiki/Event_Socket

Also take a look at the http://wiki.freeswitch.org/wiki/Event_socket_outbound for background.

Example socket diaplan:

Which connects calls to destination number 2000 to your event socket app.

== Global Hooks

Larynx provides three globals hooks you can use to perform some action at each point. The are:

Larynx.connect {|call|

you can choose to hangup the call here if you wish

}

Larynx.answer {|call|

call is answered and ready to interact with the caller

}

Larynx.hungup {|call|

finish off any logging or some such

}

Mainly you just use the answer hook. From the examples you can see can start sending commands or start an application class running. You write an app just in this block but you don't want to.

== Application Class

The application adds a sprinkling of convenience for handling a call, plus you can store instance variables and create methods for structuring you app better.

The application should define a run instance method which is used to kick it off when you call

MyApp.run(call)

The class method initialises some things for you and then calls run on the instance. From there its up to you. You can use all the commands directly rather than call them on the call instance.

== Event Hooks

The Application and Form classes have a couple of useful event hook methods available which are

class MyApp < Larynx::Application def run end

def dtmf_received(input)
  # input is the button the caller just pushed
end

def hungup
  # application specific handling of a hangup
end

end

== Form Class

The Form class is a subclass of the Application class with some added goodness. You can use the field DSL to abstract away a lot of the repetitive work and more difficult asynchronous stuff you would have to handle yourself.

When collection input from the caller you are usually needing to do one or more of a few things.

These are:

  1. Set-up something before the caller is prompted for the information.
  2. Repeat the prompt for input if the caller doesn't enter anything or does not enter enough digits.
  3. Validate the input for constraints beyond length such as a range or finding a matching database record.
  4. Perform some task, perhaps say something, if the input is invalid but before the prompt is repeated.
  5. Handle the next action if the input is valid
  6. Handle the next action if the caller fails enter valid input after a number of attempts.

You define these tasks as callbacks for each type of action, be it setup, validate, invalid, success or failure. These callbacks are run in the scope of the form class instance each time so that they can access user defined methods or instance variables you need.

Each field defined becomes an attribute on the form class instance which you can use at any time. The value is set to the caller input after each prompt, just use the field attribute method to retrieve it. A form may have as many fields defined you as need. Though the idea would be to group fields with a particular purpose together and define another form for unrelated fields.

Lets look at a simple Form class with empty callbacks in place.

class MyApp < Larynx::Form setup do

Run when the form is first run or restarted.

end

field(:my_field, :attempts => 3, :length => 1) do
  prompt :speak => 'Please enter a value.'

  setup do
    # Run once when the field is starts.
  end

  validate do
    # Run when the input is of a valid length. Return true if valid.
  end

  invalid do
    # Run when the input is not a valid length or the validate block returns false.
  end

  success do
    # Run when the input is a valid length and the validate block, if defined, returns true.
  end

  failure do
    # Run when the maximum attempts has been reached and valid input has not been entered.
  end
end

end

This form will do the following when run:

  • Define a field attribute method called my_field.
  • Attempt to obtain the field value up to 3 times.
  • Accept a single digit value as complete and valid input.

The form instance has some helper methods for moving around the form or checking on state.

  • next_field :field_name (optional) - Moves to the next field in the queue or the named field if specified.
  • restart_form - Start the form over again including the setup callback.
  • current_field - Current field being run.
  • attempt - The attempt counter for the current field
  • last_attempt? - Checks if this is the last attempt for the current field.
  • session - Access or store data in the call session.

== Menu Class

The menu class is to simplify giving a list of options to the caller and waiting for their choice. The choice can be matched to a single number, a number range, or an array.

class MyMenu < Larynx::Menu prompt :speak => 'For kicks, press 1. For laughs, press 2. To exist, press 3.', :attempts => 3

option(1) { Kicks.run(call) }
option(2) { Laughs.run(call) }
option(3) { hangup }

end