SlideShare a Scribd company logo
1 of 82
Download to read offline
Sunday, December 16, 12
Lessons Learned Using
               and Testing with Twilio
               Tips, Tricks, and Best Practices




Sunday, December 16, 12
How Test Driven
               Design started the
               Robot Apocalypse!
               ... And how it's not my fault!




Sunday, December 16, 12
Customer Call System



Sunday, December 16, 12
Wanted New Efficiencies
               1. Connecting customers to the same agent;
                  developing a personal relationship.
               2. Automatically popping up the customer record
                  on the agent's browser.
               3. Collect call metrics and tie into other
                  datapoints.




Sunday, December 16, 12
Current Provider...
               1. Offered little to no real-time integration.
               2. Was unable or unwilling to customize solution.
               3. Was expensive.




Sunday, December 16, 12
Sunday, December 16, 12
Twilio...
               1. A REST-ful API to make and manipulate calls
                  and their associated data.
               2. Makes real-time callbacks over HTTP to your
                  application about incoming and ongoing calls.
               3. Inexpensive: $1 per number, $0.01 per call leg




Sunday, December 16, 12
[censored client]



Sunday, December 16, 12
Sunday, December 16, 12
Call Flows
               1. A user can click-to-call a target; the user's
                  phone is called first and is then connected to
                  the target.
               2. A user can enter any number and call it; the
                  user's phone is called first and is then
                  connected to the number.
               3. If a target calls the mainline, they are
                  immediately connected to a user.
               4. Unknown callers to the mainline are placed on
                  a hold queue; any user can handle them.


Sunday, December 16, 12
Demo

Sunday, December 16, 12
Excellent Documentation
               https://twilio.com/docs




Sunday, December 16, 12
Productizing Twilio
               http://kalzumeus.com/2011/12/19/productizing-twilio-applications/




Sunday, December 16, 12
Treat TwiML as Your
               View
               We use Builder to generate XML




Sunday, December 16, 12
TWiML Builder View
               xml.instruct!
               xml.Response do
                 xml.Say 'Hello. Are you'
                 xml.Play 'https://s3.amazonaws.com/CarbonFive/placeholder.wav'
                 xml.Say 'If not, please hold.'
                 xml.Play 'https://s3.amazonaws.com/CarbonFive/sign_off.wav'
                 xml.Enqueue(action: goodbye_twilio_call_path(@call),
                             waitUrl: hold_twilio_call_path(@call)) do
                   xml.text! 'hold'
                 end
               end




Sunday, December 16, 12
Port-Forward Callbacks
               To Development
               Set it up yourself or use:
               localtunnel http://localtunnel.com
               forward http://forwardhq.com




Sunday, December 16, 12
Model Calls AND
               Conversations
               We made heavy use of state_machine gem
               https://github.com/pluginaweek/state_machine




Sunday, December 16, 12
Where's the
       Robopocalypse?

Sunday, December 16, 12
Testing

Sunday, December 16, 12
Deprecated Sandbox



Sunday, December 16, 12
Test Account
               Like payment providers, it allows you to make
               dummy calls, with specific phone numbers
               resulting in specific responses.




Sunday, December 16, 12
Record responses with
               VCR
               Speeds up test suite.
               https://github.com/myronmarston/vcr




Sunday, December 16, 12
Conversations make for
               Messy State Machines



Sunday, December 16, 12
Sunday, December 16, 12
Sunday, December 16, 12
On top of all that, we
               were still figuring it out!



Sunday, December 16, 12
Changes would blow
                          away functionality!




Sunday, December 16, 12
Testing became
               Manual Labor
               Our poor PM constantly clicking through
               scenarios.




Sunday, December 16, 12
Phonio
               Made use of Twilio JS Client to provide multiple
               numbers backed by another Twilio account.




Sunday, December 16, 12
How could we automate
               this?
               As part of the build and continuous integration.




Sunday, December 16, 12
Sunday, December 16, 12
I didn't know



Sunday, December 16, 12
Gaming Bots
               Scripts that would act as other players to in
               networked games.




Sunday, December 16, 12
Capybara
               RSpec and Cucumber features use it to script a
               user going through your application.




Sunday, December 16, 12
Remote Host
               Capybara.run_server = false
               Capybara.app_host = 'http://staging.cyberdyne.com'




               Alternatively
               require 'capybara/cucumber'
               require 'capybara/spec/test_app'

               Capybara.app = TestApp
               Capybara.app_host = 'http://staging.cyberdyne.com'




Sunday, December 16, 12
Supports Multi-"Users"
               using_session :ahnold do
                 visit '/signin'
                 click 'Terminate', within: '#sarah_connor'
               end

               using_session :robert do
                 visit '/sightings/new'
                 check 'have_you_seen_this_boy'
                 click 'Submit'
               end




