itpushmeup 0.3.1
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 +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Keychain Access.jpg +0 -0
- data/LICENSE +22 -0
- data/README.md +293 -0
- data/Rakefile +13 -0
- data/itpushmeup.gemspec +34 -0
- data/lib/pushmeup.rb +4 -0
- data/lib/pushmeup/amazon.rb +2 -0
- data/lib/pushmeup/android.rb +2 -0
- data/lib/pushmeup/apns/core.rb +134 -0
- data/lib/pushmeup/apns/notification.rb +50 -0
- data/lib/pushmeup/apple.rb +2 -0
- data/lib/pushmeup/fire/core.rb +103 -0
- data/lib/pushmeup/fire/notification.rb +46 -0
- data/lib/pushmeup/gcm/core.rb +114 -0
- data/lib/pushmeup/gcm/notification.rb +55 -0
- data/lib/pushmeup/version.rb +3 -0
- data/spec/lib/pushmeup_spec.rb +85 -0
- data/spec/spec_helper.rb +1 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b1ce0c87eaa8faa91085fa757f4f389b7fd113e2
|
4
|
+
data.tar.gz: bc673c052f4996014a94e4a2ab5304ba36e6244e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f316682419c126053961b6d08d8fd0d1afe6701fdea05373b5334b311d16e630cb7b01f6e67986cd575b60b7ee96996b679b630885340213522f3a92e28d4aec
|
7
|
+
data.tar.gz: 3d91c9aebbb971faee740875ae2a3fe417fde267d9f56a941794320f9123f4a2c0ccceb3b2294ba8de3684ef5c1d5d8e5f90bef2a68ef855fd79195949b81fa2
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
NEW_README.md
|
19
|
+
.idea/
|
20
|
+
.ruby-*
|
21
|
+
Gemfile.lock
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Keychain Access.jpg
ADDED
Binary file
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Nicos Karalis
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following 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 OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
# Pushmeup
|
2
|
+
|
3
|
+
### a gem for various push notification services.
|
4
|
+
|
5
|
+
## Goals
|
6
|
+
|
7
|
+
Pushmeup is an attempt to create an push notifications center that could send push to devices like:
|
8
|
+
|
9
|
+
- Android
|
10
|
+
- iOS
|
11
|
+
- Mac OS X
|
12
|
+
- Windows Phone
|
13
|
+
- And many others
|
14
|
+
|
15
|
+
Currently we have only support for ``iOS``, ``Android`` and ``Kindle Fire`` but we are planning code for more plataforms.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
$ gem install pushmeup
|
20
|
+
|
21
|
+
or add to your ``Gemfile``
|
22
|
+
|
23
|
+
gem 'pushmeup'
|
24
|
+
|
25
|
+
and install it with
|
26
|
+
|
27
|
+
$ bundle install
|
28
|
+
|
29
|
+
## APNS (Apple iOS)
|
30
|
+
|
31
|
+
### Configure
|
32
|
+
|
33
|
+
1. In Keychain access export your certificate and your private key as a ``p12``.
|
34
|
+
|
35
|
+

