Skip to content

Instantly share code, notes, and snippets.

@papaver
Created February 13, 2012 18:09
Show Gist options
  • Save papaver/1818741 to your computer and use it in GitHub Desktop.
Save papaver/1818741 to your computer and use it in GitHub Desktop.
Apple Push Notification Tester
#!/usr/bin/env ruby -w
#------------------------------------------------------------------------------
# requires
#------------------------------------------------------------------------------
require 'json'
require 'openssl'
require 'socket'
require 'yaml'
#------------------------------------------------------------------------------
# Default YAML Configuration
#------------------------------------------------------------------------------
default_config = {
:message => 'This is a test of the notification broadcast system.',
:badge => 1,
:dev => {
:cert => '/some/path',
:phrase =>'',
:tokens => ['<4ebb7e88 4dcccb1c 8e407ae3 eaed0650 8919066d a1b8b6af 62298351 38801311>'],
},
:prd => {
:cert => '/some/path',
:phrase => '',
:tokens => ['<4ebb7e88 4dcccb1c 8e407ae3 eaed0650 8919066d a1b8b6af 62298351 38801311>'],
}
}
#------------------------------------------------------------------------------
# Apple Push Notification Tester
#------------------------------------------------------------------------------
class Pusher
# apple push notification servers
@@apn_server = {
:dev => 'gateway.sandbox.push.apple.com',
:prd => 'gateway.push.apple.com',
:port => 2195
}
def initialize(mode, cert, phrase)
@options = {
:host => @@apn_server[mode],
:port => @@apn_server[:port],
:cert => cert,
:passphrase => phrase
}
end
# build apple json payload
def build_payload(message, badge)
{ :aps => { :alert => message,
:badge => badge,
:sound => 'default' } }.to_json
end
# convert notification token into hex format
def token_hex(token)
[token.scan(/\<(.+)\>/).first.first.delete(' ')].pack('H*')
end
# build apple notification packet
def build_simple_packet(token_hex, json)
packet = "\0\0 #{token_hex}\0#{json.length.chr}#{json}"
raise 'packet too large' if packet.size.to_i > 256
packet
end
# open a connection to the apple notification server
def open(options, &block)
# setup the ssl certificate
cert = File.read(options[:cert])
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = OpenSSL::PKey::RSA.new(cert, options[:passphrase])
ctx.cert = OpenSSL::X509::Certificate.new(cert)
# open socket to server
sock = TCPSocket.new(options[:host], options[:port])
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.sync = true
ssl.connect
# run block
yield ssl
# cleanup
ssl.close
sock.close
end
# send notification to server
def send_notification(token, message, badge)
token_hex = self.token_hex(token)
payload = self.build_payload(message, badge)
packet = self.build_simple_packet(token_hex, payload)
self.open(@options) do |connection|
connection.write packet
end
end
end
#------------------------------------------------------------------------------
# main
#------------------------------------------------------------------------------
config_yml = 'config.yml'
# check if config file exists, if not create a default config and let user
# know they need to fill out the config to use the script
unless File.exists?(config_yml)
File.open(config_yml, 'w') { |f| f.write(default_config.to_yaml) }
puts 'Fill out the config.yml, and re-run the script.'
exit
end
# select mode to run in
puts "Select Mode:"
puts " 1) development"
puts " 2) production:"
print ">> "
until [1, 2].include?(choice = gets.strip.to_i)
puts "Invalid entry. Please pick a valid mode."
end
# set the correct mode to run in
mode = [:dev, :prd][choice - 1]
# read in the config file
config = YAML::load(File.open(config_yml))
# send notifications out
pusher = Pusher.new(mode, config[mode][:cert], config[mode][:phrase])
config[mode][:tokens].each do |token|
pusher.send_notification(token, config[:message], config[:badge])
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment