diff --git a/package.json b/package.json index 624e32b..fc8b75d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ }, "husky": { "hooks": { - "pre-commit": "npm run lint && npm run build" + "pre-commit": "npm run lint" } } } diff --git a/src/__test__/notification-test.ts b/src/__test__/notification-test.ts index c5b25a6..c1a2e06 100644 --- a/src/__test__/notification-test.ts +++ b/src/__test__/notification-test.ts @@ -1,15 +1,26 @@ -import {Link} from '../store/model'; +import {Link, Store} from '../store/model'; import {sendNotification} from '../notification'; const link: Link = { - brand: 'brand', - cartUrl: 'http://example.com/', - model: 'model', - series: 'debug', - url: 'http://example.com/' + brand: 'test:brand', + cartUrl: 'test:cartUrl', + model: 'test:model', + series: 'test:series', + url: 'test:url' +}; + +const store: Store = { + labels: { + inStock: { + container: 'test:container', + text: ['test:text'] + } + }, + links: [link], + name: 'test:name' }; /** * Send test email. */ -sendNotification(link.cartUrl ?? link.url, link); +sendNotification(link, store); diff --git a/src/config.ts b/src/config.ts index 46ef22d..b44946f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -8,7 +8,7 @@ const browser = { isTrusted: process.env.BROWSER_TRUSTED ? process.env.BROWSER_TRUSTED === 'true' : false, maxSleep: Number(process.env.PAGE_SLEEP_MAX ?? 10000), minSleep: Number(process.env.PAGE_SLEEP_MIN ?? 5000), - open: process.env.OPEN_BROWSER === 'true' + open: process.env.OPEN_BROWSER ? process.env.OPEN_BROWSER === 'true' : true }; const logLevel = process.env.LOG_LEVEL ?? 'info'; diff --git a/src/index.ts b/src/index.ts index 73984fc..fec0073 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,7 +15,7 @@ puppeteer.use(adBlocker); */ async function main() { if (Stores.length === 0) { - Logger.error('No stores selected.'); + Logger.error('✖ no stores selected', Stores); return; } @@ -53,7 +53,6 @@ async function main() { try { void main(); } catch (error) { - // Ignoring errors; more than likely due to rate limits - Logger.error(error); + Logger.error('✖ something bad happened, resetting nvidia-snatcher', error); void main(); } diff --git a/src/logger.ts b/src/logger.ts index c736ba8..68fb432 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,3 +1,4 @@ +import {Link, Store} from './store/model'; import winston, {format} from 'winston'; import {Config} from './config'; @@ -22,3 +23,18 @@ export const Logger = winston.createLogger({ level: Config.logLevel, transports: [new winston.transports.Console({})] }); + +export const Print = { + captcha(link: Link, store: Store): string { + return `✖ [${store.name}] [${link.brand} (${link.series})] ${link.model} :: CAPTCHA`; + }, + inStock(link: Link, store: Store): string { + return `🚀🚨 [${store.name}] [${link.brand} (${link.series})] ${link.model} :: IN STOCK 🚨🚀`; + }, + outOfStock(link: Link, store: Store): string { + return `✖ [${store.name}] [${link.brand} (${link.series})] ${link.model} :: OUT OF STOCK`; + }, + rateLimit(link: Link, store: Store): string { + return `✖ [${store.name}] [${link.brand} (${link.series})] ${link.model} :: RATE LIMIT EXCEEDED`; + } +}; diff --git a/src/notification/desktop.ts b/src/notification/desktop.ts index efeb174..38de6fa 100644 --- a/src/notification/desktop.ts +++ b/src/notification/desktop.ts @@ -1,14 +1,14 @@ -import {Link} from '../store/model'; +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import notifier from 'node-notifier'; -export function sendDesktopNotification(cartUrl: string, link: Link) { +export function sendDesktopNotification(link: Link, store: Store) { (async () => { - const title = link.brand + ' ' + link.model + ' IN STOCK'; - const message = cartUrl; - notifier.notify({ - message, - title + message: link.cartUrl ? link.cartUrl : link.url, + title: Print.inStock(link, store) }); + + Logger.info('✔ desktop notification sent'); })(); } diff --git a/src/notification/discord.ts b/src/notification/discord.ts index abedf9c..3918f77 100644 --- a/src/notification/discord.ts +++ b/src/notification/discord.ts @@ -1,17 +1,18 @@ +import {Link, Store} from '../store/model'; import {MessageBuilder, Webhook} from 'discord-webhook-node'; import {Config} from '../config'; -import {Link} from '../store/model'; import {Logger} from '../logger'; const hook = new Webhook(Config.notifications.discord.webHookUrl); const notifyGroup = Config.notifications.discord.notifyGroup; -export function sendDiscordMessage(cartUrl: string, link: Link) { +export function sendDiscordMessage(link: Link, store: Store) { (async () => { try { const embed = new MessageBuilder(); embed.setTitle('Stock Notification'); - embed.addField('URL', cartUrl, true); + embed.addField('URL', link.cartUrl ? link.cartUrl : link.url, true); + embed.addField('Store', store.name, true); embed.addField('Brand', link.brand, true); embed.addField('Model', link.model, true); @@ -22,9 +23,10 @@ export function sendDiscordMessage(cartUrl: string, link: Link) { embed.setColor(0x76B900); embed.setTimestamp(); await hook.send(embed); - Logger.info(`↗ discord message sent: ${cartUrl}`); + + Logger.info('✔ discord message sent'); } catch (error) { - Logger.error(error); + Logger.error('✖ couldn\'t send discord message', error); } })(); } diff --git a/src/notification/email.ts b/src/notification/email.ts index d9245e4..d232c8d 100644 --- a/src/notification/email.ts +++ b/src/notification/email.ts @@ -1,11 +1,10 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Link} from '../store/model'; -import {Logger} from '../logger'; import Mail from 'nodemailer/lib/mailer'; import nodemailer from 'nodemailer'; const email = Config.notifications.email; -const subject = 'NVIDIA - BUY NOW'; const transporter = nodemailer.createTransport({ auth: { @@ -15,30 +14,25 @@ const transporter = nodemailer.createTransport({ service: 'gmail' }); -const mailOptions: Mail.Options = { - from: email.username, - subject, - to: email.username -}; - -export function sendEmail(cartUrl: string, link: Link) { - mailOptions.text = cartUrl; - - if (link.screenshot) { - mailOptions.attachments = [ +export function sendEmail(link: Link, store: Store) { + const mailOptions: Mail.Options = { + attachments: link.screenshot ? [ { filename: link.screenshot, path: `./${link.screenshot}` } - ]; - } + ] : undefined, + from: email.username, + subject: Print.inStock(link, store), + text: link.cartUrl ? link.cartUrl : link.url, + to: email.username + }; - transporter.sendMail(mailOptions, (error, info) => { + transporter.sendMail(mailOptions, error => { if (error) { - Logger.error(error); + Logger.error('✖ couldn\'t send email', error); } else { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - Logger.info(`↗ email sent: ${info.response}`); + Logger.info('✔ email sent'); } }); } diff --git a/src/notification/notification.ts b/src/notification/notification.ts index 68715a6..b4d7dfd 100644 --- a/src/notification/notification.ts +++ b/src/notification/notification.ts @@ -1,5 +1,6 @@ +import {Link, Store} from '../store/model'; import {Config} from '../config'; -import {Link} from '../store/model'; +import {Logger} from '../logger'; import {playSound} from './sound'; import {sendDesktopNotification} from './desktop'; import {sendDiscordMessage} from './discord'; @@ -13,44 +14,53 @@ import {sendTweet} from './twitter'; const notifications = Config.notifications; -export function sendNotification(cartUrl: string, link: Link) { +export function sendNotification(link: Link, store: Store) { if (notifications.email.username && notifications.email.password) { - sendEmail(cartUrl, link); - } - - if (notifications.slack.channel && notifications.slack.token) { - sendSlackMessage(cartUrl); - } - - if (notifications.telegram.accessToken && notifications.telegram.chatId) { - sendTelegramMessage(cartUrl); - } - - if (notifications.discord.webHookUrl) { - sendDiscordMessage(cartUrl, link); + Logger.debug('↗ sending email'); + sendEmail(link, store); } if (notifications.phone.number) { + Logger.debug('↗ sending sms'); const carrier = notifications.phone.carrier.toLowerCase(); if (carrier && notifications.phone.availableCarriers.has(carrier)) { - sendSMS(cartUrl, link); + sendSMS(link, store); } } - if (notifications.pushBulletApiKey) { - sendPushBulletNotification(cartUrl, link); - } - - if (notifications.pushover.token && notifications.pushover.username) { - sendPushoverNotification(cartUrl); - } - if (notifications.playSound) { + Logger.debug('↗ playing sound'); playSound(); } if (notifications.desktop) { - sendDesktopNotification(cartUrl, link); + Logger.debug('↗ sending desktop notification'); + sendDesktopNotification(link, store); + } + + if (notifications.discord.webHookUrl) { + Logger.debug('↗ sending discord message'); + sendDiscordMessage(link, store); + } + + if (notifications.slack.channel && notifications.slack.token) { + Logger.debug('↗ sending slack message'); + sendSlackMessage(link, store); + } + + if (notifications.telegram.accessToken && notifications.telegram.chatId) { + Logger.debug('↗ sending telegram message'); + sendTelegramMessage(link, store); + } + + if (notifications.pushBulletApiKey) { + Logger.debug('↗ sending pushbullet message'); + sendPushBulletNotification(link, store); + } + + if (notifications.pushover.token && notifications.pushover.username) { + Logger.debug('↗ sending pushover message'); + sendPushoverNotification(link, store); } if ( @@ -59,6 +69,7 @@ export function sendNotification(cartUrl: string, link: Link) { notifications.twitter.consumerKey && notifications.twitter.consumerSecret ) { - sendTweet(cartUrl, link); + Logger.debug('↗ sending twitter message'); + sendTweet(link, store); } } diff --git a/src/notification/pushbullet.ts b/src/notification/pushbullet.ts index f882748..522ca2b 100644 --- a/src/notification/pushbullet.ts +++ b/src/notification/pushbullet.ts @@ -1,19 +1,22 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Link} from '../store/model'; -import {Logger} from '../logger'; import PushBullet from 'pushbullet'; const pushBulletApiKey = Config.notifications.pushBulletApiKey; -export function sendPushBulletNotification(cartUrl: string, link: Link) { +export function sendPushBulletNotification(link: Link, store: Store) { const pusher = new PushBullet(pushBulletApiKey); - const title = `🚨 ${link.brand} ${link.model} ${link.series} 👀`; - pusher.note({}, title, cartUrl, (err: Error, result: string) => { - if (err) { - Logger.error(err); - } else { - Logger.info(`↗ pushbullet notification sent: ${result}`); - } - }); + pusher.note( + {}, + Print.inStock(link, store), + link.cartUrl ? link.cartUrl : link.url, + (error: Error) => { + if (error) { + Logger.error('✖ couldn\'t send pushbullet message', error); + } else { + Logger.info('✔ pushbullet message sent'); + } + }); } diff --git a/src/notification/pushover.ts b/src/notification/pushover.ts index c7da5b5..d038924 100644 --- a/src/notification/pushover.ts +++ b/src/notification/pushover.ts @@ -1,5 +1,6 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Logger} from '../logger'; import Push from 'pushover-notifications'; const pushover = Config.notifications.pushover; @@ -8,16 +9,17 @@ const push = new Push({ user: pushover.username }); -export function sendPushoverNotification(cartUrl: string) { +export function sendPushoverNotification(link: Link, store: Store) { const message = { - message: cartUrl + message: link.cartUrl ? link.cartUrl : link.url, + title: Print.inStock(link, store) }; - push.send(message, (err: Error, result: string) => { - if (err) { - Logger.error(err); + push.send(message, (error: Error) => { + if (error) { + Logger.error('✖ couldn\'t send pushover message', error); } else { - Logger.info(`↗ pushover notification sent: ${result}`); + Logger.info('✔ pushover message sent'); } }); } diff --git a/src/notification/slack.ts b/src/notification/slack.ts index 37918b9..c3b83e4 100644 --- a/src/notification/slack.ts +++ b/src/notification/slack.ts @@ -1,23 +1,30 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Logger} from '../logger'; import {WebClient} from '@slack/web-api'; const channel = Config.notifications.slack.channel; const token = Config.notifications.slack.token; const web = new WebClient(token); -export function sendSlackMessage(cartUrl: string) { +export function sendSlackMessage(link: Link, store: Store) { (async () => { + const givenUrl = link.cartUrl ? link.cartUrl : link.url; + try { - const result = await web.chat.postMessage({channel, text: cartUrl}); + const result = await web.chat.postMessage({ + channel, + text: `${Print.inStock(link, store)}\n${givenUrl}` + }); + if (!result.ok) { - Logger.error(result.error); + Logger.error('✖ couldn\'t send slack message', result); return; } - Logger.info(`↗ slack message sent to '${channel}': ${cartUrl}`); + Logger.info('✔ slack message sent'); } catch (error) { - Logger.error(error); + Logger.error('✖ couldn\'t send slack message', error); } })(); } diff --git a/src/notification/sms.ts b/src/notification/sms.ts index c0c289a..8959735 100644 --- a/src/notification/sms.ts +++ b/src/notification/sms.ts @@ -1,10 +1,9 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Link} from '../store/model'; -import {Logger} from '../logger'; import Mail from 'nodemailer/lib/mailer'; import nodemailer from 'nodemailer'; -const subject = 'NVIDIA - BUY NOW'; const [email, phone] = [Config.notifications.email, Config.notifications.phone]; const transporter = nodemailer.createTransport({ @@ -15,37 +14,35 @@ const transporter = nodemailer.createTransport({ service: 'gmail' }); -const mailOptions: Mail.Options = { - from: Config.notifications.email.username, - subject, - to: generateAddress() -}; - -export function sendSMS(text: string, link: Link) { - mailOptions.text = text; - - if (link.screenshot) { - mailOptions.attachments = [ +export function sendSMS(link: Link, store: Store) { + const mailOptions: Mail.Options = { + attachments: link.screenshot ? [ { filename: link.screenshot, path: `./${link.screenshot}` } - ]; - } + ] : undefined, + from: email.username, + subject: Print.inStock(link, store), + text: link.cartUrl ? link.cartUrl : link.url, + to: generateAddress() + }; - transporter.sendMail(mailOptions, (error, info) => { + transporter.sendMail(mailOptions, error => { if (error) { - Logger.error(error); + Logger.error('✖ couldn\'t send sms', error); } else { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - Logger.info(`↗ sms sent: ${info.response}`); + Logger.info('✔ sms sent'); } }); } function generateAddress() { const carrier = phone.carrier.toLowerCase(); + if (carrier && phone.availableCarriers.has(carrier)) { return [phone.number, phone.availableCarriers.get(carrier)].join('@'); } + + Logger.error('✖ unknown carrier', carrier); } diff --git a/src/notification/sound.ts b/src/notification/sound.ts index 26a9165..5227698 100644 --- a/src/notification/sound.ts +++ b/src/notification/sound.ts @@ -5,29 +5,29 @@ import playerLib from 'play-sound'; const notificationSound = Config.notifications.playSound; -Logger.info('Searching for sound player...'); +Logger.info('ℹ searching for sound player...'); const player = playerLib(); if (player.player === null) { Logger.warn('No sound player found.'); } else { const playerName: string = player.player; - Logger.info(`Sound player found: ${playerName}`); + Logger.info(`✔ sound player found: ${playerName}`); } export function playSound() { // Check if file exists - fs.access(notificationSound, fs.constants.F_OK, err => { - if (err) { - Logger.error(`error opening sound file: ${err.message}`); + fs.access(notificationSound, fs.constants.F_OK, error => { + if (error) { + Logger.error(`✖ error opening sound file: ${error.message}`); return; } - player.play(notificationSound, (err: string) => { - Logger.info('↗ playing sound'); - - if (err) { - Logger.error(`error playing sound: ${err}`); + player.play(notificationSound, (error: Error) => { + if (error) { + Logger.error('✖ couldn\'t play sound', error); } + + Logger.info('✔ played sound'); }); }); } diff --git a/src/notification/telegram.ts b/src/notification/telegram.ts index 249945b..bd67698 100644 --- a/src/notification/telegram.ts +++ b/src/notification/telegram.ts @@ -1,5 +1,6 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Logger} from '../logger'; import {TelegramClient} from 'messaging-api-telegram'; const telegram = Config.notifications.telegram; @@ -8,13 +9,15 @@ const client = new TelegramClient({ accessToken: telegram.accessToken }); -export function sendTelegramMessage(text: string) { +export function sendTelegramMessage(link: Link, store: Store) { (async () => { + const givenUrl = link.cartUrl ? link.cartUrl : link.url; + try { - await client.sendMessage(telegram.chatId, text); - Logger.info(`↗ telegram message sent to '${telegram.chatId}': ${text}`); + await client.sendMessage(telegram.chatId, `${Print.inStock(link, store)}\n${givenUrl}`); + Logger.info('✔ telegram message sent'); } catch (error) { - Logger.error(error); + Logger.error('✖ couldn\'t send telegram message', error); } })(); } diff --git a/src/notification/twitter.ts b/src/notification/twitter.ts index a701295..9d10a4d 100644 --- a/src/notification/twitter.ts +++ b/src/notification/twitter.ts @@ -1,6 +1,6 @@ +import {Link, Store} from '../store/model'; +import {Logger, Print} from '../logger'; import {Config} from '../config'; -import {Link} from '../store/model'; -import {Logger} from '../logger'; import Twitter from 'twitter'; const twitter = Config.notifications.twitter; @@ -12,18 +12,18 @@ const client = new Twitter({ consumer_secret: twitter.consumerSecret }); -export function sendTweet(cartUrl: string, link: Link) { - let status = `🛎️ Stock Notification: ${link.brand} ${link.model}\n${cartUrl}`; +export function sendTweet(link: Link, store: Store) { + let status = `${Print.inStock(link, store)}\n${link.cartUrl ? link.cartUrl : link.url}`; if (twitter.tweetTags) { status += `\n\n${twitter.tweetTags}`; } - client.post('statuses/update', {status}, err => { - if (err) { - Logger.error(err); + client.post('statuses/update', {status}, error => { + if (error) { + Logger.error('✖ couldn\'t send twitter notification', error); } else { - Logger.info(`↗ twitter notification sent: ${cartUrl}`); + Logger.info('✔ twitter notification sent'); } }); } diff --git a/src/store/lookup.ts b/src/store/lookup.ts index 24b080a..abd323f 100644 --- a/src/store/lookup.ts +++ b/src/store/lookup.ts @@ -1,8 +1,8 @@ import {Browser, Page, Response} from 'puppeteer'; import {Link, Store} from './model'; +import {Logger, Print} from '../logger'; import {closePage, delay, getSleepTime} from '../util'; import {Config} from '../config'; -import {Logger} from '../logger'; import {includesLabels} from './includes-labels'; import open from 'open'; import {sendNotification} from '../notification'; @@ -69,27 +69,12 @@ async function lookup(browser: Browser, store: Store) { } async function lookupCard(browser: Browser, store: Store, page: Page, link: Link) { - const givenWaitFor = store.customWaitFor ? store.customWaitFor : 'networkidle0'; + const givenWaitFor = store.waitUntil ? store.waitUntil : 'networkidle0'; const response: Response | null = await page.goto(link.url, {waitUntil: givenWaitFor}); - const graphicsCard = `${link.brand} ${link.model}`; if (await lookupCardInStock(store, page)) { - Logger.info(`🚀🚀🚀 [${store.name}] ${graphicsCard} IN STOCK 🚀🚀🚀`); - Logger.info(link.url); - if (Config.page.inStockWaitTime) { - inStock[store.name] = true; - setTimeout(() => { - inStock[store.name] = false; - }, 1000 * Config.page.inStockWaitTime); - } - - if (Config.page.capture) { - Logger.debug('ℹ saving screenshot'); - link.screenshot = `success-${Date.now()}.png`; - await page.screenshot({path: link.screenshot}); - } - let givenUrl = link.cartUrl ? link.cartUrl : link.url; + Logger.info(`${Print.inStock(link, store)}\n${givenUrl}`); if (Config.browser.open) { if (link.openCartAction === undefined) { @@ -99,22 +84,38 @@ async function lookupCard(browser: Browser, store: Store, page: Page, link: Link } } - sendNotification(givenUrl, link); + sendNotification(link, store); + + if (Config.page.inStockWaitTime) { + inStock[store.name] = true; + + setTimeout(() => { + inStock[store.name] = false; + }, 1000 * Config.page.inStockWaitTime); + } + + if (Config.page.capture) { + Logger.debug('ℹ saving screenshot'); + + link.screenshot = `success-${Date.now()}.png`; + await page.screenshot({path: link.screenshot}); + } + return; } if (await lookupPageHasCaptcha(store, page)) { - Logger.warn(`✖ [${store.name}] CAPTCHA from: ${graphicsCard}. Waiting for a bit with this store...`); + Logger.warn(Print.captcha(link, store)); await delay(getSleepTime()); return; } if (response && response.status() === 429) { - Logger.warn(`✖ [${store.name}] Rate limit exceeded: ${graphicsCard}`); + Logger.warn(Print.rateLimit(link, store)); return; } - Logger.info(`✖ [${store.name}] still out of stock: ${graphicsCard}`); + Logger.info(Print.outOfStock(link, store)); } async function lookupCardInStock(store: Store, page: Page) { diff --git a/src/store/model/bestbuy-ca.ts b/src/store/model/bestbuy-ca.ts index daacd4b..36dbd87 100644 --- a/src/store/model/bestbuy-ca.ts +++ b/src/store/model/bestbuy-ca.ts @@ -1,7 +1,6 @@ import {Store} from './store'; export const BestBuyCa: Store = { - customWaitFor: 'domcontentloaded', labels: { inStock: { container: '#root', @@ -22,5 +21,6 @@ export const BestBuyCa: Store = { url: 'https://www.bestbuy.ca/en-ca/product/zotac-geforce-rtx-3080-trinity-10gb-gddr6x-video-card/14953249?intl=nosplash' } ], - name: 'bestbuy-ca' + name: 'bestbuy-ca', + waitUntil: 'domcontentloaded' }; diff --git a/src/store/model/helpers/nvidia.ts b/src/store/model/helpers/nvidia.ts index 1c12360..7f63898 100644 --- a/src/store/model/helpers/nvidia.ts +++ b/src/store/model/helpers/nvidia.ts @@ -55,26 +55,29 @@ export function generateSetupAction() { const page = await browser.newPage(); - Logger.info('[nvidia] creating cart/session token...'); let response: Response | null; try { + Logger.debug('creating cart/session token...'); + response = await page.goto(nvidiaSessionUrl(nvidiaLocale), {waitUntil: 'networkidle0'}); + if (response === null) { throw new Error('NvidiaAccessTokenUnavailable'); } const data = await response.json() as NvidiaSessionTokenJSON; const accessToken = data.access_token; - - Logger.info('[nvidia] you can log into your cart now...'); const cartUrl = checkoutUrl(drLocale, accessToken); - Logger.info(cartUrl); + + Logger.debug(cartUrl); + if (Config.browser.open) { + Logger.info('ℹ opening browser for user to login'); + await open(cartUrl); } } catch (error) { - Logger.debug(error); - Logger.error('✖ [nvidia] cannot generate cart/session token, continuing without, auto-"add to cart" may not work...'); + Logger.error('✖ [nvidia] cannot generate cart/session token, continuing without; auto "add to cart" may not work', error); } await page.close(); @@ -84,11 +87,14 @@ export function generateSetupAction() { export function generateOpenCartAction(id: number, nvidiaLocale: string, drLocale: string, cardName: string) { return async (browser: Browser) => { const page = await browser.newPage(); - Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, starting auto add to cart... 🚀🚀🚀`); + + Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, starting auto add to cart 🚀🚀🚀`); + let response: Response | null; let cartUrl: string; try { - Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, getting access token... 🚀🚀🚀`); + Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, getting access token 🚀🚀🚀`); + response = await page.goto(nvidiaSessionUrl(nvidiaLocale), {waitUntil: 'networkidle0'}); if (response === null) { throw new Error('NvidiaAccessTokenUnavailable'); @@ -97,16 +103,21 @@ export function generateOpenCartAction(id: number, nvidiaLocale: string, drLocal const data = await response.json() as NvidiaSessionTokenJSON; const accessToken = data.access_token; - Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, adding to cart... 🚀🚀🚀`); + Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, adding to cart 🚀🚀🚀`); + response = await page.goto(addToCartUrl(id, drLocale, accessToken), {waitUntil: 'networkidle0'}); - Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, opening checkout page... 🚀🚀🚀`); + Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, opening checkout page 🚀🚀🚀`); + cartUrl = checkoutUrl(drLocale, accessToken); + Logger.info(cartUrl); + await open(cartUrl); } catch (error) { Logger.debug(error); - Logger.error(`✖ [nvidia] ${cardName} could not automatically add to cart, opening page`); + Logger.error(`✖ [nvidia] ${cardName} could not automatically add to cart, opening page`, error); + cartUrl = fallbackCartUrl(nvidiaLocale); await open(cartUrl); } diff --git a/src/store/model/index.ts b/src/store/model/index.ts index fb7dbac..47ec517 100644 --- a/src/store/model/index.ts +++ b/src/store/model/index.ts @@ -47,8 +47,7 @@ for (const name of Config.store.stores) { } } -const logString = `Selected stores: ${Array.from(list.keys()).join(', ')}`; -Logger.info(logString); +Logger.info(`ℹ selected stores: ${Array.from(list.keys()).join(', ')}`); export const Stores = Array.from(list.values()) as Store[]; diff --git a/src/store/model/store.ts b/src/store/model/store.ts index e06cb01..0de9a2b 100644 --- a/src/store/model/store.ts +++ b/src/store/model/store.ts @@ -1,11 +1,11 @@ import {Browser, LoadEvent} from 'puppeteer'; -export interface Element { +export type Element = { container: string; text: string[]; -} +}; -export interface Link { +export type Link = { series: string; brand: string; model: string; @@ -13,17 +13,17 @@ export interface Link { cartUrl?: string; openCartAction?: (browser: Browser) => Promise; screenshot?: string; -} +}; -export interface Labels { +export type Labels = { captcha?: Element; inStock: Element; -} +}; -export interface Store { +export type Store = { links: Link[]; labels: Labels; name: string; setupAction?: (browser: Browser) => void; - customWaitFor?: LoadEvent; -} + waitUntil?: LoadEvent; +};