Sunday, December 16, 12
How to do the same for
               phones?



Sunday, December 16, 12
Sunday, December 16, 12
We're are NOT testing
               Twilio.



Sunday, December 16, 12
We are testing WITH
               Twilio.
               An important distinction!




Sunday, December 16, 12
The Pieces to a Solution
               were lying around.




Sunday, December 16, 12
What does it look like?
                                                         Twilio Account
                          Capybara             The App
                                     Browser               of the App
              Rspec/
             Cucumber




Sunday, December 16, 12
What does it look like?
                                                         Twilio Account
                          Capybara             The App
                                     Browser               of the App
              Rspec/
             Cucumber




                                                         Twilio Account
                                                          of the Bots




Sunday, December 16, 12
What does it look like?
                                                         Twilio Account
                          Capybara             The App
                                     Browser               of the App
              Rspec/
             Cucumber




                                               Sinatra

                                                         Twilio Account
                                                          of the Bots




Sunday, December 16, 12
What does it look like?
                                                         Twilio Account
                          Capybara             The App
                                     Browser               of the App
              Rspec/
             Cucumber




                                               Sinatra

                                                         Twilio Account
                                                          of the Bots




Sunday, December 16, 12
What does it look like?
                                                                 Twilio Account
                          Capybara                     The App
                                             Browser               of the App
              Rspec/
             Cucumber




                                                       Sinatra

                                                                 Twilio Account
                                                                  of the Bots
                                     Calls




Sunday, December 16, 12
What does it look like?
                                                                 Twilio Account
                          Capybara                     The App
                                             Browser               of the App
              Rspec/
             Cucumber




                                                       Sinatra

                                                                 Twilio Account
                                                                  of the Bots
                  Bots               Calls




Sunday, December 16, 12
What does it look like?
                                                                 Twilio Account
                          Capybara                     The App
                                             Browser               of the App
              Rspec/
             Cucumber




                                                       Sinatra

                                                                 Twilio Account
                                                                  of the Bots
                  Bots               Calls




Sunday, December 16, 12
What does it look like?
                                                                       Twilio Account
                          Capybara                       The App
                                             Browser                     of the App
              Rspec/
             Cucumber




                                                          Sinatra

                                                                       Twilio Account
                                                                        of the Bots
                  Bots               Calls

                                                       Twilio Client




Sunday, December 16, 12
WOPR
         http://github.com/ZestFinance/wopr
         http://github.com/carbonfive/wopr


Sunday, December 16, 12
A @wopr of a Feature
               @javascript @wopr
               Feature: Outbound Call
                 A user can enter phone number so that they can call it

                   Scenario: Simple Session
                     Given a user is logged in
                       And the user enters a phone number
                       And the user clicks the Call button
                     Then the user's phone is called
                       And the phone number is called
                       And they are speaking to each other




Sunday, December 16, 12
Setup in Cucumber
               require 'wopr/cucumber'

               require File.join(File.dirname(__FILE__), '..', '..', 'staging')

               Wopr.configure do |config|
                 config.twilio_server_port = 4000
                 config.twilio_callback_host = 'http://rudyjahchan.fwd.wf'
                 config.twilio_account_sid = TWILIO_ACCOUNT_SID
                 config.twilio_auth_token = TWILIO_AUTH_TOKEN
               end

               require File.join(File.dirname(__FILE__), '..', '..', 'bots')

               Wopr::TwilioService.new.update_callbacks([
                 Wopr::Bot[:ahnold].phone_number,
                 Wopr::Bot[:kyle].phone_number])

               Wopr::TwilioCallbackServer.boot




Sunday, December 16, 12
Creat identified Bots
               Wopr::Bot.create(:ahnold,
                          email: 'rudy+ahnold@carbonfive.com',
                          password: 'n07@r3@1p@$$w0rd',
                          phone_number: '5558675309')

               Wopr::Bot.create(:kyle,
                          phone_number: '5557779311')

               Wopr::Bot.create(:sarah,
                          phone_number: '9006492568')




Sunday, December 16, 12
The Goal: Simpler Code
               Then /^the user's phone is called$/ do
                 bot(:ahnold).should be_on_a_call
               end

               Then /^the user's phone is not called$/ do
                 bot(:ahnold).should_not be_on_a_call
               end

               Then /^the phone number is called$/ do
                 bot(:kyle).should be_on_a_call
               end

               Then /^they are speaking to each other$/ do
                 bot(:kyle).should be_on_a_call_with(bot(:ahnold))
               end

               Given /^an unknown caller dials the main line$/ do
                 bot(:kyle).make_a_call_to(CYBERDYNE_STAGING_PHONE_NUMBER)
               end




Sunday, December 16, 12
How does it work?



