Skip to content

Commit

Permalink
feat: add uk stores (#455)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewmackrodt authored Oct 6, 2020
1 parent ee8cb12 commit b9b6b55
Show file tree
Hide file tree
Showing 17 changed files with 695 additions and 21 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,20 +135,30 @@ Here is a list of variables that you can use to customize your newly copied `.en
| Amazon (CA) | `amazon-ca`|
| Amazon (DE) | `amazon-de`|
| Amazon (NL) | `amazon-nl`|
| Amazon (UK) | `amazon-uk`|
| Aria PC | `aria`|
| ASUS | `asus` |
| B&H | `bandh`|
| Best Buy | `bestbuy`|
| Best Buy (CA) | `bestbuy-ca`|
| Box | `box`|
| CCL | `ccl`|
| Currys | `currys`|
| eBuyer | `ebuyer`|
| EVGA | `evga`|
| EVGA (EU) | `evga-eu`|
| Gamestop | `gamestop`|
| Micro Center | `microcenter`|
| Newegg | `newegg`|
| Newegg (CA) | `newegg-ca`|
| Novatech | `novatech`|
| Nvidia | `nvidia`|
| Nvidia (API) | `nvidia-api`|
| Office Depot | `officedepot`|
| Overclockers | `overclockers`|
| PNY | `pny`|
| Scan | `scan`|
| Very | `very`|
| Zotac | `zotac`|

<details>
Expand Down
8 changes: 0 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {Stores} from './store/model';
import {adBlocker} from './adblocker';
import {config} from './config';
import {fetchLinks} from './store/fetch-links';
import {getSleepTime} from './util';
import {logger} from './logger';
import puppeteer from 'puppeteer-extra';
Expand Down Expand Up @@ -50,21 +49,14 @@ async function main() {
headless: config.browser.isHeadless
});

const promises = [];
for (const store of Stores) {
logger.debug('store links', {meta: {links: store.links}});
if (store.setupAction !== undefined) {
store.setupAction(browser);
}

if (store.linksBuilder) {
promises.push(fetchLinks(store, browser));
}

setTimeout(tryLookupAndLoop, getSleepTime(), browser, store);
}

await Promise.all(promises);
}

/**
Expand Down
16 changes: 10 additions & 6 deletions src/store/fetch-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {usingResponse} from '../util';

function addNewLinks(store: Store, links: Link[], series: Series) {
if (links.length === 0) {
logger.error(Print.message('NO STORE LINKS FOUND', series, store, true));
logger.warn(Print.message('NO STORE LINKS FOUND', series, store, true));

return;
}
Expand All @@ -30,16 +30,20 @@ export async function fetchLinks(store: Store, browser: Browser) {
return;
}

const promises = [];
const promises: Array<Promise<void>> = [];

for (const {series, url} of store.linksBuilder.urls) {
for (let {series, url} of store.linksBuilder.urls) {
if (!filterSeries(series)) {
continue;
}

logger.info(Print.message('DETECTING STORE LINKS', series, store, true));
logger.debug(Print.message('DETECTING STORE LINKS', series, store, true));

promises.push(usingResponse(browser, url, async response => {
if (!Array.isArray(url)) {
url = [url];
}

url.map(x => promises.push(usingResponse(browser, x, async response => {
const text = await response?.text();

if (!text) {
Expand All @@ -51,7 +55,7 @@ export async function fetchLinks(store: Store, browser: Browser) {
const links = store.linksBuilder!.builder(docElement, series);

addNewLinks(store, links, series);
}));
})));
}

await Promise.all(promises);
Expand Down
16 changes: 16 additions & 0 deletions src/store/lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {Selector, cardPriceLimit, pageIncludesLabels} from './includes-labels';
import {closePage, delay, getSleepTime, isStatusCodeInRange} from '../util';
import {config} from '../config';
import {disableBlockerInPage} from '../adblocker';
import {fetchLinks} from './fetch-links';
import {filterStoreLink} from './filter';
import open from 'open';
import {processBackoffDelay} from './model/helpers/backoff';
import {sendNotification} from '../notification';

const inStock: Record<string, boolean> = {};

const linkBuilderLastRunTimes: Record<string, number> = {};

/**
* Responsible for looking up information about a each product within
* a `Store`. It's important that we ignore `no-await-in-loop` here
Expand Down Expand Up @@ -165,6 +168,19 @@ async function lookupCardInStock(store: Store, page: Page, link: Link) {
}

export async function tryLookupAndLoop(browser: Browser, store: Store) {
if (store.linksBuilder) {
const lastRunTime = linkBuilderLastRunTimes[store.name] ?? -1;
const ttl = store.linksBuilder.ttl ?? Number.MAX_SAFE_INTEGER;
if (lastRunTime === -1 || (Date.now() - lastRunTime) > ttl) {
try {
await fetchLinks(store, browser);
linkBuilderLastRunTimes[store.name] = Date.now();
} catch (error) {
logger.error(error.message);
}
}
}

logger.debug(`[${store.name}] Starting lookup...`);
try {
await lookup(browser, store);
Expand Down
95 changes: 95 additions & 0 deletions src/store/model/amazon-uk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {Link, Store} from './store';
import {logger} from '../../logger';
import {parseCard} from './helpers/card';

export const AmazonUk: Store = {
backoffStatusCodes: [403, 429, 503],
labels: {
captcha: {
container: 'body',
text: ['enter the characters you see below']
},
inStock: {
container: '#availability',
text: ['in stock']
},
maxPrice: {
container: 'span[class*="PriceString"]'
},
outOfStock: [
{
container: '#availability',
text: ['out of stock', 'unavailable']
},
{
container: '#backInStock',
text: ['unavailable']
}
]
},
links: [
{
brand: 'test:brand',
cartUrl: 'https://www.amazon.co.uk/gp/aws/cart/add.html?ASIN.1=B081265T5Z&Quantity.1=1',
model: 'test:model',
series: 'test:series',
url: 'https://www.amazon.co.uk/dp/B081265T5Z/'
}
],
linksBuilder: {
builder: (docElement, series) => {
const productElements = docElement.find('.s-result-list .s-result-item[data-asin]');
const links: Link[] = [];
for (let i = 0; i < productElements.length; i++) {
const productElement = productElements.eq(i);
const asin = productElement.attr()['data-asin'];

if (!asin) {
continue;
}

const url = `https://www.amazon.co.uk/dp/${asin}/`;
const titleElement = productElement.find('.sg-col-inner h2 a.a-text-normal[href] span').first();
const title = titleElement.text().trim();

if (!title || !new RegExp(`RTX.*${series}`, 'i').exec(title)) {
continue;
}

const card = parseCard(title);

if (card) {
links.push({
brand: card.brand as any,
cartUrl: `https://www.amazon.co.uk/gp/aws/cart/add.html?ASIN.1=${asin}&Quantity.1=1`,
model: card.model,
series,
url
});
} else {
logger.error(`Failed to parse card: ${title}`);
}
}

