commit a337d0ad01961a5e96bdd49e675bfcac4d7440b9 Author: Kevin MacMartin Date: Wed Dec 16 20:44:37 2020 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00acce2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +package-lock.json +node_modules +config.json diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..beaecd0 --- /dev/null +++ b/config.example.json @@ -0,0 +1,10 @@ +{ + "username":"", + "password":"", + "cv2":"", + "item_page":"", + "refresh_time":"60", + "refresh_tries": "60", + "price_limit":"925", + "auto_submit":"true" +} diff --git a/newegg-purchaser.js b/newegg-purchaser.js new file mode 100755 index 0000000..2f95e6c --- /dev/null +++ b/newegg-purchaser.js @@ -0,0 +1,164 @@ +#!/usr/bin/env node + +const puppeteer = require("puppeteer"), + config = require("./config.json"); + +function log(message) { + console.log(`${new Date().toString().replace(/ GMT.*/, "")}: ${message}`); +} + +async function clickButtonWithText(page, text) { + const [ button ] = await page.$x(`//button[contains(., '${text}')]`); + + if (typeof button !== "undefined") { + await button.click(); + return true; + } else { + return false; + } +} + +async function run() { + const browser = await puppeteer.launch({ + headless: false, + product: "firefox", + defaultViewport: { width: 1440, height: 724 } + }); + + const page = await browser.newPage(), + maxVariance = 8; + + let success = true, + randomVariance = 0, + currentTry = 1; + + log("Started bot"); + + while (true) { + await page.goto("https://secure.newegg.ca/NewMyAccount/AccountLogin.aspx?nextpage=https%3a%2f%2fwww.newegg.ca%2f", { waitUntil: "load" }); + + if (page.url().includes("signin")) { + try { + await page.waitForSelector("button.btn.btn-orange", { timeout: 2500 }); + await page.type("#labeled-input-signEmail", config.username); + await page.click("button.btn.btn-orange"); + await page.waitForTimeout(1500); + + try { + await page.waitForSelector("#labeled-input-password", { timeout: 2500 }); + await page.waitForSelector("button.btn.btn-orange"); + await page.type("#labeled-input-password", config.password); + await page.click("button.btn.btn-orange"); + log("Successfully logged in"); + } catch (err) { + log("Fail: Manual authorization code required by Newegg. This should only happen once."); + success = false; + } + } catch (err) { + log("Fail: Login form missing"); + success = false; + } + + break; + } else { + await page.waitForTimeout(1000); + } + } + + if (success) { + await page.waitForTimeout(3500); + + while (true) { + await page.goto(`https://www.newegg.ca/${config.item_page}`, { waitUntil: "load" }); + + if (await clickButtonWithText(page, "Add to cart")) { + log("Adding the product the to cart"); + await page.waitForSelector(".modal-content", { timeout: 5000 }); + await page.waitForTimeout(1000); + + if (await clickButtonWithText(page, "No, thanks")) { + log("Rejected product coverage option"); + } + + try { + await page.waitForSelector(".message-title", { timeout: 5000 }); + log("Successfully added the item to the cart!"); + } catch (error) { + log("Fail: Added to cart but no success window appeared"); + success = false; + } + + break; + } else if (currentTry > config.refresh_tries) { + log(`Fail: Maximum number of refresh tries reached (${config.refresh_tries})`); + success = false; + break; + } else { + randomVariance = Math.floor(Math.random() * maxVariance); + log(`(${currentTry}) Product currently unavailable, retrying in ${Number(config.refresh_time) + randomVariance} seconds...`); + currentTry++; + await page.waitForTimeout((Number(config.refresh_time) + randomVariance) * 1000); + } + } + } + + if (success) { + while (true) { + log("Navigating to secure checkout"); + await page.goto("https://secure.newegg.ca/shop/cart", { waitUntil: "load" }); + + const totalPriceElement = await page.$(".summary-content-total span strong"), + totalPrice = await page.evaluate(totalPriceElement => totalPriceElement.textContent, totalPriceElement); + + if (typeof totalPrice === "undefined" || totalPrice === "" || totalPrice === null) { + log("Fail: Unable to determine total price"); + break; + } + + if (Number(totalPrice) > config.price_limit) { + log(`Fail: Total price of "${totalPrice}" exceeds price limit`); + break; + } + + if (await clickButtonWithText(page, "Secure Checkout")) { + await page.waitForTimeout(3000); + + if (await clickButtonWithText(page, "Continue to payment")) { + log("Continuing to payment..."); + await page.waitForSelector("[placeholder=\"CVV2\"]", { timeout: 2500 }); + await page.waitForTimeout(1000); + log("Filling out the cv2..."); + await page.type("[placeholder=\"CVV2\"]", config.cv2); + + if (await clickButtonWithText(page, "Review your order")) { + log("Reviewing order..."); + + if (config.auto_submit === "true") { + if (await clickButtonWithText(page, "Place Order")) { + log("Order placed!!!"); + } else { + log("Fail: Unable to press the Place Order button"); + } + } else { + log("The auto_submit config value is set to false"); + } + } else { + log("Fail: Unable to click the 'Review your order' button"); + } + + break; + } else { + await page.waitForTimeout(1000); + } + } else { + await page.waitForTimeout(1000); + } + } + } + + if (!success) { + await browser.close(); + } +} + +run(); diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..4e98f3b --- /dev/null +++ b/readme.md @@ -0,0 +1,24 @@ +# Setup + +## Install Dependencies + +``` +PUPPETEER_PRODUCT=firefox npm install puppeteer +``` + +## Setup Configuration + +1. `cp config.example.json config.json` +2. Edit `config.json`: + * `username`: newegg username + * `password`: newegg password + * `cv2`: code on the back of your credit card + * `item_page`: the part of the URL (starting with a slash) for the product after `https://www.newegg.ca` (eg: `/amd-ryzen-9-5900x/p/N82E16819113664`) + * `refresh_time`: amount of time between page refreshes (with a random variance) + * `refresh_tries`: the number of times the script will check before quitting (to avoid getting banned) + * `auto_submit`: set this to false if you want to do a test run (everything up to the actual purchase) + * `price_limit`: set this to a number you don't want your cart total to exceed before checking out + +## Credits + +Inspired by https://github.com/Ataraksia/NeweggBot