Sunday, December 16, 12
Stole a LOT from
               Capybara
               Particularly threading code not to block running
               specs.




Sunday, December 16, 12
Sinatra App
               module Wopr
                 class TwilioCallbackServer < Sinatra::Base
                   VERIFICATION_PHRASE = 'SHALL WE PLAY A GAME?'

                          set :views,
                            File.join(File.dirname(__FILE__), 'templates')

                          get '/__identify__' do
                            [200, {}, VERIFICATION_PHRASE]
                          end

                          post '/calls' do
                            # ...
                          end

                          # ...

                 end
               end




Sunday, December 16, 12
Mount on Rack
               def run_server(port)
                 require 'rack/handler/thin'
                 Thin::Logging.silent = true
                 Rack::Handler::Thin.run(self,
                                         Port: port)
               rescue LoadError
                 require 'rack/handler/webrick'
                 Rack::Handler::WEBrick.run(self,
                                             Port: port,
                                             AccessLog: [],
                                             Logger: WEBrick::Log::new(nil, 0))
               end




Sunday, December 16, 12
Launch in a thread
               def boot(port=Wopr.twilio_server_port)
                 @port = port
                 unless responsive?
                   @server_thread = Thread.new { run_server(@port) }
                 end

                 Timeout.timeout(60) { @server_thread.join(0.1) until responsive? }
               end

               def responsive?
                 return false if @server_thread && @server_thread.join(0)

                   res = Net::HTTP.start('127.0.0.1', @port) do |http|
                     http.get('/__identify__')
                   end

                 if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
                   return res.body == VERIFICATION_PHRASE
                 end
               rescue Errno::ECONNREFUSED, Errno::EBADF
                 return false
               end

Sunday, December 16, 12
Server manages Calls
               post '/calls' do
                 if(call = Call.find_by_sid(params[:CallSid]))
                   call.update params
                 else
                   Call.create(params)
                 end

                 builder :default
               end




Sunday, December 16, 12
<Say /> Keep-alive
               xml.instruct!
               xml.Response do
                 xml.Say(loop: 0) do
                   xml.text! <<GIBBERISH
               Yorn desh born, der ritt de gitt der gue,
               Orn desh, dee born desh, de umn
               bork! bork! bork!
               GIBBERISH
                 end
               end




Sunday, December 16, 12
Bots can examine Calls
               module Wopr
                 class Bot

                          # ...

                   def current_call
                     Call.find_all_by_number(phone_number).select{|call|
               call.status != 'completed'}.last
                   end

                          def on_a_call?
                            wait_until do
                              current_call
                            end
                          end

                          # ...

                 end
               end



Sunday, December 16, 12
Handle Asynchronicity
                          def eventually(seconds=Wopr.default_wait_time)
                            start_time = Time.now
                            begin
                              yield
                            rescue => e
                              raise e if (Time.now - start_time) >= seconds
                              sleep 1
                              retry
                            end
                          end

                          def wait_until(seconds=Wopr.default_wait_time)
                            eventually(seconds) do
                              result = yield
                              return result if result
                              raise ConditionNotMetError
                            end
                          rescue ConditionNotMetError
                            return false
                          end



Sunday, December 16, 12
Bot makes calls w/ Call
               module Wopr
                 class Bot

                          # ...

                          def make_a_call_to(phone_number)
                            Call.make(from: self.phone_number, to: phone_number)
                          end

                   # ...
                 end
               end

               module Wopr
                 class Call
                   class << self
                     def make(options)
                       TwilioService.new.make(options)
                     end

                          # ...

Sunday, December 16, 12
TwilioClientService
               require 'twilio-ruby'

               module Wopr
                 class TwilioService
                   def initialize
                     @twilio_client = Twilio::REST::Client.new(
                       Wopr.twilio_account_sid,
                       Wopr.twilio_auth_token
                     )
                   end

                          def make(options)
                            calls.create(options.merge(
                               url: "#{Wopr.twilio_callback_host}/calls"
                            ))
                          end

                          def hangup(sid)
                            call(sid).hangup
                          end
                          # ...


Sunday, December 16, 12
Do we KNOW they're
               TALKING to each other?
               It's possible the system made two phone calls, but
               they’re not with each other.




Sunday, December 16, 12
A Solution: Play and
               Detect Dial Tones!
               One bot starts to <Gather> digits, the other
               <Plays> them, and we confirm they receive it.




Sunday, December 16, 12
Call Gather & Play
               module Wopr
                 class Call

                          # ...

                          def play(digits)
                            TwilioService.new.play sid, digits
                          end

                          def gather
                            TwilioService.new.gather sid
                          end

                          # ...

                 end
               end




