smess 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -8
- data/example_config.rb +1 -17
- data/lib/smess/output.rb +10 -2
- data/lib/smess/outputs/http_base.rb +0 -3
- data/lib/smess/outputs/smsglobal.rb +4 -4
- data/lib/smess/outputs/twilio.rb +48 -2
- data/lib/smess/sms.rb +12 -0
- data/lib/smess/version.rb +1 -1
- data/lib/smess.rb +1 -7
- data/smess.gemspec +1 -3
- metadata +7 -39
- data/lib/smess/outputs/iconectiv.rb +0 -13
- data/lib/smess/outputs/ipx.rb +0 -221
- data/lib/smess/outputs/ipxus.rb +0 -69
- data/lib/smess/outputs/mblox.rb +0 -179
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f7417e77e181e70dc6198ad7c2c7fb82616798785a9134fae691967a03e7734
|
4
|
+
data.tar.gz: 382ac7cef4d2cd4e4d4ca52bd9eb0d8d34acef4b4a0dd9da590fef8fceb9cf10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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
|
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.
|
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.
|
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
|
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
|
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
|
@@ -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
|
data/lib/smess/outputs/twilio.rb
CHANGED
@@ -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
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
|
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 '
|
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:
|
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: '
|
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: '
|
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.
|
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
|
data/lib/smess/outputs/ipx.rb
DELETED
@@ -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
|
data/lib/smess/outputs/ipxus.rb
DELETED
@@ -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
|
data/lib/smess/outputs/mblox.rb
DELETED
@@ -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
|