1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
require "apnotic"
module Noticed
module DeliveryMethods
class Ios < DeliveryMethod
cattr_accessor :development_connection_pool, :production_connection_pool
required_options :bundle_identifier, :key_id, :team_id, :apns_key, :device_tokens
def deliver
evaluate_option(:device_tokens).each do |device_token|
apn = Apnotic::Notification.new(device_token)
format_notification(apn)
connection_pool = (!!evaluate_option(:development)) ? development_pool : production_pool
connection_pool.with do |connection|
response = connection.push(apn)
raise "Timeout sending iOS push notification" unless response
connection.close
if bad_token?(response) && config[:invalid_token]
# Allow notification to cleanup invalid iOS device tokens
notification.instance_exec(device_token, &config[:invalid_token])
elsif !response.ok?
raise "Request failed #{response.body}"
end
end
end
end
private
def format_notification(apn)
apn.topic = evaluate_option(:bundle_identifier)
method = config[:format]
# Call method on Notifier if defined
if method&.is_a?(Symbol) && event.respond_to?(method, true)
event.send(method, notification, apn)
# If Proc, evaluate it on the Notification
elsif method&.respond_to?(:call)
notification.instance_exec(apn, &method)
elsif notification.params.try(:has_key?, :message)
apn.alert = notification.params[:message]
else
raise ArgumentError, "No message for iOS delivery. Either include message in params or add the 'format' option in 'deliver_by :ios'."
end
end
def bad_token?(response)
response.status == "410" || (response.status == "400" && response.body["reason"] == "BadDeviceToken")
end
def development_pool
self.class.development_connection_pool ||= new_connection_pool(development: true)
end
def production_pool
self.class.production_connection_pool ||= new_connection_pool(development: false)
end
def new_connection_pool(development:)
handler = proc do |connection|
connection.on(:error) do |exception|
Rails.logger.info "Apnotic exception raised: #{exception}"
if config[:error_handler].respond_to?(:call)
notification.instance_exec(exception, &config[:error_handler])
end
end
end
if development
Apnotic::ConnectionPool.development(connection_pool_options, pool_options, &handler)
else
Apnotic::ConnectionPool.new(connection_pool_options, pool_options, &handler)
end
end
def connection_pool_options
{
auth_method: :token,
cert_path: StringIO.new(evaluate_option(:apns_key)),
key_id: evaluate_option(:key_id),
team_id: evaluate_option(:team_id)
}
end
def pool_options
{size: evaluate_option(:pool_size) || 5}
end
end
end
end
ActiveSupport.run_load_hooks :noticed_delivery_methods_ios, Noticed::DeliveryMethods::Ios
|