From 8a74a7085f3a3a83aae7fa932f7ec9f8e70a09ee Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Mon, 14 Sep 2009 23:18:08 -0700 Subject: [PATCH] Intial Commit --- Changelog | 3 + LICENSE | 22 + README.markdown | 30 ++ Rakefile | 27 ++ example-rest.rb | 63 +++ example-twiml.rb | 73 ++++ example-utils.rb | 37 ++ lib/.svn/all-wcprops | 11 + lib/.svn/entries | 40 ++ lib/.svn/format | 1 + lib/.svn/text-base/twilio.rb.svn-base | 373 +++++++++++++++++ lib/twilio.rb | 373 +++++++++++++++++ pkg/.svn/all-wcprops | 11 + pkg/.svn/entries | 41 ++ pkg/.svn/format | 1 + pkg/.svn/prop-base/twilio-0.1.2.gem.svn-base | 5 + pkg/.svn/text-base/twilio-0.1.2.gem.svn-base | Bin 0 -> 6671 bytes pkg/twilio-0.1.2.gem | Bin 0 -> 6671 bytes pkg/twilio-2.0.0.gem | Bin 0 -> 6656 bytes tests/.svn/all-wcprops | 11 + tests/.svn/entries | 40 ++ tests/.svn/format | 1 + .../.svn/text-base/response_spec.rb.svn-base | 394 ++++++++++++++++++ tests/response_spec.rb | 394 ++++++++++++++++++ 24 files changed, 1951 insertions(+) create mode 100644 Changelog create mode 100644 LICENSE create mode 100644 README.markdown create mode 100644 Rakefile create mode 100644 example-rest.rb create mode 100644 example-twiml.rb create mode 100644 example-utils.rb create mode 100644 lib/.svn/all-wcprops create mode 100644 lib/.svn/entries create mode 100644 lib/.svn/format create mode 100644 lib/.svn/text-base/twilio.rb.svn-base create mode 100644 lib/twilio.rb create mode 100644 pkg/.svn/all-wcprops create mode 100644 pkg/.svn/entries create mode 100644 pkg/.svn/format create mode 100644 pkg/.svn/prop-base/twilio-0.1.2.gem.svn-base create mode 100644 pkg/.svn/text-base/twilio-0.1.2.gem.svn-base create mode 100644 pkg/twilio-0.1.2.gem create mode 100644 pkg/twilio-2.0.0.gem create mode 100644 tests/.svn/all-wcprops create mode 100644 tests/.svn/entries create mode 100644 tests/.svn/format create mode 100644 tests/.svn/text-base/response_spec.rb.svn-base create mode 100644 tests/response_spec.rb diff --git a/Changelog b/Changelog new file mode 100644 index 000000000..6ebf1241a --- /dev/null +++ b/Changelog @@ -0,0 +1,3 @@ +Changelog for Twilio Ruby Gem +================================================= +0.1.2 - Renamed gem \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..189ffa52f --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2008 Twilio, Inc. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.markdown b/README.markdown new file mode 100644 index 000000000..7e9dc6bf1 --- /dev/null +++ b/README.markdown @@ -0,0 +1,30 @@ +## Ruby Twilio Helper Library + +### DESCRIPTION +The Twilio REST SDK simplifies the process of makes calls to the Twilio REST. +The Twilio REST API lets to you initiate outgoing calls, list previous call, +and much more. See http://www.twilio.com/docs for more information. + +### USAGE +To use the twiliorest.rb library with Rails just copy the twiliorest.rb file +into the lib directory of your Rails application. Then just require +"twiliorest.rb" in the controller or code file you wish to use the Twilio REST +API from. As shown in example.rb, you will need to specify the ACCOUNT_ID and +ACCOUNT_TOKEN given to you by Twilio before you can make REST requests. In +addition, you will need to choose a 'Caller' and 'Called' before making +outgoing calls. See http://www.twilio.com/docs for more information. + +### GEM + +### FILES +lib/twilio.rb -- include this library in your code +example-rest.rb -- example usage of REST +example-twiml.rb -- example usage of the TwiML generator +example-utils.rb -- example usage of utilities + +### LICENSE +The Twilio Ruby Helper Library is distributed under the MIT License + +### THANKS +Jay Philips (@adhearsion) for some great advice +Michael Ansel (@michaelansel) for a great starting point \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..c28afaeb0 --- /dev/null +++ b/Rakefile @@ -0,0 +1,27 @@ +require 'rubygems' +require 'rake/gempackagetask' + +spec = Gem::Specification.new do |s| + s.name = "twilio" + s.version = "2.0.0" + s.author = "Kyle Conroy" + s.email = "kjconroy@twilio.com" + s.homepage = "http://www.twilio.com/docs" + s.description = "A Ruby gem for communicating with the Twilio API and generating TwiML" + s.platform = Gem::Platform::RUBY + s.summary = "Some description" + s.files = FileList["{lib}/*"].to_a + s.require_path = "lib" + s.test_files = FileList["{test}/response_spec.rb"].to_a + s.has_rdoc = true + s.extra_rdoc_files = ["README.markdown"] + s.add_dependency("builder", ">= 2.1.2") +end + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.need_tar = true +end + +task :default => "pkg/#{spec.name}-#{spec.version}.gem" do + puts "generated latest version" +end diff --git a/example-rest.rb b/example-rest.rb new file mode 100644 index 000000000..e3f7da5b5 --- /dev/null +++ b/example-rest.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require "lib/twilio.rb" + +# Twilio REST API version +API_VERSION = '2008-08-01' + +# Twilio AccountSid and AuthToken +ACCOUNT_SID = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' +ACCOUNT_TOKEN = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY' + +# Outgoing Caller ID previously validated with Twilio +CALLER_ID = 'NNNNNNNNNN'; + +# Create a Twilio REST account object using your Twilio account ID and token +account = TwilioRest::Account.new(ACCOUNT_SID, ACCOUNT_TOKEN) + +# =========================================================================== +# 1. Initiate a new outbound call to 415-555-1212 +# uses a HTTP POST +d = { + 'Caller' => CALLER_ID, + 'Called' => '415-555-1212', + 'Url' => 'http://demo.twilio.com/welcome', +} +resp = account.request("/#{API_VERSION}/Accounts/#{ACCOUNT_SID}/Calls", + 'POST', d) +resp.error! unless resp.kind_of? Net::HTTPSuccess +puts "code: %s\nbody: %s" % [resp.code, resp.body] + +# =========================================================================== +# 2. Get a list of recent completed calls (i.e. Status = 2) +# uses a HTTP GET +d = { 'Status' => 2 } +resp = account.request("/#{API_VERSION}/Accounts/#{ACCOUNT_SID}/Calls", + 'GET', d) +resp.error! unless resp.kind_of? Net::HTTPSuccess +puts "code: %s\nbody: %s" % [resp.code, resp.body] + +# =========================================================================== +# 3. Get a list of recent notification log entries +# uses a HTTP GET +resp = account.request("/#{API_VERSION}/Accounts/#{ACCOUNT_SID}/\ +Notifications", 'GET') +resp.error! unless resp.kind_of? Net::HTTPSuccess +puts "code: %s\nbody: %s" % [resp.code, resp.body] + +# =========================================================================== +# 4. Get a list of audio recordings for a certain call +# uses a HTTP GET +d = { 'CallSid' => 'CA5d44cc11756367a4c54e517511484900' } +resp = account.request("/#{API_VERSION}/Accounts/#{ACCOUNT_SID}/Recordings", + 'GET', d) +resp.error! unless resp.kind_of? Net::HTTPSuccess +puts "code: %s\nbody: %s" % [resp.code, resp.body] + +# =========================================================================== +# 5. Delete a specific recording +# uses a HTTP DELETE, no response is returned when using DLETE +resp = account.request( \ + "/#{API_VERSION}/Accounts/#{ACCOUNT_SID}/Recordings/\ +RE7b22d733d3e730d234e94242b9697cae", 'DELETE') +puts "code: %s" % [resp.code] diff --git a/example-twiml.rb b/example-twiml.rb new file mode 100644 index 000000000..aaee762ab --- /dev/null +++ b/example-twiml.rb @@ -0,0 +1,73 @@ +require "lib/twilio.rb" + +# =========================================================================== +# 1. Say, Dial, and Play +@r = Twilio::Response.new +@r.append(Twilio::Say.new "Hello World", :voice => "man", :loop => "10") +@r.append(Twilio::Dial.new("4155551212", :timeLimit => "45")) +@r.append(Twilio::Play.new("http://www.mp3.com")) +puts @r.respond + +# +# Hello World +# http://www.mp3.com +# 4155551212 +# + + +# =========================================================================== +# 2. Gather, Redirect +@r = Twilio::Response.new; +@g = @r.append(Twilio::Gather.new(:numDigits => "10")) +@g.append(Twilio::Say.new("Press 1")) +@r.append(Twilio::Redirect.new()) +puts @r.respond + + +# +# +# Press 1 +# +# +# + + +# =========================================================================== +# 3. Add a Say verb multiple times +@r = Twilio::Response.new +@say = Twilio::Say.new("Press 1") +@r.append(@say); +@r.append(@say); +puts @r.respond + +# +# Press 1 +# Press 1 +# + +# =========================================================================== +# 4. Set any attribute / value pair + +@r = Twilio::Response.new +@r.append(Twilio::Redirect.new(:__crazy => "delicious")) +puts @r.respond + + +# +# +# + +# =========================================================================== +# 5. Convenience methods +@r = Twilio::Response.new +@r.addSay "Hello World", :voice => "man", :language => "fr", :loop => "10" +@r.addDial "4155551212", :timeLimit => "45" +@r.addPlay "http://www.mp3.com" +puts @r.respond + +# +# Hello World +# http://www.mp3.com +# 4155551212 +# + diff --git a/example-utils.rb b/example-utils.rb new file mode 100644 index 000000000..4bf347161 --- /dev/null +++ b/example-utils.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby + +require "lib/twilio.rb" + +# new Utils Object +utils = Twilio::Utils.new( + +# This is the signature we expect for the key, url, and params below +expected_sig = 'Ma7fvTryuU51vDGO2IT5/KhivpI=' + +# Twilio AccountSid and AuthToken +ACCOUNT_SID = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' +ACCOUNT_TOKEN = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY' + +# =========================================================================== +# 1. Validate a Twilio request + +# the URL and POST parameters would typically be provided by the web +# framework that is recieving the request from Twilio (e.g. Django) +url = "http://UUUUUUUUUUUUUUUUUU" +postvars = {} + +# the request from Twilio also includes the HTTP header: X-Twilio-Signature +# containing the expected signature +signature = "SSSSSSSSSSSSSSSSSSSSSSSSSSSS" + +# Create a new Utils Object +utils = Twilio::Utils.new(ACCOUNT_SID, ACCOUNT_TOKEN); + +# Check if the signature matches the expected signature +result = utils.validateRequest(signature, url, postvars); + +if result + puts "The signature is valid!" +else + puts "The signature was NOT VALID. It might have been spoofed!" +end \ No newline at end of file diff --git a/lib/.svn/all-wcprops b/lib/.svn/all-wcprops new file mode 100644 index 000000000..d83c93e6a --- /dev/null +++ b/lib/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 66 +/twilio/!svn/ver/3533/src/php/www/site/resources/lib/twilio-rb/lib +END +twilio.rb +K 25 +svn:wc:ra_dav:version-url +V 76 +/twilio/!svn/ver/3533/src/php/www/site/resources/lib/twilio-rb/lib/twilio.rb +END diff --git a/lib/.svn/entries b/lib/.svn/entries new file mode 100644 index 000000000..c1ea05825 --- /dev/null +++ b/lib/.svn/entries @@ -0,0 +1,40 @@ +8 + +dir +4035 +https://svn.twilio.com/twilio/src/php/www/site/resources/lib/twilio-rb/lib +https://svn.twilio.com/twilio + + + +2009-07-10T01:45:32.342055Z +3533 +kjconroy + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +56c2f7cb-6bf4-4c7c-91e7-2865e9f5b938 + +twilio.rb +file + + + + +2009-07-09T22:19:20.000000Z +87d0549e38874a7b031bf5c51786cb61 +2009-07-10T01:45:32.342055Z +3533 +kjconroy + diff --git a/lib/.svn/format b/lib/.svn/format new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/lib/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/.svn/text-base/twilio.rb.svn-base b/lib/.svn/text-base/twilio.rb.svn-base new file mode 100644 index 000000000..39bc4494c --- /dev/null +++ b/lib/.svn/text-base/twilio.rb.svn-base @@ -0,0 +1,373 @@ +=begin +Copyright (c) 2009 Twilio, Inc. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +=end + +# @author Twilio +module Twilio + require 'net/http' + require 'net/https' + require 'uri' + require 'cgi' + require 'rubygems' + require 'builder' + require 'openssl' + require 'base64' + + TWILIO_API_URL = 'https://api.twilio.com' + + # Twilio REST Helpers + class RestAccount + + #initialize a twilio account object + # + #@param [String, String] Your Twilio Acount SID/ID and Auth Token + #@return [Object] Twilio account object + def initialize(id, token) + @id = id + @token = token + end + + #sends a request and gets a response from the Twilio REST API + # + #@param [String, String, Hash] + #path, the URL (relative to the endpoint URL, after the /v1 + #method, the HTTP method to use, defaults to POST + #vars, for POST or PUT, a dict of data to send + # + #@return Twilio response XML + #@raises [ArgumentError] Invalid path parameter + #@raises [NotImplementedError] Method given is not implemented + def request(path, method=nil, vars={}) + if !path || path.length < 1 + raise ArgumentError, 'Invalid path parameter' + end + if method && !['GET', 'POST', 'DELETE', 'PUT'].include?(method) + raise NotImplementedError, 'HTTP %s not implemented' % method + end + + if path[0, 1] == '/' + uri = TWILIO_API_URL + path + else + uri = TWILIO_API_URL + '/' + path + end + + return fetch(uri, vars, method) + end + + #enocde the parameters into a URL friendly string + # + #@param [Hash] URL key / values + #@return [String] Encoded URL + protected + def urlencode(params) + params.to_a.collect! \ + { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") + end + + # Create the uri for the REST call + # + #@param [String, Hash] Base URL and URL parameters + #@return [String] URI for the REST call + def build_get_uri(uri, params) + if params && params.length > 0 + if uri.include?('?') + if uri[-1, 1] != '&' + uri += '&' + end + uri += urlencode(params) + else + uri += '?' + urlencode(params) + end + end + return uri + end + + # Returns a http request for the given url and parameters + # + #@param [String, Hash, String] Base URL, URL parameters, optional METHOD + #@return [String] URI for the REST call + def fetch(url, params, method=nil) + if method && method == 'GET' + url = build_get_uri(url, params) + end + uri = URI.parse(url) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + + if method && method == 'GET' + req = Net::HTTP::Get.new(uri.request_uri) + elsif method && method == 'DELETE' + req = Net::HTTP::Delete.new(uri.request_uri) + elsif method && method == 'PUT' + req = Net::HTTP::Put.new(uri.request_uri) + req.set_form_data(params) + else + req = Net::HTTP::Post.new(uri.request_uri) + req.set_form_data(params) + end + req.basic_auth(@id, @token) + + return http.request(req) + end + end + + # Twiml Response Helpers + module Verb + module ClassMethods + @attributes = [] + @allowed_verbs = [] + attr_accessor :attributes + + def allowed_verbs(*verbs) + return @allowed_verbs if verbs == [] + @allowed_verbs = [] if @allowed_verbs.nil? + verbs.each do |verb| + @allowed_verbs << verb.to_s.capitalize + end + @allowed_verbs = @allowed_verbs.uniq + end + + def attributes(*attrs) + return @attributes if attrs == [] + @attributes = [] if @attributes.nil? + @attributes = (@attributes + attrs).uniq + attr_accessor(*@attributes) + @attributes + end + end + + def attributes + self.class.attributes + end + + #test if a given verb is allowed to be nested + # + #@param [Object] Verb to be appended + #@return [true, false] + def allowed?(verb) + self.class.allowed_verbs.nil? ? false : self.class.allowed_verbs.include?(verb.class.name.split('::')[1]) + end + + #initialize a twilio response object + # + #@param [String, Hash] Body of the verb, and a hash of the attributes + #@return [Object] Twilio Verb object + # + #@raises [ArgumentError] Invalid Argument + def initialize(body = nil, params = {}) + @children = [] + if body.class == String + @body = body + else + @body = nil + params = body || {} + end + params.each do |k,v| + if !self.class.attributes.nil? && self.class.attributes.include?(k) + send(k.to_s+"=",v) + else + raise ArgumentError, "Attribute Not Supported" + end + end + end + + #set an attribute key / value + #no error checking + # + #@param [Hash] Hash of options + #@return void + def set(params = {}) + params.each do |k,v| + self.class.attributes k.to_s + send(k.to_s+"=",v) + end + end + + #output valid Twilio markup + # + #@param [Hash] Hash of options + #@return [String] Twilio Markup (in XML) + def respond(opts = {}) + opts[:builder] ||= Builder::XmlMarkup.new(:indent => opts[:indent]) + b = opts[:builder] + attrs = {} + #attributes.each {|a| puts send(a) } unless attributes.nil? + attributes.each {|a| attrs[a] = send(a) unless send(a).nil? } unless attributes.nil? + + if @children and @body.nil? + b.__send__(self.class.to_s.split(/::/)[-1], attrs) do + @children.each {|e|e.respond( opts.merge(:skip_instruct => true) )} + end + elsif @body and @children == [] + b.__send__(self.class.to_s.split(/::/)[-1], @body, attrs) + else + raise ArgumentError, "Cannot have children and a body at the same time" + end + end + + #output valid Twilio markup encoded for inclusion in a URL + # + #@param [] + #@return [String] URL encoded Twilio Markup (XML) + def asURL() + CGI::escape(self.respond) + end + + def append(verb) + if(allowed?(verb)) + @children << verb + @children[-1] + else + raise ArgumentError, "Verb Not Supported" + end + end + + # Verb Convenience Methods + def addSay(string_to_say = nil, opts = {}) + append(Twilio::Say.new(string_to_say, opts)) + end + + def addPlay(file_to_play = nil, opts = {}) + append Twilio::Play.new(file_to_play, opts) + end + + def addGather(opts = {}) + append Twilio::Gather.new(opts) + end + + def addRecord(opts = {}) + append Twilio::Record.new(opts) + end + + def addDial(number = nil, opts = {}) + append Twilio::Dial.new(number, opts) + end + + def addRedirect(url = nil, opts = {}) + append Twilio::Redirect.new(url, opts) + end + + def addPause(opts = {}) + append Twilio::Pause.new(opts) + end + + def addHangup + append Twilio::Hangup.new + end + + def addNumber(number, opts = {}) + append Twilio::Number.new(number, opts) + end + + end + + class Say + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :voice, :language, :loop + end + + class Play + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :loop + end + + class Gather + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :action, :method, :timeout, :finishOnKey, :numDigits + allowed_verbs :play, :say, :pause + end + + class Record + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :action, :method, :timeout, :finishOnKey, :maxLength, :transcribe, :transcribeCallback + end + + class Dial + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :action, :method, :timeout, :hangupOnStar, :timeLimit, :callerId + allowed_verbs :number + end + + class Redirect + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :method + end + + class Pause + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :length + end + + class Hangup + extend Twilio::Verb::ClassMethods + include Twilio::Verb + end + + class Number + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :sendDigits, :url + end + + class Response + extend Twilio::Verb::ClassMethods + include Twilio::Verb + allowed_verbs :say, :play, :gather, :record, :dial, :redirect, :pause, :hangup + end + + # Twilio Utility function and Request Validation class + class Utils + include OpenSSL + include Digest + + #initialize a twilio utils abject + # + #@param [String, String] Your Twilio Acount SID/ID and Auth Token + #@return [Object] Twilio account object + def initialize(id, token) + @id = id + @token = token + end + + def validateRequest(signature, url, params = {}) + sorted_post_params = params.sort + data = url + sorted_post_params.each do |pkey| + data = data + pkey[0]+pkey[1] + end + digest = OpenSSL::Digest::Digest.new('sha1') + expected = Base64.encode64(OpenSSL::HMAC.digest(digest, @id, data)).strip + return expected == signature + end + end + +end diff --git a/lib/twilio.rb b/lib/twilio.rb new file mode 100644 index 000000000..39bc4494c --- /dev/null +++ b/lib/twilio.rb @@ -0,0 +1,373 @@ +=begin +Copyright (c) 2009 Twilio, Inc. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +=end + +# @author Twilio +module Twilio + require 'net/http' + require 'net/https' + require 'uri' + require 'cgi' + require 'rubygems' + require 'builder' + require 'openssl' + require 'base64' + + TWILIO_API_URL = 'https://api.twilio.com' + + # Twilio REST Helpers + class RestAccount + + #initialize a twilio account object + # + #@param [String, String] Your Twilio Acount SID/ID and Auth Token + #@return [Object] Twilio account object + def initialize(id, token) + @id = id + @token = token + end + + #sends a request and gets a response from the Twilio REST API + # + #@param [String, String, Hash] + #path, the URL (relative to the endpoint URL, after the /v1 + #method, the HTTP method to use, defaults to POST + #vars, for POST or PUT, a dict of data to send + # + #@return Twilio response XML + #@raises [ArgumentError] Invalid path parameter + #@raises [NotImplementedError] Method given is not implemented + def request(path, method=nil, vars={}) + if !path || path.length < 1 + raise ArgumentError, 'Invalid path parameter' + end + if method && !['GET', 'POST', 'DELETE', 'PUT'].include?(method) + raise NotImplementedError, 'HTTP %s not implemented' % method + end + + if path[0, 1] == '/' + uri = TWILIO_API_URL + path + else + uri = TWILIO_API_URL + '/' + path + end + + return fetch(uri, vars, method) + end + + #enocde the parameters into a URL friendly string + # + #@param [Hash] URL key / values + #@return [String] Encoded URL + protected + def urlencode(params) + params.to_a.collect! \ + { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") + end + + # Create the uri for the REST call + # + #@param [String, Hash] Base URL and URL parameters + #@return [String] URI for the REST call + def build_get_uri(uri, params) + if params && params.length > 0 + if uri.include?('?') + if uri[-1, 1] != '&' + uri += '&' + end + uri += urlencode(params) + else + uri += '?' + urlencode(params) + end + end + return uri + end + + # Returns a http request for the given url and parameters + # + #@param [String, Hash, String] Base URL, URL parameters, optional METHOD + #@return [String] URI for the REST call + def fetch(url, params, method=nil) + if method && method == 'GET' + url = build_get_uri(url, params) + end + uri = URI.parse(url) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + + if method && method == 'GET' + req = Net::HTTP::Get.new(uri.request_uri) + elsif method && method == 'DELETE' + req = Net::HTTP::Delete.new(uri.request_uri) + elsif method && method == 'PUT' + req = Net::HTTP::Put.new(uri.request_uri) + req.set_form_data(params) + else + req = Net::HTTP::Post.new(uri.request_uri) + req.set_form_data(params) + end + req.basic_auth(@id, @token) + + return http.request(req) + end + end + + # Twiml Response Helpers + module Verb + module ClassMethods + @attributes = [] + @allowed_verbs = [] + attr_accessor :attributes + + def allowed_verbs(*verbs) + return @allowed_verbs if verbs == [] + @allowed_verbs = [] if @allowed_verbs.nil? + verbs.each do |verb| + @allowed_verbs << verb.to_s.capitalize + end + @allowed_verbs = @allowed_verbs.uniq + end + + def attributes(*attrs) + return @attributes if attrs == [] + @attributes = [] if @attributes.nil? + @attributes = (@attributes + attrs).uniq + attr_accessor(*@attributes) + @attributes + end + end + + def attributes + self.class.attributes + end + + #test if a given verb is allowed to be nested + # + #@param [Object] Verb to be appended + #@return [true, false] + def allowed?(verb) + self.class.allowed_verbs.nil? ? false : self.class.allowed_verbs.include?(verb.class.name.split('::')[1]) + end + + #initialize a twilio response object + # + #@param [String, Hash] Body of the verb, and a hash of the attributes + #@return [Object] Twilio Verb object + # + #@raises [ArgumentError] Invalid Argument + def initialize(body = nil, params = {}) + @children = [] + if body.class == String + @body = body + else + @body = nil + params = body || {} + end + params.each do |k,v| + if !self.class.attributes.nil? && self.class.attributes.include?(k) + send(k.to_s+"=",v) + else + raise ArgumentError, "Attribute Not Supported" + end + end + end + + #set an attribute key / value + #no error checking + # + #@param [Hash] Hash of options + #@return void + def set(params = {}) + params.each do |k,v| + self.class.attributes k.to_s + send(k.to_s+"=",v) + end + end + + #output valid Twilio markup + # + #@param [Hash] Hash of options + #@return [String] Twilio Markup (in XML) + def respond(opts = {}) + opts[:builder] ||= Builder::XmlMarkup.new(:indent => opts[:indent]) + b = opts[:builder] + attrs = {} + #attributes.each {|a| puts send(a) } unless attributes.nil? + attributes.each {|a| attrs[a] = send(a) unless send(a).nil? } unless attributes.nil? + + if @children and @body.nil? + b.__send__(self.class.to_s.split(/::/)[-1], attrs) do + @children.each {|e|e.respond( opts.merge(:skip_instruct => true) )} + end + elsif @body and @children == [] + b.__send__(self.class.to_s.split(/::/)[-1], @body, attrs) + else + raise ArgumentError, "Cannot have children and a body at the same time" + end + end + + #output valid Twilio markup encoded for inclusion in a URL + # + #@param [] + #@return [String] URL encoded Twilio Markup (XML) + def asURL() + CGI::escape(self.respond) + end + + def append(verb) + if(allowed?(verb)) + @children << verb + @children[-1] + else + raise ArgumentError, "Verb Not Supported" + end + end + + # Verb Convenience Methods + def addSay(string_to_say = nil, opts = {}) + append(Twilio::Say.new(string_to_say, opts)) + end + + def addPlay(file_to_play = nil, opts = {}) + append Twilio::Play.new(file_to_play, opts) + end + + def addGather(opts = {}) + append Twilio::Gather.new(opts) + end + + def addRecord(opts = {}) + append Twilio::Record.new(opts) + end + + def addDial(number = nil, opts = {}) + append Twilio::Dial.new(number, opts) + end + + def addRedirect(url = nil, opts = {}) + append Twilio::Redirect.new(url, opts) + end + + def addPause(opts = {}) + append Twilio::Pause.new(opts) + end + + def addHangup + append Twilio::Hangup.new + end + + def addNumber(number, opts = {}) + append Twilio::Number.new(number, opts) + end + + end + + class Say + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :voice, :language, :loop + end + + class Play + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :loop + end + + class Gather + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :action, :method, :timeout, :finishOnKey, :numDigits + allowed_verbs :play, :say, :pause + end + + class Record + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :action, :method, :timeout, :finishOnKey, :maxLength, :transcribe, :transcribeCallback + end + + class Dial + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :action, :method, :timeout, :hangupOnStar, :timeLimit, :callerId + allowed_verbs :number + end + + class Redirect + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :method + end + + class Pause + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :length + end + + class Hangup + extend Twilio::Verb::ClassMethods + include Twilio::Verb + end + + class Number + extend Twilio::Verb::ClassMethods + include Twilio::Verb + attributes :sendDigits, :url + end + + class Response + extend Twilio::Verb::ClassMethods + include Twilio::Verb + allowed_verbs :say, :play, :gather, :record, :dial, :redirect, :pause, :hangup + end + + # Twilio Utility function and Request Validation class + class Utils + include OpenSSL + include Digest + + #initialize a twilio utils abject + # + #@param [String, String] Your Twilio Acount SID/ID and Auth Token + #@return [Object] Twilio account object + def initialize(id, token) + @id = id + @token = token + end + + def validateRequest(signature, url, params = {}) + sorted_post_params = params.sort + data = url + sorted_post_params.each do |pkey| + data = data + pkey[0]+pkey[1] + end + digest = OpenSSL::Digest::Digest.new('sha1') + expected = Base64.encode64(OpenSSL::HMAC.digest(digest, @id, data)).strip + return expected == signature + end + end + +end diff --git a/pkg/.svn/all-wcprops b/pkg/.svn/all-wcprops new file mode 100644 index 000000000..191111687 --- /dev/null +++ b/pkg/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 66 +/twilio/!svn/ver/3533/src/php/www/site/resources/lib/twilio-rb/pkg +END +twilio-0.1.2.gem +K 25 +svn:wc:ra_dav:version-url +V 83 +/twilio/!svn/ver/3533/src/php/www/site/resources/lib/twilio-rb/pkg/twilio-0.1.2.gem +END diff --git a/pkg/.svn/entries b/pkg/.svn/entries new file mode 100644 index 000000000..d0c82f84a --- /dev/null +++ b/pkg/.svn/entries @@ -0,0 +1,41 @@ +8 + +dir +4035 +https://svn.twilio.com/twilio/src/php/www/site/resources/lib/twilio-rb/pkg +https://svn.twilio.com/twilio + + + +2009-07-10T01:45:32.342055Z +3533 +kjconroy + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +56c2f7cb-6bf4-4c7c-91e7-2865e9f5b938 + +twilio-0.1.2.gem +file + + + + +2009-07-09T19:34:20.000000Z +6bf602fbdd8d21afd46e288cad23bc6c +2009-07-10T01:45:32.342055Z +3533 +kjconroy +has-props + diff --git a/pkg/.svn/format b/pkg/.svn/format new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/pkg/.svn/format @@ -0,0 +1 @@ +8 diff --git a/pkg/.svn/prop-base/twilio-0.1.2.gem.svn-base b/pkg/.svn/prop-base/twilio-0.1.2.gem.svn-base new file mode 100644 index 000000000..5e9587e65 --- /dev/null +++ b/pkg/.svn/prop-base/twilio-0.1.2.gem.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/pkg/.svn/text-base/twilio-0.1.2.gem.svn-base b/pkg/.svn/text-base/twilio-0.1.2.gem.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..7e200c1b576e6c806d7636306bd4ac94f4463f6a GIT binary patch literal 6671 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 literal 0 HcmV?d00001 diff --git a/pkg/twilio-0.1.2.gem b/pkg/twilio-0.1.2.gem new file mode 100644 index 0000000000000000000000000000000000000000..7e200c1b576e6c806d7636306bd4ac94f4463f6a GIT binary patch literal 6671 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 literal 0 HcmV?d00001 diff --git a/pkg/twilio-2.0.0.gem b/pkg/twilio-2.0.0.gem new file mode 100644 index 0000000000000000000000000000000000000000..e3fe6b885752e9f18f04a08ccf2b5b68313423a0 GIT binary patch literal 6656 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 S;(-0_Pb4EzT(H0hN9 literal 0 HcmV?d00001 diff --git a/tests/.svn/all-wcprops b/tests/.svn/all-wcprops new file mode 100644 index 000000000..4f79f1a98 --- /dev/null +++ b/tests/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 68 +/twilio/!svn/ver/3533/src/php/www/site/resources/lib/twilio-rb/tests +END +response_spec.rb +K 25 +svn:wc:ra_dav:version-url +V 85 +/twilio/!svn/ver/3533/src/php/www/site/resources/lib/twilio-rb/tests/response_spec.rb +END diff --git a/tests/.svn/entries b/tests/.svn/entries new file mode 100644 index 000000000..c0a63044c --- /dev/null +++ b/tests/.svn/entries @@ -0,0 +1,40 @@ +8 + +dir +4035 +https://svn.twilio.com/twilio/src/php/www/site/resources/lib/twilio-rb/tests +https://svn.twilio.com/twilio + + + +2009-07-10T01:45:32.342055Z +3533 +kjconroy + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +56c2f7cb-6bf4-4c7c-91e7-2865e9f5b938 + +response_spec.rb +file + + + + +2009-07-09T22:20:56.000000Z +97f58d3f82c07ba6d8bec4dbae2a1b30 +2009-07-10T01:45:32.342055Z +3533 +kjconroy + diff --git a/tests/.svn/format b/tests/.svn/format new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/tests/.svn/format @@ -0,0 +1 @@ +8 diff --git a/tests/.svn/text-base/response_spec.rb.svn-base b/tests/.svn/text-base/response_spec.rb.svn-base new file mode 100644 index 000000000..14838da17 --- /dev/null +++ b/tests/.svn/text-base/response_spec.rb.svn-base @@ -0,0 +1,394 @@ +require File.dirname(__FILE__) + '/../lib/twilio.rb' + +module AccountExampleHelperMethods + + def bad_append(verb) + + lambda {verb.append(Twilio::Response)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Say)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Play)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Gather)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Record)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Redirect)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Hangup)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Pause)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Number)}.should raise_error(ArgumentError) + + end + + def bad_attr(verb) + lambda {verb.new(:crazy => 'delicious')}.should raise_error(ArgumentError) + end + +end + +describe Twilio::Response do + include AccountExampleHelperMethods + + it "should be an empty response" do + @r = Twilio::Response.new + @r.respond.should == '' + end + + it "add attribute" do + @r = Twilio::Response.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + it "bad attribute" do + bad_attr(Twilio::Response) + end + +end + +describe Twilio::Say do + include AccountExampleHelperMethods + + it "should say hello monkey" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey")) + @r.respond.should == 'Hello Monkey' + end + + it "should say hello monkey and loop 3 times" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey", :loop => 3)) + @r.respond.should == 'Hello Monkey' + end + + it "should say have a woman say hello monkey and loop 3 times" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey", :voice => 'woman')) + @r.respond.should == 'Hello Monkey' + end + + it "should say have a woman say hello monkey and loop 3 times and be in french" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey", :language => 'fr')) + @r.respond.should == 'Hello Monkey' + end + + it "convenience method: should say have a woman say hello monkey and loop 3 times and be in french" do + @r = Twilio::Response.new + @r.addSay "Hello Monkey", :language => 'fr' + @r.respond.should == 'Hello Monkey' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Say.new("Hello Monkey")) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Say.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Play do + include AccountExampleHelperMethods + + it "should play hello monkey" do + @r = Twilio::Response.new + @r.append(Twilio::Play.new("Hello Monkey.mp3")) + @r.respond.should == 'Hello Monkey.mp3' + end + + it "should play hello monkey" do + @r = Twilio::Response.new + @r.append(Twilio::Play.new("Hello Monkey.mp3", :loop => '3')) + @r.respond.should == 'Hello Monkey.mp3' + end + + it "convenience method: should play hello monkey" do + @r = Twilio::Response.new + @r.addPlay "Hello Monkey.mp3", :loop => '3' + @r.respond.should == 'Hello Monkey.mp3' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Play.new("Hello Monkey.mp3", :loop => '3')) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Play.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Record do + include AccountExampleHelperMethods + + it "should record" do + @r = Twilio::Response.new + @r.append(Twilio::Record.new()) + @r.respond.should == '' + end + + it "should record with an action and a get method" do + r = Twilio::Response.new + r.append(Twilio::Record.new(:action => "example.com", :method => 'GET')) + r.respond.should == '' + end + + it "should record with an maxlength, finishonkey, and timeout" do + r = Twilio::Response.new + r.append(Twilio::Record.new(:timeout => "4", :finishOnKey => '#', :maxLength => "30")) + r.respond.should == '' + end + + it "should record with a transcribe and transcribeCallback" do + r = Twilio::Response.new + r.append(Twilio::Record.new(:transcribeCallback => 'example.com')) + r.respond.should == '' + end + + it "convenience methods: should record with a transcribe and transcribeCallback" do + r = Twilio::Response.new + r.addRecord :transcribeCallback => 'example.com' + r.respond.should == '' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Record.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Record.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + + +describe Twilio::Redirect do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Redirect.new()) + @r.respond.should == '' + end + + it "should redirect to a url via POST" do + @r = Twilio::Response.new + @r.append(Twilio::Redirect.new("example.com", :method => "POST")) + @r.respond.should == 'example.com' + end + + it "convenience: should redirect to a url via POST" do + @r = Twilio::Response.new + @r.addRedirect "example.com", :method => "POST" + @r.respond.should == 'example.com' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Redirect.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Redirect.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + +end + +describe Twilio::Hangup do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Hangup.new()) + @r.respond.should == '' + end + + it "convenience: should Hangup to a url via POST" do + @r = Twilio::Response.new + @r.addHangup + @r.respond.should == '' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Hangup.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Hangup.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Pause do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Pause.new()) + @r.respond.should == '' + end + + it "convenience: should Pause to a url via POST" do + @r = Twilio::Response.new + @r.addPause :length => '4' + @r.respond.should == '' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Pause.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Pause.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Dial do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Dial.new("1231231234")) + @r.respond.should == '1231231234' + end + + it "convenience: should Hangup to a url via POST" do + @r = Twilio::Response.new + @r.addDial + @r.respond.should == '' + end + + it "add a number to a dial" do + @r = Twilio::Response.new + @d = @r.append(Twilio::Dial.new()) + @d.append(Twilio::Number.new("1231231234")) + @r.respond.should == '1231231234' + end + + it "convenience: add a number to a dial" do + @r = Twilio::Response.new + @d = @r.addDial + @d.addNumber "1231231234" + @r.respond.should == '1231231234' + end + + it "add attribute" do + @r = Twilio::Dial.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + it "bad append" do + verb = Twilio::Dial.new + lambda {verb.append(Twilio::Response)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Say)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Play)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Gather)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Record)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Redirect)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Hangup)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Pause)}.should raise_error(ArgumentError) + end + +end + +describe Twilio::Gather do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Gather.new("1231231234")) + @r.respond.should == '1231231234' + end + + it "convenience: should Hangup to a url via POST" do + @r = Twilio::Response.new + @r.addGather + @r.respond.should == '' + end + + it "add a number to a Gather" do + @r = Twilio::Response.new + @g = @r.append(Twilio::Gather.new) + @g.append(Twilio::Say.new("Hello World")) + @g.append(Twilio::Play.new("Hello World.mp3")) + @g.append(Twilio::Pause.new) + @r.respond.should == 'Hello WorldHello World.mp3' + end + + it "convenience: add a number to a Gather" do + @r = Twilio::Response.new + @g = @r.addGather + @g.addSay "Hello World" + @g.addPlay "Hello World.mp3" + @g.addPause + @r.respond.should == 'Hello WorldHello World.mp3' + end + + it "add attribute" do + @r = Twilio::Gather.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + it "bad append" do + verb = Twilio::Gather.new + lambda {verb.append(Twilio::Response)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Gather)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Record)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Redirect)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Hangup)}.should raise_error(ArgumentError) + end + +end + +describe Twilio::Utils do + + it "check a signed sinature" do + + # This is the signature we expect for the key, url, and params below + expected_sig = 'Ma7fvTryuU51vDGO2IT5/KhivpI=' + + # this is the secret key for your account + AuthToken = 'a1b2c3d4' + utils = Twilio::Utils.new(AuthToken, "34"); + # this is the url that twilio requested + url = 'http://yourserver.com/twilio/index.php?id=12345&encodedtext=hello+world' + + # these are the post params twilio sent in its request + params = Hash.new + params['second_post_param'] = 'world' + params['first_post_param'] = 'hello' + + # sort the params alphabetically, and append the key and value of each to the url + signature = utils.validateRequest(expected_sig, url, params) + + # calculate the hmacsha1 signature of the data using the key and return it in base64 format + signature.should == true + + end + +end \ No newline at end of file diff --git a/tests/response_spec.rb b/tests/response_spec.rb new file mode 100644 index 000000000..14838da17 --- /dev/null +++ b/tests/response_spec.rb @@ -0,0 +1,394 @@ +require File.dirname(__FILE__) + '/../lib/twilio.rb' + +module AccountExampleHelperMethods + + def bad_append(verb) + + lambda {verb.append(Twilio::Response)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Say)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Play)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Gather)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Record)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Redirect)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Hangup)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Pause)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Number)}.should raise_error(ArgumentError) + + end + + def bad_attr(verb) + lambda {verb.new(:crazy => 'delicious')}.should raise_error(ArgumentError) + end + +end + +describe Twilio::Response do + include AccountExampleHelperMethods + + it "should be an empty response" do + @r = Twilio::Response.new + @r.respond.should == '' + end + + it "add attribute" do + @r = Twilio::Response.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + it "bad attribute" do + bad_attr(Twilio::Response) + end + +end + +describe Twilio::Say do + include AccountExampleHelperMethods + + it "should say hello monkey" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey")) + @r.respond.should == 'Hello Monkey' + end + + it "should say hello monkey and loop 3 times" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey", :loop => 3)) + @r.respond.should == 'Hello Monkey' + end + + it "should say have a woman say hello monkey and loop 3 times" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey", :voice => 'woman')) + @r.respond.should == 'Hello Monkey' + end + + it "should say have a woman say hello monkey and loop 3 times and be in french" do + @r = Twilio::Response.new + @r.append(Twilio::Say.new("Hello Monkey", :language => 'fr')) + @r.respond.should == 'Hello Monkey' + end + + it "convenience method: should say have a woman say hello monkey and loop 3 times and be in french" do + @r = Twilio::Response.new + @r.addSay "Hello Monkey", :language => 'fr' + @r.respond.should == 'Hello Monkey' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Say.new("Hello Monkey")) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Say.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Play do + include AccountExampleHelperMethods + + it "should play hello monkey" do + @r = Twilio::Response.new + @r.append(Twilio::Play.new("Hello Monkey.mp3")) + @r.respond.should == 'Hello Monkey.mp3' + end + + it "should play hello monkey" do + @r = Twilio::Response.new + @r.append(Twilio::Play.new("Hello Monkey.mp3", :loop => '3')) + @r.respond.should == 'Hello Monkey.mp3' + end + + it "convenience method: should play hello monkey" do + @r = Twilio::Response.new + @r.addPlay "Hello Monkey.mp3", :loop => '3' + @r.respond.should == 'Hello Monkey.mp3' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Play.new("Hello Monkey.mp3", :loop => '3')) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Play.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Record do + include AccountExampleHelperMethods + + it "should record" do + @r = Twilio::Response.new + @r.append(Twilio::Record.new()) + @r.respond.should == '' + end + + it "should record with an action and a get method" do + r = Twilio::Response.new + r.append(Twilio::Record.new(:action => "example.com", :method => 'GET')) + r.respond.should == '' + end + + it "should record with an maxlength, finishonkey, and timeout" do + r = Twilio::Response.new + r.append(Twilio::Record.new(:timeout => "4", :finishOnKey => '#', :maxLength => "30")) + r.respond.should == '' + end + + it "should record with a transcribe and transcribeCallback" do + r = Twilio::Response.new + r.append(Twilio::Record.new(:transcribeCallback => 'example.com')) + r.respond.should == '' + end + + it "convenience methods: should record with a transcribe and transcribeCallback" do + r = Twilio::Response.new + r.addRecord :transcribeCallback => 'example.com' + r.respond.should == '' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Record.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Record.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + + +describe Twilio::Redirect do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Redirect.new()) + @r.respond.should == '' + end + + it "should redirect to a url via POST" do + @r = Twilio::Response.new + @r.append(Twilio::Redirect.new("example.com", :method => "POST")) + @r.respond.should == 'example.com' + end + + it "convenience: should redirect to a url via POST" do + @r = Twilio::Response.new + @r.addRedirect "example.com", :method => "POST" + @r.respond.should == 'example.com' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Redirect.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Redirect.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + +end + +describe Twilio::Hangup do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Hangup.new()) + @r.respond.should == '' + end + + it "convenience: should Hangup to a url via POST" do + @r = Twilio::Response.new + @r.addHangup + @r.respond.should == '' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Hangup.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Hangup.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Pause do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Pause.new()) + @r.respond.should == '' + end + + it "convenience: should Pause to a url via POST" do + @r = Twilio::Response.new + @r.addPause :length => '4' + @r.respond.should == '' + end + + it "should raises exceptions for wrong appending" do + @r = Twilio::Response.new + @s = @r.append(Twilio::Pause.new()) + bad_append @s + end + + it "add attribute" do + @r = Twilio::Pause.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + +end + +describe Twilio::Dial do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Dial.new("1231231234")) + @r.respond.should == '1231231234' + end + + it "convenience: should Hangup to a url via POST" do + @r = Twilio::Response.new + @r.addDial + @r.respond.should == '' + end + + it "add a number to a dial" do + @r = Twilio::Response.new + @d = @r.append(Twilio::Dial.new()) + @d.append(Twilio::Number.new("1231231234")) + @r.respond.should == '1231231234' + end + + it "convenience: add a number to a dial" do + @r = Twilio::Response.new + @d = @r.addDial + @d.addNumber "1231231234" + @r.respond.should == '1231231234' + end + + it "add attribute" do + @r = Twilio::Dial.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + it "bad append" do + verb = Twilio::Dial.new + lambda {verb.append(Twilio::Response)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Say)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Play)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Gather)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Record)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Redirect)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Hangup)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Pause)}.should raise_error(ArgumentError) + end + +end + +describe Twilio::Gather do + include AccountExampleHelperMethods + + it "should redirect" do + @r = Twilio::Response.new + @r.append(Twilio::Gather.new("1231231234")) + @r.respond.should == '1231231234' + end + + it "convenience: should Hangup to a url via POST" do + @r = Twilio::Response.new + @r.addGather + @r.respond.should == '' + end + + it "add a number to a Gather" do + @r = Twilio::Response.new + @g = @r.append(Twilio::Gather.new) + @g.append(Twilio::Say.new("Hello World")) + @g.append(Twilio::Play.new("Hello World.mp3")) + @g.append(Twilio::Pause.new) + @r.respond.should == 'Hello WorldHello World.mp3' + end + + it "convenience: add a number to a Gather" do + @r = Twilio::Response.new + @g = @r.addGather + @g.addSay "Hello World" + @g.addPlay "Hello World.mp3" + @g.addPause + @r.respond.should == 'Hello WorldHello World.mp3' + end + + it "add attribute" do + @r = Twilio::Gather.new + @r.set :crazy => 'delicious' + @r.respond.should == '' + end + + it "bad append" do + verb = Twilio::Gather.new + lambda {verb.append(Twilio::Response)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Gather)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Record)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Redirect)}.should raise_error(ArgumentError) + lambda {verb.append(Twilio::Hangup)}.should raise_error(ArgumentError) + end + +end + +describe Twilio::Utils do + + it "check a signed sinature" do + + # This is the signature we expect for the key, url, and params below + expected_sig = 'Ma7fvTryuU51vDGO2IT5/KhivpI=' + + # this is the secret key for your account + AuthToken = 'a1b2c3d4' + utils = Twilio::Utils.new(AuthToken, "34"); + # this is the url that twilio requested + url = 'http://yourserver.com/twilio/index.php?id=12345&encodedtext=hello+world' + + # these are the post params twilio sent in its request + params = Hash.new + params['second_post_param'] = 'world' + params['first_post_param'] = 'hello' + + # sort the params alphabetically, and append the key and value of each to the url + signature = utils.validateRequest(expected_sig, url, params) + + # calculate the hmacsha1 signature of the data using the key and return it in base64 format + signature.should == true + + end + +end \ No newline at end of file