Skip to content

Instantly share code, notes, and snippets.

@scr4tchy
Created March 12, 2021 08:11
Show Gist options
  • Save scr4tchy/e6467f77545e81a5578e267df19d4600 to your computer and use it in GitHub Desktop.
Save scr4tchy/e6467f77545e81a5578e267df19d4600 to your computer and use it in GitHub Desktop.
ZZZZOTAC "Add to cart"
// ==UserScript==
// @name ZZZZOTAC "Add to cart"
// @namespace http://tampermonkey.net/
// @version 0.0.1
// @description Waits for a Zotac drop.. and adds to cart, from the product page, or from the product listing page (e.g. search, or category)
// @author Falcodrin Community - Mentalow#7935
// @match https://www.zotacstore.com/us/*
// @run-at document-start
// @grant GM.xmlHttpRequest
// @connect events.pagerduty.com
// ==/UserScript==
// Why?
// - In case ZZZotac drops overnight, or while you're having dinner with the parents.
// - Because Puppeteer & other headless scrapers are unable to go through the Cloudflare page.
//
// How to?
// - Install TamperMonkey in your browser
// - Click on the TamperMonkey icon in your browser's extension bar, and then "Create a new script"
// - Paste the code and save (CTRL+S does the trick)
// - Ensure that the productNames configuration variable fits your needs - it will filter the products based on whether the product name
// contains one of the strings specified.
//
// - If you'd like to be awaken by the script:
// - Sign up for for a free PagerDuty account,
// - Create a new service (Service -> Service Directory)
// - Setup an Integration for that service (Intregations -> Add a new integration -> Use our API directly: API Events v2)
// - Paste the integration key in the configuration below
// - Install the application on your phone, login, and enable the Push notifications
// - Ensure that you have a notification rule to immediately push by going to the notification rules page (My Profile -> Notification Rules)
// - Test the notification (especially with your phone in silent mode!) by going to the contact page (My Profile -> Contact Information -> Test)
// On certain devices, you may want to add PagerDuty's phones numbers and set a specific tone or DnD bypass (https://support.pagerduty.com/docs/notification-phone-numbers)
// - Test the end-to-end flow by adding a product that's in stock to the productNames and navigating to the product's page or to a listing that
// contains the product while the script is enabled
//
//
// - Navigate to the product pages you'd like to monitor, to a category, or to a search result's page
// If the page contains numerous items, you may increase the number of items shown on the page
// - A status bar will appear at the top of the page, confirming that the script is ready
// - As soon as the "Add to cart" button is detected, the script will click it, play a sound and you will be redirected to the cart
// If PagerDuty is configured, a notification will be sent prior to the redirection, allowing you to finalize the purchase (or try to, if Zzzotac is still up then..)
// Version Changelog
// 0.0.1 - First release. Credits to Akito for the banner style & original idea.
const scriptConfig = {
reloadInterval: 60, // Delay in seconds between page refreshes
productNames: ["2080", "3060", "3070", "3080", "3090"], // Filters the products that may trigger the "Add to cart" flow
pagerduty_key: "", // PagerDuty Integration key (Events v2 API) used to wake the hell outta you
};
const audio = new Audio("https://www.soundboard.com/handler/DownLoadTrack.ashx?cliptitle=retard+alert&filename=23/238537-a086a28a-e475-4933-9737-0d0c6b3f2480.mp3");
(async function() {
document.addEventListener("DOMContentLoaded", async function() {
// Setup banner
let {banner, statusInfo} = setupBanner();
document.body.append(banner);
if (document.querySelector(".product-sku") !== null) {
console.log(document.querySelectorAll(".product-sku"));
const productName = document.querySelector("div.product-name > span").innerHTML;
statusInfo.innerHTML = `👀 Patiently waiting for the ${productName}.`;
await tryCart(document.querySelector("div.main-container"), productName, statusInfo);
} else {
const products = Array.from(document.querySelector("div.category-products").querySelectorAll("div.product-details"))
.filter(e => containsSubstring(e.querySelector(".product-name > a").innerHTML, scriptConfig.productNames));
statusInfo.innerHTML = `👀 Patiently waiting for one of the ${products.length} items on this page to become available.`
products.forEach(function(product) {
tryCart(product, product.querySelector(".product-name > a").innerHTML, statusInfo);
});
}
// Get a noice timer going.
let timeLeft = scriptConfig.reloadInterval;
const statusInfoHTML = statusInfo.innerHTML;
while(timeLeft > 0) {
timeLeft--;
statusInfo.innerHTML = statusInfoHTML + `<br/>⏳&nbsp;&nbsp;Auto-reloading in ${timeLeft} seconds.`;
await sleep(1000);
}
window.location.reload(true); // skip cache
});
}());
async function tryCart(dom, productName, statusInfo) {
const addToCartButton = dom.querySelector("button.btn-cart");
if (!addToCartButton) {
return;
}
// FULL SEND -->
statusInfo.innerHTML = "💸 IT'S GO TIME!"
audio.play()
// Notify via PagerDuty!
if (scriptConfig.pagerduty_key) {
GM.xmlHttpRequest({
method: "POST",
url: "https://events.pagerduty.com/v2/enqueue",
headers: { "Content-Type": "application/json" },
data: JSON.stringify({
"routing_key": scriptConfig.pagerduty_key,
"event_action": "trigger",
"payload": {
"summary": `[Zotac drop] ${productName}`,
"source": window.location.href,
"severity": "critical"
}
})
});
}
// Good luck.
addToCartButton.click();
}
function setupBanner() {
// Initialize bottom banner for status + donation info
const banner = document.createElement("div");
banner.style.position = "absolute"; banner.style.top = "0px"; banner.style.zIndex = 100;
banner.style.width = "100%"; banner.style.padding = "6px"; banner.style.alignItems = "center";
banner.style.background = "#F9B61E";
banner.style.color = "#FFFFFF";
banner.style.fontFamily = "Verdana"; banner.style.fontSize = "16px";
banner.style.display = "flex"; banner.style.flexDirection = "row"; banner.style.justifyContent = "space-between";
// Initialize status info (left side of bottom banner)
const statusInfo = document.createElement("div");
statusInfo.style.textAlign = "left"; statusInfo.style.paddingLeft = "10px";
statusInfo.style.order = 0; statusInfo.style.flexBasis = "50%";
statusInfo.innerText = "🤦‍♂️ Waiting for Cloudflare";
// Initialize donation info (right side of bottom banner)
const donationInfo = document.createElement("div");
donationInfo.style.textAlign = "right"; donationInfo.style.paddingRight = "10px";
donationInfo.style.order = 1; donationInfo.style.flexBasis = "50%";
donationInfo.innerHTML = "Thank you! | BTC: bc1qmvx3nmxfp0rrz64l3dthh45a7gh3gpl47umqj5<br/>ETH: 0x722361E88b89Ec87A82F069c28D7AF01e47574e1";
banner.appendChild(statusInfo);
banner.appendChild(donationInfo);
return {banner, statusInfo};
}
function containsSubstring(needle, haystack) {
if(Array.isArray(haystack) === false) {
return false;
}
for(const hay of haystack) {
if(needle.includes(hay)) {
return true;
}
}
return false;
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment