Skip to content

Commit 0bde45f

Browse files
committed
Initial commit
1 parent bd7f009 commit 0bde45f

File tree

7 files changed

+269
-0
lines changed

7 files changed

+269
-0
lines changed

beep.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import beep from "beepbeep";
2+
3+
export default async function threeBeeps() {
4+
beep([1000, 500, 1000])
5+
}

main.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import costco from './stores/costco.js'
2+
import bestbuy from './stores/bestbuy.js'
3+
import amazon from './stores/amazon.js'
4+
5+
6+
// All the products to check
7+
// Current domains supported: Costco, Best Buy, Amazon,
8+
// Format: https://www.XXX.com/...
9+
const URLS = [
10+
// "https://www.bestbuy.com/site/amd-ryzen-9-5900x-4th-gen-12-core-24-threads-unlocked-desktop-processor-without-cooler/6438942.p?skuId=6438942",
11+
// "https://www.costco.com/sony-playstation-5-gaming-console-bundle.product.100691489.html",
12+
"https://www.amazon.com/gp/product/B08164VTWH/",
13+
// 'https://www.amazon.com/Coredy-Super-Strong-Automatic-Self-Charging-Medium-Pile/dp/B07NPNN57S'
14+
]
15+
16+
// How often to check for products. Too often may be dangerous, especially for Amazon.
17+
const INTERVAL = {
18+
unit: 'seconds', // seconds, m: minutes, h: hours
19+
value: 25
20+
}
21+
22+
23+
// https://www.XXX.com/... -> XXX
24+
function getDomainName(url) {
25+
let hostName = new URL(url).hostname;
26+
let host = hostName.split('.');
27+
return host[1];
28+
}
29+
30+
31+
// Calls the given store function with the set interval
32+
async function checkStore(storeFunc, url) {
33+
let timer;
34+
switch(INTERVAL.unit) {
35+
case 'seconds':
36+
timer = setInterval(storeFunc, INTERVAL.value * 1000, url, INTERVAL)
37+
break;
38+
39+
case 'minutes':
40+
timer = setInterval(storeFunc, INTERVAL.value * 1000 * 60, url, INTERVAL)
41+
break;
42+
43+
case 'hours':
44+
timer = setInterval(storeFunc, INTERVAL.value * 1000 * 60 * 60, url, INTERVAL)
45+
break;
46+
}
47+
}
48+
49+
50+
URLS.forEach(url => {
51+
let storeName;
52+
try {
53+
storeName = getDomainName(url);
54+
} catch(e) {
55+
console.error('Incorrect URL format:', url)
56+
console.error(e)
57+
}
58+
59+
switch(storeName) {
60+
case 'costco':
61+
checkStore(costco, url);
62+
break;
63+
64+
case 'bestbuy':
65+
checkStore(bestbuy, url);
66+
break;
67+
68+
case 'amazon':
69+
checkStore(amazon, url);
70+
break;
71+
72+
default:
73+
console.error('This store is not supported:', storeName)
74+
}
75+
})

package-lock.json

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "module",
3+
"dependencies": {
4+
"axios": "^0.21.0",
5+
"beepbeep": "^1.3.0",
6+
"dom-parser": "^0.1.6",
7+
"moment": "^2.29.1"
8+
}
9+
}

stores/amazon.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { fileURLToPath } from "url";
2+
import fs from "fs";
3+
import threeBeeps from "../beep.js"
4+
import axios from "axios";
5+
import moment from "moment";
6+
import DomParser from "dom-parser"; // https://www.npmjs.com/package/dom-parser
7+
8+
9+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
10+
let interval = {
11+
unit: 'seconds', // seconds, m: minutes, h: hours
12+
value: 30 // Amazon detects bots if too low, do > 10 seconds
13+
}
14+
let url = 'https://www.amazon.com/Coredy-Super-Strong-Automatic-Self-Charging-Medium-Pile/dp/B07NPNN57S'
15+
amazon(url, interval);
16+
}
17+
18+
19+
let firstRun = true;
20+
export default async function amazon(url, interval) {
21+
try {
22+
const { data } = await axios.get(url, {
23+
headers: {
24+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36'
25+
}
26+
}).catch(function (error) {
27+
console.log(error);
28+
});
29+
30+
let parser = new DomParser();
31+
let doc = parser.parseFromString(data, 'text/html');
32+
let title = doc.getElementById('productTitle').innerHTML.trim().slice(0, 150)
33+
let inventory = doc.getElementById('add-to-cart-button')
34+
35+
if (inventory != null) inventory = inventory.getAttribute('value')
36+
if (inventory != 'Add to Cart' && firstRun) {
37+
console.info(moment().format('LTS') + ': "' + title + '" not in stock at Amazon. Will keep retrying every', interval.value, interval.unit)
38+
firstRun = false;
39+
}
40+
else if (inventory != null && inventory == 'Add to Cart') {
41+
threeBeeps();
42+
console.info(moment().format('LTS') + ': ***** In Stock at Amazon *****: ', title);
43+
console.info(url);
44+
}
45+
} catch (e) {
46+
console.log('Unhandled error. Written to logAmazon.log')
47+
fs.writeFile('logAmazon.log', e, function(err, result) {
48+
if(err) console.log('File write error: ', err);
49+
});
50+
}
51+
};

stores/bestbuy.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { fileURLToPath } from "url";
2+
import fs from 'fs';
3+
import threeBeeps from "../beep.js"
4+
import axios from "axios";
5+
import moment from "moment";
6+
import DomParser from "dom-parser"; // https://www.npmjs.com/package/dom-parser
7+
8+
9+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
10+
let interval = {
11+
unit: 'seconds', // seconds, m: minutes, h: hours
12+
value: 5
13+
}
14+
let url = 'https://www.bestbuy.com/site/amd-ryzen-9-5900x-4th-gen-12-core-24-threads-unlocked-desktop-processor-without-cooler/6438942.p?skuId=6438942'
15+
bestbuy(url, interval);
16+
}
17+
18+
19+
let firstRun = true;
20+
export default async function bestbuy(url, interval) {
21+
try {
22+
var res = await axios.get(url);
23+
if (res.status === 200) {
24+
let parser = new DomParser();
25+
let doc = parser.parseFromString(res.data, 'text/html');
26+
let title = doc.getElementsByClassName('sku-title')[0].childNodes[0].textContent
27+
let inventory = doc.getElementsByClassName('btn btn-disabled btn-lg btn-block add-to-cart-button')[0].textContent
28+
if (inventory == 'Sold Out' && firstRun) {
29+
console.info(moment().format('LTS') + ': "' + title + '" not in stock at BestBuy. Will keep retrying every', interval.value, interval.unit)
30+
firstRun = false;
31+
}
32+
else if (inventory == 'Add to Cart') {
33+
threeBeeps();
34+
console.info(moment().format('LTS') + ': ***** In Stock at BestBuy *****: ', title);
35+
console.info(url);
36+
}
37+
} else {
38+
console.info(moment().format('LTS') + ': Error occured checking ' + title + '. Retrying in', interval.value, interval.unit)
39+
}
40+
41+
} catch (e) {
42+
console.log('Unhandled error. Written to logBestbuy.log')
43+
fs.writeFile('logBestbuy.log', e, function(err, result) {
44+
if(err) console.log('File write error: ', err);
45+
});
46+
}
47+
};

stores/costco.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { fileURLToPath } from "url";
2+
import fs from "fs";
3+
import threeBeeps from "../beep.js"
4+
import axios from "axios";
5+
import moment from "moment";
6+
import DomParser from "dom-parser"; // https://www.npmjs.com/package/dom-parser
7+
8+
9+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
10+
let interval = {
11+
unit: 'seconds', // seconds, m: minutes, h: hours
12+
value: 5
13+
}
14+
let url = 'https://www.costco.com/sony-playstation-5-gaming-console-bundle.product.100691489.html'
15+
costco(url, interval);
16+
}
17+
18+
19+
let firstRun = true;
20+
export default async function costco(url, interval) {
21+
try {
22+
var res = await axios.get(url);
23+
if (res.status === 200) {
24+
let parser = new DomParser();
25+
let doc = parser.parseFromString(res.data, 'text/html');
26+
let title = doc.getElementsByTagName('title')[0].innerHTML
27+
let inventory = doc.getElementById('add-to-cart-btn').getAttribute('value')
28+
29+
if (inventory == 'Out of Stock' && firstRun) {
30+
console.info(moment().format('LTS') + ': "' + title + '" not in stock at Costco. Will keep retrying every', interval.value, interval.unit)
31+
firstRun = false;
32+
}
33+
else if (inventory != 'Out of Stock') {
34+
threeBeeps();
35+
console.info(moment().format('LTS') + ': ***** In Stock at Costco *****: ', title);
36+
console.info(url);
37+
}
38+
} else {
39+
console.info(moment().format('LTS') + ': Error occured checking ' + title + '. Retrying in', interval.value, interval.unit)
40+
}
41+
42+
} catch (e) {
43+
console.log('Unhandled error. Written to logCostco.log')
44+
fs.writeFile('logCostco.log', e, function(err, result) {
45+
if(err) console.log('File write error: ', err);
46+
});
47+
}
48+
};

0 commit comments

Comments
 (0)