require 'PushRadar/version' require 'PushRadar/Targeting' require 'PushRadar/APIClient' require 'json' require 'digest/md5' module PushRadar # A realtime push notifications API service for the web, featuring advanced targeting class Radar # Creates a new instance of PushRadar with the specified API secret def initialize(api_secret) # Initialize data @data = {} # Reset the targeting options reset_targeting # Check that the API secret is a string unless api_secret.is_a?(String) raise 'The API secret must be a string value.' end # Trim the API secret api_secret.strip! # If we are running unit tests, set the unit test mode flag and skip the PushRadar API client initialisation if api_secret == 'test-secret' @is_unit_test_mode = true return else @is_unit_test_mode = false end # Check that the API secret starts with sk_ unless api_secret.start_with?('sk_') raise 'The PushRadar API secret provided is invalid. You can view your credentials from the API section of your dashboard (https://www.pushradar.com/app/api).' end # Make a new instance of a PushRadar API client with the specified API secret @api_client = APIClient.new(api_secret) end # Targets the notification to clients currently using the given browser def target_browser(browser) # Add the browser to the list of target browsers @targeting.target_browser browser # Allow method chaining self end # Targets the notification to clients currently using any of the given browsers def target_browsers(*browsers) # Target each browser browsers.each {|x| target_browser x } # Allow method chaining self end # Targets the notification to clients currently located in the given country def target_country(country_code) # Add the country to the list of target countries @targeting.target_country country_code # Allow method chaining self end # Targets the notification to clients currently located in any of the given countries def target_countries(*country_codes) # Target each country country_codes.each {|x| target_country x } # Allow method chaining self end # Targets the notification to clients currently located in Asia def target_asia # Target the continent @targeting.target_continent('AS') # Allow method chaining self end # Targets the notification to clients currently located in Africa def target_africa # Target the continent @targeting.target_continent('AF') # Allow method chaining self end # Targets the notification to clients currently located in Antarctica def target_antarctica # Target the continent @targeting.target_continent('AN') # Allow method chaining self end # Targets the notification to clients currently located in Europe def target_europe # Target the continent @targeting.target_continent('EU') # Allow method chaining self end # Targets the notification to clients currently located in North America def target_north_america # Target the continent @targeting.target_continent('NA') # Allow method chaining self end # Targets the notification to clients currently located in South America def target_south_america # Target the continent @targeting.target_continent('SA') # Allow method chaining self end # Targets the notification to clients currently located in Oceania def target_oceania # Target the continent @targeting.target_continent('OC') # Allow method chaining self end # Targets the notification to clients with the given IP address def target_ip(ip_address) # Add the IP address to the list of target IP addresses @targeting.target_ip ip_address # Allow method chaining self end # Targets the notification to clients with any of the given IP addresses def target_ips(*ip_addresses) # Target each IP address ip_addresses.each {|x| target_ip x } # Allow method chaining self end # Targets the notification to clients who have taken the given action def target_action(action) # Add the action to the list of target actions @targeting.target_action action # Allow method chaining self end # Targets the notification to clients who have taken any of the given actions def target_actions(*actions) # Target each action actions.each {|x| target_action x } # Allow method chaining self end # Targets the notification to clients who have not taken the given action def target_not_action(action) # Add the action to the list of target "not" actions @targeting.target_not_action action # Allow method chaining self end # Targets the notification to clients who have not taken any of the given actions def target_not_actions(*actions) # Target each action actions.each {|x| target_not_action x } # Allow method chaining self end # Targets the notification to a specific user (identified by their user ID) def target_user(user_id) # Target the user @targeting.target_user user_id # Allow method chaining self end # Targets the notification to specific users (identified by their user IDs) def target_users(*user_ids) # Target the user IDs user_ids.each {|x| target_user x } # Allow method chaining self end # Adds a data item to the list of data items def add_data_item(key, value) # Make sure the key is not empty if key == '' raise 'The key provided cannot be empty.' end # Add the data item unless @data.keys.include?(key) @data[key] = value end # Allow method chaining self end # Adds multiple data items to the list of data items def add_data_items(data = {}) # Add the data items data.keys.each {|x| add_data_item x, data[x] } # Allow method chaining self end # Broadcasts data on the channel specified def broadcast(channel, data = {}) # If we are running unit tests, throw an exception if @is_unit_test_mode raise 'Unit testing of the broadcast() method is not supported.' end # Trim the channel name channel.strip! # Check whether data has been provided if data.length == 0 && @data.length == 0 raise 'There is no data to broadcast.' end # Check whether the channel name contains spaces if channel.include? ' ' raise "The channel name cannot contain spaces. By convention, channel names are alphanumerical and lowercase, with optional dashes (e.g. 'test-channel')." end # Use the stored data if the data parameter is empty if data.length == 0 data = @data else data.keys.each {|x| @data[x] = data[x] } data = @data end # Initialize the hash of data to send to the server data_to_send = { notification: { channel: channel, data: data, userIDs: @targeting.instance_variable_get('@target_user_ids'), actions: @targeting.instance_variable_get('@target_actions'), notActions: @targeting.instance_variable_get('@target_not_actions'), continents: @targeting.instance_variable_get('@target_continents'), countries: @targeting.instance_variable_get('@target_countries'), ipAddresses: @targeting.instance_variable_get('@target_ips'), browsers: @targeting.instance_variable_get('@target_browsers'), }.to_json } # Broadcast the notification @api_client.post('/broadcast', data_to_send) # Reset the targeting options reset_targeting end def channel_auth(channel) # Trim the channel name channel.strip! unless (channel.start_with? "private-") || (channel.start_with? "presence-") raise "Channel authentication can only be used with private or presence channels." end # Generate the channel authentication token channel_auth_token = "channel_auth_token_" + Digest::MD5.hexdigest(channel) + "." + (0...8).map { (65 + rand(26)).chr }.join + ".0x" + (0...8).map { (65 + rand(26)).chr }.join # Broadcast the notification @api_client.post('/channel-auth', { authToken: channel_auth_token, channel: channel }) channel_auth_token end # Resets the targeting options def reset_targeting @targeting = Targeting.new end # Set which methods are private private :reset_targeting end end