One of my hobbies is PC building, using the latest and greatest hardware to maximize performance. This past year, Nvidia released their most powerful gaming GPUs to date, the RTX 30 series. Unfortunately, the release of these GPUs was plagued with bots and scalpers who would buy up the entire stock the instant it was released. These graphics cards would then make their way onto sites such as eBay at a high markup. I really wanted to purchase a Founders Edition RTX 3090 for my PC in the fall in order to have a gaming rig capable of running Cyberpunk 2077 when it was released in December. This scenario presented a unique opportunity to explore the process of creating an online bot that would automatically purchase an item for me.
During the bot/scalping fiasco, Nvidia stopped selling the graphics card on their website. The merchandise was transferred to BestBuy in an effort to utilize a more robust online storefront that was better equipped to handle this type of demand. Since BestBuy was the only store with the merchandise I wanted, the focus of my bot was set. I started doing research into different ways of automatically manipulating an internet browser.
After doing some digging, I realized that the Selenium WebDriver was the best solution for me. It took me several weeks of tinkering to get it working just right, and here is how I did it. I first started writing the bot using Python, but decided that it would be more beneficial to get some experience working in C#. There were several challenges that needed to be sorted out. First, I needed to capture data from the BestBuy webpage and then use that to check if the card was in stock or not. The easiest way to accomplish this was to have my program read the text in the “Add to Cart” button. Obviously, if that button says “Sold Out” then the card is unavailable and the bot should refresh and check again. Selenium provides a handful of ways to search through the html of a webpage: ID, class name, name, and XPath. I was able to test these functions and make sure they were working properly by having my program print something like the button text to the console to ensure that it is reading properly. Out of the four search functions provided by Selenium, I found ID and XPath to be the most reliable. I think there may be duplicate class names or names within the html of the webpage I was using and these functions were not returning the correct values. I got my program to search for the “Add to Cart” button’s ID and save the text to a string variable. This allowed the bot to determine if the card was available or not.
After a lot of trial and error, I ended up building a few functions that I used throughout the program in order to easily get the bot to react properly to changes on the webpage. First, I made a function to implement the four different search criteria provided by Selenium, as mentioned previously. My function would accept a string with the search term, the necessary data to manipulate the browser, and an integer which indicated which type of search I would be using. I wrote a simple switch statement for each of the four search types, making it extremely easy to switch between search types as I was debugging other parts of the code. In order to account for problems like internet drops or the webpage refusing to load, I added a timer to this function that would automatically refresh the page after 7 seconds if nothing was found.
Since I would be running this bot all day long and would not be watching it all of the time, I needed to generate a log file so I could see if any errors occurred with the program while I was out. Since I needed to record different pieces of data at different stages of program execution, I wrote a function to open the log file, write whatever message I passed to it, and record the current time. This was one of the most beneficial components of the bot, as it allowed me to diagnose and fix any problems that occurred during the day. A few try-catch loops were built in as well as timers to refresh the page because of the issues I was made aware of by reading the log file. Below are a few clips from the log files that were generated during execution.
In order to maintain the number of checks that occur each day even if I have to temporarily stop the bot to fix something, I created an environment variable in my machine that would record the number of times the bot checked. Then, if I stopped and restarted the bot midday, the counter was able to pick up where it left off.
Unfortunately, I realized that there would be some antibot security measures in the checkout on BestBuy. Instead of trying to program my way through the checkout process, I instead built a system to automatically notify me when the card was in the cart so I could quickly complete the purchasing process. Figuring out how to do this was fairly tricky but I managed to kind of cheat in order to get it done.
Since I am already using the bot to manipulate a web browser, I realized I could also use it to access my personal email. One thing I didn’t know is that Gmail has some system in place that detects that a bot is attempting to use the account and prevents access. Fortunately, I have an old Yahoo email account that I could use. The benefit here being that Yahoo does not have the same security measures in place and I was able to get my bot to manipulate my Yahoo email account easily. Then, it was simply a matter of creating a new draft by finding and clicking the “Compose” button. Most major phone carriers allow emails to be sent as text messages. For example, I was able to send a message to my Verizon phone by setting the address as PHONENUMBER@vtext.com. Once I figured that out, it was simply a matter of filling in the necessary text fields and clicking the “Send” button. I built a function that would accept a subject and message, and would start a process on a separate thread to send the notification. Using a different thread for the notification meant that the portion of the bot checking BestBuy would be completely unaffected.
I set it up to spam my phone with texts as the bot was attempting to click the “Add to Cart” button. Spamming became necessary since I missed the first time the card was in my cart because I was in class. It ended up notifying me while I was out running and I completed the purchase a couple of miles from my house.
***PLEASE NOTE***
I only used this bot to purchase one graphics card for myself. The code has not and will not be shared with anyone.