fcm-ruby-push-notifications 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +2 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +32 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +82 -0
- data/Rakefile +12 -0
- data/examples/apns.rb +27 -0
- data/examples/gcm.rb +25 -0
- data/examples/mpns.rb +22 -0
- data/examples/wns.rb +25 -0
- data/gemfiles/Gemfile-legacy +8 -0
- data/lib/ruby-push-notifications.rb +7 -0
- data/lib/ruby-push-notifications/apns.rb +44 -0
- data/lib/ruby-push-notifications/apns/apns_connection.rb +70 -0
- data/lib/ruby-push-notifications/apns/apns_notification.rb +91 -0
- data/lib/ruby-push-notifications/apns/apns_pusher.rb +84 -0
- data/lib/ruby-push-notifications/apns/apns_results.rb +30 -0
- data/lib/ruby-push-notifications/fcm.rb +8 -0
- data/lib/ruby-push-notifications/fcm/fcm_connection.rb +60 -0
- data/lib/ruby-push-notifications/fcm/fcm_error.rb +17 -0
- data/lib/ruby-push-notifications/fcm/fcm_notification.rb +31 -0
- data/lib/ruby-push-notifications/fcm/fcm_pusher.rb +29 -0
- data/lib/ruby-push-notifications/fcm/fcm_request.rb +28 -0
- data/lib/ruby-push-notifications/fcm/fcm_response.rb +89 -0
- data/lib/ruby-push-notifications/fcm/fcm_result.rb +52 -0
- data/lib/ruby-push-notifications/gcm.rb +6 -0
- data/lib/ruby-push-notifications/gcm/gcm_connection.rb +54 -0
- data/lib/ruby-push-notifications/gcm/gcm_error.rb +17 -0
- data/lib/ruby-push-notifications/gcm/gcm_notification.rb +31 -0
- data/lib/ruby-push-notifications/gcm/gcm_pusher.rb +35 -0
- data/lib/ruby-push-notifications/gcm/gcm_response.rb +89 -0
- data/lib/ruby-push-notifications/gcm/gcm_result.rb +52 -0
- data/lib/ruby-push-notifications/mpns.rb +5 -0
- data/lib/ruby-push-notifications/mpns/mpns_connection.rb +87 -0
- data/lib/ruby-push-notifications/mpns/mpns_notification.rb +94 -0
- data/lib/ruby-push-notifications/mpns/mpns_pusher.rb +33 -0
- data/lib/ruby-push-notifications/mpns/mpns_response.rb +82 -0
- data/lib/ruby-push-notifications/mpns/mpns_result.rb +182 -0
- data/lib/ruby-push-notifications/notification_results_manager.rb +14 -0
- data/lib/ruby-push-notifications/wns.rb +6 -0
- data/lib/ruby-push-notifications/wns/wns_access.rb +82 -0
- data/lib/ruby-push-notifications/wns/wns_connection.rb +97 -0
- data/lib/ruby-push-notifications/wns/wns_notification.rb +101 -0
- data/lib/ruby-push-notifications/wns/wns_pusher.rb +32 -0
- data/lib/ruby-push-notifications/wns/wns_response.rb +83 -0
- data/lib/ruby-push-notifications/wns/wns_result.rb +208 -0
- data/ruby-push-notifications.gemspec +28 -0
- data/spec/factories.rb +17 -0
- data/spec/factories/notifications.rb +30 -0
- data/spec/ruby-push-notifications/apns/apns_connection_spec.rb +92 -0
- data/spec/ruby-push-notifications/apns/apns_notification_spec.rb +42 -0
- data/spec/ruby-push-notifications/apns/apns_pusher_spec.rb +295 -0
- data/spec/ruby-push-notifications/gcm/gcm_connection_spec.rb +46 -0
- data/spec/ruby-push-notifications/gcm/gcm_notification_spec.rb +37 -0
- data/spec/ruby-push-notifications/gcm/gcm_pusher_spec.rb +45 -0
- data/spec/ruby-push-notifications/gcm/gcm_response_spec.rb +82 -0
- data/spec/ruby-push-notifications/mpns/mpns_connection_spec.rb +46 -0
- data/spec/ruby-push-notifications/mpns/mpns_notification_spec.rb +53 -0
- data/spec/ruby-push-notifications/mpns/mpns_pusher_spec.rb +59 -0
- data/spec/ruby-push-notifications/mpns/mpns_response_spec.rb +64 -0
- data/spec/ruby-push-notifications/wns/wns_access_spec.rb +76 -0
- data/spec/ruby-push-notifications/wns/wns_connection_spec.rb +53 -0
- data/spec/ruby-push-notifications/wns/wns_notification_spec.rb +177 -0
- data/spec/ruby-push-notifications/wns/wns_pusher_spec.rb +58 -0
- data/spec/ruby-push-notifications/wns/wns_response_spec.rb +65 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/dummy.pem +44 -0
- data/spec/support/factory_girl.rb +5 -0
- data/spec/support/results_shared_examples.rb +31 -0
- metadata +249 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module FCM
|
4
|
+
# Class that encapsulates the result of a single sent notification to a single
|
5
|
+
# Registration ID
|
6
|
+
# (https://developer.android.com/google/gcm/server-ref.html#table4)
|
7
|
+
# @author Carlos Alonso
|
8
|
+
class FCMResult ; end
|
9
|
+
|
10
|
+
# Indicates that the notification was successfully sent to the corresponding
|
11
|
+
# registration ID
|
12
|
+
class FCMResultOK < FCMResult
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
other.is_a?(FCMResultOK) || super(other)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Indicates that the notification was successfully sent to the corresponding
|
20
|
+
# registration ID but GCM sent a canonical ID for it, so the received canonical
|
21
|
+
# ID should be used as registration ID ASAP.
|
22
|
+
class FCMCanonicalIDResult < FCMResult
|
23
|
+
|
24
|
+
# @return [String]. The suggested canonical ID from GCM
|
25
|
+
attr_reader :canonical_id
|
26
|
+
|
27
|
+
def initialize(canonical_id)
|
28
|
+
@canonical_id = canonical_id
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
(other.is_a?(FCMCanonicalIDResult) && @canonical_id == other.canonical_id) ||
|
33
|
+
super(other)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# An error occurred sending the notification to the registration ID.
|
38
|
+
class FCMResultError < FCMResult
|
39
|
+
|
40
|
+
# @return [String]. The error sent by GCM
|
41
|
+
attr_accessor :error
|
42
|
+
|
43
|
+
def initialize(error)
|
44
|
+
@error = error
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
(other.is_a?(FCMResultError) && @error == other.error) || super(other)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'ruby-push-notifications/gcm/gcm_connection'
|
2
|
+
require 'ruby-push-notifications/gcm/gcm_notification'
|
3
|
+
require 'ruby-push-notifications/gcm/gcm_pusher'
|
4
|
+
require 'ruby-push-notifications/gcm/gcm_response'
|
5
|
+
require 'ruby-push-notifications/gcm/gcm_error'
|
6
|
+
require 'ruby-push-notifications/gcm/gcm_result'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module RubyPushNotifications
|
5
|
+
module GCM
|
6
|
+
# Encapsulates a connection to the GCM service
|
7
|
+
# Responsible for final connection with the service.
|
8
|
+
#
|
9
|
+
# @author Carlos Alonso
|
10
|
+
class GCMConnection
|
11
|
+
|
12
|
+
# @private The URL of the Android GCM endpoint
|
13
|
+
GCM_URL = 'https://android.googleapis.com/gcm/send'
|
14
|
+
|
15
|
+
# @private Content-Type HTTP Header string
|
16
|
+
CONTENT_TYPE_HEADER = 'Content-Type'
|
17
|
+
|
18
|
+
# @private Application/JSON content type
|
19
|
+
JSON_CONTENT_TYPE = 'application/json'
|
20
|
+
|
21
|
+
# @private Authorization HTTP Header String
|
22
|
+
AUTHORIZATION_HEADER = 'Authorization'
|
23
|
+
|
24
|
+
# Issues a POST request to the GCM send endpoint to
|
25
|
+
# submit the given notifications.
|
26
|
+
#
|
27
|
+
# @param notification [String]. The text to POST
|
28
|
+
# @param key [String]. The GCM sender id to use
|
29
|
+
# (https://developer.android.com/google/gcm/gcm.html#senderid)
|
30
|
+
# @param options [Hash] optional. Options for #post. Currently supports:
|
31
|
+
# * url [String]: URL of the GCM endpoint. Defaults to the official GCM URL.
|
32
|
+
# * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
|
33
|
+
# * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
|
34
|
+
# @return [GCMResponse]. The GCMResponse that encapsulates the received response
|
35
|
+
def self.post(notification, key, options = {})
|
36
|
+
headers = {
|
37
|
+
CONTENT_TYPE_HEADER => JSON_CONTENT_TYPE,
|
38
|
+
AUTHORIZATION_HEADER => "key=#{key}"
|
39
|
+
}
|
40
|
+
|
41
|
+
url = URI.parse options.fetch(:url, GCM_URL)
|
42
|
+
http = Net::HTTP.new url.host, url.port
|
43
|
+
http.use_ssl = url.scheme == 'https'
|
44
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
45
|
+
http.open_timeout = options.fetch(:open_timeout, 30)
|
46
|
+
http.read_timeout = options.fetch(:read_timeout, 30)
|
47
|
+
|
48
|
+
response = http.post url.path, notification, headers
|
49
|
+
|
50
|
+
GCMResponse.new response.code.to_i, response.body
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module GCM
|
4
|
+
# Base class for all GCM related errors
|
5
|
+
#
|
6
|
+
# @author Carlos Alonso
|
7
|
+
class GCMError < StandardError ; end
|
8
|
+
|
9
|
+
class MalformedGCMJSONError < GCMError ; end
|
10
|
+
|
11
|
+
class GCMAuthError < GCMError ; end
|
12
|
+
|
13
|
+
class GCMInternalError < GCMError ; end
|
14
|
+
|
15
|
+
class UnexpectedGCMResponseError < GCMError ; end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module GCM
|
4
|
+
# Encapsulates a GCM Notification.
|
5
|
+
# By default only Required fields are set.
|
6
|
+
# (https://developer.android.com/google/gcm/server-ref.html#send-downstream)
|
7
|
+
#
|
8
|
+
# @author Carlos Alonso
|
9
|
+
class GCMNotification
|
10
|
+
include RubyPushNotifications::NotificationResultsManager
|
11
|
+
|
12
|
+
# Initializes the notification
|
13
|
+
#
|
14
|
+
# @param [Array]. Array with the receiver's GCM registration ids.
|
15
|
+
# @param [Hash]. Payload to send.
|
16
|
+
def initialize(registration_ids, data)
|
17
|
+
@registration_ids = registration_ids
|
18
|
+
@data = data
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String]. The GCM's JSON format for the payload to send.
|
22
|
+
# (https://developer.android.com/google/gcm/server-ref.html#send-downstream)
|
23
|
+
def as_gcm_json
|
24
|
+
JSON.dump(
|
25
|
+
registration_ids: @registration_ids,
|
26
|
+
data: @data
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module GCM
|
4
|
+
|
5
|
+
# This class is responsible for sending notifications to the GCM service.
|
6
|
+
#
|
7
|
+
# @author Carlos Alonso
|
8
|
+
class GCMPusher
|
9
|
+
|
10
|
+
# Initializes the GCMPusher
|
11
|
+
#
|
12
|
+
# @param key [String]. GCM sender id to use
|
13
|
+
# ((https://developer.android.com/google/gcm/gcm.html#senderid))
|
14
|
+
# @param options [Hash] optional. Options for GCMPusher. Currently supports:
|
15
|
+
# * url [String]: URL of the GCM endpoint. Defaults to the official GCM URL.
|
16
|
+
# * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
|
17
|
+
# * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
|
18
|
+
def initialize(key, options = {})
|
19
|
+
@key = key
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
# Actually pushes the given notifications.
|
24
|
+
# Assigns every notification an array with the result of each
|
25
|
+
# individual notification.
|
26
|
+
#
|
27
|
+
# @param notifications [Array]. Array of GCMNotification to send.
|
28
|
+
def push(notifications)
|
29
|
+
notifications.each do |notif|
|
30
|
+
notif.results = GCMConnection.post notif.as_gcm_json, @key, @options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module GCM
|
4
|
+
|
5
|
+
# This class encapsulates a response received from the GCM service
|
6
|
+
# and helps parsing and understanding the received meesages/codes.
|
7
|
+
#
|
8
|
+
# @author Carlos Alonso
|
9
|
+
class GCMResponse
|
10
|
+
|
11
|
+
# @return [Integer] the number of successfully sent notifications
|
12
|
+
attr_reader :success
|
13
|
+
|
14
|
+
# @return [Integer] the number of failed notifications
|
15
|
+
attr_reader :failed
|
16
|
+
|
17
|
+
# @return [Integer] the number of received canonical IDS
|
18
|
+
# (https://developer.android.com/google/gcm/server-ref.html#table4)
|
19
|
+
attr_reader :canonical_ids
|
20
|
+
|
21
|
+
# @return [Array] Array of a GCMResult for every receiver of the notification
|
22
|
+
# sent indicating the result of the operation.
|
23
|
+
attr_reader :results
|
24
|
+
alias_method :individual_results, :results
|
25
|
+
|
26
|
+
# Initializes the GCMResponse and runs response parsing
|
27
|
+
#
|
28
|
+
# @param code [Integer]. The HTTP status code received
|
29
|
+
# @param body [String]. The response body received.
|
30
|
+
# @raise MalformedGCMJsonError if code == 400 Bad Request
|
31
|
+
# @raise GCMAuthError if code == 401 Unauthorized
|
32
|
+
# @raise GCMInternalError if code == 5xx
|
33
|
+
# @raise UnexpectedGCMResponseError if code != 200
|
34
|
+
def initialize(code, body)
|
35
|
+
case code
|
36
|
+
when 200
|
37
|
+
parse_response body
|
38
|
+
when 400
|
39
|
+
raise MalformedGCMJSONError, body
|
40
|
+
when 401
|
41
|
+
raise GCMAuthError, body
|
42
|
+
when 500..599
|
43
|
+
raise GCMInternalError, body
|
44
|
+
else
|
45
|
+
raise UnexpectedGCMResponseError, code
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
(other.is_a?(GCMResponse) &&
|
51
|
+
success == other.success &&
|
52
|
+
failed == other.failed &&
|
53
|
+
canonical_ids == other.canonical_ids &&
|
54
|
+
results == other.results) || super(other)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Parses the response extracting counts for successful, failed and
|
60
|
+
# containing canonical ID messages.
|
61
|
+
# Also creates the results array assigning a GCMResult subclass for each
|
62
|
+
# registration ID the notification was sent to.
|
63
|
+
#
|
64
|
+
# @param body [String]. The response body
|
65
|
+
def parse_response(body)
|
66
|
+
json = JSON.parse body, symbolize_names: true
|
67
|
+
@success = json[:success]
|
68
|
+
@failed = json[:failure]
|
69
|
+
@canonical_ids = json[:canonical_ids]
|
70
|
+
@results = (json[:results] || []).map { |result| gcm_result_for result }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Factory method that, for each GCM result object assigns a GCMResult
|
74
|
+
# subclass.
|
75
|
+
#
|
76
|
+
# @param result [Hash]. GCM Result parsed hash
|
77
|
+
# @return [GCMResult]. Corresponding GCMResult subclass
|
78
|
+
def gcm_result_for(result)
|
79
|
+
if canonical_id = result[:registration_id]
|
80
|
+
GCMCanonicalIDResult.new canonical_id
|
81
|
+
elsif error = result[:error]
|
82
|
+
GCMResultError.new error
|
83
|
+
else
|
84
|
+
GCMResultOK.new
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module GCM
|
4
|
+
# Class that encapsulates the result of a single sent notification to a single
|
5
|
+
# Registration ID
|
6
|
+
# (https://developer.android.com/google/gcm/server-ref.html#table4)
|
7
|
+
# @author Carlos Alonso
|
8
|
+
class GCMResult ; end
|
9
|
+
|
10
|
+
# Indicates that the notification was successfully sent to the corresponding
|
11
|
+
# registration ID
|
12
|
+
class GCMResultOK < GCMResult
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
other.is_a?(GCMResultOK) || super(other)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Indicates that the notification was successfully sent to the corresponding
|
20
|
+
# registration ID but GCM sent a canonical ID for it, so the received canonical
|
21
|
+
# ID should be used as registration ID ASAP.
|
22
|
+
class GCMCanonicalIDResult < GCMResult
|
23
|
+
|
24
|
+
# @return [String]. The suggested canonical ID from GCM
|
25
|
+
attr_reader :canonical_id
|
26
|
+
|
27
|
+
def initialize(canonical_id)
|
28
|
+
@canonical_id = canonical_id
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
(other.is_a?(GCMCanonicalIDResult) && @canonical_id == other.canonical_id) ||
|
33
|
+
super(other)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# An error occurred sending the notification to the registration ID.
|
38
|
+
class GCMResultError < GCMResult
|
39
|
+
|
40
|
+
# @return [String]. The error sent by GCM
|
41
|
+
attr_accessor :error
|
42
|
+
|
43
|
+
def initialize(error)
|
44
|
+
@error = error
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
(other.is_a?(GCMResultError) && @error == other.error) || super(other)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
require 'ruby-push-notifications/mpns/mpns_connection'
|
2
|
+
require 'ruby-push-notifications/mpns/mpns_notification'
|
3
|
+
require 'ruby-push-notifications/mpns/mpns_pusher'
|
4
|
+
require 'ruby-push-notifications/mpns/mpns_response'
|
5
|
+
require 'ruby-push-notifications/mpns/mpns_result'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module RubyPushNotifications
|
5
|
+
module MPNS
|
6
|
+
# Encapsulates a connection to the MPNS service
|
7
|
+
# Responsible for final connection with the service.
|
8
|
+
#
|
9
|
+
class MPNSConnection
|
10
|
+
|
11
|
+
# @private Content-Type HTTP Header string
|
12
|
+
CONTENT_TYPE_HEADER = 'Content-Type'
|
13
|
+
|
14
|
+
# @private text/xml content type
|
15
|
+
X_NOTIFICATION_CLASS = 'X-NotificationClass'
|
16
|
+
|
17
|
+
# @private Windows Phone Target
|
18
|
+
X_WINDOWSPHONE_TARGET = 'X-WindowsPhone-Target'
|
19
|
+
|
20
|
+
# @private text/xml content type
|
21
|
+
XML_CONTENT_TYPE = 'text/xml'
|
22
|
+
|
23
|
+
# @private Enumators for notification types
|
24
|
+
BASEBATCH = { tile: 1, toast: 2, raw: 3 }
|
25
|
+
|
26
|
+
# @private Enumators for delay
|
27
|
+
BATCHADDS = { delay450: 10, delay900: 20 }
|
28
|
+
|
29
|
+
# @private Windows Phone Target Types
|
30
|
+
WP_TARGETS = { toast: 'toast', tile: 'token' }
|
31
|
+
|
32
|
+
# Issues a POST request to the MPNS send endpoint to
|
33
|
+
# submit the given notifications.
|
34
|
+
#
|
35
|
+
# @param n [MPNSNotification]. The notification object to POST
|
36
|
+
# @param cert [String] optional. Contents of the PEM encoded certificate.
|
37
|
+
# @param options [Hash] optional. Options for GCMPusher. Currently supports:
|
38
|
+
# * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
|
39
|
+
# * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
|
40
|
+
# @return [Array]. The response of post
|
41
|
+
# (http://msdn.microsoft.com/pt-br/library/windows/apps/ff941099)
|
42
|
+
def self.post(n, cert = nil, options = {})
|
43
|
+
headers = build_headers(n.data[:type], n.data[:delay])
|
44
|
+
body = n.as_mpns_xml
|
45
|
+
responses = []
|
46
|
+
n.each_device do |url|
|
47
|
+
http = Net::HTTP.new url.host, url.port
|
48
|
+
http.open_timeout = options.fetch(:open_timeout, 30)
|
49
|
+
http.read_timeout = options.fetch(:read_timeout, 30)
|
50
|
+
if cert && url.scheme == 'https'
|
51
|
+
http.use_ssl = true
|
52
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
53
|
+
http.ca_file = cert
|
54
|
+
end
|
55
|
+
response = http.post url.path, body, headers
|
56
|
+
responses << { device_url: url.to_s, headers: extract_headers(response), code: response.code.to_i }
|
57
|
+
end
|
58
|
+
MPNSResponse.new responses
|
59
|
+
end
|
60
|
+
|
61
|
+
# Build Header based on type and delay
|
62
|
+
#
|
63
|
+
# @param type [Symbol]. The type of notification
|
64
|
+
# @param delay [Symbol]. The delay to be used
|
65
|
+
# @return [Hash]. Correct delay based on notification type
|
66
|
+
def self.build_headers(type, delay)
|
67
|
+
headers = {
|
68
|
+
CONTENT_TYPE_HEADER => XML_CONTENT_TYPE,
|
69
|
+
X_NOTIFICATION_CLASS => "#{(BASEBATCH[type] + (BATCHADDS[delay] || 0))}"
|
70
|
+
}
|
71
|
+
headers[X_WINDOWSPHONE_TARGET] = WP_TARGETS[type] unless type == :raw
|
72
|
+
headers
|
73
|
+
end
|
74
|
+
|
75
|
+
# Extract headers from response
|
76
|
+
# @param response [Net::HTTPResponse]. HTTP response for request
|
77
|
+
#
|
78
|
+
# @return [Hash]. Hash with headers with case-insensitive keys and string values
|
79
|
+
def self.extract_headers(response)
|
80
|
+
headers = {}
|
81
|
+
response.each_header { |k, v| headers[k] = v }
|
82
|
+
headers
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module RubyPushNotifications
|
4
|
+
module MPNS
|
5
|
+
# Encapsulates a MPNS Notification.
|
6
|
+
# Actually support for raw, toast, tiles notifications
|
7
|
+
# (http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202945)
|
8
|
+
#
|
9
|
+
class MPNSNotification
|
10
|
+
include RubyPushNotifications::NotificationResultsManager
|
11
|
+
|
12
|
+
# @return [Hash]. Payload to send.
|
13
|
+
# Toast :title => a bold message
|
14
|
+
# :message => the small message
|
15
|
+
# :param => a string parameter that is passed to the app
|
16
|
+
# Tile :image => a new image for the tile
|
17
|
+
# :count => a number to show on the tile
|
18
|
+
# :title => the new title of the tile
|
19
|
+
# :back_image => an image for the back of the tile
|
20
|
+
# :back_title => a title on the back of the tile
|
21
|
+
# :back_content => some content (text) for the back
|
22
|
+
# Raw :message => the full Hash message body
|
23
|
+
attr_reader :data
|
24
|
+
|
25
|
+
# @return [Array]. Array with the receiver's MPNS device URLs.
|
26
|
+
attr_reader :device_urls
|
27
|
+
|
28
|
+
# Initializes the notification
|
29
|
+
#
|
30
|
+
# @param [Array]. Array with the receiver's device urls.
|
31
|
+
# @param [Hash]. Payload to send.
|
32
|
+
# Toast :title => a bold message
|
33
|
+
# :message => the small message
|
34
|
+
# :param => a string parameter that is passed to the app
|
35
|
+
# Tile :image => a new image for the tile
|
36
|
+
# :count => a number to show on the tile
|
37
|
+
# :title => the new title of the tile
|
38
|
+
# :back_image => an image for the back of the tile
|
39
|
+
# :back_title => a title on the back of the tile
|
40
|
+
# :back_content => some content (text) for the back
|
41
|
+
# Raw :message => the full XML message body
|
42
|
+
def initialize(device_urls, data)
|
43
|
+
@device_urls = device_urls
|
44
|
+
@data = data
|
45
|
+
@data[:type] ||= :raw
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# @return [String]. The GCM's XML format for the payload to send.
|
50
|
+
# (http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202945)
|
51
|
+
def as_mpns_xml
|
52
|
+
xml = Builder::XmlMarkup.new
|
53
|
+
xml.instruct!
|
54
|
+
if data[:type] != :raw
|
55
|
+
xml.tag!('wp:Notification', 'xmlns:wp' => 'WPNotification') do
|
56
|
+
case data[:type]
|
57
|
+
when :toast
|
58
|
+
xml.tag!('wp:Toast') do
|
59
|
+
xml.tag!('wp:Text1') { xml.text!(data[:title]) }
|
60
|
+
xml.tag!('wp:Text2') { xml.text!(data[:message]) }
|
61
|
+
xml.tag!('wp:Param') { xml.text!(data[:param]) } if data[:param]
|
62
|
+
end
|
63
|
+
when :tile
|
64
|
+
xml.tag!('wp:Tile') do
|
65
|
+
xml.tag!('wp:BackgroundImage') { xml.text!(data[:image]) } if data[:image]
|
66
|
+
xml.tag!('wp:Count') { xml.text!(data[:count].to_s) } if data[:count]
|
67
|
+
xml.tag!('wp:Title') { xml.text!(data[:title]) } if data[:title]
|
68
|
+
xml.tag!('wp:BackBackgroundImage') { xml.text!(data[:back_image]) } if data[:back_image]
|
69
|
+
xml.tag!('wp:BackTitle') { xml.text!(data[:back_title]) } if data[:back_title]
|
70
|
+
xml.tag!('wp:BackContent') { xml.text!(data[:back_content]) } if data[:back_content]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
else
|
75
|
+
xml.root { build_hash(xml, data[:message]) }
|
76
|
+
end
|
77
|
+
xml.target!
|
78
|
+
end
|
79
|
+
|
80
|
+
def each_device
|
81
|
+
@device_urls.each do |url|
|
82
|
+
yield(URI.parse url)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_hash(xml, options)
|
87
|
+
options.each do |k, v|
|
88
|
+
xml.tag!(k.to_s) { v.is_a?(Hash) ? build_hash(xml, v) : xml.text!(v.to_s) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|