Compare commits

...

24 Commits

Author SHA1 Message Date
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
37 changed files with 773 additions and 240 deletions
+5
View File
@@ -0,0 +1,5 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
+9 -1
View File
@@ -7,4 +7,12 @@ SLACK_CHANNEL="SlackChannelName"
SLACK_TOKEN="slack-token" SLACK_TOKEN="slack-token"
STORES="bestbuy,bandh,nvidia" STORES="bestbuy,bandh,nvidia"
PHONE_NUMBER="1234567890" PHONE_NUMBER="1234567890"
CARRIER="tmobile" PHONE_CARRIER="tmobile"
PUSHOVER_TOKEN="123pushover-token456"
PUSHOVER_USER="123pushover-user-key"
OPEN_BROWSER="true"
PLAY_SOUND="false"
SCREENSHOT="true"
TELEGRAM_ACCESS_TOKEN=""
TELEGRAM_CHAT_ID="1234"
SHOW_ONLY_BRANDS="evga"
+4 -2
View File
@@ -1,7 +1,7 @@
--- ---
name: 🐛 Bug report name: 🐛 Bug report
about: Report a bug for this project about: Report a bug for this project
title: 'bug: ' title: ''
labels: 'bug' labels: 'bug'
assignees: jef assignees: jef
@@ -9,7 +9,9 @@ assignees: jef
### Description ### Description
<!-- Describe the feature here. --> <!-- Describe the bug here. -->
<!-- Please be as detailed as possible. -->
<!-- Include OS, screenshots and console outputs. -->
### Possible solution ### Possible solution
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
name: 🚀 Feature request name: 🚀 Feature request
about: Suggest a feature for this project about: Suggest a feature for this project
title: 'enhancement: ' title: ''
labels: 'enhancement' labels: 'enhancement'
assignees: jef assignees: jef
+4
View File
@@ -6,3 +6,7 @@ node_modules/
.env .env
success-*.png success-*.png
*.wav
*.mp3
*.flac
+32
View File
@@ -1,5 +1,37 @@
# Changelog # Changelog
## [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) ## [1.2.0](https://www.github.com/jef/nvidia-snatcher/compare/v1.1.0...v1.2.0) (2020-09-19)
+35 -18
View File
@@ -1,6 +1,6 @@
# 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)
[FAQ](#FAQ) | [Discord](https://discord.gg/3duFzwk) | [Issues](https://github.com/jef/nvidia-snatcher/issues) | [Wiki](https://github.com/jef/nvidia-snatcher/wiki) [FAQ](#FAQ) | [Issues](https://github.com/jef/nvidia-snatcher/issues) | [Wiki](https://github.com/jef/nvidia-snatcher/wiki)
The purpose of this bot is to get an Nvidia card. It tries multiple things to do that. The purpose of this bot is to get an Nvidia card. It tries multiple things to do that.
@@ -28,13 +28,11 @@ The purpose of this bot is to get an Nvidia card. It tries multiple things to do
> :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 :) > :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** | **EVGA** | **Amazon** | | | **Amazon** | **EVGA** | **Best Buy** | **B&H** | **Micro Center** | **Newegg** | **Nvidia** |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| **3070**| | | | | | | | **3070**| | | | | | | |
| **3080** | `✔` | `✔` | `` | `✔` | `✔` | `✔` | | **3080** | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` |
| **3090** | | | | | | | | **3090** | | | | | | | |
> :point_right: (``) Work in progress. Catchpa problems are intermittent. Use if you'd like, but expect problems.
## Installation and prerequisites ## Installation and prerequisites
@@ -58,30 +56,39 @@ At any point you want the program to stop, use <kbd>Ctrl</kbd> + <kbd>C</kbd>.
### Customization ### 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.
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** | | **Environment variable** | **Description** |
|:---:|:---:| |:---:|---|
| `EMAIL_USERNAME` | Gmail address (e.g., `jensen.robbed.us@gmail.com`); optional | | `EMAIL_USERNAME` | Gmail address (e.g., `jensen.robbed.us@gmail.com`); optional |
| `EMAIL_PASSWORD` | Gmail password; see below if you have MFA; optional | | `EMAIL_PASSWORD` | Gmail password; see below if you have MFA; optional |
| `NOTIFICATION_TEST` | Test all the notifications configured; optional, default: `false` | | `NOTIFICATION_TEST` | Test all the notifications configured; optional, default: `false` |
| `PAGE_TIMEOUT` | Navigation Timeout in milliseconds (`0` for infinite); optional, default: `30000` | | `PAGE_TIMEOUT` | Navigation Timeout in milliseconds (`0` for infinite); optional, default: `30000` |
| `PHONE_NUMBER` | 10 digit phone number, only USA, SMS may apply (e.g., `1234567890`); optional, email configuration required | | `PHONE_NUMBER` | 10 digit phone number (e.g., `1234567890`); optional, email configuration required |
| `PHONE_CARRIER` | Service provider for SMS, supports `["sprint", "tmobile", "att", "verizon"]`; optional, email configuration required | | `PHONE_CARRIER` | [Supported carriers](#supported-carriers) for SMS; optional, email configuration required |
| `RATE_LIMIT_TIMEOUT` | Rate limit timeout for each full store cycle; optional, default: `5000` | | `RATE_LIMIT_TIMEOUT` | Rate limit timeout for each full store cycle; optional, default: `5000` |
| `SHOW_ONLY_BRANDS` | If set, will only show specified brands, seperated by `,` |
| `SLACK_CHANNEL` | Slack channel for posting (e.g., `update`); optional | | `SLACK_CHANNEL` | Slack channel for posting (e.g., `update`); optional |
| `SLACK_TOKEN` | Slack API token; optional | `SLACK_TOKEN` | Slack API token; optional |
| `STORES` | List of [stores](#Supported-stores) you want to be scraped; optional, default: `nvidia` | | `STORES` | [Supported stores](#supported-stores) you want to be scraped; optional, default: `nvidia` |
| `OPEN_BROWSER` | Toggle for whether or not the browser should open when item is found, default: `true` |
| `PLAY_SOUND` | Play this sound notification if a card is found.; optional |
| `SCREENSHOT` | Capture screenshot of page on successful hit; optional, default `true` |
| `TELEGRAM_ACCESS_TOKEN` | Telegram access token; optional |
| `TELEGRAM_CHAT_ID` | Telegram chat ID; optional |
> :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: 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: Free sounds available [here](https://freesound.org/home/). Place sounds into `resources/sounds/`
#### Supported stores #### Supported stores
| **Store name** | **Store name environment variable** | | **Stores** | **Environment variable** |
|:---:|:---:| |:---:|:---:|
| Best Buy | `bestbuy`| | Best Buy | `bestbuy`|
| Amazon.ca | `amazon-ca`|
| Amazon | `amazon`| | Amazon | `amazon`|
| B&H | `bandh`| | B&H | `bandh`|
| EVGA | `evga`| | EVGA | `evga`|
@@ -89,7 +96,17 @@ First, you're going to need to copy the `.env.example` to `.env`. The current op
| Newegg | `newegg`| | Newegg | `newegg`|
| Nvidia | `nvidia`| | Nvidia | `nvidia`|
> :point_right: Look at [`.env.example`](.env.example) for an example for `.env`. #### Supported carriers
| **Carrier** | **Environment variable** | **Notes** |
|:---:|:---:|:---:|
| AT&T | `att`| |
| Google | `google`| |
| Mint | `mint`| |
| Sprint | `sprint`| |
| Telus | `telus`| |
| T-Mobile | `tmobile`| |
| Verizon | `verizon`| Works with Visible |
## FAQ ## FAQ
+177 -14
View File
@@ -1,6 +1,6 @@
{ {
"name": "nvidia-snatcher", "name": "nvidia-snatcher",
"version": "1.2.0", "version": "1.4.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -230,6 +230,11 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true "dev": true
}, },
"@types/debug": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
},
"@types/eslint-visitor-keys": { "@types/eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@@ -267,6 +272,11 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true "dev": true
}, },
"@types/lodash": {
"version": "4.14.161",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.161.tgz",
"integrity": "sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA=="
},
"@types/minimatch": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -326,6 +336,16 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"dev": true "dev": true
}, },
"@types/url-join": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/url-join/-/url-join-4.0.0.tgz",
"integrity": "sha512-awrJu8yML4E/xTwr2EMatC+HBnHGoDxc2+ImA9QyeUELI1S7dOCIZcyjki1rkwoA8P2D2NVgLAJLjnclkdLtAw=="
},
"@types/warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI="
},
"@types/yauzl": { "@types/yauzl": {
"version": "2.9.1", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
@@ -649,11 +669,26 @@
"version": "0.19.2", "version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"dev": true,
"requires": { "requires": {
"follow-redirects": "1.5.10" "follow-redirects": "1.5.10"
} }
}, },
"axios-error": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/axios-error/-/axios-error-1.0.0.tgz",
"integrity": "sha512-1JlcEEdTDeadLJ9+ImZd9eQudqqpAStpHzh9/Rb2q2UsWC+dJyQrFMTAHOzSJoFknWZ9IbVvW5gga9lG7CKLqQ==",
"requires": {
"axios": "^0.19.2",
"type-fest": "^0.15.1"
},
"dependencies": {
"type-fest": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.15.1.tgz",
"integrity": "sha512-n+UXrN8i5ioo7kqT/nF8xsEzLaqFra7k32SEsSPwvXVGyAcRgV/FUQN/sgfptJTR1oRmmq7z4IXMFSM7im7C9A=="
}
}
},
"babel-eslint": { "babel-eslint": {
"version": "10.1.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -1043,6 +1078,15 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true "dev": true
}, },
"camel-case": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz",
"integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==",
"requires": {
"pascal-case": "^3.1.1",
"tslib": "^1.10.0"
}
},
"camelcase": { "camelcase": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -1578,6 +1622,15 @@
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
"dev": true "dev": true
}, },
"dot-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz",
"integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==",
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"dot-prop": { "dot-prop": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@@ -2721,6 +2774,12 @@
"pkg-dir": "^4.1.0" "pkg-dir": "^4.1.0"
} }
}, },
"find-exec": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/find-exec/-/find-exec-1.0.1.tgz",
"integrity": "sha512-4o6QkGkpg3xK5s/47rdK9LDZRsE4JR1mrXnaAOXBngG6UKeIDJXfwtNCAkljgyy6VRh75D3FFaB0tii9vDEtIA==",
"dev": true
},
"find-root": { "find-root": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
@@ -2773,7 +2832,6 @@
"version": "1.5.10", "version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"dev": true,
"requires": { "requires": {
"debug": "=3.1.0" "debug": "=3.1.0"
}, },
@@ -2782,7 +2840,6 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
} }
@@ -2790,8 +2847,7 @@
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"dev": true
} }
} }
}, },
@@ -3549,8 +3605,7 @@
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"dev": true
}, },
"js-types": { "js-types": {
"version": "1.0.0", "version": "1.0.0",
@@ -3713,8 +3768,7 @@
"lodash": { "lodash": {
"version": "4.17.20", "version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
"dev": true
}, },
"lodash.get": { "lodash.get": {
"version": "4.4.2", "version": "4.4.2",
@@ -3795,6 +3849,22 @@
"triple-beam": "^1.3.0" "triple-beam": "^1.3.0"
} }
}, },
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lower-case": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz",
"integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==",
"requires": {
"tslib": "^1.10.0"
}
},
"lowercase-keys": { "lowercase-keys": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -3827,8 +3897,7 @@
"map-obj": { "map-obj": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz",
"integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g=="
"dev": true
}, },
"map-visit": { "map-visit": {
"version": "1.0.0", "version": "1.0.0",
@@ -3928,6 +3997,39 @@
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true "dev": true
}, },
"messaging-api-common": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/messaging-api-common/-/messaging-api-common-1.0.0.tgz",
"integrity": "sha512-4bHqgnzrR2F97V+WnxVKS6EmjElkURa9jhy0/Tv7B+Xmcl3BDEW2krFpr5LfJDwFFiaDqj8Poel81hTOoWO/Lw==",
"requires": {
"@types/debug": "^4.1.5",
"@types/lodash": "^4.14.156",
"@types/url-join": "^4.0.0",
"axios": "^0.19.2",
"camel-case": "^4.1.1",
"debug": "^4.1.1",
"lodash": "^4.17.15",
"map-obj": "^4.1.0",
"pascal-case": "^3.1.1",
"snake-case": "^3.0.3",
"url-join": "^4.0.1"
}
},
"messaging-api-telegram": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/messaging-api-telegram/-/messaging-api-telegram-1.0.0.tgz",
"integrity": "sha512-hA2LFclQV5sVGGIYosdYvhap0GrPD73GuNjYi/T9rKZhn9qA5wTihrHNXdJHmUI7QOTT3JnA3QRdkwc9wkFZuA==",
"requires": {
"@types/lodash": "^4.14.156",
"@types/warning": "^3.0.0",
"axios": "^0.19.2",
"axios-error": "^1.0.0",
"lodash": "^4.17.15",
"messaging-api-common": "^1.0.0",
"ts-invariant": "^0.4.4",
"warning": "^4.0.3"
}
},
"micro-spelling-correcter": { "micro-spelling-correcter": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz",
@@ -4145,6 +4247,15 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true "dev": true
}, },
"no-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz",
"integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==",
"requires": {
"lower-case": "^2.0.1",
"tslib": "^1.10.0"
}
},
"node-libs-browser": { "node-libs-browser": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
@@ -4559,6 +4670,15 @@
"lines-and-columns": "^1.1.6" "lines-and-columns": "^1.1.6"
} }
}, },
"pascal-case": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz",
"integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==",
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"pascalcase": { "pascalcase": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -4643,6 +4763,15 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"play-sound": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/play-sound/-/play-sound-1.1.3.tgz",
"integrity": "sha512-lqEzgtNAdfg2VUXItOtu5bTyWcqeFmnJmgvc8iHEeEOBEJdurqiGYfmCOzIpSBcwrs7XeSpvHv+Rw9dzRPc4aw==",
"dev": true,
"requires": {
"find-exec": "1.0.1"
}
},
"plur": { "plur": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
@@ -4783,6 +4912,11 @@
"ws": "^7.2.3" "ws": "^7.2.3"
} }
}, },
"pushover-notifications": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/pushover-notifications/-/pushover-notifications-1.2.2.tgz",
"integrity": "sha512-+3Xcj+kiMiouZK1Ws8yGBTyl8WMPZZdELgl/iVxYqNwDdlaObBHMhEGPRC6Zb9t0BE27ikOoOqSIO1cKZOtsDA=="
},
"querystring": { "querystring": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -5216,6 +5350,15 @@
"is-fullwidth-code-point": "^2.0.0" "is-fullwidth-code-point": "^2.0.0"
} }
}, },
"snake-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.3.tgz",
"integrity": "sha512-WM1sIXEO+rsAHBKjGf/6R1HBBcgbncKS08d2Aqec/mrDSpU80SiOU41hO7ny6DToHSyrlwTYzQBIK1FPSx4Y3Q==",
"requires": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"snapdragon": { "snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -5811,6 +5954,14 @@
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
}, },
"ts-invariant": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz",
"integrity": "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==",
"requires": {
"tslib": "^1.9.3"
}
},
"tsconfig-paths": { "tsconfig-paths": {
"version": "3.9.0", "version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
@@ -5837,8 +5988,7 @@
"tslib": { "tslib": {
"version": "1.13.0", "version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
"dev": true
}, },
"tsutils": { "tsutils": {
"version": "3.17.1", "version": "3.17.1",
@@ -6067,6 +6217,11 @@
} }
} }
}, },
"url-join": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
},
"url-parse-lax": { "url-parse-lax": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
@@ -6126,6 +6281,14 @@
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"dev": true "dev": true
}, },
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"which": { "which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+4 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "nvidia-snatcher", "name": "nvidia-snatcher",
"version": "1.2.0", "version": "1.4.0",
"description": "🔮 For all your Nvidia needs", "description": "🔮 For all your Nvidia needs",
"main": "src/index.ts", "main": "src/index.ts",
"scripts": { "scripts": {
@@ -22,9 +22,11 @@
"homepage": "https://github.com/jef/nvidia-snatcher#readme", "homepage": "https://github.com/jef/nvidia-snatcher#readme",
"dependencies": { "dependencies": {
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"messaging-api-telegram": "^1.0.0",
"nodemailer": "^6.4.11", "nodemailer": "^6.4.11",
"open": "^7.2.1", "open": "^7.2.1",
"puppeteer": "^5.3.0", "puppeteer": "^5.3.0",
"pushover-notifications": "^1.2.2",
"winston": "^3.3.3" "winston": "^3.3.3"
}, },
"devDependencies": { "devDependencies": {
@@ -32,6 +34,7 @@
"@types/node": "^14.11.1", "@types/node": "^14.11.1",
"@types/nodemailer": "^6.4.0", "@types/nodemailer": "^6.4.0",
"@types/puppeteer": "^3.0.2", "@types/puppeteer": "^3.0.2",
"play-sound": "^1.1.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^4.0.2", "typescript": "^4.0.2",
"xo": "^0.33.1" "xo": "^0.33.1"
+30 -9
View File
@@ -9,31 +9,52 @@ const notifications = {
password: process.env.EMAIL_PASSWORD ?? '' password: process.env.EMAIL_PASSWORD ?? ''
}, },
phone: { phone: {
availableCarriers: ['sprint', 'verizon', 'tmobile', 'att'], availableCarriers: new Map([
carrier: process.env.PHONE_CARRIER, ['att', 'txt.att.net'],
number: process.env.PHONE_NUMBER ['google', 'msg.fi.google.com'],
['mint', 'mailmymobile.net'],
['sprint', 'messaging.sprintpcs.com'],
['telus', 'msg.telus.com'],
['tmobile', 'tmomail.net'],
['verizon', 'vtext.com']
]),
carrier: process.env.PHONE_CARRIER ?? '',
number: process.env.PHONE_NUMBER ?? ''
},
playSound: process.env.PLAY_SOUND ?? 'false',
pushover: {
token: process.env.PUSHOVER_TOKEN,
user: process.env.PUSHOVER_USER
}, },
slack: { slack: {
channel: process.env.SLACK_CHANNEL ?? '', channel: process.env.SLACK_CHANNEL ?? '',
token: process.env.SLACK_TOKEN ?? '' token: process.env.SLACK_TOKEN ?? ''
}, },
telegram: {
accessToken: process.env.TELEGRAM_ACCESS_TOKEN ?? '',
chatId: process.env.TELEGRAM_CHAT_ID ?? ''
},
test: process.env.NOTIFICATION_TEST ?? 'false' test: process.env.NOTIFICATION_TEST ?? 'false'
}; };
const page = { const page = {
height: 1920, capture: process.env.SCREENSHOT ?? 'true',
width: 1920,
height: 1080,
navigationTimeout: Number(process.env.PAGE_TIMEOUT) ?? 30000, navigationTimeout: Number(process.env.PAGE_TIMEOUT) ?? 30000,
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
width: 1080
}; };
const openBrowser = process.env.OPEN_BROWSER ?? 'true';
const rateLimitTimeout = Number(process.env.RATE_LIMIT_TIMEOUT) ?? 5000; const rateLimitTimeout = Number(process.env.RATE_LIMIT_TIMEOUT) ?? 5000;
const stores = process.env.STORES ? process.env.STORES.split(',') : ['nvidia'];
const stores = process.env.STORES ?? 'nvidia'; const showOnlyBrands = process.env.SHOW_ONLY_BRANDS ? process.env.SHOW_ONLY_BRANDS.split(',') : [];
export const Config = { export const Config = {
notifications, notifications,
rateLimitTimeout, rateLimitTimeout,
page, page,
stores stores,
openBrowser,
showOnlyBrands
}; };
+11 -72
View File
@@ -1,95 +1,34 @@
import {Config} from './config'; import {Config} from './config';
import {Store, Stores} from './store'; import {Stores} from './store/model';
import puppeteer from 'puppeteer';
import open from 'open';
import sendNotification from './notification';
import {Logger} from './logger'; import {Logger} from './logger';
import {sendNotification} from './notification';
/** import {lookup} from './store';
* Send test email. import puppeteer from 'puppeteer';
*/
if (Config.notifications.test === 'true') {
sendNotification('test');
}
/** /**
* Starts the bot. * Starts the bot.
*/ */
async function main() { async function main() {
const results = []; const results = [];
const browser = await puppeteer.launch();
for (const store of Stores) { for (const store of Stores) {
Logger.debug(store.links); Logger.debug(store.links);
results.push(lookup(store)); results.push(lookup(browser, store));
} }
await Promise.all(results); await Promise.all(results);
await browser.close();
Logger.info('↗ trying stores again'); Logger.info('↗ trying stores again');
setTimeout(main, Config.rateLimitTimeout); setTimeout(main, Config.rateLimitTimeout);
} }
/** /**
* Responsible for looking up information about a each product within * Send test email.
* 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) { if (Config.notifications.test === 'true') {
/* eslint-disable no-await-in-loop */ sendNotification('test');
for (const link of store.links) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.setDefaultNavigationTimeout(Config.page.navigationTimeout);
await page.setUserAgent(Config.page.userAgent);
await page.setViewport({
height: Config.page.height,
width: Config.page.width
});
const graphicsCard = `${link.brand} ${link.model}`;
try {
await page.goto(link.url, {waitUntil: 'networkidle0'});
} catch {
Logger.error(`✖ [${store.name}] ${graphicsCard} skipping; timed out`);
await browser.close();
return;
}
const bodyHandle = await page.$('body');
const textContent = await page.evaluate(body => body.textContent, bodyHandle);
Logger.debug(textContent);
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);
}
await browser.close();
}
/* 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();
return oosLabels.some(label => domTextLowerCase.includes(label));
} }
/** /**
+3 -1
View File
@@ -1,11 +1,13 @@
import winston, {format} from 'winston'; import winston, {format} from 'winston';
const prettyJson = format.printf(info => { const prettyJson = format.printf(info => {
const timestamp = new Date().toLocaleTimeString();
if (typeof info.message === 'object') { if (typeof info.message === 'object') {
info.message = JSON.stringify(info.message, null, 4); info.message = JSON.stringify(info.message, null, 4);
} }
return `${info.level} :: ${info.message}`; return `[${timestamp}] ${info.level} :: ${info.message}`;
}); });
export const Logger = winston.createLogger({ export const Logger = winston.createLogger({
+6 -5
View File
@@ -3,23 +3,24 @@ import Mail from 'nodemailer/lib/mailer';
import {Config} from '../config'; import {Config} from '../config';
import {Logger} from '../logger'; import {Logger} from '../logger';
const email = Config.notifications.email;
const subject = 'NVIDIA - BUY NOW'; const subject = 'NVIDIA - BUY NOW';
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
service: 'gmail', service: 'gmail',
auth: { auth: {
user: Config.notifications.email.username, user: email.username,
pass: Config.notifications.email.password pass: email.password
} }
}); });
const mailOptions: Mail.Options = { const mailOptions: Mail.Options = {
from: Config.notifications.email.username, from: email.username,
to: Config.notifications.email.username, to: email.username,
subject subject
}; };
export default function sendEmail(text: string) { export function sendEmail(text: string) {
mailOptions.text = text; mailOptions.text = text;
transporter.sendMail(mailOptions, (error, info) => { transporter.sendMail(mailOptions, (error, info) => {
+1 -20
View File
@@ -1,20 +1 @@
import {Config} from '../config'; export * from './notification';
import sendEmail from './email';
import sendSlaskMessage from './slack';
import sendSMS from './sms';
export default function sendNotification(cartUrl: string) {
if (Config.notifications.email.username && Config.notifications.email.password) {
sendEmail(cartUrl);
}
if (Config.notifications.slack.channel && Config.notifications.slack.token) {
sendSlaskMessage(cartUrl);
}
if (Config.notifications.phone.number && Config.notifications.phone.carrier) {
if (Config.notifications.phone.availableCarriers.includes(Config.notifications.phone.carrier.toLowerCase())) {
sendSMS(cartUrl);
}
}
}
+38
View File
@@ -0,0 +1,38 @@
import {Config} from '../config';
import {sendEmail} from './email';
import {sendSMS} from './sms';
import {playSound} from './sound';
import {sendSlackMessage} from './slack';
import {sendPushoverNotification} from './pushover';
import {sendTelegramMessage} from './telegram';
const notifications = Config.notifications;
export function sendNotification(cartUrl: string) {
if (notifications.email.username && notifications.email.password) {
sendEmail(cartUrl);
}
if (notifications.slack.channel && notifications.slack.token) {
sendSlackMessage(cartUrl);
}
if (notifications.telegram.accessToken && notifications.telegram.chatId) {
sendTelegramMessage(cartUrl);
}
if (notifications.phone.number) {
const carrier = notifications.phone.carrier.toLowerCase();
if (carrier && notifications.phone.availableCarriers.has(carrier)) {
sendSMS(cartUrl);
}
}
if (notifications.pushover.token && notifications.pushover.user) {
sendPushoverNotification(cartUrl);
}
if (notifications.playSound === 'true') {
playSound();
}
}
+23
View File
@@ -0,0 +1,23 @@
import Push from 'pushover-notifications';
import {Config} from '../config';
import {Logger} from '../logger';
const pushover = Config.notifications.pushover;
const push = new Push({
user: pushover.user,
token: pushover.token
});
export function sendPushoverNotification(text: string) {
const message = {
message: text
};
push.send(message, (err: Error, result: string) => {
if (err) {
Logger.error(err);
} else {
Logger.info(`✔ Pushover notification sent: ${result}`);
}
});
}
+1 -1
View File
@@ -6,7 +6,7 @@ const channel = Config.notifications.slack.channel;
const token = Config.notifications.slack.token; const token = Config.notifications.slack.token;
const web = new WebClient(token); const web = new WebClient(token);
export default function sendSlackMessage(text: string) { export function sendSlackMessage(text: string) {
(async () => { (async () => {
try { try {
const result = await web.chat.postMessage({text, channel}); const result = await web.chat.postMessage({text, channel});
+8 -16
View File
@@ -4,19 +4,13 @@ import {Config} from '../config';
import {Logger} from '../logger'; import {Logger} from '../logger';
const subject = 'NVIDIA - BUY NOW'; const subject = 'NVIDIA - BUY NOW';
const [email, phone] = [Config.notifications.email, Config.notifications.phone];
enum carrierAddress {
sprint = 'messaging.sprintpcs.com',
verizon = 'vtext.com',
tmobile = 'tmomail.net',
att = 'txt.att.net'
}
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
service: 'gmail', service: 'gmail',
auth: { auth: {
user: Config.notifications.email.username, user: email.username,
pass: Config.notifications.email.password pass: email.password
} }
}); });
@@ -26,7 +20,7 @@ const mailOptions: Mail.Options = {
subject subject
}; };
export default function sendSMS(text: string) { export function sendSMS(text: string) {
mailOptions.text = text; mailOptions.text = text;
transporter.sendMail(mailOptions, (error, info) => { transporter.sendMail(mailOptions, (error, info) => {
@@ -34,16 +28,14 @@ export default function sendSMS(text: string) {
Logger.error(error); Logger.error(error);
} else { } else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.info(`email sent: ${info.response}`); Logger.info(`sms sent: ${info.response}`);
} }
}); });
} }
function generateAddress() { function generateAddress() {
for (const carrier of Object.keys(carrierAddress)) { const carrier = phone.carrier.toLowerCase();
if (Config.notifications.phone.carrier && carrier === Config.notifications.phone.carrier.toLowerCase()) { if (carrier && phone.availableCarriers.has(carrier)) {
// @ts-expect-error return [phone.number, phone.availableCarriers.get(carrier)].join('@');
return [Config.phone.number, carrierAddress[carrier]].join('@');
}
} }
} }
+25
View File
@@ -0,0 +1,25 @@
import playerLib = require('play-sound');
import {Config} from '../config';
import {Logger} from '../logger';
import * as fs from 'fs';
const notificationSound = './resources/sounds/' + Config.notifications.playSound;
const player = playerLib();
export function playSound() {
// Check if file exists
fs.access(notificationSound, fs.constants.F_OK, err => {
if (err) {
Logger.error(`error opening sound file: ${err.message}`);
return;
}
player.play(notificationSound, (err: string) => {
Logger.info('✔ playing sound');
if (err) {
Logger.error(`error playing sound: ${err}`);
}
});
});
}
+20
View File
@@ -0,0 +1,20 @@
import {Config} from '../config';
import {Logger} from '../logger';
import {TelegramClient} from 'messaging-api-telegram';
const telegram = Config.notifications.telegram;
const client = new TelegramClient({
accessToken: telegram.accessToken
});
export function sendTelegramMessage(text: string) {
(async () => {
try {
await client.sendMessage(telegram.chatId, text);
Logger.info(`✔ telegram message sent to '${telegram.chatId}': ${text}`);
} catch (error) {
Logger.error(error);
}
})();
}
-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'
};
-14
View File
@@ -1,14 +0,0 @@
import {Store} from './store';
export const Evga: Store = {
cartUrl: '',
links: [
{
brand: 'evga',
model: 'ftw3, xc3 black, xc3 gaming, xc3 ultra gaming',
url: 'https://www.evga.com/products/productlist.aspx?type=0&family=GeForce+30+Series+Family&chipset=RTX+3080',
oosLabels: ['out of stock']
}
],
name: 'evga'
};
+10
View File
@@ -0,0 +1,10 @@
/**
* Checks if DOM has any out-of-stock related text.
*
* @param domText Complete DOM of website.
* @param oosLabels Out-of-stock labels.
*/
export function includesLabels(domText: string, oosLabels: string[]): boolean {
const domTextLowerCase = domText.toLowerCase();
return oosLabels.some(label => domTextLowerCase.includes(label));
}
+2 -30
View File
@@ -1,30 +1,2 @@
import {BestBuy} from './bestbuy'; export * from './lookup';
import {BAndH} from './bandh'; export * from './includes-labels';
import {Evga} from './evga';
import {NewEgg} from './newegg';
import {Nvidia} from './nvidia';
import {Amazon} from './amazon';
import {MicroCenter} from './microcenter';
import {Config} from '../config';
const masterList = new Map([
['amazon', Amazon],
['bestbuy', BestBuy],
['bandh', BAndH],
['evga', Evga],
['microcenter', MicroCenter],
['newegg', NewEgg],
['nvidia', Nvidia]
]);
const list = new Map();
const storeArray = Config.stores.split(',');
for (const name of storeArray) {
list.set(name, masterList.get(name));
}
export const Stores = Array.from(list.values());
export * from './store';
+85
View File
@@ -0,0 +1,85 @@
import puppeteer from 'puppeteer';
import {Config} from '../config';
import {Logger} from '../logger';
import open from 'open';
import {Store} from './model';
import {sendNotification} from '../notification';
import {includesLabels} from './includes-labels';
/**
* Returns true if the brand should be checked for stock
*
* @param brand The brand of the GPU
*/
function filterBrand(brand: string) {
if (Config.showOnlyBrands.length === 0) {
return true;
}
return Config.showOnlyBrands.includes(brand);
}
/**
* 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 Current browser in use.
* @param store Vendor of graphics cards.
*/
export async function lookup(browser: puppeteer.Browser, store: Store) {
/* eslint-disable no-await-in-loop */
for (const link of store.links) {
if (!filterBrand(link.brand)) {
continue;
}
const page = await browser.newPage();
page.setDefaultNavigationTimeout(Config.page.navigationTimeout);
await page.setUserAgent(Config.page.userAgent);
await page.setViewport({
height: Config.page.height,
width: Config.page.width
});
const graphicsCard = `${link.brand} ${link.model}`;
try {
await page.goto(link.url, {waitUntil: 'networkidle0'});
} catch {
Logger.error(`✖ [${store.name}] ${graphicsCard} skipping; timed out`);
await page.close();
continue;
}
const bodyHandle = await page.$('body');
const textContent = await page.evaluate(body => body.textContent, bodyHandle);
Logger.debug(textContent);
if (includesLabels(textContent, link.oosLabels)) {
Logger.info(`✖ [${store.name}] still out of stock: ${graphicsCard}`);
} else if (link.captchaLabels && includesLabels(textContent, link.captchaLabels)) {
Logger.warn(`✖ [${store.name}] CAPTCHA from: ${graphicsCard}`);
} else {
Logger.info(`🚀🚀🚀 [${store.name}] ${graphicsCard} IN STOCK 🚀🚀🚀`);
Logger.info(link.url);
if (Config.page.capture === 'true') {
Logger.debug(' saving screenshot');
await page.screenshot({path: `success-${Date.now()}.png`});
}
const givenUrl = store.cartUrl ? store.cartUrl : link.url;
if (Config.openBrowser === 'true') {
await open(givenUrl);
}
sendNotification(givenUrl);
}
await page.close();
}
/* eslint-enable no-await-in-loop */
}
+84
View File
@@ -0,0 +1,84 @@
import {Store} from './store';
export const AmazonCa: Store = {
links: [
{
brand: 'msi',
model: 'gaming trio',
url: 'https://www.amazon.ca/MSI-GeForce-RTX-3080-10G/dp/B08HR7SV3M?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'ftw3gaming',
url: 'https://www.amazon.ca/EVGA-GeForce-Technology-Backplate-10G-P5-3895-KR/dp/B08HR3DPGW?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'ftw3ultra',
url: 'https://www.amazon.ca/EVGA-GeForce-Technology-Backplate-10G-P5-3897-KR/dp/B08HR3Y5GQ?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'xc3ultra',
url: 'https://www.amazon.ca/EVGA-GeForce-Cooling-Backplate-10G-P5-3885-KR/dp/B08HR55YB5?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'xc3gaming',
url: 'https://www.amazon.ca/EVGA-GeForce-Cooling-Backplate-10G-P5-3883-KR/dp/B08HR4RJ3Q?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'xc3black',
url: 'https://www.amazon.ca/EVGA-GeForce-Gaming-Cooling-10G-P5-3881-KR/dp/B08HR6FMF3?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'gigabyte',
model: 'windforce',
url: 'https://www.amazon.ca/GIGABYTE-GeForce-Graphics-WINDFORCE-GV-N3080GAMING/dp/B08HJTH61J?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'gigabyte',
model: 'windforce eagle',
url: 'https://www.amazon.ca/GIGABYTE-GeForce-Graphics-WINDFORCE-GV-N3080EAGLE/dp/B08HJS2JLJ?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'asus',
model: 'tuf',
url: 'https://www.amazon.ca/Asus-90YV0FB0-M0AM00-TUF-RTX3080-10G-GAMING/dp/B08HHDP9DW?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'asus',
model: 'tufoc',
url: 'https://www.amazon.ca/Asus-90YV0FB1-M0AM00-TUF-RTX3080-O10G-GAMING/dp/B08HH5WF97?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'msi',
model: 'ventus',
url: 'https://www.amazon.ca/MSI-GeForce-RTX-3080-10G/dp/B08HR5SXPS?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}
],
name: 'amazon-ca'
};
@@ -1,55 +1,62 @@
import {Store} from './store'; import {Store} from './store';
export const Amazon: Store = { export const Amazon: Store = {
cartUrl: '',
links: [ links: [
{ {
brand: 'pny', brand: 'pny',
model: 'xlr8', model: 'xlr8',
url: 'https://www.amazon.com/PNY-GeForce-Gaming-Epic-X-Graphics/dp/B08HBR7QBM?ref_=ast_sto_dp', url: 'https://www.amazon.com/PNY-GeForce-Gaming-Epic-X-Graphics/dp/B08HBR7QBM?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'msi', brand: 'msi',
model: 'gaming trio', model: 'gaming trio',
url: 'https://www.amazon.com/MSI-GeForce-RTX-3080-10G/dp/B08HR7SV3M?ref_=ast_sto_dp', url: 'https://www.amazon.com/MSI-GeForce-RTX-3080-10G/dp/B08HR7SV3M?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'evga', brand: 'evga',
model: 'ftw3', model: 'ftw3',
url: 'https://www.amazon.com/EVGA-10G-P5-3897-KR-GeForce-Technology-Backplate/dp/B08HR3Y5GQ?ref_=ast_sto_dp', url: 'https://www.amazon.com/EVGA-10G-P5-3897-KR-GeForce-Technology-Backplate/dp/B08HR3Y5GQ?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'evga', brand: 'evga',
model: 'xc3', model: 'xc3',
url: 'https://www.amazon.com/EVGA-10G-P5-3885-KR-GeForce-Cooling-Backplate/dp/B08HR55YB5?ref_=ast_sto_dp', url: 'https://www.amazon.com/EVGA-10G-P5-3885-KR-GeForce-Cooling-Backplate/dp/B08HR55YB5?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'gigabyte', brand: 'gigabyte',
model: 'windforce', model: 'windforce',
url: 'https://www.amazon.com/GIGABYTE-GeForce-Graphics-WINDFORCE-GV-N3080GAMING/dp/B08HJTH61J?ref_=ast_sto_dp', url: 'https://www.amazon.com/GIGABYTE-GeForce-Graphics-WINDFORCE-GV-N3080GAMING/dp/B08HJTH61J?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'gigabyte', brand: 'gigabyte',
model: 'windforce eagle', model: 'windforce eagle',
url: 'https://www.amazon.com/GIGABYTE-GeForce-Graphics-WINDFORCE-GV-N3080EAGLE/dp/B08HJS2JLJ?ref_=ast_sto_dp', url: 'https://www.amazon.com/GIGABYTE-GeForce-Graphics-WINDFORCE-GV-N3080EAGLE/dp/B08HJS2JLJ?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'asus', brand: 'asus',
model: 'tuf', model: 'tuf',
url: 'https://www.amazon.com/ASUS-Graphics-DisplayPort-Military-Grade-Certification/dp/B08HH5WF97?ref_=ast_sto_dp', url: 'https://www.amazon.com/ASUS-Graphics-DisplayPort-Military-Grade-Certification/dp/B08HH5WF97?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}, },
{ {
brand: 'msi', brand: 'msi',
model: 'ventus', model: 'ventus',
url: 'https://www.amazon.com/MSI-GeForce-RTX-3080-10G/dp/B08HR5SXPS?ref_=ast_sto_dp', url: 'https://www.amazon.com/MSI-GeForce-RTX-3080-10G/dp/B08HR5SXPS?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'] oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
} }
], ],
name: 'amazon' name: 'amazon'
+62
View File
@@ -0,0 +1,62 @@
import {Store} from './store';
export const BAndH: Store = {
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']
},
{
brand: 'asus',
model: 'tuf',
url: 'https://www.bhphotovideo.com/c/product/1593649-REG/asus_tuf_rtx3080_10g_gaming_tuf_gaming_geforce_rtx.html',
oosLabels: ['notify when available']
},
{
brand: 'gigabyte',
model: 'gaming-oc',
url: 'https://www.bhphotovideo.com/c/product/1593333-REG/gigabyte_gv_n3080gaming_oc_10gd_geforce_rtx_3080_gaming.html',
oosLabels: ['notify when available']
},
{
brand: 'zotac',
model: 'trinity',
url: 'https://www.bhphotovideo.com/c/product/1592969-REG/zotac_zt_a30800d_10p_gaming_geforce_rtx_3080.html',
oosLabels: ['notify when available']
},
{
brand: 'asus',
model: 'tuf-oc',
url: 'https://www.bhphotovideo.com/c/product/1593650-REG/asus_tuf_rtx3080_o10g_gaming_tuf_gaming_geforce_rtx.html',
oosLabels: ['notify when available']
},
{
brand: 'msi',
model: 'xtrio',
url: 'https://www.bhphotovideo.com/c/product/1593996-REG/msi_g3080gxt10_geforce_rtx_3080_gaming.html',
oosLabels: ['notify when available']
},
{
brand: 'msi',
model: 'ventus',
url: 'https://www.bhphotovideo.com/c/product/1593997-REG/msi_g3080v3x10c_geforce_rtx_3080_ventus.html',
oosLabels: ['notify when available']
},
{
brand: 'msi',
model: 'TRIO2',
url: 'https://www.bhphotovideo.com/c/product/1593645-REG/msi_geforce_rtx_3080_gaming.html',
oosLabels: ['notify when available']
},
{
brand: 'msi',
model: 'ventus-oc',
url: 'https://www.bhphotovideo.com/c/product/1593646-REG/msi_geforce_rtx_3080_ventus.html',
oosLabels: ['notify when available']
}
],
name: 'bandh'
};
@@ -1,7 +1,6 @@
import {Store} from './store'; import {Store} from './store';
export const BestBuy: Store = { export const BestBuy: Store = {
cartUrl: '',
links: [ links: [
{ {
brand: 'asus', brand: 'asus',
@@ -18,7 +17,7 @@ export const BestBuy: Store = {
{ {
brand: 'evga', brand: 'evga',
model: 'xc3 ultra', model: 'xc3 ultra',
url: 'https://www.bestbuy.com/site/asus-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-strix-graphics-card-black/6432445.p?skuId=6432445', url: 'https://www.bestbuy.com/site/evga-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-graphics-card/6432400.p?skuId=6432400',
oosLabels: ['sold out', 'coming soon'] oosLabels: ['sold out', 'coming soon']
}, },
{ {
@@ -33,7 +32,6 @@ export const BestBuy: Store = {
url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3080-10g-gddr6x-pci-express-4-0-graphics-card-black/6430621.p?skuId=6430621', url: 'https://www.bestbuy.com/site/gigabyte-geforce-rtx-3080-10g-gddr6x-pci-express-4-0-graphics-card-black/6430621.p?skuId=6430621',
oosLabels: ['sold out', 'coming soon'] oosLabels: ['sold out', 'coming soon']
}, },
{ {
brand: 'msi', brand: 'msi',
model: 'ventus 3x', model: 'ventus 3x',
+32
View File
@@ -0,0 +1,32 @@
import {Store} from './store';
export const Evga: Store = {
links: [
{
brand: 'evga',
model: 'xc3 black',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3881-KR',
oosLabels: ['out of stock']
},
{
brand: 'evga',
model: 'ftw3',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3897-KR',
oosLabels: ['out of stock']
},
{
brand: 'evga',
model: 'xc3 gaming',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3883-KR',
oosLabels: ['out of stock']
},
{
brand: 'evga',
model: 'xc3 ultra gaming',
url: 'https://www.evga.com/products/product.aspx?pn=10G-P5-3885-KR',
oosLabels: ['out of stock']
}
],
name: 'evga'
};
+30
View File
@@ -0,0 +1,30 @@
import {BestBuy} from './bestbuy';
import {BAndH} from './bandh';
import {Evga} from './evga';
import {NewEgg} from './newegg';
import {Amazon} from './amazon';
import {MicroCenter} from './microcenter';
import {Config} from '../../config';
import {Nvidia} from './nvidia';
import {AmazonCa} from './amazon-ca';
const masterList = new Map([
[Amazon.name, Amazon],
[AmazonCa.name, AmazonCa],
[BestBuy.name, BestBuy],
[BAndH.name, BAndH],
[Evga.name, Evga],
[MicroCenter.name, MicroCenter],
[NewEgg.name, NewEgg],
[Nvidia.name, Nvidia]
]);
const list = new Map();
for (const name of Config.stores) {
list.set(name, masterList.get(name));
}
export const Stores = Array.from(list.values());
export * from './store';
@@ -1,7 +1,6 @@
import {Store} from './store'; import {Store} from './store';
export const MicroCenter: Store = { export const MicroCenter: Store = {
cartUrl: '',
links: [ links: [
{ {
brand: 'evga', brand: 'evga',
@@ -1,37 +1,41 @@
import {Store} from './store'; import {Store} from './store';
export const NewEgg: Store = { export const NewEgg: Store = {
cartUrl: '',
links: [ links: [
{ {
brand: 'asus', brand: 'asus',
model: 'tuf', model: 'tuf',
url: 'https://www.newegg.com/asus-geforce-rtx-3080-tuf-rtx3080-10g-gaming/p/N82E16814126453', url: 'https://www.newegg.com/asus-geforce-rtx-3080-tuf-rtx3080-10g-gaming/p/N82E16814126453',
oosLabels: ['auto notify', 'out of stock'] oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
}, },
{ {
brand: 'evga', brand: 'evga',
model: 'black gaming', model: 'black gaming',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3881-kr/p/N82E16814487522', url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3881-kr/p/N82E16814487522',
oosLabels: ['auto notify', 'out of stock'] oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
}, },
{ {
brand: 'evga', brand: 'evga',
model: 'argb led icx3', model: 'argb led icx3',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3883-kr/p/N82E16814487521', url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3883-kr/p/N82E16814487521',
oosLabels: ['auto notify', 'out of stock'] oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
}, },
{ {
brand: 'evga', brand: 'evga',
model: 'xc3 ultra gaming', model: 'xc3 ultra gaming',
url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3885-kr/p/N82E16814487520', url: 'https://www.newegg.com/evga-geforce-rtx-3080-10g-p5-3885-kr/p/N82E16814487520',
oosLabels: ['auto notify', 'out of stock'] oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
}, },
{ {
brand: 'msi', brand: 'msi',
model: 'ventus', model: 'ventus',
url: 'https://www.newegg.com/msi-geforce-rtx-3080-rtx-3080-ventus-3x-10g/p/N82E16814137600', url: 'https://www.newegg.com/msi-geforce-rtx-3080-rtx-3080-ventus-3x-10g/p/N82E16814137600',
oosLabels: ['auto notify', 'out of stock'] oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
} }
], ],
name: 'newegg' name: 'newegg'
@@ -1,7 +1,6 @@
import {Store} from './store'; import {Store} from './store';
export const Nvidia: Store = { export const Nvidia: Store = {
cartUrl: '',
links: [ links: [
{ {
brand: 'nvidia', brand: 'nvidia',
@@ -3,10 +3,11 @@ interface Link {
model: string; model: string;
url: string; url: string;
oosLabels: string[]; oosLabels: string[];
captchaLabels?: string[];
} }
export interface Store { export interface Store {
cartUrl: string; cartUrl?: string;
links: Link[]; links: Link[];
name: string; name: string;
} }
+1
View File
@@ -0,0 +1 @@
declare module 'play-sound';
+1
View File
@@ -0,0 +1 @@
declare module 'pushover-notifications';