rumeme 0.1.0

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.
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
+