Sunday, December 16, 12
Redirect Calls to TWiML
          module Wopr
            class TwilioService

                  # ...

                  def play(sid, digits)
                    call(sid).redirect_to(
                      "#{Wopr.twilio_callback_host}/calls/#{sid}/play?digits=#{digits}"
                    )
                  end

                  def gather(sid)
                    call(sid).redirect_to(
                      "#{Wopr.twilio_callback_host}/calls/#{sid}/gather"
                    )
                  end

                  # ...

            end
          end


Sunday, December 16, 12
Prepare to <Gather />
                 module Wopr
                   class TwilioCallbackServer < Sinatra::Base

                          # ...

                          post '/calls/:sid/gather' do
                            builder :gather, locals: { sid: params[:sid] }
                          end

                     # ...
                   end
                 end

                 gather.builder
                 xml.instruct!
                 xml.Response do
                   xml.Gather(
                     timeout: "60",
                     action: "#{Wopr.twilio_callback_host}/calls/#{sid}/gathered",
                     numDigits: "4")
                 end

Sunday, December 16, 12
<Play digits="..." />
               module Wopr
                 class TwilioCallbackServer < Sinatra::Base

                          # ...

                   post '/calls/:sid/play' do
                     builder :play, locals: { sid: params[:sid], digits:
               params[:digits] }
                   end

                   # ...
                 end
               end


               play.builder
               xml.instruct!
               xml.Response do
                 xml.Play(digits: digits)
                 xml.Pause(length: 10)
               end

Sunday, December 16, 12
Gathered Digits Posted
               module Wopr
                 class TwilioCallbackServer < Sinatra::Base

                          # ...

                          post '/calls/:sid/gathered' do
                            if(call = Call.find_by_sid(params[:sid]))
                              call.gathered params[:Digits]
                            end

                            builder :default
                          end

                          # ...

                end
               end




Sunday, December 16, 12
Again Asynchronous!
               module Wopr
                 class Bot

                          # ...

                          def on_a_call_with?(another_bot)
                            current_call.gather
                            sleep 1
                            another_bot.current_call.play '6661'
                            wait_until do
                              current_call.gathered_digits.last == '6661'
                            end
                          end

                          # ...

                 end
               end




Sunday, December 16, 12
Dial-Tone not a Perfect
               Solution.
               What if tones are used to trigger actions? And
               how do we confirm audio FILE playback?




Sunday, December 16, 12
<Record> and SOX
               Retrieve the recording, digest with SOX audio
               library.




Sunday, December 16, 12
More Capybara Tie-ins?
               Given /^a user is logged in$/ do
                 bot(:ahnold).log_in
               end

               Given /^the user enters a phone number$/ do
                 bot(:ahnold).within('div#call') do
                   fill_in 'number', with: bot(:kyle).phone_number
                 end
               end




Sunday, December 16, 12
How far can we take it?



Sunday, December 16, 12
[pic]



Sunday, December 16, 12
Sample Use of wopr
               http://github.com/carbonfive/cyberdyne-systems




Sunday, December 16, 12
Questions?
               rudy@carbonfive.com
               @rudy on Twitter




Sunday, December 16, 12
Hasta la vista, baby!



Sunday, December 16, 12

More Related Content

Similar to How Test Driven Development started the Robot Apocalypse; Lessons learned using Twilio for Telephony

OpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdf
OpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdfOpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdf
OpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdfOpenStack Foundation
 
OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"
OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"
OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"Randy Bias
 
Mobile first service design
Mobile first service designMobile first service design
Mobile first service designUichong Dano Lee
 
Austin agile conf 2012 infrastructure automation-gmiranda
Austin agile conf 2012 infrastructure automation-gmirandaAustin agile conf 2012 infrastructure automation-gmiranda
Austin agile conf 2012 infrastructure automation-gmirandaGeorge Miranda
 
Dist::Zilla - Maximum Overkill for CPAN Distributions
Dist::Zilla - Maximum Overkill for CPAN DistributionsDist::Zilla - Maximum Overkill for CPAN Distributions
Dist::Zilla - Maximum Overkill for CPAN DistributionsRicardo Signes
 
DrupalCampNYC 10 - Native mobile apps with Drupal
DrupalCampNYC 10 - Native mobile apps with DrupalDrupalCampNYC 10 - Native mobile apps with Drupal
DrupalCampNYC 10 - Native mobile apps with Drupalzroger
 
What Ops Can Learn From Design
What Ops Can Learn From DesignWhat Ops Can Learn From Design
What Ops Can Learn From DesignRobert Treat
 
Scaling Django to the sky
Scaling Django to the skyScaling Django to the sky
Scaling Django to the skyNaren Arya
 
Your first sinatra app
Your first sinatra appYour first sinatra app
Your first sinatra appRubyc Slides
 