|
36
|
+
|
37
|
+
2. Run the following command to convert the ``p12`` to a ``pem`` file
|
38
|
+
|
39
|
+
$ openssl pkcs12 -in cert.p12 -out cert.pem -nodes -clcerts
|
40
|
+
|
41
|
+
3. After you have created your ``pem`` file. Set the host, port and certificate file location on the APNS class. You just need to set this once:
|
42
|
+
|
43
|
+
APNS.host = 'gateway.push.apple.com'
|
44
|
+
# gateway.sandbox.push.apple.com is default and only for development
|
45
|
+
# gateway.push.apple.com is only for production
|
46
|
+
|
47
|
+
APNS.port = 2195
|
48
|
+
# this is also the default. Shouldn't ever have to set this, but just in case Apple goes crazy, you can.
|
49
|
+
|
50
|
+
APNS.pem = '/path/to/pem/file'
|
51
|
+
# this is the file you just created
|
52
|
+
|
53
|
+
APNS.pass = ''
|
54
|
+
# Just in case your pem need a password
|
55
|
+
|
56
|
+
### Usage
|
57
|
+
|
58
|
+
#### Sending a single notification:
|
59
|
+
|
60
|
+
device_token = '123abc456def'
|
61
|
+
APNS.send_notification(device_token, 'Hello iPhone!' )
|
62
|
+
APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
|
63
|
+
|
64
|
+
#### Sending multiple notifications
|
65
|
+
|
66
|
+
device_token = '123abc456def'
|
67
|
+
n1 = APNS::Notification.new(device_token, 'Hello iPhone!' )
|
68
|
+
n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
|
69
|
+
APNS.send_notifications([n1, n2])
|
70
|
+
|
71
|
+
> All notifications passed as a parameter will be sent on a single connection, this is done to improve
|
72
|
+
> reliability with APNS servers.
|
73
|
+
|
74
|
+
#### Another way to send multiple notifications is to send notifications in a persistent connection (thread safe)
|
75
|
+
|
76
|
+
# Define that you want persistent connection
|
77
|
+
APNS.start_persistence
|
78
|
+
|
79
|
+
device_token = '123abc456def'
|
80
|
+
|
81
|
+
# Send single notifications
|
82
|
+
APNS.send_notification(device_token, 'Hello iPhone!' )
|
83
|
+
APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
|
84
|
+
|
85
|
+
# Send multiple notifications
|
86
|
+
n1 = APNS::Notification.new(device_token, 'Hello iPhone!' )
|
87
|
+
n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
|
88
|
+
APNS.send_notifications([n1, n2])
|
89
|
+
|
90
|
+
...
|
91
|
+
|
92
|
+
# Stop persistence, from this point each new push will open and close connections
|
93
|
+
APNS.stop_persistence
|
94
|
+
|
95
|
+
#### Sending more information along
|
96
|
+
|
97
|
+
APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
|
98
|
+
:other => {:sent => 'with apns gem', :custom_param => "value"})
|
99
|
+
|
100
|
+
this will result in a payload like this:
|
101
|
+
|
102
|
+
{"aps":{"alert":"Hello iPhone!","badge":1,"sound":"default"},"sent":"with apns gem", "custom_param":"value"}
|
103
|
+
|
104
|
+
### Getting your iOS device token
|
105
|
+
|
106
|
+
- (void)applicationDidFinishLaunching:(UIApplication *)application {
|
107
|
+
// Register with apple that this app will use push notification
|
108
|
+
...
|
109
|
+
|
110
|
+
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge)];
|
111
|
+
|
112
|
+
...
|
113
|
+
|
114
|
+
}
|
115
|
+
|
116
|
+
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
|
117
|
+
// Show the device token obtained from apple to the log
|
118
|
+
NSLog("deviceToken: %", deviceToken);
|
119
|
+
}
|
120
|
+
|
121
|
+
## GCM (Google Cloud Messaging)
|
122
|
+
|
123
|
+
### Configure
|
124
|
+
|
125
|
+
GCM.host = 'https://android.googleapis.com/gcm/send'
|
126
|
+
# https://android.googleapis.com/gcm/send is default
|
127
|
+
|
128
|
+
GCM.format = :json
|
129
|
+
# :json is default and only available at the moment
|
130
|
+
|
131
|
+
GCM.key = "123abc456def"
|
132
|
+
# this is the apiKey obtained from here https://code.google.com/apis/console/
|
133
|
+
|
134
|
+
### Usage
|
135
|
+
|
136
|
+
#### Sending a single notification:
|
137
|
+
|
138
|
+
destination = ["device1", "device2", "device3"]
|
139
|
+
# can be an string or an array of strings containing the regIds of the devices you want to send
|
140
|
+
|
141
|
+
data = {:key => "value", :key2 => ["array", "value"]}
|
142
|
+
# must be an hash with all values you want inside you notification
|
143
|
+
|
144
|
+
GCM.send_notification( destination )
|
145
|
+
# Empty notification
|
146
|
+
|
147
|
+
GCM.send_notification( destination, data )
|
148
|
+
# Notification with custom information
|
149
|
+
|
150
|
+
GCM.send_notification( destination, data, :collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false )
|
151
|
+
# Notification with custom information and parameters
|
152
|
+
|
153
|
+
for more information on parameters check documentation: [GCM | Android Developers](http://developer.android.com/guide/google/gcm/gcm.html#request)
|
154
|
+
|
155
|
+
#### Sending multiple notifications:
|
156
|
+
|
157
|
+
destination1 = "device1"
|
158
|
+
destination2 = ["device2"]
|
159
|
+
destination3 = ["device1", "device2", "device3"]
|
160
|
+
# can be an string or an array of strings containing the regIds of the devices you want to send
|
161
|
+
|
162
|
+
data1 = {:key => "value", :key2 => ["array", "value"]}
|
163
|
+
# must be an hash with all values you want inside you notification
|
164
|
+
|
165
|
+
options1 = {:collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false}
|
166
|
+
# options for the notification
|
167
|
+
|
168
|
+
n1 = GCM::Notification.new(destination1, data1, options1)
|
169
|
+
n2 = GCM::Notification.new(destination2, data2)
|
170
|
+
n3 = GCM::Notification.new(destination3, data3, options2)
|
171
|
+
|
172
|
+
GCM.send_notifications( [n1, n2, n3] )
|
173
|
+
# In this case, every notification has his own parameters
|
174
|
+
|
175
|
+
for more information on parameters check documentation: [GCM | Android Developers](http://developer.android.com/guide/google/gcm/gcm.html#request)
|
176
|
+
|
177
|
+
#### Getting your Android device token (regId)
|
178
|
+
|
179
|
+
Check this link [GCM: Getting Started](http://developer.android.com/guide/google/gcm/gs.html)
|
180
|
+
|
181
|
+
### (Optional) You can add multiple keys for GCM
|
182
|
+
|
183
|
+
You can use multiple keys to send notifications, to do it just do this changes in the code
|
184
|
+
|
185
|
+
#### Configure
|
186
|
+
|
187
|
+
GCM.key = { :key1 => "123abc456def", :key2 => "456def123abc" }
|
188
|
+
# the ``:key1`` and the ``:key2`` can be any object, they can be the projectID, the date, the version, doesn't matter.
|
189
|
+
# The only restrain is: they need to be valid keys for a hash.
|
190
|
+
|
191
|
+
#### Usage
|
192
|
+
|
193
|
+
# For single notification
|
194
|
+
GCM.send_notification( destination, :identity => :key1 )
|
195
|
+
# Empty notification
|
196
|
+
|
197
|
+
GCM.send_notification( destination, data, :identity => :key1 )
|
198
|
+
# Notification with custom information
|
199
|
+
|
200
|
+
GCM.send_notification( destination, data, :collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false, :identity => :key1 )
|
201
|
+
# Notification with custom information and parameters
|
202
|
+
|
203
|
+
# For multiple notifications
|
204
|
+
options1 = {}
|
205
|
+
options2 = {..., :identity => :key2}
|
206
|
+
n1 = GCM::Notification.new(destination1, data1, options1.merge({:identity => :key2}))
|
207
|
+
n2 = GCM::Notification.new(destination2, data2, :identity => :key1)
|
208
|
+
n3 = GCM::Notification.new(destination3, data3, options2)
|
209
|
+
|
210
|
+
GCM.send_notifications( [n1, n2, n3] )
|
211
|
+
# In this case, every notification has his own parameters, options and key
|
212
|
+
|
213
|
+
## FIRE (Amazon Messaging)
|
214
|
+
|
215
|
+
### Configure
|
216
|
+
|
217
|
+
FIRE.client_id = "amzn1.application-oa2-client.12345678sdfgsdfg"
|
218
|
+
# this is the Client ID obtained from your Security Profile Management on amazon developers
|
219
|
+
|
220
|
+
FIRE.client_secret = "fkgjsbegksklwr863485245ojowe345"
|
221
|
+
# this is the Client Secret obtained from your Security Profile Management on amazon developers
|
222
|
+
|
223
|
+
### Usage
|
224
|
+
|
225
|
+
#### Sending a single notification:
|
226
|
+
|
227
|
+
destination = "tydgfhewgnwe37586329586ejthe93053th346hrth3t"
|
228
|
+
# can be an string or an array of strings containing the regId of the device you want to send
|
229
|
+
|
230
|
+
data = {:key => "value", :key2 => "some value2"}
|
231
|
+
# must be an hash with all values you want inside you notification, strings only, no arrays
|
232
|
+
|
233
|
+
FIRE.send_notification( destination )
|
234
|
+
# Empty notification
|
235
|
+
|
236
|
+
FIRE.send_notification( destination, data )
|
237
|
+
# Notification with custom information
|
238
|
+
|
239
|
+
FIRE.send_notification( destination, data, :consolidationKey => "placar_score_global", :expiresAfter => 3600)
|
240
|
+
# Notification with custom information and parameters
|
241
|
+
|
242
|
+
for more information on parameters check documentation: [Amazon Messaging | Developers](https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/06-sending-a-message#Request Format)
|
243
|
+
|
244
|
+
#### Sending multiple notifications:
|
245
|
+
|
246
|
+
destination1 = "device1"
|
247
|
+
destination2 = ["device2"]
|
248
|
+
destination3 = ["device1", "device2", "device3"]
|
249
|
+
# can be an string or an array of strings containing the regIds of the devices you want to send
|
250
|
+
|
251
|
+
data1 = {:key => "value", :key2 => ["array", "value"]}
|
252
|
+
# must be an hash with all values you want inside you notification
|
253
|
+
|
254
|
+
options1 = {:consolidationKey => "placar_score_global", :expiresAfter => 3600}
|
255
|
+
# options for the notification
|
256
|
+
|
257
|
+
n1 = FIRE::Notification.new(destination1, data1, options1)
|
258
|
+
n2 = FIRE::Notification.new(destination2, data2)
|
259
|
+
n3 = FIRE::Notification.new(destination3, data3, options2)
|
260
|
+
|
261
|
+
FIRE.send_notifications( [n1, n2, n3] )
|
262
|
+
# In this case, every notification has his own parameters
|
263
|
+
|
264
|
+
for more information on parameters check documentation: [Amazon Messaging | Developers](https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/06-sending-a-message#Request Format)
|
265
|
+
|
266
|
+
#### Getting your Kindle Fire device token (regId)
|
267
|
+
|
268
|
+
Check this link [Amazon Messaging: Getting Started](https://developer.amazon.com/public/apis/engage/device-messaging)
|
269
|
+
|
270
|
+
|
271
|
+
## Status
|
272
|
+
|
273
|
+
#### Build Status
|
274
|
+
[](https://travis-ci.org/NicosKaralis/pushmeup)
|
275
|
+
[](https://codeclimate.com/github/NicosKaralis/pushmeup)
|
276
|
+
|
277
|
+
#### Dependency Status [](https://gemnasium.com/NicosKaralis/pushmeup)
|
278
|
+
|
279
|
+
## Contributing
|
280
|
+
|
281
|
+
We would be very pleased if you want to help us!
|
282
|
+
|
283
|
+
Currently we need a lot of testing so if you are good at writing tests please help us
|
284
|
+
|
285
|
+
## License
|
286
|
+
|
287
|
+
Pushmeup is released under the MIT license:
|
288
|
+
|
289
|
+
http://www.opensource.org/licenses/MIT
|
290
|
+
|
291
|
+
|
292
|
+
[](https://bitdeli.com/free "Bitdeli Badge")
|
293
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
task :test => :spec
|
9
|
+
|
10
|
+
desc "Open an irb session preloaded with this library"
|
11
|
+
task :console do
|
12
|
+
sh "irb -rubygems -I lib -r pushmeup.rb"
|
13
|
+
end
|
data/itpushmeup.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "pushmeup/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'itpushmeup'
|
7
|
+
s.version = Pushmeup::VERSION
|
8
|
+
s.authors = ["Nicos Karalis", "Michal Pawlowski"]
|
9
|
+
s.email = ["nicoskaralis@me.com", "misza222@gmail.com"]
|
10
|
+
|
11
|
+
s.homepage = "https://github.com/itsudo/pushmeup"
|
12
|
+
s.summary = %q{Send push notifications to Apple devices through ANPS and Android devices through GCM}
|
13
|
+
s.description = <<-DESC
|
14
|
+
This gem is a wrapper to send push notifications to devices.
|
15
|
+
Currently it only sends to Android or iOS devices, but more platforms will be added soon.
|
16
|
+
|
17
|
+
With APNS (Apple Push Notifications Service) you can send push notifications to Apple devices.
|
18
|
+
With GCM (Google Cloud Messaging) you can send push notifications to Android devices.
|
19
|
+
DESC
|
20
|
+
|
21
|
+
s.rubyforge_project = "pushmeup"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
|
29
|
+
s.add_dependency 'httparty'
|
30
|
+
s.add_dependency 'json'
|
31
|
+
|
32
|
+
s.add_development_dependency 'rake'
|
33
|
+
s.add_development_dependency 'rspec'
|
34
|
+
end
|
data/lib/pushmeup.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'openssl'
|
3
|
+
require 'json'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module APNS
|
7
|
+
@logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
8
|
+
@host = 'gateway.sandbox.push.apple.com'
|
9
|
+
@port = 2195
|
10
|
+
# openssl pkcs12 -in mycert.p12 -out client-cert.pem -nodes -clcerts
|
11
|
+
@pem = nil # this should be the path of the pem file not the contentes
|
12
|
+
@pass = nil
|
13
|
+
|
14
|
+
@persistent = false
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@retries = 3 # TODO: check if we really need this
|
17
|
+
|
18
|
+
@sock = nil
|
19
|
+
@ssl = nil
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_accessor :host, :pem, :port, :pass, :logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.start_persistence
|
26
|
+
@persistent = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.stop_persistence
|
30
|
+
@persistent = false
|
31
|
+
|
32
|
+
@ssl.close
|
33
|
+
@sock.close
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.send_notification(device_token, message)
|
37
|
+
n = APNS::Notification.new(device_token, message)
|
38
|
+
self.send_notifications([n])
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.send_notifications(notifications)
|
42
|
+
@logger.info "Pushmeup: sending message(s): '#{notifications.map { |n| n.packaged_message}.join("|")}' to #{self.host}"
|
43
|
+
|
44
|
+
@mutex.synchronize do
|
45
|
+
self.with_connection do
|
46
|
+
notifications.each do |n|
|
47
|
+
@ssl.write(n.packaged_notification)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.feedback
|
54
|
+
sock, ssl = self.feedback_connection
|
55
|
+
|
56
|
+
apns_feedback = []
|
57
|
+
|
58
|
+
while line = ssl.read(38) # Read lines from the socket
|
59
|
+
line.strip!
|
60
|
+
f = line.unpack('N1n1H140')
|
61
|
+
apns_feedback << { :timestamp => Time.at(f[0]), :token => f[2] }
|
62
|
+
end
|
63
|
+
|
64
|
+
ssl.close
|
65
|
+
sock.close
|
66
|
+
|
67
|
+
return apns_feedback
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def self.with_connection
|
73
|
+
attempts = 1
|
74
|
+
|
75
|
+
begin
|
76
|
+
# If no @ssl is created or if @ssl is closed we need to start it
|
77
|
+
if @ssl.nil? || @sock.nil? || @ssl.closed? || @sock.closed?
|
78
|
+
@sock, @ssl = self.open_connection
|
79
|
+
end
|
80
|
+
|
81
|
+
yield
|
82
|
+
|
83
|
+
rescue StandardError, Errno::EPIPE
|
84
|
+
raise unless attempts < @retries
|
85
|
+
|
86
|
+
@ssl.close
|
87
|
+
@sock.close
|
88
|
+
|
89
|
+
attempts += 1
|
90
|
+
retry
|
91
|
+
end
|
92
|
+
|
93
|
+
# Only force close if not persistent
|
94
|
+
unless @persistent
|
95
|
+
@ssl.close
|
96
|
+
@ssl = nil
|
97
|
+
@sock.close
|
98
|
+
@sock = nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.open_connection
|
103
|
+
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
|
104
|
+
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
|
105
|
+
|
106
|
+
context = OpenSSL::SSL::SSLContext.new
|
107
|
+
context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
|
108
|
+
context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
|
109
|
+
|
110
|
+
sock = TCPSocket.new(self.host, self.port)
|
111
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
|
112
|
+
ssl.connect
|
113
|
+
|
114
|
+
return sock, ssl
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.feedback_connection
|
118
|
+
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
|
119
|
+
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
|
120
|
+
|
121
|
+
context = OpenSSL::SSL::SSLContext.new
|
122
|
+
context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
|
123
|
+
context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
|
124
|
+
|
125
|
+
fhost = self.host.gsub('gateway','feedback')
|
126
|
+
|
127
|
+
sock = TCPSocket.new(fhost, 2196)
|
128
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
|
129
|
+
ssl.connect
|
130
|
+
|
131
|
+
return sock, ssl
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module APNS
|
2
|
+
class Notification
|
3
|
+
attr_accessor :device_token, :alert, :badge, :sound, :other, :content_available
|
4
|
+
|
5
|
+
def initialize(device_token, message)
|
6
|
+
self.device_token = device_token
|
7
|
+
if message.is_a?(Hash)
|
8
|
+
self.alert = message[:alert]
|
9
|
+
self.badge = message[:badge]
|
10
|
+
self.sound = message[:sound]
|
11
|
+
self.content_available = message[:"content-available"]
|
12
|
+
self.other = message[:other]
|
13
|
+
elsif message.is_a?(String)
|
14
|
+
self.alert = message
|
15
|
+
else
|
16
|
+
raise "Notification needs to have either a Hash or String"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def packaged_notification
|
21
|
+
pt = self.packaged_token
|
22
|
+
pm = self.packaged_message
|
23
|
+
[0, 0, 32, pt, 0, pm.bytesize, pm].pack("ccca*cca*")
|
24
|
+
end
|
25
|
+
|
26
|
+
def packaged_token
|
27
|
+
[device_token.gsub(/[\s|<|>]/,'')].pack('H*')
|
28
|
+
end
|
29
|
+
|
30
|
+
def packaged_message
|
31
|
+
aps = {'aps'=> {} }
|
32
|
+
aps['aps']['alert'] = self.alert if self.alert
|
33
|
+
aps['aps']['badge'] = self.badge if self.badge
|
34
|
+
aps['aps']['sound'] = self.sound if self.sound
|
35
|
+
aps['aps']['content-available'] = self.content_available if self.content_available
|
36
|
+
aps.merge!(self.other) if self.other
|
37
|
+
aps.to_json.gsub(/\\u([\da-fA-F]{4})/) {|m| [$1].pack("H*").unpack("n*").pack("U*")}
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(that)
|
41
|
+
device_token == that.device_token &&
|
42
|
+
alert == that.alert &&
|
43
|
+
badge == that.badge &&
|
44
|
+
sound == that.sound &&
|
45
|
+
content_available == that.content_available &&
|
46
|
+
other == that.other
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
# require 'cgi'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module FIRE
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
@host = 'https://api.amazon.com/messaging/registrations/%s/messages'
|
9
|
+
@client_id = nil
|
10
|
+
@client_secret = nil
|
11
|
+
|
12
|
+
@access_token_expiration = Time.new(0)
|
13
|
+
@access_token = nil
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :host, :client_id, :client_secret, :access_token, :access_token_expiration
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.send_notification(device_token, data = {}, options = {})
|
20
|
+
n = FIRE::Notification.new(device_token, data, options)
|
21
|
+
self.send_notifications([n])
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.send_notifications(notifications)
|
25
|
+
self.prepare_token
|
26
|
+
responses = []
|
27
|
+
notifications.each do |n|
|
28
|
+
responses << self.prepare_and_send(n)
|
29
|
+
end
|
30
|
+
responses
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.prepare_token
|
34
|
+
return if Time.now < self.access_token_expiration
|
35
|
+
|
36
|
+
token = self.get_access_token
|
37
|
+
self.access_token = token['access_token']
|
38
|
+
expires_in_sec = token['expires_in']
|
39
|
+
self.access_token_expiration = Time.now + expires_in_sec - 60
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_access_token
|
43
|
+
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
44
|
+
body = {grant_type: 'client_credentials',
|
45
|
+
scope: 'messaging:push',
|
46
|
+
client_id: self.client_id,
|
47
|
+
client_secret: self.client_secret
|
48
|
+
}
|
49
|
+
params = {headers: headers, body: body}
|
50
|
+
res = self.post('https://api.amazon.com/auth/O2/token', params)
|
51
|
+
return res.parsed_response if res.response.code.to_i == 200
|
52
|
+
raise 'Error getting access token'
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def self.prepare_and_send(n)
|
58
|
+
if !n.consolidationKey.nil? && n.expiresAfter.nil?
|
59
|
+
raise %q{If you are defining a "colapse key" you need a "time to live"}
|
60
|
+
end
|
61
|
+
self.send_push(n)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.send_push(n)
|
65
|
+
headers = {
|
66
|
+
'Authorization' => "Bearer #{self.access_token}",
|
67
|
+
'Content-Type' => 'application/json',
|
68
|
+
'Accept' => 'application/json',
|
69
|
+
'X-Amzn-Accept-Type' => 'com.amazon.device.messaging.ADMSendResult@1.0',
|
70
|
+
'X-Amzn-Type-Version' => 'com.amazon.device.messaging.ADMMessage@1.0'
|
71
|
+
}
|
72
|
+
|
73
|
+
body = {
|
74
|
+
:data => n.data
|
75
|
+
}
|
76
|
+
body.merge!({consolidationKey: n.consolidationKey}) if n.consolidationKey
|
77
|
+
body.merge!({expiresAfter: n.expiresAfter}) if n.expiresAfter
|
78
|
+
return self.send_to_server(headers, body.to_json, n.device_token)
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.send_to_server(headers, body, token)
|
82
|
+
params = {:headers => headers, :body => body}
|
83
|
+
device_dest = self.host % [token]
|
84
|
+
response = self.post(device_dest, params)
|
85
|
+
return build_response(response)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.build_response(response)
|
89
|
+
case response.code
|
90
|
+
when 200
|
91
|
+
{:response => 'success', :body => JSON.parse(response.body), :headers => response.headers, :status_code => response.code}
|
92
|
+
when 400
|
93
|
+
{:response => response.parsed_response, :status_code => response.code}
|
94
|
+
when 401
|
95
|
+
{:response => 'There was an error authenticating the sender account.', :status_code => response.code}
|
96
|
+
when 500
|
97
|
+
{:response => 'There was an internal error in the Amazaon server while trying to process the request.', :status_code => response.code}
|
98
|
+
when 503
|
99
|
+
{:response => 'Server is temporarily unavailable.', :status_code => response.code}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module FIRE
|
2
|
+
class Notification
|
3
|
+
attr_accessor :device_token, :data, :consolidationKey, :expiresAfter
|
4
|
+
|
5
|
+
def initialize(token, data, options = {})
|
6
|
+
self.device_token = token
|
7
|
+
self.data = data
|
8
|
+
|
9
|
+
@consolidationKey = options[:consolidationKey]
|
10
|
+
@expiresAfter = options[:expiresAfter]
|
11
|
+
end
|
12
|
+
|
13
|
+
def device_token=(token)
|
14
|
+
|
15
|
+
if token.is_a?(String)
|
16
|
+
@device_token = token
|
17
|
+
else
|
18
|
+
raise "device_token needs to be String"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def data=(data)
|
23
|
+
if data.is_a?(Hash)
|
24
|
+
@data = data
|
25
|
+
else
|
26
|
+
raise "data parameter must be the type of Hash"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def expiresAfter=(expiresAfter)
|
31
|
+
if expiresAfter.is_a?(Integer)
|
32
|
+
@expiresAfter = expiresAfter
|
33
|
+
else
|
34
|
+
raise %q{"expiresAfter" must be seconds as an integer value, like "100"}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(that)
|
39
|
+
device_token == that.device_token &&
|
40
|
+
data == that.data &&
|
41
|
+
consolidationKey == that.consolidationKey &&
|
42
|
+
expiresAfter == that.expiresAfter
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
# require 'cgi'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module GCM
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
@host = 'https://android.googleapis.com/gcm/send'
|
9
|
+
@format = :json
|
10
|
+
@key = nil
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :host, :format, :key
|
14
|
+
|
15
|
+
def key(identity = nil)
|
16
|
+
if @key.is_a?(Hash)
|
17
|
+
raise %{If your key is a hash of keys you'l need to pass a identifier to the notification!} if identity.nil?
|
18
|
+
return @key[identity]
|
19
|
+
else
|
20
|
+
return @key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def key_identities
|
25
|
+
if @key.is_a?(Hash)
|
26
|
+
return @key.keys
|
27
|
+
else
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.send_notification(device_tokens, data = {}, options = {})
|
34
|
+
n = GCM::Notification.new(device_tokens, data, options)
|
35
|
+
self.send_notifications([n])
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.send_notifications(notifications)
|
39
|
+
responses = []
|
40
|
+
notifications.each do |n|
|
41
|
+
responses << self.prepare_and_send(n)
|
42
|
+
end
|
43
|
+
responses
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.prepare_and_send(n)
|
49
|
+
if n.device_tokens.count < 1 || n.device_tokens.count > 1000
|
50
|
+
raise "Number of device_tokens invalid, keep it betwen 1 and 1000"
|
51
|
+
end
|
52
|
+
if !n.collapse_key.nil? && n.time_to_live.nil?
|
53
|
+
raise %q{If you are defining a "colapse key" you need a "time to live"}
|
54
|
+
end
|
55
|
+
if @key.is_a?(Hash) && n.identity.nil?
|
56
|
+
raise %{If your key is a hash of keys you'l need to pass a identifier to the notification!}
|
57
|
+
end
|
58
|
+
|
59
|
+
if self.format == :json
|
60
|
+
self.send_push_as_json(n)
|
61
|
+
elsif self.format == :text
|
62
|
+
self.send_push_as_plain_text(n)
|
63
|
+
else
|
64
|
+
raise "Invalid format"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.send_push_as_json(n)
|
69
|
+
headers = {
|
70
|
+
'Authorization' => "key=#{ self.key(n.identity) }",
|
71
|
+
'Content-Type' => 'application/json',
|
72
|
+
}
|
73
|
+
body = {
|
74
|
+
:registration_ids => n.device_tokens,
|
75
|
+
:data => n.data,
|
76
|
+
:collapse_key => n.collapse_key,
|
77
|
+
:time_to_live => n.time_to_live,
|
78
|
+
:delay_while_idle => n.delay_while_idle
|
79
|
+
}
|
80
|
+
return self.send_to_server(headers, body.to_json)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.send_push_as_plain_text(n)
|
84
|
+
raise "Still has to be done: http://developer.android.com/guide/google/gcm/gcm.html"
|
85
|
+
headers = {
|
86
|
+
# TODO: Aceitar key ser um hash
|
87
|
+
'Authorization' => "key=#{ self.key(n.identity) }",
|
88
|
+
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
|
89
|
+
}
|
90
|
+
return self.send_to_server(headers, body)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.send_to_server(headers, body)
|
94
|
+
params = {:headers => headers, :body => body}
|
95
|
+
response = self.post(self.host, params)
|
96
|
+
return build_response(response)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.build_response(response)
|
100
|
+
case response.code
|
101
|
+
when 200
|
102
|
+
{:response => 'success', :body => JSON.parse(response.body), :headers => response.headers, :status_code => response.code}
|
103
|
+
when 400
|
104
|
+
{:response => 'Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.', :status_code => response.code}
|
105
|
+
when 401
|
106
|
+
{:response => 'There was an error authenticating the sender account.', :status_code => response.code}
|
107
|
+
when 500
|
108
|
+
{:response => 'There was an internal error in the GCM server while trying to process the request.', :status_code => response.code}
|
109
|
+
when 503
|
110
|
+
{:response => 'Server is temporarily unavailable.', :status_code => response.code}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GCM
|
2
|
+
class Notification
|
3
|
+
attr_accessor :device_tokens, :data, :collapse_key, :time_to_live, :delay_while_idle, :identity
|
4
|
+
|
5
|
+
def initialize(tokens, data, options = {})
|
6
|
+
self.device_tokens = tokens
|
7
|
+
self.data = data
|
8
|
+
|
9
|
+
@collapse_key = options[:collapse_key]
|
10
|
+
@time_to_live = options[:time_to_live]
|
11
|
+
@delay_while_idle = options[:delay_while_idle]
|
12
|
+
@identity = options[:identity]
|
13
|
+
end
|
14
|
+
|
15
|
+
def device_tokens=(tokens)
|
16
|
+
if tokens.is_a?(Array)
|
17
|
+
@device_tokens = tokens
|
18
|
+
elsif tokens.is_a?(String)
|
19
|
+
@device_tokens = [tokens]
|
20
|
+
else
|
21
|
+
raise "device_tokens needs to be either a Hash or String"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def data=(data)
|
26
|
+
if data.is_a?(Hash)
|
27
|
+
@data = data
|
28
|
+
else
|
29
|
+
raise "data parameter must be the type of Hash"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def delay_while_idle=(delay_while_idle)
|
34
|
+
@delay_while_idle = (delay_while_idle == true || delay_while_idle == :true)
|
35
|
+
end
|
36
|
+
|
37
|
+
def time_to_live=(time_to_live)
|
38
|
+
if time_to_live.is_a?(Integer)
|
39
|
+
@time_to_live = time_to_live
|
40
|
+
else
|
41
|
+
raise %q{"time_to_live" must be seconds as an integer value, like "100"}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(that)
|
46
|
+
device_tokens == that.device_tokens &&
|
47
|
+
data == that.data &&
|
48
|
+
collapse_key == that.collapse_key &&
|
49
|
+
time_to_live == that.time_to_live &&
|
50
|
+
delay_while_idle == that.delay_while_idle &&
|
51
|
+
identity == that.identity
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Pushmeup do
|
4
|
+
describe "APNS" do
|
5
|
+
it "should have a APNS object" do
|
6
|
+
defined?(APNS).should_not be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should not forget the APNS default parameters" do
|
10
|
+
APNS.host.should == "gateway.sandbox.push.apple.com"
|
11
|
+
APNS.port.should == 2195
|
12
|
+
APNS.pem.should be_equal(nil)
|
13
|
+
APNS.pass.should be_equal(nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "Notifications" do
|
17
|
+
describe "#==" do
|
18
|
+
|
19
|
+
it "should properly equate objects without caring about object identity" do
|
20
|
+
a = APNS::Notification.new("123", {:alert => "hi"})
|
21
|
+
b = APNS::Notification.new("123", {:alert => "hi"})
|
22
|
+
a.should eq(b)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should add content-available to data" do
|
26
|
+
a = APNS::Notification.new("123", {:alert => "hi", :"content-available" => 1})
|
27
|
+
|
28
|
+
a.packaged_message.should == '{"aps":{"alert":"hi","content-available":1}}'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "GCM" do
|
37
|
+
it "should have a GCM object" do
|
38
|
+
defined?(GCM).should_not be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "Notifications" do
|
42
|
+
|
43
|
+
before do
|
44
|
+
@options = {:data => "dummy data"}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should allow only notifications with device_tokens as array" do
|
48
|
+
n = GCM::Notification.new("id", @options)
|
49
|
+
expect(n.device_tokens.is_a?(Array)).to eq(true)
|
50
|
+
|
51
|
+
n.device_tokens = ["a" "b", "c"]
|
52
|
+
expect(n.device_tokens.is_a?(Array)).to eq(true)
|
53
|
+
|
54
|
+
n.device_tokens = "a"
|
55
|
+
expect(n.device_tokens.is_a?(Array)).to eq(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should allow only notifications with data as hash with :data root" do
|
59
|
+
n = GCM::Notification.new("id", { :data => "data" })
|
60
|
+
|
61
|
+
expect(n.data.is_a?(Hash)).to eq(true)
|
62
|
+
n.data.should == {:data => "data"}
|
63
|
+
|
64
|
+
n.data = {:a => ["a", "b", "c"]}
|
65
|
+
expect(n.data.is_a?(Hash)).to eq(true)
|
66
|
+
n.data.should == {:a => ["a", "b", "c"]}
|
67
|
+
|
68
|
+
n.data = {:a => "a"}
|
69
|
+
expect(n.data.is_a?(Hash)).to eq(true)
|
70
|
+
n.data.should == {:a => "a"}
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#==" do
|
74
|
+
|
75
|
+
it "should properly equate objects without caring about object identity" do
|
76
|
+
a = GCM::Notification.new("id", { :data => "data" })
|
77
|
+
b = GCM::Notification.new("id", { :data => "data" })
|
78
|
+
a.should eq(b)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pushmeup'
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: itpushmeup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicos Karalis
|
8
|
+
- Michal Pawlowski
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-04-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: json
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: |2
|
71
|
+
This gem is a wrapper to send push notifications to devices.
|
72
|
+
Currently it only sends to Android or iOS devices, but more platforms will be added soon.
|
73
|
+
|
74
|
+
With APNS (Apple Push Notifications Service) you can send push notifications to Apple devices.
|
75
|
+
With GCM (Google Cloud Messaging) you can send push notifications to Android devices.
|
76
|
+
email:
|
77
|
+
- nicoskaralis@me.com
|
78
|
+
- misza222@gmail.com
|
79
|
+
executables: []
|
80
|
+
extensions: []
|
81
|
+
extra_rdoc_files: []
|
82
|
+
files:
|
83
|
+
- ".gitignore"
|
84
|
+
- ".travis.yml"
|
85
|
+
- Gemfile
|
86
|
+
- Keychain Access.jpg
|
87
|
+
- LICENSE
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- itpushmeup.gemspec
|
91
|
+
- lib/pushmeup.rb
|
92
|
+
- lib/pushmeup/amazon.rb
|
93
|
+
- lib/pushmeup/android.rb
|
94
|
+
- lib/pushmeup/apns/core.rb
|
95
|
+
- lib/pushmeup/apns/notification.rb
|
96
|
+
- lib/pushmeup/apple.rb
|
97
|
+
- lib/pushmeup/fire/core.rb
|
98
|
+
- lib/pushmeup/fire/notification.rb
|
99
|
+
- lib/pushmeup/gcm/core.rb
|
100
|
+
- lib/pushmeup/gcm/notification.rb
|
101
|
+
- lib/pushmeup/version.rb
|
102
|
+
- spec/lib/pushmeup_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
homepage: https://github.com/itsudo/pushmeup
|
105
|
+
licenses: []
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project: pushmeup
|
123
|
+
rubygems_version: 2.2.2
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Send push notifications to Apple devices through ANPS and Android devices
|
127
|
+
through GCM
|
128
|
+
test_files: []
|