Compare commits

...

146 Commits

Author SHA1 Message Date
github-actions[bot] f9ed774595 chore: release 1.5.0 (#103)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2020-09-23 23:13:49 -04:00
Jef LeCompte c53cfd67c1 chore(store): normalize amazon-de links
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 23:06:11 -04:00
admon84 e1b34a9ccf feat: filter models (#261) 2020-09-23 22:53:19 -04:00
Yu-Po Luke Chen d6a27c988c feat(store): add microcenter store location config (#215)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 22:43:56 -04:00
manybot 8a70f14743 feat(store): add amazon-de (#167)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
Co-authored-by: fuckingrobot <fuckingrobot@users.noreply.github.com>
2020-09-23 22:16:15 -04:00
Jef LeCompte 02ea40b6b2 docs: update mobile carriers
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 22:13:39 -04:00
bubba1337 47dab22d57 chore(notification): additional ca carriers (#248) 2020-09-23 22:08:38 -04:00
Jef LeCompte 676e793ef3 docs: update 3090 stores
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 22:07:17 -04:00
Mark Dietzer dd45dba82c feat(stores): add 3090 for bestbuy, newegg (#249) 2020-09-23 22:06:03 -04:00
serg06 482fb58cbf feat(stores): add 3090s for amazon-ca, bestbuy-ca, newegg-ca (#258)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 22:05:21 -04:00
Jef LeCompte 8913879593 feat: multiple discord roles and webhooks, qol for envs (#260) 2020-09-23 22:02:41 -04:00
admon84 9a53917586 fix(store): adorama captcha config (#234) 2020-09-23 22:02:26 -04:00
Vincent ab61a98bea chore(notification): warn user of improper sms and email config (#259) 2020-09-23 22:01:31 -04:00
Jef LeCompte 9f470f06e9 feat: deprecate nvidia (api), add 3080 add 3090
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 21:03:47 -04:00
Jef LeCompte 4cb52d31d5 chore: remove old cards
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 20:48:55 -04:00
Jef LeCompte 6aed674ee1 chore: add banner, only show sound if enabled
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 19:38:17 -04:00
Jef LeCompte bcbd2b6007 chore: change color dep, add banner
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 19:20:17 -04:00
Jef LeCompte 76b28a6dbd fix: color logs and notification
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 18:43:25 -04:00
Jef LeCompte a154e95681 docs: update configuration notes
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 18:07:09 -04:00
George 0ad67fe204 feat(log): colors for console logs (#207)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 17:55:27 -04:00
Jef LeCompte 2bd8446960 hotfix: series filter
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 16:05:27 -04:00
Jef LeCompte 257dd0e615 chore(store): normalize model
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 14:41:27 -04:00
Scott Cooper c78d9a98ba chore: add types for brand and series (#148)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 14:33:38 -04:00
Jef LeCompte 7c50e2b5aa chore(misc): normalize logs (#242) 2020-09-23 14:20:06 -04:00
serg06 8466f9f398 chore(misc): tiny improvements (#233)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 11:02:44 -04:00
serg06 22fd22fe74 feat(store): add bestbuy.ca (#229) 2020-09-23 10:56:23 -04:00
dependabot[bot] 98c8b1e7b8 chore(deps): bump puppeteer from 5.3.0 to 5.3.1 (#237)
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 5.3.0 to 5.3.1.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Commits](https://github.com/puppeteer/puppeteer/compare/v5.3.0...v5.3.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-23 10:53:47 -04:00
fuckingrobot 74490eae3a feat(notification): add pushbullet, add url with notifications (#226)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-23 10:45:04 -04:00
Andrew Mackrodt 20656805c1 feat: add chromium sandbox skipping (#209) 2020-09-23 08:35:56 -04:00
admon84 6608a79769 feat: invert logic (#141)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 23:33:36 -04:00
Colin Farrell a70e63bb35 chore(store): add netherland nvidia (#210)
Co-authored-by: fuckingrobot <fuckingrobot@users.noreply.github.com>
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 21:59:10 -04:00
Bryan Berger 908ed35882 feat(notification): twitter integration (#224) 2020-09-22 21:49:40 -04:00
Jef LeCompte 9d5f430119 chore(store): normalize amazon links
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 20:34:29 -04:00
bmalaski ede9f6f5a6 chore(store): strix on amazon (#206)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 20:30:05 -04:00
Jef LeCompte 136ccf3593 chore(store): add filter newegg
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 19:56:27 -04:00
Jef LeCompte 9d66683528 chore(store): add filter newegg
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 19:51:06 -04:00
Jef LeCompte cf0eac2b23 chore: add sort linting (#169) 2020-09-22 19:31:49 -04:00
bmalaski 6409646d57 fix(store): bandh removed cards (#201) 2020-09-22 19:29:28 -04:00
Paolo Moretti 605bdd7ca7 feat(store): add evga eu (#172)
Co-authored-by: fuckingrobot <fuckingrobot@users.noreply.github.com>
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 19:19:55 -04:00
Sachin Umashankar 8e7d267549 chore(store): add errors for bandh (#180)
Co-authored-by: fuckingrobot <fuckingrobot@users.noreply.github.com>
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 19:17:37 -04:00
levelfifty 190388cfe4 feat(store): add evga model (#220) 2020-09-22 19:15:23 -04:00
Jef LeCompte 1d63733681 docs: add zotac
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 19:13:44 -04:00
AndrewKurniawan 78758552b2 feat(store): add zotac store (#214) 2020-09-22 19:12:17 -04:00
dependabot[bot] 14c36bebe7 chore(deps): bump messaging-api-telegram from 1.0.0 to 1.0.1 (#196)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-22 02:29:51 -04:00
dependabot[bot] 327f7b080a chore(deps-dev): bump @types/node from 14.11.1 to 14.11.2 (#197)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.11.1 to 14.11.2.
- [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-09-22 02:28:41 -04:00
Jef LeCompte 4f7db895e2 docs: update faq 2020-09-21 23:54:42 -04:00
Jef LeCompte 3b51c02873 docs: update readme 2020-09-21 20:13:46 -04:00
fuckingrobot ebd6091a09 feat: set country in config, login to nvidia when starting (#162) 2020-09-21 20:12:45 -04:00
Jef LeCompte 3b9a1d2ea8 hotfix: missing quote 2020-09-21 19:30:05 -04:00
Jef LeCompte adb603b776 chore: remove unknown affiliate 2020-09-21 19:25:25 -04:00
Jef LeCompte 2c6866ad7f chore: remove unknown affliate 2020-09-21 19:24:07 -04:00
George 71c6774511 feat(store): add bannedSeller label for stores (#173)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-21 18:54:40 -04:00
Jef LeCompte 5f628d2c12 chore(store): add errors for evga (#176) 2020-09-21 18:24:46 -04:00
Jef LeCompte 4dfdb9eb2e chore: make SCREENSHOT default true
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-21 16:18:56 -04:00
designgears 12d25eb710 chore(store): officedepot oos label (#171) 2020-09-21 15:26:02 -04:00
xdMatthewbx 9675c5b8d6 fix(notification): change discord ping visibility (#168)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
Co-authored-by: bmalaski <5288648+bmalaski@users.noreply.github.com>
2020-09-21 14:50:40 -04:00
bmalaski 0df2dcfbd4 feat(store): add office depot (#157)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-21 12:54:45 -04:00
xdMatthewbx 76f5849889 feat(store): add newegg.ca (#160)
closes #159
2020-09-21 12:48:00 -04:00
Nachi G 6413144c1c feat: temporarily pause requests if store has stock (#147)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-21 11:38:18 -04:00
George 133a54fa17 feat: bestbuy bypass international splash, newegg add to cart (#153) 2020-09-21 11:36:25 -04:00
bmalaski debd8f57da chore(store): pny listings (#156)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-21 08:34:09 -04:00
fuckingrobot 3ea146da14 feat: update for complex add to cart, fix nvidia (#108) 2020-09-21 08:24:20 -04:00
Nachi G 722eaf3cd6 feat(notification): add desktop notifications (#140)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 20:48:27 -04:00
Jordan Garcia 7191e03a80 feat: include screenshot for emails + sms notifications (#144) 2020-09-20 20:42:20 -04:00
Mark Dietzer 0f6e570cc8 fix: memory leak due to adblocker (#139) 2020-09-20 19:38:31 -04:00
JoeSchofield 7fc1e776fd docs: update pushover variables (#136) 2020-09-20 18:26:57 -04:00
Mark Dietzer a75d214dd5 fix(nvidia): false positives (#132)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 17:46:45 -04:00
admon84 19c8f188c7 fix: newegg out-of-stock labels (#134) 2020-09-20 17:45:25 -04:00
Jef LeCompte d42736bf22 chore: update funding 2020-09-20 16:56:35 -04:00
Jef LeCompte d2893f6826 chore: update funding 2020-09-20 16:55:40 -04:00
Jef LeCompte 07b2da4fe9 chore(store): filter amazon third party vendors (#126) 2020-09-20 16:45:56 -04:00
admon84 770a13ac35 fix: newegg out-of-stock (#124) 2020-09-20 16:19:36 -04:00
admon84 d9be3fe618 feat: custom user agent (#121)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 15:56:47 -04:00
admon84 252459d5d3 feat: card series filter, fix: newegg oosLabels (#120) 2020-09-20 15:51:44 -04:00
Jef LeCompte 7efdcd8f07 chore: change test:notification to run explicitly
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 14:36:00 -04:00
Mark Dietzer 4f83b3b233 feat: add delay on captcha to try and evade faster (#119) 2020-09-20 14:27:10 -04:00
Jef LeCompte c0352961a9 refactor: extract lookup (#117) 2020-09-20 14:13:29 -04:00
Mark Dietzer dc0f710674 feat(scraping): change lookup impl, add randomize sleep (#110)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 13:52:37 -04:00
Jef LeCompte 2e5d13bda5 docs: PLAY_SOUND note 2020-09-20 13:48:45 -04:00
Jef LeCompte 2a1f15041e chore: move notification test, other refactoring (#111) 2020-09-20 13:18:32 -04:00
bmalaski a3fc07daf0 feat(notification): discord integration (#82)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 12:36:05 -04:00
bmalaski 5b91065043 feat(store): add adorama (#104)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 12:14:56 -04:00
Jef LeCompte e819e46116 fix: keep single Store from draining
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 12:00:25 -04:00
Christopher Sauer 39a478050a docs: add pushover api generation link (#107) 2020-09-20 11:26:38 -04:00
Jef LeCompte 28947be9bc fix: rateLimitTimeout not being defaulted (#106)
refactor: `browser` and `store` config object
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 11:02:57 -04:00
Jef LeCompte 3de1f81eb1 hotfix: make HEADLESS default true
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 10:39:44 -04:00
Jordan Garcia d1a5aa1f02 feat: load puppeteer faster, run stores in parallel (#83)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 10:28:45 -04:00
Forrest Marvez a501cf703b feat(store): add asus (#102)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 10:10:00 -04:00
anethema c65fa04666 fix(store): false positives for nvidia. (#85)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 09:44:49 -04:00
Jef LeCompte a538809db5 chore: update pr template
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 09:29:02 -04:00
Jef LeCompte 85faaa1ca7 chore: update desc of variables (#101)
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 09:15:57 -04:00
Mark Dietzer 103d96dc81 fix(notification): wrong condition for sounds playing (#91)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 08:47:37 -04:00
Wulfre df5ba68e94 chore(store): normalize model names (#96)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-20 08:36:19 -04:00
Lukas b7d9462e79 fix: check response for rate limiting (#58) (#98)
Co-authored-by: Lukas Szimtenings <lszimtenings@ukaachen.de>
2020-09-20 08:33:47 -04:00
George 1e9d8fec42 refactor: move cartUrl from Store to Link (#87) 2020-09-19 22:49:09 -04:00
github-actions[bot] 381f1f7505 chore: release 1.4.0 (#66)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2020-09-19 18:17:27 -04:00
Jef LeCompte ebbdfe3f63 perf: browser abstraction (#68) (#81)
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 18:13:20 -04:00
Jef LeCompte 14b1e4cee6 chore: add discord badge, update includes-labels
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 18:01:36 -04:00
vmnt 433fa8ca9f chore(logging): reformat output (#74)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 17:47:55 -04:00
Jef LeCompte cf0e959fb4 refactor: store naming and map reference
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 17:43:53 -04:00
Jef LeCompte 8c5d7d0c49 refactor: env, cartUrl optional, other consistencies
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 17:36:33 -04:00
Mark Dietzer 393d5f6898 feat(notification): add telegram (#71) 2020-09-19 17:02:00 -04:00
anethema d4de1a4638 feat(store): add amazon.ca, fix timeout (#75) 2020-09-19 17:00:14 -04:00
Thomas Herrell c85658bf82 feat(notification): add pushover (#55)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 15:55:21 -04:00
Cody Stammers 8aba7ecbdb feat(notification): add mint mobile carrier (#70) 2020-09-19 15:53:39 -04:00
Jef LeCompte ccc781b8dd docs: better format for carriers
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 13:10:09 -04:00
Jef LeCompte 6be74a19f3 feat(notification): add telus sms
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 13:07:53 -04:00
Jef LeCompte dcda82bd98 hotfix: lint
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 12:45:47 -04:00
Jef LeCompte ea5b7a0918 refactor: lookup, rm defaults (#69)
prep work for #38

Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 12:45:03 -04:00
Gylfi 3b2ba29cf1 chore(store): add bandh links (#65)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 11:23:49 -04:00
Evan Gentis a21740942b feat: webpage toggle, sound notification, fix evga links (#52)
Co-authored-by: Evan Gentis <evan.gentis@gmail.com>
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 10:42:40 -04:00
Jef LeCompte 25643c4870 refactor: map sms, fix amazon text
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 10:28:31 -04:00
Jef LeCompte ef319d4b20 chore: update issue templates
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 10:05:19 -04:00
github-actions[bot] fcb279d5de chore: release 1.3.0 (#62)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2020-09-19 09:17:46 -04:00
admon84 5c9e0b6d06 fix(store): amazon captcha false-positives (#54)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 09:02:11 -04:00
Ruochong Wu 6c3cd01685 feat(logging): add timestamp (#48)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 08:49:22 -04:00
Kevin Tong d907092b44 fix: evga xc3 ultra link (#56)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-19 08:43:45 -04:00
admon84 7cfc7c7442 fix: screenshot size, add screenshot config setting (#53) 2020-09-19 08:37:20 -04:00
admon84 971fec20e4 fix: sms carrier config, add google carrier (#44) 2020-09-18 21:39:30 -04:00
github-actions[bot] d73449dd7f chore: release 1.2.0 (#31)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2020-09-18 20:12:30 -04:00
malbert69 edf17e926f feat(store): microcenter (#39)
Co-authored-by: Ion Caza <contact@johncaza.com>
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 20:09:47 -04:00
Jef LeCompte faad3e68ef feat(ci): add npm run build
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:58:07 -04:00
Jef LeCompte 131dda4d5a hotfix: config variable reference
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:56:11 -04:00
Jef LeCompte 2246ecedf2 hotfix: config variable reference
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:55:47 -04:00
Alexandru Gutu 5a3636bcb6 feat: sms notification for usa carriers (#40)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:54:59 -04:00
Jef LeCompte ec2108f4b9 hotfix: config variable reference
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:39:18 -04:00
Jef LeCompte fad9ea04c7 refactor: doc and notification (#42)
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:34:09 -04:00
Ion Caza 643045c7e0 feat: page timeout (#22)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 19:04:29 -04:00
Vincent 7d7bd18b4d feat: more Best Buy AIBs (#41)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 18:45:12 -04:00
Jef LeCompte ec98602d5b chore: remove cart link for nvidia
no longer working

Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 18:43:34 -04:00
DakkJaniels a2fb97333c feat: add email test, fix memory leak (#24)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 18:06:40 -04:00
Esg450 c0a881a16e feat: slack integration (#34)
Co-authored-by: Evan Gentis <evan.gentis@gmail.com>
2020-09-18 17:55:26 -04:00
Vincent c2a210cc81 fix: small error in isOutOfStock logic (#33) 2020-09-18 17:28:29 -04:00
George aa856c7121 docs: add amazon 2020-09-18 15:08:04 -04:00
George f0560ce72b feat: add Amazon links (#26)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 14:35:11 -04:00
fuckingrobot 00ede13501 feat: update to check if FE in-stock via Digital River (#29)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 14:31:55 -04:00
geman220 feed4ed6e4 docs: add EVGA to table checklist (#23) 2020-09-18 11:13:18 -04:00
github-actions[bot] a152f8a5cc chore: release 1.1.0 (#15)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2020-09-18 10:27:21 -04:00
Jef LeCompte 00a0687d3e fix: exception handling TimeoutError (#20) (#21)
Signed-off-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 10:26:02 -04:00
Jef LeCompte d3ec4bc85e docs: add evga to supported stores 2020-09-18 10:11:23 -04:00
Jef LeCompte 1a13c03250 docs: remove verbose installation 2020-09-18 10:09:45 -04:00
Jef LeCompte 6c6503219f feat: add evga (#17) (#18) 2020-09-18 09:43:51 -04:00
Ali Kafagy efe799b8b9 docs: add debian support and store info (#16)
Co-authored-by: Jef LeCompte <jeffreylec@gmail.com>
2020-09-18 08:36:03 -04:00
Jef LeCompte 4a5886782b docs: add wiki link 2020-09-18 05:13:12 -04:00
Jef LeCompte e1a90c931d docs: mention api wip 2020-09-18 05:07:17 -04:00
Jef LeCompte eb4f5e0341 feat: add conventional commits (#14) 2020-09-18 04:44:03 -04:00
68 changed files with 4268 additions and 424 deletions
+12
View File
@@ -0,0 +1,12 @@
root = true
[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2
+39
View File
@@ -0,0 +1,39 @@
# ** All configuration variables are optional **
# Read https://github.com/jef/nvidia-snatcher#customization for help on customizing this file
#############################################################################################
BROWSER_TRUSTED=""
DISCORD_NOTIFY_GROUP=""
DISCORD_WEB_HOOK=""
EMAIL_USERNAME=""
EMAIL_PASSWORD=""
HEADLESS=""
IN_STOCK_WAIT_TIME=""
LOG_LEVEL=""
MICROCENTER_LOCATION=""
OPEN_BROWSER=""
PAGE_TIMEOUT=""
PHONE_NUMBER=""
PHONE_CARRIER=""
PLAY_SOUND=""
PUSHBULLET=""
PUSHOVER_TOKEN=""
PUSHOVER_USER=""
PAGE_SLEEP_MIN=""
PAGE_SLEEP_MAX=""
SHOW_ONLY_BRANDS=""
SHOW_ONLY_MODELS=""
SHOW_ONLY_SERIES=""
SLACK_CHANNEL=""
SLACK_TOKEN=""
STORES=""
COUNTRY=""
SCREENSHOT="false"
TELEGRAM_ACCESS_TOKEN=""
TELEGRAM_CHAT_ID=""
TWITTER_CONSUMER_KEY=""
TWITTER_CONSUMER_SECRET=""
TWITTER_ACCESS_TOKEN_KEY=""
TWITTER_ACCESS_TOKEN_SECRET=""
TWITTER_TWEET_TAGS=""
USER_AGENT=""
-3
View File
@@ -1,3 +0,0 @@
EMAIL_USERNAME="youremail@gmail.com"
EMAIL_PASSWORD="secretpassword"
STORES="bestbuy,bandh,nvidia"
+2
View File
@@ -1 +1,3 @@
github: jef
patreon: hijef
custom: ["https://www.paypal.me/jxf"]
+4 -2
View File
@@ -1,7 +1,7 @@
---
name: 🐛 Bug report
about: Report a bug for this project
title: 'bug: '
title: ''
labels: 'bug'
assignees: jef
@@ -9,7 +9,9 @@ assignees: jef
### Description
<!-- Describe the feature here. -->
<!-- Describe the bug here. -->
<!-- Please be as detailed as possible. -->
<!-- Include OS, screenshots and console outputs. -->
### Possible solution
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: 🚀 Feature request
about: Suggest a feature for this project
title: 'enhancement: '
title: ''
labels: 'enhancement'
assignees: jef
+7
View File
@@ -1,8 +1,15 @@
<!-- Please use Conventional Commits to label your title -->
<!-- https://www.conventionalcommits.org/en/v1.0.0/ -->
<!-- Example: feat: allow provided config object to extend other configs -->
### Description
<!-- Fixes #(issue) -->
<!-- Please also include relevant motivation and context. -->
<!-- Feel free to include your Discord tag here and we'll consider making you a ringer! -->
<!-- Continuous improvements may also grant you contributor access. -->
### Testing
<!-- Please describe the tests that you ran to verify your changes. -->
+4 -1
View File
@@ -4,7 +4,7 @@ on:
branches:
- main
jobs:
lint:
build-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
@@ -16,7 +16,10 @@ jobs:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-node-
- name: Add problem matcher
run: echo "::add-matcher::.github/xo-problem-matcher.json"
- name: Pull dependencies
run: |
npm ci
npm run build
npm run lint
+21
View File
@@ -0,0 +1,21 @@
{
"problemMatcher": [
{
"owner": "xo",
"pattern": [
{
"regexp": "^\\s+(.*):(\\d+):(\\d+)$",
"file": 1
},
{
"regexp": "^\\s+✖\\s+(\\d+):(\\d+)\\s+(.*)\\s+(.*)$",
"line": 1,
"column": 2,
"message": 3,
"code": 4,
"loop": true
}
]
}
]
}
+7
View File
@@ -1,5 +1,12 @@
.idea/
.vs/
.vscode/
build/
node_modules/
.env
success-*.png
*.wav
*.mp3
*.flac
+121
View File
@@ -1,5 +1,126 @@
# Changelog
## [1.5.0](https://www.github.com/jef/nvidia-snatcher/compare/v1.4.0...v1.5.0) (2020-09-24)
### Features
* filter models ([#261](https://www.github.com/jef/nvidia-snatcher/issues/261)) ([e1b34a9](https://www.github.com/jef/nvidia-snatcher/commit/e1b34a9ccfa45fa1a11da9af9074059b6084904b))
* **log:** colors for console logs ([#207](https://www.github.com/jef/nvidia-snatcher/issues/207)) ([0ad67fe](https://www.github.com/jef/nvidia-snatcher/commit/0ad67fe20453898ce0a6b5faff00062735411119))
* **notification:** add desktop notifications ([#140](https://www.github.com/jef/nvidia-snatcher/issues/140)) ([722eaf3](https://www.github.com/jef/nvidia-snatcher/commit/722eaf3cd680c4600b79f842c6c5acdb9e51ad71))
* **notification:** add pushbullet, add url with notifications ([#226](https://www.github.com/jef/nvidia-snatcher/issues/226)) ([74490ea](https://www.github.com/jef/nvidia-snatcher/commit/74490eae3ab30de7d7a708d5dd970e070f27f2ea))
* **notification:** twitter integration ([#224](https://www.github.com/jef/nvidia-snatcher/issues/224)) ([908ed35](https://www.github.com/jef/nvidia-snatcher/commit/908ed358826f9de530f5892ded1a54964a304d15))
* **store:** add `bannedSeller` label for stores ([#173](https://www.github.com/jef/nvidia-snatcher/issues/173)) ([71c6774](https://www.github.com/jef/nvidia-snatcher/commit/71c6774511f7ba13d34d2e40b69abf52d06e6225))
* **store:** add amazon-de ([#167](https://www.github.com/jef/nvidia-snatcher/issues/167)) ([8a70f14](https://www.github.com/jef/nvidia-snatcher/commit/8a70f147438584cc334710bc66220d05eb32fcbd))
* **store:** add bestbuy.ca ([#229](https://www.github.com/jef/nvidia-snatcher/issues/229)) ([22fd22f](https://www.github.com/jef/nvidia-snatcher/commit/22fd22fe743d3e286eae3430aecd6e7a0a5de8c0))
* **store:** add evga eu ([#172](https://www.github.com/jef/nvidia-snatcher/issues/172)) ([605bdd7](https://www.github.com/jef/nvidia-snatcher/commit/605bdd7ca73c585734f6c5df1a86f4fbfbff9163))
* **store:** add evga model ([#220](https://www.github.com/jef/nvidia-snatcher/issues/220)) ([190388c](https://www.github.com/jef/nvidia-snatcher/commit/190388cfe4a5e3f19abccd0ff786f654b9a04d2f))
* **store:** add microcenter store location config ([#215](https://www.github.com/jef/nvidia-snatcher/issues/215)) ([d6a27c9](https://www.github.com/jef/nvidia-snatcher/commit/d6a27c988c7b1011c7a10084d8283a60ed8aea5c))
* **stores:** add 3090 for bestbuy, newegg ([#249](https://www.github.com/jef/nvidia-snatcher/issues/249)) ([dd45dba](https://www.github.com/jef/nvidia-snatcher/commit/dd45dba82cb86f7e7664298dd202b93bbbd46d9f))
* **stores:** add 3090s for amazon-ca, bestbuy-ca, newegg-ca ([#258](https://www.github.com/jef/nvidia-snatcher/issues/258)) ([482fb58](https://www.github.com/jef/nvidia-snatcher/commit/482fb58cbfde6f95fb6f77de790d76e6aa2a5926))
* add chromium sandbox skipping ([#209](https://www.github.com/jef/nvidia-snatcher/issues/209)) ([2065680](https://www.github.com/jef/nvidia-snatcher/commit/20656805c1259637bb3a4db465a8d16d4780296a))
* deprecate nvidia (api), add 3080 add 3090 ([9f470f0](https://www.github.com/jef/nvidia-snatcher/commit/9f470f06e9e9fb605d340c0b0f9016d7288e8c0b))
* invert logic ([#141](https://www.github.com/jef/nvidia-snatcher/issues/141)) ([6608a79](https://www.github.com/jef/nvidia-snatcher/commit/6608a79769ff03543ab4ed2f2cead3410d7d7e99))
* multiple discord roles and webhooks, qol for envs ([#260](https://www.github.com/jef/nvidia-snatcher/issues/260)) ([8913879](https://www.github.com/jef/nvidia-snatcher/commit/8913879593252c9c83020b2e2c46bad7537b2a20))
* **store:** add newegg.ca ([#160](https://www.github.com/jef/nvidia-snatcher/issues/160)) ([76f5849](https://www.github.com/jef/nvidia-snatcher/commit/76f584988979a40269fd3641e996800a63b4b163)), closes [#159](https://www.github.com/jef/nvidia-snatcher/issues/159)
* **store:** add office depot ([#157](https://www.github.com/jef/nvidia-snatcher/issues/157)) ([0df2dcf](https://www.github.com/jef/nvidia-snatcher/commit/0df2dcfbd48235fba7126d96cd912634c5b4fdd9))
* **store:** add zotac store ([#214](https://www.github.com/jef/nvidia-snatcher/issues/214)) ([7875855](https://www.github.com/jef/nvidia-snatcher/commit/78758552b22e608dbdf3e76397f5b5efb893fef5))
* add delay on captcha to try and evade faster ([#119](https://www.github.com/jef/nvidia-snatcher/issues/119)) ([4f83b3b](https://www.github.com/jef/nvidia-snatcher/commit/4f83b3b233657841a4068a8ff9dd6c8dbff631c0))
* bestbuy bypass international splash, newegg add to cart ([#153](https://www.github.com/jef/nvidia-snatcher/issues/153)) ([133a54f](https://www.github.com/jef/nvidia-snatcher/commit/133a54fa170bb16dd26b0d72b1a02c56b3851b7f))
* card series filter, fix: newegg `oosLabels` ([#120](https://www.github.com/jef/nvidia-snatcher/issues/120)) ([252459d](https://www.github.com/jef/nvidia-snatcher/commit/252459d5d3de2b8cb25deee9ae318108e3dda2be))
* custom user agent ([#121](https://www.github.com/jef/nvidia-snatcher/issues/121)) ([d9be3fe](https://www.github.com/jef/nvidia-snatcher/commit/d9be3fe6183eaa9694b186c7a75e1f28bb31dace))
* include screenshot for emails + sms notifications ([#144](https://www.github.com/jef/nvidia-snatcher/issues/144)) ([7191e03](https://www.github.com/jef/nvidia-snatcher/commit/7191e03a80e577b59b2861289aa658cfa0ffc0fa))
* load puppeteer faster, run stores in parallel ([#83](https://www.github.com/jef/nvidia-snatcher/issues/83)) ([d1a5aa1](https://www.github.com/jef/nvidia-snatcher/commit/d1a5aa1f02ff0a8f293b93e3c078b5943908a95b))
* set country in config, login to nvidia when starting ([#162](https://www.github.com/jef/nvidia-snatcher/issues/162)) ([ebd6091](https://www.github.com/jef/nvidia-snatcher/commit/ebd6091a09fb5e52a66742767ae4b58323cd7447))
* temporarily pause requests if store has stock ([#147](https://www.github.com/jef/nvidia-snatcher/issues/147)) ([6413144](https://www.github.com/jef/nvidia-snatcher/commit/6413144c1cae89f33f852cc93870b407a784f2bb))
* update for complex add to cart, fix nvidia ([#108](https://www.github.com/jef/nvidia-snatcher/issues/108)) ([3ea146d](https://www.github.com/jef/nvidia-snatcher/commit/3ea146da14ea40d145ccfc05436beeb0a9fed8d9))
* **notification:** discord integration ([#82](https://www.github.com/jef/nvidia-snatcher/issues/82)) ([a3fc07d](https://www.github.com/jef/nvidia-snatcher/commit/a3fc07daf0a3f33f18e03d4cfc13d3477a9c4fa0))
* **scraping:** change lookup impl, add randomize sleep ([#110](https://www.github.com/jef/nvidia-snatcher/issues/110)) ([dc0f710](https://www.github.com/jef/nvidia-snatcher/commit/dc0f7106749b0afa0ff1c91cabb90b65be30e909))
* **store:** add adorama ([#104](https://www.github.com/jef/nvidia-snatcher/issues/104)) ([5b91065](https://www.github.com/jef/nvidia-snatcher/commit/5b910650430ad4806b22722efa9a013e72ea47e7))
* **store:** add asus ([#102](https://www.github.com/jef/nvidia-snatcher/issues/102)) ([a501cf7](https://www.github.com/jef/nvidia-snatcher/commit/a501cf703bb05f47af6240a4b16a3dc4dcf3baf5))
### Bug Fixes
* **store:** adorama captcha config ([#234](https://www.github.com/jef/nvidia-snatcher/issues/234)) ([9a53917](https://www.github.com/jef/nvidia-snatcher/commit/9a539175860f98de3b023009f751e59d94f0aaef))
* color logs and notification ([76b28a6](https://www.github.com/jef/nvidia-snatcher/commit/76b28a6dbdf5480c12a8c82b031c3f2880d17b11))
* **notification:** change discord ping visibility ([#168](https://www.github.com/jef/nvidia-snatcher/issues/168)) ([9675c5b](https://www.github.com/jef/nvidia-snatcher/commit/9675c5b8d61226db4652964e7f1e7399bb82d04e))
* **store:** bandh removed cards ([#201](https://www.github.com/jef/nvidia-snatcher/issues/201)) ([6409646](https://www.github.com/jef/nvidia-snatcher/commit/6409646d57bf2b2bb5a4bcf8239740abed8edafb))
* `rateLimitTimeout` not being defaulted ([#106](https://www.github.com/jef/nvidia-snatcher/issues/106)) ([28947be](https://www.github.com/jef/nvidia-snatcher/commit/28947be9bc8981d7a45a5d0e69c18d039fcd9ed3))
* check response for rate limiting ([#58](https://www.github.com/jef/nvidia-snatcher/issues/58)) ([#98](https://www.github.com/jef/nvidia-snatcher/issues/98)) ([b7d9462](https://www.github.com/jef/nvidia-snatcher/commit/b7d9462e794ef3961fb57c79ef8f66e77d25d20a))
* keep single `Store` from draining ([e819e46](https://www.github.com/jef/nvidia-snatcher/commit/e819e46116d4e0b067a59791094b5cfbd2d7cd45))
* memory leak due to adblocker ([#139](https://www.github.com/jef/nvidia-snatcher/issues/139)) ([0f6e570](https://www.github.com/jef/nvidia-snatcher/commit/0f6e570cc817dfc10bcddc5743a0faf3b1489270))
* **nvidia:** false positives ([#132](https://www.github.com/jef/nvidia-snatcher/issues/132)) ([a75d214](https://www.github.com/jef/nvidia-snatcher/commit/a75d214dd555d5e0388cb54b15be324cc25b6a15))
* newegg out-of-stock ([#124](https://www.github.com/jef/nvidia-snatcher/issues/124)) ([770a13a](https://www.github.com/jef/nvidia-snatcher/commit/770a13ac3559401b430547908d1df014582c1e37))
* newegg out-of-stock labels ([#134](https://www.github.com/jef/nvidia-snatcher/issues/134)) ([19c8f18](https://www.github.com/jef/nvidia-snatcher/commit/19c8f188c796258c469c2b4c6461fc5da3907a47))
* **notification:** wrong condition for sounds playing ([#91](https://www.github.com/jef/nvidia-snatcher/issues/91)) ([103d96d](https://www.github.com/jef/nvidia-snatcher/commit/103d96dc81d6fd097fcdbed5bdd7487d7d73bf6e))
* **store:** false positives for nvidia. ([#85](https://www.github.com/jef/nvidia-snatcher/issues/85)) ([c65fa04](https://www.github.com/jef/nvidia-snatcher/commit/c65fa04666775060532e28076a0b4af50f8dd30b))
## [1.4.0](https://www.github.com/jef/nvidia-snatcher/compare/v1.3.0...v1.4.0) (2020-09-19)
### Features
* **notification:** add mint mobile carrier ([#70](https://www.github.com/jef/nvidia-snatcher/issues/70)) ([8aba7ec](https://www.github.com/jef/nvidia-snatcher/commit/8aba7ecbdb0bfce06257b7b9066e8fccbd82e47e))
* **notification:** add pushover ([#55](https://www.github.com/jef/nvidia-snatcher/issues/55)) ([c85658b](https://www.github.com/jef/nvidia-snatcher/commit/c85658bf82fdf360e5e9d8345eaa846f0572e67c))
* **notification:** add telegram ([#71](https://www.github.com/jef/nvidia-snatcher/issues/71)) ([393d5f6](https://www.github.com/jef/nvidia-snatcher/commit/393d5f689887bf1d6f30a37eea163b2e6bbd4efa))
* **notification:** add telus sms ([6be74a1](https://www.github.com/jef/nvidia-snatcher/commit/6be74a19f3d3f999145d17ac8e91c59db2502071))
* **store:** add amazon.ca, fix timeout ([#75](https://www.github.com/jef/nvidia-snatcher/issues/75)) ([d4de1a4](https://www.github.com/jef/nvidia-snatcher/commit/d4de1a4638e903eb9518354ab6fb2f8c4befc347))
* webpage toggle, sound notification, fix evga links ([#52](https://www.github.com/jef/nvidia-snatcher/issues/52)) ([a217409](https://www.github.com/jef/nvidia-snatcher/commit/a21740942bbbbe967948062fa06cfc82c31eb755))
### Performance Improvements
* browser abstraction ([#68](https://www.github.com/jef/nvidia-snatcher/issues/68)) ([#81](https://www.github.com/jef/nvidia-snatcher/issues/81)) ([ebbdfe3](https://www.github.com/jef/nvidia-snatcher/commit/ebbdfe3f6378516112f4b6e004bbd6ccf13af685))
## [1.3.0](https://www.github.com/jef/nvidia-snatcher/compare/v1.2.0...v1.3.0) (2020-09-19)
### Features
* **logging:** add timestamp ([#48](https://www.github.com/jef/nvidia-snatcher/issues/48)) ([6c3cd01](https://www.github.com/jef/nvidia-snatcher/commit/6c3cd016850d03a6c6a894cab24ba2d3781a9af1))
### Bug Fixes
* **store:** amazon captcha false-positives ([#54](https://www.github.com/jef/nvidia-snatcher/issues/54)) ([5c9e0b6](https://www.github.com/jef/nvidia-snatcher/commit/5c9e0b6d06bd7e1223a7587fec067c8e79c9cfd6))
* evga xc3 ultra link ([#56](https://www.github.com/jef/nvidia-snatcher/issues/56)) ([d907092](https://www.github.com/jef/nvidia-snatcher/commit/d907092b443b056605e09cb2ca3e94e6ca811d9e))
* screenshot size, add screenshot config setting ([#53](https://www.github.com/jef/nvidia-snatcher/issues/53)) ([7cfc7c7](https://www.github.com/jef/nvidia-snatcher/commit/7cfc7c74429c808fa14468cdd497eb9f9aeb922c))
* sms carrier config, add google carrier ([#44](https://www.github.com/jef/nvidia-snatcher/issues/44)) ([971fec2](https://www.github.com/jef/nvidia-snatcher/commit/971fec20e441e2b12a38d5c8d17d2d4cb5e64d6b))
## [1.2.0](https://www.github.com/jef/nvidia-snatcher/compare/v1.1.0...v1.2.0) (2020-09-19)
### Features
* **ci:** add `npm run build` ([faad3e6](https://www.github.com/jef/nvidia-snatcher/commit/faad3e68efafaab135b77080b02af83429b6eca6))
* **store:** microcenter ([#39](https://www.github.com/jef/nvidia-snatcher/issues/39)) ([edf17e9](https://www.github.com/jef/nvidia-snatcher/commit/edf17e926f3d186e7630da2834d78de3e540a956))
* add Amazon links ([#26](https://www.github.com/jef/nvidia-snatcher/issues/26)) ([f0560ce](https://www.github.com/jef/nvidia-snatcher/commit/f0560ce72bfbfdd6360b85e23edaa875d58f228f))
* add email test, fix memory leak ([#24](https://www.github.com/jef/nvidia-snatcher/issues/24)) ([a2fb973](https://www.github.com/jef/nvidia-snatcher/commit/a2fb97333c6eb81250b24ccb6859e9356acded21))
* more Best Buy AIBs ([#41](https://www.github.com/jef/nvidia-snatcher/issues/41)) ([7d7bd18](https://www.github.com/jef/nvidia-snatcher/commit/7d7bd18b4dd656ec01ef2fb2d8519e2a7f34ef70))
* page timeout ([#22](https://www.github.com/jef/nvidia-snatcher/issues/22)) ([643045c](https://www.github.com/jef/nvidia-snatcher/commit/643045c7e0158fb6526bd09427b96cce7958bcea))
* slack integration ([#34](https://www.github.com/jef/nvidia-snatcher/issues/34)) ([c0a881a](https://www.github.com/jef/nvidia-snatcher/commit/c0a881a16ebb573bf35b7f29cb27e5b3c2e1fe78))
* sms notification for usa carriers ([#40](https://www.github.com/jef/nvidia-snatcher/issues/40)) ([5a3636b](https://www.github.com/jef/nvidia-snatcher/commit/5a3636bcb639bb33bc586af96264f5df2f3a8307))
* update to check if FE in-stock via Digital River ([#29](https://www.github.com/jef/nvidia-snatcher/issues/29)) ([00ede13](https://www.github.com/jef/nvidia-snatcher/commit/00ede13501082f530ea672a349816be1d31621a8))
### Bug Fixes
* small error in `isOutOfStock` logic ([#33](https://www.github.com/jef/nvidia-snatcher/issues/33)) ([c2a210c](https://www.github.com/jef/nvidia-snatcher/commit/c2a210cc815c3aa06f6f14d33954f65577d95954))
## [1.1.0](https://www.github.com/jef/nvidia-snatcher/compare/v1.0.0...v1.1.0) (2020-09-18)
### Features
* add conventional commits ([#14](https://www.github.com/jef/nvidia-snatcher/issues/14)) ([eb4f5e0](https://www.github.com/jef/nvidia-snatcher/commit/eb4f5e034176a286eabe381c98ced77cd197d7fb))
* add evga ([#17](https://www.github.com/jef/nvidia-snatcher/issues/17)) ([#18](https://www.github.com/jef/nvidia-snatcher/issues/18)) ([6c65032](https://www.github.com/jef/nvidia-snatcher/commit/6c6503219f7c188783c24a44f7052b276a4b39a3))
### Bug Fixes
* exception handling `TimeoutError` ([#20](https://www.github.com/jef/nvidia-snatcher/issues/20)) ([#21](https://www.github.com/jef/nvidia-snatcher/issues/21)) ([00a0687](https://www.github.com/jef/nvidia-snatcher/commit/00a0687d3eba6a8fca871161b447529be00c8896))
## 1.0.0 (2020-09-18)
+139 -30
View File
@@ -1,12 +1,16 @@
# nvidia-snatcher [![ci](https://github.com/jef/nvidia-snatcher/workflows/ci/badge.svg)](https://github.com/jef/nvidia-snatcher/actions?query=workflow%3Aci)
# nvidia-snatcher [![ci](https://github.com/jef/nvidia-snatcher/workflows/ci/badge.svg)](https://github.com/jef/nvidia-snatcher/actions?query=workflow%3Aci) [![discord](https://img.shields.io/discord/756303724095471617.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/Cyc7nrz)
The purpose of this bot is to get an Nvidia card. It does multiple things to try to do that.
[FAQ](#FAQ) | [Issues](https://github.com/jef/nvidia-snatcher/issues) | [Wiki](https://github.com/jef/nvidia-snatcher/wiki)
![nvidia-snatcher](media/screenshot.png)
The purpose of this bot is to get an Nvidia card. It tries multiple things to do that.
- Currently, `nvidia-snatcher` is not capable of purchasing a card for you
- Scrapes multiple websites for patterns of being stocked
- API requests are a work in progress (very soon)
- Opens browser when stock is available
- Send email to you when away from computer
- Must have Gmail
- Sends an email to you when stock is avaiable (must have Gmail)
<details>
<summary>What you may see if you're lucky</summary>
@@ -24,17 +28,13 @@ The purpose of this bot is to get an Nvidia card. It does multiple things to try
</details>
> :point_right: You may get false positives from time to time, so I apologize for that. The library currently waits for all calls to be completed before parsing, but sometimes this can unknown behavior. Patience is a virtue :)
> :point_right: You may get false positives from time to time, so I apologize for that. The library currently waits for all calls to be completed before parsing, but sometimes this can have unknown behavior. Patience is a virtue :)
| | **Best Buy** | **B&H** | **Newegg** | **Nvidia** |
|:---:|:---:|:---:|:---:|:---:|
| **3070**| | | | |
| **3080** | `✔` | `✔` | `` | `✔` |
| **3090** | | | | |
> :point_right: (``) In the process of getting working. Catchpa problems are intermittent. Use if you'd like, but expect problems.
[FAQ](#FAQ) | [Discord](https://discord.gg/3duFzwk) | [Issues](https://github.com/jef/nvidia-snatcher/issues)
| | **Adorama** | **Amazon** | **Amazon (CA)** | **ASUS** | **B&H** | **Best Buy** | **Best Buy (CA)** | **EVGA** | **Micro Center** | **Newegg** | **Newegg (CA)** | **Nvidia** | **Office Depot** | **Zotac** |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| **3070**| | | | | | | | | | | | | | |
| **3080** | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` |
| **3090** | | | | | | `✔` | `✔` | | | `✔` | `✔` | `✔` | | |
## Installation and prerequisites
@@ -42,30 +42,136 @@ Linux, macOS, and Windows are all capable operating systems.
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 `nvidia-snatcher` running!
- Download [Node.js 14](https://nodejs.org/en/)
- Download [git](https://git-scm.com/)
- Clone this project `https://github.com/jef/nvidia-snatcher.git`
- Run `npm install`
- Edit the `.env` file to your liking
- More on this in [customization](#Customization)
- Run `npm run start`
### Quick overview
Then watch the magic happen!
- [Node.js 14](https://nodejs.org/en/)
- [git](https://git-scm.com/)
- Clone this project `git clone https://github.com/jef/nvidia-snatcher.git`
- 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/nvidia-snatcher/wiki) if you need more help with installation.
### Customization
There is not much to configure (as of now), but there are some options that you can choose to utilize.
To customize `nvidia-snatcher`, make a copy of `.env-example` as `.env` and make any changes to your liking. _All environment variables are **optional**._
First, you're going to need to copy the `.env.example` to `.env`. The current options are:
Here is a list of variables that you can use to customize your newly copied `.env` file:
| **Environment variable** | **Description** |
|:---:|:---:|
| `EMAIL_USERNAME` | Gmail address; e.g. `jensen.robbed.us@gmail.com` |
| `EMAIL_PASSWORD` | Gmail password; see below if you have MFA |
| `STORES` | List of stores you want to be scraped; optional, default: `nvidia` |
| **Environment variable** | **Description** | **Notes** |
|:---:|---|---|
| `BROWSER_TRUSTED` | Skip Chromium Sandbox | Useful for containerized environments, default: `false` |
| `DESKTOP_NOTIFICATIONS` | Display desktop notifications using [node-notifier](https://www.npmjs.com/package/node-notifier) | Default: `false` |
| `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 |
| `EMAIL_USERNAME` | Gmail address | E.g.: `jensen.robbed.us@gmail.com` |
| `EMAIL_PASSWORD` | Gmail password | See below if you have MFA |
| `HEADLESS` | Puppeteer to run headless or not | Debugging related, default: `true` |
| `IN_STOCK_WAIT_TIME` | Time to wait between requests to the same store if it has cards in stock | In seconds, default: `0` |
| `LOG_LEVEL` | [Logging levels](https://github.com/winstonjs/winston#logging-levels) | Debugging related, default: `info` |
| `MICROCENTER_LOCATION` | Specific MicroCenter location to search | Default : `web` |
| `OPEN_BROWSER` | Toggle for whether or not the browser should open when item is found | Default: `true` |
| `PAGE_TIMEOUT` | Navigation Timeout in milliseconds | `0` for infinite, default: `30000` |
| `PHONE_NUMBER` | 10 digit phone number | E.g.: `1234567890`, email configuration required |
| `PHONE_CARRIER` | [Supported carriers](#supported-carriers) for SMS | Email configuration required |
| `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/) |
| `PUSHBULLET` | PushBullet API key | Generate at https://www.pushbullet.com/#settings/account | |
| `PUSHOVER_TOKEN` | Pushover access token | Generate at https://pushover.net/apps/build | |
| `PUSHOVER_USER` | Pushover username | |
| `PAGE_SLEEP_MIN` | Minimum sleep time between queries of the same store | In milliseconds, default: `5000` |
| `PAGE_SLEEP_MAX` | Maximum sleep time between queries of the same store | In milliseconds, default: `10000` |
| `SCREENSHOT` | Capture screenshot of page if a card is found | Default: `true` |
| `SHOW_ONLY_BRANDS` | Filter to show specified brands | Comma separated, e.g.: `evga,zotac` |
| `SHOW_ONLY_MODELS` | Filter to show specified models | Comma separated, e.g.: `founders edition,rog strix` |
| `SHOW_ONLY_SERIES` | Filter to show specified series | Comma separated, e.g.: `3080` |
| `SLACK_CHANNEL` | Slack channel for posting | E.g.: `update`, no need for `#` |
| `SLACK_TOKEN` | Slack API token | |
| `STORES` | [Supported stores](#supported-stores) you want to be scraped | Comma separated, default: `nvidia` |
| `COUNTRY` | [Supported country](#supported-countries) you want to be scraped | Currently only used by Nvidia, default: `usa` |
| `SCREENSHOT` | Capture screenshot of page if a card is found | Default: `true` |
| `TELEGRAM_ACCESS_TOKEN` | Telegram access token | |
| `TELEGRAM_CHAT_ID` | Telegram chat ID | |
| `USER_AGENT` | Custom User-Agent header for HTTP requests | Default: `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36` |
| `TWITTER_CONSUMER_KEY` | Twitter Consumer Key | Generate all Twitter keys at: https://developer.twitter.com/ |
| `TWITTER_CONSUMER_SECRET` | Twitter Consumer Secret | |
| `TWITTER_ACCESS_TOKEN_KEY` | Twitter Token Key | |
| `TWITTER_ACCESS_TOKEN_SECRET` | Twitter Token Secret | |
| `TWITTER_TWEET_TAGS` | Optional list of hashtags to append to the tweet message | E.g.: `#nvidia #nvidiastock` |
> :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.
> :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: You can test your notification configuration by running `npm run test:notification`.
#### Supported stores
| **Stores** | **Environment variable** |
|:---:|:---:|
| Adorama | `adorama`|
| Amazon | `amazon`|
| Amazon (CA) | `amazon-ca`|
| Amazon (DE) | `amazon-de`|
| ASUS | `asus` |
| B&H | `bandh`|
| Best Buy | `bestbuy`|
| Best Buy (CA) | `bestbuy-ca`|
| EVGA | `evga`|
| EVGA (EU) | `evga-eu`|
| Micro Center | `microcenter`|
| Newegg | `newegg`|
| Newegg (CA) | `newegg-ca`|
| Nvidia | `nvidia`|
| Nvidia (API) | `nvidia-api`|
| Office Depot | `officedepot`|
| Zotac | `zotac`|
#### Supported carriers
| **Carrier** | **Environment variable** | **Notes** |
|:---:|:---:|:---:|
| AT&T | `att`| |
| 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`| |
#### Supported countries
| **Country** | **Nvidia.com (3080 FE)** | **Nvidia.com (3090 FE)** | **Notes** |
|:---:|:---:|:---:|:---:|
| austria | `✔` | | |
| belgium | `✔` | | Nvidia supports debug |
| canada | `✔` | | |
| czechia | `✔` | | |
| denmark | `✔` | | |
| finland | `✔` | | |
| france | `✔` | | |
| germany | `✔` | | |
| great_britain | `✔` | | |
| ireland | `✔` | | |
| italy | `✔` | | |
| luxembourg | `✔` | | Nvidia supports debug |
| netherlands | `✔` | | Nvidia supports debug |
| poland | `✔` | | |
| portugal | `✔` | | |
| russia | | | Missing all IDs |
| spain | `✔` | | |
| sweden | `✔` | | |
| usa | `✔` | | Nvidia supports debug |
## 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.
@@ -78,6 +184,10 @@ First, you're going to need to copy the `.env.example` to `.env`. The current op
**Q: I got a problem and need help!** File an [issue](https://github.com/jef/nvidia-snatcher/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/nvidia-snatcher/wiki/Troubleshoot:-General:-Getting-the-latest-code)
**Q: Why don't my notifications work?** There are probably an [issue](https://github.com/jef/nvidia-snatcher/issues?q=is%3Aissue+sort%3Aupdated-desc+sound+is%3Aclosed) [that] has [already](https://github.com/jef/nvidia-snatcher/issues/182) [been](https://github.com/jef/nvidia-snatcher/issues/116) [resolved](https://github.com/jef/nvidia-snatcher/issues/155)
**Q: I'd love to contribute, how do I do that?** Make a [pull request](https://github.com/jef/nvidia-snatcher/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc)! All contributions are welcome.
**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/nvidia-snatcher/issues/11).
@@ -89,7 +199,6 @@ Thanks to the great contributors that make this project possible
Special shout to initial developers:
- [@andirew](https://github.com/andirew)
- [@davidlbowman](https://github.com/davidlbowman)
- [@fuckingrobot](https://github.com/fuckingrobot)
- [@ioncaza](https://github.com/IonCaza)
- [@malbert69](https://github.com/malbert69)
Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

+1468 -132
View File
File diff suppressed because it is too large Load Diff
+33 -4
View File
@@ -1,13 +1,14 @@
{
"name": "nvidia-snatcher",
"version": "1.0.0",
"version": "1.5.0",
"description": "🔮 For all your Nvidia needs",
"main": "src/index.ts",
"scripts": {
"build": "rimraf ./build && tsc",
"lint": "xo",
"lint:fix": "xo --fix",
"start": "npm run build && node build/index.js"
"start": "npm run build && node build/index.js",
"test:notification": "npm run build && node build/__test__/notification-test.js"
},
"repository": {
"type": "git",
@@ -21,18 +22,46 @@
},
"homepage": "https://github.com/jef/nvidia-snatcher#readme",
"dependencies": {
"chalk": "^4.1.0",
"dotenv": "^8.2.0",
"messaging-api-telegram": "^1.0.1",
"node-notifier": "^8.0.0",
"nodemailer": "^6.4.11",
"open": "^7.2.1",
"puppeteer": "^5.3.0",
"puppeteer": "^5.3.1",
"puppeteer-extra": "^3.1.15",
"puppeteer-extra-plugin-adblocker": "^2.11.6",
"puppeteer-extra-plugin-stealth": "^2.6.1",
"pushbullet": "^2.4.0",
"pushover-notifications": "^1.2.2",
"twitter": "^1.7.1",
"winston": "^3.3.3"
},
"devDependencies": {
"@types/node": "^14.11.1",
"@slack/web-api": "^5.12.0",
"@types/async": "^3.2.3",
"@types/node": "^14.11.2",
"@types/node-notifier": "^8.0.0",
"@types/nodemailer": "^6.4.0",
"@types/puppeteer": "^3.0.2",
"@types/twitter": "^1.7.0",
"discord-webhook-node": "^1.1.8",
"husky": "^4.3.0",
"play-sound": "^1.1.3",
"rimraf": "^3.0.2",
"typescript": "^4.0.2",
"xo": "^0.33.1"
},
"xo": {
"rules": {
"sort-imports": "error",
"sort-keys": "error",
"sort-vars": "error"
}
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
}
}
+26
View File
@@ -0,0 +1,26 @@
import {Link, Store} from '../store/model';
import {sendNotification} from '../notification';
const link: Link = {
brand: 'test:brand',
cartUrl: 'https://www.example.com/cartUrl',
model: 'test:model',
series: 'test:series',
url: 'https://www.example.com/url'
};
const store: Store = {
labels: {
inStock: {
container: 'test:container',
text: ['test:text']
}
},
links: [link],
name: 'test:name'
};
/**
* Send test email.
*/
sendNotification(link, store);
+11
View File
@@ -0,0 +1,11 @@
import {Page} from 'puppeteer';
import {PuppeteerExtraPluginAdblocker} from 'puppeteer-extra-plugin-adblocker';
export const adBlocker = new PuppeteerExtraPluginAdblocker({
blockTrackers: true
});
export async function disableBlockerInPage(page: Page) {
const blockerObject = await adBlocker.getBlocker();
await blockerObject.disableBlockingInPage(page);
}
+12
View File
@@ -0,0 +1,12 @@
import chalk from 'chalk';
export const banner = chalk.green.bold(`
$$\\ $$\\ $$\\ $$\\ $$\\
\\__| $$ |\\__| $$ | $$ |
$$$$$$$\\ $$\\ $$\\ $$\\ $$$$$$$ |$$\\ $$$$$$\\ $$$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\ $$$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\
$$ __$$\\\\$$\\ $$ |$$ |$$ __$$ |$$ | \\____$$\\ $$$$$$\\ $$ _____|$$ __$$\\ \\____$$\\\\_$$ _| $$ _____|$$ __$$\\ $$ __$$\\ $$ __$$\\
$$ | $$ |\\$$\\$$ / $$ |$$ / $$ |$$ | $$$$$$$ |\\______|\\$$$$$$\\ $$ | $$ | $$$$$$$ | $$ | $$ / $$ | $$ |$$$$$$$$ |$$ | \\__|
$$ | $$ | \\$$$ / $$ |$$ | $$ |$$ |$$ __$$ | \\____$$\\ $$ | $$ |$$ __$$ | $$ |$$\\ $$ | $$ | $$ |$$ ____|$$ |
$$ | $$ | \\$ / $$ |\\$$$$$$$ |$$ |\\$$$$$$$ | $$$$$$$ |$$ | $$ |\\$$$$$$$ | \\$$$$ |\\$$$$$$$\\ $$ | $$ |\\$$$$$$$\\ $$ |
\\__| \\__| \\_/ \\__| \\_______|\\__| \\_______| \\_______/ \\__| \\__| \\_______| \\____/ \\_______|\\__| \\__| \\_______|\\__|
`);
+119 -13
View File
@@ -1,29 +1,135 @@
import {resolve} from 'path';
import {banner} from './banner';
console.log(banner);
import {config} from 'dotenv';
import path from 'path';
config({path: resolve(__dirname, '../.env')});
config({path: path.resolve(__dirname, '../.env')});
const email = {
username: process.env.EMAIL_USERNAME,
password: process.env.EMAIL_PASSWORD
/**
* Returns environment variable, given array, or default array.
*
* @param environment Interested environment variable.
* @param array Default array. If not set, is `[]`.
*/
function envOrArray(environment: string | undefined, array?: string[]): string[] {
return environment ? environment.split(',') : (array ?? []);
}
/**
* Returns environment variable, given boolean, or default boolean.
*
* @param environment Interested environment variable.
* @param boolean Default boolean. If not set, is `true`.
*/
function envOrBoolean(environment: string | undefined, boolean?: boolean): boolean {
return environment ? environment === 'true' : (boolean ?? true);
}
/**
* Returns environment variable, given string, or default string.
*
* @param environment Interested environment variable.
* @param string Default string. If not set, is `''`.
*/
function envOrString(environment: string | undefined, string?: string): string {
return environment ? environment : (string ?? '');
}
/**
* Returns environment variable, given number, or default number.
*
* @param environment Interested environment variable.
* @param number Default number. If not set, is `0`.
*/
function envOrNumber(environment: string | undefined, number?: number): number {
return Number(environment ?? (number ?? 0));
}
const browser = {
isHeadless: envOrBoolean(process.env.HEADLESS),
isTrusted: envOrBoolean(process.env.BROWSER_TRUSTED, false),
maxSleep: envOrNumber(process.env.PAGE_SLEEP_MAX, 10000),
minSleep: envOrNumber(process.env.PAGE_SLEEP_MIN, 5000),
open: envOrBoolean(process.env.OPEN_BROWSER)
};
const logLevel = process.env.LOG_LEVEL ?? 'info';
const notifications = {
email: email.username && email.password
desktop: process.env.DESKTOP_NOTIFICATIONS === 'true',
discord: {
notifyGroup: envOrArray(process.env.DISCORD_NOTIFY_GROUP),
webHookUrl: envOrArray(process.env.DISCORD_WEB_HOOK)
},
email: {
password: envOrString(process.env.EMAIL_PASSWORD),
username: envOrString(process.env.EMAIL_USERNAME)
},
phone: {
availableCarriers: new Map([
['att', 'txt.att.net'],
['bell', 'txt.bell.ca'],
['fido', 'fido.ca'],
['google', 'msg.fi.google.com'],
['koodo', 'msg.koodomobile.com'],
['mint', 'mailmymobile.net'],
['rogers', 'pcs.rogers.com'],
['sprint', 'messaging.sprintpcs.com'],
['telus', 'msg.telus.com'],
['tmobile', 'tmomail.net'],
['verizon', 'vtext.com'],
['virgin', 'vmobl.com'],
['virgin-ca', 'vmobile.ca']
]),
carrier: envOrString(process.env.PHONE_CARRIER),
number: envOrString(process.env.PHONE_NUMBER)
},
playSound: envOrString(process.env.PLAY_SOUND),
pushBulletApiKey: envOrString(process.env.PUSHBULLET),
pushover: {
token: envOrString(process.env.PUSHOVER_TOKEN),
username: envOrString(process.env.PUSHOVER_USER)
},
slack: {
channel: envOrString(process.env.SLACK_CHANNEL),
token: envOrString(process.env.SLACK_TOKEN)
},
telegram: {
accessToken: envOrString(process.env.TELEGRAM_ACCESS_TOKEN),
chatId: envOrString(process.env.TELEGRAM_CHAT_ID)
},
twitter: {
accessTokenKey: envOrString(process.env.TWITTER_ACCESS_TOKEN_KEY),
accessTokenSecret: envOrString(process.env.TWITTER_ACCESS_TOKEN_SECRET),
consumerKey: envOrString(process.env.TWITTER_CONSUMER_KEY),
consumerSecret: envOrString(process.env.TWITTER_CONSUMER_SECRET),
tweetTags: envOrString(process.env.TWITTER_TWEET_TAGS)
}
};
const page = {
height: 1920,
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
width: 1080
height: 1080,
inStockWaitTime: envOrNumber(process.env.IN_STOCK_WAIT_TIME),
navigationTimeout: envOrNumber(process.env.PAGE_TIMEOUT, 30000),
screenshot: envOrBoolean(process.env.SCREENSHOT),
userAgent: envOrString(process.env.USER_AGENT, 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'),
width: 1920
};
const stores = process.env.STORES ?? 'nvidia';
const store = {
country: envOrString(process.env.COUNTRY, 'usa'),
microCenterLocation: envOrString(process.env.MICROCENTER_LOCATION, 'web'),
showOnlyBrands: envOrArray(process.env.SHOW_ONLY_BRANDS),
showOnlyModels: envOrArray(process.env.SHOW_ONLY_MODELS),
showOnlySeries: envOrArray(process.env.SHOW_ONLY_SERIES, ['3070', '3080', '3090']),
stores: envOrArray(process.env.STORES, ['nvidia'])
};
export const Config = {
email,
browser,
logLevel,
notifications,
page,
rateLimitTimeout: 5000,
stores
store
};
+31 -68
View File
@@ -1,86 +1,50 @@
import {Config} from './config';
import {Store, Stores} from './store';
import puppeteer from 'puppeteer';
import open from 'open';
import sendNotification from './notification';
import {Logger} from './logger';
import {Stores} from './store/model';
import {adBlocker} from './adblocker';
import {getSleepTime} from './util';
import puppeteer from 'puppeteer-extra';
import stealthPlugin from 'puppeteer-extra-plugin-stealth';
import {tryLookupAndLoop} from './store';
puppeteer.use(stealthPlugin());
puppeteer.use(adBlocker);
/**
* Starts the bot.
*/
async function main() {
const results = [];
for (const store of Stores) {
Logger.debug(store.links);
results.push(lookup(store));
if (Stores.length === 0) {
Logger.error('✖ no stores selected', Stores);
return;
}
await Promise.all(results);
const args: string[] = [];
Logger.info('↗ trying stores again');
setTimeout(main, Config.rateLimitTimeout);
}
// Skip Chromium Linux Sandbox
// https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#setting-up-chrome-linux-sandbox
if (Config.browser.isTrusted) {
args.push('--no-sandbox');
args.push('--disable-setuid-sandbox');
}
/**
* Responsible for looking up information about a each product within
* a `Store`. It's important that we ignore `no-await-in-loop` here
* because we don't want to get rate limited within the same store.
*
* @param store Vendor of graphics cards.
*/
async function lookup(store: Store) {
/* eslint-disable no-await-in-loop */
for (const link of store.links) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setUserAgent(Config.page.userAgent);
await page.setViewport({
const browser = await puppeteer.launch({
args,
defaultViewport: {
height: Config.page.height,
width: Config.page.width
});
},
headless: Config.browser.isHeadless
});
await page.goto(link.url, {waitUntil: 'networkidle0'});
const bodyHandle = await page.$('body');
const textContent = await page.evaluate(body => body.textContent, bodyHandle);
Logger.debug(textContent);
const graphicsCard = `${link.brand} ${link.model}`;
if (isOutOfStock(textContent, link.oosLabels)) {
Logger.info(`✖ [${store.name}] ${graphicsCard} is still out of stock`);
} else {
Logger.info(`🚀🚀🚀 [${store.name}] ${graphicsCard} IN STOCK 🚀🚀🚀`);
Logger.info(link.url);
Logger.debug(' saving screenshot');
await page.screenshot({path: `success-${Date.now()}.png`});
const givenUrl = store.cartUrl ? store.cartUrl : link.url;
await open(givenUrl);
sendNotification(givenUrl);
for (const store of Stores) {
Logger.debug(store.links);
if (store.setupAction !== undefined) {
store.setupAction(browser);
}
await browser.close();
setTimeout(tryLookupAndLoop, getSleepTime(), browser, store);
}
/* eslint-enable no-await-in-loop */
}
/**
* Checks if DOM has any out-of-stock related text.
*
* @param domText Complete DOM of website.
* @param oosLabels Out-of-stock labels.
*/
function isOutOfStock(domText: string, oosLabels: string[]) {
const domTextLowerCase = domText.toLowerCase();
let result = false;
for (const oosLabel of oosLabels) {
result = domTextLowerCase.includes(oosLabel.toLowerCase());
}
return result;
}
/**
@@ -89,7 +53,6 @@ function isOutOfStock(domText: string, oosLabels: string[]) {
try {
void main();
} catch (error) {
// Ignoring errors; more than likely due to rate limits
Logger.error(error);
Logger.error('✖ something bad happened, resetting nvidia-snatcher', error);
void main();
}
+47 -5
View File
@@ -1,15 +1,19 @@
import {Link, Store} from './store/model';
import winston, {format} from 'winston';
import {Config} from './config';
import chalk from 'chalk';
const prettyJson = format.printf(info => {
const timestamp = new Date().toLocaleTimeString();
if (typeof info.message === 'object') {
info.message = JSON.stringify(info.message, null, 4);
}
return `${info.level} :: ${info.message}`;
return chalk.grey(`[${timestamp}]`) + ` ${info.level} ` + chalk.grey('::') + ` ${info.message}`;
});
export const Logger = winston.createLogger({
level: process.env.LOG_LEVEL ?? 'info',
format: format.combine(
format.colorize(),
format.prettyPrint(),
@@ -17,7 +21,45 @@ export const Logger = winston.createLogger({
format.simple(),
prettyJson
),
transports: [
new winston.transports.Console({})
]
level: Config.logLevel,
transports: [new winston.transports.Console({})]
});
export const Print = {
captcha(link: Link, store: Store, color?: boolean): string {
if (color) {
return '✖ ' + buildProductString(link, store, true) + ' :: ' + chalk.yellow('CAPTCHA');
}
return `${buildProductString(link, store)} :: CAPTCHA`;
},
inStock(link: Link, store: Store, color?: boolean): string {
if (color) {
return chalk.green.bold(`🚀🚨 ${buildProductString(link, store, true)} :: IN STOCK 🚨🚀`);
}
return `🚀🚨 ${buildProductString(link, store)} :: IN STOCK 🚨🚀`;
},
outOfStock(link: Link, store: Store, color?: boolean): string {
if (color) {
return '✖ ' + buildProductString(link, store, true) + ' :: ' + chalk.red('OUT OF STOCK');
}
return `${buildProductString(link, store)} :: OUT OF STOCK`;
},
rateLimit(link: Link, store: Store, color?: boolean): string {
if (color) {
return '✖ ' + buildProductString(link, store, true) + ' :: ' + chalk.yellow('RATE LIMIT EXCEEDED');
}
return `${buildProductString(link, store)} :: RATE LIMIT EXCEEDED`;
}
};
function buildProductString(link: Link, store: Store, color?: boolean): string {
if (color) {
return chalk.cyan(`[${store.name}]`) + chalk.grey(` [${link.brand} (${link.series})] ${link.model}`);
}
return `[${store.name}] [${link.brand} (${link.series})] ${link.model}`;
}
+14
View File
@@ -0,0 +1,14 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import notifier from 'node-notifier';
export function sendDesktopNotification(link: Link, store: Store) {
(async () => {
notifier.notify({
message: link.cartUrl ? link.cartUrl : link.url,
title: Print.inStock(link, store)
});
Logger.info('✔ desktop notification sent');
})();
}
+38
View File
@@ -0,0 +1,38 @@
import {Link, Store} from '../store/model';
import {MessageBuilder, Webhook} from 'discord-webhook-node';
import {Config} from '../config';
import {Logger} from '../logger';
const hooks = Config.notifications.discord.webHookUrl;
const notifyGroup = Config.notifications.discord.notifyGroup;
export function sendDiscordMessage(link: Link, store: Store) {
(async () => {
try {
const embed = new MessageBuilder();
embed.setTitle('Stock Notification');
embed.addField('URL', link.cartUrl ? link.cartUrl : link.url, true);
embed.addField('Store', store.name, true);
embed.addField('Brand', link.brand, true);
embed.addField('Model', link.model, true);
if (notifyGroup) {
embed.setText(notifyGroup.join(' '));
}
embed.setColor(0x76B900);
embed.setTimestamp();
const promises = [];
for (const hook of hooks) {
promises.push(new Webhook(hook).send(embed));
}
await Promise.all(promises);
Logger.info('✔ discord message sent');
} catch (error) {
Logger.error('✖ couldn\'t send discord message', error);
}
})();
}
+25 -20
View File
@@ -1,33 +1,38 @@
import nodemailer from 'nodemailer';
import Mail from 'nodemailer/lib/mailer';
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import {Logger} from '../logger';
import Mail from 'nodemailer/lib/mailer';
import nodemailer from 'nodemailer';
const subject = 'NVIDIA - BUY NOW';
const email = Config.notifications.email;
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: Config.email.username,
pass: Config.email.password
}
pass: email.password,
user: email.username
},
service: 'gmail'
});
const mailOptions: Mail.Options = {
from: Config.email.username,
to: Config.email.username,
subject
};
export function sendEmail(link: Link, store: Store) {
const mailOptions: Mail.Options = {
attachments: link.screenshot ? [
{
filename: link.screenshot,
path: `./${link.screenshot}`
}
] : undefined,
from: email.username,
subject: Print.inStock(link, store),
text: link.cartUrl ? link.cartUrl : link.url,
to: email.username
};
export default function sendEmail(text: string) {
mailOptions.text = text;
transporter.sendMail(mailOptions, (error, info) => {
transporter.sendMail(mailOptions, error => {
if (error) {
Logger.error(error);
Logger.error('✖ couldn\'t send email', error);
} else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.info(`✔ email sent: ${info.response}`);
Logger.info('✔ email sent');
}
});
}
+1 -8
View File
@@ -1,8 +1 @@
import {Config} from '../config';
import sendEmail from './email';
export default function sendNotification(cartUrl: string) {
if (Config.notifications.email) {
sendEmail(cartUrl);
}
}
export * from './notification';
+75
View File
@@ -0,0 +1,75 @@
import {Link, Store} from '../store/model';
import {Config} from '../config';
import {Logger} from '../logger';
import {playSound} from './sound';
import {sendDesktopNotification} from './desktop';
import {sendDiscordMessage} from './discord';
import {sendEmail} from './email';
import {sendPushBulletNotification} from './pushbullet';
import {sendPushoverNotification} from './pushover';
import {sendSMS} from './sms';
import {sendSlackMessage} from './slack';
import {sendTelegramMessage} from './telegram';
import {sendTweet} from './twitter';
const notifications = Config.notifications;
export function sendNotification(link: Link, store: Store) {
if (notifications.email.username && notifications.email.password) {
Logger.debug('↗ sending email');
sendEmail(link, store);
}
if (notifications.phone.number) {
Logger.debug('↗ sending sms');
const carrier = notifications.phone.carrier;
if (carrier && notifications.phone.availableCarriers.has(carrier)) {
sendSMS(link, store);
}
}
if (notifications.playSound) {
Logger.debug('↗ playing sound');
playSound();
}
if (notifications.desktop) {
Logger.debug('↗ sending desktop notification');
sendDesktopNotification(link, store);
}
if (notifications.discord.webHookUrl) {
Logger.debug('↗ sending discord message');
sendDiscordMessage(link, store);
}
if (notifications.slack.channel && notifications.slack.token) {
Logger.debug('↗ sending slack message');
sendSlackMessage(link, store);
}
if (notifications.telegram.accessToken && notifications.telegram.chatId) {
Logger.debug('↗ sending telegram message');
sendTelegramMessage(link, store);
}
if (notifications.pushBulletApiKey) {
Logger.debug('↗ sending pushbullet message');
sendPushBulletNotification(link, store);
}
if (notifications.pushover.token && notifications.pushover.username) {
Logger.debug('↗ sending pushover message');
sendPushoverNotification(link, store);
}
if (
notifications.twitter.accessTokenKey &&
notifications.twitter.accessTokenSecret &&
notifications.twitter.consumerKey &&
notifications.twitter.consumerSecret
) {
Logger.debug('↗ sending twitter message');
sendTweet(link, store);
}
}
+22
View File
@@ -0,0 +1,22 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import PushBullet from 'pushbullet';
const pushBulletApiKey = Config.notifications.pushBulletApiKey;
export function sendPushBulletNotification(link: Link, store: Store) {
const pusher = new PushBullet(pushBulletApiKey);
pusher.note(
{},
Print.inStock(link, store),
link.cartUrl ? link.cartUrl : link.url,
(error: Error) => {
if (error) {
Logger.error('✖ couldn\'t send pushbullet message', error);
} else {
Logger.info('✔ pushbullet message sent');
}
});
}
+25
View File
@@ -0,0 +1,25 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import Push from 'pushover-notifications';
const pushover = Config.notifications.pushover;
const push = new Push({
token: pushover.token,
user: pushover.username
});
export function sendPushoverNotification(link: Link, store: Store) {
const message = {
message: link.cartUrl ? link.cartUrl : link.url,
title: Print.inStock(link, store)
};
push.send(message, (error: Error) => {
if (error) {
Logger.error('✖ couldn\'t send pushover message', error);
} else {
Logger.info('✔ pushover message sent');
}
});
}
+30
View File
@@ -0,0 +1,30 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import {WebClient} from '@slack/web-api';
const channel = Config.notifications.slack.channel;
const token = Config.notifications.slack.token;
const web = new WebClient(token);
export function sendSlackMessage(link: Link, store: Store) {
(async () => {
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
try {
const result = await web.chat.postMessage({
channel,
text: `${Print.inStock(link, store)}\n${givenUrl}`
});
if (!result.ok) {
Logger.error('✖ couldn\'t send slack message', result);
return;
}
Logger.info('✔ slack message sent');
} catch (error) {
Logger.error('✖ couldn\'t send slack message', error);
}
})();
}
+52
View File
@@ -0,0 +1,52 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import Mail from 'nodemailer/lib/mailer';
import nodemailer from 'nodemailer';
if (Config.notifications.phone.number && !Config.notifications.email.username) {
Logger.warn('✖ in order to recieve sms alerts, email notifications must also be configured');
}
const [email, phone] = [Config.notifications.email, Config.notifications.phone];
const transporter = nodemailer.createTransport({
auth: {
pass: email.password,
user: email.username
},
service: 'gmail'
});
export function sendSMS(link: Link, store: Store) {
const mailOptions: Mail.Options = {
attachments: link.screenshot ? [
{
filename: link.screenshot,
path: `./${link.screenshot}`
}
] : undefined,
from: email.username,
subject: Print.inStock(link, store),
text: link.cartUrl ? link.cartUrl : link.url,
to: generateAddress()
};
transporter.sendMail(mailOptions, error => {
if (error) {
Logger.error('✖ couldn\'t send sms', error);
} else {
Logger.info('✔ sms sent');
}
});
}
function generateAddress() {
const carrier = phone.carrier;
if (carrier && phone.availableCarriers.has(carrier)) {
return [phone.number, phone.availableCarriers.get(carrier)].join('@');
}
Logger.error('✖ unknown carrier', carrier);
}
+36
View File
@@ -0,0 +1,36 @@
import {Config} from '../config';
import {Logger} from '../logger';
import fs from 'fs';
import playerLib from 'play-sound';
let player: any;
if (Config.notifications.playSound) {
player = playerLib();
if (player.player === null) {
Logger.warn('✖ couldn\'t find sound player');
} else {
const playerName: string = player.player;
Logger.info(`✔ sound player found: ${playerName}`);
}
}
export function playSound() {
if (player.player !== null) {
fs.access(Config.notifications.playSound, fs.constants.F_OK, error => {
if (error) {
Logger.error(`✖ error opening sound file: ${error.message}`);
return;
}
player.play(Config.notifications.playSound, (error: Error) => {
if (error) {
Logger.error('✖ couldn\'t play sound', error);
}
Logger.info('✔ played sound');
});
});
}
}
+23
View File
@@ -0,0 +1,23 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import {TelegramClient} from 'messaging-api-telegram';
const telegram = Config.notifications.telegram;
const client = new TelegramClient({
accessToken: telegram.accessToken
});
export function sendTelegramMessage(link: Link, store: Store) {
(async () => {
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
try {
await client.sendMessage(telegram.chatId, `${Print.inStock(link, store)}\n${givenUrl}`);
Logger.info('✔ telegram message sent');
} catch (error) {
Logger.error('✖ couldn\'t send telegram message', error);
}
})();
}
+29
View File
@@ -0,0 +1,29 @@
import {Link, Store} from '../store/model';
import {Logger, Print} from '../logger';
import {Config} from '../config';
import Twitter from 'twitter';
const twitter = Config.notifications.twitter;
const client = new Twitter({
access_token_key: twitter.accessTokenKey,
access_token_secret: twitter.accessTokenSecret,
consumer_key: twitter.consumerKey,
consumer_secret: twitter.consumerSecret
});
export function sendTweet(link: Link, store: Store) {
let status = `${Print.inStock(link, store)}\n${link.cartUrl ? link.cartUrl : link.url}`;
if (twitter.tweetTags) {
status += `\n\n${twitter.tweetTags}`;
}
client.post('statuses/update', {status}, error => {
if (error) {
Logger.error('✖ couldn\'t send twitter notification', error);
} else {
Logger.info('✔ twitter notification sent');
}
});
}
-14
View File
@@ -1,14 +0,0 @@
import {Store} from './store';
export const BAndH: Store = {
cartUrl: '',
links: [
{
brand: 'gigabyte',
model: 'black',
url: 'https://www.bhphotovideo.com/c/product/1593333-REG/gigabyte_gv_n3080gaming_oc_10gd_geforce_rtx_3080_gaming.html?SID=s1600391647213ytuua52439',
oosLabels: ['notify when available']
}
],
name: 'bandh'
};
-20
View File
@@ -1,20 +0,0 @@
import {Store} from './store';
export const BestBuy: Store = {
cartUrl: '',
links: [
{
brand: 'nvidia',
model: 'founder edition',
url: 'https://www.bestbuy.com/site/nvidia-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-graphics-card-titanium-and-black/6429440.p?skuId=6429440',
oosLabels: ['sold out']
},
{
brand: 'gigabyte',
model: 'black',
url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3080-10g-gddr6x-pci-express-4-0-graphics-card-black/6430620.p?acampID=0&cmp=RMX&loc=Hatch&ref=198&skuId=6430620',
oosLabels: ['sold out']
}
],
name: 'bestbuy'
};
+62
View File
@@ -0,0 +1,62 @@
import {Config} from '../config';
import {Link} from './model';
/**
* Returns true if the brand should be checked for stock
*
* @param brand The brand of the GPU
*/
function filterBrand(brand: Link['brand']): boolean {
if (Config.store.showOnlyBrands.length === 0) {
return true;
}
return Config.store.showOnlyBrands.includes(brand);
}
/**
* Returns true if the model should be checked for stock
*
* @param model The model of the GPU
*/
function filterModel(model: Link['model']): boolean {
if (Config.store.showOnlyModels.length === 0) {
return true;
}
const sanitizedModel = model.replace(/\s/g, '');
for (const configModel of Config.store.showOnlyModels) {
const sanitizedConfigModel = configModel.replace(/\s/g, '');
if (sanitizedModel === sanitizedConfigModel) {
return true;
}
}
return false;
}
/**
* Returns true if the series should be checked for stock
*
* @param series The series of the GPU
*/
function filterSeries(series: Link['series']): boolean {
if (Config.store.showOnlySeries.length === 0) {
return true;
}
return Config.store.showOnlySeries.includes(series);
}
/**
* Returns true if the link should be checked for stock
*
* @param link The store link of the GPU
*/
export function filterStoreLink(link: Link): boolean {
return (
filterBrand(link.brand) &&
filterModel(link.model) &&
filterSeries(link.series)
);
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Checks if DOM has any related text.
*
* @param domText Complete DOM of website.
* @param searchLabels Search labels for a match.
*/
export function includesLabels(domText: string, searchLabels: string[]): boolean {
const domTextLowerCase = domText.toLowerCase();
return searchLabels.some(label => domTextLowerCase.includes(label));
}
+1 -32
View File
@@ -1,32 +1 @@
import {BestBuy} from './bestbuy';
import {BAndH} from './bandh';
import {NewEgg} from './newegg';
import {Nvidia} from './nvidia';
import {Config} from '../config';
const list = new Map([
['bestbuy', BestBuy],
['bandh', BAndH],
['newegg', NewEgg],
['nvidia', Nvidia]
]);
if (!Config.stores.toLowerCase().includes('bestbuy')) {
list.delete('bestbuy');
}
if (!Config.stores.toLowerCase().includes('bandh')) {
list.delete('bandh');
}
if (!Config.stores.toLowerCase().includes('newegg')) {
list.delete('newegg');
}
if (!Config.stores.toLowerCase().includes('nvidia')) {
list.delete('nvidia');
}
export const Stores = Array.from(list.values());
export * from './store';
export * from './lookup';
+134
View File
@@ -0,0 +1,134 @@
import {Browser, Page, Response} from 'puppeteer';
import {Link, Store} from './model';
import {Logger, Print} from '../logger';
import {closePage, delay, getSleepTime} from '../util';
import {Config} from '../config';
import {filterStoreLink} from './filter';
import {includesLabels} from './includes-labels';
import open from 'open';
import {sendNotification} from '../notification';
const inStock: Record<string, boolean> = {};
/**
* Responsible for looking up information about a each product within
* a `Store`. It's important that we ignore `no-await-in-loop` here
* because we don't want to get rate limited within the same store.
*
* @param browser Puppeteer browser.
* @param store Vendor of graphics cards.
*/
async function lookup(browser: Browser, store: Store) {
/* eslint-disable no-await-in-loop */
for (const link of store.links) {
if (!filterStoreLink(link)) {
continue;
}
const page = await browser.newPage();
page.setDefaultNavigationTimeout(Config.page.navigationTimeout);
await page.setUserAgent(Config.page.userAgent);
try {
await lookupCard(browser, store, page, link);
} catch (error) {
Logger.error(`✖ [${store.name}] ${link.brand} ${link.model} - ${error.message as string}`);
}
await closePage(page);
}
/* eslint-enable no-await-in-loop */
}
async function lookupCard(browser: Browser, store: Store, page: Page, link: Link) {
const givenWaitFor = store.waitUntil ? store.waitUntil : 'networkidle0';
const response: Response | null = await page.goto(link.url, {waitUntil: givenWaitFor});
if (await lookupCardInStock(store, page)) {
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
Logger.info(`${Print.inStock(link, store, true)}\n${givenUrl}`);
if (Config.browser.open) {
if (link.openCartAction === undefined) {
await open(givenUrl);
} else {
await link.openCartAction(browser);
}
}
sendNotification(link, store);
if (Config.page.inStockWaitTime) {
inStock[store.name] = true;
setTimeout(() => {
inStock[store.name] = false;
}, 1000 * Config.page.inStockWaitTime);
}
if (Config.page.screenshot) {
Logger.debug(' saving screenshot');
link.screenshot = `success-${Date.now()}.png`;
await page.screenshot({path: link.screenshot});
}
return;
}
if (await lookupPageHasCaptcha(store, page)) {
Logger.warn(Print.captcha(link, store, true));
await delay(getSleepTime());
return;
}
if (response && response.status() === 429) {
Logger.warn(Print.rateLimit(link, store, true));
return;
}
Logger.info(Print.outOfStock(link, store, true));
}
async function lookupCardInStock(store: Store, page: Page) {
const stockHandle = await page.$(store.labels.inStock.container);
const visible = await page.evaluate(element => element && element.offsetWidth > 0 && element.offsetHeight > 0, stockHandle);
if (!visible) {
return false;
}
const stockContent = await page.evaluate(element => element.outerHTML, stockHandle);
Logger.debug(stockContent);
return includesLabels(stockContent, store.labels.inStock.text);
}
async function lookupPageHasCaptcha(store: Store, page: Page) {
if (!store.labels.captcha) {
return false;
}
const captchaHandle = await page.$(store.labels.captcha.container);
const captchaContent = await page.evaluate(element => element.textContent, captchaHandle);
return includesLabels(captchaContent, store.labels.captcha.text);
}
export async function tryLookupAndLoop(browser: Browser, store: Store) {
Logger.debug(`[${store.name}] Starting lookup...`);
try {
if (Config.page.inStockWaitTime && inStock[store.name]) {
Logger.info(`[${store.name}] Has stock, waiting before trying to lookup again...`);
} else {
await lookup(browser, store);
}
} catch (error) {
Logger.error(error);
}
const sleepTime = getSleepTime();
Logger.debug(`[${store.name}] Lookup done, next one in ${sleepTime} ms`);
setTimeout(tryLookupAndLoop, sleepTime, browser, store);
}
+77
View File
@@ -0,0 +1,77 @@
import {Store} from './store';
export const Adorama: Store = {
labels: {
captcha: {
container: 'body',
text: ['please verify you are a human']
},
inStock: {
container: '.buy-section.purchase',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.adorama.com/ev08gp43067k.html'
},
{
brand: 'pny',
model: 'xlr8',
series: '3080',
url: 'https://www.adorama.com/pnv301tfxmpb.html'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: 'https://www.adorama.com/msig380gxt1.html'
},
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url: 'https://www.adorama.com/ev10g53897kr.html'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.adorama.com/ev10g53885kr.html'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url: 'https://www.adorama.com/ev10g53895kr.html'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://www.adorama.com/ev10g53883kr.html'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://www.adorama.com/ev10g53881kr.html'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.adorama.com/msig38v3x10c.html'
},
{
brand: 'pny',
model: 'xlr8 rbg',
series: '3080',
url: 'https://www.adorama.com/png30801tfxb.html'
}
],
name: 'adorama'
};
+89
View File
@@ -0,0 +1,89 @@
import {Store} from './store';
export const AmazonCa: Store = {
labels: {
captcha: {
container: 'body',
text: ['enter the characters you see below']
},
inStock: {
container: '#desktop_buybox',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.amazon.ca/dp/B07PBLD2MX'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR7SV3M'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR3DPGW'
},
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR3Y5GQ'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR55YB5'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR4RJ3Q'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR6FMF3'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HJTH61J'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HJS2JLJ'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HHDP9DW'
},
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HH5WF97'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.amazon.ca/dp/B08HR5SXPS'
}
],
name: 'amazon-ca'
};
+101
View File
@@ -0,0 +1,101 @@
import {Store} from './store';
export const AmazonDe: Store = {
labels: {
captcha: {
container: 'body',
text: ['geben sie die unten angezeigten zeichen ein']
},
inStock: {
container: '#desktop_buybox',
text: ['in den einkaufswagen']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.amazon.com/dp/B07MQ36Z6L'
},
{
brand: 'pny',
model: 'xlr8',
series: '3080',
url: 'https://www.amazon.de/dp/B08HBTJMLJ'
},
{
brand: 'pny',
model: 'xlr8-rgb',
series: '3080',
url: 'https://www.amazon.de/dp/B08HBR7QBM'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: 'https://www.amazon.de/dp/B08HM4V2DH'
},
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url: 'https://www.amazon.de/dp/B08HGYXP4C'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.amazon.de/dp/B08HJ9XFNM'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url: 'https://www.amazon.de/dp/B08HGBYWQ6'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://www.amazon.de/dp/B08HGLN78Q'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://www.amazon.de/dp/B08HH1BMQQ'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url: 'https://www.amazon.de/dp/B08HLZXHZY'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url: 'https://www.amazon.de/dp/B08HHZVZ3N'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: 'https://www.amazon.de/dp/B08HN4DSTC'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.amazon.de/dp/B08HM4M621'
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: 'https://www.amazon.de/dp/B08HR1NPPQ'
}
],
name: 'amazon-de'
};
+113
View File
@@ -0,0 +1,113 @@
import {Store} from './store';
export const Amazon: Store = {
labels: {
captcha: {
container: 'body',
text: ['enter the characters you see below']
},
inStock: {
container: '#desktop_buybox',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.amazon.com/dp/B07MQ36Z6L'
},
{
brand: 'pny',
model: 'xlr8',
series: '3080',
url: 'https://www.amazon.com/dp/B08HBR7QBM'
},
{
brand: 'pny',
model: 'xlr8 rgb',
series: '3080',
url: 'https://www.amazon.com/dp/B08HBTJMLJ'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR7SV3M'
},
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR3Y5GQ'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR55YB5'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR3DPGW'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR4RJ3Q'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR6FMF3'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url: 'https://www.amazon.com/dp/B08HJTH61J'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url: 'https://www.amazon.com/dp/B08HJS2JLJ'
},
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url: 'https://www.amazon.com/dp/B08HH5WF97'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: 'https://www.amazon.com/dp/B08HHDP9DW'
},
{
brand: 'asus',
model: 'strix',
series: '3080',
url: 'https://www.amazon.com/dp/B08J6F174Z'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.amazon.com/dp/B08HR5SXPS'
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: 'https://www.amazon.com/dp/B08HJNKT3P'
}
],
name: 'amazon'
};
+32
View File
@@ -0,0 +1,32 @@
import {Store} from './store';
export const Asus: Store = {
labels: {
inStock: {
container: '#item_add_cart',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://store.asus.com/us/item/202003AM280000002/'
},
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url: 'https://store.asus.com/us/item/202009AM160000001/'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: 'https://store.asus.com/us/item/202009AM150000004/'
}
],
name: 'asus'
};
+69
View File
@@ -0,0 +1,69 @@
import {Store} from './store';
export const BAndH: Store = {
labels: {
inStock: {
container: 'div[data-selenium="addToCartSection"]',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.bhphotovideo.com/c/product/1452927-REG/evga_06g_p4_2063_kr_geforce_rtx_2060_xc.html'
},
// TUF was removed from BH, not sure why so commenting out listing for now
// {
// brand: 'asus',
// model: 'tuf',
// series: '3080',
// url: 'https://www.bhphotovideo.com/c/product/1593649-REG/asus_tuf_rtx3080_10g_gaming_tuf_gaming_geforce_rtx.html'
// },
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url: 'https://www.bhphotovideo.com/c/product/1593333-REG/gigabyte_gv_n3080gaming_oc_10gd_geforce_rtx_3080_gaming.html'
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: 'https://www.bhphotovideo.com/c/product/1592969-REG/zotac_zt_a30800d_10p_gaming_geforce_rtx_3080.html'
},
// TUF was removed from BH, not sure why so commenting out listing for now
// {
// brand: 'asus',
// model: 'tuf oc',
// series: '3080',
// url: 'https://www.bhphotovideo.com/c/product/1593650-REG/asus_tuf_rtx3080_o10g_gaming_tuf_gaming_geforce_rtx.html'
// },
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: 'https://www.bhphotovideo.com/c/product/1593996-REG/msi_g3080gxt10_geforce_rtx_3080_gaming.html'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.bhphotovideo.com/c/product/1593997-REG/msi_g3080v3x10c_geforce_rtx_3080_ventus.html'
},
{
brand: 'msi',
model: 'gaming x trio - duplicate',
series: '3080',
url: 'https://www.bhphotovideo.com/c/product/1593645-REG/msi_geforce_rtx_3080_gaming.html'
},
{
brand: 'msi',
model: 'ventus 3x oc - duplicate',
series: '3080',
url: 'https://www.bhphotovideo.com/c/product/1593646-REG/msi_geforce_rtx_3080_ventus.html'
}
],
name: 'bandh'
};
+74
View File
@@ -0,0 +1,74 @@
import {Store} from './store';
export const BestBuyCa: Store = {
labels: {
inStock: {
container: '#root',
text: ['available online']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.bestbuy.ca/en-ca/product/msi-nvidia-geforce-rtx-2060-super-gaming-x-8gb-gddr6-video-card/14419420?intl=nosplash'
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: 'https://www.bestbuy.ca/en-ca/product/zotac-geforce-rtx-3080-trinity-10gb-gddr6x-video-card/14953249?intl=nosplash'
},
{
brand: 'msi',
model: 'ventus 3x',
series: '3080',
url: 'https://www.bestbuy.ca/en-ca/product/msi-nvidia-geforce-rtx-3080-ventus-3x-10gb-gddr6x-video-card/14950588?intl=nosplash'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.bestbuy.ca/en-ca/product/evga-geforce-rtx-3080-xc3-ultra-gaming-10gb-gddr6x-video-card/14961449?intl=nosplash'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: 'https://www.bestbuy.ca/en-ca/product/asus-tuf-gaming-geforce-rtx-3080-10gb-gddr6x-video-card/14953248?intl=nosplash'
},
{
brand: 'asus',
model: 'rog strix',
series: '3080',
url: 'https://www.bestbuy.ca/en-ca/product/asus-rog-strix-geforce-rtx-3080-10gb-gddr6x-video-card/14954116?intl=nosplash'
},
{
brand: 'zotac',
model: 'trinity',
series: '3090',
url: 'https://www.bestbuy.ca/en-ca/product/zotac-geforce-rtx-3090-trinity-24gb-gddr6x-video-card/14953250?intl=nosplash'
},
{
brand: 'asus',
model: 'tuf',
series: '3090',
url: 'https://www.bestbuy.ca/en-ca/product/asus-tuf-gaming-geforce-rtx-3090-24gb-gddr6x-video-card/14953247?intl=nosplash'
},
{
brand: 'asus',
model: 'rog strix',
series: '3090',
url: 'https://www.bestbuy.ca/en-ca/product/asus-rog-strix-geforce-rtx-3090-24gb-gddr6x-video-card/14954117?intl=nosplash'
},
{
brand: 'msi',
model: 'ventus 3x',
series: '3090',
url: 'https://www.bestbuy.ca/en-ca/product/msi-nvidia-geforce-rtx-3090-ventus-3x-oc-24gb-gddr6x-video-card/14966477?intl=nosplash'
}
],
name: 'bestbuy-ca',
waitUntil: 'domcontentloaded'
};
+104
View File
@@ -0,0 +1,104 @@
import {Store} from './store';
export const BestBuy: Store = {
labels: {
inStock: {
container: '.v-m-bottom-g',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.bestbuy.com/site/nvidia-geforce-rtx-2060-super-8gb-gddr6-pci-express-graphics-card-black-silver/6361329.p?skuId=6361329&intl=nosplash'
},
{
brand: 'nvidia',
cartUrl: 'https://api.bestbuy.com/click/-/6429440/cart',
model: 'founders edition',
series: '3080',
url: 'https://www.bestbuy.com/site/nvidia-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-graphics-card-titanium-and-black/6429440.p?skuId=6429440&intl=nosplash'
},
{
brand: 'asus',
cartUrl: 'https://api.bestbuy.com/click/-/6432445/cart',
model: 'rog strix',
series: '3080',
url: 'https://www.bestbuy.com/site/asus-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-strix-graphics-card-black/6432445.p?skuId=6432445&intl=nosplash'
},
{
brand: 'evga',
cartUrl: 'https://api.bestbuy.com/click/-/6432399/cart',
model: 'xc3 black',
series: '3080',
url: 'https://www.bestbuy.com/site/evga-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-graphics-card/6432399.p?skuId=6432399&intl=nosplash'
},
{
brand: 'evga',
cartUrl: 'https://api.bestbuy.com/click/-/6432400/cart',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.bestbuy.com/site/evga-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-graphics-card/6432400.p?skuId=6432400&intl=nosplash'
},
{
brand: 'gigabyte',
cartUrl: 'https://api.bestbuy.com/click/-/6430620/cart',
model: 'gaming oc',
series: '3080',
url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3080-10g-gddr6x-pci-express-4-0-graphics-card-black/6430620.p?acampID=0&cmp=RMX&loc=Hatch&ref=198&skuId=6430620&intl=nosplash'
},
{
brand: 'gigabyte',
cartUrl: 'https://api.bestbuy.com/click/-/6430621/cart',
model: 'eagle oc',
series: '3080',
url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3080-10g-gddr6x-pci-express-4-0-graphics-card-black/6430621.p?skuId=6430621&intl=nosplash'
},
{
brand: 'msi',
cartUrl: 'https://api.bestbuy.com/click/-/6430175/cart',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.bestbuy.com/site/msi-geforce-rtx-3080-ventus-3x-10g-oc-bv-gddr6x-pci-express-4-0-graphic-card-black-silver/6430175.p?skuId=6430175&intl=nosplash'
},
{
brand: 'nvidia',
model: 'founders edition',
series: '3090',
url: 'https://www.bestbuy.com/site/nvidia-geforce-rtx-3090-24gb-gddr6x-pci-express-4-0-graphics-card-titanium-and-black/6429434.p?skuId=6429434'
},
{
brand: 'asus',
model: 'rog strix',
series: '3090',
url: 'https://www.bestbuy.com/site/asus-geforce-rtx-3090-24gb-gddr6x-pci-express-4-0-strix-graphics-card-black/6432447.p?skuId=6432447'
},
{
brand: 'asus',
model: 'tuf',
series: '3090',
url: 'https://www.bestbuy.com/site/asus-tuf-rtx-3090-24gb-gddr6x-pci-express-4-0-graphics-card-black/6432446.p?skuId=6432446'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3090',
url: 'https://www.bestbuy.com/site/msi-geforce-rtx-3090-ventus-3x-24g-oc-bv-24gb-gddr6x-pci-express-4-0-graphics-card-black-silver/6430215.p?skuId=6430215'
},
{
brand: 'gigabyte',
model: 'gaming',
series: '3090',
url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3090-24g-gddr6x-pci-express-4-0-graphics-card-black/6430623.p?skuId=6430623'
},
{
brand: 'gigabyte',
model: 'eagle',
series: '3090',
url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3090-24g-gddr6x-pci-express-4-0-graphics-card-black/6430624.p?skuId=6430624'
}
],
name: 'bestbuy'
};
+37
View File
@@ -0,0 +1,37 @@
import {Store} from './store';
export const EvgaEu: Store = {
labels: {
inStock: {
container: '.product-buy-specs',
text: ['add to cart']
}
},
links: [
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://eu.evga.com/products/product.aspx?pn=10G-P5-3881-KR'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url: 'https://eu.evga.com/products/product.aspx?pn=10G-P5-3895-KR'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://eu.evga.com/products/product.aspx?pn=10G-P5-3883-KR'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://eu.evga.com/products/product.aspx?pn=10G-P5-3885-KR'
}
],
name: 'evga-eu'
};
+50
View File
@@ -0,0 +1,50 @@
import {Store} from './store';
export const Evga: Store = {
labels: {
inStock: {
container: '.product-buy-specs',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.evga.com/products/product.aspx?pn=06G-P4-2065-KR'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3881-KR'
},
{
brand: 'evga',
model: 'ftw3 ultra',
series: '3080',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3897-KR'
},
{
brand: 'evga',
model: 'ftw3',
series: '3080',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3895-KR'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3883-KR'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3885-KR'
}
],
name: 'evga'
};
+167
View File
@@ -0,0 +1,167 @@
import {Browser, Response} from 'puppeteer';
import {NvidiaRegionInfo, regionInfos} from '../nvidia-api';
import {Config} from '../../../config';
import {Link} from '../store';
import {Logger} from '../../../logger';
import open from 'open';
import {timestampUrlParameter} from '../../timestamp-url-parameter';
const nvidiaApiKey = '9485fa7b159e42edb08a83bde0d83dia';
function getRegionInfo(): NvidiaRegionInfo {
const country = Array.from(regionInfos.keys()).includes(Config.store.country) ? Config.store.country : 'usa';
const defaultRegionInfo: NvidiaRegionInfo = {drLocale: 'en_us', fe2060SuperId: 5379432500, fe3080Id: 5438481700, fe3090Id: null, nvidiaLocale: 'en_us'};
return regionInfos.get(country) ?? defaultRegionInfo;
}
function digitalRiverStockUrl(id: number, drLocale: string): string {
return `https://api.digitalriver.com/v1/shoppers/me/products/${id}/inventory-status?` +
`&apiKey=${nvidiaApiKey}` +
`&locale=${drLocale}` +
timestampUrlParameter();
}
interface NvidiaSessionTokenJSON {
access_token: string;
}
function nvidiaSessionUrl(nvidiaLocale: string): string {
return `https://store.nvidia.com/store/nvidia/SessionToken?format=json&locale=${nvidiaLocale}` +
`&apiKey=${nvidiaApiKey}` +
timestampUrlParameter();
}
function addToCartUrl(id: number, drLocale: string, token: string): string {
return 'https://api.digitalriver.com/v1/shoppers/me/carts/active/line-items?format=json&method=post' +
`&productId=${id}` +
`&token=${token}` +
'&quantity=1' +
`&locale=${drLocale}` +
timestampUrlParameter();
}
function checkoutUrl(drLocale: string, token: string): string {
return `https://api.digitalriver.com/v1/shoppers/me/carts/active/web-checkout?token=${token}&locale=${drLocale}`;
}
function fallbackCartUrl(nvidiaLocale: string): string {
return `https://www.nvidia.com/${nvidiaLocale}/shop/geforce?${timestampUrlParameter()}`;
}
export function generateSetupAction() {
return async (browser: Browser) => {
const {drLocale, nvidiaLocale} = getRegionInfo();
const page = await browser.newPage();
let response: Response | null;
try {
Logger.debug('creating cart/session token...');
response = await page.goto(nvidiaSessionUrl(nvidiaLocale), {waitUntil: 'networkidle0'});
if (response === null) {
throw new Error('NvidiaAccessTokenUnavailable');
}
const data = await response.json() as NvidiaSessionTokenJSON;
const accessToken = data.access_token;
const cartUrl = checkoutUrl(drLocale, accessToken);
Logger.debug(cartUrl);
if (Config.browser.open) {
Logger.info(' opening browser for user to login');
await open(cartUrl);
}
} catch (error) {
Logger.error('✖ [nvidia] cannot generate cart/session token, continuing without; auto "add to cart" may not work', error);
}
await page.close();
};
}
export function generateOpenCartAction(id: number, nvidiaLocale: string, drLocale: string, cardName: string) {
return async (browser: Browser) => {
const page = await browser.newPage();
Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, starting auto add to cart 🚀🚀🚀`);
let response: Response | null;
let cartUrl: string;
try {
Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, getting access token 🚀🚀🚀`);
response = await page.goto(nvidiaSessionUrl(nvidiaLocale), {waitUntil: 'networkidle0'});
if (response === null) {
throw new Error('NvidiaAccessTokenUnavailable');
}
const data = await response.json() as NvidiaSessionTokenJSON;
const accessToken = data.access_token;
Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, adding to cart 🚀🚀🚀`);
response = await page.goto(addToCartUrl(id, drLocale, accessToken), {waitUntil: 'networkidle0'});
Logger.info(`🚀🚀🚀 [nvidia] ${cardName}, opening checkout page 🚀🚀🚀`);
cartUrl = checkoutUrl(drLocale, accessToken);
Logger.info(cartUrl);
await open(cartUrl);
} catch (error) {
Logger.debug(error);
Logger.error(`✖ [nvidia] ${cardName} could not automatically add to cart, opening page`, error);
cartUrl = fallbackCartUrl(nvidiaLocale);
await open(cartUrl);
}
await page.close();
return cartUrl;
};
}
export function generateLinks(): Link[] {
const {drLocale, nvidiaLocale, fe3080Id, fe3090Id, fe2060SuperId} = getRegionInfo();
const links: Link[] = [];
if (fe2060SuperId) {
links.push({
brand: 'test:brand',
model: 'test:model',
openCartAction: generateOpenCartAction(fe2060SuperId, nvidiaLocale, drLocale, 'TEST CARD debug'),
series: 'test:series',
url: digitalRiverStockUrl(fe2060SuperId, drLocale)
});
}
if (fe3080Id) {
links.push({
brand: 'nvidia',
model: 'founders edition',
openCartAction: generateOpenCartAction(fe3080Id, nvidiaLocale, drLocale, 'nvidia founders edition 3080'),
series: '3080',
url: digitalRiverStockUrl(fe3080Id, drLocale)
});
}
if (fe3090Id) {
links.push({
brand: 'nvidia',
model: 'founders edition',
openCartAction: generateOpenCartAction(fe3090Id, nvidiaLocale, drLocale, 'nvidia founders edition 3090'),
series: '3090',
url: digitalRiverStockUrl(fe3090Id, drLocale)
});
}
return links;
}
+69
View File
@@ -0,0 +1,69 @@
import {Adorama} from './adorama';
import {Amazon} from './amazon';
import {AmazonCa} from './amazon-ca';
import {AmazonDe} from './amazon-de';
import {Asus} from './asus';
import {BAndH} from './bandh';
import {BestBuy} from './bestbuy';
import {BestBuyCa} from './bestbuy-ca';
import {Config} from '../../config';
import {Evga} from './evga';
import {EvgaEu} from './evga-eu';
import {Logger} from '../../logger';
import {MicroCenter} from './microcenter';
import {Newegg} from './newegg';
import {NeweggCa} from './newegg-ca';
import {Nvidia} from './nvidia';
import {NvidiaApi} from './nvidia-api';
import {OfficeDepot} from './officedepot';
import {Store} from './store';
import {Zotac} from './zotac';
const masterList = new Map([
[Adorama.name, Adorama],
[Amazon.name, Amazon],
[AmazonCa.name, AmazonCa],
[AmazonDe.name, AmazonDe],
[Asus.name, Asus],
[BAndH.name, BAndH],
[BestBuy.name, BestBuy],
[BestBuyCa.name, BestBuyCa],
[Evga.name, Evga],
[EvgaEu.name, EvgaEu],
[MicroCenter.name, MicroCenter],
[Newegg.name, Newegg],
[NeweggCa.name, NeweggCa],
[Nvidia.name, Nvidia],
[NvidiaApi.name, NvidiaApi],
[OfficeDepot.name, OfficeDepot],
[Zotac.name, Zotac]
]);
const list = new Map();
for (const name of Config.store.stores) {
if (masterList.has(name)) {
list.set(name, masterList.get(name));
} else {
const logString = `No store named ${name}, skipping.`;
Logger.warn(logString);
}
}
Logger.info(` selected stores: ${Array.from(list.keys()).join(', ')}`);
if (Config.store.showOnlyBrands.length > 0) {
Logger.info(` selected brands: ${Config.store.showOnlyBrands.join(', ')}`);
}
if (Config.store.showOnlyModels.length > 0) {
Logger.info(` selected models: ${Config.store.showOnlyModels.join(', ')}`);
}
if (Config.store.showOnlySeries.length > 0) {
Logger.info(` selected series: ${Config.store.showOnlySeries.join(', ')}`);
}
export const Stores = Array.from(list.values()) as Store[];
export * from './store';
+93
View File
@@ -0,0 +1,93 @@
import {Config} from '../../config';
import {Store} from './store';
const MicroCenterLocation = Config.store.microCenterLocation;
const microCenterLocationToId: Map<string, string> = new Map([
['web', '029'],
['brooklyn', '115'],
['brentwood', '095'],
['cambridge', '121'],
['chicago', '151'],
['columbus', '141'],
['dallas', '131'],
['devin', '181'],
['duluth', '065'],
['fairfax', '081'],
['flushing', '145'],
['houston', '155'],
['madison-heights', '055'],
['marietta', '041'],
['mayfiend-heights', '051'],
['north-jersey', '075'],
['overland-park', '191'],
['parkville', '125'],
['rockville', '085'],
['sharonville', '071'],
['st-davids', '061'],
['st-louis-park', '045'],
['tustin', '101'],
['westbury', '171'],
['westmont', '025'],
['yonkers', '105']
]);
let storeId: string;
if (microCenterLocationToId.get(MicroCenterLocation) === undefined) {
storeId = '029';
} else {
storeId = microCenterLocationToId.get(MicroCenterLocation)!;
}
export const MicroCenter: Store = {
labels: {
inStock: {
container: '#cart-options',
text: ['in stock']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: `https://www.microcenter.com/product/618433/evga-geforce-rtx-2060-ko-ultra-overclocked-dual-fan-6gb-gddr6-pcie-30-graphics-card/?storeid=${storeId}`
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: `https://www.microcenter.com/product/628344/evga-geforce-rtx-3080-xc3-ultra-gaming-triple-fan-10gb-gddr6x-pcie-40-graphics-card/?storeid=${storeId}`
},
{
brand: 'msi',
model: 'ventus 3x',
series: '3080',
url: `https://www.microcenter.com/product/628331/msi-geforce-rtx-3080-ventus-3x-overclocked-triple-fan-10gb-gddr6x-pcie-40-graphics-card/?storeid=${storeId}`
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: `https://www.microcenter.com/product/628303/asus-geforce-rtx-3080-tuf-gaming-triple-fan-10gb-gddr6x-pcie-40-graphics-card/?storeid=${storeId}`
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: `https://www.microcenter.com/product/628330/msi-geforce-rtx-3080-gaming-x-trio-triple-fan-10gb-gddr6x-pcie-40-graphics-card/?storeid=${storeId}`
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: `https://www.microcenter.com/product/628340/evga-geforce-rtx-3080-xc3-black-triple-fan-10gb-gddr6x-pcie-40-graphics-card/?storeid=${storeId}`
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: `https://www.microcenter.com/product/628607/zotac-geforce-rtx-3080-trinity-overclocked-triple-fan-10gb-gddr6x-pcie-40-graphics-card/?storeid=${storeId}`
}
],
name: 'microcenter'
};
+119
View File
@@ -0,0 +1,119 @@
import {Store} from './store';
export const NeweggCa: Store = {
labels: {
captcha: {
container: 'body',
text: ['are you a human?']
},
inStock: {
container: '#landingpage-cart .btn-primary span',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.newegg.ca/evga-geforce-rtx-2060-06g-p4-2066-kr/p/N82E16814487488'
},
{
brand: 'asus',
model: 'tuf',
series: '3080',
url: 'https://www.newegg.ca/asus-geforce-rtx-3080-tuf-rtx3080-10g-gaming/p/N82E16814126453'
},
{
brand: 'evga',
model: 'xc3 black',
series: '3080',
url: 'https://www.newegg.ca/evga-geforce-rtx-3080-10g-p5-3881-kr/p/N82E16814487522'
},
{
brand: 'evga',
model: 'xc3',
series: '3080',
url: 'https://www.newegg.ca/evga-geforce-rtx-3080-10g-p5-3883-kr/p/N82E16814487521'
},
{
brand: 'evga',
model: 'xc3 ultra',
series: '3080',
url: 'https://www.newegg.ca/evga-geforce-rtx-3080-10g-p5-3885-kr/p/N82E16814487520'
},
{
brand: 'msi',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.newegg.ca/msi-geforce-rtx-3080-rtx-3080-ventus-3x-10g-oc/p/N82E16814137598'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3080',
url: 'https://www.newegg.ca/msi-geforce-rtx-3080-rtx-3080-gaming-x-trio-10g/p/N82E16814137597'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3080',
url: 'https://www.newegg.ca/gigabyte-geforce-rtx-3080-gv-n3080gaming-oc-10gd/p/N82E16814932329'
},
{
brand: 'gigabyte',
model: 'eagle oc',
series: '3080',
url: 'https://www.newegg.ca/gigabyte-geforce-rtx-3080-gv-n3080eagle-oc-10gd/p/N82E16814932330'
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: 'https://www.newegg.ca/zotac-geforce-rtx-3080-zt-a30800d-10p/p/N82E16814500502'
},
{
brand: 'asus',
model: 'tuf oc',
series: '3080',
url: 'https://www.newegg.ca/asus-geforce-rtx-3080-tuf-rtx3080-o10g-gaming/p/N82E16814126452'
},
{
brand: 'msi',
model: 'gaming x trio',
series: '3090',
url: 'https://www.newegg.ca/msi-geforce-rtx-3090-rtx-3090-gaming-x-trio-24g/p/N82E16814137595'
},
{
brand: 'gigabyte',
model: 'gaming oc',
series: '3090',
url: 'https://www.newegg.ca/gigabyte-geforce-rtx-3090-gv-n3090gaming-oc-24gd/p/N82E16814932327'
},
{
brand: 'msi',
model: 'ventus 3x',
series: '3090',
url: 'https://www.newegg.ca/msi-geforce-rtx-3090-rtx-3090-ventus-3x-24g-oc/p/N82E16814137596'
},
{
brand: 'zotac',
model: 'trinity',
series: '3090',
url: 'https://www.newegg.ca/zotac-geforce-rtx-3090-zt-a30900d-10p/p/N82E16814500503'
},
{
brand: 'asus',
model: 'tuf',
series: '3090',
url: 'https://www.newegg.ca/asus-geforce-rtx-3090-tuf-rtx3090-o24g-gaming/p/N82E16814126454'
},
{
brand: 'asus',
model: 'rog strix',
series: '3090',
url: 'https://www.newegg.ca/asus-geforce-rtx-3090-rog-strix-rtx3090-o24g-gaming/p/N82E16814126456'
}
],
name: 'newegg-ca'
};
+185
View File
@@ -0,0 +1,185 @@
import {Store} from './store';
export const Newegg: Store = {
labels: {
captcha: {
container: 'body',
text: ['are you a human?']
},
inStock: {
container: '#landingpage-cart .btn-primary span',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.newegg.com/evga-geforce-rtx-2060-06g-p4-2066-kr/p/N82E16814487488'
},
{
brand: 'asus',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814126453',
model: 'tuf',
series: '3080',
url: 'https://www.newegg.com/asus-geforce-rtx-3080-tuf-rtx3080-10g-gaming/p/N82E16814126453'
},
{
brand: 'evga',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814487522',
model: 'xc3 black',
series: '3080',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3881-kr/p/N82E16814487522'
},
{
brand: 'evga',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814487521',
model: 'xc3',
series: '3080',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3883-kr/p/N82E16814487521'
},
// Removed from Newegg currently not available in US
// {
// brand: 'evga',
// cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814487520',
// model: 'xc3 ultra',
// series: '3080',
// url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3885-kr/p/N82E16814487520'
// },
{
brand: 'msi',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814137600',
model: 'ventus 3x',
series: '3080',
url: 'https://www.newegg.com/msi-geforce-rtx-3080-rtx-3080-ventus-3x-10g/p/N82E16814137600'
},
{
brand: 'msi',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814137598',
model: 'ventus 3x oc',
series: '3080',
url: 'https://www.newegg.com/msi-geforce-rtx-3080-rtx-3080-ventus-3x-10g-oc/p/N82E16814137598'
},
{
brand: 'msi',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814137597',
model: 'gaming x trio',
series: '3080',
url: 'https://www.newegg.com/msi-geforce-rtx-3080-rtx-3080-gaming-x-trio-10g/p/N82E16814137597'
},
{
brand: 'gigabyte',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814932329',
model: 'gaming oc',
series: '3080',
url: 'https://www.newegg.com/gigabyte-geforce-rtx-3080-gv-n3080gaming-oc-10gd/p/N82E16814932329'
},
{
brand: 'gigabyte',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814932330',
model: 'eagle oc',
series: '3080',
url: 'https://www.newegg.com/gigabyte-geforce-rtx-3080-gv-n3080eagle-oc-10gd/p/N82E16814932330'
},
{
brand: 'zotac',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814500502',
model: 'trinity',
series: '3080',
url: 'https://www.newegg.com/zotac-geforce-rtx-3080-zt-a30800d-10p/p/N82E16814500502'
},
{
brand: 'asus',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814126457',
model: 'rog strix',
series: '3080',
url: 'https://www.newegg.com/asus-geforce-rtx-3080-rog-strix-rtx3080-o10g-gaming/p/N82E16814126457'
},
{
brand: 'asus',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814126452',
model: 'tuf oc',
series: '3080',
url: 'https://www.newegg.com/asus-geforce-rtx-3080-tuf-rtx3080-o10g-gaming/p/N82E16814126452'
},
{
brand: 'zotac',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814500504',
model: 'trinity oc',
series: '3080',
url: 'https://www.newegg.com/zotac-geforce-rtx-3080-zt-t30800j-10p/p/N82E16814500504'
},
{
brand: 'asus',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=',
model: 'tuf',
series: '3090',
url: 'https://www.newegg.com/asus-geforce-rtx-3090-tuf-rtx3090-24g-gaming/p/N82E16814126455'
},
{
brand: 'msi',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814137595',
model: 'gaming x trio',
series: '3090',
url: 'https://www.newegg.com/msi-geforce-rtx-3090-rtx-3090-gaming-x-trio-24g/p/N82E16814137595'
},
{
brand: 'msi',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814137596',
model: 'ventus 3x oc',
series: '3090',
url: 'https://www.newegg.com/msi-geforce-rtx-3090-rtx-3090-ventus-3x-24g-oc/p/N82E16814137596'
},
{
brand: 'zotac',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814500503',
model: 'trinity',
series: '3090',
url: 'https://www.newegg.com/zotac-geforce-rtx-3090-zt-a30900d-10p/p/N82E16814500503'
},
{
brand: 'msi',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814137599',
model: 'ventus 3x',
series: '3090',
url: 'https://www.newegg.com/msi-geforce-rtx-3090-rtx-3090-ventus-3x-24g/p/N82E16814137599'
},
{
brand: 'evga',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814487525',
model: 'ftw3 gaming',
series: '3090',
url: 'https://www.newegg.com/evga-geforce-rtx-3090-24g-p5-3985-kr/p/N82E16814487525'
},
{
brand: 'evga',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814487524',
model: 'xc3 ultra gaming',
series: '3090',
url: 'https://www.newegg.com/evga-geforce-rtx-3090-24g-p5-3975-kr/p/N82E16814487524'
},
{
brand: 'evga',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814487526',
model: 'ftw3 ultra gaming',
series: '3090',
url: 'https://www.newegg.com/evga-geforce-rtx-3090-24g-p5-3987-kr/p/N82E16814487526'
},
{
brand: 'gigabyte',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814932327',
model: 'gaming',
series: '3090',
url: 'https://www.newegg.com/gigabyte-geforce-rtx-3090-gv-n3090gaming-oc-24gd/p/N82E16814932327'
},
{
brand: 'gigabyte',
cartUrl: 'https://secure.newegg.com/Shopping/AddtoCart.aspx?Submit=ADD&ItemList=N82E16814932328',
model: 'eagle',
series: '3090',
url: 'https://www.newegg.com/gigabyte-geforce-rtx-3090-gv-n3090eagle-oc-24gd/p/N82E16814932328'
}
],
name: 'newegg'
};
+46
View File
@@ -0,0 +1,46 @@
import {generateLinks, generateSetupAction} from './helpers/nvidia';
import {Store} from './store';
// Region/country set by config file, silently ignores null / missing values and defaults to usa
export interface NvidiaRegionInfo {
drLocale: string;
fe3080Id: number | null;
fe3090Id: number | null;
fe2060SuperId: number | null;
nvidiaLocale: string;
}
export const regionInfos = new Map<string, NvidiaRegionInfo>([
['austria', {drLocale: 'de_de', fe2060SuperId: null, fe3080Id: 5440853700, fe3090Id: null, nvidiaLocale: 'de_de'}],
['belgium', {drLocale: 'fr_fr', fe2060SuperId: 5394902700, fe3080Id: 5438795700, fe3090Id: null, nvidiaLocale: 'fr_fr'}],
['canada', {drLocale: 'en_us', fe2060SuperId: null, fe3080Id: 5438481700, fe3090Id: null, nvidiaLocale: 'en_ca'}],
['czechia', {drLocale: 'en_gb', fe2060SuperId: null, fe3080Id: 5438793800, fe3090Id: null, nvidiaLocale: 'en_gb'}],
['denmark', {drLocale: 'en_gb', fe2060SuperId: null, fe3080Id: 5438793300, fe3090Id: null, nvidiaLocale: 'en_gb'}],
['finland', {drLocale: 'en_gb', fe2060SuperId: null, fe3080Id: 5438793300, fe3090Id: null, nvidiaLocale: 'en_gb'}],
['france', {drLocale: 'fr_fr', fe2060SuperId: null, fe3080Id: 5438795200, fe3090Id: null, nvidiaLocale: 'fr_fr'}],
['germany', {drLocale: 'de_de', fe2060SuperId: null, fe3080Id: 5438792300, fe3090Id: null, nvidiaLocale: 'de_de'}],
['great_britain', {drLocale: 'en_gb', fe2060SuperId: null, fe3080Id: 5438792800, fe3090Id: null, nvidiaLocale: 'en_gb'}],
['ireland', {drLocale: 'en_gb', fe2060SuperId: null, fe3080Id: 5438792800, fe3090Id: null, nvidiaLocale: 'en_gb'}],
['italy', {drLocale: 'it_it', fe2060SuperId: null, fe3080Id: 5438796200, fe3090Id: null, nvidiaLocale: 'it_it'}],
['luxembourg', {drLocale: 'fr_fr', fe2060SuperId: 5394902700, fe3080Id: 5438795700, fe3090Id: null, nvidiaLocale: 'fr_fr'}],
['netherlands', {drLocale: 'nl_nl', fe2060SuperId: 5394903500, fe3080Id: 5438796700, fe3090Id: null, nvidiaLocale: 'nl_nl'}],
['poland', {drLocale: 'pl_pl', fe2060SuperId: null, fe3080Id: 5438797700, fe3090Id: null, nvidiaLocale: 'pl_pSl'}],
['portugal', {drLocale: 'en_gb', fe2060SuperId: null, fe3080Id: 5438794300, fe3090Id: null, nvidiaLocale: 'en_gb'}],
['russia', {drLocale: 'ru_ru', fe2060SuperId: null, fe3080Id: null, fe3090Id: null, nvidiaLocale: 'ru_ru'}],
['spain', {drLocale: 'es_es', fe2060SuperId: null, fe3080Id: 5438794800, fe3090Id: null, nvidiaLocale: 'es_es'}],
['sweden', {drLocale: 'sv_SE', fe2060SuperId: null, fe3080Id: 5438798100, fe3090Id: null, nvidiaLocale: 'sv_se'}],
['usa', {drLocale: 'en_us', fe2060SuperId: 5379432500, fe3080Id: 5438481700, fe3090Id: null, nvidiaLocale: 'en_us'}]
]);
export const NvidiaApi: Store = {
labels: {
inStock: {
container: 'body',
text: ['product_inventory_in_stock']
}
},
links: generateLinks(),
name: 'nvidia-api',
setupAction: generateSetupAction()
};
+35
View File
@@ -0,0 +1,35 @@
import {Store} from './store';
export const Nvidia: Store = {
labels: {
captcha: {
container: 'body',
text: ['are you a human?']
},
inStock: {
container: '.main-container',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.newegg.com/evga-geforce-rtx-2060-06g-p4-2066-kr/p/N82E16814487488'
},
{
brand: 'nvidia',
model: 'founders edition',
series: '3080',
url: 'https://www.nvidia.com/en-us/shop/geforce/gpu/?page=1&limit=9&locale=en-us&gpu=RTX%203080&category=GPU&manufacturer=NVIDIA&gpu_filter=RTX%203090~1,RTX%203080~1,RTX%203070~0,TITAN%20RTX~0,RTX%202080%20Ti~0,RTX%202070%20SUPER~0,RTX%202060%20SUPER~0,RTX%202060~0,GTX%201660%20Ti~0,GTX%201660%20SUPER~0,GTX%201660~0,GTX%201650%20SUPER~0,GTX%201650~0'
},
{
brand: 'nvidia',
model: 'founders edition',
series: '3090',
url: 'https://www.nvidia.com/en-us/shop/geforce/gpu/?page=1&limit=9&locale=en-us&gpu=RTX%203090&category=GPU&manufacturer=NVIDIA&gpu_filter=RTX%203090~1,RTX%203080~1,RTX%203070~0,TITAN%20RTX~0,RTX%202080%20Ti~0,RTX%202070%20SUPER~0,RTX%202060%20SUPER~0,RTX%202060~0,GTX%201660%20Ti~0,GTX%201660%20SUPER~0,GTX%201660~0,GTX%201650%20SUPER~0,GTX%201650~0'
}
],
name: 'nvidia'
};
+35
View File
@@ -0,0 +1,35 @@
import {Store} from './store';
export const OfficeDepot: Store = {
labels: {
captcha: {
container: 'body',
text: ['please verify you are a human']
},
inStock: {
container: '#productPurchase',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://www.officedepot.com/a/products/4652239/EVGA-GeForce-RTX-2060-Graphic-Card/'
},
{
brand: 'pny',
model: 'xlr8',
series: '3080',
url: 'https://www.officedepot.com/a/products/7189374/PNY-GeForce-RTX-3080-10GB-GDDR6X/'
},
{
brand: 'pny',
model: 'xlr8 rgb',
series: '3080',
url: 'https://www.officedepot.com/a/products/7791294/PNY-GeForce-RTX-3080-10GB-GDDR6X/'
}
],
name: 'officedepot'
};
+29
View File
@@ -0,0 +1,29 @@
import {Browser, LoadEvent} from 'puppeteer';
export type Element = {
container: string;
text: string[];
};
export type Link = {
brand: 'test:brand' | 'asus' | 'evga' | 'gigabyte' | 'pny' | 'msi' | 'nvidia' | 'zotac';
series: 'test:series' | '3070' | '3080' | '3090';
model: string;
url: string;
cartUrl?: string;
openCartAction?: (browser: Browser) => Promise<string>;
screenshot?: string;
};
export type Labels = {
captcha?: Element;
inStock: Element;
};
export type Store = {
links: Link[];
labels: Labels;
name: string;
setupAction?: (browser: Browser) => void;
waitUntil?: LoadEvent;
};
+32
View File
@@ -0,0 +1,32 @@
import {Store} from './store';
export const Zotac: Store = {
labels: {
inStock: {
container: '.add-to-cart-wrapper',
text: ['add to cart']
}
},
links: [
{
brand: 'test:brand',
model: 'test:model',
series: 'test:series',
url: 'https://store.zotac.com/zotac-gaming-geforce-rtx-2060-twin-fan-zt-t20600f-10m'
},
{
brand: 'zotac',
model: 'trinity',
series: '3080',
url: 'https://store.zotac.com/zotac-gaming-geforce-rtx-3080-trinity-zt-a30800d-10p'
},
{
brand: 'zotac',
model: 'trinity OC',
series: '3080',
url: 'https://store.zotac.com/zotac-gaming-geforce-rtx-3080-trinity-oc-zt-a30800j-10p'
}
],
name: 'zotac'
};
-38
View File
@@ -1,38 +0,0 @@
import {Store} from './store';
export const NewEgg: Store = {
cartUrl: '',
links: [
{
brand: 'asus',
model: 'tuf',
url: 'https://www.newegg.com/asus-geforce-rtx-3080-tuf-rtx3080-10g-gaming/p/N82E16814126453',
oosLabels: ['auto notify', 'out of stock']
},
{
brand: 'evga',
model: 'black gaming',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3881-kr/p/N82E16814487522',
oosLabels: ['auto notify', 'out of stock']
},
{
brand: 'evga',
model: 'argb led icx3',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3883-kr/p/N82E16814487521',
oosLabels: ['auto notify', 'out of stock']
},
{
brand: 'evga',
model: 'xc3 ultra gaming',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3885-kr/p/N82E16814487520',
oosLabels: ['auto notify', 'out of stock']
},
{
brand: 'msi',
model: 'ventus',
url: 'https://www.newegg.com/msi-geforce-rtx-3080-rtx-3080-ventus-3x-10g/p/N82E16814137600',
oosLabels: ['auto notify', 'out of stock']
}
],
name: 'newegg'
};
-20
View File
@@ -1,20 +0,0 @@
import {Store} from './store';
export const Nvidia: Store = {
cartUrl: 'https://store.nvidia.com/store/nvidia/en_US/buy/productID.5438481700/clearCart.yes/nextPage.QuickBuyCartPage',
links: [
{
brand: 'nvidia',
model: 'founders edition',
url: 'https://www.nvidia.com/en-us/geforce/buy/',
oosLabels: ['out of stock']
},
{
brand: 'nvidia',
model: 'founders edition',
url: 'https://www.nvidia.com/en-us/shop/geforce/?page=1&limit=9&locale=en-us&search=3080',
oosLabels: ['out of stock']
}
],
name: 'nvidia'
};
-12
View File
@@ -1,12 +0,0 @@
interface Link {
brand: string;
model: string;
url: string;
oosLabels: string[];
}
export interface Store {
cartUrl: string;
links: Link[];
name: string;
}
+8
View File
@@ -0,0 +1,8 @@
/**
* Generates unique URL param to prevent cached responses (similar to jQuery that Nvidia uses)
*
* @return string in format &=1111111111111 (time since epoch in ms)
*/
export function timestampUrlParameter(): string {
return `&_=${Date.now()}`;
}
+1
View File
@@ -0,0 +1 @@
declare module 'play-sound';
+1
View File
@@ -0,0 +1 @@
declare module 'pushbullet';
+1
View File
@@ -0,0 +1 @@
declare module 'pushover-notifications';
+18
View File
@@ -0,0 +1,18 @@
import {Config} from './config';
import {Page} from 'puppeteer';
import {disableBlockerInPage} from './adblocker';
export function getSleepTime() {
return Config.browser.minSleep + (Math.random() * (Config.browser.maxSleep - Config.browser.minSleep));
}
export async function delay(ms: number) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
export async function closePage(page: Page) {
await disableBlockerInPage(page);
await page.close();
}
+2 -1
View File
@@ -11,6 +11,7 @@
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": true,
"sourceMap": true
}
}