Posts Tagged ‘Multi-channel’

Send a Fax with your Voice!

Monday, February 21st, 2011

Tropo just recently partnered with PamFax to deliver faxing capabilities via an API.  Jason Goecke wrapped their API with a very simple Ruby gem called pamfaxr available at GitHub and installable using a “gem install pamfaxr” from your command line.  Using the PamFaxr Ruby gem, I will demonstrate how to send a simple fax as well as how to build a Tropo Voice to Fax transcription application using our Scripting API!

Getting Started

Before you can start sending faxes, you need to head over to the PamFax site and sign up for a PamFax account. After you fill out and submit the form, you’ll get an email with your login credentials and how to get started.

You also need to install the Ruby gem called pamfaxr on to the system where you are going to run the application that will send the fax. The Ruby gem is available at GitHub and should install simply by typing “gem install pamfaxr” from your command line. You will need to edit the code to have your PamFax username and password.

Sending A Fax From The Command Line

Before we involve Tropo, here is the Ruby/Sinatra code for simply sending a fax from the command line using the PamFaxr gem:

require 'rubygems'
require 'pamfaxr'

# Pass user name and password
pamfaxr = PamFaxr.new :username => 'your_username',
                      :password => 'secret'

# Create a new FaxJob
faxjob = pamfaxr.create_fax_job

# Add the cover sheet
covers = pamfaxr.list_available_covers
pamfaxr.set_cover(covers['Covers']['content'][1]['id'], 'Chris was here 2!')

# Add files
# pamfaxr.add_remote_file('https://s3.amazonaws.com/pamfax-test/R-intro.pdf')
# pamfaxr.add_file('examples/R-intro.pdf')

# Add a recipient
pamfaxr.add_recipient('+14802191300')

# Loop until the fax is ready to send
loop do
  fax_state = pamfaxr.get_state
  break if fax_state['FaxContainer']['state'] == 'ready_to_send'
  sleep 5
end

# Send the fax
pamfaxr.send_fax

Just copy that code into a text file, edit it to have your information in it and then run it with ruby from your command line. In a short bit you should have a fax waiting for you.

Sending Faxes From Tropo

Now for the fun Tropo Voice to Fax application code! The first code snippet is written in Ruby runs on the Tropo cloud. It greets the caller, asks for your fax number, records your voice/fax message and sends the transcription via a callback to the Ruby/Sinatra application that sends the actual fax.  Here’s the Tropo code:

say "Welcome to the Tropo fax demo.", :voice => 'dave'

result = ask "What's your fax number? Please include your country code.", {
   :choices => "[11 DIGITS]", :voice => 'dave'}

record "Please say what you would like for me to fax.", {
    :beep => false,
    :voice => 'dave',
    :maxTIme => 60,
    :silenceTimeout  => 2,
    :transcriptionOutURI => "http://web1.tunnlr.com:11053/transcribe?fax=" + result.value
    }

say "Your voice is being converted to a facsimily!  Go check your fax machine!  Goodbye.", :voice => 'dave'

hangup

Here is the complimentary Ruby/Sinatra code that catches the Tropo transcription callback and sends the fax of the transcription to the number specified in the Tropo script using the PamFaxr gem as demonstrated above. This code needs to be run on a publicly-accessible web server to which Tropo can connect and send the data. You have several options including:

  • Running the code on your own publicly-available webserver
  • Running the code on a hosting service like Heroku
  • Running the code on your local machine and use a service like Tunnlr to make the service available

Once you have the Ruby/Sinatra code below running in one of those locations, you’ll just update the Tropo code above with the correct URL (on line 11).

The code is here:

require "rubygems"
require "sinatra"
require 'json'
require 'pamfaxr'

pamfaxr = PamFaxr.new :username => 'your_username',
                      :password => 'secret'

post "/transcribe" do

  transcript_json = JSON.parse(request.body.read)
  identifier = transcript_json['result']['identifier']
  transcript = transcript_json['result']['transcription']

  # Create a new FaxJob
  faxjob = pamfaxr.create_fax_job

  # Add the cover sheet
  covers = pamfaxr.list_available_covers
  pamfaxr.set_cover(covers['Covers']['content'][1]['id'], transcript)

  # Add a recipient
  pamfaxr.add_recipient('+' + params[:fax])

  # Loop until the fax is ready to send
  loop do
    fax_state = pamfaxr.get_state
    break if fax_state['FaxContainer']['state'] == 'ready_to_send'
    sleep 5
  end

  # Send the fax
  pamfaxr.send_fax

end

Wow, that was fun!  I hope that you enjoyed this demonstration and I hope that you find both our PamFaxr gem and our Tropo Scripting API useful and powerful.

An Example of How to Make a Tropo App Respond Differently to Different Channels (including Twitter)

Thursday, March 25th, 2010

Would you like to have your Tropo.com app respond differently depending upon which communications channel is used to connect to the app? Would you like to, for instance, have the app provide more of an introduction to voice callers but not for text channels like IM, SMS or Twitter?

Earlier today, I wrote about how to add Twitter support to a Tropo app and at the end of the piece I mentioned that my Yahoo!Weather sample application responded with more messages that I really wanted it to send via Twitter. In this post, I will show how you can use the “currentCall” session variable to tweak your app to respond differently depending upon which channel is being used.

First, let’s look at how the app originally responded via IM to a ZIP code:

tropobyIM.jpg

After one IM to the app I received seven responses! Not good. Perhaps in IM it might be okay, but in SMS or Twitter it is a bit too verbose.

REMOVING EXTRA MESSAGES IN TEXT MODE (BUT NOT IN VOICE)

If you look at those messages, the first and last messages are “extra” messages that introduce the app to the user and then clue the user into the fact that the app is done. We could just eliminate them, but then, particularly with the final message, we’re being kind of rude to the person who is calling in. We are just giving the report and hanging up. Perfectly fine in a text-based interaction, but not great for a voice interaction.

To modify the app to respond differently according to channels, we need to use the “currentCall” session variable. With that variable you have access to the parameters of the “callObject, including, most relevant to this issue, “channel“. So in python, my personal language of choice, I can add this at the beginning:

if currentCall.channel == "VOICE":
    say( "Welcome to the Tropo dot com sample Yahoo weather app." )

and this at the end:

if currentCall.channel == "VOICE":
    say( "Thats all. Goodbye!" )

With those two additions, I’ve now made it so that if you call the application at +1 (407) 374-3994 you will hear those phrases but if you use a text channel (SMS, IM or Twitter) you will not see those messages.

I then went through the python code and eliminated the multiple “say” commands so that the location, time, temperature and condition were all written on one line. The result is that now the interaction is down to 2 messages:

tropobyIM2.jpg

MORE TWEAKING FOR VOICE

This is obviously better, but notice that first message “Enter the ZIP code...“. If you hear that in a voice call, will you realize that you could either enter the ZIP code in digits on your keypad or say the ZIP code? Probably not… you probably think you can only use DTMF digits. So let’s change the prompt a bit:

if currentCall.channel == "VOICE":
    say( "Welcome to the Tropo dot com sample Yahoo weather app." )
    result = ask( "Please say or enter the ZIP code for a weather check", { 'choices' : "[5 DIGITS]" })
else:
    result = ask( "Enter the ZIP code for a weather check", { 'choices' : "[5 DIGITS]" })

Now we have a more friendly prompt for voice that clues people in to the fact that they can either enter digits or use speech recognition.

REMOVING THE INITIAL PROMPT IF DATA IS PROVIDED

The next step is obviously to get rid of that initial “Enter the ZIP code...” prompt for a text channel if a ZIP code is sent in the initial message. This points to a major difference between voice and text channels. With voice, you need to have that initial prompt. With text, the user can already know what they need to send and just send that data along.

The currentCall session variable also has a “initialText” parameter that includes the text sent by the user. So I can see that the next step I need to do is something like:

If currentCall.initialText is a valid ZIP code, don’t send the first prompt. If currentCall.initialText is NOT a valid ZIP code, or is just general text, do send the initial prompt.

Unfortunately: 1) I have to leave in a few minutes to catch a plane to fly back to my home in NH; and 2) I’ve been spending more of my days writing in PowerPoint versus python, so my coding is a bit rusty. My first reaction is just to do a try / except but I know I’ll need to play with it a bit. So I’ll tweak it some more later.

PLAYING WITH THE CODE

If you are a pythonista, you’re welcome to play with my sample app. I uploaded it to Github at:

http://github.com/danyork/tropo-twitter-weather-sample

If you come up with an elegant way to get rid of that initial prompt, I’ll be glad to write about your solution here.

And if you create a similar app in another language, I’m also glad to write about that here as well.

USING OTHER currentCall OPTIONS

Beyond “channel” and “initialText” there are a number of other interesting parameters found on the callObject reference page. While currentCall.channel lets you broadly differentiate between voice and text channels, currentCall.network lets you get even more granular and respond to specific channels differently. And of course currentCall.callerID is always a useful variable to have around.

With that, I’ve got a plane to catch… stay tuned for more…