trails 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ === 1.0.1 / 2010-02-06
2
+
3
+ * Fix basic errors; still no tests :(
4
+
1
5
  === 1.0.0 / 2010-02-05
2
6
 
3
7
  * 1 major enhancement
data/Manifest.txt CHANGED
@@ -3,6 +3,9 @@ History.txt
3
3
  Manifest.txt
4
4
  README.txt
5
5
  Rakefile
6
- bin/trails
7
6
  lib/trails.rb
7
+ lib/trails/exception.rb
8
+ lib/trails/twilio/account.rb
9
+ lib/trails/twilio/call_handling.rb
10
+ lib/trails/twilio/incoming.rb
8
11
  test/test_trails.rb
data/README.txt CHANGED
@@ -14,11 +14,11 @@ Includes support for a special MimeType? (twiml), and functional tests: as_twili
14
14
 
15
15
  == SYNOPSIS:
16
16
 
17
- class ApplicationController < ActionController::Base
18
- ..
17
+ class ApplicationController < ActionController::Base
18
+ ..
19
19
  include Twilio::CallHandling
20
- ..
21
- end
20
+ ..
21
+ end
22
22
 
23
23
  == REQUIREMENTS:
24
24
 
@@ -33,7 +33,7 @@ end
33
33
 
34
34
  == LICENSE:
35
35
 
36
- Copyright [2010] [Hemant Bhanoo]
36
+ Copyright 2010 Hemant Bhanoo
37
37
 
38
38
  Licensed under the Apache License, Version 2.0 (the "License");
39
39
  you may not use this file except in compliance with the License.
data/lib/trails.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Trails
2
- VERSION = '1.0.0'
2
+ VERSION = '1.0.1'
3
3
  end
4
4
  begin
5
5
  TwilioRest
@@ -7,8 +7,6 @@ rescue
7
7
  require 'twiliorest.rb'
8
8
  end
9
9
 
10
- require 'action_controller' # yes?
11
-
12
10
  require 'trails/exception.rb'
13
11
  require 'trails/twilio/account'
14
12
  require 'trails/twilio/call_handling'
@@ -0,0 +1,6 @@
1
+ module Trails
2
+ module Exception
3
+ class UnknownAccount < ::Exception; end
4
+ class InvalidSignature < ::Exception; end
5
+ end # module Exception
6
+ end # module Trails
@@ -0,0 +1,134 @@
1
+ module Trails
2
+ module Twilio
3
+ class Account
4
+ attr_reader :config
5
+ def initialize( opts = {} )
6
+ if( opts.blank? )
7
+ STDERR.puts "no opts specified. trying to pull opts from #{self.class.config.inspect}"
8
+ opts = self.class.config[self.class.config.keys.first]
9
+ end
10
+ @config = opts.dup
11
+ @sid = @config[:sid] || raise( "no sid specified on #{self}" )
12
+ @token = @config[:token]
13
+ @logger = @config[:logger]
14
+ end
15
+
16
+ def self.sid_from_request( request )
17
+ ( :development == RAILS_ENV.to_sym ) ? request.params['AccountSid'] : request.env["HTTP_X_TWILIO_ACCOUNTSID"]
18
+ end
19
+
20
+ def self.from_request( request )
21
+ sid = sid_from_request( request )
22
+ unless( config.has_key?( sid ) )
23
+ logger.warn{ "unknown account #{sid}. Request params: #{request.inspect}" }
24
+ raise Trails::Exception::UnknownAccount.new( sid )
25
+ end
26
+ account = new( config[sid].dup )
27
+ raise Trails::Exception::InvalidSignature unless account.verify_caller( request )
28
+ account
29
+ end
30
+
31
+ def verify_caller( request )
32
+ # TODO: check caller credentials here. :)
33
+ return true
34
+ end
35
+
36
+ # Make outgoing calls.
37
+ def call( number, handler_url, opts = {} )
38
+ params = {
39
+ 'Caller' => opts['Caller'],
40
+ 'Called' => number,
41
+ 'Url' => handler_url,
42
+ 'Method' => opts['Method'] || 'GET',
43
+ 'Timeout' => opts['Timeout'] || 15
44
+ }
45
+ api_version = opts[:api_version] || '2008-08-01'
46
+ logger.debug( "Calling twlio with params: #{params.inspect}" )
47
+ make_request( File.join( base_uri, 'Calls' ), 'POST', params )
48
+ end
49
+
50
+ # still a WIP:
51
+ # number: to
52
+ # body: text
53
+ # url : response url
54
+ # opts:
55
+ # :from => number
56
+ # :method => GET/POST
57
+ MAX_SMS_LENGTH = 160
58
+ def send_sms( number, body, url, opts = {} )
59
+ params = {
60
+ 'From' => opts[:from] || @config[:default_number],
61
+ 'To' => number,
62
+ 'Body' => body,
63
+ 'Url' => url,
64
+ 'Method' => opts[:method] || 'POST'
65
+ }
66
+ url = File.join( base_uri, 'SMS/Messages' )
67
+ logger.debug{ "Calling #{url} with #{params.inspect}" }
68
+ make_request(url, 'POST', params )
69
+ end
70
+
71
+ def incoming_numbers( reset = false )
72
+ if( @incoming_numbers.nil? || reset )
73
+ response =
74
+ make_request( File.join( base_uri, 'IncomingPhoneNumbers' ), 'GET' )
75
+
76
+ if( 200 == response.code.to_i )
77
+ @raw_incoming_numbers = Hpricot( response.body )
78
+ else
79
+ raise "got response code #{response.code} and body #{response.body}"
80
+ end
81
+ @incoming_numbers = @raw_incoming_numbers.search( '//phonenumber').
82
+ collect{|j| j.inner_html}
83
+ end
84
+ return @incoming_numbers
85
+ end
86
+
87
+ def outgoing_numbers( reset = false )
88
+ if( @outgoing_numbers.nil? || reset )
89
+ response =
90
+ make_request( "/2008-08-01/Accounts/#{@sid}/OutgoingCallerIds",
91
+ 'GET' )
92
+ @outgoing_numbers_raw = Hpricot( response.body ) if( 200 == response.code.to_i )
93
+ @outgoing_numbers = @outgoing_numbers_raw.search( '//phonenumber').
94
+ collect{|j| j.inner_html}
95
+ end
96
+ return @outgoing_numbers
97
+ end
98
+
99
+ protected
100
+
101
+
102
+ # This makes it easy to create and call the TwilioRest library without
103
+ # having to worry about where credentials come from and stuff.
104
+ def make_request( *args )
105
+ @twilio_account ||= TwilioRest::Account.new( @sid, @token )
106
+ @twilio_account.request( *args )
107
+ end
108
+
109
+ def base_uri( opts = {} )
110
+ api_version = opts[:api_version] || @api_version || '2008-08-01'
111
+ sid = opts[:sid] || @sid
112
+ "/#{api_version}/Accounts/#{sid}/"
113
+ end
114
+
115
+ def logger
116
+ self.class.logger
117
+ end
118
+ def self.logger
119
+ return @logger unless @logger.nil?
120
+ @logger = Logger.new( STDERR )
121
+ @logger.level = Logger::WARN
122
+ return @logger
123
+ end
124
+ def self.config
125
+ @@cfg ||= YAML::load_file( config_file )
126
+ end
127
+
128
+ def self.config_file
129
+ return File.join( RAILS_ROOT, 'config', 'twilio.yml' )
130
+ end
131
+
132
+ end # class Account
133
+ end # module Twilio
134
+ end # module Trails
@@ -0,0 +1,40 @@
1
+ module Trails
2
+ module Twilio
3
+ module CallHandling
4
+ def self.included( klass )
5
+ raise "can\'t include #{self} in #{klass} - not a Controller?" unless
6
+ klass.respond_to?( :before_filter )
7
+ Mime::Type.register_alias( "text/html", :twiml ) unless Mime.const_defined?( 'TWIML' )
8
+ klass.send( :before_filter, :setup_incoming_call )
9
+ klass.send( :attr_reader, :incoming_call )
10
+ klass.send( :alias_method_chain, :protect_against_forgery?, :twilio )
11
+ end
12
+
13
+ protected
14
+
15
+ def protect_against_forgery_with_twilio?
16
+ is_twilio_call? ? false : protect_against_forgery_without_twilio?
17
+ end
18
+
19
+ def setup_incoming_call
20
+ return unless is_twilio_call?
21
+ logger.debug( "at the beginning, request.params = #{request.parameters}" )
22
+ request.format = :twiml
23
+ response.content_type = 'text/xml'
24
+ @incoming_call = Trails::Twilio::Incoming.new( request )
25
+ end
26
+
27
+ # TODO: Move this onto the request object,
28
+ # it makes more sense to say:
29
+ #
30
+ # request.is_twilio_call?
31
+ #
32
+ def is_twilio_call?
33
+ return !Trails::Twilio::Account.sid_from_request( request ).blank?
34
+ end
35
+
36
+ protected
37
+
38
+ end # module CallHandling
39
+ end # module Twilio
40
+ end # module Trails
@@ -0,0 +1,58 @@
1
+ module Trails
2
+ module Twilio
3
+ class Incoming
4
+ attr_reader :account
5
+ def initialize( request, opts = {} )
6
+ @request = request
7
+ @account = Trails::Twilio::Account.from_request( request )
8
+ end
9
+
10
+ protected
11
+ attr_reader :request
12
+ def twilio_data
13
+ @twilio_data ||= requests.params.slice( INCOMING_VARS ).dup
14
+ end
15
+
16
+ INCOMING_VARS = [
17
+ # Always available:
18
+ 'CallGuid', # A unique identifier for this call, generated by Twilio. It's 34 characters long, and always starts with the letters CA.
19
+ 'Caller', # The phone number of the party that initiated the call. If the call is inbound, then it is the caller's caller-id. If the call is outbound, i.e., initiated by making a request to the REST Call API, then this is the phone number you specify as the caller-id.
20
+ 'Called', # The phone number of the party that was called. If the call is inbound, then it's your application phone number. If the call is outbound, then it's the phone number you provided to call.
21
+ 'AccountGuid', # Your Twilio account number which is the Twilio Account GUID for the call. It is 34 characters long, and always starts with the letters AC.
22
+ 'CallStatus', # The status of the phone call. The value can be "in-progress", "completed", "busy", "failed" or "no-answer". For a call that was answered and is currently going on, the status would be "in-progress". For a call that couldn't be started because the called party was busy, didn't pick up, or the number dialed wasn't valid: "busy", "no-answer", or "failed" would be returned. If the call finished because the call ended or was hung up, the status would be "completed".
23
+ 'CallerCity', # The city of the caller.
24
+ 'CallerState', # The state or province of the caller.
25
+ 'CallerZip', # The postal code of the caller.
26
+ 'CallerCountry', # The country of the caller.
27
+ 'CalledCity', # The city of the called party.
28
+ 'CalledState', # The state or province of the called party.
29
+ 'CalledZip', # The postal code of the called party.
30
+ 'CalledCountry', # The country of the called party.
31
+ # Gather:
32
+ 'Digits', # The digits received from the caller
33
+
34
+ 'RecordingUrl', # The URL of the recorded audio file
35
+ 'Duration', # The time duration of the recorded audio file
36
+ 'Digits', # What (if any) key was pressed to end the recording
37
+
38
+ # SMS:
39
+ 'SmsMessageSid', # Message SID
40
+ 'AccountSid', # Account ID
41
+ 'From', #
42
+ 'To', #
43
+ 'Body', # 160 chars
44
+
45
+ ]
46
+ public
47
+ INCOMING_VARS.uniq.each do |pname|
48
+ mname = pname.gsub( /[A-Z]/ ) { |s| "_#{s.downcase}" }.gsub( /^_/, '' )
49
+ # some extra debugging code here:
50
+ # ActionController::Base.logger.debug{ "defining method: #{mname} for param #{pname}" }
51
+ define_method( mname ) do
52
+ return request.params[ pname ]
53
+ end # define_method
54
+ end # each
55
+
56
+ end #
57
+ end # module Twilio
58
+ end # module Trails
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hemant Bhanoo
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-05 00:00:00 -08:00
12
+ date: 2010-02-06 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -48,8 +48,8 @@ description: |-
48
48
  Includes support for a special MimeType? (twiml), and functional tests: as_twilio{ get '/my_action' }
49
49
  email:
50
50
  - hemant@bhanoo.com
51
- executables:
52
- - trails
51
+ executables: []
52
+
53
53
  extensions: []
54
54
 
55
55
  extra_rdoc_files:
@@ -62,8 +62,11 @@ files:
62
62
  - Manifest.txt
63
63
  - README.txt
64
64
  - Rakefile
65
- - bin/trails
66
65
  - lib/trails.rb
66
+ - lib/trails/exception.rb
67
+ - lib/trails/twilio/account.rb
68
+ - lib/trails/twilio/call_handling.rb
69
+ - lib/trails/twilio/incoming.rb
67
70
  - test/test_trails.rb
68
71
  has_rdoc: true
69
72
  homepage: http://code.google.com/p/twilio-on-rails/
data/bin/trails DELETED
File without changes