Working With Social APIs - SoMeT12
Working With Social APIs - SoMeT12Working With Social APIs - SoMeT12
Working With Social APIs - SoMeT12Mario Vasquez
 
Cloud Austin 2013: Conferenced2013
Cloud Austin 2013: Conferenced2013Cloud Austin 2013: Conferenced2013
Cloud Austin 2013: Conferenced2013Karthik Gaekwad
 
Build successful sites fast with Drupal distributions like Commons, OpenPubli...
Build successful sites fast with Drupal distributions like Commons, OpenPubli...Build successful sites fast with Drupal distributions like Commons, OpenPubli...
Build successful sites fast with Drupal distributions like Commons, OpenPubli...Acquia
 
Build successful sites fast with Drupal Distributions
Build successful sites fast with Drupal DistributionsBuild successful sites fast with Drupal Distributions
Build successful sites fast with Drupal DistributionsAcquia
 
20 minuti di zappa e non ci pensi più
20 minuti di zappa e non ci pensi più20 minuti di zappa e non ci pensi più
20 minuti di zappa e non ci pensi piùDaniel Depaoli
 
Html5 new sword for interactive app
Html5 new sword for interactive appHtml5 new sword for interactive app
Html5 new sword for interactive appYohan Totting
 
Maximiliano Firtman
Maximiliano FirtmanMaximiliano Firtman
Maximiliano FirtmanColombia3.0
 
DiUS Computing Lca Rails Final
DiUS  Computing Lca Rails FinalDiUS  Computing Lca Rails Final
DiUS Computing Lca Rails FinalRobert Postill
 
mobile application business - for MT2 at Sripatum
mobile application business - for MT2 at Sripatummobile application business - for MT2 at Sripatum
mobile application business - for MT2 at SripatumCharath Petthongchai
 

Similar to How Test Driven Development started the Robot Apocalypse; Lessons learned using Twilio for Telephony (20)

OpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdf
OpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdfOpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdf
OpenStack-Design-Summit-HA-Pairs-Are-Not-The-Only-Answer copy.pdf
 
OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"
OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"
OpenStack Summit :: Redundancy Doesn't Always Mean "HA" or "Cluster"
 
Mobile first service design
Mobile first service designMobile first service design
Mobile first service design
 
Austin agile conf 2012 infrastructure automation-gmiranda
Austin agile conf 2012 infrastructure automation-gmirandaAustin agile conf 2012 infrastructure automation-gmiranda
Austin agile conf 2012 infrastructure automation-gmiranda
 
Smartgears
SmartgearsSmartgears
Smartgears
 
Dist::Zilla - Maximum Overkill for CPAN Distributions
Dist::Zilla - Maximum Overkill for CPAN DistributionsDist::Zilla - Maximum Overkill for CPAN Distributions
Dist::Zilla - Maximum Overkill for CPAN Distributions
 
DrupalCampNYC 10 - Native mobile apps with Drupal
DrupalCampNYC 10 - Native mobile apps with DrupalDrupalCampNYC 10 - Native mobile apps with Drupal
DrupalCampNYC 10 - Native mobile apps with Drupal
 
What Ops Can Learn From Design
What Ops Can Learn From DesignWhat Ops Can Learn From Design
What Ops Can Learn From Design
 
Scaling Django to the sky
Scaling Django to the skyScaling Django to the sky
Scaling Django to the sky
 
Your first sinatra app
Your first sinatra appYour first sinatra app
Your first sinatra app
 
Working With Social APIs - SoMeT12
Working With Social APIs - SoMeT12Working With Social APIs - SoMeT12
Working With Social APIs - SoMeT12
 
Remote mobile debugging
Remote mobile debuggingRemote mobile debugging
Remote mobile debugging
 
Cloud Austin 2013: Conferenced2013
Cloud Austin 2013: Conferenced2013Cloud Austin 2013: Conferenced2013
Cloud Austin 2013: Conferenced2013
 
Build successful sites fast with Drupal distributions like Commons, OpenPubli...
Build successful sites fast with Drupal distributions like Commons, OpenPubli...Build successful sites fast with Drupal distributions like Commons, OpenPubli...
Build successful sites fast with Drupal distributions like Commons, OpenPubli...
 
Build successful sites fast with Drupal Distributions
Build successful sites fast with Drupal DistributionsBuild successful sites fast with Drupal Distributions
Build successful sites fast with Drupal Distributions
 
20 minuti di zappa e non ci pensi più
20 minuti di zappa e non ci pensi più20 minuti di zappa e non ci pensi più
20 minuti di zappa e non ci pensi più
 
Html5 new sword for interactive app
Html5 new sword for interactive appHtml5 new sword for interactive app
Html5 new sword for interactive app
 
Maximiliano Firtman
Maximiliano FirtmanMaximiliano Firtman
Maximiliano Firtman
 
