unipush 0.1.5 → 0.1.6

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.
Files changed (3) hide show
  1. checksums.yaml +15 -0
  2. data/lib/unipush.rb +433 -199
  3. metadata +6 -8
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YmUyZDE1OGVlMDQ2NTZlMTE0MTBiOTFjY2EyNzExYWU1NzdkNWYzMA==
5
+ data.tar.gz: !binary |-
6
+ ZDcxM2QzMTUxYTY5ZmFiODg1YzNlM2UxMDYxNjk2MWQ2NTI4NmEyZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NDg5Mzg5ZTU5Y2QwOWQxYTg3ZGNmYmQyOGZiYjdlMjQxYjZjMDVmMDg2NWU3
10
+ ODU0N2UxMjkwZDU2YTdiMThmYzFhZGEyYTgxOGYwYzMyMGQxZjQ1MjM2ZDUy
11
+ Mzg2MDEyODVmYjk4MjBkYTFlMTUwMzZiYWIzOTE2MzVkYjQ5YjA=
12
+ data.tar.gz: !binary |-
13
+ YjFiYTVjMDRjMDhkYjEzMmU4M2FlNDAzYjBkNTM5OGQxZGQ2ZWMyNzI5YmVh
14
+ Y2Q4OThlZDU3M2ZkMzdkMTM4NzhhYWZmOWM1Nzk5YWM4NTAwMTZhZTY0NDUx
15
+ M2FiNDhjMWZlMzBjOGNkZDc0NDM4NzhkNmE4OTFhNWY1MGVkYzQ=
data/lib/unipush.rb CHANGED
@@ -1,249 +1,483 @@
1
1
  require 'openssl'
2
2
  require 'socket'
3
3
  require 'active_support/core_ext'
4
- class Unipush
5
- attr_accessor :ios_cert_path, :ssl, :socket, :ret # пусть к файлу сертификата
4
+ require 'net/http'
5
+ require 'net/https'
6
+ require 'nokogiri'
6
7
 
8
+ module Unipush
9
+ ## APNS
10
+ class Apns_push
11
+ attr_accessor :ios_cert_path
7
12
 
8
- def initialize(mode='production')
9
- if mode == 'production'
10
- @ios_push_url = 'gateway.push.apple.com'
11
- @ios_feedback_url = 'feedback.push.apple.com'
12
- else
13
- @ios_push_url = 'sandbox.gateway.push.apple.com'
14
- @ios_feedback_url = 'sandbox.feedback.push.apple.com'
15
- end
16
- @ios_push_port = '2195'
17
- @ios_feedback_port = '2196'
18
- @ios_cert_path = ''
13
+ #####################
14
+ # mode = production / development
15
+ # production - use gateway.push.apple.com and feedback.push.apple.com
16
+ # development - use sandbox.gateway.push.apple.com and sandbox.feedback.push.apple.com
17
+ #####################
18
+ def initialize(mode='production')
19
+ if mode == 'production'
20
+ @ios_push_url = 'gateway.push.apple.com'
21
+ @ios_feedback_url = 'feedback.push.apple.com'
22
+ else
23
+ @ios_push_url = 'sandbox.gateway.push.apple.com'
24
+ @ios_feedback_url = 'sandbox.feedback.push.apple.com'
25
+ end
26
+ @ios_push_port = '2195'
27
+ @ios_feedback_port = '2196'
28
+ @ios_cert_path = ''
19
29
 
30
+ @last_error = []
31
+ @unsent_messages = []
20
32
 
21
- @android_auth_url = 'https://www.google.com/accounts/ClientLogin'
22
- @android_push_url = 'https://android.apis.google.com/c2dm/send'
33
+ @sock = nil
34
+ @ssl = nil
23
35
 
24
- @last_error = []
25
- @unsent_messages = []
36
+ @apns_feedback_conn = nil
37
+ end
26
38
 
27
- @sock = nil
28
- @ssl = nil
39
+ ####################
40
+ # return array of errors
41
+ ####################
42
+ def get_last_error
43
+ @last_error.empty? ? false : @last_error
44
+ end
29
45
 
30
- @apns_feedback_conn = nil
31
- end
46
+ ####################
47
+ # array with unsent messages ids
48
+ ####################
49
+ def get_unsent_messages
50
+ @unsent_messages.nil? ? false : @unsent_messages
51
+ end
32
52
 
33
- def get_last_error
34
- @last_error.empty? ? false : @last_error
35
- end
53
+ #################
54
+ # messages = [message1, message2, ...]
55
+ # message=[token, message={:text=>"", :badge=>0, :newsstand=>true, :track=>true, :add=>{:param1=>1, :param2=>2}}]
56
+ ##################
57
+ def send_messages(messages)
58
+ begin
59
+ messages.each_with_index do |m, k|
60
+ mes = prepare_ios_message(m[0], m[1], k)
61
+ if mes
62
+ if apns_check_conn
63
+ cnt = 0
64
+ @ssl.write(mes)
65
+ begin
66
+ cnt += 1
67
+ tmp = @ssl.read_nonblock(8)
68
+ reply = tmp.unpack("CCN")
69
+ @unsent_messages.push(reply[2])
70
+ @last_error.push("Could not send message #{reply[2]} width error: "+reply.join(", "))
71
+ self.apns_close
72
+ break
73
+ rescue IO::WaitReadable
74
+ retry if cnt<1000
75
+ end
76
+ puts k, cnt
77
+ end
78
+ else
79
+ @unsent_messages = [] if @unsent_messages.nil?
80
+ @unsent_messages.push(k)
81
+ end
82
+ end
83
+ rescue
84
+ @last_error.push("Could not send messages. Exception: #{$!.inspect}")
85
+ false
86
+ else
87
+ true
88
+ end
89
+ end
36
90
 
37
- def get_unsent_messages
38
- @unsent_messages.nil? ? false : @unsent_messages
39
- end
40
91
 
41
- def apns_connect
42
- cert_path = @ios_cert_path
43
- if FileTest.exist?(cert_path)
44
- certificate = File.read(cert_path)
45
- context = OpenSSL::SSL::SSLContext.new
46
- context.key = OpenSSL::PKey::RSA.new(certificate)
47
- context.cert = OpenSSL::X509::Certificate.new(certificate)
48
- @sock = TCPSocket.new(@ios_push_url, @ios_push_port)
49
- @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
50
- @ssl.sync = true
51
- @ssl.connect
52
- else
53
- @last_error.push("Certificate file does not exist")
54
- false
92
+ #######################
93
+ # Get unregistered tokens for app
94
+ # Return array(Time:timestamp, String:token)
95
+ #####################
96
+ def get_unregistered_tokens
97
+ cert_path = @ios_cert_path
98
+ if FileTest.exist?(cert_path)
99
+ begin
100
+ certificate = File.read(cert_path)
101
+ context = OpenSSL::SSL::SSLContext.new
102
+ context.key = OpenSSL::PKey::RSA.new(certificate)
103
+ context.cert = OpenSSL::X509::Certificate.new(certificate)
104
+ # получим удаленные токены
105
+ sock = TCPSocket.new("feedback.push.apple.com", 2196)
106
+ ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
107
+ ssl.connect
108
+ apns_feedback = []
109
+ while line = ssl.read(38)
110
+ line.strip!
111
+ f = line.unpack("NnH*")
112
+ apns_feedback << [Time.at(f[0]), f[2]]
113
+ end
114
+ ssl.close
115
+ sock.close
116
+ # и вернем их для удаления
117
+ ret = []
118
+ unless apns_feedback.empty?
119
+ apns_feedback.each do |ff|
120
+ ret.push(ff[1])
121
+ end
122
+ end
123
+ ret
124
+ rescue
125
+ @last_error.push("Could not get tokens. Exception: #{$!.inspect}")
126
+ false
127
+ end
128
+ else
129
+ @last_error.push("Certificate file does not exist")
130
+ false
131
+ end
55
132
  end
