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.
- checksums.yaml +15 -0
- data/lib/unipush.rb +433 -199
- 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
|
-
|
5
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
22
|
-
|
33
|
+
@sock = nil
|
34
|
+
@ssl = nil
|
23
35
|
|
24
|
-
|
25
|
-
|
36
|
+
@apns_feedback_conn = nil
|
37
|
+
end
|
26
38
|
|
27
|
-
|
28
|
-
|
39
|
+
####################
|
40
|
+
# return array of errors
|
41
|
+
####################
|
42
|
+
def get_last_error
|
43
|
+
@last_error.empty? ? false : @last_error
|
44
|
+
end
|
29
45
|
|
30
|
-
|
31
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
159
|
+
else
|
160
|
+
apns_connect
|
94
161
|
end
|
95
|
-
|
96
|
-
token = [token].pack('H*')
|
162
|
+
@ssl.state == "SSLOK "
|
97
163
|
end
|
98
164
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
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
|
-
@
|
134
|
-
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
242
|
-
|
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
|
-
|
245
|
-
|
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
|
-
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-
|
11
|
+
date: 2013-10-24 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
|
-
description: A simple push notification sender for iOS and
|
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:
|
39
|
+
rubygems_version: 2.0.7
|
42
40
|
signing_key:
|
43
|
-
specification_version:
|
41
|
+
specification_version: 4
|
44
42
|
summary: Unipush
|
45
43
|
test_files: []
|