4info 1.3.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,16 +8,16 @@ If you're using 4info.com as your SMS gateway this gem will give you a painless
8
8
  Setting Up Your Model
9
9
  =====
10
10
 
11
- Include FourInfo::Contactable into your User class or whatever you're using to represent an entity with a phone number.
11
+ Include Txter::Contactable into your User class or whatever you're using to represent an entity with a phone number.
12
12
 
13
13
  class User < ActiveRecord::Base
14
- include FourInfo::Contactable
14
+ include Txter::Contactable
15
15
  end
16
16
 
17
17
  You can also specify which attributes you'd like to use instead of the defaults
18
18
 
19
19
  class User < ActiveRecord::Base
20
- include FourInfo::Contactable
20
+ include Txter::Contactable
21
21
 
22
22
  sms_phone_number_column :mobile_number
23
23
  sms_blocked_column :is_sms_blocked
@@ -31,15 +31,15 @@ You can also specify which attributes you'd like to use instead of the defaults
31
31
  Turning the thing on
32
32
  ---
33
33
 
34
- Because it can be expensive to send TXTs accidentally, it's required that you manually configure FourInfo in your app. Put this line in config/environments/production.rb or anything that loads _only_ in your production environment:
34
+ Because it can be expensive to send TXTs accidentally, it's required that you manually configure Txter in your app. Put this line in config/environments/production.rb or anything that loads _only_ in your production environment:
35
35
 
36
- FourInfo.mode = :live
36
+ Txter.mode = :live
37
37
 
38
38
  Skipping this step (or adding any other value) will prevent TXTs from actually being sent.
39
39
 
40
40
  You'll also want to configure your setup with your client_id and client_key. Put this in the same file as above or in a separate initializer if you wish:
41
41
 
42
- FourInfo.configure do |config|
42
+ Txter.configure do |config|
43
43
  # these two are required:
44
44
  # (replace them with your actual account info)
45
45
  config.client_id = 12345
@@ -93,19 +93,19 @@ Receiving Messages From 4info.com
93
93
  ====
94
94
 
95
95
  You can also receive data posted to you from 4info.com. This is how you'll receive messages and notices that users have been blocked.
96
- All you need is to create a bare controller and include FourInfo::Controller into it. Then specify which Ruby class you're using as a contactable user model (likely User)
96
+ All you need is to create a bare controller and include Txter::Controller into it. Then specify which Ruby class you're using as a contactable user model (likely User)
97
97
 
98
98
 
99
99
  class SMSController < ApplicationController
100
- include FourInfo::Controller
100
+ include Txter::Controller
101
101
 
102
- sms_contactable User # or whichever class you included FourInfo::Contactable into
102
+ sms_contactable User # or whichever class you included Txter::Contactable into
103
103
  end
104
104
 
105
105
  And hook this up in your routes.rb file like so:
106
106
 
107
107
  ActionController::Routing::Routes.draw do |map|
108
- map.route '4info', :controller => 'four_info', :action => :index
108
+ map.route '4info', :controller => 'txter', :action => :index
109
109
  end
110
110
 
111
111
  Now just tell 4info.com to POST messages and block notices to you at:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.4
1
+ 2.0.0
data/init.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  # Include hook code here
2
2
 
3
- require 'four_info'
3
+ require 'txter'
@@ -1,4 +1,4 @@
1
- module FourInfo
1
+ module Txter
2
2
  class << self
3
3
  def mode
4
4
  @@mode ||= :test
@@ -26,7 +26,9 @@ module FourInfo
26
26
 
27
27
  attr_accessor :client_id
28
28
  attr_accessor :client_key
29
+ attr_accessor :gateway
29
30
  attr_accessor :short_code
31
+ attr_accessor :default_from_phone_number
30
32
  attr_accessor :proxy_address
31
33
  attr_accessor :proxy_port
32
34
  attr_accessor :proxy_username
@@ -1,4 +1,4 @@
1
- module FourInfo
1
+ module Txter
2
2
  module Contactable
3
3
 
