pushmeup_tns 0.1.4.hf1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +18 -0
- data/.rvmrc +48 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/Keychain Access.jpg +0 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +20 -0
- data/README.md +204 -0
- data/README.rdoc +19 -0
- data/Rakefile +13 -0
- data/lib/pushmeup/android.rb +2 -0
- data/lib/pushmeup/apns/core.rb +85 -0
- data/lib/pushmeup/apns/notification.rb +39 -0
- data/lib/pushmeup/apple.rb +2 -0
- data/lib/pushmeup/gcm/core.rb +114 -0
- data/lib/pushmeup/gcm/notification.rb +46 -0
- data/lib/pushmeup/version.rb +3 -0
- data/lib/pushmeup-tns.rb +0 -0
- data/lib/pushmeup.rb +3 -0
- data/pushmeup-tns.gemspec +34 -0
- data/spec/lib/pushmeup_spec.rb +57 -0
- data/spec/spec_helper.rb +1 -0
- data/test/helper.rb +18 -0
- data/test/test_pushmeup-tns.rb +7 -0
- metadata +140 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3@pushmeup"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.11.5 (master)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create "$environment_id" || {
|
31
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
32
|
+
return 1
|
33
|
+
}
|
34
|
+
fi
|
35
|
+
|
36
|
+
# If you use bundler, this might be useful to you:
|
37
|
+
if [[ -s Gemfile ]] && {
|
38
|
+
! builtin command -v bundle >/dev/null ||
|
39
|
+
builtin command -v bundle | grep $rvm_path/bin/bundle >/dev/null
|
40
|
+
}
|
41
|
+
then
|
42
|
+
printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
43
|
+
gem install bundler
|
44
|
+
fi
|
45
|
+
if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
46
|
+
then
|
47
|
+
bundle install | grep -vE '^Using|Your bundle is complete'
|
48
|
+
fi
|
data/.travis.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.8.7
|
4
|
+
- 1.9.2
|
5
|
+
- 1.9.3
|
6
|
+
- jruby-18mode # JRuby in 1.8 mode
|
7
|
+
- jruby-19mode # JRuby in 1.9 mode
|
8
|
+
- rbx-18mode
|
9
|
+
- rbx-19mode
|
10
|
+
# uncomment this line if your project needs to run something other than `rake`:
|
11
|
+
# script: bundle exec rspec spec
|
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/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Thet Naing Swe
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,204 @@
|
|
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`` and ``Android`` 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
|
45
|
+
|
46
|
+
APNS.port = 2195
|
47
|
+
# this is also the default. Shouldn't ever have to set this, but just in case Apple goes crazy, you can.
|
48
|
+
|
49
|
+
APNS.pem = '/path/to/pem/file'
|
50
|
+
# this is the file you just created
|
51
|
+
|
52
|
+
APNS.pass = ''
|
53
|
+
# Just in case your pem need a password
|
54
|
+
|
55
|
+
### Usage
|
56
|
+
|
57
|
+
#### Sending a single notification:
|
58
|
+
|
59
|
+
device_token = '123abc456def'
|
60
|
+
APNS.send_notification(device_token, 'Hello iPhone!' )
|
61
|
+
APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
|
62
|
+
|
63
|
+
#### Sending multiple notifications
|
64
|
+
|
65
|
+
device_token = '123abc456def'
|
66
|
+
n1 = APNS::Notification.new(device_token, 'Hello iPhone!' )
|
67
|
+
n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
|
68
|
+
APNS.send_notifications([n1, n2])
|
69
|
+
|
70
|
+
#### Sending more information along
|
71
|
+
|
72
|
+
APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
|
73
|
+
:other => {:sent => 'with apns gem', :custom_param => "value"})
|
74
|
+
|
75
|
+
this will result in a payload like this:
|
76
|
+
|
77
|
+
{"aps":{"alert":"Hello iPhone!","badge":1,"sound":"default"},"sent":"with apns gem", "custom_param":"value"}
|
78
|
+
|
79
|
+
### Getting your iOS device token
|
80
|
+
|
81
|
+
- (void)applicationDidFinishLaunching:(UIApplication *)application {
|
82
|
+
// Register with apple that this app will use push notification
|
83
|
+
...
|
84
|
+
|
85
|
+
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge)];
|
86
|
+
|
87
|
+
...
|
88
|
+
|
89
|
+
}
|
90
|
+
|
91
|
+
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
|
92
|
+
// Show the device token obtained from apple to the log
|
93
|
+
NSLog("deviceToken: %", deviceToken);
|
94
|
+
}
|
95
|
+
|
96
|
+
## GCM (Google Cloud Messaging)
|
97
|
+
|
98
|
+
### Configure
|
99
|
+
|
100
|
+
GCM.host = 'https://android.googleapis.com/gcm/send'
|
101
|
+
# https://android.googleapis.com/gcm/send is default
|
102
|
+
|
103
|
+
GCM.format = :json
|
104
|
+
# :json is default and only available at the moment
|
105
|
+
|
106
|
+
GCM.key = "123abc456def"
|
107
|
+
# this is the apiKey obtained from here https://code.google.com/apis/console/
|
108
|
+
|
109
|
+
### Usage
|
110
|
+
|
111
|
+
#### Sending a single notification:
|
112
|
+
|
113
|
+
destination = ["device1", "device2", "device3"]
|
114
|
+
# can be an string or an array of strings containing the regIds of the devices you want to send
|
115
|
+
|
116
|
+
data = {:key => "value", :key2 => ["array", "value"]}
|
117
|
+
# must be an hash with all values you want inside you notification
|
118
|
+
|
119
|
+
GCM.send_notification( destination )
|
120
|
+
# Empty notification
|
121
|
+
|
122
|
+
GCM.send_notification( destination, data )
|
123
|
+
# Notification with custom information
|
124
|
+
|
125
|
+
GCM.send_notification( destination, data, :collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false )
|
126
|
+
# Notification with custom information and parameters
|
127
|
+
|
128
|
+
for more information on parameters check documentation: [GCM | Android Developers](http://developer.android.com/guide/google/gcm/gcm.html#request)
|
129
|
+
|
130
|
+
#### Sending multiple notifications:
|
131
|
+
|
132
|
+
destination1 = "device1"
|
133
|
+
destination2 = ["device2"]
|
134
|
+
destination3 = ["device1", "device2", "device3"]
|
135
|
+
# can be an string or an array of strings containing the regIds of the devices you want to send
|
136
|
+
|
137
|
+
data1 = {:key => "value", :key2 => ["array", "value"]}
|
138
|
+
# must be an hash with all values you want inside you notification
|
139
|
+
|
140
|
+
options1 = {:collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false}
|
141
|
+
# options for the notification
|
142
|
+
|
143
|
+
n1 = GCM::Notification.new(destination1, data1, options1)
|
144
|
+
n2 = GCM::Notification.new(destination2, data2)
|
145
|
+
n3 = GCM::Notification.new(destination3, data3, options2)
|
146
|
+
|
147
|
+
GCM.send_notifications( [n1, n2, n3] )
|
148
|
+
# In this case, every notification has his own parameters
|
149
|
+
|
150
|
+
for more information on parameters check documentation: [GCM | Android Developers](http://developer.android.com/guide/google/gcm/gcm.html#request)
|
151
|
+
|
152
|
+
#### Getting your Android device token (regId)
|
153
|
+
|
154
|
+
Check this link [GCM: Getting Started](http://developer.android.com/guide/google/gcm/gs.html)
|
155
|
+
|
156
|
+
### (Optional) You can add multiple keys for GCM
|
157
|
+
|
158
|
+
You can use multiple keys to send notifications, to do it just do this changes in the code
|
159
|
+
|
160
|
+
#### Configure
|
161
|
+
|
162
|
+
GCM.key = { :key1 => "123abc456def", :key2 => "456def123abc" }
|
163
|
+
# the ``:key1`` and the ``:key2`` can be any object, they can be the projectID, the date, the version, doesn't matter.
|
164
|
+
# The only restrain is: they need to be valid keys for a hash.
|
165
|
+
|
166
|
+
#### Usage
|
167
|
+
|
168
|
+
# For single notification
|
169
|
+
GCM.send_notification( destination, :identity => :key1 )
|
170
|
+
# Empty notification
|
171
|
+
|
172
|
+
GCM.send_notification( destination, data, :identity => :key1 )
|
173
|
+
# Notification with custom information
|
174
|
+
|
175
|
+
GCM.send_notification( destination, data, :collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false, :identity => :key1 )
|
176
|
+
# Notification with custom information and parameters
|
177
|
+
|
178
|
+
# For multiple notifications
|
179
|
+
options1 = {}
|
180
|
+
options2 = {..., :identity => :key2}
|
181
|
+
n1 = GCM::Notification.new(destination1, data1, options1.merge({:identity => :key2}))
|
182
|
+
n2 = GCM::Notification.new(destination2, data2, :identity => :key1)
|
183
|
+
n3 = GCM::Notification.new(destination3, data3, options2)
|
184
|
+
|
185
|
+
GCM.send_notifications( [n1, n2, n3] )
|
186
|
+
# In this case, every notification has his own parameters, options and key
|
187
|
+
|
188
|
+
## Status
|
189
|
+
|
190
|
+
#### Build Status [![Build Status](https://secure.travis-ci.org/NicosKaralis/pushmeup.png?branch=master)](http://travis-ci.org/NicosKaralis/pushmeup) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/NicosKaralis/pushmeup)
|
191
|
+
|
192
|
+
#### Dependency Status [![Dependency Status](https://gemnasium.com/NicosKaralis/pushmeup.png?travis)](https://gemnasium.com/NicosKaralis/pushmeup)
|
193
|
+
|
194
|
+
## Contributing
|
195
|
+
|
196
|
+
We would be very pleased if you want to help us!
|
197
|
+
|
198
|
+
Currently we need a lot of testing so if you are good at writing tests please help us
|
199
|
+
|
200
|
+
## License
|
201
|
+
|
202
|
+
Pushmeup is released under the MIT license:
|
203
|
+
|
204
|
+
http://www.opensource.org/licenses/MIT
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= pushmeup-tns
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to pushmeup-tns
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2013 Thet Naing Swe. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
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
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'openssl'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module APNS
|
6
|
+
|
7
|
+
@host = 'gateway.sandbox.push.apple.com'
|
8
|
+
@port = 2195
|
9
|
+
# openssl pkcs12 -in mycert.p12 -out client-cert.pem -nodes -clcerts
|
10
|
+
@pem = nil # this should be the path of the pem file not the contentes
|
11
|
+
@pass = nil
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :host, :pem, :port, :pass
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.send_notification(device_token, message)
|
18
|
+
n = APNS::Notification.new(device_token, message)
|
19
|
+
self.send_notifications([n])
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.send_notifications(notifications)
|
23
|
+
sock, ssl = self.open_connection
|
24
|
+
|
25
|
+
notifications.each do |n|
|
26
|
+
ssl.write(n.packaged_notification)
|
27
|
+
end
|
28
|
+
|
29
|
+
ssl.close
|
30
|
+
sock.close
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.feedback
|
34
|
+
sock, ssl = self.feedback_connection
|
35
|
+
|
36
|
+
apns_feedback = []
|
37
|
+
|
38
|
+
while line = ssl.read(38) # Read lines from the socket
|
39
|
+
line.strip!
|
40
|
+
f = line.unpack('N1n1H140')
|
41
|
+
apns_feedback << { :timestamp => Time.at(f[0]), :token => f[2] }
|
42
|
+
end
|
43
|
+
|
44
|
+
ssl.close
|
45
|
+
sock.close
|
46
|
+
|
47
|
+
return apns_feedback
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def self.open_connection
|
53
|
+
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
|
54
|
+
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
|
55
|
+
|
56
|
+
context = OpenSSL::SSL::SSLContext.new
|
57
|
+
context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
|
58
|
+
context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
|
59
|
+
|
60
|
+
sock = TCPSocket.new(self.host, self.port)
|
61
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
|
62
|
+
ssl.connect
|
63
|
+
|
64
|
+
return sock, ssl
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.feedback_connection
|
68
|
+
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
|
69
|
+
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
|
70
|
+
|
71
|
+
context = OpenSSL::SSL::SSLContext.new
|
72
|
+
context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
|
73
|
+
context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
|
74
|
+
|
75
|
+
fhost = self.host.gsub('gateway','feedback')
|
76
|
+
puts fhost
|
77
|
+
|
78
|
+
sock = TCPSocket.new(fhost, 2196)
|
79
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
|
80
|
+
ssl.connect
|
81
|
+
|
82
|
+
return sock, ssl
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module APNS
|
2
|
+
class Notification
|
3
|
+
attr_accessor :device_token, :alert, :badge, :sound, :other
|
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.other = message[:other]
|
12
|
+
elsif message.is_a?(String)
|
13
|
+
self.alert = message
|
14
|
+
else
|
15
|
+
raise "Notification needs to have either a hash or string"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def packaged_notification
|
20
|
+
pt = self.packaged_token
|
21
|
+
pm = self.packaged_message
|
22
|
+
[0, 0, 32, pt, 0, pm.bytesize, pm].pack("ccca*cca*")
|
23
|
+
end
|
24
|
+
|
25
|
+
def packaged_token
|
26
|
+
[device_token.gsub(/[\s|<|>]/,'')].pack('H*')
|
27
|
+
end
|
28
|
+
|
29
|
+
def packaged_message
|
30
|
+
aps = {'aps'=> {} }
|
31
|
+
aps['aps']['alert'] = self.alert if self.alert
|
32
|
+
aps['aps']['badge'] = self.badge if self.badge
|
33
|
+
aps['aps']['sound'] = self.sound if self.sound
|
34
|
+
aps.merge!(self.other) if self.other
|
35
|
+
aps.to_json.gsub(/\\u([\da-fA-F]{4})/) {|m| [$1].pack("H*").unpack("n*").pack("U*")}
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
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,46 @@
|
|
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 tpe 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
|
+
end
|
46
|
+
end
|
data/lib/pushmeup-tns.rb
ADDED
File without changes
|
data/lib/pushmeup.rb
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 = 'pushmeup_tns'
|
7
|
+
s.version = Pushmeup::VERSION
|
8
|
+
s.authors = ["Thet Naing Swe"]
|
9
|
+
s.email = ["thetnswe@gmail.com"]
|
10
|
+
|
11
|
+
s.homepage = "https://github.com/thetnswe/pushmeup-tns"
|
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,57 @@
|
|
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_false
|
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
|
+
end
|
17
|
+
|
18
|
+
describe "GCM" do
|
19
|
+
it "should have a GCM object" do
|
20
|
+
defined?(GCM).should_not be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "Notifications" do
|
24
|
+
|
25
|
+
before do
|
26
|
+
@options = {:data => "dummy data"}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should allow only notifications with device_tokens as array" do
|
30
|
+
n = GCM::Notification.new("id", @options)
|
31
|
+
n.device_tokens.is_a?(Array).should be_true
|
32
|
+
|
33
|
+
n.device_tokens = ["a" "b", "c"]
|
34
|
+
n.device_tokens.is_a?(Array).should be_true
|
35
|
+
|
36
|
+
n.device_tokens = "a"
|
37
|
+
n.device_tokens.is_a?(Array).should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should allow only notifications with data as hash with :data root" do
|
41
|
+
n = GCM::Notification.new("id", {:data => "data"})
|
42
|
+
|
43
|
+
n.data.is_a?(Hash).should be_true
|
44
|
+
n.data.should == {:data => "data"}
|
45
|
+
|
46
|
+
n.data = {:a => ["a", "b", "c"]}
|
47
|
+
n.data.is_a?(Hash).should be_true
|
48
|
+
n.data.should == {:a => ["a", "b", "c"]}
|
49
|
+
|
50
|
+
n.data = {:a => "a"}
|
51
|
+
n.data.is_a?(Hash).should be_true
|
52
|
+
n.data.should == {:a => "a"}
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pushmeup'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'pushmeup-tns'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pushmeup_tns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.4.hf1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Thet Naing Swe
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: ! " This gem is a wrapper to send push notifications
|
79
|
+
to devices.\n Currently it only sends to Android or iOS devices,
|
80
|
+
but more platforms will be added soon.\n\n With APNS (Apple
|
81
|
+
Push Notifications Service) you can send push notifications to Apple devices.\n
|
82
|
+
\ With GCM (Google Cloud Messaging) you can send push notifications
|
83
|
+
to Android devices.\n"
|
84
|
+
email:
|
85
|
+
- thetnswe@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- .document
|
91
|
+
- .gitignore
|
92
|
+
- .rvmrc
|
93
|
+
- .travis.yml
|
94
|
+
- Gemfile
|
95
|
+
- Keychain Access.jpg
|
96
|
+
- LICENSE
|
97
|
+
- LICENSE.txt
|
98
|
+
- README.md
|
99
|
+
- README.rdoc
|
100
|
+
- Rakefile
|
101
|
+
- lib/pushmeup-tns.rb
|
102
|
+
- lib/pushmeup.rb
|
103
|
+
- lib/pushmeup/android.rb
|
104
|
+
- lib/pushmeup/apns/core.rb
|
105
|
+
- lib/pushmeup/apns/notification.rb
|
106
|
+
- lib/pushmeup/apple.rb
|
107
|
+
- lib/pushmeup/gcm/core.rb
|
108
|
+
- lib/pushmeup/gcm/notification.rb
|
109
|
+
- lib/pushmeup/version.rb
|
110
|
+
- pushmeup-tns.gemspec
|
111
|
+
- spec/lib/pushmeup_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
- test/helper.rb
|
114
|
+
- test/test_pushmeup-tns.rb
|
115
|
+
homepage: https://github.com/thetnswe/pushmeup-tns
|
116
|
+
licenses: []
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ! '>'
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 1.3.1
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project: pushmeup
|
135
|
+
rubygems_version: 1.8.24
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: Send push notifications to Apple devices through ANPS and Android devices
|
139
|
+
through GCM
|
140
|
+
test_files: []
|