planbnet

Pinging the Apple Store using node.js

Every once in a while, Apple decides to take down their online store for a few hours to release some new shiny stuff. I’m not sure if they have to close the whole website because of the archaic WebObjects framework they’re still using or just to generate hype, but the yellow post-it note is well known across the Internet.

So, when there’s a new iPhone or iPad ready for sale, people keep hitting the refresh buttons of their browsers to be the first to place an order. I have to admin that I have also participated in this procedure once. But because I’m lazy (and I was at work at the time the sale started) I created a litte script that pings the Apple Store every few seconds and sends me a push notification using the great Boxcar service.

While this would have probably worked just as well in any other language, I chose node.js to implement this little script. Maybe someone else wants to use it, too, so I’m publishing it here. Please note that you may have to adjust some URLs to it works for your country (it’s currently set up for the German Apple Store)

var sys = require('util'),
fs = require('fs'),
http = require('http'),
https = require('https');

var BOXCAR_USER = 'BOXCAR_USERNAME';
var BOXCAR_PASSWORD = 'BOXCAR_PASSWORD';
var BOXCAR_AUTH = 'Basic ' + new Buffer(BOXCAR_USER + ':' + BOXCAR_PASSWORD).toString('base64');

function notify(app, title) {
  var url = "http://store.apple.com/de";
  var icon = "http://images.apple.com/euro/iphone/built-in-apps/images/nav_icon_app_store.png";
  var uniqueid = "notify"+(new Date().getTime());
  var params = {
      "notification[message]": title,
      "notification[from_screen_name]": app,
      "notification[from_remote_service_id]": uniqueid,
      "notification[source_url]": url,
      "notification[icon_url]": icon  
  };

  var dataArr = [];
  for (obj in params){
    if (params[obj]){
      dataArr.push(obj + '=' + params[obj]);  
    }
  }
  var postdata = dataArr.join('&');

  var headers = {'Host': 'boxcar.io',
    'Authorization': BOXCAR_AUTH,
      'Content-Length': postdata.length};

  var options = {
    host: 'boxcar.io',
    port: 80,
    path: '/notifications',
    method: 'POST',
    headers: headers
  };
  
  console.log("Sending request to boxcar: " + title);
  var req = http.request(options, function(res) {
      console.log("Sent notification for " + title);
  });
  req.write(postdata);
  req.end();
}

function refreshPage() {

  var headers = {
    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Charset':'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
    'Accept-Language':'de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4',
    'Cache-Control':'max-age=0',
    'Connection':'close',
    'Host':'store.apple.com',
    'User-Agent':'Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1'
  };

  console.log("Refresh " + new Date());
  var client = http.createClient(80, 'store.apple.com'); 
  var request = client.request('GET', '/de/', headers);
  request.end();
  request.on('response', function (response) {

    if (response.statusCode == 200) {
      var pagedata = "";
      response.setEncoding('utf8');

      response.on('data', function (chunk) {
        pagedata += chunk;
      });

      response.on('end', function() {
        if (!pagedata.match("title_backsoon1.gif") ) {
          console.log("success!");
          notify("Apple Store online!", "GOGOGO!!!!");
        } else {
          console.log("nothing...");
        }
      });

    } else {
      console.log('STATUS: ' + response.statusCode);
    }

  });
}

refreshPage();
var polling = setInterval ( refreshPage, 30000 );

That’s it, without much comment. It’s not very efficient and has to manually stopped when the store is back online, but it did help me to order my iPhone 4S without any lags (a minute later, the site went slow as hell). If you have questions, don’t hestitate to contact me.