4
4
  Attributes = [ :sms_phone_number,
@@ -27,14 +27,14 @@ module FourInfo
27
27
  # provide helper methods to access the right value
28
28
  # no matter which column it's stored in.
29
29
  #
30
- # e.g.: @user.four_info_sms_confirmation_code
30
+ # e.g.: @user.txter_sms_confirmation_code
31
31
  # == @user.send(User.sms_confirmation_code_column)
32
32
  model.class_eval "
33
- def four_info_#{attribute}
33
+ def txter_#{attribute}
34
34
  send self.class.#{attribute}_column
35
35
  end
36
- alias_method :four_info_#{attribute}?, :four_info_#{attribute}
37
- def four_info_#{attribute}=(value)
36
+ alias_method :txter_#{attribute}?, :txter_#{attribute}
37
+ def txter_#{attribute}=(value)
38
38
  send self.class.#{attribute}_column.to_s+'=', value
39
39
  end
40
40
  "
@@ -47,7 +47,7 @@ module FourInfo
47
47
  model.before_save :normalize_sms_phone_number
48
48
  model.class_eval do
49
49
  def normalize_sms_phone_number
50
- self.four_info_sms_phone_number = FourInfo.numerize(four_info_sms_phone_number)
50
+ self.txter_sms_phone_number = Txter.numerize(txter_sms_phone_number)
51
51
  end
52
52
  end
53
53
  end
@@ -62,28 +62,44 @@ module FourInfo
62
62
  if msg.to_s.size > 160 && !allow_multiple
63
63
  raise ArgumentError, "SMS Message is too long. Either specify that you want multiple messages or shorten the string."
64
64
  end
65
- return false if msg.to_s.strip.blank? || four_info_sms_blocked?
65
+ return false if msg.to_s.strip.blank? || txter_sms_blocked?
66
66
  return false unless sms_confirmed?
67
67
 
68
68
  # split into pieces that fit as individual messages.
69
69
  msg.to_s.scan(/.{1,160}/m).map do |text|
70
- FourInfo::Request.new.deliver_message(text, four_info_sms_phone_number).success?
70
+ if Txter.deliver(text, txter_sms_phone_number).success?
71
+ text.size
72
+ else
73
+ false
74
+ end
71
75
  end
72
76
  end
73
77
 
74
- # Sends an SMS validation request via xml to the 4info gateway.
75
- # If request succeeds the 4info-generated confirmation code is saved
76
- # in the contactable record.
78
+ # Sends an SMS validation request through the gateway
77
79
  def send_sms_confirmation!
78
- return false if four_info_sms_blocked?
80
+ return false if txter_sms_blocked?
79
81
  return true if sms_confirmed?
80
- return false if four_info_sms_phone_number.blank?
82
+ return false if txter_sms_phone_number.blank?
83
+
84
+ confirmation_code = Txter.generate_confirmation_code
85
+
86
+ # Use this class' confirmation_message method if it
87
+ # exists, otherwise use the generic message
88
+ message = (self.class.respond_to?(:confirmation_message) ?
89
+ self.class :
90
+ Txter).confirmation_message(confirmation_code)
91
+
92
+ if message.to_s.size > 160
93
+ raise ArgumentError, "SMS Confirmation Message is too long. Limit it to 160 characters of unescaped text."
94
+ end
81
95
 
82
- # If we're using a custom short code we'll
83
- # need to create a custom configuration message
84
- FourInfo.configuration.short_code ?
85
- confirm_four_info_sms_with_custom_message :
86
- confirm_four_info_sms_with_default_message
96
+ response = Txter.deliver(message, txter_sms_phone_number)
97
+
98
+ if response.success?
99
+ update_txter_sms_confirmation confirmation_code
100
+ else
101
+ false
102
+ end
87
103
  end
88
104
 
89
105
 
@@ -91,11 +107,11 @@ module FourInfo
91
107
  # If request succeeds, changes the contactable record's
92
108
  # sms_blocked_column to false.
93
109
  def unblock_sms!
94
- return false unless four_info_sms_blocked?
110
+ return false unless txter_sms_blocked?
95
111
 
96
- response = FourInfo::Request.new.unblock(four_info_sms_phone_number)
112
+ response = Txter.unblock(txter_sms_phone_number)
97
113
  if response.success?
98
- self.four_info_sms_blocked = 'false'
114
+ self.txter_sms_blocked = 'false'
99
115
  save
100
116
  else
101
117
  false
@@ -106,9 +122,9 @@ module FourInfo
106
122
  # code. If they match then the current phone number is set
107
123
  # as confirmed by the user.
108
124
  def sms_confirm_with(code)
109
- if four_info_sms_confirmation_code.to_s.downcase == code.downcase
125
+ if txter_sms_confirmation_code.to_s.downcase == code.downcase
110
126
  # save the phone number into the 'confirmed phone number' attribute
111
- self.four_info_sms_confirmed_phone_number = four_info_sms_phone_number
127
+ self.txter_sms_confirmed_phone_number = txter_sms_phone_number
112
128
  save
113
129
  else
114
130
  false
@@ -118,47 +134,16 @@ module FourInfo
118
134
  # Returns true if the current phone number has been confirmed by
119
135
  # the user for recieving TXT messages.
120
136
  def sms_confirmed?
121
- return false if four_info_sms_confirmed_phone_number.blank?
122
- four_info_sms_confirmed_phone_number == four_info_sms_phone_number
137
+ return false if txter_sms_confirmed_phone_number.blank?
138
+ txter_sms_confirmed_phone_number == txter_sms_phone_number
123
139
  end
124
140
 
125
141
  protected
126
- def confirm_four_info_sms_with_custom_message
127
- confirmation_code = FourInfo.generate_confirmation_code
128
-
129
- # Use this class' confirmation_message method if it
130
- # exists, otherwise use the generic message
131
- message = (self.class.respond_to?(:confirmation_message) ?
132
- self.class :
133
- FourInfo).confirmation_message(confirmation_code)
134
-
135
- if message.to_s.size > 160
136
- raise ArgumentError, "SMS Confirmation Message is too long. Limit it to 160 characters of unescaped text."
137
- end
138
-
139
- response = FourInfo::Request.new.deliver_message(message, four_info_sms_phone_number)
140
-
141
- if response.success?
142
- update_four_info_sms_confirmation confirmation_code
143
- else
144
- false
145
- end
146
- end
147
-
148
- def confirm_four_info_sms_with_default_message
149
- response = FourInfo::Request.new.confirm(four_info_sms_phone_number)
150
-
151
- if response.success?
152
- update_four_info_sms_confirmation response.confirmation_code
153
- else
154
- false
155
- end
156
- end
157
142
 
158
- def update_four_info_sms_confirmation(new_code)
159
- self.four_info_sms_confirmation_code = new_code
160
- self.four_info_sms_confirmation_attempted = Time.now.utc
161
- self.four_info_sms_confirmed_phone_number = nil
143
+ def update_txter_sms_confirmation(new_code)
144
+ self.txter_sms_confirmation_code = new_code
145
+ self.txter_sms_confirmation_attempted = Time.now.utc
146
+ self.txter_sms_confirmed_phone_number = nil
162
147
  save
163
148
  end
164
149
  end
@@ -1,4 +1,4 @@
1
- module FourInfo
1
+ module Txter
2
2
  module Controller
3
3
 
4
4
  def self.included(controller)
@@ -25,7 +25,7 @@ module FourInfo
25
25
  def recieve_xml
26
26
 
27
27
  unless defined?(@@contactable_class)
28
- raise RuntimeError, "Please define your user class in the FourInfo controller via the 'sms_contactable' method"
28
+ raise RuntimeError, "Please define your user class in the Txter controller via the 'sms_contactable' method"
29
29
  end
30
30
 
31
31
  request = params[:request]
@@ -33,7 +33,7 @@ module FourInfo
33
33
  case request['type']
34
34
  when 'BLOCK'
35
35
  @contactable = find_contactable(request[:block][:recipient][:id])
36
- @contactable.four_info_sms_blocked = true
36
+ @contactable.txter_sms_blocked = true
37
37
  @contactable.save!
38
38
  when 'MESSAGE'
39
39
  @contactable = find_contactable(request[:message][:sender][:id])
@@ -0,0 +1,45 @@
1
+ module Txter
2
+ class Gateway
3
+ class Request
4
+ end
5
+
6
+ class Response
7
+ def initialize(*args)
8
+ @options = args.last
9
+ end
10
+
11
+ def success?
12
+ :success == @options[:status]
13
+ end
14
+ end
15
+ Success = Txter::Gateway::Response.new(:status => :success)
16
+ Error = Txter::Gateway::Response.new(:status => :error)
17
+
18
+ def self.deliver(*args)
19
+ # subclasses should actually do something here
20
+ Success
21
+ end
22
+
23
+ def self.unblock(*args)
24
+ # subclasses should actually do something here
25
+ Success
26
+ end
27
+
28
+ def self.current
29
+ case Txter.configuration.gateway
30
+ when 'twilio'
31
+ gem 'twiliolib'
32
+ require 'twiliolib'
33
+ GatewayTwilio
34
+ when '4info'
35
+ Gateway4info
36
+ when 'test'
37
+ Txter::Gateway
38
+ else
39
+ raise "You need to specify your Txter gateway!"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ require File.join File.dirname(__FILE__), 'gateway_4info'
45
+ require File.join File.dirname(__FILE__), 'gateway_twilio'
@@ -0,0 +1,114 @@
1
+ module Txter
2
+ class Gateway4info < Txter::Gateway
3
+
4
+ API = 'http://gateway.4info.net/msg'
5
+
6
+ def self.deliver(message, number)
7
+ Gateway4info.perform Request.new.deliver_message(message, number)
8
+ end
9
+
10
+ def self.perform(body)
11
+ if :live == Txter.mode
12
+ require 'net/http'
13
+ uri = URI.parse API
14
+ body = start do |http|
15
+ http.post(
16
+ uri.path,
17
+ body,
18
+ {'Content-Type' => 'text/xml'}
19
+ ).read_body
20
+ end
21
+ Response.new(body)
22
+ else
23
+ Txter.log "Would have sent to 4info.net: #{body}"
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def self.start
30
+ c = Txter.configuration
31
+ net = c.proxy_address ?
32
+ Net::HTTP::Proxy(
33
+ c.proxy_address,
34
+ c.proxy_port,
35
+ c.proxy_username,
36
+ c.proxy_password) :
37
+ Net::HTTP
38
+ uri = URI.parse API
39
+ net.start(uri.host, uri.port) do |http|
40
+ yield http
41
+ end
42
+ end
43
+
44
+ class Response < Txter::Gateway::Response
45
+ def initialize(xml)
46
+ gem 'hpricot'
47
+ require 'hpricot'
48
+ @xml = xml
49
+ @body = Hpricot.parse(xml)
50
+ end
51
+
52
+ def inspect
53
+ @xml.to_s
54
+ end
55
+
56
+ def [](name)
57
+ nodes = (@body/name)
58
+ 1 == nodes.size ? nodes.first : nodes
59
+ end
60
+
61
+ def success?
62
+ 'Success' == self['message'].inner_text
63
+ end
64
+
65
+ def confirmation_code
66
+ self[:confcode].inner_text
67
+ end
68
+ end
69
+
70
+ class Request < Txter::Gateway::Request
71
+
72
+ attr_accessor :number
73
+ attr_accessor :message
74
+ attr_accessor :config
75
+
76
+ def initialize
77
+ unless Txter.configured?
78
+ raise "You need to configure Txter before using it!"
79
+ end
80
+ self.config = Txter.configuration
81
+ end
82
+
83
+ def deliver_message(message, number)
84
+ self.number = Txter.internationalize(number)
85
+ self.message = message
86
+
87
+ template(:deliver).render(self)
88
+ end
89
+
90
+ def confirm(number)
91
+ self.number = Txter.internationalize(number)
92
+
93
+ template(:confirm).render(self)
94
+ end
95
+
96
+ def unblock(number)
97
+ self.number = Txter.internationalize(number)
98
+
99
+ template(:unblock).render(self)
100
+ end
101
+
102
+ protected
103
+
104
+ def template(name)
105
+ # Haml templates for XML
106
+ require 'cgi'
107
+ templates = Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), '4info_templates', '*.haml')))
108
+ file = templates.detect {|t| File.basename(t).chomp('.haml').to_sym == name.to_sym }
109
+ raise ArgumentError, "Missing 4Info template: #{name}" unless file
110
+ Haml::Engine.new(File.read(file))
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,45 @@
1
+ module Txter
2
+ class GatewayTwilio < Txter::Gateway
3
+
4
+ API_VERSION = '2008-08-01'
5
+
6
+ class << self
7
+
8
+ def deliver(message, to, from = nil)
9
+
10
+ from ||= Txter.configuration.default_from_phone_number
11
+ raise "'From' number required for Twilio" unless from
12
+
13
+ response = post 'To' => to,
14
+ 'From' => from,
15
+ 'Body' => message
16
+
17
+ Net::HTTPCreated == response.code_type ?
18
+ Txter::Gateway::Success :
19
+ Txter::Gateway::Error
20
+ end
21
+
22
+ def account
23
+ @account ||= begin
24
+ if Txter.configuration.client_id.blank? ||
25
+ Txter.configuration.client_key.blank?
26
+ raise "Add your Twilio account id (as client_id) and token (as client_key) to the Txter.configure block"
27
+ end
28
+
29
+ Twilio::RestAccount.new(
30
+ Txter.configuration.client_id,
31
+ Txter.configuration.client_key
32
+ )
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def post(data = {})
39
+ account.request "/#{API_VERSION}/Accounts/#{Txter.configuration.client_id}/SMS/Messages",
40
+ "POST",
41
+ data
42
+ end
43
+ end
44
+ end
45
+ end