aspsms 0.97

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.
Files changed (5) hide show
  1. data/LICENSE +20 -0
  2. data/README +28 -0
  3. data/bin/aspsms +186 -0
  4. data/lib/aspsms.rb +410 -0
  5. metadata +67 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2005-2011, Daniel Roethlisberger <daniel@roe.ch>
2
+ All rights reserved.
3
+
4
+ Redistribution and use, with or without modification, are permitted
5
+ provided that the following conditions are met:
6
+ 1. Redistributions must retain the above copyright notice, this list of
7
+ conditions and the following disclaimer.
8
+ 2. The name of the author may not be used to endorse or promote products
9
+ derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
13
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
14
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
15
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
16
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
17
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
18
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
19
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
20
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,28 @@
1
+ Ruby ASPSMS -- aspsms.com short message service gateway library and client
2
+ http://www.roe.ch/ASPSMS
3
+
4
+ Copyright (C) 2005-2011, Daniel Roethlisberger <daniel@roe.ch>
5
+ All rights reserved.
6
+
7
+ Ruby ASPSMS is both a ruby library and a command line client for painfree,
8
+ UNIX-style interaction with the aspsms.com short message gateways.
9
+
10
+ Written to conform with the ASPSMS XML Interface Specs 1.91, 2007-12-05.
11
+ - Support for text SMS, show credits, and originator unlocking/testing.
12
+ - Conforms to ASPSMS failure safety recommendations.
13
+ - Delivery status notification is supported by the low-level library
14
+ but not exposed in the easy API nor the command line client.
15
+
16
+ Configuration is read from ~/.aspsms or [/usr/local]/etc/aspsms by default:
17
+
18
+ # ASPSMS configuration
19
+ # mandatory options:
20
+ userkey XYZXYZXYZXYZ
21
+ password y0UrPasSw0rD
22
+ # optional default originator:
23
+ originator +41XXXXXXXXX
24
+ # optional gateway override:
25
+ gateway othergateway:port
26
+
27
+ Make sure to set sensible permissions (0600 is a good start).
28
+
data/bin/aspsms ADDED
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set et ts=2 sw=2 ft=ruby:
3
+ #
4
+ # Ruby ASPSMS -- aspsms.com short message service gateway library and client
5
+ # http://www.roe.ch/ASPSMS
6
+ #
7
+ # Copyright (C) 2005-2011, Daniel Roethlisberger <daniel@roe.ch>
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use, with or without modification, are permitted
11
+ # provided that the following conditions are met:
12
+ # 1. Redistributions must retain the above copyright notice, this list of
13
+ # conditions and the following disclaimer.
14
+ # 2. The name of the author may not be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require 'aspsms'
29
+ require 'getoptlong'
30
+
31
+ class String
32
+ def is_phone_number?
33
+ self.match(/^\+[0-9]{2,20}$/)
34
+ end
35
+ end
36
+
37
+ $bn = File.basename($0)
38
+
39
+ def usage
40
+ puts <<EOF
41
+ Usage:
42
+ #{$bn} [-M] [-v] [-c file] [-o orig] [-t [n]] [-f] [-b] rcpt [...]
43
+ #{$bn} -C [-v] [-c file]
44
+ #{$bn} -O [-v] [-c file] [-o orig]
45
+ #{$bn} -U [-v] [-c file] [-o orig] [-u code]
46
+ Actions:
47
+ -M send text message read from stdin (implied if recipients are given)
48
+ -C retrieve credit balance
49
+ -O check originator authorization
50
+ -U send originator unlock code; unlock if used with -u
51
+ Parameters:
52
+ -o specify originator (phone number or up to 11 characters)
53
+ -t truncate message to n bytes, or 160 if used without argument
54
+ -f/-b flashing/blinking text SMS
55
+ -u specify unlock code
56
+ -c specify config file (default: ~/.aspsms, [/usr/local]/etc/aspsms)
57
+ -v verbose
58
+ -h/-V show help/version and exit
59
+ Recipient phone number format: international format, e.g. +41XXXXXXXXX
60
+ EOF
61
+ end
62
+
63
+ def die(reason, detail = nil)
64
+ STDERR.print "#{$0}: #{reason}"
65
+ STDERR.puts detail.nil? ? '' : " -- #{detail}"
66
+ exit 1
67
+ end
68
+
69
+ actions = []
70
+ truncate = -1
71
+ code = nil
72
+ cf = nil
73
+ verbose = false
74
+ opts = {}
75
+ begin
76
+ GetoptLong.new(
77
+ [ "-M", GetoptLong::NO_ARGUMENT ],
78
+ [ "-C", GetoptLong::NO_ARGUMENT ],
79
+ [ "-O", GetoptLong::NO_ARGUMENT ],
80
+ [ "-U", GetoptLong::NO_ARGUMENT ],
81
+ [ "-o", GetoptLong::REQUIRED_ARGUMENT ],
82
+ [ "-t", GetoptLong::OPTIONAL_ARGUMENT ],
83
+ [ "-f", GetoptLong::NO_ARGUMENT ],
84
+ [ "-b", GetoptLong::NO_ARGUMENT ],
85
+ [ "-u", GetoptLong::REQUIRED_ARGUMENT ],
86
+ [ "-c", GetoptLong::REQUIRED_ARGUMENT ],
87
+ [ "-v", GetoptLong::NO_ARGUMENT ],
88
+ [ "-h", GetoptLong::NO_ARGUMENT ],
89
+ [ "-V", GetoptLong::NO_ARGUMENT ]
90
+ ).each do |opt, arg|
91
+ case opt
92
+ when '-h'
93
+ usage
94
+ exit 0
95
+ when '-V'
96
+ puts "#{$bn}: Ruby ASPSMS"
97
+ puts "Copyright (C) 2005-2011, Daniel Roethlisberger <daniel@roe.ch>"
98
+ puts "http://www.roe.ch/ASPSMS"
99
+ exit 0
100
+ when '-M'
101
+ actions << :textsms
102
+ when '-C'
103
+ actions << :credits
104
+ when '-O'
105
+ actions << :auth
106
+ when '-U'
107
+ actions << :unlock
108
+ when '-o'
109
+ opts[:originator] = arg
110
+ when '-t'
111
+ unless arg.empty? || arg.to_i > 0
112
+ die('invalid option argument', '-t expects positive integer')
113
+ end
114
+ truncate = arg.empty? ? 160 : arg.to_i
115
+ when '-f'
116
+ opts[:flashing] = true
117
+ when '-b'
118
+ opts[:blinking] = true
119
+ when '-u'
120
+ code = arg
121
+ when '-c'
122
+ cf = arg
123
+ when '-v'
124
+ verbose = true
125
+ end
126
+ end
127
+ rescue GetoptLong::MissingArgument, GetoptLong::InvalidOption
128
+ exit 1
129
+ end
130
+ actions << :textsms unless ARGV.empty? || actions.include?(:textsms)
131
+
132
+ if actions.length > 1
133
+ die('incompatible options', '-M, -C, -O and -U mutually exclusive')
134
+ end
135
+ if actions[0] != :textsms && (truncate > 0 ||
136
+ opts.has_key?(:flashing) ||
137
+ opts.has_key?(:blinking))
138
+ die('incompatible options', '-t, -f and -b require -M')
139
+ end
140
+ if actions[0] != :unlock && !code.nil?
141
+ die('incompatible options', '-c requires -U')
142
+ end
143
+ if actions.include?(:credits) && opts.has_key?(:originator)
144
+ die('incompatible options', '-o requires -M, -O or -U')
145
+ end
146
+
147
+ # XXX catch exceptions and print more sensible error messages;
148
+ # add custom exception classes to the ASPSMS:: namespace.
149
+ aspsms = ASPSMS::Easy.new(cf)
150
+ actions.each do |action|
151
+ case action
152
+ when :credits
153
+ credits = aspsms.show_credits
154
+ print credits
155
+ puts verbose ? ' credits' : ''
156
+ exit credits.to_i > 0 ? 0 : 1
157
+ when :auth
158
+ res = aspsms.check_originator_authorization(opts)
159
+ puts res ? 'authorized' : 'not authorized' if verbose
160
+ exit res ? 0 : 1
161
+ when :unlock
162
+ if code.nil?
163
+ res = aspsms.send_originator_unlock_code(opts)
164
+ puts 'sent unlock code' if verbose
165
+ else
166
+ res = aspsms.unlock_originator(code, opts)
167
+ puts 'unlocked originator' if verbose
168
+ end
169
+ exit 0
170
+ when :textsms
171
+ rcpt = ARGV.dup
172
+ rcpt.each do |rcpt|
173
+ unless rcpt.is_phone_number?
174
+ die('invalid phone number', rcpt)
175
+ end
176
+ end
177
+ text = STDIN.read.gsub(/\s+/, ' ').gsub(/^\s+|\s+$/, '')
178
+ text = text[0..(truncate - 1)] if truncate > 0
179
+ credits_used = aspsms.send_text_sms(text, rcpt, opts)
180
+ puts "message delivered (#{credits_used} credits used)" if verbose
181
+ exit 0
182
+ end
183
+ end
184
+ usage
185
+ exit 1
186
+
data/lib/aspsms.rb ADDED
@@ -0,0 +1,410 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set et ts=2 sw=2:
3
+ #
4
+ # Ruby ASPSMS -- aspsms.com short message service gateway library and client
5
+ # http://www.roe.ch/ASPSMS
6
+ #
7
+ # Copyright (C) 2005-2011, Daniel Roethlisberger <daniel@roe.ch>
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use, with or without modification, are permitted
11
+ # provided that the following conditions are met:
12
+ # 1. Redistributions must retain the above copyright notice, this list of
13
+ # conditions and the following disclaimer.
14
+ # 2. The name of the author may not be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require 'rexml/document'
29
+ require 'net/http'
30
+ require 'uri'
31
+ require 'iconv'
32
+
33
+ module ASPSMS
34
+ FILES = [ "#{ENV['HOME']}/.aspsms", '/etc/aspsms', '/usr/local/etc/aspsms' ]
35
+ CHARSET = 'latin1'
36
+
37
+ # Represents a configuration, file based or hash based.
38
+ class Config
39
+ def self.charset_from_environment
40
+ ['LC_ALL', 'LC_CTYPE', 'LANG'].each do |key|
41
+ if ENV.has_key?(key)
42
+ return ENV[key].match(/\./) ? ENV[key].sub(/^.*\./, '') : CHARSET
43
+ end
44
+ end
45
+ return CHARSET
46
+ end
47
+
48
+ @password, @userkey, @originator, @gateway, @charset = nil,nil,nil,nil,nil
49
+
50
+ def initialize(confdata)
51
+ if confdata.kind_of?(Hash)
52
+ set(confdata)
53
+ elsif confdata.kind_of?(String)
54
+ load(confdata)
55
+ else
56
+ autoload
57
+ end
58
+ @charset = ASPSMS::Config.charset_from_environment
59
+ if ENV.has_key?('http_proxy')
60
+ uri = URI.parse(ENV['http_proxy'])
61
+ user, pass = uri.userinfo.split(/:/) if uri.userinfo
62
+ @http_class = Net::HTTP::Proxy(uri.host, uri.port, user, pass)
63
+ else
64
+ @http_class = Net::HTTP
65
+ end
66
+ end
67
+
68
+ def autoload
69
+ FILES.each do |fn|
70
+ begin
71
+ load(fn)
72
+ return
73
+ rescue Errno::ENOENT
74
+ # ignore if not found
75
+ end
76
+ raise "No configuration file found (#{FILES.join(' ')})"
77
+ end
78
+ end
79
+
80
+ def load(fn)
81
+ conf = {}
82
+ File.open(fn).each do |line|
83
+ line.chomp.scan(/^\s*([a-zA-Z0-9]+)\s+([^\s#]+)/) do |k,v|
84
+ case k.downcase
85
+ when 'password'
86
+ conf[:password] = v
87
+ when 'userkey'
88
+ conf[:userkey] = v
89
+ when 'sender' # backwards compat
90
+ conf[:originator] = v
91
+ when 'originator'
92
+ conf[:originator] = v
93
+ when 'gateway'
94
+ conf[:gateway] = v
95
+ end
96
+ end
97
+ end
98
+ set(conf)
99
+ end
100
+
101
+ def set(conf)
102
+ @password = conf[:password] if conf.has_key?(:password)
103
+ @userkey = conf[:userkey] if conf.has_key?(:userkey)
104
+ @originator = conf[:originator] if conf.has_key?(:originator)
105
+ @gateway = conf[:gateway] if conf.has_key?(:gateway)
106
+
107
+ raise "#{fn}: 'userkey' missing!" if @userkey.nil?
108
+ raise "#{fn}: 'password' missing!" if @password.nil?
109
+ if !@gateway.nil? && !@gateway.match(/^[a-zA-Z0-9\[\]:._-]+:[0-9]{1,5}$/)
110
+ raise "#{fn}: 'gateway' not in format 'host:port'!"
111
+ end
112
+ end
113
+
114
+ def userkey
115
+ @userkey
116
+ end
117
+
118
+ def password
119
+ @password
120
+ end
121
+
122
+ def originator
123
+ @originator.nil? ? 'aspsms' : @originator
124
+ end
125
+
126
+ def gateways
127
+ if @gateway.nil?
128
+ return [ 'xml1.aspsms.com:5061', 'xml2.aspsms.com:5098',
129
+ 'xml1.aspsms.com:5098', 'xml2.aspsms.com:5061' ]
130
+ else
131
+ return [ @gateway ]
132
+ end
133
+ end
134
+
135
+ def useragent
136
+ "ASPSMS::Gateway (http://www.roe.ch/ASPSMS)"
137
+ end
138
+
139
+ def http_class
140
+ @http_class
141
+ end
142
+
143
+ def utf8(str)
144
+ unless @charset.match(/utf-?8/i)
145
+ return Iconv::iconv('utf-8', @charset, str)[0]
146
+ else
147
+ return str
148
+ end
149
+ end
150
+ end
151
+
152
+ # Represents a request of specific types.
153
+ # Handles construction of the request XML document.
154
+ class Request
155
+ class Abstract
156
+ include REXML
157
+
158
+ attr_accessor :userkey, :password
159
+
160
+ def initialize(cfg)
161
+ @cfg = cfg
162
+ @userkey = @cfg.userkey
163
+ @password = @cfg.password
164
+ end
165
+
166
+ def to_s
167
+ doc = Document.new
168
+ doc << REXML::XMLDecl.new('1.0', 'ISO-8859-1')
169
+ root = Element.new('aspsms')
170
+ root.elements.add(Element.new('Userkey').
171
+ add_text(@cfg.utf8(userkey)))
172
+ root.elements.add(Element.new('Password').
173
+ add_text(@cfg.utf8(password)))
174
+ each_element do |element|
175
+ root.elements.add(element)
176
+ end
177
+ root.elements.add(Element.new('Action').
178
+ add_text(self.class.name.gsub(/^.*::/, '')))
179
+ doc << root
180
+ doc.to_s
181
+ end
182
+ end
183
+
184
+ class SendTextSMS < Abstract
185
+ attr_accessor :originator, :recipients, :text, :flashing, :blinking,
186
+ :tx_refs, :url_buffered, :url_nondelivery, :url_delivery
187
+ def initialize(cfg)
188
+ super(cfg)
189
+ @originator = @cfg.originator
190
+ @recipients = []
191
+ @text = ''
192
+ @flashing = false
193
+ @blinking = false
194
+ @tx_refs = []
195
+ @url_buffered = ''
196
+ @url_delivery = ''
197
+ @url_nondelivery = ''
198
+ end
199
+ def each_element
200
+ yield REXML::Element.new('Originator').add_text(@cfg.utf8(originator))
201
+ @recipients = [ @recipients ] unless @recipients.kind_of?(Array)
202
+ txr = @tx_refs.dup
203
+ recipients.each do |recipient|
204
+ rcpt = REXML::Element.new('Recipient')
205
+ rcpt.elements.add(Element.new('PhoneNumber').
206
+ add_text(@cfg.utf8(recipient)))
207
+ rcpt.elements.add(Element.new('TransRefNumber').
208
+ add_text(txr.shift)) unless txr.empty?
209
+ yield rcpt
210
+ end
211
+ yield REXML::Element.new('MessageData').add_text(@cfg.utf8(text))
212
+ yield REXML::Element.new('UsedCredits').add_text('1')
213
+ yield REXML::Element.new('FlashingSMS').add_text('1') if flashing
214
+ yield REXML::Element.new('BlinkingSMS').add_text('1') if blinking
215
+ yield REXML::Element.new('URLBufferedMessageNotification').
216
+ add_text(@cfg.utf8(url_buffered)) unless url_buffered.empty?
217
+ yield REXML::Element.new('URLDeliveryNotification').
218
+ add_text(@cfg.utf8(url_delivery)) unless url_delivery.empty?
219
+ yield REXML::Element.new('URLNonDeliveryNotification').
220
+ add_text(@cfg.utf8(url_nondelivery)) unless url_nondelivery.empty?
221
+ end
222
+ end
223
+
224
+ class SendOriginatorUnlockCode < Abstract
225
+ attr_accessor :originator
226
+ def initialize(cfg)
227
+ super(cfg)
228
+ @originator = @cfg.originator
229
+ end
230
+
231
+ def each_element
232
+ yield REXML::Element.new('Originator').add_text(@cfg.utf8(originator))
233
+ end
234
+ end
235
+
236
+ class UnlockOriginator < Abstract
237
+ attr_accessor :originator, :code
238
+ def initialize(cfg)
239
+ super(cfg)
240
+ @originator = @cfg.originator
241
+ @code = ''
242
+ end
243
+
244
+ def each_element
245
+ yield REXML::Element.new('Originator').
246
+ add_text(@cfg.utf8(originator))
247
+ yield REXML::Element.new('OriginatorUnlockCode').
248
+ add_text(@cfg.utf8(code))
249
+ end
250
+ end
251
+
252
+ class CheckOriginatorAuthorization < Abstract
253
+ attr_accessor :originator
254
+ def initialize(cfg)
255
+ super(cfg)
256
+ @originator = @cfg.originator
257
+ end
258
+
259
+ def each_element
260
+ yield REXML::Element.new('Originator').add_text(@cfg.utf8(originator))
261
+ end
262
+ end
263
+
264
+ class ShowCredits < Abstract
265
+ def initialize(cfg)
266
+ super(cfg)
267
+ end
268
+
269
+ def each_element
270
+ # no additional elements
271
+ end
272
+ end
273
+ end
274
+
275
+ # Represents a response to a request of any type.
276
+ # Handles parsing of the response XML document and
277
+ # provides easy access to data fields therein.
278
+ class Response
279
+ def self.parse(cfg, xmlstr)
280
+ args = {}
281
+ doc = REXML::Document.new(xmlstr)
282
+ doc.root.each_element('*') do |element|
283
+ args[element.name] = element.text.chomp
284
+ end
285
+ raise "'ErrorCode' missing!" if args['ErrorCode'].nil?
286
+ raise "'ErrorDescription' missing!" if args['ErrorDescription'].nil?
287
+ Response.new(cfg, args)
288
+ end
289
+
290
+ def initialize(cfg, args)
291
+ @cfg = cfg
292
+ @args = args
293
+ end
294
+ # 1 Ok
295
+ # 30 Originator not Authorized
296
+ # 31 Originator already Authorized
297
+ def success?
298
+ ['1', '30', '31'].include?(errno)
299
+ end
300
+
301
+ def authorized?
302
+ errno == '31'
303
+ end
304
+
305
+ def errno
306
+ @args['ErrorCode']
307
+ end
308
+
309
+ def errdesc
310
+ @args['ErrorDescription']
311
+ end
312
+
313
+ def credits
314
+ @args['Credits']
315
+ end
316
+
317
+ def credits_used
318
+ @args['CreditsUsed']
319
+ end
320
+
321
+ def to_s
322
+ "#{@args['ErrorCode']}: #{@args['ErrorDescription']} #{@args.inspect}"
323
+ end
324
+ end
325
+
326
+ # Handles network communication with the ASPSMS gateways.
327
+ class Gateway
328
+ attr_reader :host, :port
329
+ def self.send(cfg, req)
330
+ cfg.gateways.each do |spec|
331
+ begin
332
+ resp = Gateway.new(cfg, spec).send(req)
333
+ return resp
334
+ rescue
335
+ # continue with next gateway in list
336
+ # XXX collect exception objects and add to raise below
337
+ end
338
+ end
339
+ raise 'Failed to send request to gateways!'
340
+ end
341
+
342
+ def initialize(cfg, gw)
343
+ @cfg = cfg
344
+ if gw.match(/^(.*):([0-9]{1,5})$/)
345
+ @host, @port = $1, $2
346
+ end
347
+ @http = @cfg.http_class.new(host, port)
348
+ end
349
+
350
+ def send(req)
351
+ headers = { 'User-Agent' => @cfg.useragent,
352
+ 'Content-Type' => 'text/xml' }
353
+ resp = @http.post('/xmlsvr.asp', req.to_s, headers)
354
+ ASPSMS::Response.parse(@cfg, resp.body)
355
+ end
356
+ end
357
+
358
+ # Easy straightforward API class; for most use cases,
359
+ # this should be all that is needed.
360
+ class Easy
361
+ def initialize(conf = nil)
362
+ @cfg = ASPSMS::Config.new(conf)
363
+ end
364
+
365
+ def show_credits
366
+ request = ASPSMS::Request::ShowCredits.new(@cfg)
367
+ response = ASPSMS::Gateway.send(@cfg, request)
368
+ raise 'Error status from server!' unless response.success?
369
+ response.credits
370
+ end
371
+
372
+ def send_text_sms(text, recipients, opts = {})
373
+ request = ASPSMS::Request::SendTextSMS.new(@cfg)
374
+ request.text = text
375
+ request.recipients = recipients
376
+ request.originator = opts[:originator] if opts.has_key?(:originator)
377
+ request.flashing = opts[:flashing] if opts.has_key?(:flashing)
378
+ request.blinking = opts[:blinking] if opts.has_key?(:blinking)
379
+ response = ASPSMS::Gateway.send(@cfg, request)
380
+ raise 'Error status from server!' unless response.success?
381
+ response.credits_used
382
+ end
383
+
384
+ def send_originator_unlock_code(opts = {})
385
+ request = ASPSMS::Request::SendOriginatorUnlockCode.new(@cfg)
386
+ request.originator = opts[:originator] if opts.has_key?(:originator)
387
+ response = ASPSMS::Gateway.send(@cfg, request)
388
+ raise 'Error status from server!' unless response.success?
389
+ response
390
+ end
391
+
392
+ def unlock_originator(code, opts = {})
393
+ request = ASPSMS::Request::UnlockOriginator.new(@cfg)
394
+ request.code = code
395
+ request.originator = opts[:originator] if opts.has_key?(:originator)
396
+ response = ASPSMS::Gateway.send(@cfg, request)
397
+ raise 'Error status from server!' unless response.success?
398
+ response
399
+ end
400
+
401
+ def check_originator_authorization(opts = {})
402
+ request = ASPSMS::Request::CheckOriginatorAuthorization.new(@cfg)
403
+ request.originator = opts[:originator] if opts.has_key?(:originator)
404
+ response = ASPSMS::Gateway.send(@cfg, request)
405
+ raise 'Error status from server!' unless response.success?
406
+ response.authorized?
407
+ end
408
+ end
409
+ end
410
+
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aspsms
3
+ version: !ruby/object:Gem::Version
4
+ hash: 201
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 97
9
+ version: "0.97"
10
+ platform: ruby
11
+ authors:
12
+ - Daniel Roethlisberger
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-08-04 00:00:00 Z
18
+ dependencies: []
19
+
20
+ description: aspsms.com SMS gateway library and client
21
+ email: daniel@roe.ch
22
+ executables:
23
+ - aspsms
24
+ extensions: []
25
+
26
+ extra_rdoc_files: []
27
+
28
+ files:
29
+ - README
30
+ - LICENSE
31
+ - lib/aspsms.rb
32
+ - bin/aspsms
33
+ homepage: http://www.roe.ch/ASPSMS
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.7.2
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: aspsms.com SMS gateway library and client
66
+ test_files: []
67
+