DiUS Computing Lca Rails Final
DiUS  Computing Lca Rails FinalDiUS  Computing Lca Rails Final
DiUS Computing Lca Rails Final
 
mobile application business - for MT2 at Sripatum
mobile application business - for MT2 at Sripatummobile application business - for MT2 at Sripatum
mobile application business - for MT2 at Sripatum
 

Recently uploaded

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetEnjoy Anytime
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Hyundai Motor Group
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 

Recently uploaded (20)

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 

How Test Driven Development started the Robot Apocalypse; Lessons learned using Twilio for Telephony

  • 2. Lessons Learned Using and Testing with Twilio Tips, Tricks, and Best Practices Sunday, December 16, 12
  • 3. How Test Driven Design started the Robot Apocalypse! ... And how it's not my fault! Sunday, December 16, 12
  • 5. Wanted New Efficiencies 1. Connecting customers to the same agent; developing a personal relationship. 2. Automatically popping up the customer record on the agent's browser. 3. Collect call metrics and tie into other datapoints. Sunday, December 16, 12
  • 6. Current Provider... 1. Offered little to no real-time integration. 2. Was unable or unwilling to customize solution. 3. Was expensive. Sunday, December 16, 12
  • 8. Twilio... 1. A REST-ful API to make and manipulate calls and their associated data. 2. Makes real-time callbacks over HTTP to your application about incoming and ongoing calls. 3. Inexpensive: $1 per number, $0.01 per call leg Sunday, December 16, 12
  • 11. Call Flows 1. A user can click-to-call a target; the user's phone is called first and is then connected to the target. 2. A user can enter any number and call it; the user's phone is called first and is then connected to the number. 3. If a target calls the mainline, they are immediately connected to a user. 4. Unknown callers to the mainline are placed on a hold queue; any user can handle them. Sunday, December 16, 12
  • 13. Excellent Documentation https://twilio.com/docs Sunday, December 16, 12
  • 14. Productizing Twilio http://kalzumeus.com/2011/12/19/productizing-twilio-applications/ Sunday, December 16, 12
  • 15. Treat TwiML as Your View We use Builder to generate XML Sunday, December 16, 12
  • 16. TWiML Builder View xml.instruct! xml.Response do xml.Say 'Hello. Are you' xml.Play 'https://s3.amazonaws.com/CarbonFive/placeholder.wav' xml.Say 'If not, please hold.' xml.Play 'https://s3.amazonaws.com/CarbonFive/sign_off.wav' xml.Enqueue(action: goodbye_twilio_call_path(@call), waitUrl: hold_twilio_call_path(@call)) do xml.text! 'hold' end end Sunday, December 16, 12
  • 17. Port-Forward Callbacks To Development Set it up yourself or use: localtunnel http://localtunnel.com forward http://forwardhq.com Sunday, December 16, 12
  • 18. Model Calls AND Conversations We made heavy use of state_machine gem https://github.com/pluginaweek/state_machine Sunday, December 16, 12
  • 19. Where's the Robopocalypse? Sunday, December 16, 12
  • 22. Test Account Like payment providers, it allows you to make dummy calls, with specific phone numbers resulting in specific responses. Sunday, December 16, 12
  • 23. Record responses with VCR Speeds up test suite. https://github.com/myronmarston/vcr Sunday, December 16, 12
  • 24. Conversations make for Messy State Machines Sunday, December 16, 12
  • 27. On top of all that, we were still figuring it out! Sunday, December 16, 12
  • 28. Changes would blow away functionality! Sunday, December 16, 12
  • 29. Testing became Manual Labor Our poor PM constantly clicking through scenarios. Sunday, December 16, 12
  • 30. Phonio Made use of Twilio JS Client to provide multiple numbers backed by another Twilio account. Sunday, December 16, 12
  • 31. How could we automate this? As part of the build and continuous integration. Sunday, December 16, 12
  • 33. I didn't know Sunday, December 16, 12
  • 34. Gaming Bots Scripts that would act as other players to in networked games. Sunday, December 16, 12
  • 35. Capybara RSpec and Cucumber features use it to script a user going through your application. Sunday, December 16, 12
  • 36. Remote Host Capybara.run_server = false Capybara.app_host = 'http://staging.cyberdyne.com' Alternatively require 'capybara/cucumber' require 'capybara/spec/test_app' Capybara.app = TestApp Capybara.app_host = 'http://staging.cyberdyne.com' Sunday, December 16, 12
  • 37. Supports Multi-"Users" using_session :ahnold do visit '/signin' click 'Terminate', within: '#sarah_connor' end using_session :robert do visit '/sightings/new' check 'have_you_seen_this_boy' click 'Submit' end Sunday, December 16, 12
  • 38. How to do the same for phones? Sunday, December 16, 12
  • 40. We're are NOT testing Twilio. Sunday, December 16, 12
  • 41. We are testing WITH Twilio. An important distinction! Sunday, December 16, 12
  • 42. The Pieces to a Solution were lying around. Sunday, December 16, 12
  • 43. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sunday, December 16, 12
  • 44. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Twilio Account of the Bots Sunday, December 16, 12
  • 45. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sinatra Twilio Account of the Bots Sunday, December 16, 12
  • 46. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sinatra Twilio Account of the Bots Sunday, December 16, 12
  • 47. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sinatra Twilio Account of the Bots Calls Sunday, December 16, 12
  • 48. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sinatra Twilio Account of the Bots Bots Calls Sunday, December 16, 12
  • 49. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sinatra Twilio Account of the Bots Bots Calls Sunday, December 16, 12
  • 50. What does it look like? Twilio Account Capybara The App Browser of the App Rspec/ Cucumber Sinatra Twilio Account of the Bots Bots Calls Twilio Client Sunday, December 16, 12
  • 51. WOPR http://github.com/ZestFinance/wopr http://github.com/carbonfive/wopr Sunday, December 16, 12
  • 52. A @wopr of a Feature @javascript @wopr Feature: Outbound Call A user can enter phone number so that they can call it Scenario: Simple Session Given a user is logged in And the user enters a phone number And the user clicks the Call button Then the user's phone is called And the phone number is called And they are speaking to each other Sunday, December 16, 12
  • 53. Setup in Cucumber require 'wopr/cucumber' require File.join(File.dirname(__FILE__), '..', '..', 'staging') Wopr.configure do |config| config.twilio_server_port = 4000 config.twilio_callback_host = 'http://rudyjahchan.fwd.wf' config.twilio_account_sid = TWILIO_ACCOUNT_SID config.twilio_auth_token = TWILIO_AUTH_TOKEN end require File.join(File.dirname(__FILE__), '..', '..', 'bots') Wopr::TwilioService.new.update_callbacks([ Wopr::Bot[:ahnold].phone_number, Wopr::Bot[:kyle].phone_number]) Wopr::TwilioCallbackServer.boot Sunday, December 16, 12
  • 54. Creat identified Bots Wopr::Bot.create(:ahnold, email: 'rudy+ahnold@carbonfive.com', password: 'n07@r3@1p@$$w0rd', phone_number: '5558675309') Wopr::Bot.create(:kyle, phone_number: '5557779311') Wopr::Bot.create(:sarah, phone_number: '9006492568') Sunday, December 16, 12
  • 55. The Goal: Simpler Code Then /^the user's phone is called$/ do bot(:ahnold).should be_on_a_call end Then /^the user's phone is not called$/ do bot(:ahnold).should_not be_on_a_call end Then /^the phone number is called$/ do bot(:kyle).should be_on_a_call end Then /^they are speaking to each other$/ do bot(:kyle).should be_on_a_call_with(bot(:ahnold)) end Given /^an unknown caller dials the main line$/ do bot(:kyle).make_a_call_to(CYBERDYNE_STAGING_PHONE_NUMBER) end Sunday, December 16, 12
  • 56. How does it work? Sunday, December 16, 12
  • 57. Stole a LOT from Capybara Particularly threading code not to block running specs. Sunday, December 16, 12
  • 58. Sinatra App module Wopr class TwilioCallbackServer < Sinatra::Base VERIFICATION_PHRASE = 'SHALL WE PLAY A GAME?' set :views, File.join(File.dirname(__FILE__), 'templates') get '/__identify__' do [200, {}, VERIFICATION_PHRASE] end post '/calls' do # ... end # ... end end Sunday, December 16, 12
  • 59. Mount on Rack def run_server(port) require 'rack/handler/thin' Thin::Logging.silent = true Rack::Handler::Thin.run(self, Port: port) rescue LoadError require 'rack/handler/webrick' Rack::Handler::WEBrick.run(self, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0)) end Sunday, December 16, 12
  • 60. Launch in a thread def boot(port=Wopr.twilio_server_port) @port = port unless responsive? @server_thread = Thread.new { run_server(@port) } end Timeout.timeout(60) { @server_thread.join(0.1) until responsive? } end def responsive? return false if @server_thread && @server_thread.join(0) res = Net::HTTP.start('127.0.0.1', @port) do |http| http.get('/__identify__') end if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection) return res.body == VERIFICATION_PHRASE end rescue Errno::ECONNREFUSED, Errno::EBADF return false end Sunday, December 16, 12
  • 61. Server manages Calls post '/calls' do if(call = Call.find_by_sid(params[:CallSid])) call.update params else Call.create(params) end builder :default end Sunday, December 16, 12
  • 62. <Say /> Keep-alive xml.instruct! xml.Response do xml.Say(loop: 0) do xml.text! <<GIBBERISH Yorn desh born, der ritt de gitt der gue, Orn desh, dee born desh, de umn bork! bork! bork! GIBBERISH end end Sunday, December 16, 12
  • 63. Bots can examine Calls module Wopr class Bot # ... def current_call Call.find_all_by_number(phone_number).select{|call| call.status != 'completed'}.last end def on_a_call? wait_until do current_call end end # ... end end Sunday, December 16, 12
  • 64. Handle Asynchronicity def eventually(seconds=Wopr.default_wait_time) start_time = Time.now begin yield rescue => e raise e if (Time.now - start_time) >= seconds sleep 1 retry end end def wait_until(seconds=Wopr.default_wait_time) eventually(seconds) do result = yield return result if result raise ConditionNotMetError end rescue ConditionNotMetError return false end Sunday, December 16, 12
  • 65. Bot makes calls w/ Call module Wopr class Bot # ... def make_a_call_to(phone_number) Call.make(from: self.phone_number, to: phone_number) end # ... end end module Wopr class Call class << self def make(options) TwilioService.new.make(options) end # ... Sunday, December 16, 12
  • 66. TwilioClientService require 'twilio-ruby' module Wopr class TwilioService def initialize @twilio_client = Twilio::REST::Client.new( Wopr.twilio_account_sid, Wopr.twilio_auth_token ) end def make(options) calls.create(options.merge( url: "#{Wopr.twilio_callback_host}/calls" )) end def hangup(sid) call(sid).hangup end # ... Sunday, December 16, 12
  • 67. Do we KNOW they're TALKING to each other? It's possible the system made two phone calls, but they’re not with each other. Sunday, December 16, 12
  • 68. A Solution: Play and Detect Dial Tones! One bot starts to <Gather> digits, the other <Plays> them, and we confirm they receive it. Sunday, December 16, 12
  • 69. Call Gather & Play module Wopr class Call # ... def play(digits) TwilioService.new.play sid, digits end def gather TwilioService.new.gather sid end # ... end end Sunday, December 16, 12
  • 70. Redirect Calls to TWiML module Wopr class TwilioService # ... def play(sid, digits) call(sid).redirect_to( "#{Wopr.twilio_callback_host}/calls/#{sid}/play?digits=#{digits}" ) end def gather(sid) call(sid).redirect_to( "#{Wopr.twilio_callback_host}/calls/#{sid}/gather" ) end # ... end end Sunday, December 16, 12
  • 71. Prepare to <Gather /> module Wopr class TwilioCallbackServer < Sinatra::Base # ... post '/calls/:sid/gather' do builder :gather, locals: { sid: params[:sid] } end # ... end end gather.builder xml.instruct! xml.Response do xml.Gather( timeout: "60", action: "#{Wopr.twilio_callback_host}/calls/#{sid}/gathered", numDigits: "4") end Sunday, December 16, 12
  • 72. <Play digits="..." /> module Wopr class TwilioCallbackServer < Sinatra::Base # ... post '/calls/:sid/play' do builder :play, locals: { sid: params[:sid], digits: params[:digits] } end # ... end end play.builder xml.instruct! xml.Response do xml.Play(digits: digits) xml.Pause(length: 10) end Sunday, December 16, 12
  • 73. Gathered Digits Posted module Wopr class TwilioCallbackServer < Sinatra::Base # ... post '/calls/:sid/gathered' do if(call = Call.find_by_sid(params[:sid])) call.gathered params[:Digits] end builder :default end # ... end end Sunday, December 16, 12
  • 74. Again Asynchronous! module Wopr class Bot # ... def on_a_call_with?(another_bot) current_call.gather sleep 1 another_bot.current_call.play '6661' wait_until do current_call.gathered_digits.last == '6661' end end # ... end end Sunday, December 16, 12
  • 75. Dial-Tone not a Perfect Solution. What if tones are used to trigger actions? And how do we confirm audio FILE playback? Sunday, December 16, 12
  • 76. <Record> and SOX Retrieve the recording, digest with SOX audio library. Sunday, December 16, 12
  • 77. More Capybara Tie-ins? Given /^a user is logged in$/ do bot(:ahnold).log_in end Given /^the user enters a phone number$/ do bot(:ahnold).within('div#call') do fill_in 'number', with: bot(:kyle).phone_number end end Sunday, December 16, 12
  • 78. How far can we take it? Sunday, December 16, 12
  • 80. Sample Use of wopr http://github.com/carbonfive/cyberdyne-systems Sunday, December 16, 12
  • 81. Questions? rudy@carbonfive.com @rudy on Twitter Sunday, December 16, 12
  • 82. Hasta la vista, baby! Sunday, December 16, 12