smess 1.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.
@@ -0,0 +1,61 @@
1
+ module Smess
2
+ class Etisalatdemo
3
+ include Smess::Logging
4
+
5
+ def initialize
6
+ @smtp_settings = {
7
+ address: "exmail.emirates.net.ae",
8
+ port: 25,
9
+ domain: 'eim.ae',
10
+ user_name: ENV["SMESS_ETISALATDEMO_USER"],
11
+ password: ENV["SMESS_ETISALATDEMO_PASS"],
12
+ authentication: 'plain',
13
+ enable_starttls_auto: false
14
+ }
15
+ end
16
+
17
+ def deliver_sms(sms)
18
+ return false unless sms.kind_of? Sms
19
+ @sms = sms
20
+
21
+ local_from_var = from_address
22
+ mail = Mail.new do
23
+ from local_from_var
24
+ to "+#{sms.to}@email2sms.ae"
25
+ subject "Smess Message"
26
+ body sms.message.strip_nongsm_chars
27
+ end
28
+
29
+ mail.delivery_method :smtp, @smtp_settings
30
+
31
+ begin
32
+ mail.deliver
33
+ rescue Exception => e
34
+ result = {
35
+ response_code: "-1",
36
+ response: {text: "Email2sms: Delivery Error: #{e.inspect}"},
37
+ data: {
38
+ to: sms.to,
39
+ text: sms.message.strip_nongsm_chars,
40
+ from: from_address
41
+ }
42
+ }
43
+ else
44
+ result = {
45
+ response_code: "0",
46
+ response: {text: "Email2sms: Delivery Successful"},
47
+ data: {
48
+ to: sms.to,
49
+ text: sms.message.strip_nongsm_chars,
50
+ from: from_address
51
+ }
52
+ }
53
+ end
54
+ end
55
+
56
+ def from_address
57
+ "#{@smtp_settings[:user_name]}@#{@smtp_settings[:domain]}"
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,87 @@
1
+ require 'uri'
2
+ require 'httpi'
3
+
4
+ module Smess
5
+ class GlobalMouth
6
+ include Smess::Logging
7
+
8
+
9
+ def compute_hash(values = [])
10
+ hash = "#{username}#{values.join}"
11
+ auth_hash = Digest::MD5.hexdigest "#{username}:#{password}"
12
+ Digest::MD5.hexdigest "#{hash}#{auth_hash}"
13
+ end
14
+
15
+
16
+ def deliver_sms(sms)
17
+ return false unless sms.kind_of? Sms
18
+
19
+ url = "https://mcm.globalmouth.com:8443/api/mcm"
20
+ from = sms.originator || sender_id
21
+ message_id = Digest::MD5.hexdigest "#{Time.now.strftime('%Y%m%d%H%M%S')}#{sms.to}-#{SecureRandom.hex(6)}"
22
+
23
+ params = {
24
+ username: username,
25
+ msisdn: "+#{sms.to}",
26
+ body: sms.message.strip_nongsm_chars.encode("ISO-8859-1"),
27
+ originator: from,
28
+ ref: message_id,
29
+ dlr: "true"
30
+ }
31
+ params[:hash] = compute_hash(
32
+ [sms.message.strip_nongsm_chars.encode("ISO-8859-1"), params[:originator], params[:msisdn]]
33
+ )
34
+ request = HTTPI::Request.new
35
+ request.url = "#{url}?#{params.to_query}"
36
+
37
+ begin
38
+ HTTPI.log_level = :debug
39
+ response = HTTPI.get request
40
+
41
+ rescue Exception => e
42
+ logger.warn response
43
+ # connection problem or some error
44
+ result = {
45
+ response_code: '-1',
46
+ response: {
47
+ temporaryError: 'true',
48
+ responseCode: e.code,
49
+ responseText: e.message
50
+ },
51
+ data: {
52
+ to: sms.to,
53
+ text: sms.message.strip_nongsm_chars,
54
+ from: from
55
+ }
56
+ }
57
+ else
58
+ response_code = response.body.split(/\n/).first
59
+ response_code = "0" if response_code == "200"
60
+ # Successful response
61
+ result = {
62
+ message_id: message_id,
63
+ response_code: response_code.to_s,
64
+ response: {body: response.body},
65
+ destination_address: sms.to,
66
+ data: {
67
+ to: sms.to,
68
+ text: sms.message.strip_nongsm_chars,
69
+ from: from
70
+ }
71
+ }
72
+ end
73
+ result
74
+ end
75
+
76
+ def username
77
+ ENV["SMESS_GLOBAL_MOUTH_USER"].dup # paranoid safeguard
78
+ end
79
+ def password
80
+ ENV["SMESS_GLOBAL_MOUTH_PASS"]
81
+ end
82
+ def sender_id
83
+ ENV["SMESS_GLOBAL_MOUTH_SENDER_ID"]
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,48 @@
1
+ module Smess
2
+ class Iconectiv < Ipxus
3
+
4
+ def build_sms_payload
5
+ # SOAP data
6
+ @sms_options = {
7
+ "correlationId" => Time.now.strftime('%Y%m%d%H%M%S') + @sms.to,
8
+ "originatingAddress" => account[:shortcode],
9
+ "originatorTON" => "0",
10
+ "destinationAddress" => nil,
11
+ "userData" => "",
12
+ "userDataHeader" => "#NULL#",
13
+ "DCS" => "-1",
14
+ "PID" => "-1",
15
+ "relativeValidityTime" => "-1",
16
+ "deliveryTime" => "#NULL#",
17
+ "statusReportFlags" => "1", # 1
18
+ "accountName" => account[:account_name],
19
+ "tariffClass" => "USD0",
20
+ "VAT" => "-1",
21
+ "referenceId" => "#NULL#",
22
+ "serviceName" => account[:service_name],
23
+ "serviceCategory" => "#NULL#",
24
+ "serviceMetaData" => "#NULL#",
25
+ "campaignName" => "#NULL#",
26
+ "username" => account[:username],
27
+ "password" => account[:password]
28
+ }
29
+ end
30
+
31
+ def account
32
+ @account ||= {
33
+ sms_url: 'http://66.70.32.42:32005/api/services2/SmsApi52?wsdl',
34
+ shortcode: ENV["SMESS_ICONECTIV_SHORTCODE"],
35
+ username: ENV["SMESS_ICONECTIV_USER"],
36
+ password: ENV["SMESS_ICONECTIV_PASS"],
37
+ account_name: ENV["SMESS_ICONECTIV_ACCOUNT_NAME"],
38
+ service_name: ENV["SMESS_SERVICE_NAME"],
39
+ service_meta_data_t_mobile: ENV["SMESS_ICONECTIV_SERVICE_META_DATA_T_MOBILE_US"],
40
+ service_meta_data_verizon: ENV["SMESS_ICONECTIV_SERVICE_META_DATA_VERIZON"]
41
+ }
42
+ end
43
+
44
+ def fallback_to_twilio
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,223 @@
1
+ module Smess
2
+ class Ipx
3
+ include Smess::Logging
4
+
5
+ def initialize
6
+ @endpoint = account[:sms_url]
7
+
8
+ @credentials = {
9
+ :name => account[:username],
10
+ :pass => account[:password]
11
+ }
12
+ end
13
+
14
+ def account
15
+ @account ||= {
16
+ sms_url: 'http://europe.ipx.com/api/services2/SmsApi52?wsdl',
17
+ shortcode: ENV["SMESS_IPX_SHORTCODE"],
18
+ username: ENV["SMESS_IPX_USER"],
19
+ password: ENV["SMESS_IPX_PASS"],
20
+ account_name: ENV["SMESS_IPX_ACCOUNT_NAME"],
21
+ service_name: ENV["SMESS_SERVICE_NAME"]
22
+ }
23
+ end
24
+
25
+ def build_sms_payload
26
+ # SOAP data
27
+ @sms_options = {
28
+ "correlationId" => Time.now.strftime('%Y%m%d%H%M%S') + @sms.to,
29
+ "originatingAddress" => account[:shortcode],
30
+ "originatorTON" => "0",
31
+ "destinationAddress" => nil,
32
+ "userData" => "",
33
+ "userDataHeader" => "#NULL#",
34
+ "DCS" => "-1",
35
+ "PID" => "-1",
36
+ "relativeValidityTime" => "-1",
37
+ "deliveryTime" => "#NULL#",
38
+ "statusReportFlags" => "1", # 1
39
+ "accountName" => account[:account_name],
40
+ "tariffClass" => "USD0",
41
+ "VAT" => "-1",
42
+ "referenceId" => "#NULL#",
43
+ "serviceName" => account[:service_name],
44
+ "serviceCategory" => "#NULL#",
45
+ "serviceMetaData" => "#NULL#",
46
+ "campaignName" => "#NULL#",
47
+ "username" => account[:username],
48
+ "password" => account[:password]
49
+ }
50
+ end
51
+
52
+ def deliver_sms(sms)
53
+ return false unless sms.kind_of? Sms
54
+
55
+ @sms = sms
56
+ build_sms_payload
57
+
58
+ set_originator sms.originator
59
+ @sms_options["destinationAddress"] = sms.to
60
+
61
+ perform_operator_adaptation sms.to
62
+
63
+ # validate sms contents
64
+ parts = Smess.split_sms(sms.message.strip_nongsm_chars)
65
+ return false if parts[0].empty?
66
+ # if we have several parts, send them as concatenated sms
67
+ if parts.length > 1
68
+ logger.info "Num Parts: #{parts.length.to_s}"
69
+ # create concat-sms smpp header
70
+ ref_id = Random.new.rand(255).to_s(16).rjust(2,"0")
71
+ num_parts = parts.length
72
+ @sms_options["userDataHeader"] = "050003#{ref_id}#{num_parts.to_s(16).rjust(2,'0')}01" # {050003}{ff}{02}{01} {concat-command}{id to link all parts}{total num parts}{num of current part}
73
+ end
74
+
75
+ @sms_options["userData"] = parts.shift
76
+
77
+ result = send_one_sms
78
+ result[:data] = @sms_options.dup
79
+ result[:data].delete "password"
80
+ result[:data]["userData"] = sms.message.strip_nongsm_chars
81
+
82
+ # fallback...
83
+ unless result[:response_code].to_s == "0"
84
+ logger.info "IPX_ERROR: #{result}"
85
+ return fallback_to_twilio
86
+ end
87
+
88
+ # send aditional parts if we have them
89
+ if parts.length > 0 && result[:response_code] != "-1"
90
+ logger.info "Sending more parts..."
91
+ set_originator sms.originator
92
+ @sms_options["destinationAddress"] = sms.to
93
+
94
+ more_results = []
95
+ parts.each_with_index do |part,i|
96
+ logger.info "Sending Part #{(i+2).to_s}"
97
+ @sms_options["userData"] = part
98
+ @sms_options["userDataHeader"] = "050003#{ref_id}#{num_parts.to_s(16).rjust(2,'0')}#{(i+2).to_s(16).rjust(2,'0')}"
99
+ @sms_options["correlationId"] = Time.now.strftime('%Y%m%d%H%M%S') + @sms.to + (i+1).to_s
100
+ more_results << send_one_sms
101
+ # we don't actually return the status for any of these which is cheating
102
+ end
103
+ end
104
+
105
+ result
106
+ end
107
+
108
+ private
109
+
110
+ def soap_client
111
+ Savon.configure do |config|
112
+ config.log_level = :info
113
+ config.raise_errors = false
114
+ end
115
+
116
+
117
+ endpoint = @endpoint
118
+ mm7ns = wsdl_namespace
119
+ credentials = @credentials
120
+
121
+ client = Savon::Client.new do |wsdl, http|
122
+ wsdl.endpoint = endpoint
123
+ wsdl.namespace = mm7ns
124
+
125
+ http.open_timeout = 15
126
+ http.read_timeout = 60 # Won't set read timeout to 10 minutes!! (IPX are crazy)
127
+ http.auth.basic credentials[:name], credentials[:pass] unless credentials.nil?
128
+ end
129
+ client
130
+ end
131
+
132
+ # Delivery reliability, particularly in the US, is appalling
133
+ # and being able to reduce non-deliveries by more than half
134
+ # is a big deal when sending transactional messages.
135
+ def fallback_to_twilio
136
+ @sms.output = :twilio
137
+ @sms.deliver
138
+ end
139
+
140
+ def get_response_hash_from(response)
141
+ response.to_hash[:submit_rsp]
142
+ end
143
+
144
+ def get_message_id_from hash
145
+ hash[:message_id] rescue ''
146
+ end
147
+
148
+ def set_originator(originator)
149
+ @sms_options["originatingAddress"] = originator
150
+ @sms_options["originatorTON"] = (originator.length == 5 && originator.to_i.to_s == originator) ? "0" : "1"
151
+ end
152
+
153
+ def xmlns
154
+ "http://www.ipx.com/api/services/smsapi52/types"
155
+ end
156
+
157
+ def wsdl_namespace
158
+ "http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-6-MM7-1-2"
159
+ end
160
+
161
+ def send_one_sms
162
+ client = soap_client
163
+ sms_options = @sms_options
164
+ begin
165
+ response = client.request "SendRequest", "xmlns" => xmlns do
166
+ soap.body = sms_options
167
+ end
168
+ rescue Exception => e
169
+ result = {
170
+ :response_code => "-1",
171
+ :response => {
172
+ :temporaryError =>"true",
173
+ :responseCode => "-1",
174
+ :responseText => "MM: System Communication Error. #{e.inspect}"
175
+ }
176
+ }
177
+ # LOG error here?
178
+ return result
179
+ end
180
+ return parse_sms_response response
181
+ end
182
+
183
+
184
+ def parse_sms_response(response)
185
+ # logger.debug " --- "
186
+ # logger.debug response.to_hash
187
+ # logger.debug " --- "
188
+ if response.http_error? || response.soap_fault?
189
+ result = {
190
+ :response_code => "-1",
191
+ :response => {
192
+ :temporaryError =>"true",
193
+ :responseCode => "-1",
194
+ :responseText => response.http_error || response.soap_fault.to_hash
195
+ }
196
+ }
197
+ # LOG error here?
198
+ return result
199
+ end
200
+
201
+
202
+ hash = response.to_hash[:send_response]
203
+ message_id = ""
204
+ message_id = hash[:message_id] if hash.has_key? :message_id
205
+ response_code = hash[:response_code]
206
+
207
+ result = {
208
+ :message_id => message_id,
209
+ :response_code => response_code,
210
+ :response => hash
211
+ }
212
+ end
213
+
214
+
215
+ # Called before final message assembly
216
+ # used to look up the operator and make changes to the SOAP data for some carriers
217
+ def perform_operator_adaptation(msisdn)
218
+ end
219
+
220
+
221
+
222
+ end
223
+ end
@@ -0,0 +1,103 @@
1
+ module Smess
2
+ class Ipxus < Ipx
3
+
4
+ def account
5
+ @account ||= {
6
+ sms_url: 'http://europe.ipx.com/api/services2/SmsApi52?wsdl',
7
+ shortcode: ENV["SMESS_IPX_SHORTCODE"],
8
+ username: ENV["SMESS_IPX_USER"],
9
+ password: ENV["SMESS_IPX_PASS"],
10
+ account_name: ENV["SMESS_IPX_ACCOUNT_NAME"],
11
+ service_name: ENV["SMESS_SERVICE_NAME"],
12
+ service_meta_data_t_mobile_us: ENV["SMESS_IPX_SERVICE_META_DATA_T_MOBILE_US"],
13
+ service_meta_data_verizon: ENV["SMESS_IPX_SERVICE_META_DATA_VERIZON"]
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ def set_originator(originator)
20
+ # Cannot set custom originator in the US
21
+ end
22
+
23
+ # Called before final message assembly
24
+ # used to look up the operator and make changes to the MM7 for Verizon and T-mobile
25
+ def perform_operator_adaptation(msisdn)
26
+ if @mms && @mms.slides.empty?
27
+ @mm7body["mm7:ServiceCode"] << ";contentMetaData=devicediscovery=true"
28
+ return false
29
+ else
30
+ operator_data = lookup_operator msisdn
31
+ unless operator_data[:operator].nil?
32
+ method_name = "adapt_for_#{operator_data[:operator].to_underscore.gsub(" ","_")}"
33
+ send(method_name,msisdn) if respond_to?(:"#{method_name}", true)
34
+ end
35
+ end
36
+ end
37
+
38
+ def lookup_operator(msisdn)
39
+ orig_endpoint = @endpoint
40
+ orig_credentials = @credentials
41
+ @endpoint = "http://europe.ipx.com/api/services/ConsumerLookupApi09"
42
+ @credentials = nil
43
+ client = soap_client
44
+ client.wsdl.namespace = "http://www.ipx.com/api/services/consumerlookupapi09/types"
45
+ body = {
46
+ "correlationId" => Time.now.strftime('%Y%m%d%H%M%S') + msisdn,
47
+ "consumerId" => msisdn,
48
+ "campaignName" => "#NULL#",
49
+ "username" => account[:username],
50
+ "password" => account[:password]
51
+ }
52
+
53
+ begin
54
+ response = client.request "ResolveOperatorRequest", "xmlns"=>"http://www.ipx.com/api/services/consumerlookupapi09/types" do
55
+ soap.body = body
56
+ end
57
+ rescue Exception => e
58
+ result = {
59
+ :response_code => "-1",
60
+ :response => {
61
+ :temporaryError =>"true",
62
+ :responseCode => "-1",
63
+ :responseText => "MM: System Communication Error"
64
+ }
65
+ }
66
+ # LOG error here?
67
+ @endpoint = orig_endpoint
68
+ @credentials = orig_credentials
69
+ return result
70
+ end
71
+ @endpoint = orig_endpoint
72
+ @credentials = orig_credentials
73
+ return parse_operator_response response
74
+ end
75
+
76
+ def parse_operator_response(response)
77
+ if response.http_error? || response.soap_fault?
78
+ result = {
79
+ :response_code => "-1",
80
+ :response => {
81
+ :temporaryError =>"true",
82
+ :responseCode => "-1",
83
+ :responseText => response.http_error || response.soap_fault.to_hash
84
+ }
85
+ }
86
+ # LOG error here?
87
+ return result
88
+ end
89
+
90
+ hash = response.to_hash[:resolve_operator_response]
91
+ end
92
+
93
+
94
+ def adapt_for_verizon(msisdn)
95
+ @sms_options["serviceMetaData"] = account[:service_meta_data_verizon]
96
+ end
97
+
98
+ def adapt_for_t_mobile_us(msisdn)
99
+ @sms_options["serviceMetaData"] = account[:service_meta_data_t_mobile_us]
100
+ end
101
+
102
+ end
103
+ end