aspsms 0.97

Sign up to get free protection for your applications and to get access to all the features.
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
+