Skip to content

Instantly share code, notes, and snippets.

Last active February 18, 2016 15:53
Show Gist options
  • Save gdeglin/103a7d25a63f73ee9517 to your computer and use it in GitHub Desktop.
Save gdeglin/103a7d25a63f73ee9517 to your computer and use it in GitHub Desktop.
OneSignal Hacker News Web Push Notifications. OneSignal+Algolia
# This script runs every 5 minutes to send out Web Push
# notifications to subscribers about trending stories on Hacker News
# Technologies used:
# HTML5 Push API (
# OneSignal (Web Push Notification delivery and SDK)
# Algolia (Provider of an excellent hacker news Search API)
# Ruby, Redis, Sidekiq, Sidetiq
require 'redis'
require 'json'
require 'rest-client'
class DemoNotifications
sidekiq_options :retry => false
# We use a Sidekiq (A background worker service) with Sidetiq (A sidekiq plugin for scheduling jobs) to run this every 5 minutes
include Sidekiq::Worker
include Sidetiq::Schedulable
recurrence { hourly.minute_of_hour(0,5,10,15,20,25,30,35,40,45,50,55) }
def perform(last_occurrence, current_occurrence)
return unless Rails.env == 'production'
# We use redis to store and check if we've already delivered this story to subscribers
redis =
# Fetch stories created within the last 24 hours with at least 250 points
url = ">=250,created_at_i>=#{ - 60*60*24}"
puts "Fetching new story @ `#{url}`."
response = JSON.parse(RestClient.get(url))
# Sort stories by how many points they have
hits = response['hits'].sort_by { |h| h['points'] }
count = 0
hits.each do |hit|
unless redis.exists('hnstory:'+hit['objectID']) # Don't send the story to subscribers if we've already sent it
redis.setex('hnstory:'+hit['objectID'], 60*60*24*3, true) # Expire the data after a few days so we don't store it unnecessarily
puts "New top story: `#{hit['title']}`."
url = hit['url']
url = "{hit['objectID']}" if url.blank? # url is blank if it's a self post
"#{unixTimestampToRelativeTime(hit['created_at_i'])}, #{hit['points']} points, #{hit['num_comments']} comments",
count +=1
if (count == 0)
puts 'No new top stories.'
def create_notification(title, body, icon, url)
params = {
'app_id' => 'e3b80c8e-3b06-11e5-a05d-df3641e92340', # Our OneSignal App ID that visitors have subscribed to
'contents' => {'en'=> body},
'headings' => {'en'=> title},
'isChromeWeb' => true,
'included_segments' => ['All'],
'chrome_web_icon' => icon,
'url' => url
headers = {
'Content-Type' => 'application/json',
'Authorization' => 'Basic [secret]', # OneSiganel Rest API secret key
# Use the OneSignal API to send the story to all subscribers!
response = RestClient::Request.execute(
method: :post,
url: '',
payload: params,
headers: headers
puts "Send successful! \n\t #{response}"
rescue => e
puts e.inspect
def unixTimestampToRelativeTime(timestamp)
a = (
case a
when 0 then
'just now'
when 1 then
'a second ago'
when 2..59 then
a.to_s+' seconds ago'
when 60..119 then
'a minute ago' #120 = 2 minutes
when 120..3540 then
(a/60).to_i.to_s+' minutes ago'
when 3541..7100 then
'an hour ago' # 3600 = 1 hour
when 7101..82800 then
((a+99)/3600).to_i.to_s+' hours ago'
when 82801..172000 then
'a day ago' # 86400 = 1 day
when 172001..518400 then
((a+800)/(60*60*24)).to_i.to_s+' days ago'
when 518400..1036800 then
'a week ago'
((a+180000)/(60*60*24*7)).to_i.to_s+' weeks ago'
<script src="" async></script>
<link rel="manifest" href="manifest.json">
var OneSignal = OneSignal || [];
OneSignal.push(["init", {path: '/', appId: "[OneSignal App]", autoRegister: true}]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment