Compare commits

..

74 Commits

Author SHA1 Message Date
github-actions[bot] b97d581607 chore: release 3.3.0 (#1233) 2020-12-13 00:41:50 -05:00
Mark Dietzer eb700762a4 fix(adblock): proxy another function to produce less errors (#1379) 2020-12-13 00:40:57 -05:00
Jef LeCompte 38630d478a docs: update mkdocs appearance 2020-12-13 00:26:47 -05:00
Jef LeCompte 2a5e2e8733 chore: update logos 2020-12-13 00:23:20 -05:00
James White 579cb97a0d feat(store): add several Australian stores, add some 3080 brands/models (#1367) 2020-12-12 20:14:02 -05:00
Conor Broderick f70998f0d9 feat(store): add Irish store variants (#1373) 2020-12-12 20:08:05 -05:00
sohaiiil 02825d009b fix(store): update UK stores (#1372) 2020-12-12 20:07:10 -05:00
Jef LeCompte 0d75025cbb docs(discord): add xbox* groups
Resolves #1352
2020-12-12 09:46:43 -05:00
Mike H db47827e6e docs(filter): update El Corte Inglés location (#1369) 2020-12-12 09:44:03 -05:00
Jef LeCompte c3beedced8 fix(newegg): price selector and mobile inStock
Fixes #1356
2020-12-12 09:38:35 -05:00
Dylan Fontaine 0525119b9b fix(bestbuy-ca): update selectors to help false positives (#1278)
Resolves #895
2020-12-12 09:23:19 -05:00
Jef LeCompte b6964b02f3 fix(amazon-nl): inStock selector
Fixes #1366
2020-12-12 09:19:28 -05:00
moiLegacy 7329c6ede0 fix(newegg): dedupe nitro+ models (#1300)
Fixes #1230
2020-12-12 09:06:41 -05:00
sohaiiil cccfde245a feat(discord): dynamic currency symbol (#1328) 2020-12-12 09:01:54 -05:00
Jef LeCompte ad0b2fe525 fix(discord): notification groupings 2020-12-11 18:04:22 -05:00
Jef LeCompte 064ce83f03 ci: add workflow_dispatch for quick releases 2020-12-11 13:28:32 -05:00
Jef LeCompte ef452d8e58 fix(config): remove quotes 2020-12-11 13:27:19 -05:00
Jef LeCompte dca916b64e fix(config): disable docker by default 2020-12-11 12:50:18 -05:00
Jef LeCompte e2bf5749c1 docs: update docker-compose title 2020-12-11 12:38:29 -05:00
Jef LeCompte da78362045 docs: update docker-compose information 2020-12-11 12:37:33 -05:00
Egidijus Ligeika c74ea28014 fix(docker): run in docker, and build scripts for docker (#1291)
Updates unnecessary need to grant special privileges when running in Docker.
2020-12-11 12:13:11 -05:00
Will Whitney 67b19a7a8b feat(playstation): add queuing selector (#1173) 2020-12-11 12:08:47 -05:00
Jef LeCompte 5786481b31 docs: medimax is de store 2020-12-11 12:02:48 -05:00
Jef LeCompte 9b7c7e2881 fix: .env backwards compatibility 2020-12-11 08:11:57 -05:00
Jef LeCompte 7e3020a927 docs: include max prices for adding a store
Based on https://github.com/jef/streetmerchant/discussions/1146#discussioncomment-168047
2020-12-11 02:56:37 -05:00
Jef LeCompte 3386e8f50d revert: allow users to still use USER_AGENT
Removed from documentation, but older users can utilize
until fully removed in 4.0.0
2020-12-11 02:50:04 -05:00
dependabot[bot] 7bfcc81fdc chore(deps): bump twilio from 3.53.0 to 3.54.0 (#1287)
Bumps [twilio](https://github.com/twilio/twilio-node) from 3.53.0 to 3.54.0.
- [Release notes](https://github.com/twilio/twilio-node/releases)
- [Changelog](https://github.com/twilio/twilio-node/blob/main/CHANGES.md)
- [Commits](https://github.com/twilio/twilio-node/compare/3.53.0...3.54.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-11 02:41:05 -05:00
dependabot[bot] 37962f00e5 chore(deps): bump twitch-chat-client from 4.3.5 to 4.3.6 (#1308)
Bumps [twitch-chat-client](https://github.com/d-fischer/twitch/tree/HEAD/packages/twitch-chat-client) from 4.3.5 to 4.3.6.
- [Release notes](https://github.com/d-fischer/twitch/releases)
- [Commits](https://github.com/d-fischer/twitch/commits/v4.3.6/packages/twitch-chat-client)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-11 02:06:54 -05:00
dependabot[bot] d92d58d235 chore(deps-dev): bump @types/puppeteer from 5.4.1 to 5.4.2 (#1289)
Bumps [@types/puppeteer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/puppeteer) from 5.4.1 to 5.4.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/puppeteer)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-11 02:06:35 -05:00
Jef LeCompte 9b3906ca00 refactor: change .env to be dotenv (#1341) 2020-12-11 01:57:49 -05:00
dependabot[bot] 0683430768 chore(deps): bump actions/setup-node from v2.1.2 to v2.1.3 (#1338)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from v2.1.2 to v2.1.3.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.1.2...27082cecf3ff7a1742dbd5e12605f0cb59dce2d9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-11 01:16:34 -05:00
dependabot[bot] b6ea0ea991 chore(deps-dev): bump @types/node from 14.14.10 to 14.14.12 (#1340)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.10 to 14.14.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-11 01:16:23 -05:00
Jef LeCompte 68eede5487 docs: update format for models and countries 2020-12-11 01:14:39 -05:00
moodswung 29175c77a8 feat(store): add darkhero motherboard to asus and microcenter (#1336) 2020-12-11 01:13:26 -05:00
xeaon a44621f8f5 fix: add setRequestInterception (#1312)
Fixes #826
2020-12-11 01:09:08 -05:00
Dylan Fontaine 27cfb94de8 feat(store): add EBGames (#1281) 2020-12-11 01:02:32 -05:00
Jef LeCompte b599c23b51 feat(lookup): use random user agents (#1335) 2020-12-11 00:33:32 -05:00
Jef LeCompte 2115c0c906 chore: remove default label on bug issue 2020-12-10 15:14:21 -05:00
Eugene Shalygin 4f60ad2a82 chore(store): add a few AMD listings for German stores (#1321) 2020-12-10 13:28:30 -05:00
Warwick Marangos 8d192317ba feat(notification): add sound player executable option (#1301)
Pass the path to the player executable as a players array to play-sound so that it will check if the executable exists.
2020-12-09 20:59:10 -05:00
SizzlinSeal 3841666d03 chore(amazon-ca): add rtx 3070 cards (#1299) 2020-12-09 19:17:22 -05:00
Sebastian 2632386a5b feat(notification): add discord groups (#1211) 2020-12-09 14:54:51 -05:00
Jef LeCompte 9dc04b3638 chore: move linksBuilder log, add outOfStock amd-de 2020-12-09 11:30:56 -05:00
Jef LeCompte b2738370b4 chore(amazon): fix evga model 2020-12-09 10:32:42 -05:00
moiLegacy 54ef0ef995 chore(officedepot): add 3070 (#1280) 2020-12-09 02:23:43 -05:00
StanZha ba5c50495b chore(store): add rx6900xt for microcenter, newegg, bestbuy, amd (#1284) 2020-12-09 02:22:57 -05:00
Jef LeCompte 0e7de05fd2 docs: fix reference 2020-12-08 22:24:49 -05:00
Jef LeCompte 578027a36b chore(evga): add 3080 hydro copper
Resolve #965
2020-12-08 17:58:08 -05:00
Jef LeCompte 5e1dfc3568 fix(eprice): inStock selector
Fixes #1067

Co-authored-by: Maecenass
2020-12-08 17:47:02 -05:00
Zethus Suen 5955d10a7f feat(docs): add table sorting (#1258) 2020-12-08 17:22:33 -05:00
Jef LeCompte 64f55c1b86 chore: update labels 2020-12-08 17:09:36 -05:00
Jef LeCompte 1538937661 chore: fix issue template config 2020-12-08 17:06:58 -05:00
Jef LeCompte 46bed1ef3c chore: update issue templates 2020-12-08 17:02:00 -05:00
Jef LeCompte b56d214059 docs: fix hotlink
Fixes #1273
2020-12-08 16:51:13 -05:00
Jef LeCompte 625ae0ee53 docs: add rotating proxy note 2020-12-08 16:33:55 -05:00
Jef LeCompte d7602f3a0f docs: add discussion board 2020-12-08 15:54:26 -05:00
dtsulik b806e1bb9c chore(newegg): add powercolor red devil, red dragon (#1219) 2020-12-08 15:41:17 -05:00
Jef LeCompte 58f0a9c774 fix(bestbuy-ca): add outOfStock text
Resolves #1269
2020-12-08 15:18:05 -05:00
Jef LeCompte 48a46d0217 chore: remove husky, use include with tsconfig
Let CI handle linting
2020-12-08 15:07:47 -05:00
Jeremy Beasley 53eeff0bfc docs: fix typo in README (#1268) 2020-12-08 15:02:42 -05:00
Dylan Fontaine ac0bd2ac14 feat(store): add Toys R Us (#1257) 2020-12-08 15:01:08 -05:00
Jef LeCompte 0072dda90b fix(pushover): add expire and retry
Fixes #983
2020-12-08 14:20:15 -05:00
Jef LeCompte d56bcddb05 chore(store): add xbox to bestbuy-ca and amazon-ca
Resolves #982
2020-12-08 13:49:30 -05:00
Jef LeCompte 5aa4fdc7b9 chore: remove feature request and questions
Remove issue templates in favor of new https://github.com/jef/streetmerchant/discussions! This is beneficial so we can keep issues related to app development.
2020-12-08 13:01:45 -05:00
Jef LeCompte 60508e2ddb chore(deps): update twitch, husky, ts-node, xo 2020-12-08 11:50:04 -05:00
dependabot[bot] 4f7c9ed84f chore(deps-dev): bump @types/cheerio from 0.22.22 to 0.22.23 (#1251)
Bumps [@types/cheerio](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/cheerio) from 0.22.22 to 0.22.23.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/cheerio)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-08 11:47:47 -05:00
Luca Rinaldi e3eb38655a fix(store): italian store checks (#1212) 2020-12-08 11:37:30 -05:00
Dylan Fontaine b535b470ca feat(store): add Walmart (CA) (#1253) 2020-12-08 11:35:00 -05:00
Hangur 30bed2ac01 feat(store): add Megekko (#1216) 2020-12-08 08:36:20 -05:00
Darwin Do 84a6ff5973 chore(amazon): add 3060ti cards (#1229) 2020-12-07 12:01:41 -05:00
Simone Cavazzoni 84f2cb7d52 fix(amazon-it): maxPrice selector and links (#1239) 2020-12-07 12:00:27 -05:00
Jef LeCompte d5bd7e3db2 docs: update image link 2020-12-07 00:26:10 -05:00
Jef LeCompte 1f63e99804 fix(ci): mkdocs deps 2020-12-07 00:21:38 -05:00
Jef LeCompte 243109a4ff feat(docs): add mkdocs 2020-12-07 00:18:43 -05:00
163 changed files with 4477 additions and 1383 deletions
+8 -3
View File
@@ -1,12 +1,17 @@
root = true root = true
[*] [*]
indent_style = tab indent_style = space
indent_size = 2
end_of_line = lf end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.{yml, json, md}] [*.md]
indent_style = space indent_style = space
indent_size = 2 indent_size = 4
[*.ts]
indent_style = tab
indent_size = 4
-98
View File
@@ -1,98 +0,0 @@
# ** All configuration variables are optional **
# Read https://github.com/jef/streetmerchant#customization for help on customizing this file
#############################################################################################
ASCII_BANNER=""
ASCII_COLOR=""
AUTO_ADD_TO_CART=""
BROWSER_TRUSTED=""
COUNTRY=""
DESKTOP_NOTIFICATIONS=""
DISCORD_NOTIFY_GROUP=""
DISCORD_WEB_HOOK=""
EMAIL_PASSWORD=""
EMAIL_TO=""
EMAIL_USERNAME=""
HEADLESS=""
IN_STOCK_WAIT_TIME=""
LOG_LEVEL=""
LOW_BANDWIDTH=""
MAX_PRICE_SERIES_3060TI=""
MAX_PRICE_SERIES_3070=""
MAX_PRICE_SERIES_3080=""
MAX_PRICE_SERIES_3090=""
MAX_PRICE_SERIES_CORSAIR_SF=""
MAX_PRICE_SERIES_RX6800=""
MAX_PRICE_SERIES_RX6800XT=""
MAX_PRICE_SERIES_RX6900XT=""
MAX_PRICE_SERIES_RYZEN5600=""
MAX_PRICE_SERIES_RYZEN5800=""
MAX_PRICE_SERIES_RYZEN5900=""
MAX_PRICE_SERIES_RYZEN5950=""
MAX_PRICE_SERIES_SONYPS5C=""
MAX_PRICE_SERIES_SONYPS5DE=""
MAX_PRICE_SERIES_TEST=""
MICROCENTER_LOCATION=""
MQTT_BROKER_ADDRESS=""
MQTT_BROKER_PORT=""
MQTT_CLIENT_ID=""
MQTT_PASSWORD=""
MQTT_QOS=""
MQTT_TOPIC=""
MQTT_USERNAME=""
NVIDIA_ADD_TO_CART_ATTEMPTS=""
NVIDIA_SESSION_TTL=""
OPEN_BROWSER=""
PAGE_BACKOFF_MIN=""
PAGE_BACKOFF_MAX=""
PAGE_SLEEP_MIN=""
PAGE_SLEEP_MAX=""
PAGE_TIMEOUT=""
PAGERDUTY_INTEGRATION_KEY=""
PAGERDUTY_SEVERITY=""
PHILIPS_HUE_API_KEY=""
PHILIPS_HUE_CLOUD_ACCESS_TOKEN=""
PHILIPS_HUE_CLOUD_CLIENT_ID=""
PHILIPS_HUE_CLOUD_CLIENT_SECRET=""
PHILIPS_HUE_CLOUD_REFRESH_TOKEN=""
PHILIPS_HUE_LAN_BRIDGE_IP=""
PHILIPS_HUE_LIGHT_COLOR=""
PHILIPS_HUE_LIGHT_IDS=""
PHILIPS_HUE_LIGHT_PATTERN=""
PHONE_CARRIER=""
PHONE_NUMBER=""
PLAY_SOUND=""
PROXY_ADDRESS=""
PROXY_PROTOCOL=""
PROXY_PORT=""
PUSHBULLET=""
PUSHOVER_TOKEN=""
PUSHOVER_USER=""
PUSHOVER_PRIORITY=""
SCREENSHOT=""
SHOW_ONLY_BRANDS=""
SHOW_ONLY_MODELS=""
SHOW_ONLY_SERIES=""
SLACK_CHANNEL=""
SLACK_TOKEN=""
SMTP_ADDRESS=""
SMTP_PORT=""
STORES=""
TELEGRAM_ACCESS_TOKEN=""
TELEGRAM_CHAT_ID=""
TWILIO_ACCOUNT_SID=""
TWILIO_AUTH_TOKEN=""
TWILIO_FROM_NUMBER=""
TWILIO_TO_NUMBER=""
TWITCH_ACCESS_TOKEN=""
TWITCH_CHANNEL=""
TWITCH_CLIENT_ID=""
TWITCH_CLIENT_SECRET=""
TWITCH_REFRESH_TOKEN=""
TWITTER_ACCESS_TOKEN_KEY=""
TWITTER_ACCESS_TOKEN_SECRET=""
TWITTER_CONSUMER_KEY=""
TWITTER_CONSUMER_SECRET=""
TWITTER_TWEET_TAGS=""
USER_AGENT=""
WEB_PORT=""
@@ -1,15 +1,12 @@
--- ---
name: 🐛 Bug report name: "\U0001F41B Bug report"
about: Report a bug for this project about: Report a bug for this project
title: '' title: ''
labels: 'type: bug' labels: ''
assignees: jef assignees: jef
--- ---
<!--- Feel free to join our discord server (https://discord.gg/gbVY4vB9JF) for general support --->
<!--- If this acts as a feature request please ask yourself if this modification is something the whole userbase will benefit from --->
## Expected Behavior ## Expected Behavior
<!--- Tell us what should happen --> <!--- Tell us what should happen -->
@@ -30,7 +27,7 @@ assignees: jef
## Environment ## Environment
**OS:** **OS:**
**.env file:** **dotenv file:**
```dotenv ```dotenv
+11
View File
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: 💡 Have an idea for a new feature?
url: https://github.com/jef/streetmerchant/discussions
about: Create a new idea discussion!
- name: 🙇 Need help with streetmerchant?
url: https://github.com/jef/streetmerchant/discussions
about: Create a new help discussion if it hasn't been asked before!
- name: 💬 Want to talk with others that use streetmerchant?
url: https://discord.gg/gbVY4vB9JF
about: Join our Discord to hangout and talk shop!
-16
View File
@@ -1,16 +0,0 @@
---
name: 🚀 Feature request
about: Suggest a feature for this project
title: ''
labels: 'type: enhancement'
assignees: jef
---
## Description
<!-- Describe the feature here -->
## Possible solution
<!-- Describe the possible solution here -->
-12
View File
@@ -1,12 +0,0 @@
---
name: 🤔 Question
about: Open to all questions
title: ''
labels: 'type: question'
assignees: jef
---
## Description
<!-- Describe the question here -->
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v2.1.2 - uses: actions/setup-node@v2.1.3
with: with:
node-version: 14.15.0 node-version: 14.15.0
- uses: actions/cache@v2 - uses: actions/cache@v2
+19
View File
@@ -0,0 +1,19 @@
name: documentation
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Python runtime
uses: actions/setup-python@v2
with:
python-version: 3.x
- name: Install Python dependencies
run: pip install mkdocs-material mkdocs-git-revision-date-plugin mkdocs-macros-plugin
- name: Deploy documentation
run: mkdocs gh-deploy --force
+1
View File
@@ -2,6 +2,7 @@ name: nightly
on: on:
schedule: schedule:
- cron: '0 0 * * *' - cron: '0 0 * * *'
workflow_dispatch: {}
jobs: jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+3 -3
View File
@@ -3,10 +3,10 @@
.vscode/ .vscode/
build/ build/
node_modules/ node_modules/
src/config/*.yaml
.env* .env
.*env dotenv
!.env-example
success-*.png success-*.png
*.wav *.wav
+47
View File
@@ -1,5 +1,52 @@
# Changelog # Changelog
## [3.3.0](https://www.github.com/jef/streetmerchant/compare/v3.2.0...v3.3.0) (2020-12-13)
### Features
* **discord:** dynamic currency symbol ([#1328](https://www.github.com/jef/streetmerchant/issues/1328)) ([cccfde2](https://www.github.com/jef/streetmerchant/commit/cccfde245affc11506c69d1ef6c30c09953307d4))
* **docs:** add mkdocs ([243109a](https://www.github.com/jef/streetmerchant/commit/243109a4ffdfe1d1efe961af0b5cf28fd7e6ef1d))
* **docs:** add table sorting ([#1258](https://www.github.com/jef/streetmerchant/issues/1258)) ([5955d10](https://www.github.com/jef/streetmerchant/commit/5955d10a7f2106c5ae1275fadacf2945626ca008))
* **lookup:** use random user agents ([#1335](https://www.github.com/jef/streetmerchant/issues/1335)) ([b599c23](https://www.github.com/jef/streetmerchant/commit/b599c23b51735212e5369ce03a44bbd1bccafa42))
* **notification:** add discord groups ([#1211](https://www.github.com/jef/streetmerchant/issues/1211)) ([2632386](https://www.github.com/jef/streetmerchant/commit/2632386a5b99f3b23166e88f92af809f0036a6b7))
* **notification:** add sound player executable option ([#1301](https://www.github.com/jef/streetmerchant/issues/1301)) ([8d19231](https://www.github.com/jef/streetmerchant/commit/8d192317badfd71a7cd8f0115ba9e6fc951c1874))
* **playstation:** add queuing selector ([#1173](https://www.github.com/jef/streetmerchant/issues/1173)) ([67b19a7](https://www.github.com/jef/streetmerchant/commit/67b19a7a8b3dead5f5ab9575ee2b4f09924ce851))
* **store:** add darkhero motherboard to asus and microcenter ([#1336](https://www.github.com/jef/streetmerchant/issues/1336)) ([29175c7](https://www.github.com/jef/streetmerchant/commit/29175c77a8fcbc082c944b057a919dfcc22ba606))
* **store:** add EBGames ([#1281](https://www.github.com/jef/streetmerchant/issues/1281)) ([27cfb94](https://www.github.com/jef/streetmerchant/commit/27cfb94de8d9ccef3c7fc76b250aee17d7c80257))
* **store:** add Irish store variants ([#1373](https://www.github.com/jef/streetmerchant/issues/1373)) ([f70998f](https://www.github.com/jef/streetmerchant/commit/f70998f0d9a6cdfbc59fb31fecb87f801b0b3037))
* **store:** add Megekko ([#1216](https://www.github.com/jef/streetmerchant/issues/1216)) ([30bed2a](https://www.github.com/jef/streetmerchant/commit/30bed2ac016e9e543f67b1098819a484bc1394c3))
* **store:** add several Australian stores, add some 3080 brands/models ([#1367](https://www.github.com/jef/streetmerchant/issues/1367)) ([579cb97](https://www.github.com/jef/streetmerchant/commit/579cb97a0d151cc374a37493b043ba4a399b55db))
* **store:** add Toys R Us ([#1257](https://www.github.com/jef/streetmerchant/issues/1257)) ([ac0bd2a](https://www.github.com/jef/streetmerchant/commit/ac0bd2ac14d686a0f30931e885da65a1d7af6856))
* **store:** add Walmart (CA) ([#1253](https://www.github.com/jef/streetmerchant/issues/1253)) ([b535b47](https://www.github.com/jef/streetmerchant/commit/b535b470cad38af034889b4d27578b20136e166d))
### Bug Fixes
* **adblock:** proxy another function to produce less errors ([#1379](https://www.github.com/jef/streetmerchant/issues/1379)) ([eb70076](https://www.github.com/jef/streetmerchant/commit/eb700762a4eee3a802bd81f0fb62e76243027c42))
* **amazon-it:** maxPrice selector and links ([#1239](https://www.github.com/jef/streetmerchant/issues/1239)) ([84f2cb7](https://www.github.com/jef/streetmerchant/commit/84f2cb7d52fac7c3dbdd31318a51330e25808784))
* **amazon-nl:** inStock selector ([b6964b0](https://www.github.com/jef/streetmerchant/commit/b6964b02f38c947fd476d86de35d5983e5b229dd)), closes [#1366](https://www.github.com/jef/streetmerchant/issues/1366)
* **bestbuy-ca:** add outOfStock text ([58f0a9c](https://www.github.com/jef/streetmerchant/commit/58f0a9c7740c46f3d820c8765dee5b0fc8516353)), closes [#1269](https://www.github.com/jef/streetmerchant/issues/1269)
* **bestbuy-ca:** update selectors to help false positives ([#1278](https://www.github.com/jef/streetmerchant/issues/1278)) ([0525119](https://www.github.com/jef/streetmerchant/commit/0525119b9bae47425a6c01720d6a1ea6ab8e6a02)), closes [#895](https://www.github.com/jef/streetmerchant/issues/895)
* **ci:** mkdocs deps ([1f63e99](https://www.github.com/jef/streetmerchant/commit/1f63e9980499ac567845473dc48e51cab78c838e))
* **config:** disable docker by default ([dca916b](https://www.github.com/jef/streetmerchant/commit/dca916b64e9ac2d05d5e6af19e5466b8a0623ed5))
* **config:** remove quotes ([ef452d8](https://www.github.com/jef/streetmerchant/commit/ef452d8e586536a5039ee532d655d2951d551ac3))
* **discord:** notification groupings ([ad0b2fe](https://www.github.com/jef/streetmerchant/commit/ad0b2fe525d6403735ddcbf4ef55b91eb36ac10a))
* **docker:** run in docker, and build scripts for docker ([#1291](https://www.github.com/jef/streetmerchant/issues/1291)) ([c74ea28](https://www.github.com/jef/streetmerchant/commit/c74ea28014ee0d827ea216083844f92e30f142c9))
* **eprice:** inStock selector ([5e1dfc3](https://www.github.com/jef/streetmerchant/commit/5e1dfc35680029620a1de7bc7a523140f2dae8af)), closes [#1067](https://www.github.com/jef/streetmerchant/issues/1067)
* **newegg:** dedupe nitro+ models ([#1300](https://www.github.com/jef/streetmerchant/issues/1300)) ([7329c6e](https://www.github.com/jef/streetmerchant/commit/7329c6ede0a5ab6d644b30ac878f4e7da11e43a8)), closes [#1230](https://www.github.com/jef/streetmerchant/issues/1230)
* **newegg:** price selector and mobile inStock ([c3beedc](https://www.github.com/jef/streetmerchant/commit/c3beedced82141e6bbb0735b3edb7c573907aa7a)), closes [#1356](https://www.github.com/jef/streetmerchant/issues/1356)
* **pushover:** add `expire` and `retry` ([0072dda](https://www.github.com/jef/streetmerchant/commit/0072dda90b637b93647cf3a35dc612cf43d89445)), closes [#983](https://www.github.com/jef/streetmerchant/issues/983)
* **store:** update UK stores ([#1372](https://www.github.com/jef/streetmerchant/issues/1372)) ([02825d0](https://www.github.com/jef/streetmerchant/commit/02825d009bd028f6dcdca02e2c155e77a7df8654))
* `.env` backwards compatibility ([9b7c7e2](https://www.github.com/jef/streetmerchant/commit/9b7c7e2881d756909af191094bda435ca7ef7e9b))
* add setRequestInterception ([#1312](https://www.github.com/jef/streetmerchant/issues/1312)) ([a44621f](https://www.github.com/jef/streetmerchant/commit/a44621f8f50cac9ba6614b0f89320d210370047a)), closes [#826](https://www.github.com/jef/streetmerchant/issues/826)
* **store:** italian store checks ([#1212](https://www.github.com/jef/streetmerchant/issues/1212)) ([e3eb386](https://www.github.com/jef/streetmerchant/commit/e3eb38655a3cefb85c4dbd573494955e06c72f71))
### Reverts
* allow users to still use `USER_AGENT` ([3386e8f](https://www.github.com/jef/streetmerchant/commit/3386e8f50da27a800c5289c3b6bd7a1f76e77a49))
## [3.2.0](https://www.github.com/jef/streetmerchant/compare/v3.1.0...v3.2.0) (2020-12-06) ## [3.2.0](https://www.github.com/jef/streetmerchant/compare/v3.1.0...v3.2.0) (2020-12-06)
+17
View File
@@ -0,0 +1,17 @@
.DEFAULT_GOAL := run
.PHONY: build
build:
docker-compose build streetmerchant
.PHONY: run
run:
docker-compose up
.PHONY: run-detached
run-detached:
docker-compose up -d
.PHONY: stop
stop:
docker-compose down
+38 -552
View File
@@ -1,562 +1,48 @@
<p align="center"><a href="https://github.com/jef/streetmerchant#readme"><img src="https://raw.githubusercontent.com/jef/streetmerchant/main/media/streetmerchant.png" alt="streetmerchant" /></a></p> <p align="center">
<p align="center">The world's easiest, most powerful stock checker <a href="https://jef.codes/streetmerchant"
<br/><br/> ><img
<a href="https://github.com/jef/streetmerchant/actions?query=workflow%3Aci"><img src="https://github.com/jef/streetmerchant/workflows/ci/badge.svg" /></a> src="https://raw.githubusercontent.com/jef/streetmerchant/main/docs/assets/images/streetmerchant-banner.png"
<a href="https://discord.gg/gbVY4vB9JF"><img src="https://img.shields.io/discord/773913070665859073.svg?label=chat&logo=discord&logoColor=ffffff&color=7389D8" alt="Tweet" /></a> alt="streetmerchant"
<a href="https://twitter.com/intent/tweet?text=Beat%20the%20masses%20with%20streetmerchant&url=https://github.com/jef/streetmerchant&hashtags=typescript,opensource,bot,shopping"><img src="https://img.shields.io/badge/twitter-share-green?logo=twitter&style=social" alt="Tweet" /></a> /></a>
<br/><br/> </p>
<a href="https://github.com/jef/streetmerchant#faq">FAQ</a> | <p align="center">
<a href="https://github.com/jef/streetmerchant/issues">Issues</a> | <strong>The world's easiest, most powerful stock checker</strong>
<a href="https://github.com/jef/streetmerchant/wiki">Wiki</a> </p>
<p align="center">
<a href="https://github.com/jef/streetmerchant/actions?query=workflow%3Aci"
><img src="https://github.com/jef/streetmerchant/workflows/ci/badge.svg"
/></a>
<a href="https://discord.gg/gbVY4vB9JF"
><img
src="https://img.shields.io/discord/773913070665859073.svg?label=chat&logo=discord&logoColor=ffffff&color=7389D8"
alt="Tweet"
/></a>
<a
href="https://twitter.com/intent/tweet?text=Beat%20the%20masses%20with%20streetmerchant&url=https://github.com/jef/streetmerchant&hashtags=typescript,opensource,bot,shopping"
><img
src="https://img.shields.io/badge/twitter-share-green?logo=twitter&style=social"
alt="Tweet"
/></a>
</p>
<p align="center">
<em>To get started, visit <a href="https://jef.codes/streetmerchant">jef.codes/streetmerchant</a></em>
</p> </p>
## Features ## Features
- Scrapes multiple websites for patterns of being stocked via API and Chromium First and foremost, this service _will not_ automatically buy for you.
- Opens browser when stock is available
- Ability to send notifications when stock is available
> :point_right: The bot _will not_ automatically buy for you - **Checks stock continuously** -- runs 24/7, 365, looking for the items you want.
- **Ready for checkout** -- ability to add to cart when available and even opens the browser for you.
- **Notifications galore** -- when you're not by your computer, worry free with notifications to most platforms and devices when an item comes in stock.
## Installation overview ## Quick start
Linux, macOS, and Windows are all capable operating systems. streetmerchant runs on Node.js 14:
You do not need any computer skills, smarts, or anything of that nature. You are very capable as you have made it this ```shell
far. Some basic understanding how a terminal, git, and or Node.js is a bonus, but that does not limit you to git clone https://github.com/jef/streetmerchant.git
getting `streetmerchant` running! cd streetmerchant && npm i && npm run start
### Installation: native
| Reference | Note |
|:---:|---|
| tag | Example, `v1.0.0`; stable |
| `main` | Latest HEAD; not tagged, could be unstable |
- [Node.js 14](https://nodejs.org/en/)
- [git](https://git-scm.com/)
- Clone this project `git clone https://github.com/jef/streetmerchant.git`
- To checkout a particular ref, use `git checkout <ref name>` after cloning
- Navigate to this project by entering `cd streetmerchant`
- Run `npm install`
- Copy `.env-example` to a new file `.env` and edit the `.env` file to your liking using
your [favorite text editor](https://code.visualstudio.com/)
- More on this in [customization](#Customization)
- Run `npm run start` to start
At any point you want the program to stop, use <kbd>Ctrl</kbd> + <kbd>C</kbd>.
> :point_right: Please visit the [wiki](https://github.com/jef/streetmerchant/wiki) if you need more help with installation.
### Installation: Docker
Available via GitHub Container Registry.
| Tag | Note |
|:---:|---|
| `latest` | Latest release; stable |
| `nightly` | Latest HEAD each day at midnight UTC; could be unstable |
```sh
# to run
docker run --cap-add=SYS_ADMIN \
-it --rm --env-file ./.env \
ghcr.io/jef/streetmerchant:nightly
# to test notifications
docker run --cap-add=SYS_ADMIN \
-it --rm --env-file ./.env \
ghcr.io/jef/streetmerchant:nightly test:notification:production
``` ```
### Developer notes For more information and customization, visit [jef.codes/streetmerchant/getting-started](https://jef.codes/streetmerchant/getting-started).
The command `npm run start:dev` can be used instead of `npm run start` to automatically restart the project when
filesystem changes are detected in the `src/` folder or `.env` file.
## Customization
To customize `streetmerchant`, make a copy of `.env-example` as `.env` and make any changes to your liking. _All
environment variables are **optional**._
<details>
<summary>Expand to see all available options</summary>
### Application
| Environment variable | Description | Notes |
|:---:|---|---|
| `AUTO_ADD_TO_CART` | Enable auto add to cart on support stores | Default: `true` |
| `BROWSER_TRUSTED` | Skip Chromium Sandbox | Useful for containerized environments, default: `false` |
| `HEADLESS` | Puppeteer to run headless or not | Debugging related, default: `true` |
| `INCOGNITO` | Puppeteer to run incognito or not | Debugging related, default: `false` |
| `IN_STOCK_WAIT_TIME` | Time to wait between requests to the same link if it has that card in stock | In seconds, default: `0` |
| `LOG_LEVEL` | [Logging levels](https://github.com/winstonjs/winston#logging-levels) | Debugging related, default: `info` |
| `LOW_BANDWIDTH` | Blocks images/fonts to reduce traffic | Disables ad blocker, default: `false` |
| `OPEN_BROWSER` | Toggle for whether or not the browser should open when item is found | Default: `true` |
| `PAGE_BACKOFF_MIN` | Minimum backoff time between retrying requests for the same store when a forbidden response is received | Default: `10000` |
| `PAGE_BACKOFF_MAX` | Maximum backoff time between retrying requests for the same store when a forbidden response is received | Default: `3600000` |
| `PAGE_SLEEP_MIN` | Minimum sleep time between queries of the same product page | In milliseconds, default: `5000` |
| `PAGE_SLEEP_MAX` | Maximum sleep time between queries of the same product page | In milliseconds, default: `10000` |
| `PAGE_TIMEOUT` | Navigation Timeout in milliseconds | `0` for infinite, default: `30000` |
| `PROXY_PROTOCOL` | protocol of proxy server, such as `socks5` | default: `http` |
| `PROXY_ADDRESS` | IP Address or fqdn of proxy server |
| `PROXY_PORT` | TCP Port number on which the proxy is listening for connections | Default: `80` |
| `SCREENSHOT` | Capture screenshot of page if a card is found | Default: `true` |
| `USER_AGENT` | Custom User-Agents headers for HTTP requests | Newline separated, e.g.: `USER_AGENT_STRING1 \n USER_AGENT_STRING2` | | Default: `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36` |
| `WEB_PORT` | Starts a webserver to be able to control the bot while it is running; optional | Default: disabled |
> :point_right: You can find your computer's user agent by [searching google for "my user agent"](http://google.com/search?q=my+user+agent)
> :point_right: Data usage is [known to be high](https://github.com/jef/streetmerchant/issues?q=is%3Aissue+sort%3Aupdated-desc+bandwidth). This is expected as the program scrapes many websites in parallel 24/7. To help reduce this, use `LOW_BANDWIDTH="true"`. We are looking into other solutions as well, but is low priority.
### Filters
| Environment variable | Description | Notes |
|:---:|---|---|
| `COUNTRY` | [Supported country](#supported-countries) you want to be scraped | Only used with `nvidia-api`, default: `usa` |
| `MAX_PRICE_SERIES_3060TI` | Maximum price allowed for a match, applies 3060 Ti series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_3070` | Maximum price allowed for a match, applies 3070 series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_3080` | Maximum price allowed for a match, applies 3080 series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_3090` | Maximum price allowed for a match, applies 3090 series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_CORSAIR_SF` | Maximum price allowed for a match, applies to Corsair PSUs | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PSUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5600` | Maximum price allowed for a match, applies AMD 5600 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5800` | Maximum price allowed for a match, applies AMD 5800 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5900` | Maximum price allowed for a match, applies AMD 5900 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5950` | Maximum price allowed for a match, applies AMD 5950 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_SONYPS5C` | Maximum price allowed for a match, applies PS5 console | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PS5 above `1234` will be skipped. |
| `MAX_PRICE_SERIES_SONYPS5DE` | Maximum price allowed for a match, applies PS5 digital edition | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PS5 above `1234` will be skipped. |
| `MAX_PRICE_SERIES_TEST` | Maximum price allowed for a match, applies `test:series` | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PS5 above `1234` will be skipped. |
| `MICROCENTER_LOCATION` | Specific MicroCenter location(s) to search | Comma separated, e.g.: `marietta,duluth`, default: `web` |
| `NVIDIA_ADD_TO_CART_ATTEMPTS` | The maximum number of times the `nvidia-api` add to cart feature will be attempted before failing | Default: `10` |
| `NVIDIA_SESSION_TTL` | The time in milliseconds to keep the cart active while using `nvidia-api` | Default: `60000` |
| `SHOW_ONLY_BRANDS` | Filter to show specified brands | Comma separated, e.g.: `evga,zotac` |
| `SHOW_ONLY_MODELS` | Filter to show specified models | Both supported formats are comma separated <br/><br/>1. Standard E.g.: `founders edition,rog strix` <br/><br/> 2. Advanced E.g: `MODEL:SERIES`, E.g: `founders edition:3090,rog strix` |
| `SHOW_ONLY_SERIES` | Filter to show specified series | Comma separated, e.g.: `3080,ryzen5900` |
| `STORES` | [Supported stores](#supported-stores) you want to be scraped | Both supported formats are comma separated <br/><br/>1. Standard E.g.: `"nvidia"` <br/><br/> 2. Advanced E.g: `STORE:PAGE_SLEEP_MIN:PAGE_SLEEP_MAX`, E.g: `nvidia:10000:30000` <br/><br/>Default: `nvidia` |
<details>
<summary>Supported stores</summary>
> :point_right: Used with the `STORES` variable.
| Stores | Environment variable |
|:---:|:---:|
| Adorama | `adorama`|
| Alternate (DE) | `alternate`|
| Alternate (NL) | `alternate-nl`|
| Amazon | `amazon`|
| Amazon (CA) | `amazon-ca`|
| Amazon (DE) | `amazon-de`|
| Amazon (DE) Warehouse | `amazon-de-warehouse`|
| Amazon (ES) | `amazon-es`|
| Amazon (FR) | `amazon-fr`|
| Amazon (IT) | `amazon-it`|
| Amazon (NL) | `amazon-nl`|
| Amazon (UK) | `amazon-uk`|
| AMD | `amd`|
| AMD (CA) | `amd-ca`|
| AMD (DE) | `amd-de`|
| AMD (IT) | `amd-it`|
| AntOnline | `antonline`|
| Argos (UK) | `argos`|
| Aria PC (UK) | `aria`|
| ARLT (DE) | `arlt`|
| ASUS | `asus` |
| ASUS (DE) | `asus-de` |
| Azerty (NL) | `azerty`|
| B&H | `bandh`|
| Best Buy | `bestbuy`|
| Best Buy (CA) | `bestbuy-ca`|
| Box (UK) | `box`|
| CanadaComputers (CA) | `canadacomputers` |
| Caseking (DE) | `caseking`|
| CCL (UK) | `ccl`|
| Comet (IT) | `comet`|
| Computeruniverse (DE) | `computeruniverse` |
| Coolblue (NL) | `coolblue`|
| Coolmod (ES) | `coolmod`|
| Corsair | `corsair`|
| Currys (UK) | `currys`|
| Cyberport (DE) | `cyberport` |
| eBuyer (UK) | `ebuyer`|
| El Corte Inglés | `elcorteingles`|
| ePrice (IT) | `eprice`|
| Euronics (IT) | `euronics`|
| Euronics (DE) | `euronics-de`|
| EVGA | `evga`|
| EVGA (EU) | `evga-eu`|
| Expert | `expert`|
| Galaxus (DE) | `galaxus`|
| Game (UK) | `game`|
| Gamestop | `gamestop`|
| Gamestop (DE) | `gamestop-de`|
| Kabum (BR) | `kabum`|
| Mediamarkt (DE) | `mediamarkt`|
| Medimax | `medimax`|
| MemoryExpress (CA) | `memoryexpress`|
| Micro Center | `microcenter`|
| Mindfactory (DE) | `mindfactory` |
| Newegg | `newegg`|
| Newegg (CA) | `newegg-ca`|
| Notebooksbilliger (DE) |`notebooksbilliger`|
| Novatech (UK) | `novatech`|
| Nvidia | `nvidia`|
| Nvidia (API) | `nvidia-api`|
| Office Depot | `officedepot`|
| Otto | `otto`|
| Overclockers (UK) | `overclockers`|
| PCComponentes (ES) | `pccomponentes`|
| PlayStation | `playstation`|
| PNY | `pny`|
| Proshop (DE) | `proshop-de`|
| Proshop (DK) | `proshop-dk`|
| Saturn (DE) | `saturn`|
| Scan (UK) | `scan`|
| Smyths Toys (UK) | `smythstoys`|
| Spielegrotte | `spielegrotte`|
| Target | `target`|
| Unieuro (IT) | `unieuro`|
| Very (UK) | `very`|
| Walmart | `walmart`|
| Wipoid | `wipoid`|
| Zotac | `zotac`|
| TopAchat | `topachat`|
<details>
<summary>Micro Center stores</summary>
> :point_right: Used with the `MICROCENTER_LOCATION` variable.
> :point_right: Before using `web`, please review [this issue comment](https://github.com/jef/streetmerchant/issues/442#issuecomment-703297393).
| Store name |
|:---:|
| `brooklyn` |
| `brentwood` |
| `cambridge` |
| `chicago` |
| `columbus` |
| `dallas` |
| `denver` |
| `duluth` |
| `fairfax` |
| `flushing` |
| `houston` |
| `madison-heights` |
| `marietta` |
| `mayfield-heights` |
| `north-jersey` |
| `overland-park` |
| `parkville` |
| `rockville` |
| `sharonville` |
| `st-davids` |
| `st-louis-park` |
| `tustin` |
| `westbury` |
| `westmont` |
| `yonkers` |
</details>
</details>
<details>
<summary>Supported brands and models</summary>
> :point_right: Used with the `SHOW_ONLY_BRANDS` and `SHOW_ONLY_MODELS` variables.
| Brand | Model |
|:---:|---|
| `amd` | `5600x`, `5800x`, `5900x`, `5950x`, `amd reference` |
| `asus` | `dual`, `dual oc`, `strix`, `strix oc`, `tuf`, `tuf oc` |
| `corsair` | `750 platinum`, `600 platinum` |
| `evga` | `ftw3`, `ftw3 ultra`, `xc3`, `xc3 black`, `xc3 ultra` |
| `gainward` | `phantom gs`, `phoenix`, `phoenix gs`, `phoenix gs oc` |
| `gigabyte` | `aorus master`, `aorus xtreme`, `eagle`, `eagle oc`, `gaming`, `gaming oc`, `turbo`, `vision`, `vision oc` |
| `inno3d` | `gaming x3`, `ichill x3`, `ichill x4`, `twin x2 oc` |
| `kfa2` | `sg`, `sg oc` |
| `microsoft` | `xbox series x`, `xbox series s` |
| `msi` | `gaming x trio`, `ventus 2x oc`, `ventus 3x`, `ventus 3x oc` |
| `nvidia` | `founders edition` |
| `palit` | `gamerock oc`, `gaming pro`, `gaming pro oc` |
| `pny` | `dual fan`, `xlr8 revel`, `xlr8 uprising` |
| `sony` | `ps5 console`, `ps5 digital` |
| `xfx` | `merc`, `amd reference` |
| `zotac` | `amp holo`, `amp extreme holo`, `trinity`, `trinity oc`, `twin edge`, `twin edge oc` |
</details>
<details>
<summary>Supported series</summary>
> :point_right: Used with the `SHOW_ONLY_SERIES` variable.
| Series | Environment variable |
|:---:|:---:|
| `AMD Ryzen 5600x` | `ryzen5600` |
| `AMD Ryzen 5800x` | `ryzen5800` |
| `AMD Ryzen 5900x` | `ryzen5900` |
| `AMD Ryzen 5950x` | `ryzen5950` |
| `AMD RX 6800` | `rx6800` |
| `AMD RX 6800XT` | `rx6800xt` |
| `AMD RX 6900XT` | `rx6900xt` |
| `Nvidia RTX 3060 Ti` | `3060ti` |
| `Nvidia RTX 3070` | `3070` |
| `Nvidia RTX 3080` | `3080` |
| `Nvidia RTX 3090` | `3090` |
| `Corsair SFX PSU` | `sf` |
| `Sony PS5` | `sonyps5c` |
| `Sony PS5 Digital Edition` | `sonyps5de` |
| `Xbox Series S` | `xboxss` |
| `Xbox Series X` | `xboxsx` |
</details>
<details>
<summary>Supported countries (used with nvidia and nvidia-api)</summary>
> :point_right: Used with the `COUNTRY` variable.
| Country | 3080 FE | 3090 FE | Test Card | Notes |
|:---:|:---:|:---:|:---:|:---:|
| austria | `✔` | `✔` | `✔` | |
| belgium | `✔` | `✔` | `✔` | |
| canada | `✔` | `✔` | `✔` | |
| czechia | `✔` | `✔` | `✔` | |
| denmark | `✔` | | `✔` | Missing RTX 3090 |
| finland | `✔` | | `✔` | Missing RTX 3090 |
| france | `✔` | `✔` | `✔` | |
| germany | `✔` | `✔` | `✔` | |
| great_britain | `✔` | `✔` | `✔` | |
| ireland | `✔` | `✔` | `✔` | |
| italy | `✔` | `✔` | `✔` | |
| luxembourg | `✔` | `✔` | `✔` | |
| netherlands | `✔` | `✔` | `✔` | |
| norway | `✔` | `✔` | `✔` | |
| poland | `✔` | `✔` | `✔` | |
| portugal | `✔` | | | RTX 3080 only |
| spain | `✔` | `✔` | `✔` | |
| sweden | `✔` | `✔` | `✔` | |
| usa | `✔` | `✔` | `✔` | |
</details>
### Notifications
> :point_right: You can test your notification configuration by running `npm run test:notification`.
<details>
<summary>Desktop</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `DESKTOP_NOTIFICATIONS` | Display desktop notifications using [node-notifier](https://www.npmjs.com/package/node-notifier) | Default: `false` |
| `PLAY_SOUND` | Play this sound notification if a card is found | Relative path accepted, valid formats: wav, mp3, flac, E.g.: `path/to/notification.wav`, [free sounds available](https://notificationsounds.com/) |
</details>
<details>
<summary>Discord</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `DISCORD_NOTIFY_GROUP` | Discord group you would like to notify | Can be comma separated, use role ID, E.g.: `<@&2834729847239842>` |
| `DISCORD_WEB_HOOK` | Discord Web Hook URL | Can be comma separated, use whole webhook URL |
</details>
<details>
<summary>Email and SMS</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `EMAIL_PASSWORD` | Gmail password | See below if you have MFA |
| `EMAIL_TO` | Destination Email | Defaults to username if not set. Can be comma separated |
| `EMAIL_USERNAME` | Gmail address | E.g.: `jensen.robbed.us@gmail.com` |
| `PHONE_CARRIER` | [Supported carriers](#supported-carriers) for SMS | E.g.: `att` or `att,verizon,google`, email configuration required. If multiple phone numbers are listed, enter a carrier for each phone number |
| `PHONE_NUMBER` | 10 digit phone number(s) | E.g.: `1234567890` or `1234567890,0987654321,11112223333`, email configuration required |
| `SMTP_ADDRESS` | IP Address or fqdn of smtp server |
| `SMTP_PORT` | TCP Port number on which the smtp server is listening for connections | Default: `25` |
> :point_right: If you have multi-factor authentication (MFA), you will need to create an [app password](https://myaccount.google.com/apppasswords) and use this instead of your Gmail password.
#### Supported carriers
| Carrier | Environment variable | Notes |
|:---:|:---:|:---:|
| AT&T | `att` | |
| AT&T Prepaid | `attgo` | |
| Bell | `bell` | |
| Fido | `fido` | |
| Google | `google`| |
| Koodo | `koodo` | |
| Mint | `mint`| |
| Rogers | `rogers` | |
| Sprint | `sprint`| |
| Telus | `telus`| |
| T-Mobile | `tmobile`| |
| Verizon | `verizon`| Works with Visible |
| Virgin | `virgin`| |
| Virgin (CA) | `virgin-ca`| |
</details>
<details>
<summary>MQTT</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `MQTT_BROKER_ADDRESS` | IP address or URL of MQTT Broker | e.g 192.168.1.xxx or broker.hivemq.com |
| `MQTT_BROKER_PORT` | Network port of MQTT Broker | Default: 1883 |
| `MQTT_CLIENT_ID` | Unique ClientID (only if required by MQTT Broker), typically not required when only publishing alerts | eg. client-123456 |
| `MQTT_PASSWORD` | MQTT password - only use with MQTT brokers on private networks, if required. Will not be sent over public networks for safety. | e.g mysecret |
| `MQTT_QOS` | QoS level for published alerts to broker (https://www.npmjs.com/package/mqtt#about-qos) | Default: 0, Can be 0, 1, or 2 |
| `MQTT_TOPIC` | Topic to publish alerts to. Can include %store%, %series%, %brand%, %model% for dynamic topics | Default: streetmerchant/alert e.g nv-alert/%store%/%series%/%brand%/%model%/alert |
| `MQTT_USERNAME` | MQTT username - (only if required by MQTT Broker) | e.g myusername |
</details>
<details>
<summary>PagerDuty</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `PAGERDUTY_INTEGRATION_KEY` | PagerDuty Events API v2 Integration Key. Obtain one in PagerDuty - <Service you want to use> - Integrations | |
| `PAGERDUTY_SEVERITY` | Severity of PagerDuty events | Default: `info` |
</details>
<details>
<summary>Philips Hue</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `PHILIPS_HUE_API_KEY` | Hue Bridge API Key | **Required**, generate key using instructions [here](https://developers.meethue.com/develop/get-started-2/). This will be used for both LAN and cloud access over the official Remote Hue API. |
| `PHILIPS_HUE_LAN_BRIDGE_IP` | LAN IP Address of your Hue Bridge | LAN only, e.g. `192.168.x.x`|
| `PHILIPS_HUE_LIGHT_IDS` | Light IDs | Optional (all if not supplied). Comma seperated, e.g.: `1`, `2` |See Hue App → About for IDs |
| `PHILIPS_HUE_LIGHT_COLOR` | Color in RGB Format | Optional (NVIDIA green if not supplied). Comma separated, e.g.: `255`, `255`, `255`|
| `PHILIPS_HUE_LIGHT_PATTERN` | `blink` or empty | Optional - lights will flash for 30 seconds if `blink` is supplied. |
| `PHILIPS_HUE_CLOUD_ACCESS_TOKEN` | Remote Access Token | Cloud only, the access token obtained from Philips's Remote Hue API. Instructions to generate [here](https://developers.meethue.com/develop/hue-api/remote-authentication/). |
| `PHILIPS_HUE_CLOUD_REFRESH_TOKEN` | Remote Refresh Token | Cloud only, the refresh token obtained from Philips's Remote Hue API. |
| `PHILIPS_HUE_CLOUD_CLIENT_ID` | Remote Client ID | Cloud only, the client ID to use when accessing the Remote Hue API. |
| `PHILIPS_HUE_CLOUD_CLIENT_SECRET` | Remote Client Secret | Cloud only, the client secret to use when accessing the Remote Hue API. |
> :point_right: [Video demonstration](https://vimeo.com/476083242)
</details>
<details>
<summary>Pushbullet</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `PUSHBULLET` | PushBullet API key | Generate at https://www.pushbullet.com/#settings/account | |
</details>
<details>
<summary>Pushover</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `PUSHOVER_TOKEN` | Pushover access token | Generate at https://pushover.net/apps/build | |
| `PUSHOVER_USER` | Pushover username | |
| `PUSHOVER_PRIORITY` | Pushover message priority |
</details>
<details>
<summary>Slack</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `SLACK_CHANNEL` | Slack channel for posting | E.g.: `update`, no need for `#` |
| `SLACK_TOKEN` | Slack API token | |
</details>
<details>
<summary>Telegram</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `TELEGRAM_ACCESS_TOKEN` | Telegram access token | |
| `TELEGRAM_CHAT_ID` | Telegram chat ID | Comma seperated, e.g.: `123456789`, `123456789,987654321` |
</details>
<details>
<summary>Twilio</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `TWILIO_ACCOUNT_SID` | Twilio Account SID | Can be found on twilio.com/console |
| `TWILIO_AUTH_TOKEN` | Twilio Auth Token | Can be found on twilio.com/console |
| `TWILIO_FROM_NUMBER` | Twilio provided phone number to send messages from | Include country code e.g +4401234567890 |
| `TWILIO_TO_NUMBER` | Mobile number to send SMS to | Include country code e.g +4401234567890 |
</details>
<details>
<summary>Twitter</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `TWITTER_ACCESS_TOKEN_KEY` | Twitter Token Key | |
| `TWITTER_ACCESS_TOKEN_SECRET` | Twitter Token Secret | |
| `TWITTER_CONSUMER_KEY` | Twitter Consumer Key | Generate all Twitter keys at: https://developer.twitter.com/ |
| `TWITTER_CONSUMER_SECRET` | Twitter Consumer Secret | |
| `TWITTER_TWEET_TAGS` | Optional list of hashtags to append to the tweet message | E.g.: `#nvidia #nvidiastock` |
</details>
<details>
<summary>Twitch</summary>
| Environment variable | Description | Notes |
|:---:|---|---|
| `TWITCH_CLIENT_ID` | Twitch client ID | |
| `TWITCH_CLIENT_SECRET`| Twitch client secret | |
| `TWITCH_ACCESS_TOKEN` | Twitch access token | |
| `TWITCH_REFRESH_TOKEN` | Twitch refresh token | |
| `TWITCH_CHANNEL` | Twitch channel | |
</details>
</details>
## FAQ
**Q: What's Node.js and how do I install it?** Visit [their website](https://nodejs.org/en/) and download and install
it. Very straight forward. Otherwise, Google more information related to your system needs.
**Q: Will this harm my computer?** No.
**Q: Have you gotten a card yet?** YES! :tada: :rocket:
<details>
<summary>Screenshot</summary>
![screenshot](https://i.imgur.com/59CRzGq.png)
</details>
**Q: Will I get banned from of the stores?** Perhaps, but it's the risk we're willing to take! To help minimize this, take a look at [#1050](https://github.com/jef/streetmerchant/issues/1050).
**Q: I got a problem and need help!** File an [issue](https://github.com/jef/streetmerchant/issues/new/choose). I'll do my best to get to you. I work a full time job and this is only a hobby of mine.
**Q: How do I get the latest code?** Take look at this [wiki page](https://github.com/jef/streetmerchant/wiki/Troubleshoot:-General:-Getting-the-latest-code).
**Q: Why don't my notifications work?** There is probably an [issue](https://github.com/jef/streetmerchant/issues?q=is%3Aissue+sort%3Aupdated-desc+sound+is%3Aclosed) that has [already](https://github.com/jef/streetmerchant/issues/182) [been](https://github.com/jef/streetmerchant/issues/116) [resolved](https://github.com/jef/streetmerchant/issues/155).
**Q: I'd love to contribute, how do I do that?** Make a [pull request](https://github.com/jef/streetmerchant/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc)! All contributions are welcome.
**Q: How do I add a store?** Take a look at [this page](https://github.com/jef/streetmerchant/wiki/Help:-Configuration:-Adding-a-store) on the wiki.
**Q: Why do I have to download all this stuff just to get this bot working?** Well, I would rather you didn't either. See [#11](https://github.com/jef/streetmerchant/issues/11).
**Q: Why does Amazon show an error page (with a picture of an animal) instead of adding to cart or going to the detail page?** This is intended; see [#733](https://github.com/jef/streetmerchant/issues/733). This indicates that the item is out of stock and only available from a third-party seller (often at a markup).
**Q: I'm using streetmerchant in the cloud and X isn't working.** There is _a lot_ of undefined behavior with using streetmerchant in the cloud. Some sites may block IPs from your cloud provider. It is possible that a VPN will help circumvent these problems.
<p align="center"><a href="https://github.com/jef/streetmerchant#readme"><img src="https://raw.githubusercontent.com/jef/streetmerchant/main/media/terminal.gif" /></a></p>
+10
View File
@@ -0,0 +1,10 @@
version: '3.0'
services:
streetmerchant:
image: ghcr.io/jef/streetmerchant:latest
build:
context: .
dockerfile: Dockerfile
container_name: streetmerchant
env_file:
- dotenv
+11
View File
@@ -0,0 +1,11 @@
# About
## Background
Remember on September 17th, 2020 at 9 AM EST the Nvidia site went from **Notify Me** to **Out of Stock** instantly? Well, they didn't sell any cards. The real reason was that they weren't ready to sell them to us yet. That's right, they turned off their third party storefronts because they were being overloaded with our clicks. They still kept the other cards that use those APIs online, but they removed that one. It was re-enabled at some point for a brief moment, but the same thing happened -- servers overloaded with API requests.
This is where streetmerchant comes in. It doesn't buy anything for you, but it makes it more of a stress free job to refresh and check sites while you go about your daily business. People took off work, missed appointments, and gave up other lively needs in hopes to buy a _graphics card_. Now we reach beyond graphics cards in hopes for other products!
Please enjoy,
jef
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

+1
View File
@@ -0,0 +1 @@
../CHANGELOG.md
+67
View File
@@ -0,0 +1,67 @@
# FAQ
## What's Node.js and how do I install it?
Visit [their website](https://nodejs.org/en/) and download and install it. Very straight forward. Otherwise, Google more information related to your system needs.
## Will this harm my computer?
No.
## Have you gotten a card yet?
[Sure did!](https://i.imgur.com/59CRzGq.png)
## Will I get banned from of the stores?
Perhaps, but it's the risk we're willing to take! To help minimize this, take a look at [#1050](https://github.com/jef/streetmerchant/issues/1050).
## I got a problem and need help
File an [issue](https://github.com/jef/streetmerchant/issues/new/choose). I'll do my best to get to you. I work a full time job and this is only a hobby of mine.
## How do I get the latest code?
Run the following commands:
```shell
git pull origin main
npm install
npm run start
```
If you changed the code at all, this will most likely fail. You can clear out your changes by doing:
```shell
git checkout .
git pull origin main
npm install
npm run start
```
You can also to [git-stash](https://git-scm.com/docs/git-stash), but we won't expand on that here.
## Why don't my notifications work?
There is probably an [issue](https://github.com/jef/streetmerchant/issues?q=is%3Aissue+sort%3Aupdated-desc+sound+is%3Aclosed) that has [already](https://github.com/jef/streetmerchant/issues/182) [been](https://github.com/jef/streetmerchant/issues/116) [resolved](https://github.com/jef/streetmerchant/issues/155).
## I'd love to contribute, how do I do that?
Make a [pull request](https://github.com/jef/streetmerchant/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc)! All contributions are welcome.
## How do I add a store?
Here's a little write-up I did: [Adding a store](help/general.md#adding-a-store).
## Why do I have to download all this stuff just to get this bot working?
Well, I would rather you didn't either. See [#11](https://github.com/jef/streetmerchant/issues/11).
## Why does Amazon show an error page (with a picture of an animal) instead of adding to cart or going to the detail page?
This is intended; see [#733](https://github.com/jef/streetmerchant/issues/733). This indicates that the item is out of stock and only available from a third-party seller (often at a markup).
## I'm using streetmerchant in the cloud and X isn't working.
There is _a lot_ of undefined behavior with using streetmerchant in the cloud. Some sites may block IPs from your cloud provider. It is possible that a VPN will help circumvent these problems.
+105
View File
@@ -0,0 +1,105 @@
# Getting started
You do not need any computer skills, smarts, or anything of that nature. You are very capable as you have made it this far. Some basic understanding how a terminal, git, and or Node.js is a bonus, but that does not limit you to getting streetmerchant running!
## Prerequisites
- [git](https://git-scm.com/)
- Either [Node.js 14](https://nodejs.org/en/) or [Docker (1.13.0+)](https://docs.docker.com/get-docker/) (advanced users)
## Using Node.js
| Reference | Note |
|:---:|---|
| tag | Example, `v1.0.0`; stable |
| `main` | Latest HEAD; not tagged, could be unstable |
1. Download [Node.js 14](https://nodejs.org/en/)
1. Clone this project `git clone https://github.com/jef/streetmerchant.git`.
1. To checkout a particular reference, use `git checkout <ref name>` after cloning.
1. Navigate to this project by entering `cd streetmerchant`.
1. Run `npm install`.
1. Make a copy of `dotenv-example` and name it `dotenv`.
1. Edit the `dotenv` file to your liking using a text editor (like [vscode](https://code.visualstudio.com/)).
1. Run `npm run start` to start.
At any point you want the program to stop, use ++ctrl+c++.
???+ tip
Community based help can also be found on the [wiki](https://github.com/jef/streetmerchant/wiki). Feel free to check that out if you're having problems running. If you're still having problems running, you're probably not the first. Make some searches through the [GitHub issues](https://github.com/jef/streetmerchant/issues) before making one.
## Using Docker
Available via GitHub Container Registry.
| Tag | Note |
|:---:|---|
| `latest` | Latest release; stable |
| `nightly` | Latest HEAD each day at midnight UTC; could be unstable |
```sh
# to run docker nightly
docker run -it --rm \
--env-file ./dotenv \
ghcr.io/jef/streetmerchant:nightly
# to test notifications
docker run -it --rm \
--env-file ./dotenv \
ghcr.io/jef/streetmerchant:nightly test:notification:production
```
See [Developing in docker](#developing-in-docker) for more information
## Customization
To customize streetmerchant, make a copy of `dotenv-example` as `dotenv` and make any changes to your liking. View [Reference](reference/application.md) for more information on variables and their usage.
???+ tip
All environment variables are optional.
## For developers
### Developing in Node.js
The command `npm run start:dev` can be used instead of `npm run start` to automatically restart the project when filesystem changes are detected in the `src/` folder or `dotenv` file.
Use `npm run lint` to see any linting uses and `npm run lint:fix` to automatically fix the issues it can.
### Developing in Docker
#### Prerequisites
- [Docker (1.13.0+)](https://docs.docker.com/get-docker/)
- [GNU Make](https://www.gnu.org/software/make/)
- Optional, but increases quality of life
- [Docker Compose](https://docs.docker.com/compose/install/)
- Depending on your system, you may already have this. Check before installing.
#### Starting with `docker-compose`
1. Make a copy of `dotenv-example` and name it `dotenv`.
1. Edit the `dotenv` file to your liking using a text editor (like [vscode](https://code.visualstudio.com/)).
1. If you're using `make`, you can run `make run` to start and `make stop` to stop.
1. There are more options in the `Makefile`, feel free to explore.
1. If you do not have `make`, you can run `docker-compose` directly:
```shell
# To start
# Use `-d` if you want to run in detached mode
# Use `--build` if you want to build the image before running (default image is latest in GitHub Container Registry).
docker-compose up
# To break down
docker-compose down
```
???+ note
If you are using `WEB_PORT`, then you will need to add the following to docker-compose:
```
ports:
- "<WEB_PORT>:<WEB_PORT>"
```
Replace `<WEB_PORT>` with the value you gave in your `dotenv`.
+133
View File
@@ -0,0 +1,133 @@
# General
## Adding a store
???+ note
This is subject to change in the future
In the following examples, I will be using "NewStore" as the store I'm wanting to add.
### Creating a store file
First, create a TypeScript file in `src/store/model`. In this example, I'll create a file named `new-store.ts`. At this point, you can copy and paste any of the other stores and change accordingly.
#### How to grab a container (aka selector)
For the containers, what you'll wanna do is use <kbd>F12</kbd> on the site you want to Inspect and click this button
![image](https://user-images.githubusercontent.com/12074633/100685326-2669da80-334a-11eb-93a9-8ac2c659f5f3.png)
Hover over the item you want and it should give you the context:
![image](https://user-images.githubusercontent.com/12074633/100685310-1e119f80-334a-11eb-91aa-b77b0ff6c2b1.png)
You can also right-click on any website element and select 'Inspect'. That should also give you the same results.
Some people will decide to choose a parent element as it can be unique. Like this case!
I'd rather use `.button.spin-button.prod-ProductCTA--primary.button--primary` instead of `.spin-button-children` as there are probably other elements on the page that are also `.spin-button-children`.
The reason why we use these selectors anyway is to wait for the webpage to load these specific elements, to help eliminate false positives.
For easily getting the selector, you can also copy it by right clicking on the tag, Copy > Copy selector.
![image](https://user-images.githubusercontent.com/12074633/100933096-d2323800-34ba-11eb-8f06-d106f43b7ad3.png)
### Updating the models
You'll now want to add the new store to `src/store/model/index.ts`.
This is what it will look like:
```diff
--- a/src/store/model/index.ts
+++ b/src/store/model/index.ts
@@ -48,6 +48,7 @@ import {MicroCenter} from './microcenter';
import {Mindfactory} from './mindfactory';
import {Newegg} from './newegg';
import {NeweggCa} from './newegg-ca';
+import {NewStore} from './new-store';
import {Notebooksbilliger} from './notebooksbilliger';
import {Novatech} from './novatech';
import {Nvidia} from './nvidia';
@@ -123,6 +124,7 @@ export const storeList = new Map([
[Mindfactory.name, Mindfactory],
[Newegg.name, Newegg],
[NeweggCa.name, NeweggCa],
+ [NewStore.name, NewStore],
[Notebooksbilliger.name, Notebooksbilliger],
[Novatech.name, Novatech],
[Nvidia.name, Nvidia],
```
After that, you're pretty much set. If you plan on adding new models or series, you will have to add them to `src/store/model/store.ts` and `src/config.ts`.
Here's an example:
```diff
--- a/src/store/model/store.ts
+++ b/src/store/model/store.ts
@@ -23,6 +23,7 @@ export type Brand =
| 'kfa2'
| 'microsoft'
| 'msi'
+ | 'new brand'
| 'nvidia'
| 'palit'
| 'pny'
@@ -37,6 +38,7 @@ export type Series =
| '3070'
| '3080'
| '3090'
+ | 'new series'
| 'rx6800'
| 'rx6800xt'
| 'rx6900xt'
@@ -83,6 +85,7 @@ export type Model =
| 'ichill x2'
| 'ichill x3'
| 'ichill x4'
+ | 'new model'
| 'nitro+'
| 'nitro oc se'
| 'nitro oc'
```
```diff
--- a/src/config.ts
+++ b/src/config.ts
@@ -308,6 +308,7 @@ const store = {
country: envOrString(process.env.COUNTRY, 'usa'),
maxPrice: {
series: {
+ 'new series': -1,
'3060ti': envOrNumber(process.env.MAX_PRICE_SERIES_3060TI),
3070: envOrNumber(process.env.MAX_PRICE_SERIES_3070),
3080: envOrNumber(process.env.MAX_PRICE_SERIES_3080),
@@ -337,6 +338,7 @@ const store = {
};
}),
showOnlySeries: envOrArray(process.env.SHOW_ONLY_SERIES, [
+ 'new series',
'3060ti',
'3070',
'3080',
```
And voila! You're done! If you'd like to contribute to the project, feel free to create a [Pull Request](https://github.com/jef/streetmerchant/compare)! Don't forget to add the store (and brand, model, and series if you added) to the `README.md`.
???+ tip
Here's an [example](https://github.com/jef/streetmerchant/commit/af96c5f2e808af7496f3c3299e4cf173105de48b). If you'd like to include a max price, you can add `MAX_PRICE_SERIES_NEW_SERIES` to the above instead of the `-1`.
## Creating a Discord webhook
Take a look at Discord's [Intro to Webhooks](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks), that should get you going.
This is the main portion:
![image](https://user-images.githubusercontent.com/12074633/101225522-a4d2bf00-365f-11eb-8c35-d0f013e561d6.png)
![image](https://user-images.githubusercontent.com/12074633/101225550-b87e2580-365f-11eb-8be6-48b324b37916.png)
Use the full URL that you just copied and set that value to `DISOCRD_WEB_HOOK`.
+42
View File
@@ -0,0 +1,42 @@
# Troubleshoot
## Captcha issues
### Option 1
If you're running into problems, try running in headful mode: `HEADLESS="false"`.
This will open a browser and run streetmerchant. Note that this isn't a great solution for those running in a headless environment, i.e.: VPS, cloud, docker. Instead, it would be a good solution for those running on separate computer that won't be blocked by running in the background.
### Option 2
As a last case scenario, use `PUPPETEER_EXECUTABLE_PATH`. This will use your computer's Chrome browser. You can run this is headless or headful mode.
> From the puppeteer doc:
>
> `PUPPETEER_EXECUTABLE_PATH` - specify an executable path to be used in `puppeteer.launch`. See [puppeteer.launch([options])](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#puppeteerlaunchoptions) on how the executable path is inferred. **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/puppeteer/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
For example:
`dotenv`:
```
PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome-stable
```
This will vary depending on your operating system and install path. Please use full paths.
## macOS code signing
If you're getting a popup like this:
![image](https://user-images.githubusercontent.com/12074633/93616357-a36bf180-f9a2-11ea-82fa-da2a44807802.png)
Then run this command:
```sh
sudo codesign --force --deep --sign - ./node_modules/puppeteer/.local-chromium/mac-800071/chrome-mac/Chromium.app
```
???+ tip
The `mac-800071` may be different on your machine, so I would start from `./node_modules/puppeteer/.local-chromium` and auto complete from there.
+38
View File
@@ -0,0 +1,38 @@
[![streetmerchant](assets/images/streetmerchant-banner.png)](https://jef.codes/streetmerchant)
## Features
First and foremost, this service _will not_ automatically buy for you.
- **Checks stock continuously** -- runs 24/7, 365, looking for the items you want.
- **Ready for checkout** -- ability to add to cart when available and even opens the browser for you.
- **Notifications galore** -- when you're not by your computer, worry free with notifications to most platforms and devices when an item comes in stock.
## Getting started
You'll find most of the content on the left sidebar. The right sidebar will help you navigate a page.
### Contributing
- Give helpful tips and tricks to the [community based wiki](https://github.com/jef/streetmerchant/wiki).
- Add to the documentation through [pull requests](https://github.com/jef/streetmerchant/pulls).
- Fork and make a pull request to the repository.
### Looking for help
- Have an idea, question, or need help? Visit our [GitHub discussion board](https://github.com/jef/streetmerchant/discussions).
- Ran into a bug? File a [GitHub issue](https://github.com/jef/streetmerchant/issues/new/choose).
- Looking to hang out and talk shop? Join us on [Discord](https://discord.gg/gbVY4vB9JF).
### Supporting the project
The best way to support me is to donate to [Diabetes Research Institute](https://www.diabetesresearch.org/Give).
> The Diabetes Research Institute leads the world in cure-focused diabetes research.
>
> [diabetesresearch.org](https://www.diabetesresearch.org/about-DRI)
If you feel inclined to support me directly, here are those options:
- [GitHub Sponsors](https://github.com/sponsors/jef)
- [Paypal](https://www.paypal.me/jxf)
+6
View File
@@ -0,0 +1,6 @@
app.document$.subscribe(function() {
var tables = document.querySelectorAll("article table")
tables.forEach(function(table) {
new Tablesort(table)
})
})
+29
View File
@@ -0,0 +1,29 @@
# Application
| Environment variable | Description |
|:---:|---|
| `AUTO_ADD_TO_CART` | Enable auto add to cart on support stores, default: `true` |
| `BROWSER_TRUSTED` | Skip Chromium Sandbox. Useful for containerized environments, default: `false` |
| `HEADLESS` | Puppeteer to run headless or not. Debugging related, default: `true` |
| `INCOGNITO` | Puppeteer to run incognito or not. Debugging related, default: `false` |
| `IN_STOCK_WAIT_TIME` | Time to wait between requests to the same link if it has that card in stock. In seconds, default: `0` |
| `LOG_LEVEL` | [Logging levels](https://github.com/winstonjs/winston#logging-levels). Debugging related, default: `info` |
| `LOW_BANDWIDTH` | Blocks images/fonts to reduce traffic. Disables ad blocker, default: `false` |
| `OPEN_BROWSER` | Toggle for whether or not the browser should open when item is found. Default: `true` |
| `PAGE_BACKOFF_MIN` | Minimum backoff time between retrying requests for the same store when a forbidden response is received. Default: `10000` |
| `PAGE_BACKOFF_MAX` | Maximum backoff time between retrying requests for the same store when a forbidden response is received. Default: `3600000` |
| `PAGE_SLEEP_MIN` | Minimum sleep time between queries of the same product page. In milliseconds, default: `5000` |
| `PAGE_SLEEP_MAX` | Maximum sleep time between queries of the same product page. In milliseconds, default: `10000` |
| `PAGE_TIMEOUT` | Navigation Timeout in milliseconds. `0` for infinite, default: `30000` |
| `PROXY_PROTOCOL` | Protocol of proxy server, such as `socks5`. Default: `http` |
| `PROXY_ADDRESS` | IP Address or fqdn of proxy server |
| `PROXY_PORT` | TCP Port number on which the proxy is listening for connections. Default: `80` |
| `SCREENSHOT` | Capture screenshot of page if a card is found. Default: `true` |
| `WEB_PORT` | Starts a webserver to be able to control the bot while it is running. Setting this value starts this service. |
???+ info
You can also have a list of proxies that are rotated while searching stores. Proxies can be read from a file named `STORENAME.proxies` in the format of `socks5://username:password@ip`; one per line. In this case, there is no need to use the `PROXY_*` environments.
???+ tip
- You can also have a list of proxies that are rotated while searching stores. Proxies can be read from a file named `STORENAME.proxies` in the format of `socks5://username:password@ip`; one per line.
- Data usage is [known to be high](https://github.com/jef/streetmerchant/issues?q=is%3Aissue+sort%3Aupdated-desc+bandwidth). This is expected as the program scrapes many websites in parallel 24/7. To help reduce this, use `LOW_BANDWIDTH="true"`. We are looking into other solutions as well, but is low priority.
+224
View File
@@ -0,0 +1,224 @@
# Filter
| Environment variable | Description | Notes |
|:---:|---|---|
| `COUNTRY` | [Supported country](#supported-countries) you want to be scraped | Only used with `nvidia-api`, default: `usa` |
| `MAX_PRICE_SERIES_3060TI` | Maximum price allowed for a match, applies 3060 Ti series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_3070` | Maximum price allowed for a match, applies 3070 series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_3080` | Maximum price allowed for a match, applies 3080 series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_3090` | Maximum price allowed for a match, applies 3090 series cards (does not apply to these sites: Nvidia, Asus, EVGA) | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - Cards above `1234` will be skipped. |
| `MAX_PRICE_SERIES_CORSAIR_SF` | Maximum price allowed for a match, applies to Corsair PSUs | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PSUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_DARKHERO` | Maximum price allowed for a match, applies to ASUS Dark Hero motherboards | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PSUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5600` | Maximum price allowed for a match, applies AMD 5600 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5800` | Maximum price allowed for a match, applies AMD 5800 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5900` | Maximum price allowed for a match, applies AMD 5900 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_RYZEN5950` | Maximum price allowed for a match, applies AMD 5950 series cpus | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - CPUs above `1234` will be skipped. |
| `MAX_PRICE_SERIES_SONYPS5C` | Maximum price allowed for a match, applies PS5 console | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PS5 above `1234` will be skipped. |
| `MAX_PRICE_SERIES_SONYPS5DE` | Maximum price allowed for a match, applies PS5 digital edition | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PS5 above `1234` will be skipped. |
| `MAX_PRICE_SERIES_TEST` | Maximum price allowed for a match, applies `test:series` | Default: leave empty for no limit, otherwise enter a price (enter whole dollar amounts only, avoid use of: dollar symbols, commas, and periods.) e.g.: `1234` - PS5 above `1234` will be skipped. |
| `MICROCENTER_LOCATION` | Specific MicroCenter location(s) to search | Comma separated, e.g.: `marietta,duluth`, default: `web` |
| `NVIDIA_ADD_TO_CART_ATTEMPTS` | The maximum number of times the `nvidia-api` add to cart feature will be attempted before failing | Default: `10` |
| `NVIDIA_SESSION_TTL` | The time in milliseconds to keep the cart active while using `nvidia-api` | Default: `60000` |
| `SHOW_ONLY_BRANDS` | Filter to show specified brands | Comma separated, e.g.: `evga,zotac` |
| `SHOW_ONLY_MODELS` | Filter to show specified models | Both supported formats are comma separated <br/><br/>1. Standard E.g.: `founders edition,rog strix` <br/><br/> 2. Advanced E.g: `MODEL:SERIES`, E.g: `founders edition:3090,rog strix` |
| `SHOW_ONLY_SERIES` | Filter to show specified series | Comma separated, e.g.: `3080,ryzen5900` |
| `STORES` | [Supported stores](#supported-stores) you want to be scraped | Both supported formats are comma separated <br/><br/>1. Standard E.g.: `"nvidia"` <br/><br/> 2. Advanced E.g: `STORE:PAGE_SLEEP_MIN:PAGE_SLEEP_MAX`, E.g: `nvidia:10000:30000` <br/><br/>Default: `nvidia` |
## Supported stores
Used with the `STORES` variable.
| Stores | Country | Environment variable |
|---|---|---|
| Adorama | US | `adorama`|
| Alternate | DE | `alternate`|
| Alternate | NL | `alternate-nl`|
| Amazon | US | `amazon`|
| Amazon | CA | `amazon-ca`|
| Amazon | DE | `amazon-de`|
| Amazon Warehouse | DE | `amazon-de-warehouse`|
| Amazon | ES | `amazon-es`|
| Amazon | FR | `amazon-fr`|
| Amazon | IT | `amazon-it`|
| Amazon | NL | `amazon-nl`|
| Amazon | UK | `amazon-uk`|
| AMD | US | `amd`|
| AMD | CA | `amd-ca`|
| AMD | DE | `amd-de`|
| AMD | IT | `amd-it`|
| AntOnline | US | `antonline`|
| Argos | UK | `argos`|
| Argos | IE | `argos-ie`|
| Aria PC | UK | `aria`|
| ARLT | DE | `arlt`|
| ASUS | US | `asus` |
| ASUS | DE | `asus-de` |
| Azerty | NL | `azerty`|
| B&H | US | `bandh`|
| Best Buy | US | `bestbuy`|
| Best Buy | CA | `bestbuy-ca`|
| Box | UK | `box`|
| BPCTech | AU | `bpctech`|
| CanadaComputers | CA | `canadacomputers` |
| Caseking | DE | `caseking`|
| CCL | UK | `ccl`|
| Centrecom | AU | `centrecom`|
| Comet | IT | `comet`|
| Computeruniverse | DE | `computeruniverse` |
| Coolblue | NL | `coolblue`|
| Coolmod | ES | `coolmod`|
| Corsair | US | `corsair`|
| CPL | AU | `cpl`|
| Currys | UK | `currys`|
| Cyberport | DE | `cyberport` |
| EBGames | CA | `ebgames`|
| eBuyer | UK | `ebuyer`|
| El Corte Inglés | ES | `elcorteingles`|
| ePrice | IT | `eprice`|
| Euronics | IT | `euronics`|
| Euronics | DE | `euronics-de`|
| EVGA | US | `evga`|
| EVGA | EU | `evga-eu`|
| Expert | DE | `expert`|
| Galaxus | DE | `galaxus`|
| Game | UK | `game`|
| Gamestop | US | `gamestop`|
| Gamestop | DE | `gamestop-de`|
| Gamestop | IE | `gamestop-ie`|
| Harvey Normans | IE | | `harveynorman-ie` |
| Kabum | BR | `kabum`|
| Mediamarkt | DE | `mediamarkt`|
| Medimax | DE | `medimax`|
| Megekko | NL | `megekko`|
| MemoryExpress | CA | `memoryexpress`|
| Micro Center | US | `microcenter`|
| Mindfactory | DE | `mindfactory` |
| Mwave | AU | `mwave`|
| Newegg | US | `newegg`|
| Newegg | CA | `newegg-ca`|
| Notebooksbilliger | DE |`notebooksbilliger`|
| Novatech | UK | `novatech`|
| Nvidia | US | `nvidia`|
| Nvidia API | | `nvidia-api`|
| Office Depot | US | `officedepot`|
| Otto | DE | `otto`|
| Overclockers | UK | `overclockers`|
| PCCG | AU | `pccg`|
| PCComponentes | ES | `pccomponentes`|
| PlayStation | US | `playstation`|
| PNY | US | `pny`|
| Proshop | DE | `proshop-de`|
| Proshop | DK | `proshop-dk`|
| Saturn | DE | `saturn`|
| Scan | UK | `scan`|
| Scorptec | AU | `scorptec`|
| Smyths Toys | UK | `smythstoys`|
| Smyths Toys | IE | `smythstoys-ie`|
| Spielegrotte | DE | `spielegrotte`|
| Target | US | `target`|
| Tesco | IE | `tesco-ie`|
| Toys R Us | US | `toysrus` |
| Umart | AU | `umart`|
| Unieuro | IT | `unieuro`|
| Very | UK | `very`|
| Walmart | US | `walmart`|
| Walmart | CA | `walmart-ca`|
| Wipoid | ES | `wipoid`|
| Zotac | US | `zotac`|
| TopAchat | GR | `topachat`|
### Micro Center stores
Used with the `MICROCENTER_LOCATION` variable.
???+ note
Before using `web`, please review [this issue comment](https://github.com/jef/streetmerchant/issues/442#issuecomment-703297393).
| | | | |
|---|---|---|---|
| `brooklyn` | `brentwood` | `cambridge` | `chicago` |
| `columbus` | `dallas` | `denver` | `duluth` |
| `fairfax` | `flushing` | `houston` | `madison-heights` |
| `marietta` | `mayfield-heights` | `north-jersey` | `overland-park` |
| `parkville` | `rockville` | `sharonville` | `st-davids` |
| `st-louis-park` | `tustin` | `westbury` | `westmont` |
| `yonkers` |
## Supported brands and models
Used with the `SHOW_ONLY_BRANDS` and `SHOW_ONLY_MODELS` variables.
| Brand | Model |
|:---:|---|
| `amd` | `5600x`, `5800x`, `5900x`, `5950x`, `amd reference` |
| `asus` | `crosshair viii`, `dual`, `dual oc`, `strix`, `strix oc`, `tuf`, `tuf oc` |
| `corsair` | `750 platinum`, `600 platinum` |
| `colorful` | `igame advanced oc`, `igame vulcan oc` |
| `evga` | `ftw3`, `ftw3 ultra`, `ftw3 ultra hydro copper`, `xc3`, `xc3 black`, `xc3 ultra` |
| `gainward` | `phantom gs`, `phoenix`, `phoenix gs`, `phoenix gs oc` |
| `gigabyte` | `aorus master`, `aorus xtreme`, `aorus xtreme waterforce`, `aorus xtreme waterforce wb`, `eagle`, `eagle oc`, `gaming`, `gaming oc`, `turbo`, `vision`, `vision oc` |
| `galax` | `sg`, `sg oc` |
| `inno3d` | `gaming x3`, `ichill x3`, `ichill x4`, `ichill frostbite`, `twin x2 oc` |
| `kfa2` | `sg`, `sg oc` |
| `leadtek` | `hurricane` |
| `microsoft` | `xbox series x`, `xbox series s` |
| `msi` | `gaming x trio`, `suprim x`, `ventus 2x oc`, `ventus 3x`, `ventus 3x oc` |
| `nvidia` | `founders edition` |
| `palit` | `gamerock oc`, `gaming pro`, `gaming pro oc` |
| `pny` | `dual fan`, `xlr8 revel`, `xlr8 uprising` |
| `sony` | `ps5 console`, `ps5 digital` |
| `xfx` | `merc`, `amd reference` |
| `zotac` | `amp holo`, `amp extreme holo`, `trinity`, `trinity oc`, `twin edge`, `twin edge oc` |
## Supported series
Used with the `SHOW_ONLY_SERIES` variable.
| Series | Value |
|:---:|:---:|
| AMD Ryzen 5600x | `ryzen5600` |
| AMD Ryzen 5800x | `ryzen5800` |
| AMD Ryzen 5900x | `ryzen5900` |
| AMD Ryzen 5950x | `ryzen5950` |
| AMD RX 6800 | `rx6800` |
| AMD RX 6800XT | `rx6800xt` |
| AMD RX 6900XT | `rx6900xt` |
| Nvidia RTX 3060 Ti | `3060ti` |
| Nvidia RTX 3070 | `3070` |
| Nvidia RTX 3080 | `3080` |
| Nvidia RTX 3090 | `3090` |
| Corsair SFX PSU | `sf` |
| Crosshair VIII | `darkhero` |
| Sony PS5 | `sonyps5c` |
| Sony PS5 Digital Edition | `sonyps5de` |
| Xbox Series S | `xboxss` |
| Xbox Series X | `xboxsx` |
## Supported countries
Used with the `COUNTRY` variable.
???+ attention
Used _only_ with `nvidia` and `nvidia-api`.
| Country | 3080 FE | 3090 FE | Test Card | Notes |
|:---:|:---:|:---:|:---:|:---:|
| `austria` | `✔` | `✔` | `✔` | |
| `belgium` | `✔` | `✔` | `✔` | |
| `canada` | `✔` | `✔` | `✔` | |
| `czechia` | `✔` | `✔` | `✔` | |
| `denmark` | `✔` | | `✔` | Missing RTX 3090 |
| `finland` | `✔` | | `✔` | Missing RTX 3090 |
| `france` | `✔` | `✔` | `✔` | |
| `germany` | `✔` | `✔` | `✔` | |
| `great_britain` | `✔` | `✔` | `✔` | |
| `ireland` | `✔` | `✔` | `✔` | |
| `italy` | `✔` | `✔` | `✔` | |
| `luxembourg` | `✔` | `✔` | `✔` | |
| `netherlands` | `✔` | `✔` | `✔` | |
| `norway` | `✔` | `✔` | `✔` | |
| `poland` | `✔` | `✔` | `✔` | |
| `portugal` | `✔` | | | RTX 3080 only |
| `spain` | `✔` | `✔` | `✔` | |
| `sweden` | `✔` | `✔` | `✔` | |
| `usa` | `✔` | `✔` | `✔` | |
+193
View File
@@ -0,0 +1,193 @@
# Notification
You can test your notification configuration by running `npm run test:notification`.
## Desktop
| Environment variable | Description |
|---|---|
| `DESKTOP_NOTIFICATIONS` | Display desktop notifications using [node-notifier](https://www.npmjs.com/package/node-notifier). |
| `PLAY_SOUND` | Play this sound notification if a product is found. Relative path accepted, valid formats: wav, mp3, flac, E.g.: `path/to/notification.wav`, [free sounds available](https://notificationsounds.com/) |
| `SOUND_PLAYER` | Override the default sound player using the specified executable. |
???+ attention
If you're on Windows, you must have the proper library to run.
## Discord
| Environment variable | Description |
|:---:|---|
| `DISCORD_NOTIFY_GROUP` | Discord group you would like to notify |
| `DISCORD_NOTIFY_GROUP_3060TI` | Discord group to notify on 3060 Ti stock |
| `DISCORD_NOTIFY_GROUP_3070` | Discord group to notify on 3070 stock |
| `DISCORD_NOTIFY_GROUP_3080` | Discord group to notify on 3080 stock |
| `DISCORD_NOTIFY_GROUP_3090` | Discord group to notify on 3090 stock |
| `DISCORD_NOTIFY_GROUP_RYZEN5600` | Discord group to notify on 5600X stock |
| `DISCORD_NOTIFY_GROUP_RYZEN5800` | Discord group to notify on 5800X stock |
| `DISCORD_NOTIFY_GROUP_RYZEN5900` | Discord group to notify on 5900X stock |
| `DISCORD_NOTIFY_GROUP_RYZEN5950` | Discord group to notify on 5950X stock |
| `DISCORD_NOTIFY_GROUP_DARKHERO` | Discord group to notify on Dark Hero stock |
| `DISCORD_NOTIFY_GROUP_CORSAIR_SF` | Discord group to notify on Corsair SF stock |
| `DISCORD_NOTIFY_GROUP_SONYPS5C` | Discord group to notify on Sony PS5 stock |
| `DISCORD_NOTIFY_GROUP_SONYPS5DE` | Discord group to notify on Sony PS5 Digital stock |
| `DISCORD_NOTIFY_GROUP_XBOXSX` | Discord group to notify on Xbox Series X stock |
| `DISCORD_NOTIFY_GROUP_XBOXSS` | Discord group to notify on Xbox Series S stock |
| `DISCORD_NOTIFY_GROUP_TEST` | Discord group to notify on test stock |
| `DISCORD_WEB_HOOK` | Discord Web Hook URL. Can be comma separated. Use whole webhook URL |
???+ note
All ground can be comma separated. `DISCORD_NOTIFY_GROUP` is always used if set.
- If you're using a role, please use `<@&2834729847239842>`
- If you're using a user, please use `<@2834729847239842>`
## Email and SMS
Default provider is Gmail. If you use a different email provider, you must provide SMTP settings.
| Environment variable | Description |
|:---:|---|
| `EMAIL_PASSWORD` | Email password. (See below for Gmail MFA users) |
| `EMAIL_TO` | Destination Email. Defaults to username if not set. Can be comma separated |
| `EMAIL_USERNAME` | Email address |
| `PHONE_CARRIER` | [Supported carriers](#supported-carriers) for SMS. E.g.: `att` or `att,verizon,google`, email configuration required. If multiple phone numbers are listed, enter a carrier for each phone number |
| `PHONE_NUMBER` | 10 digit phone number(s). E.g.: `1234567890` or `1234567890,0987654321,11112223333`, email configuration required |
| `SMTP_ADDRESS` | IP Address or FQDN of SMTP server |
| `SMTP_PORT` | TCP Port number on which the smtp server is listening for connections. Default: `25` |
???+ attention
If you use Gmail and have multi-factor authentication (MFA), you will need to create an [app password](https://myaccount.google.com/apppasswords) and use this instead of your Gmail password.
### Supported carriers
| Carrier | Environment variable |
|:---:|:---:|
| AT&T | `att` |
| AT&T Prepaid | `attgo` |
| Bell | `bell` |
| Fido | `fido` |
| Google | `google`|
| Koodo | `koodo` |
| Mint | `mint`|
| Rogers | `rogers` |
| Sprint | `sprint`|
| Telus | `telus`|
| T-Mobile | `tmobile`|
| Verizon | `verizon`|
| Virgin | `virgin`|
| Virgin (CA) | `virgin-ca`|
| Visible | `visible`|
## MQTT
| Environment variable | Description |
|:---:|---|
| `MQTT_BROKER_ADDRESS` | IP address or URL of MQTT Broker, e.g.: `192.168.1.xxx` or `broker.hivemq.com` |
| `MQTT_BROKER_PORT` | Network port of MQTT Broker. Default: `1883` |
| `MQTT_CLIENT_ID` | Unique Client ID (only if required by MQTT Broker), typically not required when only publishing alerts |
| `MQTT_PASSWORD` | MQTT password - only use with MQTT brokers on private networks, if required. Will not be sent over public networks for safety |
| `MQTT_QOS` | QoS level for published alerts to broker (https://www.npmjs.com/package/mqtt#about-qos). Default: `0`, Can be `0`, `1`, or `2` |
| `MQTT_TOPIC` | Topic to publish alerts to. Can include `%store%`, `%series%`, `%brand%`, `%model%` for dynamic topics. Default: `streetmerchant/alert`. E.g.: `nv-alert/%store%/%series%/%brand%/%model%/alert` |
| `MQTT_USERNAME` | MQTT username - (only if required by MQTT Broker) |
## PagerDuty
Obtained in PagerDuty - <Service you want to use> - Integrations
| Environment variable | Description |
|:---:|---|
| `PAGERDUTY_INTEGRATION_KEY` | PagerDuty Events API v2 Integration Key. |
| `PAGERDUTY_SEVERITY` | Severity of PagerDuty events |
## Philips Hue
Generate required keys using [instructions](https://developers.meethue.com/develop/get-started-2/). This will be used for both LAN and cloud access over the official Remote Hue API.
For cloud only usage, instructions to generate are located [here](https://developers.meethue.com/develop/hue-api/remote-authentication/).
> :point_right: Here's a [video demonstration](https://vimeo.com/476083242).
| Environment variable | Description |
|:---:|---|
| `PHILIPS_HUE_API_KEY` | Hue Bridge API Key |
| `PHILIPS_HUE_LAN_BRIDGE_IP` | LAN IP Address of your Hue Bridge. LAN only, e.g. `192.168.x.x` |
| `PHILIPS_HUE_LIGHT_IDS` | Light IDs. All lights if not supplied. Can be comma separated, e.g.: `1,2`. See Hue App -> About for IDs |
| `PHILIPS_HUE_LIGHT_COLOR` | Color in RGB Format. Nvidia green if not supplied. Can be comma separated, e.g.: `255,255,255` |
| `PHILIPS_HUE_LIGHT_PATTERN` | Lights will flash for 30 seconds if `blink` is given |
| `PHILIPS_HUE_CLOUD_ACCESS_TOKEN` | Cloud Access Token. Cloud only |
| `PHILIPS_HUE_CLOUD_REFRESH_TOKEN` | Cloud Refresh Token. Cloud only |
| `PHILIPS_HUE_CLOUD_CLIENT_ID` | Cloud Client ID. Cloud only |
| `PHILIPS_HUE_CLOUD_CLIENT_SECRET` | Cloud Client Secret. Cloud only |
## Pushbullet
Generate token at https://www.pushbullet.com/#settings/account.
| Environment variable | Description |
|:---:|---|
| `PUSHBULLET` | API key |
## Pushover
Generate token at https://pushover.net/apps/build.
| Environment variable | Description |
|:---:|---|
| `PUSHOVER_EXPIRE` | How many seconds your notification will continue to be retried for (every `PUSHOVER_RETRY` seconds) |
| `PUSHOVER_RETRY` | How often (in seconds) the Pushover servers will send the same notification to the user |
| `PUSHOVER_PRIORITY` | Message priority |
| `PUSHOVER_TOKEN` | API token |
| `PUSHOVER_USER` | Username |
???+ note
`PUSHOVER_EXPIRE` and `PUSHOVER_RETRY` are only used when `PUSHOVER_PRIORITY="2"`
## Slack
| Environment variable | Description |
|:---:|---|
| `SLACK_CHANNEL` | Channel for posting |
| `SLACK_TOKEN` | API token |
## Telegram
| Environment variable | Description |
|:---:|---|
| `TELEGRAM_ACCESS_TOKEN` | Access token |
| `TELEGRAM_CHAT_ID` | Chat ID. Can be comma separated, e.g.: `123456789,987654321` |
## Twilio
Token generation can be found at https://twilio.com/console.
| Environment variable | Description |
|:---:|---|
| `TWILIO_ACCOUNT_SID` | Account SID |
| `TWILIO_AUTH_TOKEN` | Auth Token |
| `TWILIO_FROM_NUMBER` | Provided phone number to send messages from |
| `TWILIO_TO_NUMBER` | Mobile number to send SMS to |
???+ note
Include country codes in phone numbers. Example: `+4401234567890`
## Twitter
Generate all Twitter keys at: https://developer.twitter.com/
| Environment variable | Description |
|:---:|---|
| `TWITTER_ACCESS_TOKEN_KEY` | Twitter Token Key |
| `TWITTER_ACCESS_TOKEN_SECRET` | Twitter Token Secret |
| `TWITTER_CONSUMER_KEY` | Twitter Consumer Key |
| `TWITTER_CONSUMER_SECRET` | Twitter Consumer Secret |
| `TWITTER_TWEET_TAGS` | List of hashtags to append to the tweet message, e.g.: `#nvidia #nvidiastock` |
## Twitch
| Environment variable | Description |
|:---:|---|
| `TWITCH_CLIENT_ID` | Twitch client ID |
| `TWITCH_CLIENT_SECRET`| Twitch client secret |
| `TWITCH_ACCESS_TOKEN` | Twitch access token |
| `TWITCH_REFRESH_TOKEN` | Twitch refresh token |
| `TWITCH_CHANNEL` | Twitch channel |
+117
View File
@@ -0,0 +1,117 @@
# ** All configuration variables are optional **
# Copy this file and name it as `dotenv` (without backticks)
# Read https://jef.codes/streetmerchant/getting-started/#customization for help on customizing this file
########################################################################################################
ASCII_BANNER=
ASCII_COLOR=
AUTO_ADD_TO_CART=
BROWSER_TRUSTED=
COUNTRY=
DESKTOP_NOTIFICATIONS=
DISCORD_NOTIFY_GROUP=
DISCORD_NOTIFY_GROUP_3060TI=
DISCORD_NOTIFY_GROUP_3070=
DISCORD_NOTIFY_GROUP_3080=
DISCORD_NOTIFY_GROUP_3090=
DISCORD_NOTIFY_GROUP_CORSAIR_SF=
DISCORD_NOTIFY_GROUP_RX6800=
DISCORD_NOTIFY_GROUP_RX6800XT=
DISCORD_NOTIFY_GROUP_RX6900XT=
DISCORD_NOTIFY_GROUP_RYZEN5600=
DISCORD_NOTIFY_GROUP_RYZEN5800=
DISCORD_NOTIFY_GROUP_RYZEN5900=
DISCORD_NOTIFY_GROUP_RYZEN5950=
DISCORD_NOTIFY_GROUP_SONYPS5C=
DISCORD_NOTIFY_GROUP_SONYPS5DE=
DISCORD_NOTIFY_GROUP_XBOXSX=
DISCORD_NOTIFY_GROUP_XBOXSS=
DISCORD_NOTIFY_GROUP_TEST=
DISCORD_WEB_HOOK=
EMAIL_PASSWORD=
EMAIL_TO=
EMAIL_USERNAME=
HEADLESS=
IN_STOCK_WAIT_TIME=
LOG_LEVEL=
LOW_BANDWIDTH=
MAX_PRICE_SERIES_3060TI=
MAX_PRICE_SERIES_3070=
MAX_PRICE_SERIES_3080=
MAX_PRICE_SERIES_3090=
MAX_PRICE_SERIES_CORSAIR_SF=
MAX_PRICE_SERIES_RX6800=
MAX_PRICE_SERIES_RX6800XT=
MAX_PRICE_SERIES_RX6900XT=
MAX_PRICE_SERIES_RYZEN5600=
MAX_PRICE_SERIES_RYZEN5800=
MAX_PRICE_SERIES_RYZEN5900=
MAX_PRICE_SERIES_RYZEN5950=
MAX_PRICE_SERIES_SONYPS5C=
MAX_PRICE_SERIES_SONYPS5DE=
MAX_PRICE_SERIES_TEST=
MICROCENTER_LOCATION=
MQTT_BROKER_ADDRESS=
MQTT_BROKER_PORT=
MQTT_CLIENT_ID=
MQTT_PASSWORD=
MQTT_QOS=
MQTT_TOPIC=
MQTT_USERNAME=
NVIDIA_ADD_TO_CART_ATTEMPTS=
NVIDIA_SESSION_TTL=
OPEN_BROWSER=
PAGE_BACKOFF_MIN=
PAGE_BACKOFF_MAX=
PAGE_SLEEP_MIN=
PAGE_SLEEP_MAX=
PAGE_TIMEOUT=
PAGERDUTY_INTEGRATION_KEY=
PAGERDUTY_SEVERITY=
PHILIPS_HUE_API_KEY=
PHILIPS_HUE_CLOUD_ACCESS_TOKEN=
PHILIPS_HUE_CLOUD_CLIENT_ID=
PHILIPS_HUE_CLOUD_CLIENT_SECRET=
PHILIPS_HUE_CLOUD_REFRESH_TOKEN=
PHILIPS_HUE_LAN_BRIDGE_IP=
PHILIPS_HUE_LIGHT_COLOR=
PHILIPS_HUE_LIGHT_IDS=
PHILIPS_HUE_LIGHT_PATTERN=
PHONE_CARRIER=
PHONE_NUMBER=
PLAY_SOUND=
PROXY_ADDRESS=
PROXY_PROTOCOL=
PROXY_PORT=
PUSHBULLET=
PUSHOVER_EXPIRE=
PUSHOVER_RETRY=
PUSHOVER_TOKEN=
PUSHOVER_USER=
PUSHOVER_PRIORITY=
SCREENSHOT=
SHOW_ONLY_BRANDS=
SHOW_ONLY_MODELS=
SHOW_ONLY_SERIES=
SLACK_CHANNEL=
SLACK_TOKEN=
SMTP_ADDRESS=
SMTP_PORT=
STORES=
TELEGRAM_ACCESS_TOKEN=
TELEGRAM_CHAT_ID=
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_FROM_NUMBER=
TWILIO_TO_NUMBER=
TWITCH_ACCESS_TOKEN=
TWITCH_CHANNEL=
TWITCH_CLIENT_ID=
TWITCH_CLIENT_SECRET=
TWITCH_REFRESH_TOKEN=
TWITTER_ACCESS_TOKEN_KEY=
TWITTER_ACCESS_TOKEN_SECRET=
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_TWEET_TAGS=
WEB_PORT=
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 KiB

+91
View File
@@ -0,0 +1,91 @@
# Project information
site_name: streetmerchant
site_url: https://jef.codes/streetmerchant
site_author: Jef LeCompte
site_description: The world's easiest, most powerful stock checker
# Repository
repo_name: jef/streetmerchant
repo_url: https://github.com/jef/streetmerchant
# Copyright
copyright: Copyright &copy; 2016 - 2020 Jef LeCompte
# Configuration
theme:
favicon: https://raw.githubusercontent.com/jef/streetmerchant/main/docs/assets/images/streetmerchant-logo.png
features:
- search.suggest
font:
text: Noto Sans
code: JetBrains Mono
icon:
repo: octicons/octoface-24
logo: assets/images/streetmerchant-logo.png
language: en
name: material
palette:
scheme: default
primary: indigo
accent: indigo
# Plugins
plugins:
- git-revision-date
- macros
- search
# Customization
extra:
social:
- icon: fontawesome/brands/github
link: https://github.com/jef
- icon: fontawesome/brands/twitter
link: https://twitter.com/hijxf
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/jeflecompte/
extra_javascript:
- https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js
- javascripts/tables.js
# Extensions
markdown_extensions:
- admonition
- attr_list
- footnotes
- meta
- toc:
permalink: true
- pymdownx.caret
- pymdownx.critic
- pymdownx.details
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.highlight:
linenums: true
- pymdownx.inlinehilite
- pymdownx.keys
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.tabbed
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.tilde
# Page tree
nav:
- Home: index.md
- Getting started: getting-started.md
- Reference:
- Application: reference/application.md
- Filter: reference/filter.md
- Notification: reference/notification.md
- Help:
- General: help/general.md
- Troubleshoot: help/troubleshoot.md
- FAQ: faq.md
- Changelog: changelog.md
- About: about.md
+1 -1
View File
@@ -3,6 +3,6 @@
"ext": "ts", "ext": "ts",
"watch": [ "watch": [
"src/", "src/",
".env" "dotenv"
] ]
} }
+321 -365
View File
File diff suppressed because it is too large Load Diff
+12 -15
View File
@@ -27,6 +27,7 @@
"dependencies": { "dependencies": {
"@jef/pushbullet": "^2.4.3", "@jef/pushbullet": "^2.4.3",
"@slack/web-api": "^5.14.0", "@slack/web-api": "^5.14.0",
"@types/random-useragent": "^0.3.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"cheerio": "^1.0.0-rc.3", "cheerio": "^1.0.0-rc.3",
"discord.js": "^12.5.1", "discord.js": "^12.5.1",
@@ -47,41 +48,37 @@
"puppeteer-extra-plugin-stealth": "^2.6.5", "puppeteer-extra-plugin-stealth": "^2.6.5",
"puppeteer-page-proxy": "^1.2.8", "puppeteer-page-proxy": "^1.2.8",
"pushover-notifications": "^1.2.2", "pushover-notifications": "^1.2.2",
"twilio": "^3.53.0", "random-useragent": "^0.5.0",
"twitch": "^4.3.3", "twilio": "^3.54.0",
"twitch-auth": "^4.3.3", "twitch": "^4.3.5",
"twitch-chat-client": "^4.3.3", "twitch-auth": "^4.3.5",
"twitch-chat-client": "^4.3.6",
"twitter": "^1.7.1", "twitter": "^1.7.1",
"winston": "^3.3.3" "winston": "^3.3.3"
}, },
"devDependencies": { "devDependencies": {
"@types/async": "^3.2.5", "@types/async": "^3.2.5",
"@types/cheerio": "^0.22.22", "@types/cheerio": "^0.22.23",
"@types/node": "^14.14.10", "@types/node": "^14.14.12",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.5.7",
"@types/node-notifier": "^8.0.0", "@types/node-notifier": "^8.0.0",
"@types/nodemailer": "^6.4.0", "@types/nodemailer": "^6.4.0",
"@types/puppeteer": "^5.4.1", "@types/puppeteer": "^5.4.2",
"@types/twitter": "^1.7.0", "@types/twitter": "^1.7.0",
"husky": "^4.3.0",
"nodemon": "^2.0.6", "nodemon": "^2.0.6",
"ts-node": "^9.1.0", "ts-node": "^9.1.1",
"typescript": "^4.1.2", "typescript": "^4.1.2",
"webpack": "^5.10.0", "webpack": "^5.10.0",
"xo": "^0.35.0" "xo": "^0.36.1"
}, },
"xo": { "xo": {
"prettier": true, "prettier": true,
"ignores": "docs/javascripts",
"rules": { "rules": {
"no-warning-comments": "off", "no-warning-comments": "off",
"sort-imports": "error", "sort-imports": "error",
"sort-keys": "error", "sort-keys": "error",
"sort-vars": "error" "sort-vars": "error"
} }
},
"husky": {
"hooks": {
"pre-commit": "npm run all"
}
} }
} }
+1
View File
@@ -11,6 +11,7 @@ const link: Link = {
}; };
const store: Store = { const store: Store = {
currency: '',
labels: { labels: {
inStock: { inStock: {
container: 'test:container', container: 'test:container',
+40 -14
View File
@@ -1,10 +1,13 @@
import {existsSync, readFileSync} from 'fs';
import {banner} from './banner'; import {banner} from './banner';
import dotenv from 'dotenv';
import {config as config_} from 'dotenv';
import path from 'path'; import path from 'path';
import {readFileSync} from 'fs';
config_({path: path.resolve(__dirname, '../.env')}); if (existsSync(path.resolve(__dirname, '../dotenv'))) {
dotenv.config({path: path.resolve(__dirname, '../dotenv')});
} else {
dotenv.config({path: path.resolve(__dirname, '../.env')});
}
console.info( console.info(
banner.render( banner.render(
@@ -66,8 +69,8 @@ function envOrNumber(environment: string | undefined, number?: number): number {
/** /**
* Returns environment variable, given number, or default number, * Returns environment variable, given number, or default number,
* while handling .env input errors for a Min/Max pair. * while handling dotenv input errors for a Min/Max pair.
* .env errors handled: * dotenv errors handled:
* - Min/Max swapped (Min larger than Max, Max smaller than Min) * - Min/Max swapped (Min larger than Max, Max smaller than Min)
* - Min larger than default Max when no Max defined * - Min larger than default Max when no Max defined
* - Max smaller than default Min when no Min defined * - Max smaller than default Min when no Min defined
@@ -106,8 +109,8 @@ function envOrNumberMin(
/** /**
* Returns environment variable, given number, or default number, * Returns environment variable, given number, or default number,
* while handling .env input errors for a Min/Max pair. * while handling dotenv input errors for a Min/Max pair.
* .env errors handled: * dotenv errors handled:
* - Min/Max swapped (Min larger than Max, Max smaller than Min) * - Min/Max swapped (Min larger than Max, Max smaller than Min)
* - Min larger than default Max when no Max defined * - Min larger than default Max when no Max defined
* - Max smaller than default Min when no Min defined * - Max smaller than default Min when no Min defined
@@ -169,10 +172,11 @@ const browser = {
process.env.PAGE_SLEEP_MAX, process.env.PAGE_SLEEP_MAX,
5000 5000
), ),
open: envOrBoolean(process.env.OPEN_BROWSER) open: envOrBoolean(process.env.OPEN_BROWSER),
userAgent: ''
}; };
const docker = envOrBoolean(process.env.DOCKER); const docker = envOrBoolean(process.env.DOCKER, false);
const logLevel = envOrString(process.env.LOG_LEVEL, 'info'); const logLevel = envOrString(process.env.LOG_LEVEL, 'info');
@@ -180,6 +184,26 @@ const notifications = {
desktop: process.env.DESKTOP_NOTIFICATIONS === 'true', desktop: process.env.DESKTOP_NOTIFICATIONS === 'true',
discord: { discord: {
notifyGroup: envOrArray(process.env.DISCORD_NOTIFY_GROUP), notifyGroup: envOrArray(process.env.DISCORD_NOTIFY_GROUP),
notifyGroupSeries: {
'3060ti': envOrArray(process.env.DISCORD_NOTIFY_GROUP_3060TI),
3070: envOrArray(process.env.DISCORD_NOTIFY_GROUP_3070),
3080: envOrArray(process.env.DISCORD_NOTIFY_GROUP_3080),
3090: envOrArray(process.env.DISCORD_NOTIFY_GROUP_3090),
darkhero: envOrArray(process.env.DISCORD_NOTIFY_GROUP_DARKHERO),
rx6800: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RX6800),
rx6800xt: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RX6800XT),
rx6900xt: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RX6900XT),
ryzen5600: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RYZEN5600),
ryzen5800: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RYZEN5800),
ryzen5900: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RYZEN5900),
ryzen5950: envOrArray(process.env.DISCORD_NOTIFY_GROUP_RYZEN5950),
sf: envOrArray(process.env.DISCORD_NOTIFY_GROUP_CORSAIR_SF),
sonyps5c: envOrArray(process.env.DISCORD_NOTIFY_GROUP_SONYPS5C),
sonyps5de: envOrArray(process.env.DISCORD_NOTIFY_GROUP_SONYPS5DE),
'test:series': envOrArray(process.env.DISCORD_NOTIFY_GROUP_TEST),
xboxss: envOrArray(process.env.DISCORD_NOTIFY_GROUP_XBOXSS),
xboxsx: envOrArray(process.env.DISCORD_NOTIFY_GROUP_XBOXSX)
},
webhooks: envOrArray(process.env.DISCORD_WEB_HOOK) webhooks: envOrArray(process.env.DISCORD_WEB_HOOK)
}, },
email: { email: {
@@ -232,7 +256,8 @@ const notifications = {
['tmobile', 'tmomail.net'], ['tmobile', 'tmomail.net'],
['verizon', 'vtext.com'], ['verizon', 'vtext.com'],
['virgin', 'vmobl.com'], ['virgin', 'vmobl.com'],
['virgin-ca', 'vmobile.ca'] ['virgin-ca', 'vmobile.ca'],
['visible', 'vtext.com']
]), ]),
carrier: envOrArray(process.env.PHONE_CARRIER), carrier: envOrArray(process.env.PHONE_CARRIER),
number: envOrArray(process.env.PHONE_NUMBER) number: envOrArray(process.env.PHONE_NUMBER)
@@ -240,7 +265,9 @@ const notifications = {
playSound: envOrString(process.env.PLAY_SOUND), playSound: envOrString(process.env.PLAY_SOUND),
pushbullet: envOrString(process.env.PUSHBULLET), pushbullet: envOrString(process.env.PUSHBULLET),
pushover: { pushover: {
expire: envOrNumber(process.env.PUSHOVER_EXPIRE),
priority: envOrNumber(process.env.PUSHOVER_PRIORITY), priority: envOrNumber(process.env.PUSHOVER_PRIORITY),
retry: envOrNumber(process.env.PUSHOVER_RETRY),
token: envOrString(process.env.PUSHOVER_TOKEN), token: envOrString(process.env.PUSHOVER_TOKEN),
username: envOrString(process.env.PUSHOVER_USER) username: envOrString(process.env.PUSHOVER_USER)
}, },
@@ -248,6 +275,7 @@ const notifications = {
channel: envOrString(process.env.SLACK_CHANNEL), channel: envOrString(process.env.SLACK_CHANNEL),
token: envOrString(process.env.SLACK_TOKEN) token: envOrString(process.env.SLACK_TOKEN)
}, },
soundPlayer: envOrString(process.env.SOUND_PLAYER),
telegram: { telegram: {
accessToken: envOrString(process.env.TELEGRAM_ACCESS_TOKEN), accessToken: envOrString(process.env.TELEGRAM_ACCESS_TOKEN),
chatId: envOrArray(process.env.TELEGRAM_CHAT_ID) chatId: envOrArray(process.env.TELEGRAM_CHAT_ID)
@@ -284,9 +312,6 @@ const page = {
inStockWaitTime: envOrNumber(process.env.IN_STOCK_WAIT_TIME), inStockWaitTime: envOrNumber(process.env.IN_STOCK_WAIT_TIME),
screenshot: envOrBoolean(process.env.SCREENSHOT), screenshot: envOrBoolean(process.env.SCREENSHOT),
timeout: envOrNumber(process.env.PAGE_TIMEOUT, 30000), timeout: envOrNumber(process.env.PAGE_TIMEOUT, 30000),
userAgents: envOrArray(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
}; };
@@ -312,6 +337,7 @@ const store = {
3070: envOrNumber(process.env.MAX_PRICE_SERIES_3070), 3070: envOrNumber(process.env.MAX_PRICE_SERIES_3070),
3080: envOrNumber(process.env.MAX_PRICE_SERIES_3080), 3080: envOrNumber(process.env.MAX_PRICE_SERIES_3080),
3090: envOrNumber(process.env.MAX_PRICE_SERIES_3090), 3090: envOrNumber(process.env.MAX_PRICE_SERIES_3090),
darkhero: envOrNumber(process.env.MAX_PRICE_SERIES_DARKHERO),
rx6800: envOrNumber(process.env.MAX_PRICE_SERIES_RX6800), rx6800: envOrNumber(process.env.MAX_PRICE_SERIES_RX6800),
rx6800xt: envOrNumber(process.env.MAX_PRICE_SERIES_RX6800XT), rx6800xt: envOrNumber(process.env.MAX_PRICE_SERIES_RX6800XT),
rx6900xt: envOrNumber(process.env.MAX_PRICE_SERIES_RX6900XT), rx6900xt: envOrNumber(process.env.MAX_PRICE_SERIES_RX6900XT),
+13 -3
View File
@@ -1,6 +1,6 @@
import {startAPIServer, stopAPIServer} from './web'; import {config} from './config'; // Needs to be loaded first
import {startAPIServer, stopAPIServer} from './web'; // eslint-disable-line sort-imports
import {Browser} from 'puppeteer'; import {Browser} from 'puppeteer';
import {config} from './config';
import {getSleepTime} from './util'; import {getSleepTime} from './util';
import {logger} from './logger'; import {logger} from './logger';
import puppeteer from 'puppeteer-extra'; import puppeteer from 'puppeteer-extra';
@@ -26,8 +26,13 @@ async function main() {
} }
// https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#tips // https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#tips
// https://stackoverflow.com/questions/48230901/docker-alpine-with-node-js-and-chromium-headless-puppeter-failed-to-launch-c
if (config.docker) { if (config.docker) {
args.push('--disable-dev-shm-usage'); args.push('--disable-dev-shm-usage');
args.push('--no-sandbox');
args.push('--disable-setuid-sandbox');
args.push('--headless');
args.push('--disable-gpu');
} }
// Add the address of the proxy server if defined // Add the address of the proxy server if defined
@@ -37,8 +42,11 @@ async function main() {
); );
} }
await stop(); if (args.length > 0) {
logger.info(' puppeteer config: ', args);
}
await stop();
browser = await puppeteer.launch({ browser = await puppeteer.launch({
args, args,
defaultViewport: { defaultViewport: {
@@ -48,6 +56,8 @@ async function main() {
headless: config.browser.isHeadless headless: config.browser.isHeadless
}); });
config.browser.userAgent = await browser.userAgent();
for (const store of storeList.values()) { for (const store of storeList.values()) {
logger.debug('store links', {meta: {links: store.links}}); logger.debug('store links', {meta: {links: store.links}});
if (store.setupAction !== undefined) { if (store.setupAction !== undefined) {
+11 -4
View File
@@ -20,7 +20,9 @@ const prettyJson = winston.format.printf((info) => {
export const logger = winston.createLogger({ export const logger = winston.createLogger({
format: winston.format.combine( format: winston.format.combine(
winston.format.colorize(), winston.format.colorize(),
winston.format.metadata({fillExcept: ['level', 'message', 'timestamp']}), winston.format.metadata({
fillExcept: ['level', 'message', 'timestamp']
}),
prettyJson prettyJson
), ),
level: config.logLevel, level: config.logLevel,
@@ -129,7 +131,9 @@ export const Print = {
'✖ ' + '✖ ' +
buildProductString(link, store, true) + buildProductString(link, store, true) +
' :: ' + ' :: ' +
chalk.yellow(`PRICE ${link.price ?? ''} EXCEEDS LIMIT ${maxPrice}`) chalk.yellow(
`PRICE ${link.price ?? ''} EXCEEDS LIMIT ${maxPrice}`
)
); );
} }
@@ -180,7 +184,8 @@ export const Print = {
}, },
productInStock(link: Link): string { productInStock(link: Link): string {
let productString = `Product Page: ${link.url}`; let productString = `Product Page: ${link.url}`;
if (link.cartUrl) productString += `\nAdd To Cart Link: ${link.cartUrl}`; if (link.cartUrl)
productString += `\nAdd To Cart Link: ${link.cartUrl}`;
return productString; return productString;
}, },
@@ -204,7 +209,9 @@ function buildSetupString(
color?: boolean color?: boolean
): string { ): string {
if (color) { if (color) {
return chalk.cyan(`[${store.name}]`) + chalk.grey(` [setup (${topic})]`); return (
chalk.cyan(`[${store.name}]`) + chalk.grey(` [setup (${topic})]`)
);
} }
return `[${store.name}] [setup (${topic})]`; return `[${store.name}] [setup (${topic})]`;
+27 -5
View File
@@ -4,7 +4,7 @@ import {config} from '../config';
import {logger} from '../logger'; import {logger} from '../logger';
const discord = config.notifications.discord; const discord = config.notifications.discord;
const {notifyGroup, webhooks} = discord; const {notifyGroup, webhooks, notifyGroupSeries} = discord;
function getIdAndToken(webhook: string) { function getIdAndToken(webhook: string) {
const match = /.*\/webhooks\/(\d+)\/(.+)/.exec(webhook); const match = /.*\/webhooks\/(\d+)\/(.+)/.exec(webhook);
@@ -31,33 +31,55 @@ export function sendDiscordMessage(link: Link, store: Store) {
'> provided by [streetmerchant](https://github.com/jef/streetmerchant) with :heart:' '> provided by [streetmerchant](https://github.com/jef/streetmerchant) with :heart:'
) )
.setThumbnail( .setThumbnail(
'https://raw.githubusercontent.com/jef/streetmerchant/main/media/streetmerchant-square.png' 'https://raw.githubusercontent.com/jef/streetmerchant/main/docs/assets/images/streetmerchant-logo.png'
) )
.setColor('#52b788') .setColor('#52b788')
.setTimestamp(); .setTimestamp();
embed.addField('Store', store.name, true); embed.addField('Store', store.name, true);
if (link.price) embed.addField('Price', `$${link.price}`, true); if (link.price)
embed.addField(
'Price',
`${store.currency}${link.price}`,
true
);
embed.addField('Product Page', link.url); embed.addField('Product Page', link.url);
if (link.cartUrl) embed.addField('Add to Cart', link.cartUrl); if (link.cartUrl) embed.addField('Add to Cart', link.cartUrl);
embed.addField('Brand', link.brand, true); embed.addField('Brand', link.brand, true);
embed.addField('Model', link.model, true); embed.addField('Model', link.model, true);
embed.addField('Series', link.series, true); embed.addField('Series', link.series, true);
embed.setTimestamp();
let notifyText: string[] = [];
if (notifyGroup) {
notifyText = notifyText.concat(notifyGroup);
}
if (Object.keys(notifyGroupSeries).indexOf(link.series) !== 0) {
notifyText = notifyText.concat(
notifyGroupSeries[link.series]
);
}
const promises = []; const promises = [];
for (const webhook of webhooks) { for (const webhook of webhooks) {
const {id, token} = getIdAndToken(webhook); const {id, token} = getIdAndToken(webhook);
const client = new Discord.WebhookClient(id, token); const client = new Discord.WebhookClient(id, token);
promises.push({ promises.push({
client, client,
message: client.send(notifyGroup.join(' '), { message: client.send(notifyText.join(' '), {
embeds: [embed], embeds: [embed],
username: 'streetmerchant' username: 'streetmerchant'
}) })
}); });
} }
(await Promise.all(promises)).forEach(({client}) => client.destroy()); (await Promise.all(promises)).forEach(({client}) =>
client.destroy()
);
logger.info('✔ discord message sent'); logger.info('✔ discord message sent');
} catch (error: unknown) { } catch (error: unknown) {
+1 -1
View File
@@ -65,7 +65,7 @@ function generateTopic(link: Link, store: Store, topic: string): string {
/** /**
* Basic protection against sending credentials in the clear over public networks. * Basic protection against sending credentials in the clear over public networks.
* - Returns 'true' if password is supplied in .env but address/URL is not part of a private network * - Returns 'true' if password is supplied in dotenv but address/URL is not part of a private network
* - Private networks evaluated: Class A, B, or C private IP's or linklocal URL ("*.local") * - Private networks evaluated: Class A, B, or C private IP's or linklocal URL ("*.local")
* - TLS could be implemented, however, the majority of MQTT services on the internet do not require user authentication. * - TLS could be implemented, however, the majority of MQTT services on the internet do not require user authentication.
* - If you find a 'cloud' MQTT broker requiring authentication for publishing alerts, consider using another MQTT service (for now). * - If you find a 'cloud' MQTT broker requiring authentication for publishing alerts, consider using another MQTT service (for now).
+2 -2
View File
@@ -18,12 +18,12 @@ import {sendTwitchMessage} from './twitch';
export function sendNotification(link: Link, store: Store) { export function sendNotification(link: Link, store: Store) {
// Priority // Priority
playSound(); playSound();
sendDiscordMessage(link, store);
sendDesktopNotification(link, store);
sendEmail(link, store); sendEmail(link, store);
sendSms(link, store); sendSms(link, store);
sendDesktopNotification(link, store);
// Non-priority // Non-priority
adjustPhilipsHueLights(); adjustPhilipsHueLights();
sendDiscordMessage(link, store);
sendMqttMessage(link, store); sendMqttMessage(link, store);
sendPagerDutyNotification(link, store); sendPagerDutyNotification(link, store);
sendPushbulletNotification(link, store); sendPushbulletNotification(link, store);
+23 -11
View File
@@ -45,13 +45,14 @@ const adjustLightsWithAPI = (hueBridge: Api) => {
const arrayOfIDs = lightIds.split(','); const arrayOfIDs = lightIds.split(',');
arrayOfIDs.forEach((light) => { arrayOfIDs.forEach((light) => {
logger.debug('adjusting all hue lights'); logger.debug('adjusting all hue lights');
(hueBridge.lights.setLightState(light, lightState) as Promise<any>).catch( (hueBridge.lights.setLightState(
(error: Error) => { light,
logger.error('Failed to adjust all lights.'); lightState
logger.error(error); ) as Promise<any>).catch((error: Error) => {
throw error; logger.error('Failed to adjust all lights.');
} logger.error(error);
); throw error;
});
}); });
} else { } else {
// Adjust all light IDs // Adjust all light IDs
@@ -102,15 +103,26 @@ export function adjustPhilipsHueLights() {
} else if (hue.apiKey && hue.clientId && hue.clientSecret) { } else if (hue.apiKey && hue.clientId && hue.clientSecret) {
logger.info('↗ adjusting Philips Hue lights over cloud'); logger.info('↗ adjusting Philips Hue lights over cloud');
(async () => { (async () => {
logger.debug('Attempting to connect to Philips Hue bridge over cloud'); logger.debug(
const remoteBootstrap = hueAPI.api.createRemote(clientId, clientSecret); 'Attempting to connect to Philips Hue bridge over cloud'
);
const remoteBootstrap = hueAPI.api.createRemote(
clientId,
clientSecret
);
if (hue.accessToken && hue.refreshToken) { if (hue.accessToken && hue.refreshToken) {
remoteBootstrap remoteBootstrap
.connectWithTokens(accessToken, refreshToken, remoteApiUsername) .connectWithTokens(
accessToken,
refreshToken,
remoteApiUsername
)
.then( .then(
(hueBridge) => { (hueBridge) => {
adjustLightsWithAPI(hueBridge); adjustLightsWithAPI(hueBridge);
logger.info('✔ adjusted Philips Hue lights over cloud'); logger.info(
'✔ adjusted Philips Hue lights over cloud'
);
}, },
(error: Error) => { (error: Error) => {
logger.error( logger.error(
+19 -9
View File
@@ -4,20 +4,30 @@ import Push, {PushoverMessage} from 'pushover-notifications';
import {config} from '../config'; import {config} from '../config';
const pushover = config.notifications.pushover; const pushover = config.notifications.pushover;
const push = new Push({
token: pushover.token,
user: pushover.username
});
export function sendPushoverNotification(link: Link, store: Store) { export function sendPushoverNotification(link: Link, store: Store) {
if (pushover.token && pushover.username) { if (pushover.token && pushover.username) {
logger.debug('↗ sending pushover message'); logger.debug('↗ sending pushover message');
const message: PushoverMessage = { const push = new Push({
message: link.cartUrl ? link.cartUrl : link.url, token: pushover.token,
priority: pushover.priority, user: pushover.username
title: Print.inStock(link, store) });
};
const message: PushoverMessage =
pushover.priority < 2
? {
message: link.cartUrl ? link.cartUrl : link.url,
priority: pushover.priority,
title: Print.inStock(link, store)
}
: {
expire: pushover.expire,
message: link.cartUrl ? link.cartUrl : link.url,
priority: pushover.priority,
retry: pushover.retry,
title: Print.inStock(link, store)
};
push.send(message, (error: Error) => { push.send(message, (error: Error) => {
if (error) { if (error) {
+1 -1
View File
@@ -4,7 +4,7 @@ import {WebClient} from '@slack/web-api';
import {config} from '../config'; import {config} from '../config';
const slack = config.notifications.slack; const slack = config.notifications.slack;
const channel = slack.channel; const channel = slack.channel.replace('#', '');
const token = slack.token; const token = slack.token;
const web = new WebClient(token); const web = new WebClient(token);
+20 -12
View File
@@ -6,7 +6,9 @@ import {logger} from '../logger';
let player: PlaySound; let player: PlaySound;
if (config.notifications.playSound) { if (config.notifications.playSound) {
player = playerLib(); player = config.notifications.soundPlayer
? playerLib({players: [config.notifications.soundPlayer]})
: playerLib();
if (player.player === null) { if (player.player === null) {
logger.warn("✖ couldn't find sound player"); logger.warn("✖ couldn't find sound player");
@@ -20,19 +22,25 @@ export function playSound() {
if (config.notifications.playSound && player.player !== null) { if (config.notifications.playSound && player.player !== null) {
logger.debug('↗ playing sound'); logger.debug('↗ playing sound');
fs.access(config.notifications.playSound, fs.constants.F_OK, (error) => { fs.access(
if (error) { config.notifications.playSound,
logger.error(`✖ error opening sound file: ${error.message}`); fs.constants.F_OK,
return; (error) => {
}
player.play(config.notifications.playSound, (error: Error) => {
if (error) { if (error) {
logger.error("✖ couldn't play sound", error); logger.error(
`✖ error opening sound file: ${error.message}`
);
return;
} }
logger.info('✔ played sound'); player.play(config.notifications.playSound, (error: Error) => {
}); if (error) {
}); logger.error("✖ couldn't play sound", error);
}
logger.info('✔ played sound');
});
}
);
} }
} }
+6 -2
View File
@@ -39,7 +39,9 @@ const chatClient: ChatClient = new ChatClient(
{ {
accessToken, accessToken,
expiryTimestamp: expiryTimestamp:
expiryDate === null ? null : expiryDate.getTime(), expiryDate === null
? null
: expiryDate.getTime(),
refreshToken refreshToken
}, },
null, null,
@@ -90,7 +92,9 @@ export function sendTwitchMessage(link: Link, store: Store) {
logger.debug('↗ sending twitch message'); logger.debug('↗ sending twitch message');
messages.push( messages.push(
`${Print.inStock(link, store)}\n${link.cartUrl ? link.cartUrl : link.url}` `${Print.inStock(link, store)}\n${
link.cartUrl ? link.cartUrl : link.url
}`
); );
if (!alreadySaying) { if (!alreadySaying) {
+19 -5
View File
@@ -7,7 +7,9 @@ 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.debug(Print.message('NO STORE LINKS FOUND', series, store, true)); logger.debug(
Print.message('NO STORE LINKS FOUND', series, store, true)
);
return; return;
} }
@@ -20,7 +22,12 @@ function addNewLinks(store: Store, links: Link[], series: Series) {
} }
logger.debug( logger.debug(
Print.message(`FOUND ${newLinks.length} STORE LINKS`, series, store, true) Print.message(
`FOUND ${newLinks.length} STORE LINKS`,
series,
store,
true
)
); );
logger.debug(JSON.stringify(newLinks, null, 2)); logger.debug(JSON.stringify(newLinks, null, 2));
@@ -39,7 +46,9 @@ export async function fetchLinks(store: Store, browser: Browser) {
continue; continue;
} }
logger.debug(Print.message('DETECTING STORE LINKS', series, store, true)); logger.debug(
Print.message('DETECTING STORE LINKS', series, store, true)
);
if (!Array.isArray(url)) { if (!Array.isArray(url)) {
url = [url]; url = [url];
@@ -51,12 +60,17 @@ export async function fetchLinks(store: Store, browser: Browser) {
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;
} }
const docElement = cheerio.load(text).root(); const docElement = cheerio.load(text).root();
const links = store.linksBuilder!.builder(docElement, series); const links = store.linksBuilder!.builder(
docElement,
series
);
addNewLinks(store, links, series); addNewLinks(store, links, series);
}) })
+4 -1
View File
@@ -29,7 +29,10 @@ function filterModel(model: Link['model'], series: Link['series']): boolean {
const sanitizedSeries = series.replace(/\s/g, ''); const sanitizedSeries = series.replace(/\s/g, '');
for (const configModelEntry of config.store.showOnlyModels) { for (const configModelEntry of config.store.showOnlyModels) {
const sanitizedConfigModel = configModelEntry.name.replace(/\s/g, ''); const sanitizedConfigModel = configModelEntry.name.replace(/\s/g, '');
const sanitizedConfigSeries = configModelEntry.series.replace(/\s/g, ''); const sanitizedConfigSeries = configModelEntry.series.replace(
/\s/g,
''
);
if (sanitizedConfigSeries) { if (sanitizedConfigSeries) {
if ( if (
sanitizedSeries === sanitizedConfigSeries && sanitizedSeries === sanitizedConfigSeries &&
+49 -18
View File
@@ -1,4 +1,11 @@
import {Browser, Page, PageEventObj, Request, Response} from 'puppeteer'; import {
Browser,
Page,
PageEventObj,
Request,
RespondOptions,
Response
} from 'puppeteer';
import {Link, Store, getStores} from './model'; import {Link, Store, getStores} from './model';
import {Print, logger} from '../logger'; import {Print, logger} from '../logger';
import {Selector, getPrice, pageIncludesLabels} from './includes-labels'; import {Selector, getPrice, pageIncludesLabels} from './includes-labels';
@@ -95,6 +102,14 @@ async function handleAdBlock(request: Request, adBlockRequestHandler: any) {
resolve(true); resolve(true);
}; };
const respondFunc = async (response: RespondOptions) => {
try {
await request.respond(response);
} catch {}
resolve(true);
};
const requestProxy = new Proxy(request, { const requestProxy = new Proxy(request, {
get(target, prop, receiver) { get(target, prop, receiver) {
if (prop === 'continue') { if (prop === 'continue') {
@@ -105,9 +120,14 @@ async function handleAdBlock(request: Request, adBlockRequestHandler: any) {
return abortFunc; return abortFunc;
} }
if (prop === 'respond') {
return respondFunc;
}
return Reflect.get(target, prop, receiver); return Reflect.get(target, prop, receiver);
} }
}); });
adBlockRequestHandler(requestProxy); adBlockRequestHandler(requestProxy);
}); });
} }
@@ -126,10 +146,10 @@ async function lookup(browser: Browser, store: Store) {
} }
if (store.linksBuilder) { if (store.linksBuilder) {
logger.info(`[${store.name}] Running linksBuilder...`);
const lastRunTime = linkBuilderLastRunTimes[store.name] ?? -1; const lastRunTime = linkBuilderLastRunTimes[store.name] ?? -1;
const ttl = store.linksBuilder.ttl ?? Number.MAX_SAFE_INTEGER; const ttl = store.linksBuilder.ttl ?? Number.MAX_SAFE_INTEGER;
if (lastRunTime === -1 || Date.now() - lastRunTime > ttl) { if (lastRunTime === -1 || Date.now() - lastRunTime > ttl) {
logger.info(`[${store.name}] Running linksBuilder...`);
try { try {
await fetchLinks(store, browser); await fetchLinks(store, browser);
linkBuilderLastRunTimes[store.name] = Date.now(); linkBuilderLastRunTimes[store.name] = Date.now();
@@ -152,16 +172,18 @@ async function lookup(browser: Browser, store: Store) {
const proxy = nextProxy(store); const proxy = nextProxy(store);
const useAdBlock = !config.browser.lowBandwidth && !store.disableAdBlocker; const useAdBlock =
!config.browser.lowBandwidth && !store.disableAdBlocker;
const customContext = config.browser.isIncognito; const customContext = config.browser.isIncognito;
const context = customContext const context = customContext
? await browser.createIncognitoBrowserContext() ? await browser.createIncognitoBrowserContext()
: browser.defaultBrowserContext(); : browser.defaultBrowserContext();
const page = await context.newPage(); const page = await context.newPage();
await page.setRequestInterception(true);
page.setDefaultNavigationTimeout(config.page.timeout); page.setDefaultNavigationTimeout(config.page.timeout);
await page.setUserAgent(getRandomUserAgent()); await page.setUserAgent(await getRandomUserAgent());
let adBlockRequestHandler: any; let adBlockRequestHandler: any;
let pageProxy; let pageProxy;
@@ -212,13 +234,12 @@ async function lookup(browser: Browser, store: Store) {
statusCode = await lookupCard(browser, store, page, link); statusCode = await lookupCard(browser, store, page, link);
} catch (error: unknown) { } catch (error: unknown) {
logger.error( logger.error(
`✖ [${store.name}] ${link.brand} ${link.series} ${link.model} - ${ `✖ [${store.name}] ${link.brand} ${link.series} ${
(error as Error).message link.model
}` } - ${(error as Error).message}`
); );
const client = await page.target().createCDPSession(); const client = await page.target().createCDPSession();
await client.send('Network.clearBrowserCookies'); await client.send('Network.clearBrowserCookies');
// Await client.send('Network.clearBrowserCache');
} }
if (pageProxy) { if (pageProxy) {
@@ -266,7 +287,9 @@ async function lookupCard(
if (await lookupCardInStock(store, page, link)) { if (await lookupCardInStock(store, page, link)) {
const givenUrl = const givenUrl =
link.cartUrl && config.store.autoAddToCart ? link.cartUrl : link.url; link.cartUrl && config.store.autoAddToCart
? 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) {
@@ -313,7 +336,11 @@ async function lookupCardInStock(store: Store, page: Page, link: Link) {
if (store.labels.bannedSeller) { if (store.labels.bannedSeller) {
if ( if (
await pageIncludesLabels(page, store.labels.bannedSeller, baseOptions) 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;
@@ -340,6 +367,15 @@ async function lookupCardInStock(store: Store, page: Page, link: Link) {
// return store.realTimeInventoryLookup(link.itemNumber); // return store.realTimeInventoryLookup(link.itemNumber);
// } // }
if (store.labels.outOfStock) {
if (
await pageIncludesLabels(page, store.labels.outOfStock, baseOptions)
) {
logger.info(Print.outOfStock(link, store, true));
return false;
}
}
if (store.labels.inStock) { if (store.labels.inStock) {
const options = { const options = {
...baseOptions, ...baseOptions,
@@ -366,19 +402,14 @@ async function lookupCardInStock(store: Store, page: Page, link: Link) {
} }
} }
if (store.labels.outOfStock) {
if (await pageIncludesLabels(page, store.labels.outOfStock, baseOptions)) {
logger.info(Print.outOfStock(link, store, true));
return false;
}
}
return true; return true;
} }
export async function tryLookupAndLoop(browser: Browser, store: Store) { export async function tryLookupAndLoop(browser: Browser, store: Store) {
if (!browser.isConnected()) { if (!browser.isConnected()) {
logger.debug(`[${store.name}] Ending this loop as browser is disposed...`); logger.debug(
`[${store.name}] Ending this loop as browser is disposed...`
);
return; return;
} }
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Adorama: Store = { export const Adorama: Store = {
currency: '$',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AlternateNL: Store = { export const AlternateNL: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.stockStatus', container: '.stockStatus',
+13
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Alternate: Store = { export const Alternate: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.stockStatus', container: '.stockStatus',
@@ -393,6 +394,18 @@ export const Alternate: Store = {
series: 'rx6800xt', series: 'rx6800xt',
url: 'https://www.alternate.de/product/1697044' url: 'https://www.alternate.de/product/1697044'
}, },
{
brand: 'asus',
model: 'tuf oc',
series: 'rx6900xt',
url: 'https://www.alternate.de/product/1702773'
},
{
brand: 'msi',
model: 'amd reference',
series: 'rx6900xt',
url: 'https://www.alternate.de/product/1700508'
},
{ {
brand: 'microsoft', brand: 'microsoft',
model: 'xbox series x', model: 'xbox series x',
+57
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AmazonCa: Store = { export const AmazonCa: Store = {
currency: '$',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
@@ -23,6 +24,46 @@ export const AmazonCa: Store = {
series: 'test:series', series: 'test:series',
url: 'https://www.amazon.ca/dp/B07PBLD2MX' url: 'https://www.amazon.ca/dp/B07PBLD2MX'
}, },
{
brand: 'msi',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08KWN2LZG&Quantity.1=1',
model: 'gaming x trio',
series: '3070',
url: 'https://www.amazon.ca/dp/B08KWN2LZG'
},
{
brand: 'msi',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08KWLMZV4&Quantity.1=1',
model: 'ventus 3x oc',
series: '3070',
url: 'https://www.amazon.ca/dp/B08KWLMZV4'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08LW46GH2&Quantity.1=1',
model: 'xc3',
series: '3070',
url: 'https://www.amazon.ca/dp/B08LW46GH2'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08L8L71SM&Quantity.1=1',
model: 'xc3 ultra',
series: '3070',
url: 'https://www.amazon.ca/dp/B08L8L71SM'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08L8L9TCZ&Quantity.1=1',
model: 'ftw3 ultra',
series: '3070',
url: 'https://www.amazon.ca/dp/B08L8L9TCZ'
},
{ {
brand: 'msi', brand: 'msi',
cartUrl: cartUrl:
@@ -214,6 +255,22 @@ export const AmazonCa: Store = {
model: 'ps5 digital', model: 'ps5 digital',
series: 'sonyps5de', series: 'sonyps5de',
url: 'https://www.amazon.ca/dp/B08GS1N24H' url: 'https://www.amazon.ca/dp/B08GS1N24H'
},
{
brand: 'microsoft',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08H75RTZ8&Quantity.1=1',
model: 'xbox series x',
series: 'xboxsx',
url: 'https://www.amazon.ca/dp/B08H75RTZ8'
},
{
brand: 'microsoft',
cartUrl:
'https://www.amazon.ca/gp/aws/cart/add.html?ASIN.1=B08G9J44ZN&Quantity.1=1',
model: 'xbox series s',
series: 'xboxss',
url: 'https://www.amazon.ca/dp/B08G9J44ZN'
} }
], ],
name: 'amazon-ca' name: 'amazon-ca'
+1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const AmazonDeWarehouse: Store = { export const AmazonDeWarehouse: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '€',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
+1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const AmazonDe: Store = { export const AmazonDe: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '€',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
+1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const AmazonEs: Store = { export const AmazonEs: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '€',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
+1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const AmazonFr: Store = { export const AmazonFr: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '€',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
+9 -5
View File
@@ -2,27 +2,31 @@ import {Store} from './store';
export const AmazonIt: Store = { export const AmazonIt: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '€',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
text: ['enter the characters you see below'] text: [
'Inserisci i caratteri visualizzati nello spazio sottostante'
]
}, },
inStock: { inStock: {
container: '#desktop_buybox', container: '#desktop_buybox',
text: ['Aggiungi al carrello'] text: ['Aggiungi al carrello']
}, },
maxPrice: { maxPrice: {
container: '#priceblock_ourprice' container: '#priceblock_ourprice',
euroFormat: true
} }
}, },
links: [ links: [
{ {
brand: 'asus', brand: 'asus',
cartUrl: cartUrl:
'https://www.amazon.it/gp/aws/cart/add.html?ASIN.1=B07PW9VBK5&Quantity.1=1', 'https://www.amazon.it/gp/aws/cart/add.html?ASIN.1=B08KHFZN9P&Quantity.1=1',
model: 'dual', model: 'dual',
series: '3070', series: '3070',
url: 'https://www.amazon.it/dp/B07PW9VBK5' url: 'https://www.amazon.it/dp/B08KHFZN9P'
}, },
{ {
brand: 'asus', brand: 'asus',
@@ -116,7 +120,7 @@ export const AmazonIt: Store = {
brand: 'evga', brand: 'evga',
cartUrl: cartUrl:
'https://www.amazon.it/gp/aws/cart/add.html?ASIN.1=B08L8L9TCZ&Quantity.1=1', 'https://www.amazon.it/gp/aws/cart/add.html?ASIN.1=B08L8L9TCZ&Quantity.1=1',
model: 'ftw3', model: 'ftw3 ultra',
series: '3070', series: '3070',
url: 'https://www.amazon.it/dp/B08L8L9TCZ' url: 'https://www.amazon.it/dp/B08L8L9TCZ'
}, },
+136 -12
View File
@@ -1,20 +1,22 @@
import {Store} from './store'; import {Store} from './store';
export const AmazonNl: Store = { export const AmazonNl: Store = {
currency: '€',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
text: ['voer de karakters in die u hieronder ziet'] text: ['voer de karakters in die u hieronder ziet']
}, },
inStock: { inStock: [
container: '#availability', {
text: [ container: '#add-to-cart-button',
'op voorraad', text: ['in winkelwagen']
'verkrijgbaar vanaf', },
'wordt gewoonlijk verzonden binnen', {
'nog slechts' container: '#buy-now-button',
] text: ['nu kopen']
}, }
],
maxPrice: { maxPrice: {
container: '#priceblock_ourprice', container: '#priceblock_ourprice',
euroFormat: true euroFormat: true
@@ -26,7 +28,9 @@ export const AmazonNl: Store = {
}, },
{ {
container: '#outOfStock', container: '#outOfStock',
text: ['we weten niet of en wanneer dit item weer op voorraad is'] text: [
'we weten niet of en wanneer dit item weer op voorraad is'
]
} }
] ]
}, },
@@ -34,10 +38,10 @@ export const AmazonNl: Store = {
{ {
brand: 'test:brand', brand: 'test:brand',
cartUrl: cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B083GGYNQ6&Quantity.1=1', 'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B085K45C3S&Quantity.1=1',
model: 'test:model', model: 'test:model',
series: 'test:series', series: 'test:series',
url: 'https://www.amazon.nl/dp/B083GGYNQ6' url: 'https://www.amazon.nl/dp/B085K45C3S'
}, },
{ {
brand: 'msi', brand: 'msi',
@@ -79,6 +83,126 @@ export const AmazonNl: Store = {
series: '3080', series: '3080',
url: 'https://www.amazon.nl/dp/B08HN7VVLJ' url: 'https://www.amazon.nl/dp/B08HN7VVLJ'
}, },
{
brand: 'msi',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08P2DQ28S&Quantity.1=1',
model: 'ventus 2x oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08P2DQ28S'
},
{
brand: 'msi',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08NW2YJB2&Quantity.1=1',
model: 'ventus 2x oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08NW2YJB2'
},
{
brand: 'zotac',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08P3XJLJJ&Quantity.1=1',
model: 'twin edge',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08P3XJLJJ'
},
{
brand: 'zotac',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08P3V572B&Quantity.1=1',
model: 'twin edge oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08P3V572B'
},
{
brand: 'zotac',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08P3BJ9Y8&Quantity.1=1',
model: 'twin edge oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08P3BJ9Y8'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08P3JPX8P&Quantity.1=1',
model: 'eagle',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08P3JPX8P'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08NW2X37Z&Quantity.1=1',
model: 'gaming oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08NW2X37Z'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08NW528S1&Quantity.1=1',
model: 'eagle oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08NW528S1'
},
{
brand: 'pny',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08NW693LG&Quantity.1=1',
model: 'xlr8 revel',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08NW693LG'
},
{
brand: 'asus',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B083Z5P6TX&Quantity.1=1',
model: 'tuf oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B083Z5P6TX'
},
{
brand: 'asus',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08NZ4G4T2&Quantity.1=1',
model: 'tuf oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08NZ4G4T2'
},
{
brand: 'asus',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08P2HBBLX&Quantity.1=1',
model: 'dual oc',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08P2HBBLX'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08PDN36R3&Quantity.1=1',
model: 'ftw3',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08PDN36R3'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08PDP837W&Quantity.1=1',
model: 'xc gaming',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08PDP837W'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.nl/gp/aws/cart/add.html?ASIN.1=B08PDMVPZ4&Quantity.1=1',
model: 'xc3 black',
series: '3060ti',
url: 'https://www.amazon.nl/dp/B08PDMVPZ4'
},
{ {
brand: 'amd', brand: 'amd',
cartUrl: cartUrl:
+11 -5
View File
@@ -4,6 +4,7 @@ import {parseCard} from './helpers/card';
export const AmazonUk: Store = { export const AmazonUk: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '£',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
@@ -112,6 +113,16 @@ export const AmazonUk: Store = {
}, },
ttl: 300000, ttl: 300000,
urls: [ urls: [
{
series: '3060ti',
url:
'https://www.amazon.co.uk/s?k=%2B%22RTX+3060+TI%22+-2060+-2070+-2080+-SUPER+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675291sb_noss'
},
{
series: '3070',
url:
'https://www.amazon.co.uk/s?k=%2B%22RTX+3070%22+-2060+-2070+-2080+-SUPER+-GTX+-3080&rh=n%3A430500031&ref=nb_sb_noss'
},
{ {
series: '3080', series: '3080',
url: [ url: [
@@ -125,11 +136,6 @@ export const AmazonUk: Store = {
'https://www.amazon.co.uk/s?k=%2B%22RTX+3090%22+-3080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675291', 'https://www.amazon.co.uk/s?k=%2B%22RTX+3090%22+-3080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675291',
'https://www.amazon.co.uk/s?k=%2B%22RTX+3090%22+-3080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675594&page=2' 'https://www.amazon.co.uk/s?k=%2B%22RTX+3090%22+-3080+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675594&page=2'
] ]
},
{
series: '3060ti',
url:
'https://www.amazon.co.uk/s?k=%2B%22RTX+3060+TI%22+-2060+-2070+-2080+-SUPER+-GTX&i=computers&rh=n%3A430500031%2Cp_n_availability%3A419162031&s=relevancerank&dc&qid=1601675291sb_noss'
} }
] ]
}, },
+106 -1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const Amazon: Store = { export const Amazon: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '$',
labels: { labels: {
captcha: { captcha: {
container: 'body', container: 'body',
@@ -30,6 +31,110 @@ export const Amazon: Store = {
series: 'test:series', series: 'test:series',
url: 'https://www.amazon.com/dp/B083248S3B' url: 'https://www.amazon.com/dp/B083248S3B'
}, },
{
brand: 'asus',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P2HBBLX&Quantity.1=1',
model: 'dual',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P2HBBLX'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08NYP7KG6&Quantity.1=1',
model: 'gaming oc',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08NYP7KG6'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08NYPLXPJ&Quantity.1=1',
model: 'gaming oc pro',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08NYPLXPJ'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08NYNJ6RC&Quantity.1=1',
model: 'eagle',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08NYNJ6RC'
},
{
brand: 'asus',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B083Z5P6TX&Quantity.1=1',
model: 'tuf',
series: '3060ti',
url: 'https://www.amazon.com/dp/B083Z5P6TX'
},
{
brand: 'msi',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P2D3JSG&Quantity.1=1',
model: 'gaming x trio',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P2D3JSG'
},
{
brand: 'msi',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P2DQ28S&Quantity.1=1',
model: 'ventus 2x',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P2DQ28S'
},
{
brand: 'zotac',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P3XJLJJ&Quantity.1=1',
model: 'twin edge oc',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P3XJLJJ'
},
{
brand: 'zotac',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P3V572B&Quantity.1=1',
model: 'twin edge',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P3V572B'
},
{
brand: 'asus',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P2D1JZZ&Quantity.1=1',
model: 'ko',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P2D1JZZ'
},
{
brand: 'asus',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B083Z7TR8Z&Quantity.1=1',
model: 'strix',
series: '3060ti',
url: 'https://www.amazon.com/dp/B083Z7TR8Z'
},
{
brand: 'evga',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08P2H5LW2&Quantity.1=1',
model: 'ftw3',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08P2H5LW2'
},
{
brand: 'gigabyte',
cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08NYPKW1Z&Quantity.1=1',
model: 'eagle oc',
series: '3060ti',
url: 'https://www.amazon.com/dp/B08NYPKW1Z'
},
{ {
brand: 'asus', brand: 'asus',
cartUrl: cartUrl:
@@ -146,7 +251,7 @@ export const Amazon: Store = {
brand: 'evga', brand: 'evga',
cartUrl: cartUrl:
'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08L8L9TCZ&Quantity.1=1', 'https://www.amazon.com/gp/aws/cart/add.html?ASIN.1=B08L8L9TCZ&Quantity.1=1',
model: 'ftw3', model: 'ftw3 ultra',
series: '3070', series: '3070',
url: 'https://www.amazon.com/dp/B08L8L9TCZ' url: 'https://www.amazon.com/dp/B08L8L9TCZ'
}, },
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AmdCa: Store = { export const AmdCa: Store = {
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: '.btn-shopping-cart', container: '.btn-shopping-cart',
+21
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AmdDe: Store = { export const AmdDe: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.btn-shopping-cart', container: '.btn-shopping-cart',
@@ -9,6 +10,10 @@ export const AmdDe: Store = {
maxPrice: { maxPrice: {
container: '.product-page-description h4', container: '.product-page-description h4',
euroFormat: true euroFormat: true
},
outOfStock: {
container: '.btn-radeon',
text: ['ausverkauft']
} }
}, },
links: [ links: [
@@ -50,6 +55,14 @@ export const AmdDe: Store = {
series: 'ryzen5600', series: 'ryzen5600',
url: 'https://www.amd.com/de/direct-buy/5450881700/de' url: 'https://www.amd.com/de/direct-buy/5450881700/de'
}, },
{
brand: 'amd',
cartUrl:
'https://www.amd.com/de/direct-buy/5458374000/de?add-to-cart=true',
model: 'amd reference',
series: 'rx6800',
url: 'https://www.amd.com/de/direct-buy/5458374000/de'
},
{ {
brand: 'amd', brand: 'amd',
cartUrl: cartUrl:
@@ -57,6 +70,14 @@ export const AmdDe: Store = {
model: 'amd reference', model: 'amd reference',
series: 'rx6800xt', series: 'rx6800xt',
url: 'https://www.amd.com/de/direct-buy/5458374100/de' url: 'https://www.amd.com/de/direct-buy/5458374100/de'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/de/direct-buy/5458374200/de?add-to-cart=true',
model: 'amd reference',
series: 'rx6900xt',
url: 'https://www.amd.com/de/direct-buy/5458374200/de'
} }
], ],
name: 'amd-de' name: 'amd-de'
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AmdIt: Store = { export const AmdIt: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.btn-shopping-cart', container: '.btn-shopping-cart',
+96
View File
@@ -0,0 +1,96 @@
import {Store} from './store';
export const AmdUk: Store = {
currency: '£',
labels: {
inStock: [
{
container: '.btn-shopping-cart',
text: ['add to cart']
},
{
container: '.btn-radeon',
text: ['add to cart']
}
],
maxPrice: {
container: '.product-page-description h4',
euroFormat: false
},
outOfStock: [
{
container: '.product-out-of-stock',
text: ['out of stock']
},
{
container: '.btn-radeon',
text: ['sold out']
}
]
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.amd.com/en/direct-buy/5450881400/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5450881400/gb?add-to-cart=true',
model: '5950x',
series: 'ryzen5950',
url: 'https://www.amd.com/en/direct-buy/5450881400/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5450881500/gb?add-to-cart=true',
model: '5900x',
series: 'ryzen5900',
url: 'https://www.amd.com/en/direct-buy/5450881500/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5450881600/gb?add-to-cart=true',
model: '5800x',
series: 'ryzen5800',
url: 'https://www.amd.com/en/direct-buy/5450881600/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5450881700/gb?add-to-cart=true',
model: '5600x',
series: 'ryzen5600',
url: 'https://www.amd.com/en/direct-buy/5450881700/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5458374000/gb?add-to-cart=true',
model: 'amd reference',
series: 'rx6800',
url: 'https://www.amd.com/en/direct-buy/5458374000/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5458374100/gb?add-to-cart=true',
model: 'amd reference',
series: 'rx6800xt',
url: 'https://www.amd.com/en/direct-buy/5458374100/gb'
},
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5458374200/gb?add-to-cart=true',
model: 'amd reference',
series: 'rx6900xt',
url: 'https://www.amd.com/en/direct-buy/5458374200/gb'
}
],
name: 'amd-uk'
};
+9
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Amd: Store = { export const Amd: Store = {
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: '.btn-shopping-cart', container: '.btn-shopping-cart',
@@ -50,6 +51,14 @@ export const Amd: Store = {
series: 'ryzen5600', series: 'ryzen5600',
url: 'https://www.amd.com/en/direct-buy/5450881700/us' url: 'https://www.amd.com/en/direct-buy/5450881700/us'
}, },
{
brand: 'amd',
cartUrl:
'https://www.amd.com/en/direct-buy/5458372200/us?add-to-cart=true',
model: 'amd reference',
series: 'rx6900xt',
url: 'https://www.amd.com/en/direct-buy/5458372200/us'
},
{ {
brand: 'amd', brand: 'amd',
cartUrl: cartUrl:
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AntOnline: Store = { export const AntOnline: Store = {
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: '.uk-button', container: '.uk-button',
+35
View File
@@ -0,0 +1,35 @@
import {Store} from './store';
export const ArgosIE: Store = {
currency: '€',
labels: {
inStock: {
container: '.btnbuyreserve',
text: ['buy or reserve']
},
maxPrice: {
container: '.price'
},
outOfStock: {
container: '#subCopy',
text: ["We're working hard to get more stock."]
}
},
links: [
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url:
'http://www.argos.ie/static/Product/partNumber/8448262/Trail/searchtext%3EXBOX+SERIES+X.htm'
},
{
brand: 'sony',
model: 'ps5 digital',
series: 'sonyps5de',
url:
'http://www.argos.ie/static/Product/partNumber/8349000/Trail/searchtext%3EPS5+CONSOLE.htm'
}
],
name: 'argos-ie'
};
+16 -3
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Argos: Store = { export const Argos: Store = {
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: 'button[data-test="add-to-trolley-button-button"', container: 'button[data-test="add-to-trolley-button-button"',
@@ -18,16 +19,28 @@ export const Argos: Store = {
url: 'https://www.argos.co.uk/product/5718469' url: 'https://www.argos.co.uk/product/5718469'
}, },
{ {
brand: 'asus', brand: 'sony',
model: 'ps5 console', model: 'ps5 console',
series: 'sonyps5c', series: 'sonyps5c',
url: 'https://www.argos.co.uk/product/6795199' url: 'https://www.argos.co.uk/product/8349000'
}, },
{ {
brand: 'sony', brand: 'sony',
model: 'ps5 digital', model: 'ps5 digital',
series: 'sonyps5de', series: 'sonyps5de',
url: 'https://www.argos.co.uk/product/6795151' url: 'https://www.argos.co.uk/product/8349024'
},
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url: 'https://www.argos.co.uk/product/8448262'
},
{
brand: 'microsoft',
model: 'xbox series s',
series: 'xboxss',
url: 'https://www.argos.co.uk/product/8448248'
} }
], ],
name: 'argos' name: 'argos'
+6 -5
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card'; import {getProductLinksBuilder} from './helpers/card';
export const Aria: Store = { export const Aria: Store = {
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: '#addQuantity', container: '#addQuantity',
@@ -44,6 +45,11 @@ export const Aria: Store = {
url: url:
'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3060+Ti' 'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3060+Ti'
}, },
{
series: '3070',
url:
'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3070'
},
{ {
series: '3080', series: '3080',
url: url:
@@ -53,11 +59,6 @@ export const Aria: Store = {
series: '3090', series: '3090',
url: url:
'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3090' 'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3090'
},
{
series: '3060ti',
url:
'https://www.aria.co.uk/Products/Components/Graphics+Cards/NVIDIA+GeForce/GeForce+RTX+3060+Ti'
} }
] ]
}, },
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Arlt: Store = { export const Arlt: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.articleDesc .shippingtext', container: '.articleDesc .shippingtext',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const AsusDe: Store = { export const AsusDe: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.buybox--button', container: '.buybox--button',
+12 -2
View File
@@ -3,6 +3,7 @@ import fetch from 'node-fetch';
export const Asus: Store = { export const Asus: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: '#item_add_cart', container: '#item_add_cart',
@@ -51,17 +52,26 @@ export const Asus: Store = {
model: 'strix oc', model: 'strix oc',
series: '3080', series: '3080',
url: 'https://store.asus.com/us/item/202009AM290000002' url: 'https://store.asus.com/us/item/202009AM290000002'
},
{
brand: 'asus',
itemNumber: '202011AM200000003',
model: 'crosshair viii',
series: 'darkhero',
url: 'https://store.asus.com/us/item/202011AM200000003'
} }
], ],
name: 'asus', name: 'asus',
realTimeInventoryLookup: async (itemNumber: string) => { realTimeInventoryLookup: async (itemNumber: string) => {
const request_url = 'https://store.asus.com/us/category/get_real_time_data'; const request_url =
'https://store.asus.com/us/category/get_real_time_data';
const response = await fetch(request_url, { const response = await fetch(request_url, {
body: 'sm_seq_list%5B%5D=' + itemNumber, body: 'sm_seq_list%5B%5D=' + itemNumber,
headers: { headers: {
'accept-language': 'en-US,en;q=0.9', 'accept-language': 'en-US,en;q=0.9',
'cache-control': 'no-cache', 'cache-control': 'no-cache',
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8' 'content-type':
'application/x-www-form-urlencoded; charset=UTF-8'
}, },
method: 'POST' method: 'POST'
}); });
+15 -38
View File
@@ -2,18 +2,19 @@ import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card'; import {getProductLinksBuilder} from './helpers/card';
export const Awd: Store = { export const Awd: Store = {
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: '.vs-stock .ty-qty-in-stock', container: '.vs-stock .ty-qty-in-stock',
text: ['item(s)'] text: ['item(s)']
}, },
maxPrice: { maxPrice: {
container: '.product-info .ty-price-num', container: '.ty-price',
euroFormat: false // Note: Awd uses non-euroFromat as price seperator euroFormat: false // Note: Awd uses non-euroFromat as price seperator
}, },
outOfStock: { outOfStock: {
container: '.vs-stock', container: '.vs-stock.ty-float-left',
text: ['out of stock'] text: ['Out-of-stock']
} }
}, },
links: [ links: [
@@ -24,34 +25,6 @@ export const Awd: Store = {
url: url:
'https://www.awd-it.co.uk/asus-nvidia-geforce-gt-710-silent-low-profile-2gb-gddr5-graphics-card-pci-e.html' 'https://www.awd-it.co.uk/asus-nvidia-geforce-gt-710-silent-low-profile-2gb-gddr5-graphics-card-pci-e.html'
}, },
{
brand: 'asus',
model: 'amd reference',
series: 'rx6800',
url:
'https://www.awd-it.co.uk/asus-tuf-gaming-radeon-rx-6800-oc-edition-16gb-gddr6-graphics-card.html'
},
{
brand: 'asus',
model: 'amd reference',
series: 'rx6800',
url:
'https://www.awd-it.co.uk/asus-rog-strix-radeon-rx-6800-oc-edition-16gb-gddr6-graphics-card.html'
},
{
brand: 'gigabyte',
model: 'amd reference',
series: 'rx6800',
url:
'https://www.awd-it.co.uk/gigabyte-radeon-rx-6800-gaming-oc-16gb-gddr6-graphics-card.html'
},
{
brand: 'gigabyte',
model: 'amd reference',
series: 'rx6800xt',
url:
'https://www.awd-it.co.uk/gigabyte-radeon-rx-6800-xt-gaming-oc-16gb-gddr6-graphics-card.html'
},
{ {
brand: 'amd', brand: 'amd',
model: '5600x', model: '5600x',
@@ -85,10 +58,19 @@ export const Awd: Store = {
builder: getProductLinksBuilder({ builder: getProductLinksBuilder({
productsSelector: '.grid-list .ty-grid-list__item', productsSelector: '.grid-list .ty-grid-list__item',
sitePrefix: 'https://www.awd-it.co.uk', sitePrefix: 'https://www.awd-it.co.uk',
titleSelector: '.title-price-wrapper', titleSelector: '.product-title'
urlSelector: 'a[href]'
}), }),
urls: [ urls: [
{
series: 'rx6800',
url:
'https://www.awd-it.co.uk/components/graphics-cards/ati/amd-radeon-6800-6800xt.html'
},
{
series: '3060ti',
url:
'https://www.awd-it.co.uk/components/graphics-cards/nvidia/nvidia-geforce-rtx-3060ti.html'
},
{ {
series: '3070', series: '3070',
url: url:
@@ -103,11 +85,6 @@ export const Awd: Store = {
series: '3090', series: '3090',
url: url:
'https://www.awd-it.co.uk/components/graphics-cards/nvidia/nvidia-geforce-rtx-3090.html' 'https://www.awd-it.co.uk/components/graphics-cards/nvidia/nvidia-geforce-rtx-3090.html'
},
{
series: '3060ti',
url:
'https://www.awd-it.co.uk/components/graphics-cards/nvidia/nvidia-geforce-rtx-3060ti.html'
} }
] ]
}, },
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Azerty: Store = { export const Azerty: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.orderdelay', container: '.orderdelay',
+1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const BAndH: Store = { export const BAndH: Store = {
backoffStatusCodes: [403, 429], backoffStatusCodes: [403, 429],
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: 'div[data-selenium="addToCartSection"]', container: 'div[data-selenium="addToCartSection"]',
+19 -9
View File
@@ -1,19 +1,15 @@
import {Store} from './store'; import {Store} from './store';
export const BestBuyCa: Store = { export const BestBuyCa: Store = {
currency: '$',
labels: { labels: {
inStock: {
container: '.x-checkout-experience-new',
text: ['add to cart']
},
maxPrice: { maxPrice: {
container: container: 'div[class*="pricingContainer"]',
'div[class^="productPricingContainer"] span[class^="screenReaderOnly_"',
euroFormat: false euroFormat: false
}, },
outOfStock: { outOfStock: {
container: 'button.addToCartButton.disabled_XY3i_', container: '.addToCartButton:disabled',
text: ['Add to Cart'] text: ['add to cart']
} }
}, },
links: [ links: [
@@ -22,7 +18,7 @@ export const BestBuyCa: Store = {
model: 'test:model', model: 'test:model',
series: 'test:series', series: 'test:series',
url: url:
'https://www.bestbuy.ca/en-ca/product/evga-geforce-gtx-1660-xc-ultra-6gb-gddr5-video-card/14119081?intl=nosplash' 'https://www.bestbuy.ca/en-ca/product/zotac-nvidia-geforce-gtx-1660-super-twin-fan-6gb-gddr6-video-card/14415897'
}, },
{ {
brand: 'msi', brand: 'msi',
@@ -163,6 +159,20 @@ export const BestBuyCa: Store = {
series: 'sonyps5de', series: 'sonyps5de',
url: url:
'https://www.bestbuy.ca/en-ca/product/playstation-5-digital-edition-console-online-only/14962184?intl=nosplash' 'https://www.bestbuy.ca/en-ca/product/playstation-5-digital-edition-console-online-only/14962184?intl=nosplash'
},
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url:
'https://www.bestbuy.ca/en-ca/product/xbox-series-x-1tb-console-new-model-online-only/14964951'
},
{
brand: 'microsoft',
model: 'xbox series s',
series: 'xboxss',
url:
'https://www.bestbuy.ca/en-ca/product/xbox-series-s-512gb-console-new-model-online-only/14964950'
} }
], ],
name: 'bestbuy-ca', name: 'bestbuy-ca',
+11 -3
View File
@@ -1,15 +1,16 @@
import {Store} from './store'; import {Store} from './store';
export const BestBuy: Store = { export const BestBuy: Store = {
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: '[data-sticky-media-gallery] .fulfillment-add-to-cart-button', container:
'[data-sticky-media-gallery] .fulfillment-add-to-cart-button',
text: ['add to cart'] text: ['add to cart']
}, },
maxPrice: { maxPrice: {
container: container:
'[data-sticky-media-gallery] .priceView-price .priceView-hero-price span', '[data-sticky-media-gallery] .priceView-price .priceView-hero-price span'
euroFormat: false
} }
}, },
links: [ links: [
@@ -445,6 +446,13 @@ export const BestBuy: Store = {
url: url:
'https://www.bestbuy.com/site/xfx-merc-319-amd-radeon-rx-6800xt-16gb-gddr6-pci-express-4-0-gaming-graphics-card-black/6442585.p?skuId=6442585' 'https://www.bestbuy.com/site/xfx-merc-319-amd-radeon-rx-6800xt-16gb-gddr6-pci-express-4-0-gaming-graphics-card-black/6442585.p?skuId=6442585'
}, },
{
brand: 'xfx',
model: 'amd reference',
series: 'rx6900xt',
url:
'https://www.bestbuy.com/site/xfx-amd-radeon-rx-6900-xt-16gb-gddr6-pci-express-4-0-gaming-graphics-card-black/6444358.p?skuId=6444358'
},
{ {
brand: 'corsair', brand: 'corsair',
cartUrl: 'https://api.bestbuy.com/click/-/6351845/cart', cartUrl: 'https://api.bestbuy.com/click/-/6351845/cart',
+39 -5
View File
@@ -2,16 +2,19 @@ import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card'; import {getProductLinksBuilder} from './helpers/card';
export const Box: Store = { export const Box: Store = {
backoffStatusCodes: [403, 429, 503],
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: '#divBuyButton', container: '#divBuyButton',
text: ['add to basket'] text: ['add to basket']
}, },
maxPrice: { maxPrice: {
container: '.p-right-wrapper .pq-price', container: '.p-price',
euroFormat: false // Note: Box uses non-euroFromat as price seperator euroFormat: false // Note: Box uses non-euroFromat as price seperator
}, },
outOfStock: { outOfStock: {
container: '#divBuyButton',
text: ['request stock alert', 'coming soon'] text: ['request stock alert', 'coming soon']
} }
}, },
@@ -22,6 +25,41 @@ export const Box: Store = {
series: 'test:series', series: 'test:series',
url: url:
'https://www.box.co.uk/Gigabyte-GeForce-RTX-2080-Super-8GB-Wind_2724554.html' 'https://www.box.co.uk/Gigabyte-GeForce-RTX-2080-Super-8GB-Wind_2724554.html'
},
{
brand: 'sony',
model: 'ps5 console',
series: 'sonyps5c',
url:
'https://www.box.co.uk/CFI-1015A-Sony-Playstation-5-Console_3199689.html'
},
{
brand: 'sony',
model: 'ps5 digital',
series: 'sonyps5de',
url:
'https://www.box.co.uk/CFI-1015B-Sony-PlayStation-5-Digital-Edition-Conso_3199692.html'
},
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url:
'https://www.box.co.uk/RRT-00007-Xbox-Series-X-Console_3201195.html'
},
{
brand: 'microsoft',
model: 'xbox series s',
series: 'xboxss',
url:
'https://www.box.co.uk/RRS-00007-Xbox-Series-S-Console_3201197.html'
},
{
brand: 'amd',
model: 'tuf oc',
series: 'rx6900xt',
url:
'https://www.box.co.uk/90YV0GE0-M0NM00-ASUS-Radeon-RX-X6900XT-16GB-OC-Gaming-Gr_3561243.html'
} }
], ],
linksBuilder: { linksBuilder: {
@@ -46,10 +84,6 @@ export const Box: Store = {
{ {
series: '3090', series: '3090',
url: 'https://www.box.co.uk/rtx-3090-graphics-cards' url: 'https://www.box.co.uk/rtx-3090-graphics-cards'
},
{
series: '3060ti',
url: 'https://www.box.co.uk/rtx-3060-ti-graphics-cards'
} }
] ]
}, },
+171
View File
@@ -0,0 +1,171 @@
import {Store} from './store';
export const Bpctech: Store = {
backoffStatusCodes: [403, 429],
currency: '$',
labels: {
inStock: {
container: '.productStockStatus',
text: ['in stock']
},
maxPrice: {
container: 'div.price-box.price-final_price > span > span',
euroFormat: false
}
},
links: [
{
brand: 'asus',
model: 'tuf',
series: '3080',
url:
'https://www.bpctech.com.au/tuf-rtx3080-10g-gaming-asus-geforce-rtx-3080-tuf-gaming-10gb-video-card.html'
},
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url:
'https://www.bpctech.com.au/tuf-rtx3080-o10g-gaming-asus-geforce-rtx-3080-tuf-gaming-oc-10gb-video-card.html'
},
{
brand: 'asus',
model: 'strix oc',
series: '3080',
url:
'https://www.bpctech.com.au/rog-strix-rtx3080-o10g-gaming-asus-geforce-rtx-3080-rog-strix-oc-10gb-video-card.html'
},
// {
// brand: 'colorful',
// model: 'igame advanced oc',
// series: '3080',
// url: 'https://www.bpctech.com.au/colorful-advancedoc10g-v-colorful-igame-geforce-rtx-3080-advanced-oc-10g-v-video-card.html'
// },
// {
// brand: 'colorful',
// model: 'igame vulcan oc',
// series: '3080',
// url: 'https://www.bpctech.com.au/colorful-vulcanoc10g-v-colorful-igame-geforce-rtx-3080-vulcan-oc-10g-video-card.html'
// },
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url:
'https://www.bpctech.com.au/10g-p5-3897-kr-evga-geforce-rtx-3080-ftw3-ultra-gaming-10gb-video-card.html'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url:
'https://www.bpctech.com.au/10g-p5-3881-kr-evga-geforce-rtx-3080-xc3-black-gaming-10gb-video-card.html'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url:
'https://www.bpctech.com.au/10g-p5-3885-kr-evga-geforce-rtx-3080-xc3-ultra-gaming-10gb-video-card.html'
},
{
brand: 'galax',
model: 'sg oc',
series: '3080',
url:
'https://www.bpctech.com.au/38nwm3md99nn-galax-geforce-rtx-3080-sg-1-click-oc-10gb-video-card.html'
},
{
brand: 'gigabyte',
model: 'vision oc',
series: '3080',
url:
'https://www.bpctech.com.au/gv-n3080vision-oc-10gd-gigabyte-geforce-rtx-3080-vision-oc-10gb-video-card.html'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url:
'https://www.bpctech.com.au/gv-n3080gaming-oc-10gd-gigabyte-geforce-rtx-3080-gaming-oc-10gb-video-card.html'
},
{
brand: 'gigabyte',
model: 'aorus master',
series: '3080',
url:
'https://www.bpctech.com.au/gv-n3080aorus-m-10gd-gigabyte-geforce-rtx-3080-aorus-master-10g-video-card.html'
},
{
brand: 'gigabyte',
model: 'aorus xtreme',
series: '3080',
url:
'https://www.bpctech.com.au/gv-n3080aorus-x-10gd-gigabyte-aorus-geforce-rtx-3080-xtreme-10gb-video-card.html'
},
{
brand: 'gigabyte',
model: 'aorus xtreme waterforce',
series: '3080',
url:
'https://www.bpctech.com.au/gv-n3080aorusx-w-10gd-gigabyte-aorus-geforce-rtx-3080-xtreme-waterforce-10gb-video-card.html'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url:
'https://www.bpctech.com.au/gv-n3080eagle-oc-10gd-gigabyte-geforce-rtx-3080-eagle-oc-10gb-video-card.html'
},
{
brand: 'inno3d',
model: 'ichill x4',
series: '3080',
url:
'https://www.bpctech.com.au/c30804-106xx-1810va36-inno3d-geforce-rtx-3080-ichill-x4-10gb-video-card.html'
},
{
brand: 'inno3d',
model: 'ichill x3',
series: '3080',
url:
'https://www.bpctech.com.au/c30803-106xx-1810va37-inno3d-geforce-rtx-3080-ichill-x3-10gb-video-card.html'
},
{
brand: 'inno3d',
model: 'twin x2 oc',
series: '3080',
url:
'https://www.bpctech.com.au/n30802-106xx-1810va34-inno3d-geforce-rtx-3080-twin-x2-oc-10gb-video-card.html'
},
{
brand: 'leadtek',
model: 'hurricane',
series: '3080',
url:
'https://www.bpctech.com.au/rtx3080-hurricane-leadtek-geforce-rtx-3080-hurricane-10gb-video-card.html'
},
{
brand: 'msi',
model: 'suprim x',
series: '3080',
url:
'https://www.bpctech.com.au/rtx3080-suprim-x-10g-msi-geforce-rtx-3080-suprim-x-10gb-video-card.html'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url:
'https://www.bpctech.com.au/rtx3080-ventus-3x-10g-oc-msi-rtx-3080-ventus-3x-10g-oc-video-card.html'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url:
'https://www.bpctech.com.au/rtx3080-gaming-x-trio-10g-msi-rtx-3080-gaming-x-trio-10g-video-card.html'
}
],
name: 'bpctech'
};
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const CanadaComputers: Store = { export const CanadaComputers: Store = {
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: 'div[class="pi-prod-availability"]', container: 'div[class="pi-prod-availability"]',
+1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const Caseking: Store = { export const Caseking: Store = {
backoffStatusCodes: [403, 429], backoffStatusCodes: [403, 429],
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.delivery_container', container: '.delivery_container',
+8 -2
View File
@@ -3,6 +3,7 @@ import {getProductLinksBuilder} from './helpers/card';
export const Ccl: Store = { export const Ccl: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: '#pnlAddToBasket', container: '#pnlAddToBasket',
@@ -62,6 +63,11 @@ export const Ccl: Store = {
titleSelector: '.productList_Detail a[title]' titleSelector: '.productList_Detail a[title]'
}), }),
urls: [ urls: [
{
series: '3060ti',
url:
'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/GeForce-RTX-3060-Ti-Graphics-Cards/'
},
{ {
series: '3070', series: '3070',
url: url:
@@ -88,9 +94,9 @@ export const Ccl: Store = {
'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/AMD-Radeon-RX-6800-XT-Graphics-Cards/' 'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/AMD-Radeon-RX-6800-XT-Graphics-Cards/'
}, },
{ {
series: '3060ti', series: 'rx6900xt',
url: url:
'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/GeForce-RTX-3060-Ti-Graphics-Cards/' 'https://www.cclonline.com/category/430/PC-Components/Graphics-Cards/attributeslist/1268064/'
} }
] ]
}, },
+88
View File
@@ -0,0 +1,88 @@
import {Store} from './store';
export const Centrecom: Store = {
backoffStatusCodes: [403, 429],
currency: '$',
labels: {
inStock: {
container:
'.prod_stores_stock > li:nth-child(1) > span:nth-child(2)',
text: ['In Stock']
},
maxPrice: {
container: 'div.prod_price_current.product-price > span',
euroFormat: false
},
outOfStock: {
container:
'.prod_stores_stock > li:nth-child(1) > span:nth-child(2)',
text: ['Sold Out']
}
},
links: [
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url:
'https://www.centrecom.com.au/asus-geforce-rtx-3080-tuf-oc-10gb-gaming-graphics-card'
},
{
brand: 'galax',
model: 'sg oc',
series: '3080',
url:
'https://www.centrecom.com.au/galax-geforce-rtx-3080-sg-1-click-oc-graphics-card'
},
{
brand: 'gigabyte',
model: 'aorus master',
series: '3080',
url:
'https://www.centrecom.com.au/gigabyte-aorus-geforce-rtx-3080-master-graphics-card'
},
{
brand: 'gigabyte',
model: 'aorus xtreme',
series: '3080',
url:
'https://www.centrecom.com.au/gigabyte-geforce-rtx-3080-aorus-extreme-10gb-gddr6x-graphics-card'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url:
'https://www.centrecom.com.au/gigabyte-geforce-rtx-3080-eagle-oc-10g-graphics-card'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url:
'https://www.centrecom.com.au/gigabyte-geforce-rtx-3080-gaming-oc-10g-graphics-card'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url:
'https://www.centrecom.com.au/msi-geforce-rtx-3080-gaming-x-trio-10g-graphics-card'
},
{
brand: 'msi',
model: 'suprim x',
series: '3080',
url:
'https://www.centrecom.com.au/msi-geforce-rtx-3080-suprim-x-10g-graphics-card'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url:
'https://www.centrecom.com.au/msi-geforce-rtx-3080-ventus-3x-oc-10gb-graphics-card'
}
],
name: 'centrecom'
};
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Comet: Store = { export const Comet: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.caption', container: '.caption',
+5 -2
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Computeruniverse: Store = { export const Computeruniverse: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.availability', container: '.availability',
@@ -59,7 +60,8 @@ export const Computeruniverse: Store = {
brand: 'evga', brand: 'evga',
model: 'xc3 black', model: 'xc3 black',
series: '3070', series: '3070',
url: 'https://www.computeruniverse.net/de/evga-geforce-rtx3070-xc3-black' url:
'https://www.computeruniverse.net/de/evga-geforce-rtx3070-xc3-black'
}, },
{ {
brand: 'gainward', brand: 'gainward',
@@ -142,7 +144,8 @@ export const Computeruniverse: Store = {
brand: 'pny', brand: 'pny',
model: 'dual fan', model: 'dual fan',
series: '3070', series: '3070',
url: 'https://www.computeruniverse.net/de/pny-geforce-rtx3070-m-dual-8-gb' url:
'https://www.computeruniverse.net/de/pny-geforce-rtx3070-m-dual-8-gb'
}, },
{ {
brand: 'pny', brand: 'pny',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Coolblue: Store = { export const Coolblue: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.product-order', container: '.product-order',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Coolmod: Store = { export const Coolmod: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.product-availability', container: '.product-availability',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Corsair: Store = { export const Corsair: Store = {
currency: '$',
labels: { labels: {
inStock: { inStock: {
container: '.add_to_cart_form', container: '.add_to_cart_form',
+154
View File
@@ -0,0 +1,154 @@
import {Store} from './store';
export const Cpl: Store = {
backoffStatusCodes: [403, 429],
currency: '$',
labels: {
inStock: {
container:
'div.product-stock > table > tbody > tr:nth-child(1) > td.stock-value',
text: ['In Stock']
},
outOfStock: {
container:
'div.product-stock > table > tbody > tr:nth-child(1) > td.stock-value',
text: ['Pre Order']
}
},
links: [
{
brand: 'asus',
model: 'strix oc',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/asus-rog-strix-rtx3080-10g-gaming-graphics-card.html'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/asus-tuf-rtx3080-10g-gaming-graphics-card.html'
},
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/asus-tuf-rtx3080-o10g-gaming-graphics-card.html'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/evga-geforce-rtx-3080-ftw3-gaming-10g-p5-3895-kr-10gb-gddr6x-icx3-technology-argb-led-metal-backplate-hdmi-dpx3.html'
},
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/evga-geforce-rtx-3080-ftw3-ultra-gaming-10g-p5-3897-kr-10gb-gddr6x-icx3-technology-argb-led-metal-backplate-hdmi-dpx3.html'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/evga-geforce-rtx-3080-xc3-black-gaming-10g-p5-3881-kr-10gb-gddr6x-icx3-cooling-argb-led-hdmi-dpx3.html'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/evga-geforce-rtx-3080-xc3-gaming-10g-p5-3883-kr-10gb-gddr6x-icx3-cooling-argb-led-metal-backplate-hdmi-dpx3.html'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/evga-geforce-rtx-3080-xc3-ultra-gaming-10g-p5-3885-kr-10gb-gddr6x-icx3-cooling-argb-led-metal-backplate-hdmi-dpx3.html'
},
{
brand: 'gigabyte',
model: 'aorus master',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/gigabyte-gv-n3080aorus-m-10gd-aorus-geforce-rtx-3080-master-10gb.html'
},
{
brand: 'gigabyte',
model: 'aorus xtreme',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/gigabyte-aorus-geforce-rtx-3080-xtreme-10g.html'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/gigabyte-geforce-rtx-3080-eagle-oc-10gb-graphics-card-gv-n3080eagle-oc-10gd.html'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/gigabyte-gv-n3080gaming-oc-10gd-graphics-card.html'
},
{
brand: 'gigabyte',
model: 'vision oc',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/gigabyte-gv-n3080vision-oc-10gd-geforce-rtx-3080-vision-oc-10gb.html'
},
{
brand: 'inno3d',
model: 'ichill x3',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/inno3d-c30803-106xx-1810va37-geforce-rtx-3080-ichill-x3-10g.html'
},
{
brand: 'inno3d',
model: 'ichill x4',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/inno3d-c30804-106xx-1810va36-geforce-rtx-3080-ichill-x4-10g.html'
},
{
brand: 'leadtek',
model: 'hurricane',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/leadtek-12789000110-geforce-rtx-3080-hurricane.html'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/msi-rtx-3080-gaming-x-trio-10g-graphics-card.html'
},
{
brand: 'msi',
model: 'suprim x',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/msi-geforce-rtx-3080-suprim-x-10g-graphics-card.html'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url:
'https://cplonline.com.au/graphics-cards/geforce-rtx-3080/msi-rtx-3080-ventus-3x-10g-oc-graphics-card.html'
}
],
name: 'cpl'
};
+31 -6
View File
@@ -2,13 +2,15 @@ import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card'; import {getProductLinksBuilder} from './helpers/card';
export const Currys: Store = { export const Currys: Store = {
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: '#product-actions button', container: '#product-actions button',
text: ['add to basket'] text: ['add to basket']
}, },
maxPrice: { maxPrice: {
container: '#product-actions span[class*="ProductPriceBlock__Price"]', container:
'#product-actions span[class*="ProductPriceBlock__Price"]',
euroFormat: false // Note: Currys uses non-euroFromat as price seperator euroFormat: false // Note: Currys uses non-euroFromat as price seperator
}, },
outOfStock: { outOfStock: {
@@ -51,6 +53,34 @@ export const Currys: Store = {
series: 'ryzen5950', series: 'ryzen5950',
url: url:
'https://www.currys.co.uk/gbuk/computing-accessories/components-upgrades/processors/amd-ryzen-9-5950x-processor-10216688-pdt.html' 'https://www.currys.co.uk/gbuk/computing-accessories/components-upgrades/processors/amd-ryzen-9-5950x-processor-10216688-pdt.html'
},
{
brand: 'sony',
model: 'ps5 console',
series: 'sonyps5c',
url:
'https://www.currys.co.uk/gbuk/gaming/console-gaming/consoles/sony-playstation-5-825-gb-10203370-pdt.html'
},
{
brand: 'sony',
model: 'ps5 digital',
series: 'sonyps5de',
url:
'https://www.currys.co.uk/gbuk/playstation-5-sony-1714-commercial.html'
},
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url:
'https://www.currys.co.uk/gbuk/gaming/console-gaming/consoles/microsoft-xbox-series-x-1-tb-10203371-pdt.html'
},
{
brand: 'microsoft',
model: 'xbox series s',
series: 'xboxss',
url:
'https://www.currys.co.uk/gbuk/gaming/console-gaming/consoles/microsoft-xbox-series-s-512-gb-ssd-10205195-pdt.html'
} }
], ],
linksBuilder: { linksBuilder: {
@@ -80,11 +110,6 @@ export const Currys: Store = {
series: '3090', series: '3090',
url: url:
'https://www.currys.co.uk/gbuk/rtx-3090/components-upgrades/graphics-cards/324_3091_30343_xx_ba00013562-bv00313725/xx-criteria.html' 'https://www.currys.co.uk/gbuk/rtx-3090/components-upgrades/graphics-cards/324_3091_30343_xx_ba00013562-bv00313725/xx-criteria.html'
},
{
series: '3060ti',
url:
'https://www.currys.co.uk/gbuk/rtx-3060-ti/components-upgrades/graphics-cards/324_3091_30343_xx_ba00013562-bv00313952/xx-criteria.html'
} }
] ]
}, },
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Cyberport: Store = { export const Cyberport: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.tooltipAvailabilityParent', container: '.tooltipAvailabilityParent',
+50
View File
@@ -0,0 +1,50 @@
import {Store} from './store';
export const EbGames: Store = {
currency: '$',
labels: {
maxPrice: {
container: '.singleVariantText .prodPriceCont'
},
outOfStock: {
container: '#btnAddToCart[style*="display:none;"] ',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url:
'https://www.ebgames.ca/Switch/Games/727918/mario-kart-8-deluxe'
},
{
brand: 'sony',
model: 'ps5 console',
series: 'sonyps5c',
url: 'https://www.ebgames.ca/PS5/Games/877522'
},
{
brand: 'sony',
model: 'ps5 digital',
series: 'sonyps5de',
url: 'https://www.ebgames.ca/PS5/Games/877523'
},
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url:
'https://www.ebgames.ca/Xbox%20Series%20X/Games/877779/xbox-series-x'
},
{
brand: 'microsoft',
model: 'xbox series s',
series: 'xboxss',
url:
'https://www.ebgames.ca/Xbox%20Series%20X/Games/877780/xbox-series-s'
}
],
name: 'ebgames'
};
+25 -9
View File
@@ -2,10 +2,11 @@ import {Store} from './store';
import {getProductLinksBuilder} from './helpers/card'; import {getProductLinksBuilder} from './helpers/card';
export const Ebuyer: Store = { export const Ebuyer: Store = {
currency: '£',
labels: { labels: {
inStock: { inStock: {
container: '.purchase-info', container: '.purchase-info__cta',
text: ['add to basket', 'in stock'] text: ['add to basket', 'pre-order']
}, },
maxPrice: { maxPrice: {
container: '.purchase-info__price .price', container: '.purchase-info__price .price',
@@ -13,7 +14,7 @@ export const Ebuyer: Store = {
}, },
outOfStock: { outOfStock: {
container: '.purchase-info', container: '.purchase-info',
text: ['coming soon', 'we are expecting this item on'] text: ['coming soon']
} }
}, },
links: [ links: [
@@ -28,7 +29,8 @@ export const Ebuyer: Store = {
brand: 'sony', brand: 'sony',
model: 'ps5 console', model: 'ps5 console',
series: 'sonyps5c', series: 'sonyps5c',
url: 'https://www.ebuyer.com/1125329-sony-playstation-5-console-cfi-1015a' url:
'https://www.ebuyer.com/1125329-sony-playstation-5-console-cfi-1015a'
}, },
{ {
brand: 'sony', brand: 'sony',
@@ -37,6 +39,20 @@ export const Ebuyer: Store = {
url: url:
'https://www.ebuyer.com/1125332-sony-playstation-5-digital-edition-cfi-1015b' 'https://www.ebuyer.com/1125332-sony-playstation-5-digital-edition-cfi-1015b'
}, },
{
brand: 'microsoft',
model: 'xbox series x',
series: 'xboxsx',
url:
'https://www.ebuyer.com/1133948-xbox-series-x-console-rrt-00007'
},
{
brand: 'microsoft',
model: 'xbox series s',
series: 'xboxss',
url:
'https://www.ebuyer.com/1133947-xbox-series-s-all-digital-console-rrs-00007'
},
{ {
brand: 'amd', brand: 'amd',
model: '5600x', model: '5600x',
@@ -84,6 +100,11 @@ export const Ebuyer: Store = {
url: url:
'https://www.ebuyer.com/store/Components/cat/Graphics-Cards-AMD/subcat/AMD-RX-6800-XT' 'https://www.ebuyer.com/store/Components/cat/Graphics-Cards-AMD/subcat/AMD-RX-6800-XT'
}, },
{
series: 'rx6900xt',
url:
'https://www.ebuyer.com/store/Components/cat/Graphics-Cards-AMD/subcat/AMD-RX-6900-XT'
},
{ {
series: '3060ti', series: '3060ti',
url: url:
@@ -103,11 +124,6 @@ export const Ebuyer: Store = {
series: '3090', series: '3090',
url: url:
'https://www.ebuyer.com/store/Components/cat/Graphics-Cards-Nvidia/subcat/GeForce-RTX-3090' 'https://www.ebuyer.com/store/Components/cat/Graphics-Cards-Nvidia/subcat/GeForce-RTX-3090'
},
{
series: '3060ti',
url:
'https://www.ebuyer.com/store/Components/cat/Graphics-Cards-Nvidia/subcat/GeForce-RTX-3060-Ti'
} }
] ]
}, },
+3 -1
View File
@@ -2,6 +2,7 @@ import {Store} from './store';
export const Elcorteingles: Store = { export const Elcorteingles: Store = {
backoffStatusCodes: [403, 429, 503], backoffStatusCodes: [403, 429, 503],
currency: '€',
labels: { labels: {
// Captcha: { // Captcha: {
// container: 'body', // container: 'body',
@@ -12,7 +13,8 @@ export const Elcorteingles: Store = {
// }, // },
inStock: [ inStock: [
{ {
container: '.product_detail-purchase.mb-2.c12 .js-add-cart-text', container:
'.product_detail-purchase.mb-2.c12 .js-add-cart-text',
text: ['a la cesta'] text: ['a la cesta']
} }
], ],
+13 -2
View File
@@ -1,17 +1,28 @@
import {Store} from './store'; import {Store} from './store';
export const Eprice: Store = { export const Eprice: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.form_aggiungi_articolo', container: '.topSideDx',
text: ['AGGIUNGI AL CARRELLO'] text: ['disponibile', 'pochi pezzi']
}, },
maxPrice: { maxPrice: {
container: '#PrezzoClasic span[class*="big"]', container: '#PrezzoClasic span[class*="big"]',
euroFormat: true euroFormat: true
},
outOfStock: {
container: '.dispo',
text: ['ESAURITO O FUORI PROD.']
} }
}, },
links: [ links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.eprice.it/cuffie-con-microfono-APPLE/d-9030906'
},
{ {
brand: 'msi', brand: 'msi',
model: 'ventus 3x oc', model: 'ventus 3x oc',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const EuronicsDE: Store = { export const EuronicsDE: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.buy-btn--cart-text', container: '.buy-btn--cart-text',
+1
View File
@@ -1,6 +1,7 @@
import {Store} from './store'; import {Store} from './store';
export const Euronics: Store = { export const Euronics: Store = {
currency: '€',
labels: { labels: {
inStock: { inStock: {
container: '.purchaseButtonsWidth', container: '.purchaseButtonsWidth',

Some files were not shown because too many files have changed in this diff Show More