56
- end
57
133
 
134
+ private
58
135
 
59
- def apns_check_conn
60
- if @ssl && @sock
61
- unless @ssl.state == "SSLOK "
62
- self.apns_connect
136
+ def apns_connect
137
+ cert_path = @ios_cert_path
138
+ if FileTest.exist?(cert_path)
139
+ certificate = File.read(cert_path)
140
+ context = OpenSSL::SSL::SSLContext.new
141
+ context.key = OpenSSL::PKey::RSA.new(certificate)
142
+ context.cert = OpenSSL::X509::Certificate.new(certificate)
143
+ @sock = TCPSocket.new(@ios_push_url, @ios_push_port)
144
+ @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
145
+ @ssl.sync = true
146
+ @ssl.connect
147
+ else
148
+ @last_error.push("Certificate file does not exist")
149
+ false
63
150
  end
64
- else
65
- self.apns_connect
66
151
  end
67
- @ssl.state == "SSLOK "
68
- end
69
152
 
70
- def apns_close
71
- if @ssl
72
- @ssl.close
73
- @sock.close
74
- end
75
- end
76
153
 
77
- #message={:text=>"", :badge=>0, :newsstand=>true, :track=>true, :add=>{:param1=>1, :param2=>2}}
78
- def prepare_ios_message(token, message, message_id=0)
79
- json = ""
80
- if message[:newsstand]
81
- json = '{"aps":{"content-available":1}}'
82
- token = [token].pack('H*')
83
- elsif message[:text] && message[:text] != ''
84
- badge = message[:badge].nil? ? "0" : message[:badge].to_s
85
- sound = message[:sound].nil? ? "default" : message[:sound].to_s
86
- add_str = ""
87
-
88
- unless message[:add].nil?
89
- add_fields = []
90
- message[:add].each do |k, m|
91
- add_fields.push('"'+k.to_s+'":"'+m.gsub(/['"\\\x0]/,'\\\\\0')+'"')
154
+ def apns_check_conn
155
+ if @ssl && @sock
156
+ unless @ssl.state == "SSLOK "
157
+ apns_connect
92
158
  end
93
- add_str = ","+add_fields.join(",")
159
+ else
160
+ apns_connect
94
161
  end
95
- json = '{"aps":{"alert":"'+message[:text].gsub(/['"\\\x0]/,'\\\\\0')+'","badge":"'+badge+'","sound":"'+sound+'"}'+add_str+'}'
96
- token = [token].pack('H*')
162
+ @ssl.state == "SSLOK "
97
163
  end
98
164
 
99
- if message[:track]
100
- tt = Time.now + 1.day
101
- mes = [1, message_id, tt.to_i, 0, 32, token, 0, json.bytesize, json].pack("cNNcca*cca*")
102
- else
103
- mes = [0, 0, 32, token, 0, json.bytesize, json].pack("ccca*cca*")
165
+ def apns_close
166
+ if @ssl
167
+ @ssl.close
168
+ @sock.close
169
+ end
170
+ end
171
+
172
+ #message={:text=>"", :badge=>0, :newsstand=>true, :track=>true, :add=>{:param1=>1, :param2=>2}}
173
+ def prepare_ios_message(token, message, message_id=0)
174
+ json = ""
175
+ if message[:newsstand]
176
+ json = '{"aps":{"content-available":1}}'
177
+ token = [token].pack('H*')
178
+ elsif message[:text] && message[:text] != ''
179
+ badge = message[:badge].nil? ? "0" : message[:badge].to_s
180
+ sound = message[:sound].nil? ? "default" : message[:sound].to_s
181
+ add_str = ""
182
+
183
+ unless message[:add].nil?
184
+ add_fields = []
185
+ message[:add].each do |k, m|
186
+ add_fields.push('"'+k.to_s+'":"'+m.gsub(/['"\\\x0]/,'\\\\\0')+'"')
187
+ end
188
+ add_str = ","+add_fields.join(",")
189
+ end
190
+ json = '{"aps":{"alert":"'+message[:text].gsub(/['"\\\x0]/,'\\\\\0')+'","badge":"'+badge+'","sound":"'+sound+'"}'+add_str+'}'
191
+ token = [token].pack('H*')
192
+ end
193
+
194
+ if message[:track]
195
+ tt = Time.now + 1.day
196
+ mes = [1, message_id, tt.to_i, 0, 32, token, 0, json.bytesize, json].pack("cNNcca*cca*")
197
+ else
198
+ mes = [0, 0, 32, token, 0, json.bytesize, json].pack("ccca*cca*")
199
+ end
200
+
201
+ mes.nil? ? false : mes
104
202
  end
105
203
 
106
- mes.nil? ? false : mes
107
204
  end
108
205
 
109
- # messages = [message2, message2, ...]
110
- # message=[token, message={:text=>"", :badge=>0, :newsstand=>true, :track=>true, :add=>{:param1=>1, :param2=>2}}]
111
- def send_ios_messages(messages)
112
- begin
113
- messages.each_with_index do |m, k|
114
- mes = prepare_ios_message(m[0], m[1], k)
115
- if mes
116
- if self.apns_check_conn
117
- cnt = 0
118
- @ssl.write(mes)
119
- begin
120
- cnt += 1
121
- tmp = @ssl.read_nonblock(8)
122
- reply = tmp.unpack("CCN")
123
- @unsent_messages.push(reply[2])
124
- @last_error.push("Could not send message #{reply[2]} width error: "+reply.join(", "))
125
- self.apns_close
126
- break
127
- rescue IO::WaitReadable
128
- retry if cnt<1000
206
+
207
+
208
+
209
+ ## Android
210
+ class Android_push
211
+ attr_accessor :ret, # variable for store the response
212
+ :gcm_key, # API key for GCM
213
+ :gcm_url, # GCM URL
214
+ :wp_
215
+
216
+
217
+ #####################
218
+ # mode = production / development
219
+ # production - use gateway.push.apple.com and feedback.push.apple.com
220
+ # development - use sandbox.gateway.push.apple.com and sandbox.feedback.push.apple.com
221
+ #####################
222
+
223
+ def initialize(mode='production')
224
+ if mode == 'production'
225
+ @ios_push_url = 'gateway.push.apple.com'
226
+ @ios_feedback_url = 'feedback.push.apple.com'
227
+ else
228
+ @ios_push_url = 'sandbox.gateway.push.apple.com'
229
+ @ios_feedback_url = 'sandbox.feedback.push.apple.com'
230
+ end
231
+ @ios_push_port = '2195'
232
+ @ios_feedback_port = '2196'
233
+ @ios_cert_path = ''
234
+
235
+
236
+ @gcm_url = 'https://android.googleapis.com/gcm/send'
237
+ @gcm_key = nil?
238
+
239
+ @last_error = []
240
+ @unsent_messages = []
241
+
242
+ @sock = nil
243
+ @ssl = nil
244
+
245
+ @apns_feedback_conn = nil
246
+ end
247
+
248
+ def get_last_error
249
+ @last_error.empty? ? false : @last_error
250
+ end
251
+
252
+ def get_unsent_messages
253
+ @unsent_messages.nil? ? false : @unsent_messages
254
+ end
255
+
256
+
257
+ ##########################
258
+ # message - text message
259
+ # registration_ids - array of devices
260
+ ##########################
261
+ def send_android_messages(message, registration_ids)
262
+ changed_registration_ids = []
263
+ invalid_registration_ids = []
264
+
265
+ if @gcm_key.nil?
266
+ @last_error.push('GCM api key is null')
267
+ false
268
+ else
269
+ if message && registration_ids.size>0
270
+ err_mes = []
271
+ ok_mes = []
272
+
273
+ # сформируем пакеты по 1000 сообщений в каждом
274
+ t_cnt = 0
275
+ data_packets = []
276
+ current_packet = []
277
+
278
+ registration_ids.each do |tt|
279
+ t_cnt+=1 #надо отсчитать 1000 токенов
280
+ current_packet.push(tt)
281
+ if t_cnt == 1000 # отсчитали 1000, запишем пакет и сбросим счетчик
282
+ data_packets.push(current_packet)
283
+ current_packet = []
284
+ t_cnt = 0
129
285
  end
130
- puts k, cnt
286
+ end
287
+ # last packet
288
+ if t_cnt>0
289
+ data_packets.push(current_packet)
290
+ end
291
+
292
+
293
+ # а теперь отправим пакеты сообщений
294
+ data_packets.each_with_index do |dp, p_num|
295
+ if dp.size>0
296
+ data = {'registration_ids'=> dp,
297
+ 'data' => {'message'=>message}
298
+ }
299
+ data = data.to_json
300
+ headers = { "Authorization" => "key=#{@gcm_key}",
301
+ "Content-type" => "application/json"}
302
+ uri = URI.parse(@gcm_url)
303
+ http = Net::HTTP.new(uri.host, uri.port)
304
+ http.use_ssl = true
305
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
306
+
307
+ response = http.post(uri.path, data, headers)
308
+
309
+ if response.code == '200'
310
+ result = JSON.parse(response.body)
311
+ if result['failure']<=0 && result['canonical_ids']<=0 # ошибок нет, все отправилось
312
+ ok_mes.push('Success')
313
+ else
314
+ result['results'].each_with_index do |res, k|
315
+ if res['message_id'] && res['registration_id'] # если сменился ID девайса
316
+ changed_registration_ids.push([dp[k]], res['registration_id'])
317
+ elsif res['error']
318
+ case res['error']
319
+ when 'NotRegistered', 'InvalidRegistration' # удалим токен
320
+ if dp[k].size>0
321
+ invalid_registration_ids.push(dp[k])
322
+ end
323
+ else
324
+ @last_error.push(res['error'])
325
+ end
326
+ end
327
+ end
328
+ end
329
+ else
330
+ @last_error.push("Could not sent packet #{p_num}")
331
+ end
332
+ end
333
+ end
334
+ if @last_error.size > 0
335
+ false
336
+ else
337
+ true
131
338
  end
132
339
  else
133
- @unsent_messages = [] if @unsent_messages.nil?
134
- @unsent_messages.push(k)
340
+ @last_error.push('No data given for sending')
341
+ false
135
342
  end
136
343
  end
137
- rescue
138
- @last_error.push("Could not send messages. Exception: #{$!.inspect}")
139
- false
140
- else
141
- true
142
344
  end
345
+
143
346
  end
144
347
 
145
- def get_unregistered_tokens
146
- cert_path = @ios_cert_path
147
- if FileTest.exist?(cert_path)
148
- begin
149
- certificate = File.read(cert_path)
150
- context = OpenSSL::SSL::SSLContext.new
151
- context.key = OpenSSL::PKey::RSA.new(certificate)
152
- context.cert = OpenSSL::X509::Certificate.new(certificate)
153
- # получим удаленные токены
154
- sock = TCPSocket.new("feedback.push.apple.com", 2196)
155
- ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
156
- ssl.connect
157
- apns_feedback = []
158
- while line = ssl.read(38)
159
- line.strip!
160
- f = line.unpack("NnH*")
161
- apns_feedback << [Time.at(f[0]), f[2]]
162
- end
163
- ssl.close
164
- sock.close
165
- # и вернем их для удаления
166
- ret = []
167
- unless apns_feedback.empty?
168
- apns_feedback.each do |ff|
169
- ret.push(ff[1])
348
+
349
+ # Windows Phone
350
+ class WinPhone_push
351
+ attr_accessor :client_id,
352
+ :client_secret
353
+
354
+ def initialize
355
+ @client_id = nil
356
+ @client_secret = nil
357
+
358
+ @auth_token_url = 'https://login.live.com/accesstoken.srf'
359
+ @access_token = nil
360
+
361
+ @last_error = []
362
+ end
363
+
364
+
365
+ #########
366
+ # send_tile()
367
+ #
368
+ #########
369
+ def send_tile(device_urls, title, bg_image, count, back_title=nil, back_text=nil, back_bg_image=nil)
370
+ if device_urls && title
371
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
372
+ xml['wp'].Notification('xmlns:wp'=>'WPNotification') do
373
+ xml['wp'].Tile{
374
+ xml['wp'].BackgroundImage{
375
+ xml.text bg_image
376
+ }
377
+ xml['wp'].Title{
378
+ xml.text title
379
+ }
380
+ xml['wp'].Count{
381
+ xml.text count.to_s
382
+ }
383
+
384
+ if back_title
385
+ xml['wp'].BackTitle{
386
+ xml.text back_title
387
+ }
388
+ end
389
+
390
+ if back_text
391
+ xml['wp'].BackContent{
392
+ xml.text back_text
393
+ }
394
+ end
395
+
396
+ if back_bg_image
397
+ xml['wp'].BackBackgroundImage{
398
+ xml.text back_bg_image
399
+ }
400
+ end
401
+ }
170
402
  end
171
403
  end
172
- ret
173
- rescue
174
- @last_error.push("Could not get tokens. Exception: #{$!.inspect}")
404
+
405
+ content = builder.to_xml
406
+ puts content
407
+
408
+ headers = {
409
+ 'X-WindowsPhone-Target' => 'token',
410
+ 'Content-Type' => 'text/xml',
411
+ 'X-NotificationClass' => '1',
412
+ 'Content-Length' => content.bytesize.to_s
413
+ }
414
+
415
+ send_message(headers, content, device_urls)
416
+ else
417
+ @last_error.push('no data')
175
418
  false
176
419
  end
177
- else
178
- @last_error.push("Certificate file does not exist")
179
- false
180
420
  end
181
- end
182
421
 
183
422
 
184
- def self.send_android_messages(app_code, messages)
185
- # Данные для доступа к точке отправки
186
- email = 'finansmag.app@gmail.com'
187
- password = 'developer123'
188
- data = "accountType=HOSTED_OR_GOOGLE&Email=#{email}&Passwd=#{password}&service=ac2dm"
189
- headers = { "Content-type" => "application/x-www-form-urlencoded",
190
- "Content-length" => "#{data.length}"}
191
-
192
-
193
- # Получаем токен
194
- uri = URI.parse(AUTH_URL)
195
- http = Net::HTTP.new(uri.host, uri.port)
196
- http.use_ssl = true
197
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
198
- response = http.post(uri.path, data, headers)
199
- @auth_token = response.body.split("\n")[2].gsub("Auth=", "")
200
-
201
- unless @auth_token.empty?
202
- err_mes = []
203
- messages.each do |m|
204
- data = ['registration_id'=> m[2],
205
- 'collapse_key' => 'c2dm',
206
- 'data.message' => m[1]
207
- ]
208
- data = data.map{|k, v| "&#{k}=#{URI.escape(v.to_s)}"}.reduce{|k, v| k + v}
209
- headers = { "Authorization" => "GoogleLogin auth=#{@auth_token}",
210
- "Content-type" => "application/x-www-form-urlencoded",
211
- "Content-length" => "#{data.length}" }
212
- uri = URI.parse(PUSH_URL)
213
- http = Net::HTTP.new(uri.host, uri.port)
214
- http.use_ssl = true
215
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
216
-
217
- result = http.post(uri.path, data, headers)
218
-
219
- if result.code == 200
220
- if result.body.split("\n")[1][0, 5].downcase == "error"
221
- error = result.body.split("\n")[1].gsub("Error=", "")
222
- case error
223
- when 'NotRegistered' #Пользователь удалил приложение
224
- Token.where(:token => m[2]).destroy
225
- PushMessage.find(m[0]).update_attribute(:error, (t 'errors.android.app_deleted'))
226
- when 'MessageTooBig' # слишком длинный текст сообщения
227
- PushMessage.find(m[0]).update_attribute(:error, (t 'errors.android.messagge_too_large'))
228
- when 'QuotaExceeded', 'DeviceQuotaExceeded' # слишком много сообщений, надо подождать
229
- sleep 10
230
- redo #Попробуем еще раз отправить
231
- else
232
- PushMessage.find(m[0]).update_attribute(:error, (t 'errors.android.common_error' + error))
233
- end
234
- err_mes.push(m[0])
423
+ #########
424
+ # send_toast()
425
+ #
426
+ #########
427
+ def send_toast(device_urls, text1, text2=nil)
428
+ if device_urls && text1
429
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
430
+ xml['wp'].Notification('xmlns:wp'=>'WPNotification') do
431
+ xml['wp'].Toast{
432
+ xml['wp'].Text1{
433
+ xml.text text1
434
+ }
435
+ if text2
436
+ xml['wp'].Text2{
437
+ xml.text text2
438
+ }
439
+ end
440
+ }
235
441
  end
236
- else
237
- PushMessage.find(m[0]).update_attribute(:error, 'Error. Could not connect to c2dm server')
238
- return ['error', "Error. Could not connect to c2dm server"]
239
442
  end
443
+
444
+ content = builder.to_xml
445
+
446
+ headers = {
447
+ 'X-WindowsPhone-Target' => 'toast',
448
+ 'Content-Type' => 'text/xml',
449
+ 'X-NotificationClass' => '2',
450
+ 'Content-Length' => content.bytesize.to_s
451
+ }
452
+
453
+ send_message(headers, content, device_urls)
454
+ else
455
+ @last_error.push('no data')
456
+ false
240
457
  end
241
- if err_mes.size > 0
242
- return ['error', "Error sending #{err_mes.size} messages"]
458
+ end
459
+
460
+ private
461
+
462
+ #############
463
+ # send_message(string: type, string: content, array: device_uris)\
464
+ #############
465
+ def send_message(headers, content, device_uris)
466
+ ret = []
467
+ threads = []
468
+ Thread.abort_on_exception = false
469
+ device_uris.each do |du|
470
+ threads << Thread.new do
471
+ uri = URI.parse(du)
472
+ http = Net::HTTP.new(uri.host, uri.port)
473
+
474
+ response = http.post(uri.path, content, headers)
475
+ ret.push(response)
476
+ end
243
477
  end
244
- else
245
- return ['error', "Authorization error. Could not receive token"]
478
+ threads.each {|thr| thr.join}
479
+ ret
246
480
  end
247
- end
248
481
 
482
+ end
249
483
  end
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unipush
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
5
- prerelease:
4
+ version: 0.1.6
6
5
  platform: ruby
7
6
  authors:
8
7
  - Alexey Kirov
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-08-12 00:00:00.000000000 Z
11
+ date: 2013-10-24 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: A simple push notification sender for iOS and Android
13
+ description: A simple push notification sender for iOS, Android and Windows phone
15
14
  email: alexey.s.kirov@gmail.com
16
15
  executables: []
17
16
  extensions: []
@@ -20,26 +19,25 @@ files:
20
19
  - lib/unipush.rb
21
20
  homepage: http://rubygems.org/gems/unipush
22
21
  licenses: []
22
+ metadata: {}
23
23
  post_install_message:
24
24
  rdoc_options: []
25
25
  require_paths:
26
26
  - lib
27
27
  required_ruby_version: !ruby/object:Gem::Requirement
28
- none: false
29
28
  requirements:
30
29
  - - ! '>='
31
30
  - !ruby/object:Gem::Version
32
31
  version: '0'
33
32
  required_rubygems_version: !ruby/object:Gem::Requirement
34
- none: false
35
33
  requirements:
36
34
  - - ! '>='
37
35
  - !ruby/object:Gem::Version
38
36
  version: '0'
39
37
  requirements: []
40
38
  rubyforge_project:
41
- rubygems_version: 1.8.23
39
+ rubygems_version: 2.0.7
42
40
  signing_key:
43
- specification_version: 3
41
+ specification_version: 4
44
42
  summary: Unipush
45
43
  test_files: []