Storygraph Giveaway Bot
2024
As of 2024, if you navigate to cypress.io you'll see that the makers of Cypress (or their marketing team) proudly call out the following 3 reasons for why you should use it.
Automate
and Accelerate
are exactly what I managed to do with my unorthodox usage of the framework.
This all started when my friend broke her wrist. She offhandedly mentioned to me how she was unable to use her laptop, specifically her mouse and trackpad, until she was fully healed. Unfortunately, that meant she couldn't enter the monthly book giveaways on Storygraph. At first, I offered to enter them for her manually, but then I discovered she wanted to enter nearly 100 giveaways. To do so, I needed to click at least 3 links/buttons to enter a single giveaway, and this didn't include navigating back to the giveaway landing page to scroll to the next one, or entering different giveaway formats (digital, audio, or physical).
300 clicks (at minimum) wasn't going to break my wrist, but it was going to push me closer to developing RSI (Repetitive Strain Injury).
So I made a bot.
I've been following the developer of Storygraph, Nadia Odunayo, for a very long time on social media, and when she mentioned that Storygraph was developed with Tailwind, I thought "Neat!" and forgot about it for a few months. That was, until I needed to enter nearly 100 giveaways.
My very first thought when I started this project was to determine if I needed to reverse engineer any obfuscated CSS. If you peek at the CSS on most websites, you'll often see gibberish. This is generally accomplished via an obfuscator. I don't have many very kind thoughts towards this practice, but that's a story for another day.
Fortunately, Storygraph doesn't obfuscate their CSS 🎉! So I began sketching out a rough end to end user flow for entering giveaways.
Then I began translating this into Cypress, by predominantly using the get command. To do this, I referenced the relevant pages on Storygraph to find ids, if the element had any, or a series of unique classes that mapped to the component I wanted to click.
Here's an example of how I translated a list of requirements to a cypress flow to filter for digital and print giveaways in the US.
cy.visit('https://app.thestorygraph.com/giveaways')
cy.get('*[class^="toggle-filter-menu cursor-pointer hidden"]').click()
cy.get('[id^=format_print]').click()
cy.get('[id^=format_digital]').click()
Now all I had to do now was enter my filtered list of giveaways. To accomplish this, I gathered up every "View Giveaway" link on the page into a list, then iterated through them with a reusable individual giveway entry subprocess as shown below.
cy.contains('Enter giveaway').then(($giveawayButtons) => {
for (let j = 0; j < $giveawayButtons.length; j++) {
cy.contains('Enter giveaway').eq(0).click()
cy.get('*[class^="show-when-enabled"]').first().click()
}
})
Once these two core concepts (filtering and entering) were put together, I was able to enter giveaways for my friend! I had previously recorded myself taking about 10 seconds to enter each giveaway which meant I would need to spend ~16.66 minutes in total, whereas this script took about 5 seconds per entry for a total of ~8.33 minutes, during which I could be reading a book :D.
Here's a demo of the full bot in action!
During some of my rounds of testing, I had forgotten to filter down to only US giveaways, and accidentally entered one in Ireland. Of which I won 😨
Fortunately, the book publisher was happy to work with me and we donated the prize to local shop in Ireland 🎉