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.
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-19mode # JRuby in 1.9 mode
6
+ # uncomment this line if your project needs to run something other than `rake`:
7
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pushmeup.gemspec
4
+ gemspec
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.
@@ -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
+ ![Keychain Access](https://raw.github.com/NicosKaralis/pushmeup/master/Keychain Access.jpg)
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
+ [![Build Status](https://travis-ci.org/NicosKaralis/pushmeup.png?branch=master)](https://travis-ci.org/NicosKaralis/pushmeup)
275
+ [![Code Climate](https://codeclimate.com/github/NicosKaralis/pushmeup.png)](https://codeclimate.com/github/NicosKaralis/pushmeup)
276
+
277
+ #### Dependency Status [![Dependency Status](https://gemnasium.com/NicosKaralis/pushmeup.png?travis)](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
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/NicosKaralis/pushmeup/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
293
+
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ require "pushmeup/version"
2
+ require "pushmeup/apple"
3
+ require "pushmeup/android"
4
+ require "pushmeup/amazon"
@@ -0,0 +1,2 @@
1
+ require "pushmeup/fire/core"
2
+ require "pushmeup/fire/notification"
@@ -0,0 +1,2 @@
1
+ require "pushmeup/gcm/core"
2
+ require "pushmeup/gcm/notification"
@@ -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,2 @@
1
+ require "pushmeup/apns/core"
2
+ require "pushmeup/apns/notification"
@@ -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,3 @@
1
+ module Pushmeup
2
+ VERSION = "0.3.1"
3
+ 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
@@ -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: []