return links;
},
ttl: 300000,
urls: [
{
series: '3080',
url: [
'https://www.amazon.co.uk/s?k=%2B%22RTX+3080%22+-2080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675291',
'https://www.amazon.co.uk/s?k=%2B%22RTX+3080%22+-2080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675594&page=2'
]
},
{
series: '3090',
url: [
'https://www.amazon.co.uk/s?k=%2B%22RTX+3090%22+-3080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675291',
'https://www.amazon.co.uk/s?k=%2B%22RTX+3090%22+-3080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675594&page=2'
]
}
]
},
name: 'amazon-uk'
};
42 changes: 42 additions & 0 deletions src/store/model/aria.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card';

export const Aria: Store = {
labels: {
inStock: {
container: '#addQuantity',
text: ['add to shopping basket']
},
outOfStock: {
container: '.fBox',
text: ['out of stock', 'there is currently no stock of this item']
}
},
links: [
{
brand: 'test:brand',
model: 'CARD',
series: 'test:series',
url: 'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+2060+Super/Gigabyte+NVIDIA+GeForce+RTX+2060+SUPER+8GB+WINDFORCE+OC+Turing+Graphics+Card+%2B+RTX+Bundle%21?productId=71541'
}
],
linksBuilder: {
builder: getProductLinksBuilder({
productsSelector: '#productListingInner .listTable .listTableTr',
sitePrefix: 'https://www.aria.co.uk',
titleSelector: 'strong > a[href]'
}),
urls: [
{
series: '3080',
url: 'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3080'
},
{
series: '3090',
url: 'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3090'
}
]
},
name: 'aria',
waitUntil: 'domcontentloaded'
};
45 changes: 45 additions & 0 deletions src/store/model/box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card';

export const Box: Store = {
labels: {
inStock: {
container: '#divBuyButton',
text: ['add to basket']
},
outOfStock: {
text: ['request stock alert', 'coming soon']
}
},
links: [
{
brand: 'test:brand',
model: 'CARD',
series: 'test:series',
url: 'https://www.box.co.uk/ASUS-TUF-GeForce-RTX-2060-6GB-Gaming-Gra_2669497.html'
}
],
linksBuilder: {
builder: getProductLinksBuilder({
productsSelector: '.products-right .p-list',
sitePrefix: 'https://www.box.co.uk',
titleSelector: '.p-list-section > h3 > a[href]'
}),
urls: [
{
series: '3070',
url: 'https://www.box.co.uk/rtx-3070-graphics-cards'
},
{
series: '3080',
url: 'https://www.box.co.uk/rtx-3080-graphics-cards'
},
{
series: '3090',
url: 'https://www.box.co.uk/rtx-3090-graphics-cards'
}
]
},
name: 'box',
waitUntil: 'domcontentloaded'
};
47 changes: 47 additions & 0 deletions src/store/model/ccl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card';

export const Ccl: Store = {
labels: {
inStock: {
container: '#pnlAddToBasket',
text: ['add to basket']
},
outOfStock: {
container: '#pnlSoldOut',
text: ['sold out', 'coming soon']
}
},
links: [
{
brand: 'test:brand',
model: 'CARD',
series: 'test:series',
url: 'https://www.cclonline.com/product/296443/RTX-2060-SUPER-VENTUS-GP-OC/Graphics-Cards/MSI-GeForce-RTX-2060-SUPER-VENTUS-GP-OC-8GB-Overclocked-Graphics-Card/VGA5671/'
}
],
linksBuilder: {
builder: getProductLinksBuilder({
productsSelector: '.productListingContainerOuter .productList',
sitePrefix: 'https://www.cclonline.com',
titleAttribute: 'title',
titleSelector: '.productList_Detail a[title]'
}),
urls: [
{
series: '3070',
url: 'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/GeForce-RTX-3070-Graphics-Cards/'
},
{
series: '3080',
url: 'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/GeForce-RTX-3080-Graphics-Cards/'
},
{
series: '3090',
url: 'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/GeForce-RTX-3090-Graphics-Cards/'
}
]
},
name: 'ccl',
waitUntil: 'domcontentloaded'
};
Loading

0 comments on commit b9b6b55

Please sign in to comment.