rumeme 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ = RuMeMe
2
+ Ruby SDK for Message Media SMS Gateway API.
3
+ Since Message Media doesn't provide any ruby projects support, we decided to port it from available SDKs (mainly from PHP SDK; and some .NET usage).
4
+ For more information checkout {Message Media SMS APIs}[http://www.message-media.com/sms-gateway.html].
5
+
6
+ = To Do
7
+ * Refactoring to make api more rubyish.
8
+ * Add unit tests
9
+ * Extend documentation with Usage section.
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+
4
+ GEM_ROOT = File.dirname(__FILE__).freeze
5
+ VERSION_FILE = File.join(GEM_ROOT, 'lib', 'rumeme', 'version')
6
+
7
+ require VERSION_FILE
8
+
9
+ gemspec = Gem::Specification.new do |s|
10
+ s.name = 'rumeme'
11
+ s.version = Rumeme::VERSION
12
+ s.summary = 'Ruby SDK for Message Media SMS Gateway API'
13
+
14
+ s.files = FileList['[A-Z]*', 'lib/**/*.rb']
15
+ s.require_path = 'lib'
16
+
17
+ s.extra_rdoc_files = ['README.rdoc']
18
+ s.rdoc_options = ['--line-numbers', "--main", "README.rdoc"]
19
+
20
+ s.add_runtime_dependency('nokogiri', '1.4.1')
21
+
22
+ s.authors = ['antlypls']
23
+ s.email = 'antlypls@gmail.com'
24
+ s.homepage = 'http://github.com/programmable/rumeme'
25
+
26
+ s.platform = Gem::Platform::RUBY
27
+ end
28
+
29
+ Rake::GemPackageTask.new gemspec do |pkg|
30
+ pkg.need_tar = true
31
+ pkg.need_zip = true
32
+ end
@@ -0,0 +1,12 @@
1
+ module Rumeme
2
+ class Configuration
3
+ attr_accessor :username
4
+ attr_accessor :password
5
+ attr_accessor :use_message_id
6
+ attr_accessor :secure
7
+ #attr_accessor :proxy_settings
8
+ attr_accessor :allow_splitting
9
+ attr_accessor :allow_long_messages
10
+ end
11
+ end
12
+
@@ -0,0 +1,11 @@
1
+ module Rumeme
2
+ # This class defines constants used to specify a message delivery status.
3
+ class MessageStatus
4
+ NONE = 0
5
+ PENDING = 1
6
+ DELIVERED = 2
7
+ FAILED = 3
8
+ end
9
+ end
10
+
11
+
@@ -0,0 +1,319 @@
1
+ require "net/http"
2
+ require "net/https"
3
+ require 'rubygems'
4
+ require 'nokogiri'
5
+
6
+ module Rumeme
7
+
8
+ # This is the main class used to interface with the M4U SMS messaging server.
9
+ class SmsInterface
10
+ # allow_splitting, allow_long_messages, response_code, response_message, username, password, use_message_id, secure, http_connection, server_list, message_list,
11
+ # http_proxy, http_proxy_port, http_proxy_auth, https_proxy, https_proxy_port, https_proxy_auth, text_buffer,
12
+
13
+ # Constructor.
14
+ #
15
+ # The allowSplitting parameter determines whether messages over
16
+ # 160 characters will be split over multiple SMSes or truncated.
17
+ #
18
+ # The allowLongMessages parameter enables messages longer than 160
19
+ # characters to be sent as special concatenated messages. For this
20
+ # to take effect, the allowSplitting parameter must be set to false.
21
+
22
+ def initialize
23
+ @username = Rumeme.configuration.username
24
+ @password = Rumeme.configuration.password
25
+ @use_message_id = Rumeme.configuration.use_message_id
26
+ @secure = Rumeme.configuration.secure
27
+
28
+ @allow_splitting, @allow_long_messages = Rumeme.configuration.allow_splitting, Rumeme.configuration.allow_long_messages
29
+
30
+ @response_code = -1
31
+ @response_message = nil
32
+ @use_message_id = false
33
+ @message_list = []
34
+ @http_connection = nil
35
+ @http_proxy = nil
36
+ @http_proxy_port = 80
37
+ @http_proxy_auth = nil
38
+ @https_proxy = nil
39
+ @https_proxy_port = 443;
40
+ @https_proxy_auth = nil
41
+ @text_buffer = nil
42
+ @server_list = ["smsmaster.m4u.com.au", "smsmaster1.m4u.com.au", "smsmaster2.m4u.com.au"]
43
+ end
44
+
45
+
46
+ # Set the HTTP proxy server, if one is being used.
47
+ # Also specify an optional proxy username and password.
48
+ # only for php version
49
+ def set_http_proxy proxy, port = 80, username = nil, password = nil
50
+ @http_proxy, @http_proxy_port = proxy, port
51
+ @http_proxy_username, @http_proxy_password = username, password
52
+ @http_proxy_auth = Base64.encode64("#{username}:#{password}").chop unless username.nil? || password.nil?
53
+ raise 'proxy is not supported'
54
+ end
55
+
56
+ # Set the HTTPS proxy server, if one is being used.
57
+ # Also specify an optional proxy username and password.
58
+ # only for php version
59
+ def set_https_proxy proxy, port = 443, username = nil, password = nil
60
+ @https_proxy, @https_proxy_port = proxy, port
61
+ @https_proxy_auth = Base64.encode64("#{username}:#{password}").chop unless username.nil? || password.nil?
62
+ raise 'proxy is not supported'
63
+ end
64
+
65
+ # Return the response code received from calls to
66
+ # changePassword, getCreditsRemaining, sendMessages, and
67
+ # checkReplies.
68
+ attr_reader :response_code
69
+
70
+ # Return the message that was returned with the response code.
71
+ attr_reader :response_message
72
+
73
+ # Add a message to be sent.
74
+ def add_message phone, message_text, message_id = 0, delay = 0, validity_period = 169, delivery_report = false
75
+ p 'in add_message '
76
+ phone = strip_invalid(phone);
77
+ return if phone.empty?
78
+
79
+ if message_text.length <= 160
80
+ @message_list << SmsMessage.new(phone, message_text, message_id, delay, validity_period, delivery_report)
81
+ return
82
+ end
83
+
84
+ if (@allow_long_messages) # Use concatenation.
85
+ @message_list << SmsMessage.new(phone, message_text[0..1071], message_id, delay, validity_period, delivery_report) # 1071??? WTF ??? see php code
86
+ return
87
+ end
88
+
89
+ if !@allow_splitting
90
+ @message_list << SmsMessage.new(phone, message_text[0..160], message_id, delay, validity_period, delivery_report)
91
+ return
92
+ end
93
+
94
+ ml = []
95
+ maxlen = 152;
96
+ while message_text.length > maxlen
97
+ if (pos = message_text[0..maxlen].rindex(" ")) == 0
98
+ pos = maxlen - 1
99
+ end
100
+
101
+ ml << message_text[0..pos+1]
102
+ message_text = message_text[pos + 1 .. -1]
103
+ maxlen = 147;
104
+ end
105
+ ml << message_text
106
+
107
+ ml.each_index {|i|
108
+ ni = i + 1
109
+ if (i == 0)
110
+ m = ml[i]
111
+ else
112
+ m = "(#{ni}/#{ml.size})#{ml[i]}"
113
+ end
114
+
115
+ if (ni != ml.size )
116
+ m << "...(#{ni}/#{ml.size})"
117
+ end
118
+
119
+ @message_list << SmsMessage.new(phone, m, message_id, delay + 30*i, validity_period, delivery_report)
120
+ }
121
+ end
122
+
123
+ # Clear all the messages from the list.
124
+ def clear_messages
125
+ @message_list.clear
126
+ end
127
+
128
+ # Open a connection to the specified server.
129
+ # proxy is not supported
130
+ def open_server_connection server, secure
131
+ p "in open_server_connection: #{server} #{secure}"
132
+ if secure
133
+ @http_connection = Net::HTTP.new(server, 443)
134
+ @http_connection.use_ssl = true
135
+
136
+ else
137
+ @http_connection = Net::HTTP.new(server, 80)
138
+ end
139
+
140
+ p @http_connection.inspect
141
+
142
+ @http_connection.nil? ? false : true
143
+ end
144
+
145
+ # 4 php api compatibility, returns response code from latest http flush
146
+ def read_response_code
147
+ @latest_response_code
148
+ end
149
+
150
+ # Change the password on the local machine and server.
151
+ # not implemented
152
+ def change_password
153
+ raise 'Not Implemented'
154
+ end
155
+
156
+ # Return the list of replies we have received.
157
+ def check_replies auto_confirm = true
158
+ connect
159
+ p 'in check_replies'
160
+ return nil if @http_connection.nil?
161
+ @text_buffer << "CHECKREPLY2.0\r\n.\r\n"
162
+
163
+ if (!flush_buffer || read_response_code != 150)
164
+ close
165
+ return nil
166
+ end
167
+
168
+ p @response_message
169
+
170
+ messages = @response_message.split("\r\n")[1..-2].map{|message_line| SmsReply.parse(message_line, @use_message_id)}
171
+
172
+ close
173
+
174
+ if auto_confirm && messages.size > 0
175
+ confirm_replies_received
176
+ end
177
+
178
+ return messages
179
+ end
180
+
181
+ # sends confirmation to server
182
+ def confirm_replies_received
183
+ connect
184
+ p 'in confirm_replies_received'
185
+ return nil if @http_connection.nil?
186
+ ok = true
187
+
188
+ @text_buffer << "CONFIRM_RECEIVED\r\n.\r\n";
189
+ if !flush_buffer
190
+ ok = false
191
+ end
192
+
193
+ close
194
+ p "result: #{ok}"
195
+
196
+ return ok;
197
+ end
198
+
199
+ # Returns the credits remaining (for prepaid users only).
200
+ def get_credits_remaining
201
+ connect
202
+ return -2 if @http_connection.nil?
203
+ @text_buffer << "MESSAGES\r\n.\r\n"
204
+
205
+ if (!flush_buffer)
206
+ close
207
+ return -2
208
+ end
209
+
210
+ if response_message =~ /^(\d+)\s+OK\s+(\d+).+/
211
+ if $1.to_i != 100
212
+ p 'M4U code is not 100'
213
+ return -1
214
+ end
215
+ return $2.to_i
216
+ else
217
+ p "cant parse response: #{response_message}"
218
+ return -1
219
+ end
220
+ end
221
+
222
+ # Sends all the messages that have been added with the
223
+ # add_message command.
224
+ def send_messages
225
+ connect
226
+ return false if @http_connection.nil?
227
+ @text_buffer << "MESSAGES2.0\r\n"
228
+
229
+ @message_list.each {|sm|
230
+ s = "#{sm.message_id} #{sm.phone_number} #{sm.delay} #{sm.validity_period} "
231
+ s << (sm.delivery_report ? "1 " : "0 ")
232
+ s << "#{sm.message}\r\n";
233
+ @text_buffer << s
234
+ }
235
+
236
+ ok = true
237
+ @text_buffer << ".\r\n";
238
+ if (!flush_buffer || (read_response_code / 100) != 1)
239
+ ok = false;
240
+ end
241
+
242
+ close
243
+ return ok
244
+ end
245
+
246
+ private
247
+
248
+ # Strip invalid characters from the phone number.
249
+ def strip_invalid phone
250
+ "+#{phone.gsub(/[^0-9]/, '')}"
251
+ end
252
+
253
+ # Connect to the M4U server
254
+ def connect
255
+ p 'in connect'
256
+ return unless @http_connection.nil?
257
+
258
+ @server_list.all? {|server| !open_server_connection(server, @secure)} #unusefull code open_server_connection, do not connetct to server, just creates http object, so we can't check availability of the server
259
+
260
+ return if @http_connection.nil?
261
+
262
+ @text_buffer = "m4u\r\nUSER=#{@username}"
263
+ if @use_message_id
264
+ @text_buffer << "#"
265
+ end
266
+ @text_buffer << "\r\nPASSWORD=#{@password}\r\nVER=PHP1.0\r\n";
267
+ end
268
+
269
+ # only 4 php compatibility, just free object reference
270
+ def close
271
+ return if @http_connection.nil?
272
+ #@http_connection.finish
273
+ @http_connection = nil
274
+ end
275
+
276
+ # Flush the text buffer to the HTTP connection.
277
+ def flush_buffer
278
+ p 'in flush_buffer'
279
+ p "buffer: #{@text_buffer}"
280
+ headers = {
281
+ 'Content-Length' => @text_buffer.length.to_s
282
+ }
283
+
284
+ path = '/'
285
+
286
+ begin
287
+ resp, data = @http_connection.post(path, @text_buffer, headers)
288
+ p resp.inspect
289
+ p data.inspect
290
+ rescue
291
+ p "error: #{$!}"
292
+ return false
293
+ end
294
+
295
+
296
+ if resp.code.to_i != 200
297
+ p 'http response code != 200'
298
+ return false
299
+ end
300
+
301
+ @latest_response_code = @response_code = resp.code.to_i
302
+
303
+ doc = Nokogiri::HTML(data)
304
+
305
+ return false if doc.xpath('//title').text != "M4U SMSMASTER"
306
+
307
+ @response_message = @latest_response = doc.xpath('//body').text.strip
308
+ if @response_message =~ /^(\d+)\s+/
309
+ @latest_response_code = @response_code = $1.to_i
310
+ end
311
+
312
+ p "latest response code: #{ @latest_response_code}"
313
+ p "response #{@response_message.inspect}"
314
+
315
+ @text_buffer = ''
316
+ return true
317
+ end
318
+ end
319
+ end
@@ -0,0 +1,12 @@
1
+ module Rumeme
2
+ # This class represents an SMS message.
3
+ class SmsMessage
4
+ attr_reader :phone_number, :message, :message_id, :delay, :validity_period, :delivery_report
5
+
6
+ # Constructor.
7
+ def initialize phone_number, message, message_id, delay, validity_period, delivery_report
8
+ @phone_number, @message, @message_id, @delay, @validity_period, @delivery_report = phone_number, message, message_id, delay, validity_period, delivery_report
9
+ @message = message.gsub("\n",'\n').gsub("\r",'\r').gsub("\\",'\\\\')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,74 @@
1
+ module Rumeme
2
+ # This class represents an SMS reply.
3
+ class SmsReply
4
+ #attr_accessor :phone_number, :message, :message_id, :when, :status
5
+ attr_reader :phone_number, :message, :message_id, :when, :status
6
+
7
+ #Constructor.
8
+ def initialize phone_number, message, message_id, _when, status
9
+ @phone_number, @message, @message_id, @when, @status = phone_number, message, message_id, _when, status
10
+ end
11
+
12
+ # Unescape any escaped characters in the string.
13
+ def self.unescape line
14
+ line.nil? ? nil : line.gsub('\n', "\n").gsub('\r', "\r").gsub('\\\\', "\\")
15
+ end
16
+
17
+ #Parse a reply from a string.
18
+ # Format is: messageID phone when message
19
+ # Or if no message ID: phone when message
20
+ # Or if delivery receipt: messageID messageStatus when
21
+ # php suxx. reimplement using regex
22
+ def self.parse line, use_message_id
23
+ p "parsing line: #{line}. #{use_message_id}."
24
+ message_id = 0;
25
+ status = MessageStatus::NONE
26
+
27
+ prev_idx = 0;
28
+ if (idx = line.index(' ')) == nil
29
+ return nil
30
+ end
31
+
32
+ if (use_message_id)
33
+ if line[0..idx] =~ /\d+/
34
+ message_id = line[0..idx].to_i
35
+ else
36
+ return nil
37
+ end
38
+
39
+ prev_idx = idx + 1
40
+ if (idx = line.index(' ', idx + 1)) == nil
41
+ return nil
42
+ end
43
+ end
44
+
45
+ phone = line[prev_idx .. idx - 1]
46
+
47
+ if phone.length == 1
48
+ status = case phone # why not use to_i ??
49
+ when "1"
50
+ MessageStatus::PENDING
51
+ when "2"
52
+ MessageStatus::DELIVERED
53
+ when "3"
54
+ MessageStatus::FAILED
55
+ else
56
+ nil
57
+ end
58
+ phone = ""
59
+ end
60
+
61
+ prev_idx = idx + 1;
62
+ idx = line.index(' ', idx + 1) || line.length
63
+
64
+ if line[prev_idx .. idx-1] =~ /\d+/
65
+ when_ = $&.to_i
66
+ else
67
+ return nil
68
+ end
69
+
70
+ message = (status != MessageStatus::NONE) || (line.length < idx + 2) ? "" : unescape(line[idx + 1 .. -1])
71
+ return SmsReply.new(phone, message, message_id, when_, status)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,14 @@
1
+ module Rumeme
2
+ # This class defines constants used to specify the validity period
3
+ # of messages injected into the SMS network.
4
+ class ValidityPeriod
5
+ MINIMUM = 0
6
+ ONE_HOUR = 11
7
+ SIX_HOURS= 71
8
+ ONE_DAY = 167
9
+ TWO_DAYS = 168
10
+ THREE_DAYS = 169
11
+ ONE_WEEK = 173
12
+ MAXIMUM = 255
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Rumeme
2
+ VERSION = "0.1.0".freeze
3
+ end
data/lib/rumeme.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "rumeme/configuration"
2
+ require "rumeme/message_status"
3
+ require "rumeme/validity_period"
4
+ require "rumeme/sms_message"
5
+ require "rumeme/sms_reply"
6
+ require "rumeme/sms_interface"
7
+
8
+ module Rumeme
9
+ class << self
10
+ attr_accessor :configuration
11
+
12
+ def configure
13
+ self.configuration ||= Configuration.new
14
+ yield(configuration)
15
+ end
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rumeme
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - antlypls
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-09 00:00:00 +04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: nokogiri
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 1
31
+ version: 1.4.1
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description:
35
+ email: antlypls@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ files:
43
+ - README.rdoc
44
+ - Rakefile
45
+ - lib/rumeme.rb
46
+ - lib/rumeme/sms_interface.rb
47
+ - lib/rumeme/version.rb
48
+ - lib/rumeme/configuration.rb
49
+ - lib/rumeme/sms_message.rb
50
+ - lib/rumeme/message_status.rb
51
+ - lib/rumeme/sms_reply.rb
52
+ - lib/rumeme/validity_period.rb
53
+ has_rdoc: true
54
+ homepage: http://github.com/programmable/rumeme
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --line-numbers
60
+ - --main
61
+ - README.rdoc
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.6
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Ruby SDK for Message Media SMS Gateway API
85
+ test_files: []
86
+