fcm-ruby-push-notifications 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +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
|