Navigate back to the homepage

Hacking my Honeymoon with Javascript 不

Alec Brunelle,
May 1st, 20192 min read

When my wife saw this post on Instagram, she was immediately hooked:

lady eating breakfast with giraffes
Instagram Link

With our honeymoon in Kenya on the horizon, we set out to book a room. Consulting my aunt who had been to Kenya years ago, she stayed here and had no difficulties booking. It came to our surprise when we heard that this place was fully booked a year or two in advance.

The sudden popularity had to stem from something. A little researched showed this place being recently Ellened.

Damn it, Ellen.

Initially, we checked their website to see if the dates we would be in Kenya were available, no luck. We then emailed the manor and again, no beauno, we were told we were put on their waitlist. Likely competing with other people on the waitlist, and our trip only being a few months away, me and my wifes hopes drew thin.

The search for solutions

The website they were using to show availability was read-only, no functionality to book rooms.

trying-my-hardest-to-click-on-readonly-elements

Calling and email were the only way to reach them, a slow and arduous process. I assumed that when a date became free, their website would update first and then they would start contacting waitlist members. This way, they would still get bookings if people fell through.

Assumptions

What I assumed next is that if we were to contact them the day the room became available, likely we would bypass the waitlist. But checking the website every hour was not going to fun.

I put my programmer pants on and thought that this would be a good use case for a good-ol web-scrapper, jazz hands. Hit the site every 30 min and SMS both my phone and my wifes so that we could give them a call. Unlikely that this 1990s Kenyan website had protection against bots.

What looked like a simple table turned out to be a simple table:

1// Example of a unbooked day HTML node
2
3<td
4 width="25"
5 unselectable="on"
6 ab="0"
7 style="border-top: none; "
8 name="WB15:Salas Camp:Keekorok Honeymoon
9 Tent-Tent 1:0*:1:11e8485f8b9898cc8de0ac1f6b165406:0"
10 id="WB15:07:28:2019"
11 darkness="0"
12 onmousedown="mouseDownFunction(arguments[0]);"
13 onmouseup="cMouseUp(arguments[0]);"
14 onmouseover="mouseOverFunction(arguments[0]);"
15 class="overbooking calIndicator0"
16>
17 1
18</td>

This is what I needed to find, if it the node text was 1, it was available.

After investigating the simple html structure, I started writing the Node.js service to scrap it. I stumbled upon an NPM module, crawler, which gave me all I needed out of the box.

1const Crawler = require("crawler");
2
3const startCrawler = async () => {
4 return new Promise(resolve => {
5 const c = new Crawler({
6 maxConnections: 10,
7 callback: (error, res, done) => {
8 if (error) {
9 console.log(error);
10 throw new Error(
11 `Error with sending request to website! ${JSON.stringify(error)}`
12 );
13 }
14 const $ = res.$;
15 // get the table of bookings
16 const results = $("#tblCalendar tbody tr").slice(12, 17);
17 done();
18 // return the results
19 resolve(results);
20 }
21 });
22 // hit giraffe manors website
23 c.queue(
24 "http://thesafaricollection.resrequest.com/reservation.php?20+2019-02-08" +
25 "+RS12:RS14:RS16:WB656:RS2274+15:20:30:25++WB5++n/a++true+true+0+0"
26 );
27 });
28};

This took a bit of debugging but now I had the HTML from Giraffe Manors website to play around with.

Next up, I went searching through the results with an NPM package called cheerio.

1const parseResults = async () => {
2 let availability = false;
3
4 // get HMTL
5 const results = await startCrawler();
6
7 for (let x = 0; x < results.length; x++) {
8 // Feb 13th - Feb 20th
9 const validDates = cheerio(results[x]).find("td").slice(7, 14);
10 // See if any of the dates are not booked
11 for (let y = 0; y < validDates.length; y++) {
12 if (parseInt(validDates[y].children[0].data, 10) === 1) {
13 availability = true;
14 }
15 }
16 }
17 ...

Now comes the interesting part, SMS text my wife when the room show as available. I used Twilio for this but many other services exist. This required setting up a free account, I know I wouldnt be sending more than a few SMS messages.

1...
2 // send text message if availability
3 if (availability) {
4 // Your Account Sid and Auth Token from twilio.com/console
5 const accountSid = process.env.ACCOUND_SID;
6 const authToken = process.env.AUTH_TOKEN;
7 const twilio = require("twilio");
8 const client = twilio(accountSid, authToken);
9
10 client.messages
11 .create({
12 body: "Giraffe manor is available for our dates!",
13 from: process.env.SMS_FROM,
14 to: process.env.SMS_TO
15 })
16 .then(message => console.log(`Sent a text! ${message.sid}`))
17 .done();
18 return;
19 }
20 console.log("No availability!");
21}

After testing with a few dates that were unbooked, it worked! Now to schedule it to run every 5 minutes (because why not?).

1const schedule = require("node-schedule");
2
3schedule.scheduleJob("*/5 * * * *", () => {
4 console.log("Running availability checker!");
5 try {
6 main();
7 } catch (e) {
8 console.log(`Error! ${JSON.stringify(e)}`);
9 }
10});

To host and run the code, I chose Heroku as I have experience with it and knew the free tier would work for what I needed. I have no idea how their free tier supports background service jobs but anyways.

A couple of weeks later, (I actually forgot it was running), my wife received the text to her phone! We called them immediately and got it! Seemingly bypassing the waitlist, just like we had hoped. She got a barrage of texts and used up my free tier on Twilio as I didnt write a stop method when it found an available room 不

I particularly liked doing this because its not often I code to solve a problem in my life but I thought it would be worth it for pictures like this:

me-and-zeena-giraffe-manor-pic

This was one example of how I used my programming skills for a real world problem. I would love to hear a problem you may have solved, comment here.

Here is the code if you want to take a peek

Join our email list and get notified about new content

Be the first to receive our latest content with the ability to opt-out at anytime. We promise to not spam your inbox or share your email with any third parties.

More articles from Alec Brunelle

How Learning Elixir Made Me a Better Programmer

After getting comfortable with a couple programming technologies, developers usually stop there.

August 20th, 20185 min read

The Best Parts of The Pragmatic Programmer

The Pragmatic Programmer remains relevant since its 1999 release.

March 11th, 20184 min read
20172020 Alec Brunelle
Link to $https://twitter.com/yourboybigalLink to $https://github.com/aleccool213Link to $https://www.linkedin.com/in/alecbrunelle/Link to $https://unsplash.com/@aleccool21Link to $https://medium.com/@yourboybigalLink to $https://stackoverflow.com/users/3287767/aleccool21