Auto-Returning Phone Calls with Tropo

March 5th, 2012 by cmatthieu

Using Tropo’s Scripting API and Ruby, I wrote a clever little script that allows you to automatically return missed calls from people as soon as you hang up a phone call.

Here is the use case:

  • User A calls user B
  • While users A and B talking, user C calls user B
  • Tropo script records the missed user C call and returns their call after B hangs up. The Tropo script automatically calls users B and C and bridges them in a conference call.

Here is a video to help demonstrate the concept:

Here is the Ruby script using Iris Couch as a JSON datastore directly from the Tropo cloud!

require 'rubygems'
require 'net/http'
require 'json'

# Setup couchdb JSON datastore calls
module Couch

  class Server
    def initialize(host, port, options = nil)
      @host = host
      @port = port
      @options = options
    end

    def delete(uri)
      request(Net::HTTP::Delete.new(uri))
    end

    def get(uri)
      request(Net::HTTP::Get.new(uri))
    end

    def put(uri, json)
      req = Net::HTTP::Put.new(uri)
      req["content-type"] = "application/json"
      req.body = json
      request(req)
    end

    def post(uri, json)
      req = Net::HTTP::Post.new(uri)
      req["content-type"] = "application/json"
      req.body = json
      request(req)
    end

    def request(req)
      res = Net::HTTP.start(@host, @port) { |http|http.request(req) }
      unless res.kind_of?(Net::HTTPSuccess)
        handle_error(req, res)
      end
      res
    end

    private

    def handle_error(req, res)
      e = RuntimeError.new("#{res.code}:#{res.message}\nMETHOD:#{req.method}\nURI:#{req.path}\n#{res.body}")
      raise e
    end
  end
end

# define couchdb server name
server = Couch::Server.new("tropo.couchone.com", "80")

# get callerid
callerid = $currentCall.callerID
if callerid.length == 10
  callerid = "1" + callerid
end

say "welcome to tropo's call back demo"

ask "Please enter the country code and phone number of the person you are trying to reach followed by the pound sign.", {
    :choices => "[8-15 DIGITS]",
    :terminator => '#',
    :timeout => 15.0,
    :mode => "dtmf",
    :interdigitTimeout => 5 ,
    :onChoice => lambda { |event|

      target = event.value

      # check to see if record exists indicating that the called party is on another call
      res = server.get("/callerback/" + target) rescue nil
      jsonresp = res.body rescue nil

      # phone number record will exist if called party is on the phone
      if jsonresp

          # if on the phone, write number to return call when available
          postcallback = server.put("/callerback/" + target + "q", '{"phone":' + callerid + '}') rescue nil
          say target + " is on the phone now but will return your call shortly."
          hangup

      else

        # transfer call
        say "Please wait while we transfer your call. Press star to cancel the transfer."

        # write record to show that the dialed party is unavailable
        postoncall = server.put("/callerback/" + target, "{}") rescue nil

        # transfer call 
        transfer target, {
            :playvalue => "http://www.phono.com/audio/holdmusic.mp3",
            :terminator => "*",
            :onTimeout => lambda { |event|
                # no answer
                say "Sorry, but nobody answered at " + target.to_s + " please try again later."
                hangup
                }   
              }

        # Transfer blocks. The code below will not be executed until the transferred call completed.

        # find and delete current called party record
        getcurrentrec = server.get("/callerback/" + target) rescue nil
        jsongetcurrentrec = getcurrentrec.body rescue nil
        currentdata = JSON.parse(jsongetcurrentrec)
        deletecurrent = server.delete("/callerback/" + target + "?rev=" + currentdata["_rev"].to_s )

        # check to see if a number exists to callback and delete it
        getcallback = server.get("/callerback/" + target + "q") rescue nil
        jsoncallback = getcallback.body rescue nil

        # If someone called while we were on the phone, let's call them back!
        if jsoncallback

          # find and delete their callback number
          callbackdata = JSON.parse(jsoncallback)
          callerid = callbackdata["phone"]
          deletecallback = server.delete("/callerback/" + target + "q?rev=" + callbackdata["_rev"].to_s )  

          # Call the 2 numbers and bridge the new call using conference

          # create unique conference room based on timestamp
          timeVar  = Time.new
          conferenceID = timeVar.strftime("%Y%H%M%S")

          # set conference options
          conferenceOptions={
                          :mute=>false,
                          :playTones=>true,
                          :leaveprompt=>"beep"
                          }

          threads = []

          # Call First Leg (User)
          call 'tel:+' + target, {
                  :callerID => callerid.to_s,
                  :onAnswer => lambda{|event|
                    log "@"*5 + "User has answered"

                    #Create second thread for second leg (Operator)
                    threads << Thread.new do
                        log "@"*5 + "Start second thread"
                            call 'tel:+' + callerid.to_s, {
                                :callerID=>target.to_s,
                                :onAnswer=>lambda{|confevent|
                                    log "@"*5+"Operator answered join conference"
                                    newCall2 = confevent.value
                                    #announce caller
                                    newCall2.say("You have a call from " + target.to_s)
                                    #join operator to conference
                                    newCall2.conference(conferenceID,conferenceOptions)
                                }
                            }
                    end #thread

                    newCall = event.value
                    newCall.say("Please hold while we connect you to " + callerid.to_s)
                    newCall.conference(conferenceID,conferenceOptions)

                  }
                }

                threads.each { |t| t.join }

          end #jsoncallback

      end #jsonresp        

    },
    :onBadChoice => lambda { |event|
        say "We did not recognize that phone number"
    }
}

Related posts:

  1. Meet Phono – Tropo’s Web Phone
  2. Dial Multiple Phone and SIP Numbers & First to Answer Wins
  3. Control your Christmas lights by phone using Arduino and Tropo
  4. Ruby on Rails Conference Caller
  5. How to Build a Reminder Service Using Tropo and Ruby on Rails

Tags: , , ,

Leave a Reply

Please note: By submitting a comment you agree to comply with our Comment Policy. We welcome all comments, positive or negative, but do reserve the right to remove all or part of blog comments that do not comply with our policy.

Additionally, the first time you leave a comment on this blog, it will be held for moderation. After that first comment has been approved, future comments will be posted without delay.

Additional comments powered by BackType