mirror of
https://github.com/opelly27/streetmerchant.git
synced 2026-05-20 02:57:34 +00:00
chore: refactor config and fix audits (#406)
This commit is contained in:
Generated
+1392
-30
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -23,6 +23,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/jef/nvidia-snatcher#readme",
|
"homepage": "https://github.com/jef/nvidia-snatcher#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hijef/pushbullet": "^2.4.2",
|
||||||
"@slack/web-api": "^5.12.0",
|
"@slack/web-api": "^5.12.0",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"cheerio": "^1.0.0-rc.3",
|
"cheerio": "^1.0.0-rc.3",
|
||||||
@@ -38,7 +39,6 @@
|
|||||||
"puppeteer-extra-plugin-adblocker": "^2.11.6",
|
"puppeteer-extra-plugin-adblocker": "^2.11.6",
|
||||||
"puppeteer-extra-plugin-block-resources": "^2.2.7",
|
"puppeteer-extra-plugin-block-resources": "^2.2.7",
|
||||||
"puppeteer-extra-plugin-stealth": "^2.6.2",
|
"puppeteer-extra-plugin-stealth": "^2.6.2",
|
||||||
"pushbullet": "^2.4.0",
|
|
||||||
"pushover-notifications": "^1.2.2",
|
"pushover-notifications": "^1.2.2",
|
||||||
"twilio": "^3.49.4",
|
"twilio": "^3.49.4",
|
||||||
"twitter": "^1.7.1",
|
"twitter": "^1.7.1",
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-node": "^9.0.0",
|
"ts-node": "^9.0.0",
|
||||||
"typescript": "^4.0.2",
|
"typescript": "^4.0.2",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
"xo": "^0.33.1"
|
"xo": "^0.33.1"
|
||||||
},
|
},
|
||||||
"xo": {
|
"xo": {
|
||||||
|
|||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
import {banner} from './banner';
|
import {banner} from './banner';
|
||||||
console.log(banner);
|
console.log(banner);
|
||||||
|
|
||||||
import {config} from 'dotenv';
|
import {config as config_} from 'dotenv';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
config({path: path.resolve(__dirname, '../.env')});
|
config_({path: path.resolve(__dirname, '../.env')});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns environment variable, given array, or default array.
|
* Returns environment variable, given array, or default array.
|
||||||
@@ -91,7 +91,7 @@ const notifications = {
|
|||||||
number: envOrString(process.env.PHONE_NUMBER)
|
number: envOrString(process.env.PHONE_NUMBER)
|
||||||
},
|
},
|
||||||
playSound: envOrString(process.env.PLAY_SOUND),
|
playSound: envOrString(process.env.PLAY_SOUND),
|
||||||
pushBulletApiKey: envOrString(process.env.PUSHBULLET),
|
pushbullet: envOrString(process.env.PUSHBULLET),
|
||||||
pushover: {
|
pushover: {
|
||||||
priority: envOrString(process.env.PUSHOVER_PRIORITY),
|
priority: envOrString(process.env.PUSHOVER_PRIORITY),
|
||||||
token: envOrString(process.env.PUSHOVER_TOKEN),
|
token: envOrString(process.env.PUSHOVER_TOKEN),
|
||||||
@@ -128,8 +128,8 @@ const nvidia = {
|
|||||||
const page = {
|
const page = {
|
||||||
height: 1080,
|
height: 1080,
|
||||||
inStockWaitTime: envOrNumber(process.env.IN_STOCK_WAIT_TIME),
|
inStockWaitTime: envOrNumber(process.env.IN_STOCK_WAIT_TIME),
|
||||||
navigationTimeout: envOrNumber(process.env.PAGE_TIMEOUT, 30000),
|
|
||||||
screenshot: envOrBoolean(process.env.SCREENSHOT),
|
screenshot: envOrBoolean(process.env.SCREENSHOT),
|
||||||
|
timeout: envOrNumber(process.env.PAGE_TIMEOUT, 30000),
|
||||||
userAgent: envOrString(process.env.USER_AGENT, 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'),
|
userAgent: envOrString(process.env.USER_AGENT, 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'),
|
||||||
width: 1920
|
width: 1920
|
||||||
};
|
};
|
||||||
@@ -149,7 +149,7 @@ const store = {
|
|||||||
stores: envOrArray(process.env.STORES, ['nvidia'])
|
stores: envOrArray(process.env.STORES, ['nvidia'])
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Config = {
|
export const config = {
|
||||||
browser,
|
browser,
|
||||||
logLevel,
|
logLevel,
|
||||||
notifications,
|
notifications,
|
||||||
|
|||||||
+12
-12
@@ -1,16 +1,16 @@
|
|||||||
import {Config} from './config';
|
|
||||||
import {Logger} from './logger';
|
|
||||||
import {Stores} from './store/model';
|
import {Stores} from './store/model';
|
||||||
import {adBlocker} from './adblocker';
|
import {adBlocker} from './adblocker';
|
||||||
|
import {config} from './config';
|
||||||
import {fetchLinks} from './store/fetch-links';
|
import {fetchLinks} from './store/fetch-links';
|
||||||
import {getSleepTime} from './util';
|
import {getSleepTime} from './util';
|
||||||
|
import {logger} from './logger';
|
||||||
import puppeteer from 'puppeteer-extra';
|
import puppeteer from 'puppeteer-extra';
|
||||||
import resourceBlock from 'puppeteer-extra-plugin-block-resources';
|
import resourceBlock from 'puppeteer-extra-plugin-block-resources';
|
||||||
import stealthPlugin from 'puppeteer-extra-plugin-stealth';
|
import stealthPlugin from 'puppeteer-extra-plugin-stealth';
|
||||||
import {tryLookupAndLoop} from './store';
|
import {tryLookupAndLoop} from './store';
|
||||||
|
|
||||||
puppeteer.use(stealthPlugin());
|
puppeteer.use(stealthPlugin());
|
||||||
if (Config.browser.lowBandwidth) {
|
if (config.browser.lowBandwidth) {
|
||||||
puppeteer.use(resourceBlock({
|
puppeteer.use(resourceBlock({
|
||||||
blockedTypes: new Set(['image', 'font'])
|
blockedTypes: new Set(['image', 'font'])
|
||||||
}));
|
}));
|
||||||
@@ -23,7 +23,7 @@ if (Config.browser.lowBandwidth) {
|
|||||||
*/
|
*/
|
||||||
async function main() {
|
async function main() {
|
||||||
if (Stores.length === 0) {
|
if (Stores.length === 0) {
|
||||||
Logger.error('✖ no stores selected', Stores);
|
logger.error('✖ no stores selected', Stores);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,28 +31,28 @@ async function main() {
|
|||||||
|
|
||||||
// Skip Chromium Linux Sandbox
|
// Skip Chromium Linux Sandbox
|
||||||
// https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#setting-up-chrome-linux-sandbox
|
// https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#setting-up-chrome-linux-sandbox
|
||||||
if (Config.browser.isTrusted) {
|
if (config.browser.isTrusted) {
|
||||||
args.push('--no-sandbox');
|
args.push('--no-sandbox');
|
||||||
args.push('--disable-setuid-sandbox');
|
args.push('--disable-setuid-sandbox');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the address of the proxy server if defined
|
// Add the address of the proxy server if defined
|
||||||
if (Config.proxy.address) {
|
if (config.proxy.address) {
|
||||||
args.push(`--proxy-server=http://${Config.proxy.address}:${Config.proxy.port}`);
|
args.push(`--proxy-server=http://${config.proxy.address}:${config.proxy.port}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const browser = await puppeteer.launch({
|
const browser = await puppeteer.launch({
|
||||||
args,
|
args,
|
||||||
defaultViewport: {
|
defaultViewport: {
|
||||||
height: Config.page.height,
|
height: config.page.height,
|
||||||
width: Config.page.width
|
width: config.page.width
|
||||||
},
|
},
|
||||||
headless: Config.browser.isHeadless
|
headless: config.browser.isHeadless
|
||||||
});
|
});
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
for (const store of Stores) {
|
for (const store of Stores) {
|
||||||
Logger.debug(store.links);
|
logger.debug(store.links);
|
||||||
if (store.setupAction !== undefined) {
|
if (store.setupAction !== undefined) {
|
||||||
store.setupAction(browser);
|
store.setupAction(browser);
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,6 @@ async function main() {
|
|||||||
try {
|
try {
|
||||||
void main();
|
void main();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error('✖ something bad happened, resetting nvidia-snatcher', error);
|
logger.error('✖ something bad happened, resetting nvidia-snatcher', error);
|
||||||
void main();
|
void main();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
import {Link, Store} from './store/model';
|
import {Link, Store} from './store/model';
|
||||||
import winston, {format} from 'winston';
|
import winston, {format} from 'winston';
|
||||||
import {Config} from './config';
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import {config} from './config';
|
||||||
|
|
||||||
const prettyJson = format.printf(info => {
|
const prettyJson = format.printf(info => {
|
||||||
const timestamp = new Date().toLocaleTimeString();
|
const timestamp = new Date().toLocaleTimeString();
|
||||||
@@ -13,7 +13,7 @@ const prettyJson = format.printf(info => {
|
|||||||
return chalk.grey(`[${timestamp}]`) + ` ${info.level} ` + chalk.grey('::') + ` ${info.message}`;
|
return chalk.grey(`[${timestamp}]`) + ` ${info.level} ` + chalk.grey('::') + ` ${info.message}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Logger = winston.createLogger({
|
export const logger = winston.createLogger({
|
||||||
format: format.combine(
|
format: format.combine(
|
||||||
format.colorize(),
|
format.colorize(),
|
||||||
format.prettyPrint(),
|
format.prettyPrint(),
|
||||||
@@ -21,7 +21,7 @@ export const Logger = winston.createLogger({
|
|||||||
format.simple(),
|
format.simple(),
|
||||||
prettyJson
|
prettyJson
|
||||||
),
|
),
|
||||||
level: Config.logLevel,
|
level: config.logLevel,
|
||||||
transports: [new winston.transports.Console({})]
|
transports: [new winston.transports.Console({})]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,10 +76,10 @@ export const Print = {
|
|||||||
},
|
},
|
||||||
maxPrice(link: Link, store: Store, price: number, color?: boolean): string {
|
maxPrice(link: Link, store: Store, price: number, color?: boolean): string {
|
||||||
if (color) {
|
if (color) {
|
||||||
return '✖ ' + buildProductString(link, store, true) + ' :: ' + chalk.yellow(`PRICE ${price} EXCEEDS LIMIT ${Config.store.maxPrice}`);
|
return '✖ ' + buildProductString(link, store, true) + ' :: ' + chalk.yellow(`PRICE ${price} EXCEEDS LIMIT ${config.store.maxPrice}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `✖ ${buildProductString(link, store)} :: PRICE ${price} EXCEEDS LIMIT ${Config.store.maxPrice}`;
|
return `✖ ${buildProductString(link, store)} :: PRICE ${price} EXCEEDS LIMIT ${config.store.maxPrice}`;
|
||||||
},
|
},
|
||||||
message(message: string, topic: string, store: Store, color?: boolean): string {
|
message(message: string, topic: string, store: Store, color?: boolean): string {
|
||||||
if (color) {
|
if (color) {
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
|
import {config} from '../config';
|
||||||
import notifier from 'node-notifier';
|
import notifier from 'node-notifier';
|
||||||
|
|
||||||
export function sendDesktopNotification(link: Link, store: Store) {
|
const desktop = config.notifications.desktop;
|
||||||
(async () => {
|
|
||||||
notifier.notify({
|
|
||||||
message: link.cartUrl ? link.cartUrl : link.url,
|
|
||||||
title: Print.inStock(link, store)
|
|
||||||
});
|
|
||||||
|
|
||||||
Logger.info('✔ desktop notification sent');
|
export function sendDesktopNotification(link: Link, store: Store) {
|
||||||
})();
|
if (desktop) {
|
||||||
|
logger.debug('↗ sending desktop notification');
|
||||||
|
(async () => {
|
||||||
|
notifier.notify({
|
||||||
|
message: link.cartUrl ? link.cartUrl : link.url,
|
||||||
|
title: Print.inStock(link, store)
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('✔ desktop notification sent');
|
||||||
|
})();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-30
@@ -1,38 +1,43 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {MessageBuilder, Webhook} from 'discord-webhook-node';
|
import {MessageBuilder, Webhook} from 'discord-webhook-node';
|
||||||
import {Config} from '../config';
|
import {config} from '../config';
|
||||||
import {Logger} from '../logger';
|
import {logger} from '../logger';
|
||||||
|
|
||||||
const hooks = Config.notifications.discord.webHookUrl;
|
const discord = config.notifications.discord;
|
||||||
const notifyGroup = Config.notifications.discord.notifyGroup;
|
const hooks = discord.webHookUrl;
|
||||||
|
const notifyGroup = discord.notifyGroup;
|
||||||
|
|
||||||
export function sendDiscordMessage(link: Link, store: Store) {
|
export function sendDiscordMessage(link: Link, store: Store) {
|
||||||
(async () => {
|
if (discord.webHookUrl.length > 0) {
|
||||||
try {
|
logger.debug('↗ sending discord message');
|
||||||
const embed = new MessageBuilder();
|
|
||||||
embed.setTitle('Stock Notification');
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (notifyGroup) {
|
(async () => {
|
||||||
embed.setText(notifyGroup.join(' '));
|
try {
|
||||||
|
const embed = new MessageBuilder();
|
||||||
|
embed.setTitle('Stock Notification');
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (notifyGroup) {
|
||||||
|
embed.setText(notifyGroup.join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
embed.setColor(0x76B900);
|
||||||
|
embed.setTimestamp();
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
for (const hook of hooks) {
|
||||||
|
promises.push(new Webhook(hook).send(embed));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
logger.info('✔ discord message sent');
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('✖ couldn\'t send discord message', error);
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
embed.setColor(0x76B900);
|
}
|
||||||
embed.setTimestamp();
|
|
||||||
|
|
||||||
const promises = [];
|
|
||||||
for (const hook of hooks) {
|
|
||||||
promises.push(new Webhook(hook).send(embed));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
|
|
||||||
Logger.info('✔ discord message sent');
|
|
||||||
} catch (error) {
|
|
||||||
Logger.error('✖ couldn\'t send discord message', error);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-22
@@ -1,10 +1,10 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
|
||||||
import Mail from 'nodemailer/lib/mailer';
|
import Mail from 'nodemailer/lib/mailer';
|
||||||
|
import {config} from '../config';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
const email = Config.notifications.email;
|
const email = config.notifications.email;
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
auth: {
|
auth: {
|
||||||
@@ -15,24 +15,28 @@ const transporter = nodemailer.createTransport({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function sendEmail(link: Link, store: Store) {
|
export function sendEmail(link: Link, store: Store) {
|
||||||
const mailOptions: Mail.Options = {
|
if (email.username && email.password) {
|
||||||
attachments: link.screenshot ? [
|
logger.debug('↗ sending email');
|
||||||
{
|
|
||||||
filename: link.screenshot,
|
|
||||||
path: `./${link.screenshot}`
|
|
||||||
}
|
|
||||||
] : undefined,
|
|
||||||
from: email.username,
|
|
||||||
subject: Print.inStock(link, store),
|
|
||||||
text: link.cartUrl ? link.cartUrl : link.url,
|
|
||||||
to: email.to
|
|
||||||
};
|
|
||||||
|
|
||||||
transporter.sendMail(mailOptions, error => {
|
const mailOptions: Mail.Options = {
|
||||||
if (error) {
|
attachments: link.screenshot ? [
|
||||||
Logger.error('✖ couldn\'t send email', error);
|
{
|
||||||
} else {
|
filename: link.screenshot,
|
||||||
Logger.info('✔ email sent');
|
path: `./${link.screenshot}`
|
||||||
}
|
}
|
||||||
});
|
] : undefined,
|
||||||
|
from: email.username,
|
||||||
|
subject: Print.inStock(link, store),
|
||||||
|
text: link.cartUrl ? link.cartUrl : link.url,
|
||||||
|
to: email.to
|
||||||
|
};
|
||||||
|
|
||||||
|
transporter.sendMail(mailOptions, error => {
|
||||||
|
if (error) {
|
||||||
|
logger.error('✖ couldn\'t send email', error);
|
||||||
|
} else {
|
||||||
|
logger.info('✔ email sent');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Config} from '../config';
|
|
||||||
import {Logger} from '../logger';
|
|
||||||
import {playSound} from './sound';
|
import {playSound} from './sound';
|
||||||
import {sendDesktopNotification} from './desktop';
|
import {sendDesktopNotification} from './desktop';
|
||||||
import {sendDiscordMessage} from './discord';
|
import {sendDiscordMessage} from './discord';
|
||||||
import {sendEmail} from './email';
|
import {sendEmail} from './email';
|
||||||
import {sendPushBulletNotification} from './pushbullet';
|
import {sendPushbulletNotification} from './pushbullet';
|
||||||
import {sendPushoverNotification} from './pushover';
|
import {sendPushoverNotification} from './pushover';
|
||||||
import {sendSMS} from './sms';
|
import {sendSMS} from './sms';
|
||||||
import {sendSlackMessage} from './slack';
|
import {sendSlackMessage} from './slack';
|
||||||
@@ -13,69 +11,16 @@ import {sendTelegramMessage} from './telegram';
|
|||||||
import {sendTweet} from './twitter';
|
import {sendTweet} from './twitter';
|
||||||
import {sendTwilioMessage} from './twilio';
|
import {sendTwilioMessage} from './twilio';
|
||||||
|
|
||||||
const notifications = Config.notifications;
|
|
||||||
|
|
||||||
export function sendNotification(link: Link, store: Store) {
|
export function sendNotification(link: Link, store: Store) {
|
||||||
if (notifications.email.username && notifications.email.password) {
|
sendEmail(link, store);
|
||||||
Logger.debug('↗ sending email');
|
sendSMS(link, store);
|
||||||
sendEmail(link, store);
|
playSound();
|
||||||
}
|
sendDesktopNotification(link, store);
|
||||||
|
sendDiscordMessage(link, store);
|
||||||
if (notifications.phone.number) {
|
sendSlackMessage(link, store);
|
||||||
Logger.debug('↗ sending sms');
|
sendTelegramMessage(link, store);
|
||||||
const carrier = notifications.phone.carrier;
|
sendTwilioMessage(link, store);
|
||||||
if (carrier && notifications.phone.availableCarriers.has(carrier)) {
|
sendPushbulletNotification(link, store);
|
||||||
sendSMS(link, store);
|
sendPushoverNotification(link, store);
|
||||||
}
|
sendTweet(link, store);
|
||||||
}
|
|
||||||
|
|
||||||
if (notifications.playSound) {
|
|
||||||
Logger.debug('↗ playing sound');
|
|
||||||
playSound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notifications.desktop) {
|
|
||||||
Logger.debug('↗ sending desktop notification');
|
|
||||||
sendDesktopNotification(link, store);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notifications.discord.webHookUrl.length > 0) {
|
|
||||||
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.twilio.accountSid && notifications.twilio.authToken) {
|
|
||||||
Logger.debug('↗ sending twilio message');
|
|
||||||
sendTwilioMessage(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 (
|
|
||||||
notifications.twitter.accessTokenKey &&
|
|
||||||
notifications.twitter.accessTokenSecret &&
|
|
||||||
notifications.twitter.consumerKey &&
|
|
||||||
notifications.twitter.consumerSecret
|
|
||||||
) {
|
|
||||||
Logger.debug('↗ sending twitter message');
|
|
||||||
sendTweet(link, store);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
import PushBullet from '@hijef/pushbullet';
|
||||||
import PushBullet from 'pushbullet';
|
import {config} from '../config';
|
||||||
|
|
||||||
const pushBulletApiKey = Config.notifications.pushBulletApiKey;
|
const pushbullet = config.notifications.pushbullet;
|
||||||
|
|
||||||
export function sendPushBulletNotification(link: Link, store: Store) {
|
export function sendPushbulletNotification(link: Link, store: Store) {
|
||||||
const pusher = new PushBullet(pushBulletApiKey);
|
if (pushbullet) {
|
||||||
|
logger.debug('↗ sending pushbullet message');
|
||||||
|
|
||||||
pusher.note(
|
const pusher = new PushBullet(pushbullet);
|
||||||
{},
|
|
||||||
Print.inStock(link, store),
|
pusher.note(
|
||||||
link.cartUrl ? link.cartUrl : link.url,
|
{},
|
||||||
(error: Error) => {
|
Print.inStock(link, store),
|
||||||
if (error) {
|
link.cartUrl ? link.cartUrl : link.url,
|
||||||
Logger.error('✖ couldn\'t send pushbullet message', error);
|
(error: Error) => {
|
||||||
} else {
|
if (error) {
|
||||||
Logger.info('✔ pushbullet message sent');
|
logger.error('✖ couldn\'t send pushbullet message', error);
|
||||||
}
|
} else {
|
||||||
});
|
logger.info('✔ pushbullet message sent');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
|
||||||
import Push from 'pushover-notifications';
|
import Push from 'pushover-notifications';
|
||||||
|
import {config} from '../config';
|
||||||
|
|
||||||
const pushover = Config.notifications.pushover;
|
const pushover = config.notifications.pushover;
|
||||||
const push = new Push({
|
const push = new Push({
|
||||||
token: pushover.token,
|
token: pushover.token,
|
||||||
user: pushover.username
|
user: pushover.username
|
||||||
});
|
});
|
||||||
|
|
||||||
export function sendPushoverNotification(link: Link, store: Store) {
|
export function sendPushoverNotification(link: Link, store: Store) {
|
||||||
const message = {
|
if (pushover.token && pushover.username) {
|
||||||
message: link.cartUrl ? link.cartUrl : link.url,
|
logger.debug('↗ sending pushover message');
|
||||||
priority: pushover.priority,
|
|
||||||
title: Print.inStock(link, store)
|
|
||||||
};
|
|
||||||
|
|
||||||
push.send(message, (error: Error) => {
|
const message = {
|
||||||
if (error) {
|
message: link.cartUrl ? link.cartUrl : link.url,
|
||||||
Logger.error('✖ couldn\'t send pushover message', error);
|
priority: pushover.priority,
|
||||||
} else {
|
title: Print.inStock(link, store)
|
||||||
Logger.info('✔ pushover message sent');
|
};
|
||||||
}
|
|
||||||
});
|
push.send(message, (error: Error) => {
|
||||||
|
if (error) {
|
||||||
|
logger.error('✖ couldn\'t send pushover message', error);
|
||||||
|
} else {
|
||||||
|
logger.info('✔ pushover message sent');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-20
@@ -1,30 +1,35 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
|
||||||
import {WebClient} from '@slack/web-api';
|
import {WebClient} from '@slack/web-api';
|
||||||
|
import {config} from '../config';
|
||||||
|
|
||||||
const channel = Config.notifications.slack.channel;
|
const slack = config.notifications.slack;
|
||||||
const token = Config.notifications.slack.token;
|
const channel = slack.channel;
|
||||||
|
const token = slack.token;
|
||||||
const web = new WebClient(token);
|
const web = new WebClient(token);
|
||||||
|
|
||||||
export function sendSlackMessage(link: Link, store: Store) {
|
export function sendSlackMessage(link: Link, store: Store) {
|
||||||
(async () => {
|
if (slack.channel && slack.token) {
|
||||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
logger.debug('↗ sending slack message');
|
||||||
|
|
||||||
try {
|
(async () => {
|
||||||
const result = await web.chat.postMessage({
|
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||||
channel,
|
|
||||||
text: `${Print.inStock(link, store)}\n${givenUrl}`
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.ok) {
|
try {
|
||||||
Logger.error('✖ couldn\'t send slack message', result);
|
const result = await web.chat.postMessage({
|
||||||
return;
|
channel,
|
||||||
|
text: `${Print.inStock(link, store)}\n${givenUrl}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
logger.error('✖ couldn\'t send slack message', result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('✔ slack message sent');
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('✖ couldn\'t send slack message', error);
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
Logger.info('✔ slack message sent');
|
}
|
||||||
} catch (error) {
|
|
||||||
Logger.error('✖ couldn\'t send slack message', error);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-24
@@ -1,14 +1,14 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
|
||||||
import Mail from 'nodemailer/lib/mailer';
|
import Mail from 'nodemailer/lib/mailer';
|
||||||
|
import {config} from '../config';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
if (Config.notifications.phone.number && !Config.notifications.email.username) {
|
if (config.notifications.phone.number && !config.notifications.email.username) {
|
||||||
Logger.warn('✖ in order to recieve sms alerts, email notifications must also be configured');
|
logger.warn('✖ in order to recieve sms alerts, email notifications must also be configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
const [email, phone] = [Config.notifications.email, Config.notifications.phone];
|
const [email, phone] = [config.notifications.email, config.notifications.phone];
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
auth: {
|
auth: {
|
||||||
@@ -19,26 +19,33 @@ const transporter = nodemailer.createTransport({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function sendSMS(link: Link, store: Store) {
|
export function sendSMS(link: Link, store: Store) {
|
||||||
const mailOptions: Mail.Options = {
|
if (phone.number) {
|
||||||
attachments: link.screenshot ? [
|
logger.debug('↗ sending sms');
|
||||||
{
|
const carrier = phone.carrier;
|
||||||
filename: link.screenshot,
|
|
||||||
path: `./${link.screenshot}`
|
|
||||||
}
|
|
||||||
] : undefined,
|
|
||||||
from: email.username,
|
|
||||||
subject: Print.inStock(link, store, false, true),
|
|
||||||
text: link.cartUrl ? link.cartUrl : link.url,
|
|
||||||
to: generateAddress()
|
|
||||||
};
|
|
||||||
|
|
||||||
transporter.sendMail(mailOptions, error => {
|
if (carrier && phone.availableCarriers.has(carrier)) {
|
||||||
if (error) {
|
const mailOptions: Mail.Options = {
|
||||||
Logger.error('✖ couldn\'t send sms', error);
|
attachments: link.screenshot ? [
|
||||||
} else {
|
{
|
||||||
Logger.info('✔ sms sent');
|
filename: link.screenshot,
|
||||||
|
path: `./${link.screenshot}`
|
||||||
|
}
|
||||||
|
] : undefined,
|
||||||
|
from: email.username,
|
||||||
|
subject: Print.inStock(link, store, false, true),
|
||||||
|
text: link.cartUrl ? link.cartUrl : link.url,
|
||||||
|
to: generateAddress()
|
||||||
|
};
|
||||||
|
|
||||||
|
transporter.sendMail(mailOptions, error => {
|
||||||
|
if (error) {
|
||||||
|
logger.error('✖ couldn\'t send sms', error);
|
||||||
|
} else {
|
||||||
|
logger.info('✔ sms sent');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateAddress() {
|
function generateAddress() {
|
||||||
@@ -48,5 +55,5 @@ function generateAddress() {
|
|||||||
return [phone.number, phone.availableCarriers.get(carrier)].join('@');
|
return [phone.number, phone.availableCarriers.get(carrier)].join('@');
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error('✖ unknown carrier', carrier);
|
logger.error('✖ unknown carrier', carrier);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-11
@@ -1,35 +1,37 @@
|
|||||||
import {Config} from '../config';
|
import {config} from '../config';
|
||||||
import {Logger} from '../logger';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import {logger} from '../logger';
|
||||||
import playerLib from 'play-sound';
|
import playerLib from 'play-sound';
|
||||||
|
|
||||||
let player: any;
|
let player: any;
|
||||||
|
|
||||||
if (Config.notifications.playSound) {
|
if (config.notifications.playSound) {
|
||||||
player = playerLib();
|
player = playerLib();
|
||||||
|
|
||||||
if (player.player === null) {
|
if (player.player === null) {
|
||||||
Logger.warn('✖ couldn\'t find sound player');
|
logger.warn('✖ couldn\'t find sound player');
|
||||||
} else {
|
} else {
|
||||||
const playerName: string = player.player;
|
const playerName: string = player.player;
|
||||||
Logger.info(`✔ sound player found: ${playerName}`);
|
logger.info(`✔ sound player found: ${playerName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function playSound() {
|
export function playSound() {
|
||||||
if (player.player !== null) {
|
if (config.notifications.playSound && player.player !== null) {
|
||||||
fs.access(Config.notifications.playSound, fs.constants.F_OK, error => {
|
logger.debug('↗ playing sound');
|
||||||
|
|
||||||
|
fs.access(config.notifications.playSound, fs.constants.F_OK, error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
Logger.error(`✖ error opening sound file: ${error.message}`);
|
logger.error(`✖ error opening sound file: ${error.message}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.play(Config.notifications.playSound, (error: Error) => {
|
player.play(config.notifications.playSound, (error: Error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
Logger.error('✖ couldn\'t play sound', error);
|
logger.error('✖ couldn\'t play sound', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info('✔ played sound');
|
logger.info('✔ played sound');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
|
||||||
import {TelegramClient} from 'messaging-api-telegram';
|
import {TelegramClient} from 'messaging-api-telegram';
|
||||||
|
import {config} from '../config';
|
||||||
|
|
||||||
const telegram = Config.notifications.telegram;
|
const telegram = config.notifications.telegram;
|
||||||
|
|
||||||
const client = new TelegramClient({
|
const client = new TelegramClient({
|
||||||
accessToken: telegram.accessToken
|
accessToken: telegram.accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
export function sendTelegramMessage(link: Link, store: Store) {
|
export function sendTelegramMessage(link: Link, store: Store) {
|
||||||
(async () => {
|
if (telegram.accessToken && telegram.chatId) {
|
||||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
logger.debug('↗ sending telegram message');
|
||||||
|
|
||||||
try {
|
(async () => {
|
||||||
await client.sendMessage(telegram.chatId, `${Print.inStock(link, store)}\n${givenUrl}`);
|
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||||
Logger.info('✔ telegram message sent');
|
|
||||||
} catch (error) {
|
try {
|
||||||
Logger.error('✖ couldn\'t send telegram message', error);
|
await client.sendMessage(telegram.chatId, `${Print.inStock(link, store)}\n${givenUrl}`);
|
||||||
}
|
logger.info('✔ telegram message sent');
|
||||||
})();
|
} catch (error) {
|
||||||
|
logger.error('✖ couldn\'t send telegram message', error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-19
@@ -1,25 +1,33 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
import {Twilio} from 'twilio';
|
||||||
import twilio from 'twilio';
|
import {config} from '../config';
|
||||||
|
|
||||||
const config = Config.notifications.twilio;
|
const twilio = config.notifications.twilio;
|
||||||
|
let client: Twilio;
|
||||||
|
|
||||||
|
if (twilio.accountSid && twilio.authToken) {
|
||||||
|
client = new Twilio(twilio.accountSid, twilio.authToken);
|
||||||
|
}
|
||||||
|
|
||||||
export function sendTwilioMessage(link: Link, store: Store) {
|
export function sendTwilioMessage(link: Link, store: Store) {
|
||||||
(async () => {
|
if (client) {
|
||||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
logger.debug('↗ sending twilio message');
|
||||||
const message = `${Print.inStock(link, store)}\n${givenUrl}`;
|
|
||||||
|
|
||||||
try {
|
(async () => {
|
||||||
const client = twilio(config.accountSid, config.authToken);
|
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||||
await client.messages.create({
|
const message = `${Print.inStock(link, store)}\n${givenUrl}`;
|
||||||
body: message,
|
|
||||||
from: config.from,
|
try {
|
||||||
to: config.to
|
await client.messages.create({
|
||||||
});
|
body: message,
|
||||||
Logger.info('✔ twilio message sent');
|
from: twilio.from,
|
||||||
} catch (error) {
|
to: twilio.to
|
||||||
Logger.error('✖ couldn\'t send twilio message', error);
|
});
|
||||||
}
|
logger.info('✔ twilio message sent');
|
||||||
})();
|
} catch (error) {
|
||||||
|
logger.error('✖ couldn\'t send twilio message', error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-13
@@ -1,9 +1,9 @@
|
|||||||
import {Link, Store} from '../store/model';
|
import {Link, Store} from '../store/model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Config} from '../config';
|
|
||||||
import Twitter from 'twitter';
|
import Twitter from 'twitter';
|
||||||
|
import {config} from '../config';
|
||||||
|
|
||||||
const twitter = Config.notifications.twitter;
|
const twitter = config.notifications.twitter;
|
||||||
|
|
||||||
const client = new Twitter({
|
const client = new Twitter({
|
||||||
access_token_key: twitter.accessTokenKey,
|
access_token_key: twitter.accessTokenKey,
|
||||||
@@ -13,17 +13,21 @@ const client = new Twitter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function sendTweet(link: Link, store: Store) {
|
export function sendTweet(link: Link, store: Store) {
|
||||||
let status = `${Print.inStock(link, store)}\n${link.cartUrl ? link.cartUrl : link.url}`;
|
if (twitter.accessTokenKey && twitter.accessTokenSecret && twitter.consumerKey && twitter.consumerSecret) {
|
||||||
|
logger.debug('↗ sending twitter message');
|
||||||
|
|
||||||
if (twitter.tweetTags) {
|
let status = `${Print.inStock(link, store)}\n${link.cartUrl ? link.cartUrl : link.url}`;
|
||||||
status += `\n\n${twitter.tweetTags}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.post('statuses/update', {status}, error => {
|
if (twitter.tweetTags) {
|
||||||
if (error) {
|
status += `\n\n${twitter.tweetTags}`;
|
||||||
Logger.error('✖ couldn\'t send twitter notification', error);
|
|
||||||
} else {
|
|
||||||
Logger.info('✔ twitter notification sent');
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
client.post('statuses/update', {status}, error => {
|
||||||
|
if (error) {
|
||||||
|
logger.error('✖ couldn\'t send twitter notification', error);
|
||||||
|
} else {
|
||||||
|
logger.info('✔ twitter notification sent');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Link, Series, Store} from './model';
|
import {Link, Series, Store} from './model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Browser} from 'puppeteer';
|
import {Browser} from 'puppeteer';
|
||||||
import cheerio from 'cheerio';
|
import cheerio from 'cheerio';
|
||||||
import {filterSeries} from './filter';
|
import {filterSeries} from './filter';
|
||||||
@@ -7,7 +7,7 @@ import {usingResponse} from '../util';
|
|||||||
|
|
||||||
function addNewLinks(store: Store, links: Link[], series: Series) {
|
function addNewLinks(store: Store, links: Link[], series: Series) {
|
||||||
if (links.length === 0) {
|
if (links.length === 0) {
|
||||||
Logger.error(Print.message('NO STORE LINKS FOUND', series, store, true));
|
logger.error(Print.message('NO STORE LINKS FOUND', series, store, true));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,8 @@ function addNewLinks(store: Store, links: Link[], series: Series) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info(Print.message(`FOUND ${newLinks.length} STORE LINKS`, series, store, true));
|
logger.info(Print.message(`FOUND ${newLinks.length} STORE LINKS`, series, store, true));
|
||||||
Logger.debug(JSON.stringify(newLinks, null, 2));
|
logger.debug(JSON.stringify(newLinks, null, 2));
|
||||||
|
|
||||||
store.links = store.links.concat(newLinks);
|
store.links = store.links.concat(newLinks);
|
||||||
}
|
}
|
||||||
@@ -37,13 +37,13 @@ export async function fetchLinks(store: Store, browser: Browser) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info(Print.message('DETECTING STORE LINKS', series, store, true));
|
logger.info(Print.message('DETECTING STORE LINKS', series, store, true));
|
||||||
|
|
||||||
promises.push(usingResponse(browser, url, async response => {
|
promises.push(usingResponse(browser, url, async response => {
|
||||||
const text = await response?.text();
|
const text = await response?.text();
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
Logger.error(Print.message('NO RESPONSE', series, store, true));
|
logger.error(Print.message('NO RESPONSE', series, store, true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -1,5 +1,5 @@
|
|||||||
import {Config} from '../config';
|
|
||||||
import {Link} from './model';
|
import {Link} from './model';
|
||||||
|
import {config} from '../config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the brand should be checked for stock
|
* Returns true if the brand should be checked for stock
|
||||||
@@ -7,11 +7,11 @@ import {Link} from './model';
|
|||||||
* @param brand The brand of the GPU
|
* @param brand The brand of the GPU
|
||||||
*/
|
*/
|
||||||
function filterBrand(brand: Link['brand']): boolean {
|
function filterBrand(brand: Link['brand']): boolean {
|
||||||
if (Config.store.showOnlyBrands.length === 0) {
|
if (config.store.showOnlyBrands.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Config.store.showOnlyBrands.includes(brand);
|
return config.store.showOnlyBrands.includes(brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,12 +20,12 @@ function filterBrand(brand: Link['brand']): boolean {
|
|||||||
* @param model The model of the GPU
|
* @param model The model of the GPU
|
||||||
*/
|
*/
|
||||||
function filterModel(model: Link['model']): boolean {
|
function filterModel(model: Link['model']): boolean {
|
||||||
if (Config.store.showOnlyModels.length === 0) {
|
if (config.store.showOnlyModels.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedModel = model.replace(/\s/g, '');
|
const sanitizedModel = model.replace(/\s/g, '');
|
||||||
for (const configModel of Config.store.showOnlyModels) {
|
for (const configModel of config.store.showOnlyModels) {
|
||||||
const sanitizedConfigModel = configModel.replace(/\s/g, '');
|
const sanitizedConfigModel = configModel.replace(/\s/g, '');
|
||||||
if (sanitizedModel === sanitizedConfigModel) {
|
if (sanitizedModel === sanitizedConfigModel) {
|
||||||
return true;
|
return true;
|
||||||
@@ -41,11 +41,11 @@ function filterModel(model: Link['model']): boolean {
|
|||||||
* @param series The series of the GPU
|
* @param series The series of the GPU
|
||||||
*/
|
*/
|
||||||
export function filterSeries(series: Link['series']): boolean {
|
export function filterSeries(series: Link['series']): boolean {
|
||||||
if (Config.store.showOnlySeries.length === 0) {
|
if (config.store.showOnlySeries.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Config.store.showOnlySeries.includes(series);
|
return config.store.showOnlySeries.includes(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Element, LabelQuery, Pricing} from './model';
|
import {Element, LabelQuery, Pricing} from './model';
|
||||||
import {Logger} from '../logger';
|
|
||||||
import {Page} from 'puppeteer';
|
import {Page} from 'puppeteer';
|
||||||
|
import {logger} from '../logger';
|
||||||
|
|
||||||
export type Selector = {
|
export type Selector = {
|
||||||
requireVisible: boolean;
|
requireVisible: boolean;
|
||||||
@@ -44,7 +44,7 @@ export async function pageIncludesLabels(page: Page, query: LabelQuery, options:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug(contents);
|
logger.debug(contents);
|
||||||
|
|
||||||
return includesLabels(contents, query.text);
|
return includesLabels(contents, query.text);
|
||||||
}));
|
}));
|
||||||
@@ -103,7 +103,7 @@ export async function cardPriceLimit(page: Page, query: Pricing, max: number, op
|
|||||||
const priceSeperator = query.euroFormat ? /\./g : /,/g;
|
const priceSeperator = query.euroFormat ? /\./g : /,/g;
|
||||||
const cardpriceNumber = Number.parseFloat(cardPrice.replace(priceSeperator, '').match(/\d+/g)!.join('.'));
|
const cardpriceNumber = Number.parseFloat(cardPrice.replace(priceSeperator, '').match(/\d+/g)!.join('.'));
|
||||||
|
|
||||||
Logger.debug(`Raw card price: ${cardPrice} | Limit: ${max}`);
|
logger.debug(`Raw card price: ${cardPrice} | Limit: ${max}`);
|
||||||
return cardpriceNumber > max ? cardpriceNumber : null;
|
return cardpriceNumber > max ? cardpriceNumber : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-26
@@ -1,9 +1,9 @@
|
|||||||
import {Browser, Page, Response} from 'puppeteer';
|
import {Browser, Page, Response} from 'puppeteer';
|
||||||
import {Link, Store} from './model';
|
import {Link, Store} from './model';
|
||||||
import {Logger, Print} from '../logger';
|
import {Print, logger} from '../logger';
|
||||||
import {Selector, cardPriceLimit, pageIncludesLabels} from './includes-labels';
|
import {Selector, cardPriceLimit, pageIncludesLabels} from './includes-labels';
|
||||||
import {closePage, delay, getSleepTime, isStatusCodeInRange} from '../util';
|
import {closePage, delay, getSleepTime, isStatusCodeInRange} from '../util';
|
||||||
import {Config} from '../config';
|
import {config} from '../config';
|
||||||
import {disableBlockerInPage} from '../adblocker';
|
import {disableBlockerInPage} from '../adblocker';
|
||||||
import {filterStoreLink} from './filter';
|
import {filterStoreLink} from './filter';
|
||||||
import open from 'open';
|
import open from 'open';
|
||||||
@@ -27,20 +27,20 @@ async function lookup(browser: Browser, store: Store) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.page.inStockWaitTime && inStock[link.url]) {
|
if (config.page.inStockWaitTime && inStock[link.url]) {
|
||||||
Logger.info(Print.inStockWaiting(link, store, true));
|
logger.info(Print.inStockWaiting(link, store, true));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
page.setDefaultNavigationTimeout(Config.page.navigationTimeout);
|
page.setDefaultNavigationTimeout(config.page.timeout);
|
||||||
await page.setUserAgent(Config.page.userAgent);
|
await page.setUserAgent(config.page.userAgent);
|
||||||
|
|
||||||
if (store.disableAdBlocker) {
|
if (store.disableAdBlocker) {
|
||||||
try {
|
try {
|
||||||
await disableBlockerInPage(page);
|
await disableBlockerInPage(page);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(error);
|
logger.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ async function lookup(browser: Browser, store: Store) {
|
|||||||
try {
|
try {
|
||||||
statusCode = await lookupCard(browser, store, page, link);
|
statusCode = await lookupCard(browser, store, page, link);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`✖ [${store.name}] ${link.brand} ${link.model} - ${error.message as string}`);
|
logger.error(`✖ [${store.name}] ${link.brand} ${link.model} - ${error.message as string}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must apply backoff before closing the page, e.g. if CloudFlare is
|
// Must apply backoff before closing the page, e.g. if CloudFlare is
|
||||||
@@ -67,16 +67,16 @@ async function lookupCard(browser: Browser, store: Store, page: Page, link: Link
|
|||||||
const response: Response | null = await page.goto(link.url, {waitUntil: givenWaitFor});
|
const response: Response | null = await page.goto(link.url, {waitUntil: givenWaitFor});
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
Logger.debug(Print.noResponse(link, store, true));
|
logger.debug(Print.noResponse(link, store, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
const successStatusCodes = store.successStatusCodes ?? [[0, 399]];
|
const successStatusCodes = store.successStatusCodes ?? [[0, 399]];
|
||||||
const statusCode = response?.status() ?? 0;
|
const statusCode = response?.status() ?? 0;
|
||||||
if (!isStatusCodeInRange(statusCode, successStatusCodes)) {
|
if (!isStatusCodeInRange(statusCode, successStatusCodes)) {
|
||||||
if (statusCode === 429) {
|
if (statusCode === 429) {
|
||||||
Logger.warn(Print.rateLimit(link, store, true));
|
logger.warn(Print.rateLimit(link, store, true));
|
||||||
} else {
|
} else {
|
||||||
Logger.warn(Print.badStatusCode(link, store, statusCode, true));
|
logger.warn(Print.badStatusCode(link, store, statusCode, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusCode;
|
return statusCode;
|
||||||
@@ -84,9 +84,9 @@ async function lookupCard(browser: Browser, store: Store, page: Page, link: Link
|
|||||||
|
|
||||||
if (await lookupCardInStock(store, page, link)) {
|
if (await lookupCardInStock(store, page, link)) {
|
||||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||||
Logger.info(`${Print.inStock(link, store, true)}\n${givenUrl}`);
|
logger.info(`${Print.inStock(link, store, true)}\n${givenUrl}`);
|
||||||
|
|
||||||
if (Config.browser.open) {
|
if (config.browser.open) {
|
||||||
if (link.openCartAction === undefined) {
|
if (link.openCartAction === undefined) {
|
||||||
await open(givenUrl);
|
await open(givenUrl);
|
||||||
} else {
|
} else {
|
||||||
@@ -96,16 +96,16 @@ async function lookupCard(browser: Browser, store: Store, page: Page, link: Link
|
|||||||
|
|
||||||
sendNotification(link, store);
|
sendNotification(link, store);
|
||||||
|
|
||||||
if (Config.page.inStockWaitTime) {
|
if (config.page.inStockWaitTime) {
|
||||||
inStock[link.url] = true;
|
inStock[link.url] = true;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
inStock[link.url] = false;
|
inStock[link.url] = false;
|
||||||
}, 1000 * Config.page.inStockWaitTime);
|
}, 1000 * config.page.inStockWaitTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.page.screenshot) {
|
if (config.page.screenshot) {
|
||||||
Logger.debug('ℹ saving screenshot');
|
logger.debug('ℹ saving screenshot');
|
||||||
|
|
||||||
link.screenshot = `success-${Date.now()}.png`;
|
link.screenshot = `success-${Date.now()}.png`;
|
||||||
await page.screenshot({path: link.screenshot});
|
await page.screenshot({path: link.screenshot});
|
||||||
@@ -126,36 +126,36 @@ async function lookupCardInStock(store: Store, page: Page, link: Link) {
|
|||||||
const options = {...baseOptions, requireVisible: true, type: 'outerHTML' as const};
|
const options = {...baseOptions, requireVisible: true, type: 'outerHTML' as const};
|
||||||
|
|
||||||
if (!await pageIncludesLabels(page, store.labels.inStock, options)) {
|
if (!await pageIncludesLabels(page, store.labels.inStock, options)) {
|
||||||
Logger.info(Print.outOfStock(link, store, true));
|
logger.info(Print.outOfStock(link, store, true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.labels.outOfStock) {
|
if (store.labels.outOfStock) {
|
||||||
if (await pageIncludesLabels(page, store.labels.outOfStock, baseOptions)) {
|
if (await pageIncludesLabels(page, store.labels.outOfStock, baseOptions)) {
|
||||||
Logger.info(Print.outOfStock(link, store, true));
|
logger.info(Print.outOfStock(link, store, true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.labels.bannedSeller) {
|
if (store.labels.bannedSeller) {
|
||||||
if (await pageIncludesLabels(page, store.labels.bannedSeller, baseOptions)) {
|
if (await pageIncludesLabels(page, store.labels.bannedSeller, baseOptions)) {
|
||||||
Logger.warn(Print.bannedSeller(link, store, true));
|
logger.warn(Print.bannedSeller(link, store, true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.labels.maxPrice) {
|
if (store.labels.maxPrice) {
|
||||||
const priceLimit = await cardPriceLimit(page, store.labels.maxPrice, Config.store.maxPrice, baseOptions);
|
const priceLimit = await cardPriceLimit(page, store.labels.maxPrice, config.store.maxPrice, baseOptions);
|
||||||
if (priceLimit) {
|
if (priceLimit) {
|
||||||
Logger.info(Print.maxPrice(link, store, priceLimit, true));
|
logger.info(Print.maxPrice(link, store, priceLimit, true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.labels.captcha) {
|
if (store.labels.captcha) {
|
||||||
if (await pageIncludesLabels(page, store.labels.captcha, baseOptions)) {
|
if (await pageIncludesLabels(page, store.labels.captcha, baseOptions)) {
|
||||||
Logger.warn(Print.captcha(link, store, true));
|
logger.warn(Print.captcha(link, store, true));
|
||||||
await delay(getSleepTime());
|
await delay(getSleepTime());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -165,14 +165,14 @@ async function lookupCardInStock(store: Store, page: Page, link: Link) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function tryLookupAndLoop(browser: Browser, store: Store) {
|
export async function tryLookupAndLoop(browser: Browser, store: Store) {
|
||||||
Logger.debug(`[${store.name}] Starting lookup...`);
|
logger.debug(`[${store.name}] Starting lookup...`);
|
||||||
try {
|
try {
|
||||||
await lookup(browser, store);
|
await lookup(browser, store);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(error);
|
logger.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sleepTime = getSleepTime();
|
const sleepTime = getSleepTime();
|
||||||
Logger.debug(`[${store.name}] Lookup done, next one in ${sleepTime} ms`);
|
logger.debug(`[${store.name}] Lookup done, next one in ${sleepTime} ms`);
|
||||||
setTimeout(tryLookupAndLoop, sleepTime, browser, store);
|
setTimeout(tryLookupAndLoop, sleepTime, browser, store);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {Link, Store} from '..';
|
import {Link, Store} from '..';
|
||||||
import {Logger, Print} from '../../../logger';
|
import {Print, logger} from '../../../logger';
|
||||||
import {delay, isStatusCodeInRange} from '../../../util';
|
import {delay, isStatusCodeInRange} from '../../../util';
|
||||||
import {Config} from '../../../config';
|
import {config} from '../../../config';
|
||||||
|
|
||||||
type Backoff = {
|
type Backoff = {
|
||||||
count: number;
|
count: number;
|
||||||
@@ -27,26 +27,26 @@ export async function processBackoffDelay(store: Store, link: Link, statusCode:
|
|||||||
let backoff = stores[store.name];
|
let backoff = stores[store.name];
|
||||||
|
|
||||||
if (!backoff) {
|
if (!backoff) {
|
||||||
backoff = {count: 0, time: Config.browser.minBackoff};
|
backoff = {count: 0, time: config.browser.minBackoff};
|
||||||
stores[store.name] = backoff;
|
stores[store.name] = backoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isBackoff) {
|
if (!isBackoff) {
|
||||||
if (backoff.count > 0) {
|
if (backoff.count > 0) {
|
||||||
backoff.count--;
|
backoff.count--;
|
||||||
backoff.time = Math.max(backoff.time / 2, Config.browser.minBackoff);
|
backoff.time = Math.max(backoff.time / 2, config.browser.minBackoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backoffTime = backoff.time;
|
const backoffTime = backoff.time;
|
||||||
Logger.debug(Print.backoff(link, store, {delay: backoffTime, statusCode}, true));
|
logger.debug(Print.backoff(link, store, {delay: backoffTime, statusCode}, true));
|
||||||
|
|
||||||
await delay(backoff.time);
|
await delay(backoff.time);
|
||||||
|
|
||||||
backoff.count++;
|
backoff.count++;
|
||||||
backoff.time = Math.min(backoff.time * 2, Config.browser.maxBackoff);
|
backoff.time = Math.min(backoff.time * 2, config.browser.maxBackoff);
|
||||||
|
|
||||||
return backoffTime;
|
return backoffTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {NvidiaRegionInfo, regionInfos} from '../nvidia-api';
|
import {NvidiaRegionInfo, regionInfos} from '../nvidia-api';
|
||||||
import {usingPage, usingResponse} from '../../../util';
|
import {usingPage, usingResponse} from '../../../util';
|
||||||
import {Browser} from 'puppeteer';
|
import {Browser} from 'puppeteer';
|
||||||
import {Config} from '../../../config';
|
import {config} from '../../../config';
|
||||||
import {Logger} from '../../../logger';
|
import {logger} from '../../../logger';
|
||||||
import open from 'open';
|
import open from 'open';
|
||||||
|
|
||||||
interface NvidiaSessionTokenJSON {
|
interface NvidiaSessionTokenJSON {
|
||||||
@@ -34,7 +34,7 @@ export class NvidiaCart {
|
|||||||
|
|
||||||
await this.refreshSessionToken();
|
await this.refreshSessionToken();
|
||||||
|
|
||||||
setTimeout(callback, Config.nvidia.sessionTtl);
|
setTimeout(callback, config.nvidia.sessionTtl);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isKeepAlive = true;
|
this.isKeepAlive = true;
|
||||||
@@ -47,7 +47,7 @@ export class NvidiaCart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get regionInfo(): NvidiaRegionInfo {
|
public get regionInfo(): NvidiaRegionInfo {
|
||||||
const country = Config.store.country;
|
const country = config.store.country;
|
||||||
const regionInfo = regionInfos.get(country);
|
const regionInfo = regionInfos.get(country);
|
||||||
if (!regionInfo) {
|
if (!regionInfo) {
|
||||||
throw new Error(`Unknown country ${country}`);
|
throw new Error(`Unknown country ${country}`);
|
||||||
@@ -62,20 +62,20 @@ export class NvidiaCart {
|
|||||||
|
|
||||||
public async addToCard(productId: number, name: string): Promise<string> {
|
public async addToCard(productId: number, name: string): Promise<string> {
|
||||||
let cartUrl: string | undefined;
|
let cartUrl: string | undefined;
|
||||||
Logger.info(`🚀🚀🚀 [nvidia] ${name}, starting auto add to cart 🚀🚀🚀`);
|
logger.info(`🚀🚀🚀 [nvidia] ${name}, starting auto add to cart 🚀🚀🚀`);
|
||||||
try {
|
try {
|
||||||
Logger.info(`🚀🚀🚀 [nvidia] ${name}, adding to cart 🚀🚀🚀`);
|
logger.info(`🚀🚀🚀 [nvidia] ${name}, adding to cart 🚀🚀🚀`);
|
||||||
let lastError: Error | string | undefined;
|
let lastError: Error | string | undefined;
|
||||||
|
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
for (let i = 0; i < Config.nvidia.addToCardAttempts; i++) {
|
for (let i = 0; i < config.nvidia.addToCardAttempts; i++) {
|
||||||
try {
|
try {
|
||||||
cartUrl = await this.addToCartAndGetLocationRedirect(productId);
|
cartUrl = await this.addToCartAndGetLocationRedirect(productId);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`✖ [nvidia] ${name} could not automatically add to cart, attempt ${i + 1} of ${Config.nvidia.addToCardAttempts}`, error);
|
logger.error(`✖ [nvidia] ${name} could not automatically add to cart, attempt ${i + 1} of ${config.nvidia.addToCardAttempts}`, error);
|
||||||
Logger.debug(error);
|
logger.debug(error);
|
||||||
|
|
||||||
lastError = error;
|
lastError = error;
|
||||||
}
|
}
|
||||||
@@ -87,13 +87,13 @@ export class NvidiaCart {
|
|||||||
throw lastError;
|
throw lastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info(`🚀🚀🚀 [nvidia] ${name}, opening checkout page 🚀🚀🚀`);
|
logger.info(`🚀🚀🚀 [nvidia] ${name}, opening checkout page 🚀🚀🚀`);
|
||||||
Logger.info(cartUrl);
|
logger.info(cartUrl);
|
||||||
|
|
||||||
await open(cartUrl);
|
await open(cartUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`✖ [nvidia] ${name} could not automatically add to cart, opening page`);
|
logger.error(`✖ [nvidia] ${name} could not automatically add to cart, opening page`);
|
||||||
Logger.debug(error);
|
logger.debug(error);
|
||||||
|
|
||||||
cartUrl = this.fallbackCartUrl;
|
cartUrl = this.fallbackCartUrl;
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ export class NvidiaCart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refreshSessionToken(): Promise<void> {
|
public async refreshSessionToken(): Promise<void> {
|
||||||
Logger.debug('ℹ [nvidia] refreshing session token');
|
logger.debug('ℹ [nvidia] refreshing session token');
|
||||||
try {
|
try {
|
||||||
const result = await usingResponse(this.browser, this.sessionUrl, async response => {
|
const result = await usingResponse(this.browser, this.sessionUrl, async response => {
|
||||||
return response?.json() as NvidiaSessionTokenJSON | undefined;
|
return response?.json() as NvidiaSessionTokenJSON | undefined;
|
||||||
@@ -126,10 +126,10 @@ export class NvidiaCart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.sessionToken = result.session_token;
|
this.sessionToken = result.session_token;
|
||||||
Logger.debug(`ℹ [nvidia] session_token=${result.session_token}`);
|
logger.debug(`ℹ [nvidia] session_token=${result.session_token}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message: string = typeof error === 'object' ? error.message : error;
|
const message: string = typeof error === 'object' ? error.message : error;
|
||||||
Logger.error(`✖ [nvidia] ${message}`);
|
logger.error(`✖ [nvidia] ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ export class NvidiaCart {
|
|||||||
const url = 'https://api-prod.nvidia.com/direct-sales-shop/DR/add-to-cart';
|
const url = 'https://api-prod.nvidia.com/direct-sales-shop/DR/add-to-cart';
|
||||||
const sessionToken = await this.getSessionToken();
|
const sessionToken = await this.getSessionToken();
|
||||||
|
|
||||||
Logger.info(`ℹ [nvidia] session_token=${sessionToken}`);
|
logger.info(`ℹ [nvidia] session_token=${sessionToken}`);
|
||||||
|
|
||||||
const locationData = await usingPage(this.browser, async page => {
|
const locationData = await usingPage(this.browser, async page => {
|
||||||
page.removeAllListeners('request');
|
page.removeAllListeners('request');
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import {NvidiaRegionInfo, regionInfos} from '../nvidia-api';
|
import {NvidiaRegionInfo, regionInfos} from '../nvidia-api';
|
||||||
import {Browser} from 'puppeteer';
|
import {Browser} from 'puppeteer';
|
||||||
import {Config} from '../../../config';
|
|
||||||
import {Link} from '../store';
|
import {Link} from '../store';
|
||||||
import {NvidiaCart} from './nvidia-cart';
|
import {NvidiaCart} from './nvidia-cart';
|
||||||
|
import {config} from '../../../config';
|
||||||
import {timestampUrlParameter} from '../../timestamp-url-parameter';
|
import {timestampUrlParameter} from '../../timestamp-url-parameter';
|
||||||
|
|
||||||
function getRegionInfo(): NvidiaRegionInfo {
|
function getRegionInfo(): NvidiaRegionInfo {
|
||||||
let country = Config.store.country;
|
let country = config.store.country;
|
||||||
if (!regionInfos.has(country)) {
|
if (!regionInfos.has(country)) {
|
||||||
country = 'usa';
|
country = 'usa';
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ export function generateSetupAction() {
|
|||||||
return async (browser: Browser) => {
|
return async (browser: Browser) => {
|
||||||
cart = new NvidiaCart(browser);
|
cart = new NvidiaCart(browser);
|
||||||
|
|
||||||
if (Config.browser.open) {
|
if (config.browser.open) {
|
||||||
cart.keepAlive();
|
cart.keepAlive();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-11
@@ -8,11 +8,9 @@ import {AsusDe} from './asus-de';
|
|||||||
import {BAndH} from './bandh';
|
import {BAndH} from './bandh';
|
||||||
import {BestBuy} from './bestbuy';
|
import {BestBuy} from './bestbuy';
|
||||||
import {BestBuyCa} from './bestbuy-ca';
|
import {BestBuyCa} from './bestbuy-ca';
|
||||||
import {Config} from '../../config';
|
|
||||||
import {Evga} from './evga';
|
import {Evga} from './evga';
|
||||||
import {EvgaEu} from './evga-eu';
|
import {EvgaEu} from './evga-eu';
|
||||||
import {Gamestop} from './gamestop';
|
import {Gamestop} from './gamestop';
|
||||||
import {Logger} from '../../logger';
|
|
||||||
import {MicroCenter} from './microcenter';
|
import {MicroCenter} from './microcenter';
|
||||||
import {Newegg} from './newegg';
|
import {Newegg} from './newegg';
|
||||||
import {NeweggCa} from './newegg-ca';
|
import {NeweggCa} from './newegg-ca';
|
||||||
@@ -22,6 +20,8 @@ import {OfficeDepot} from './officedepot';
|
|||||||
import {Pny} from './pny';
|
import {Pny} from './pny';
|
||||||
import {Store} from './store';
|
import {Store} from './store';
|
||||||
import {Zotac} from './zotac';
|
import {Zotac} from './zotac';
|
||||||
|
import {config} from '../../config';
|
||||||
|
import {logger} from '../../logger';
|
||||||
|
|
||||||
const masterList = new Map([
|
const masterList = new Map([
|
||||||
[Adorama.name, Adorama],
|
[Adorama.name, Adorama],
|
||||||
@@ -49,27 +49,27 @@ const masterList = new Map([
|
|||||||
|
|
||||||
const list = new Map();
|
const list = new Map();
|
||||||
|
|
||||||
for (const name of Config.store.stores) {
|
for (const name of config.store.stores) {
|
||||||
if (masterList.has(name)) {
|
if (masterList.has(name)) {
|
||||||
list.set(name, masterList.get(name));
|
list.set(name, masterList.get(name));
|
||||||
} else {
|
} else {
|
||||||
const logString = `No store named ${name}, skipping.`;
|
const logString = `No store named ${name}, skipping.`;
|
||||||
Logger.warn(logString);
|
logger.warn(logString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info(`ℹ selected stores: ${Array.from(list.keys()).join(', ')}`);
|
logger.info(`ℹ selected stores: ${Array.from(list.keys()).join(', ')}`);
|
||||||
|
|
||||||
if (Config.store.showOnlyBrands.length > 0) {
|
if (config.store.showOnlyBrands.length > 0) {
|
||||||
Logger.info(`ℹ selected brands: ${Config.store.showOnlyBrands.join(', ')}`);
|
logger.info(`ℹ selected brands: ${config.store.showOnlyBrands.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.store.showOnlyModels.length > 0) {
|
if (config.store.showOnlyModels.length > 0) {
|
||||||
Logger.info(`ℹ selected models: ${Config.store.showOnlyModels.join(', ')}`);
|
logger.info(`ℹ selected models: ${config.store.showOnlyModels.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.store.showOnlySeries.length > 0) {
|
if (config.store.showOnlySeries.length > 0) {
|
||||||
Logger.info(`ℹ selected series: ${Config.store.showOnlySeries.join(', ')}`);
|
logger.info(`ℹ selected series: ${config.store.showOnlySeries.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Stores = Array.from(list.values()) as Store[];
|
export const Stores = Array.from(list.values()) as Store[];
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import {Config} from '../../config';
|
|
||||||
import {Store} from './store';
|
import {Store} from './store';
|
||||||
|
import {config} from '../../config';
|
||||||
|
|
||||||
|
const microCenterLocation = config.store.microCenterLocation;
|
||||||
|
|
||||||
const MicroCenterLocation = Config.store.microCenterLocation;
|
|
||||||
const microCenterLocationToId: Map<string, string> = new Map([
|
const microCenterLocationToId: Map<string, string> = new Map([
|
||||||
['web', '029'],
|
['web', '029'],
|
||||||
['brooklyn', '115'],
|
['brooklyn', '115'],
|
||||||
@@ -32,10 +33,10 @@ const microCenterLocationToId: Map<string, string> = new Map([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let storeId: string;
|
let storeId: string;
|
||||||
if (microCenterLocationToId.get(MicroCenterLocation) === undefined) {
|
if (microCenterLocationToId.get(microCenterLocation) === undefined) {
|
||||||
storeId = '029';
|
storeId = '029';
|
||||||
} else {
|
} else {
|
||||||
storeId = microCenterLocationToId.get(MicroCenterLocation)!;
|
storeId = microCenterLocationToId.get(microCenterLocation)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MicroCenter: Store = {
|
export const MicroCenter: Store = {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
declare module 'pushbullet';
|
declare module '@hijef/pushbullet';
|
||||||
|
|||||||
+7
-7
@@ -1,11 +1,11 @@
|
|||||||
import {Browser, Page, Response} from 'puppeteer';
|
import {Browser, Page, Response} from 'puppeteer';
|
||||||
import {Config} from './config';
|
|
||||||
import {Logger} from './logger';
|
|
||||||
import {StatusCodeRangeArray} from './store/model';
|
import {StatusCodeRangeArray} from './store/model';
|
||||||
|
import {config} from './config';
|
||||||
import {disableBlockerInPage} from './adblocker';
|
import {disableBlockerInPage} from './adblocker';
|
||||||
|
import {logger} from './logger';
|
||||||
|
|
||||||
export function getSleepTime() {
|
export function getSleepTime() {
|
||||||
return Config.browser.minSleep + (Math.random() * (Config.browser.maxSleep - Config.browser.minSleep));
|
return config.browser.minSleep + (Math.random() * (config.browser.maxSleep - config.browser.minSleep));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function delay(ms: number) {
|
export async function delay(ms: number) {
|
||||||
@@ -47,8 +47,8 @@ export async function usingResponse<T>(
|
|||||||
|
|
||||||
export async function usingPage<T>(browser: Browser, cb: (page: Page, browser: Browser) => Promise<T>): Promise<T> {
|
export async function usingPage<T>(browser: Browser, cb: (page: Page, browser: Browser) => Promise<T>): Promise<T> {
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
page.setDefaultNavigationTimeout(Config.page.navigationTimeout);
|
page.setDefaultNavigationTimeout(config.page.timeout);
|
||||||
await page.setUserAgent(Config.page.userAgent);
|
await page.setUserAgent(config.page.userAgent);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await cb(page, browser);
|
return await cb(page, browser);
|
||||||
@@ -56,13 +56,13 @@ export async function usingPage<T>(browser: Browser, cb: (page: Page, browser: B
|
|||||||
try {
|
try {
|
||||||
await closePage(page);
|
await closePage(page);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(error);
|
logger.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function closePage(page: Page) {
|
export async function closePage(page: Page) {
|
||||||
if (!Config.browser.lowBandwidth) {
|
if (!config.browser.lowBandwidth) {
|
||||||
await disableBlockerInPage(page);
|
await disableBlockerInPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user