itpushmeup 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []