sms-rb 0.0.1

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,24 @@
1
+ # sms-rb
2
+
3
+ (c) Copyright 2010 Pat Nakajima
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
data/bin/sms ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'sms')
4
+ require 'optparse'
5
+
6
+ from = to = nil
7
+ OptionParser.new do |opts|
8
+ opts.banner = 'Usage: sms "some message" [options]'
9
+ opts.separator "Specific options:"
10
+ opts.on('-t', '--to [PHONE NUMBER]', "Recipient's number") { |v| to ||= v }
11
+ opts.on('-f', '--from [PHONE NUMBER]', "Your Twilio number") { |v| from ||= v }
12
+ end.parse!
13
+
14
+ ENV['TWILIO_ID'] || abort("Please set ENV['TWILIO_ID'] to your Twilio API id.")
15
+ ENV['TWILIO_SECRET'] || abort("Please set ENV['TWILIO_SECRET] to your Twilio API secret key.")
16
+
17
+ if from.nil? and ENV['TWILIO_PHONE'].nil?
18
+ abort <<-ERR
19
+ Twilio number blank.
20
+ Please set ENV['TWILIO_PHONE'] or pass the --from option.
21
+ ERR
22
+ end
23
+
24
+ if to.nil?
25
+ abort 'Recipient number blank.'
26
+ end
27
+
28
+ begin
29
+ SMS.text! ARGV.join(' '), :from => from, :to => to
30
+ rescue => e
31
+ abort('Failure: ' + e.message)
32
+ end
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), 'sms', 'twiliolib')
3
+
4
+ module SMS
5
+ TWILIO_SEND = "/2008-08-01/Accounts/#{ENV['TWILIO_ID']}/SMS/Messages"
6
+
7
+ def self.text(message, options={})
8
+ twilio = Twilio::RestAccount.new ENV['TWILIO_ID'], ENV['TWILIO_SECRET']
9
+ res = twilio.request TWILIO_SEND, 'POST',
10
+ 'To' => options[:to],
11
+ 'From' => options[:from] || ENV['TWILIO_PHONE'],
12
+ 'Body' => message
13
+ if res.code == 201
14
+ true
15
+ else
16
+ if options[:raise]
17
+ raise(res.body.match(/<Message>(Message body is required)<\/Message>/)[1])
18
+ else
19
+ false
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.text!(message, options={})
25
+ text(message, options.merge(:raise => true))
26
+ end
27
+ end
File without changes
@@ -0,0 +1,392 @@
1
+ =begin
2
+ Copyright (c) 2009 Twilio, Inc.
3
+
4
+ Permission is hereby granted, free of charge, to any person
5
+ obtaining a copy of this software and associated documentation
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following
11
+ conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ OTHER DEALINGS IN THE SOFTWARE.
24
+ =end
25
+
26
+ # @author Twilio
27
+ module Twilio
28
+ require 'net/http'
29
+ require 'net/https'
30
+ require 'uri'
31
+ require 'cgi'
32
+ require 'rubygems'
33
+ require 'builder'
34
+ require 'openssl'
35
+ require 'base64'
36
+
37
+ TWILIO_API_URL = 'https://api.twilio.com'
38
+
39
+ # Twilio REST Helpers
40
+ class RestAccount
41
+
42
+ #initialize a twilio account object
43
+ #
44
+ #@param [String, String] Your Twilio Acount SID/ID and Auth Token
45
+ #@return [Object] Twilio account object
46
+ def initialize(id, token)
47
+ @id = id
48
+ @token = token
49
+ end
50
+
51
+ #sends a request and gets a response from the Twilio REST API
52
+ #
53
+ #@param [String, String, Hash]
54
+ #path, the URL (relative to the endpoint URL, after the /v1
55
+ #method, the HTTP method to use, defaults to POST
56
+ #vars, for POST or PUT, a dict of data to send
57
+ #
58
+ #@return Twilio response XML
59
+ #@raises [ArgumentError] Invalid path parameter
60
+ #@raises [NotImplementedError] Method given is not implemented
61
+ def request(path, method=nil, vars={})
62
+ if !path || path.length < 1
63
+ raise ArgumentError, 'Invalid path parameter'
64
+ end
65
+ if method && !['GET', 'POST', 'DELETE', 'PUT'].include?(method)
66
+ raise NotImplementedError, 'HTTP %s not implemented' % method
67
+ end
68
+
69
+ if path[0, 1] == '/'
70
+ uri = TWILIO_API_URL + path
71
+ else
72
+ uri = TWILIO_API_URL + '/' + path
73
+ end
74
+
75
+ return fetch(uri, vars, method)
76
+ end
77
+
78
+ #enocde the parameters into a URL friendly string
79
+ #
80
+ #@param [Hash] URL key / values
81
+ #@return [String] Encoded URL
82
+ protected
83
+ def urlencode(params)
84
+ params.to_a.collect! \
85
+ { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&")
86
+ end
87
+
88
+ # Create the uri for the REST call
89
+ #
90
+ #@param [String, Hash] Base URL and URL parameters
91
+ #@return [String] URI for the REST call
92
+ def build_get_uri(uri, params)
93
+ if params && params.length > 0
94
+ if uri.include?('?')
95
+ if uri[-1, 1] != '&'
96
+ uri += '&'
97
+ end
98
+ uri += urlencode(params)
99
+ else
100
+ uri += '?' + urlencode(params)
101
+ end
102
+ end
103
+ return uri
104
+ end
105
+
106
+ # Returns a http request for the given url and parameters
107
+ #
108
+ #@param [String, Hash, String] Base URL, URL parameters, optional METHOD
109
+ #@return [String] URI for the REST call
110
+ def fetch(url, params, method=nil)
111
+ if method && method == 'GET'
112
+ url = build_get_uri(url, params)
113
+ end
114
+ uri = URI.parse(url)
115
+
116
+ http = Net::HTTP.new(uri.host, uri.port)
117
+ http.use_ssl = true
118
+
119
+ if method && method == 'GET'
120
+ req = Net::HTTP::Get.new(uri.request_uri)
121
+ elsif method && method == 'DELETE'
122
+ req = Net::HTTP::Delete.new(uri.request_uri)
123
+ elsif method && method == 'PUT'
124
+ req = Net::HTTP::Put.new(uri.request_uri)
125
+ req.set_form_data(params)
126
+ else
127
+ http.use_ssl = true
128
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
129
+ req = Net::HTTP::Post.new(uri.request_uri)
130
+ req.set_form_data(params)
131
+ end
132
+ req.basic_auth(@id, @token)
133
+
134
+ return http.request(req)
135
+ end
136
+ end
137
+
138
+ # Twiml Response Helpers
139
+ module Verb
140
+ module ClassMethods
141
+ @attributes = []
142
+ @allowed_verbs = []
143
+ attr_accessor :attributes
144
+
145
+ def allowed_verbs(*verbs)
146
+ return @allowed_verbs if verbs == []
147
+ @allowed_verbs = [] if @allowed_verbs.nil?
148
+ verbs.each do |verb|
149
+ @allowed_verbs << verb.to_s.capitalize
150
+ end
151
+ @allowed_verbs = @allowed_verbs.uniq
152
+ end
153
+
154
+ def attributes(*attrs)
155
+ return @attributes if attrs == []
156
+ @attributes = [] if @attributes.nil?
157
+ @attributes = (@attributes + attrs).uniq
158
+ attr_accessor(*@attributes)
159
+ @attributes
160
+ end
161
+ end
162
+
163
+ def attributes
164
+ self.class.attributes
165
+ end
166
+
167
+ #test if a given verb is allowed to be nested
168
+ #
169
+ #@param [Object] Verb to be appended
170
+ #@return [true, false]
171
+ def allowed?(verb)
172
+ self.class.allowed_verbs.nil? ? false : self.class.allowed_verbs.include?(verb.class.name.split('::')[1])
173
+ end
174
+
175
+ #initialize a twilio response object
176
+ #
177
+ #@param [String, Hash] Body of the verb, and a hash of the attributes
178
+ #@return [Object] Twilio Verb object
179
+ #
180
+ #@raises [ArgumentError] Invalid Argument
181
+ def initialize(body = nil, params = {})
182
+ @children = []
183
+ if body.class == String
184
+ @body = body
185
+ else
186
+ @body = nil
187
+ params = body || {}
188
+ end
189
+ params.each do |k,v|
190
+ if !self.class.attributes.nil? && self.class.attributes.include?(k)
191
+ send(k.to_s+"=",v)
192
+ else
193
+ raise ArgumentError, "Attribute Not Supported"
194
+ end
195
+ end
196
+ end
197
+
198
+ #set an attribute key / value
199
+ #no error checking
200
+ #
201
+ #@param [Hash] Hash of options
202
+ #@return void
203
+ def set(params = {})
204
+ params.each do |k,v|
205
+ self.class.attributes k.to_s
206
+ send(k.to_s+"=",v)
207
+ end
208
+ end
209
+
210
+ #output valid Twilio markup
211
+ #
212
+ #@param [Hash] Hash of options
213
+ #@return [String] Twilio Markup (in XML)
214
+ def respond(opts = {})
215
+ opts[:builder] ||= Builder::XmlMarkup.new(:indent => opts[:indent])
216
+ b = opts[:builder]
217
+ attrs = {}
218
+ attributes.each {|a| attrs[a] = send(a) unless send(a).nil? } unless attributes.nil?
219
+
220
+ if @children and @body.nil?
221
+ b.__send__(self.class.to_s.split(/::/)[-1], attrs) do
222
+ @children.each {|e|e.respond( opts.merge(:skip_instruct => true) )}
223
+ end
224
+ elsif @body and @children == []
225
+ b.__send__(self.class.to_s.split(/::/)[-1], @body, attrs)
226
+ else
227
+ raise ArgumentError, "Cannot have children and a body at the same time"
228
+ end
229
+ end
230
+
231
+ #output valid Twilio markup encoded for inclusion in a URL
232
+ #
233
+ #@param []
234
+ #@return [String] URL encoded Twilio Markup (XML)
235
+ def asURL()
236
+ CGI::escape(self.respond)
237
+ end
238
+
239
+ def append(verb)
240
+ if(allowed?(verb))
241
+ @children << verb
242
+ @children[-1]
243
+ else
244
+ raise ArgumentError, "Verb Not Supported"
245
+ end
246
+ end
247
+
248
+ # Verb Convenience Methods
249
+ def addSay(string_to_say = nil, opts = {})
250
+ append Twilio::Say.new(string_to_say, opts)
251
+ end
252
+
253
+ def addPlay(file_to_play = nil, opts = {})
254
+ append Twilio::Play.new(file_to_play, opts)
255
+ end
256
+
257
+ def addGather(opts = {})
258
+ append Twilio::Gather.new(opts)
259
+ end
260
+
261
+ def addRecord(opts = {})
262
+ append Twilio::Record.new(opts)
263
+ end
264
+
265
+ def addDial(number = nil, opts = {})
266
+ append Twilio::Dial.new(number, opts)
267
+ end
268
+
269
+ def addRedirect(url = nil, opts = {})
270
+ append Twilio::Redirect.new(url, opts)
271
+ end
272
+
273
+ def addPause(opts = {})
274
+ append Twilio::Pause.new(opts)
275
+ end
276
+
277
+ def addHangup
278
+ append Twilio::Hangup.new
279
+ end
280
+
281
+ def addNumber(number, opts = {})
282
+ append Twilio::Number.new(number, opts)
283
+ end
284
+
285
+ def addConference(room, opts = {})
286
+ append Twilio::Conference.new(room, opts)
287
+ end
288
+
289
+ def addSms(msg, opts = {})
290
+ append Twilio::Sms.new(msg, opts)
291
+ end
292
+
293
+ end
294
+
295
+ class Say
296
+ extend Twilio::Verb::ClassMethods
297
+ include Twilio::Verb
298
+ attributes :voice, :language, :loop
299
+ end
300
+
301
+ class Play
302
+ extend Twilio::Verb::ClassMethods
303
+ include Twilio::Verb
304
+ attributes :loop
305
+ end
306
+
307
+ class Gather
308
+ extend Twilio::Verb::ClassMethods
309
+ include Twilio::Verb
310
+ attributes :action, :method, :timeout, :finishOnKey, :numDigits
311
+ allowed_verbs :play, :say, :pause
312
+ end
313
+
314
+ class Record
315
+ extend Twilio::Verb::ClassMethods
316
+ include Twilio::Verb
317
+ attributes :action, :method, :timeout, :finishOnKey, :maxLength, :transcribe, :transcribeCallback
318
+ end
319
+
320
+ class Dial
321
+ extend Twilio::Verb::ClassMethods
322
+ include Twilio::Verb
323
+ attributes :action, :method, :timeout, :hangupOnStar, :timeLimit, :callerId
324
+ allowed_verbs :number, :conference
325
+ end
326
+
327
+ class Redirect
328
+ extend Twilio::Verb::ClassMethods
329
+ include Twilio::Verb
330
+ attributes :method
331
+ end
332
+
333
+ class Pause
334
+ extend Twilio::Verb::ClassMethods
335
+ include Twilio::Verb
336
+ attributes :length
337
+ end
338
+
339
+ class Hangup
340
+ extend Twilio::Verb::ClassMethods
341
+ include Twilio::Verb
342
+ end
343
+
344
+ class Number
345
+ extend Twilio::Verb::ClassMethods
346
+ include Twilio::Verb
347
+ attributes :sendDigits, :url
348
+ end
349
+
350
+ class Conference
351
+ extend Twilio::Verb::ClassMethods
352
+ include Twilio::Verb
353
+ attributes :muted, :beep, :startConferenceOnEnter, :endConferenceOnExit, :waitUrl, :waitMethod
354
+ end
355
+
356
+ class Sms
357
+ extend Twilio::Verb::ClassMethods
358
+ include Twilio::Verb
359
+ attributes :to, :from, :statusCallback, :action, :method
360
+ end
361
+
362
+ class Response
363
+ extend Twilio::Verb::ClassMethods
364
+ include Twilio::Verb
365
+ allowed_verbs :say, :play, :gather, :record, :dial, :redirect, :pause, :hangup, :sms
366
+ end
367
+
368
+ # Twilio Utility function and Request Validation class
369
+ class Utils
370
+
371
+ #initialize a twilio utils abject
372
+ #
373
+ #@param [String, String] Your Twilio Acount SID/ID and Auth Token
374
+ #@return [Object] Twilio account object
375
+ def initialize(id, token)
376
+ @id = id
377
+ @token = token
378
+ end
379
+
380
+ def validateRequest(signature, url, params = {})
381
+ sorted_post_params = params.sort
382
+ data = url
383
+ sorted_post_params.each do |pkey|
384
+ data = data + pkey[0]+pkey[1]
385
+ end
386
+ digest = OpenSSL::Digest::Digest.new('sha1')
387
+ expected = Base64.encode64(OpenSSL::HMAC.digest(digest, @token, data)).strip
388
+ return expected == signature
389
+ end
390
+ end
391
+
392
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sms-rb
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Pat Nakajima
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-29 00:00:00 -04:00
19
+ default_executable: sms
20
+ dependencies: []
21
+
22
+ description:
23
+ email: patnakajima@gmail.com
24
+ executables:
25
+ - sms
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - README.md
32
+ - bin/sms
33
+ - lib/sms.rb
34
+ - lib/sms/.gitignore
35
+ - lib/sms/twiliolib.rb
36
+ has_rdoc: true
37
+ homepage:
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Send text messages with Twilio.
70
+ test_files: []
71
+