sasquatch 2013/Sasan Hezarkhani

How I [almost] hacked my Sasquatch2013 ticket

This post contains code. NO, DON’T GO AWAY!

Sasan “Gootik” Hezarkhani
4 min readAug 19, 2013

--

Back when I decided to go to Sasquatch it was a little too late and the tickets (wristbands really…) were sold out. A bunch of us decided to try and find tickets on Craigslist. However, new posts were getting sold as soon as they came up. Panic kicked in, I either had to stay on top of Craigslist and took time off work… or did some sort of magic to get my hands on tickets.

I am a programmer and there is no denying that I try to automate all aspects of my life. If it can be done by a computer, why should I spend MY time on it? Finding Craigslist posts and E-mailing people was what I wanted to automate.

Disclaimer

It is illegal to purchase or resell event tickets (unless otherwise stated), and you should not try what I did.

Finally there.
Side story:A bridge fell apart on our 6 hour drive from Vancouver to The Gorge.But nothing could stop us.

The Plan

This would be a simple, ugly program:

  • Go to Craigslist.
  • Search for Sasquatch ticket posts in Vancouver.
  • If there are new posts that are below my maximum budget, send the seller an E-mail.
  • Try again in ~2 minutes.

However, I decided against sending automatic E-mails; just because I didn’t want to be annoying and wanted to have some level of control over whom I send E-mails to. Instead, I wanted to get a text message on my phone as soon as a new posting came up. This way, I could be anywhere and did not have to worry about missing precious time looking for tickets.

Ingredients

I chose to write the entire thing in Node.js, because it’s a really fun language and it was nice to get away from the everyday language that I use at my work place.

I tried a few different libraries for scraping/processing HTML, but Cheerio was the perfect choice. I used Cheerio to read Craigslist’s HTML and scrape the important information that I needed.

Twilio is a great service that let’s you do all sorts of operations with a phone line. As a free member you get a single number in the region of your choice and $25 (this might have changed now) pre-filled in your account. This was great because each text message costs around ¢80 so I could send A LOT of messages before I ran out of free money.

Recipe

I decided to do a little bit more and wrapped the whole program in Express.js to add a nice little FrontEnd so that I could see what was going on.

All of my program is done in a single express route function. Using express I can refresh the page using HTML to get new posts, instead of running the program in a cron or on an JS interval.

We start by making a simple GET request to the Craigslist search and get a list of all posts using cheerio:

var SEARCH_URI = 'http://vancouver.en.craigslist.ca/search/?areaID=16&subAreaID=&query=sasquatch&catAbb=sss';request({
uri: SEARCH_URI
}, function(err, response, body){
if(response === undefined || err && response.statusCode !== 200) {
console.log(‘Request error.’);
}
var $ = cheerio.load(body);var cnt = 0;
$('.row').each(function() {
if(cnt > 20)
return;
cnt++;
var title = $(this).find('.pl a')
, link = title.attr('href')
, price = $(this).find('.l2 .pnr .pp .price').text()
, obj = {title: title, price: price, link: link}
;
DB.findByLink(link, function(err, item) {
if(item === null) {
if(parseInt(price.substring(1,price.length), 10) < 500 &&
title.text().toLowerCase().indexOf('wanted') == -1 &&
title.text().toLowerCase().indexOf('lf') == -1 &&
title.text().toLowerCase().indexOf('wtb') == -1) {
queueCnt++;
q.push({obj: obj, link: link}, function() {
console.log('DONE ' + link);
});
}
} else {
tix.push(item);
}
});
});
});

Once the page is loaded in cheerio (I’m using $ to refer to the page DOM) we loop through all the posts and get all the information we can from the page.

Because I wasn’t too worried about the database and wanted something really quick, DB is an in-memory database that I wrote that is an array with some wrapper functions in it.

If the link/post is not found in our DB, we add the post to be in our crawl queue (using q) otherwise it’s added to the final post list.

Here, I’m using the NPM package q to queue up all of the individual post crawls and then return the complete result set to the page/user once everything is crawled. My crawling function is as follows:

var $ = cheerio.load(body)
, tix = []
, numNew = 0
, q = async.queue(function (task, callback) {
fetch(task.link, function(a) {
var email = a(‘.dateReplyBar > a’).text()
, msecondUTC = a(‘.dateReplyBar date’).attr(‘title’)
;
task.obj.email = email;
task.obj.date = new Date(parseInt(msecondUTC, 10));
DB.save(task.obj, function(err, items) {
if(!FIRST) {
sendNewTicketMessage(task.obj, RECEIVING_NUMBER);
numNew++;
}
callback();
});
});
}, 2);
q.drain = function() {
DB.findAll(function(err, tix) {
res.render(‘sasquatch’, {tix: tix, numNew: numNew });
FIRST = false;
});
};

Here, the fetch function makes a GET request to the page and returns a cheerio instance of the page’s body. Once that’s done the sendNewTicketMessage function is called and the SMS is sent.

q.drain defines a function that is called once the queue is emptied. In this case we render the page with a list of all posts.

Did it work?

Yes. It worked perfectly. However, I did not end up going to the festival with a ticket found on Craigslist. A great person gave me a wristband, and I shall thank her forever. Maybe I’ll write a story on that too…

The Code

You can find my final code here on Github. Please keep in mind that it’s a code written in 35 minutes and looks pretty ugly.

--

--

Sasan “Gootik” Hezarkhani

A Hacker/Programmer currently working on real time bidding systems for Vungle. Used to do Games at Origin at Electronic Arts.