smess 2.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 939361e7af3aaff1ede9e6f14ffc19eeba98cf70d48f91da5234115e9672ccb9
4
- data.tar.gz: 8ddc196e5cc1c5230f633e69c38cb4e099a1fb0efe093cd6dbfe965e6c3ea9dc
3
+ metadata.gz: 0f7417e77e181e70dc6198ad7c2c7fb82616798785a9134fae691967a03e7734
4
+ data.tar.gz: 382ac7cef4d2cd4e4d4ca52bd9eb0d8d34acef4b4a0dd9da590fef8fceb9cf10
5
5
  SHA512:
6
- metadata.gz: d5f031c724a2966ae661a35a768d5980c7de9122ccdac97f012fda80b93c779b13799b6a167097f99c6cda4cd851776f550bf2c533a3ed9e260a75d14f9922e5
7
- data.tar.gz: e6dca30228c547204c99cf6439e5b8ba280cbe52102de347de023868c2cb880bc94941a95b261919c70b532bd501991a552a8c4cc82d88d4d340ea877e25e972
6
+ metadata.gz: 5e2899a348d8090368337f2df34bf0b027f723a110a9af024f952d07d51fd59267bfd35b7758585301e51eff46d01b41fc8b04418c07daf19f1ff61d700a9662
7
+ data.tar.gz: fbb2a7d9b07ed6fbd787e442ed97cb4e70e2ac514fcc1f3609f9c772ade17ddbccff55911695679993f26ddacf5e0d19c871ca11bd954bfb1ff9ee0d44f6522d
data/README.md CHANGED
@@ -7,14 +7,11 @@ This is a messy SMS messenger supporting every aggregator I have gotten my hands
7
7
 
8
8
  In 2008 I started working on SMS and MMS messaging applications. Within the first 2 months it was apparent that I needed an abstraction that could route messages to different aggregators (messaging providers) to support different countries. No one API supports the entire word in any useful way.
9
9
 
10
- This is that abstraction finally cleaned up (a little) enabling it to be public. Everyone from mBlox to Clickatell. The following 7 aggregators are supported.
10
+ This is that abstraction finally cleaned up (a little) enabling it to be public. Everyone from Twilio to Clickatell. The following aggregators are supported.
11
11
 
12
12
  * Auto - will automatically select the best option for each country. Most often using Global Mouth.
13
13
  * Clickatell - http://www.clickatell.com
14
14
  * Global Mouth - http://www.globalmouth.com
15
- * Iconectiv - http://www.iconectiv.com
16
- * IPX - http://www.ipx.com (who knows what'll happen to them)
17
- * mBlox - http://www.mblox.com
18
15
  * SMS Global - https://www.smsglobal.com
19
16
  * Twilio - http://www.twilio.com
20
17
 
@@ -25,13 +22,13 @@ There is also a _test_ aggregator that you should set evrything to use when runn
25
22
 
26
23
  * You want to send SMS messages from Ruby code.
27
24
  * You want one API to call regardless of which aggregator you switch to.
28
- * You may even want to do it to people in multiple countries using the "best" aggregator for each.
25
+ * You may even want message people in multiple countries using the "best" aggregator for each.
29
26
 
30
27
  ## Why?
31
28
 
32
29
  It may be old and crappy but it has served many millions of SMS messages in production across all continents of the globe. Being in production it has some nice touches that may serve you well.
33
30
 
34
- There is automatic fallback delivery in places where it really makes a difference. Delivery reliability, particularly in the US, is appalling and being able to reduce non-deliveries by more than half is a big deal when sending transactional messages.
31
+ There is automatic fallback delivery in places where it really makes a difference. Delivery reliability is not always good and being able to reduce non-deliveries by more than half is a big deal when sending transactional messages.
35
32
 
36
33
  The aggregator outputs are a very simple plugin system so you can subclass, modify and write your own. The protocol is just one method accepting a single argument.
37
34
 
@@ -119,7 +116,7 @@ There are also convenience methods patched onto String to normalize phone number
119
116
  "\r\n +(070-123)\n45 67\n".msisdn(46)
120
117
  #=> 46701234567
121
118
  ```
122
- US numbers cannot be banged into shape as much as most international numbers can. This is due to area codes not conforming to leading 0 as most other countries do. Tt is actually not terribly simple to see the difference between the US area code 850 and the North Korean country code 850. :)
119
+ US numbers cannot be banged into shape as much as most international numbers can. This is due to area codes not conforming to leading 0 as most other countries do. It is actually not terribly simple to see the difference between the US area code 850 and the North Korean country code 850. :)
123
120
 
124
121
  More usage is pretty clear from the specs.
125
122
 
@@ -129,7 +126,7 @@ Being Swedish, disclaimers are required.
129
126
 
130
127
  * Much of the code is old and crappy. It started in 2008 in PHP, ported to Ruby in 2010.
131
128
  * There are OK specs for the simple stuff. No specs for the more error-prone API calls. There are live tests that can be run to verify end-to-end messaging, though.
132
- * Does not handle the other part of messaging... accepting and processing Delivery Reports. (I have the code... it is just not extracted yet.)
129
+ * Does not handle the other part of messaging... accepting and processing Delivery Reports.
133
130
 
134
131
 
135
132
 
data/example_config.rb CHANGED
@@ -36,25 +36,9 @@ Smess.configure do |config|
36
36
  }
37
37
  })
38
38
 
39
- config.register_output({
40
- name: :iconectiv,
41
- country_codes: ["1"],
42
- type: :iconectiv,
43
- config: {
44
- sms_url: ENV["SMESS_ICONECTIV_URL"],
45
- username: ENV["SMESS_ICONECTIV_USER"],
46
- password: ENV["SMESS_ICONECTIV_PASS"],
47
- shortcode: ENV["SMESS_ICONECTIV_SHORTCODE"],
48
- account_name: ENV["SMESS_ICONECTIV_ACCOUNT_NAME"],
49
- service_name: ENV["SMESS_SERVICE_NAME"],
50
- service_meta_data_verizon: ENV["SMESS_ICONECTIV_SERVICE_META_DATA_VERIZON"],
51
- service_meta_data_t_mobile_us: ENV["SMESS_ICONECTIV_SERVICE_META_DATA_T_MOBILE_US"]
52
- }
53
- })
54
-
55
39
  config.register_output({
56
40
  name: :twilio,
57
- country_codes: ["971"],
41
+ country_codes: ["1", "971"],
58
42
  type: :twilio,
59
43
  config: {
60
44
  sid: ENV["SMESS_TWILIO_SID"],
data/lib/smess/output.rb CHANGED
@@ -11,14 +11,22 @@ module Smess
11
11
 
12
12
  # should be used to make a reasonable validation that the configuration provided is good.
13
13
  def validate_config
14
- raise NotImplementedError.new("You must define validate_config in your Smess output class")
14
+ raise NoMethodError.new("You must define validate_config in your Smess output class")
15
15
  end
16
16
 
17
17
  # entry point to the sms delivery process.
18
18
  def deliver
19
- raise NotImplementedError.new("You must define deliver in your Smess output class")
19
+ raise NoMethodError.new("You must define deliver in your Smess output class")
20
20
  end
21
21
 
22
+ # entry point to the verification process.
23
+ def verify(using: 'none')
24
+ raise NoMethodError.new("Verify API is not supported by this Smess output")
25
+ end
26
+ def check(code)
27
+ raise NoMethodError.new("Verify API is not supported by this Smess output")
28
+ end
29
+
22
30
  def send_feedback(_message_sid)
23
31
  nil
24
32
  end
@@ -42,9 +42,6 @@ module Smess
42
42
  def http_request(method, request)
43
43
  response = HTTPI.send(method, request)
44
44
  normal_result(response)
45
- rescue => e
46
- logger.warn response
47
- result_for_error(e)
48
45
  end
49
46
 
50
47
  def result_for_error(e)
@@ -37,9 +37,9 @@ module Smess
37
37
  end
38
38
 
39
39
  def normal_result(response)
40
- first_response = response.body.split(/\r\n/).first.split(";")
41
- response_code = first_response.first.split(':').last.to_i
42
- message_id = first_response.last.split('SMSGlobalMsgID:').last
40
+ first_response = response.body.split(/\r\n/).first.split(";") rescue nil
41
+ response_code = first_response.first.split(':').last.to_i rescue response.code.to_s
42
+ message_id = first_response.last.split('SMSGlobalMsgID:').last rescue ""
43
43
  # Successful response
44
44
  {
45
45
  message_id: message_id,
@@ -51,4 +51,4 @@ module Smess
51
51
  end
52
52
 
53
53
  end
54
- end
54
+ end
@@ -9,7 +9,7 @@ module Smess
9
9
  @results = []
10
10
  end
11
11
 
12
- attr_accessor :sid, :auth_token, :from, :messaging_service_sid, :callback_url
12
+ attr_accessor :sid, :auth_token, :from, :messaging_service_sid, :callback_url, :verify_service_sid
13
13
 
14
14
  def validate_config
15
15
  @sid = config.fetch(:sid)
@@ -17,6 +17,7 @@ module Smess
17
17
  @from = config.fetch(:from, nil)
18
18
  @messaging_service_sid = config.fetch(:messaging_service_sid, nil)
19
19
  @callback_url = config.fetch(:callback_url)
20
+ @verify_service_sid = config.fetch(:verify_service_sid, nil)
20
21
  end
21
22
 
22
23
  def send_feedback(message_sid)
@@ -27,6 +28,51 @@ module Smess
27
28
  send_one_sms sms.message
28
29
  end
29
30
 
31
+ def verify(using: 'sms')
32
+ response = client.verify.v2
33
+ .services(verify_service_sid)
34
+ .verifications
35
+ .create(to: to, channel: using)
36
+ {
37
+ 'sid' => response.sid,
38
+ 'service_sid' => response.service_sid,
39
+ 'account_sid' => response.account_sid,
40
+ 'to' => response.to,
41
+ 'channel' => response.channel,
42
+ 'status' => response.status,
43
+ 'valid' => response.valid,
44
+ 'lookup' => response.lookup,
45
+ 'amount' => response.amount,
46
+ 'payee' => response.payee,
47
+ 'send_code_attempts' => response.send_code_attempts,
48
+ 'date_created' => response.date_created,
49
+ 'date_updated' => response.date_updated,
50
+ 'sna' => response.sna,
51
+ 'url' => response.url
52
+ }
53
+ end
54
+
55
+ def check(code)
56
+ response = client.verify.v2
57
+ .services(verify_service_sid)
58
+ .verification_checks
59
+ .create(to: to, code: code)
60
+ {
61
+ 'sid' => response.sid,
62
+ 'service_sid' => response.service_sid,
63
+ 'account_sid' => response.account_sid,
64
+ 'to' => response.to,
65
+ 'channel' => response.channel,
66
+ 'status' => response.status,
67
+ 'valid' => response.valid,
68
+ 'amount' => response.amount,
69
+ 'payee' => response.payee,
70
+ 'date_created' => response.date_created,
71
+ 'date_updated' => response.date_updated,
72
+ 'sna_attempts_error_codes' => response.sna_attempts_error_codes,
73
+ }
74
+ end
75
+
30
76
  private
31
77
 
32
78
  attr_accessor :results
@@ -62,7 +108,7 @@ module Smess
62
108
  opts.merge!(sender)
63
109
  response = create_client_message(opts)
64
110
  result = normal_result(response)
65
- rescue => e
111
+ rescue Twilio::REST::RestError => e
66
112
  puts "got exception #{e.inspect}"
67
113
  result = result_for_error(e)
68
114
  end
data/lib/smess/sms.rb CHANGED
@@ -17,6 +17,18 @@ module Smess
17
17
  self.results = {sent_with: output}.merge(out.deliver)
18
18
  end
19
19
 
20
+ def verify(using: 'sms')
21
+ out = Smess.named_output_instance(output)
22
+ out.sms = self
23
+ self.results = {sent_with: output}.merge(out.verify(using: using))
24
+ end
25
+
26
+ def check(code)
27
+ out = Smess.named_output_instance(output)
28
+ out.sms = self
29
+ self.results = {sent_with: output}.merge(out.check(code))
30
+ end
31
+
20
32
  def delivered?
21
33
  results[:response_code] == "0"
22
34
  end
data/lib/smess/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Smess
2
- VERSION = '2.3.0'
2
+ VERSION = '3.0.0'
3
3
  end
data/lib/smess.rb CHANGED
@@ -2,8 +2,6 @@
2
2
  smess_path = File.expand_path('.', File.dirname(__FILE__))
3
3
  $:.unshift(smess_path) if File.directory?(smess_path) && !$:.include?(smess_path)
4
4
 
5
- require 'mail'
6
- require 'savon'
7
5
  require 'active_support'
8
6
  require 'active_support/core_ext'
9
7
 
@@ -14,17 +12,13 @@ require 'smess/utils'
14
12
  require 'smess/sms'
15
13
  require 'smess/outputs/http_base'
16
14
  require 'smess/outputs/auto'
17
- require 'smess/outputs/ipx'
18
- require 'smess/outputs/ipxus'
19
15
  require 'smess/outputs/card_board_fish'
20
16
  require 'smess/outputs/clickatell'
21
17
  require 'smess/outputs/smsglobal'
22
18
  require 'smess/outputs/global_mouth'
23
19
  require 'smess/outputs/link_mobility'
24
- require 'smess/outputs/mblox'
25
20
  require 'smess/outputs/twilio'
26
21
  require 'smess/outputs/twilio_whatsapp'
27
- require 'smess/outputs/iconectiv'
28
22
  require 'smess/outputs/test'
29
23
 
30
24
  require 'string_ext'
@@ -60,7 +54,7 @@ module Smess
60
54
  @nothing = false
61
55
  @default_output = nil
62
56
  @default_sender_id = "Smess"
63
- @output_types = %i{auto card_board_fish clickatell global_mouth link_mobility iconectiv mblox smsglobal twilio twilio_whatsapp}
57
+ @output_types = %i{auto card_board_fish clickatell global_mouth link_mobility smsglobal twilio twilio_whatsapp}
64
58
  @configured_outputs = {}
65
59
  @output_by_country_code = {}
66
60
 
data/smess.gemspec CHANGED
@@ -17,9 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.add_development_dependency 'rspec', '>= 2.4.0'
18
18
  s.add_development_dependency 'simplecov'
19
19
  s.add_development_dependency 'dotenv'
20
- s.add_dependency 'mail', '~> 2.7'
21
- s.add_dependency 'savon', '1.2.0'
22
- s.add_dependency 'httpi', '~> 1.1'
20
+ s.add_dependency 'httpi', '~> 3.0'
23
21
  s.add_dependency 'clickatell', '~> 0'
24
22
  s.add_dependency 'twilio-ruby', '~> 5.26'
25
23
  s.add_dependency 'activesupport', '>= 5.2.6', '< 7.0.0'
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smess
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Westin
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2023-06-15 00:00:00.000000000 Z
@@ -52,48 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: mail
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '2.7'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '2.7'
69
- - !ruby/object:Gem::Dependency
70
- name: savon
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - '='
74
- - !ruby/object:Gem::Version
75
- version: 1.2.0
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - '='
81
- - !ruby/object:Gem::Version
82
- version: 1.2.0
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: httpi
85
57
  requirement: !ruby/object:Gem::Requirement
86
58
  requirements:
87
59
  - - "~>"
88
60
  - !ruby/object:Gem::Version
89
- version: '1.1'
61
+ version: '3.0'
90
62
  type: :runtime
91
63
  prerelease: false
92
64
  version_requirements: !ruby/object:Gem::Requirement
93
65
  requirements:
94
66
  - - "~>"
95
67
  - !ruby/object:Gem::Version
96
- version: '1.1'
68
+ version: '3.0'
97
69
  - !ruby/object:Gem::Dependency
98
70
  name: clickatell
99
71
  requirement: !ruby/object:Gem::Requirement
@@ -161,11 +133,7 @@ files:
161
133
  - lib/smess/outputs/clickatell.rb
162
134
  - lib/smess/outputs/global_mouth.rb
163
135
  - lib/smess/outputs/http_base.rb
164
- - lib/smess/outputs/iconectiv.rb
165
- - lib/smess/outputs/ipx.rb
166
- - lib/smess/outputs/ipxus.rb
167
136
  - lib/smess/outputs/link_mobility.rb
168
- - lib/smess/outputs/mblox.rb
169
137
  - lib/smess/outputs/smsglobal.rb
170
138
  - lib/smess/outputs/test.rb
171
139
  - lib/smess/outputs/twilio.rb
@@ -179,7 +147,7 @@ homepage: https://github.com/eimermusic/smess
179
147
  licenses:
180
148
  - MIT
181
149
  metadata: {}
182
- post_install_message:
150
+ post_install_message:
183
151
  rdoc_options: []
184
152
  require_paths:
185
153
  - lib
@@ -194,8 +162,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
162
  - !ruby/object:Gem::Version
195
163
  version: 1.3.7
196
164
  requirements: []
197
- rubygems_version: 3.1.6
198
- signing_key:
165
+ rubygems_version: 3.2.33
166
+ signing_key:
199
167
  specification_version: 4
200
168
  summary: A messy SMS messenger supporting every aggregator I have gotten my hands
201
169
  on
@@ -1,13 +0,0 @@
1
- module Smess
2
- class Iconectiv < Ipxus
3
-
4
- private
5
-
6
- # iConectiv asserts that this is all that is still reqquired and that it wont break other carriers.
7
- # test of major carriers confirm this too... let's see how the small ones do.
8
- def perform_operator_adaptation(msisdn)
9
- adapt_for_t_mobile_us msisdn
10
- end
11
-
12
- end
13
- end
@@ -1,221 +0,0 @@
1
- module Smess
2
- class Ipx < Output
3
- include Smess::Logging
4
-
5
- attr_accessor :sms_url, :username, :password, :shortcode, :account_name, :service_name, :service_meta_data_verizon, :service_meta_data_t_mobile_us
6
- def validate_config
7
- @sms_url = config.fetch(:sms_url)
8
- @username = config.fetch(:username)
9
- @password = config.fetch(:password)
10
- @shortcode = config.fetch(:shortcode)
11
- @account_name = config.fetch(:account_name)
12
- @service_name = config.fetch(:service_name)
13
- @service_meta_data_verizon = config.fetch(:service_meta_data_verizon, "")
14
- @service_meta_data_t_mobile_us = config.fetch(:service_meta_data_t_mobile_us, "")
15
-
16
- @results = []
17
- @endpoint = sms_url
18
- @credentials = {
19
- name: username,
20
- pass: password
21
- }
22
- end
23
-
24
- def deliver
25
- set_originator(sms.originator)
26
- perform_operator_adaptation(sms.to)
27
-
28
- parts.each_with_index do |part, i|
29
- populate_soap_body(part, i)
30
- results << send_one_sms
31
-
32
- # halt and use fallback on error...
33
- if last_result_was_error
34
- logger.info "IPX_ERROR: #{results.last}"
35
- return fallback_to_twilio || results.first
36
- end
37
- end
38
-
39
- # we don't actually return the status for any of additional messages which is cheating
40
- results.first
41
- end
42
-
43
- private
44
-
45
- attr_reader :sms
46
- attr_accessor :results
47
-
48
- def soap_body
49
- @soap_body ||= {
50
- "correlationId" => Time.now.strftime('%Y%m%d%H%M%S') + sms.to,
51
- "originatingAddress" => shortcode,
52
- "originatorTON" => "0",
53
- "destinationAddress" => sms.to,
54
- "userData" => "",
55
- "userDataHeader" => "#NULL#",
56
- "DCS" => "-1",
57
- "PID" => "-1",
58
- "relativeValidityTime" => "-1",
59
- "deliveryTime" => "#NULL#",
60
- "statusReportFlags" => "1", # 1
61
- "accountName" => account_name,
62
- "tariffClass" => "USD0", # needs to be extracted and variable per country
63
- "VAT" => "-1",
64
- "referenceId" => "#NULL#",
65
- "serviceName" => service_name,
66
- "serviceCategory" => "#NULL#",
67
- "serviceMetaData" => "#NULL#",
68
- "campaignName" => "#NULL#",
69
- "username" => username,
70
- "password" => password
71
- }
72
- end
73
-
74
-
75
- def soap_client
76
- Savon.configure do |config|
77
- config.log_level = :info
78
- config.raise_errors = false
79
- end
80
-
81
- endpoint = @endpoint
82
- mm7ns = wsdl_namespace
83
- credentials = @credentials
84
-
85
- client = Savon::Client.new do |wsdl, http|
86
- wsdl.endpoint = endpoint
87
- wsdl.namespace = mm7ns
88
-
89
- http.open_timeout = 15
90
- http.read_timeout = 60 # Won't set read timeout to 10 minutes!! (IPX are crazy)
91
- http.auth.basic credentials[:name], credentials[:pass] unless credentials.nil?
92
- end
93
- client
94
- end
95
-
96
- # Delivery reliability, particularly in the US, is appalling
97
- # and being able to reduce non-deliveries by more than half
98
- # is a big deal when sending transactional messages.
99
- def fallback_to_twilio
100
- sms.output = :twilio
101
- sms.deliver
102
- end
103
-
104
- def get_response_hash_from(response)
105
- response.to_hash[:submit_rsp]
106
- end
107
-
108
- def get_message_id_from hash
109
- hash[:message_id] rescue ''
110
- end
111
-
112
- def set_originator(originator)
113
- soap_body["originatingAddress"] = originator
114
- soap_body["originatorTON"] = (originator.length == 5 && originator.to_i.to_s == originator) ? "0" : "1"
115
- end
116
-
117
- def xmlns
118
- "http://www.ipx.com/api/services/smsapi52/types"
119
- end
120
-
121
- def wsdl_namespace
122
- "http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-6-MM7-1-2"
123
- end
124
-
125
- def parts
126
- @parts ||= split_parts
127
- end
128
-
129
- def split_parts
130
- Smess.split_sms(sms.message.strip_nongsm_chars).reject {|s| s.empty? }
131
- end
132
-
133
- # {050003}{ff}{02}{01} {concat-command}{id to link all parts}{total num parts}{num of current part}
134
- def concatenation_udh(num, total)
135
- "050003#{ref_id}#{total.to_s(16).rjust(2,'0')}#{(num).to_s(16).rjust(2,'0')}"
136
- end
137
-
138
- def ref_id
139
- @ref_id ||= Random.new.rand(255).to_s(16).rjust(2,"0")
140
- end
141
-
142
- def populate_soap_body(part, i)
143
- # if we have several parts, send them as concatenated sms using UDH codes
144
- soap_body["userDataHeader"] = concatenation_udh(i+1, parts.length) if parts.length > 1
145
- soap_body["userData"] = part
146
- soap_body["correlationId"] = Time.now.strftime('%Y%m%d%H%M%S') + sms.to + (i+1).to_s
147
- end
148
-
149
- def send_one_sms
150
- client = soap_client
151
- soap_body_var = soap_body
152
- begin
153
- response = client.request "SendRequest", "xmlns" => xmlns do
154
- soap.body = soap_body_var
155
- end
156
- result = parse_sms_response(response)
157
- rescue => e
158
- result = result_for_error(e)
159
- # LOG error here?
160
- end
161
- result
162
- end
163
-
164
- def last_result_was_error
165
- results.last.fetch(:response_code, '').to_s != "0"
166
- end
167
-
168
- def parse_sms_response(response)
169
- if response.http_error? || response.soap_fault?
170
- e = Struct.new(:code, :message).new("-1", response.http_error || response.soap_fault.to_hash)
171
- result = result_for_error(e)
172
- else
173
- result = normal_result(response)
174
- end
175
- result
176
- end
177
-
178
- def result_for_error(e)
179
- {
180
- response_code: '-1',
181
- response: {
182
- temporaryError: 'true',
183
- responseCode: '-1',
184
- responseText: e.message
185
- },
186
- data: result_data
187
- }
188
- end
189
-
190
- def normal_result(response)
191
- hash = response.to_hash[:send_response]
192
- message_id = ""
193
- message_id = hash[:message_id] if hash.has_key? :message_id
194
- response_code = hash[:response_code]
195
-
196
- {
197
- message_id: message_id,
198
- response_code: response_code,
199
- response: hash,
200
- destination_address: sms.to,
201
- data: result_data
202
- }
203
- end
204
-
205
- def result_data
206
- data = soap_body.dup
207
- data.delete "password"
208
- data["userData"] = sms.message.strip_nongsm_chars
209
- data
210
- end
211
-
212
-
213
- # Called before final message assembly
214
- # used to look up the operator and make changes to the SOAP data for some carriers
215
- def perform_operator_adaptation(msisdn)
216
- end
217
-
218
-
219
-
220
- end
221
- end
@@ -1,69 +0,0 @@
1
- module Smess
2
- class Ipxus < Ipx
3
-
4
- private
5
-
6
- def set_originator(originator)
7
- # Cannot set custom originator in the US
8
- end
9
-
10
- # Called before final message assembly
11
- # used to look up the operator and make changes to the MM7 for Verizon and T-mobile
12
- def perform_operator_adaptation(msisdn)
13
- operator_data = lookup_operator msisdn
14
- unless operator_data[:operator].nil?
15
- method_name = "adapt_for_#{operator_data[:operator].smess_to_underscore.gsub(" ","_")}"
16
- send(method_name, msisdn) if respond_to?(:"#{method_name}", true)
17
- end
18
- end
19
-
20
- def lookup_operator(msisdn)
21
- orig_endpoint = @endpoint
22
- orig_credentials = @credentials
23
- @endpoint = "http://europe.ipx.com/api/services/ConsumerLookupApi09"
24
- @credentials = nil
25
- client = soap_client
26
- client.wsdl.namespace = "http://www.ipx.com/api/services/consumerlookupapi09/types"
27
- body = {
28
- "correlationId" => Time.now.strftime('%Y%m%d%H%M%S') + msisdn,
29
- "consumerId" => msisdn,
30
- "campaignName" => "#NULL#",
31
- "username" => username,
32
- "password" => password
33
- }
34
-
35
- begin
36
- response = client.request "ResolveOperatorRequest", "xmlns"=>"http://www.ipx.com/api/services/consumerlookupapi09/types" do
37
- soap.body = body
38
- end
39
- result = parse_operator_response(response)
40
- rescue => e
41
- result = result_for_error(e)
42
- ensure
43
- @endpoint = orig_endpoint
44
- @credentials = orig_credentials
45
- end
46
- result
47
- end
48
-
49
- def parse_operator_response(response)
50
- if response.http_error? || response.soap_fault?
51
- e = Struct.new(:code, :message).new("-1", response.http_error || response.soap_fault.to_hash)
52
- result = result_for_error(e)
53
- else
54
- result = response.to_hash[:resolve_operator_response]
55
- end
56
- result
57
- end
58
-
59
-
60
- def adapt_for_verizon(msisdn)
61
- soap_body["serviceMetaData"] = service_meta_data_verizon
62
- end
63
-
64
- def adapt_for_t_mobile_us(msisdn)
65
- soap_body["serviceMetaData"] = service_meta_data_t_mobile_us
66
- end
67
-
68
- end
69
- end
@@ -1,179 +0,0 @@
1
- require 'uri'
2
- require 'httpi'
3
-
4
- module Smess
5
- class Mblox < Output
6
- include Smess::Logging
7
-
8
- def initialize(config)
9
- super
10
- @results = []
11
- end
12
-
13
- def deliver
14
- xml_params = {
15
- subscriber_number: sms.to,
16
- message: ""
17
- }
18
-
19
- parts.each_with_index do |part, i|
20
- xml_params[:message] = part
21
- xml_params[:udh] = concatenation_udh(i+1, parts.length) if parts.length > 1
22
- results << send_one_sms(xml_params)
23
- end
24
-
25
- results.first
26
- end
27
-
28
- attr_accessor :username, :password, :shortcode, :profile_id, :sid
29
- def validate_config
30
- @username = config.fetch(:username)
31
- @password = config.fetch(:password)
32
- @shortcode = config.fetch(:shortcode)
33
- @profile_id = config.fetch(:profile_id)
34
- @sid = config.fetch(:sid)
35
- end
36
-
37
-
38
- def hash_data_for(xml_params)
39
- rand = (SecureRandom.random_number*100000000).to_i
40
- @message_id = rand
41
-
42
- xml_hash = {
43
- notification_request: {
44
- notification_header: {
45
- partner_name: username,
46
- partner_password: password
47
- },
48
- notification_list: {
49
- notification: {
50
- message: xml_params[:message],
51
- profile: profile_id,
52
- udh: xml_params.fetch(:udh,""),
53
- sender_i_d: from,
54
- # expire_date: "",
55
- # operator: "",
56
- # tariff: "",
57
- subscriber: {
58
- subscriber_number: xml_params[:subscriber_number],
59
- session_id: ""
60
- },
61
- # tags: '<Tag Name=”Number”>56</Tag><Tag Name=”City”>Paris</Tag>',
62
- # service_desc: "",
63
- # content_type: "",
64
- service_id: sid,
65
- attributes!: { sender_i_d: { "Type" => "Shortcode" } }
66
- },
67
- attributes!: { notification: { "SequenceNumber" => "1", "MessageType" => "SMS" } } # FlashSMS
68
- },
69
- attributes!: { notification_list: { "BatchID" => @message_id } }
70
- },
71
- attributes!: { notification_request: { "Version" => "3.5" } }
72
- }
73
- xml_hash[:notification_request][:notification_list][:notification].delete :udh unless xml_params.key? :udh
74
- xml_hash
75
- end
76
-
77
- private
78
-
79
- attr_reader :sms
80
- attr_accessor :results
81
-
82
- def from
83
- shortcode
84
- end
85
-
86
- def parts
87
- @parts ||= split_parts
88
- end
89
-
90
- def split_parts
91
- Smess.split_sms(sms.message.strip_nongsm_chars).reject {|s| s.empty? }
92
- end
93
-
94
- def send_one_sms(xml_params)
95
- request.url = 'https://xml4.us.mblox.com:443/send'
96
- request.headers["Content-Type"] = "application/x-www-form-urlencoded"
97
- request.body = http_body(xml_params)
98
-
99
- begin
100
- HTTPI.log_level = :debug
101
- response = HTTPI.post request
102
- result = normal_result(response)
103
- rescue => e
104
- logger.warn response
105
- # connection problem or some error
106
- result = result_for_error(e)
107
- end
108
- result
109
- end
110
-
111
- def http_body(xml_params)
112
- xml = xml_data_for(xml_params)
113
- "XMLDATA="+URI::encode( xml.encode("ISO-8859-1") )
114
- end
115
-
116
- def xml_data_for(xml_params)
117
- Gyoku.convert_symbols_to :camelcase
118
- '<?xml version="1.0"?>'+
119
- Gyoku.xml( hash_data_for(xml_params) )
120
- end
121
-
122
- def concatenation_udh(num, total)
123
- "050003#{ref_id}#{total.to_s(16).rjust(2,'0')}#{(num).to_s(16).rjust(2,'0')}".scan(/../).join(':').prepend(':')
124
- end
125
-
126
- def ref_id
127
- @ref_id ||= Random.new.rand(255).to_s(16).rjust(2,"0")
128
- end
129
-
130
- def normal_result(response)
131
- response_data = Nori.parse(response.body)
132
- response_code = response_code_for response_data
133
- # Successful response
134
- result = {
135
- message_id: @message_id,
136
- response_code: response_code,
137
- response: response_data,
138
- destination_address: sms.to,
139
- data: result_data
140
- }
141
- end
142
-
143
- def response_code_for(response_data)
144
- request_result_code = response_data[:notification_request_result][:notification_result_header][:request_result_code] rescue "-1"
145
- return "request:#{request_result_code}" unless request_result_code == "0"
146
-
147
- notification_result_code = response_data[:notification_request_result][:notification_result_list][:notification_result][:notification_result_code] rescue "-1"
148
- return "notification:#{notification_result_code}" unless notification_result_code == "0"
149
-
150
- subscriber_result_code = response_data[:notification_request_result][:notification_result_list][:notification_result][:subscriber_result][:subscriber_result_code] rescue "-1"
151
- (subscriber_result_code == "0") ? subscriber_result_code : "subscriber:#{subscriber_result_code}"
152
- end
153
-
154
- def request
155
- @request ||= HTTPI::Request.new
156
- end
157
-
158
- def result_for_error(e)
159
- {
160
- response_code: '-1',
161
- response: {
162
- temporaryError: 'true',
163
- responseCode: '-1',
164
- responseText: e.message
165
- },
166
- data: result_data
167
- }
168
- end
169
-
170
- def result_data
171
- {
172
- to: sms.to,
173
- text: sms.message.strip_nongsm_chars,
174
- from: from
175
- }
176
- end
177
-
178
- end
179
- end