sms-rb 0.0.1

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