You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
4.0 KiB
JavaScript

import fetch from 'node-fetch';
import Aplay from 'node-aplay';
const url = 'https://www.bestbuy.com/gateway/graphql';
const headers = {
'Host': 'www.bestbuy.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0',
'Accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br, zstd',
'Referer': 'https://www.bestbuy.com/site/nvidia-geforce-rtx-5090-32gb-gddr7-graphics-card-dark-gun-metal/6614151.p?skuId=6614151',
'X-REQUEST-ID': 'add-on-selector',
'X-CLIENT-ID': 'ATTACH-accessories-in-variations',
'Content-Type': 'application/json',
'mode': 'cors',
'Origin': 'https://www.bestbuy.com',
'Connection': 'keep-alive',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'Priority': 'u=4',
'TE': 'trailers'
};
const skuIds = ["6614151","6578810","6616096","6617487","6614119","6614120","6616095","6616092","6616096","6614122","6616093","6616090","6616091","6615930","6615929","6615931"]
const queries = skuIds.map(skuId => ({
skuId,
query: `
query AOS_fetchButtonStateData($zip: String!, $store: String!) {
productBySkuId(skuId: "${skuId}") {
fulfillmentOptions(
input: {
buttonState: {context: PDP, destinationZipCode: $zip, storeId: $store},
shipping: {destinationZipCode: $zip},
inStorePickup: {storeId: $store}
}
) {
buttonStates {
buttonState
planButtonState
displayText
skuId
}
}
}
}
`
}));
//CHANGE YOUR STORES HERE
const params = [
{ zip: "97504", store: "2517" },
];
let lines = 0;
let found = false;
let winningResult = null;
function playAlarm() {
new Aplay('alarm.wav').play();
}
async function fetchGraphQL(zip, store, query) {
const body = {
query,
variables: { zip, store },
operationName: "AOS_fetchButtonStateData"
};
try {
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(body)
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return await response.json();
} catch (err) {
console.error(`Error fetching for zip ${zip}, store ${store} query ${query}:`, err);
return null;
}
}
async function pollStore(zip, store) {
console.log(`Starting poll for zip ${zip} and store ${store}...`);
while (!found) {
for (const { skuId, query } of queries) {
const data = await fetchGraphQL(zip, store, query);
if (
data &&
data.data &&
data.data.productBySkuId &&
data.data.productBySkuId.fulfillmentOptions &&
data.data.productBySkuId.fulfillmentOptions.buttonStates &&
data.data.productBySkuId.fulfillmentOptions.buttonStates.length > 0
) {
const buttonState = data.data.productBySkuId.fulfillmentOptions.buttonStates[0].buttonState;
console.log(`skuId: ${skuId} Store: ${store} Ship to: ${zip} -> buttonState: ${buttonState}`);
if (buttonState !== "SOLD_OUT") {
found = true;
winningResult = { skuId, zip, store, buttonState, data };
playAlarm()
return winningResult;
}
} else {
console.log(`Incomplete data for zip ${zip}, store ${store}.`);
}
lines++
if(lines > 30) {
lines = 0;
process.stdout.write('\x1B[3J\x1B[2J\x1B[H');
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
return null;
}
async function runAllPollers() {
const pollers = params.map(({ zip, store }) => pollStore(zip, store));
const result = await Promise.race(pollers);
console.log("Found a store with available button state:");
console.log(JSON.stringify(result, null, 2));
process.exit(0);
}
runAllPollers();