Compare commits

..

51 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
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
42 changed files with 1271 additions and 228 deletions
+5
View File
@@ -0,0 +1,5 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
+18
View File
@@ -0,0 +1,18 @@
EMAIL_USERNAME="youremail@gmail.com"
EMAIL_PASSWORD="secretpassword"
NOTIFICATION_TEST="false"
PAGE_TIMEOUT="30000"
RATE_LIMIT_TIMEOUT="5000"
SLACK_CHANNEL="SlackChannelName"
SLACK_TOKEN="slack-token"
STORES="bestbuy,bandh,nvidia"
PHONE_NUMBER="1234567890"
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"
-3
View File
@@ -1,3 +0,0 @@
EMAIL_USERNAME="youremail@gmail.com"
EMAIL_PASSWORD="secretpassword"
STORES="bestbuy,bandh,nvidia"
+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
+4
View File
@@ -1,3 +1,7 @@
<!-- 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) -->
+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
+65
View File
@@ -1,5 +1,70 @@
# 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)
### 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)
+70 -28
View File
@@ -1,12 +1,14 @@
# 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)
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 +26,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)
| | **Amazon** | **EVGA** | **Best Buy** | **B&H** | **Micro Center** | **Newegg** | **Nvidia** |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| **3070**| | | | | | | |
| **3080** | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` | `✔` |
| **3090** | | | | | | | |
## Installation and prerequisites
@@ -42,30 +40,74 @@ 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.
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` |
|:---:|---|
| `EMAIL_USERNAME` | Gmail address (e.g., `jensen.robbed.us@gmail.com`); optional |
| `EMAIL_PASSWORD` | Gmail password; see below if you have MFA; optional |
| `NOTIFICATION_TEST` | Test all the notifications configured; optional, default: `false` |
| `PAGE_TIMEOUT` | Navigation Timeout in milliseconds (`0` for infinite); optional, default: `30000` |
| `PHONE_NUMBER` | 10 digit phone number (e.g., `1234567890`); 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` |
| `SHOW_ONLY_BRANDS` | If set, will only show specified brands, seperated by `,` |
| `SLACK_CHANNEL` | Slack channel for posting (e.g., `update`); optional |
| `SLACK_TOKEN` | Slack API token; optional |
| `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: Free sounds available [here](https://freesound.org/home/). Place sounds into `resources/sounds/`
#### Supported stores
| **Stores** | **Environment variable** |
|:---:|:---:|
| Best Buy | `bestbuy`|
| Amazon.ca | `amazon-ca`|
| Amazon | `amazon`|
| B&H | `bandh`|
| EVGA | `evga`|
| Micro Center | `microcenter`|
| Newegg | `newegg`|
| Nvidia | `nvidia`|
#### 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
**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.
+345 -9
View File
@@ -1,6 +1,6 @@
{
"name": "nvidia-snatcher",
"version": "1.0.0",
"version": "1.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -173,6 +173,48 @@
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
"dev": true
},
"@slack/logger": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@slack/logger/-/logger-2.0.0.tgz",
"integrity": "sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==",
"dev": true,
"requires": {
"@types/node": ">=8.9.0"
}
},
"@slack/types": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@slack/types/-/types-1.9.0.tgz",
"integrity": "sha512-RmwgMWqOtzd2JPXdiaD/tyrDD0vtjjRDFdxN1I3tAxwBbg4aryzDUVqFc8na16A+3Xik/UN8X1hvVTw8J4EB9w==",
"dev": true
},
"@slack/web-api": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-5.12.0.tgz",
"integrity": "sha512-ygSnNHVid7PltGo7W36f2SNVHyliemkzxn9uSwgnWNF7CHmWBKWAylU/eoDml9l5K7akMOxbousiurOw4XqOFg==",
"dev": true,
"requires": {
"@slack/logger": ">=1.0.0 <3.0.0",
"@slack/types": "^1.7.0",
"@types/is-stream": "^1.1.0",
"@types/node": ">=8.9.0",
"@types/p-queue": "^2.3.2",
"axios": "^0.19.0",
"eventemitter3": "^3.1.0",
"form-data": "^2.5.0",
"is-stream": "^1.1.0",
"p-queue": "^2.4.2",
"p-retry": "^4.0.0"
},
"dependencies": {
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
}
}
},
"@szmarczak/http-timer": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
@@ -188,6 +230,11 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@@ -204,6 +251,15 @@
"@types/node": "*"
}
},
"@types/is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/json-schema": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
@@ -216,6 +272,11 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"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": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -248,6 +309,12 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/p-queue": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/p-queue/-/p-queue-2.3.2.tgz",
"integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@@ -263,6 +330,22 @@
"@types/node": "*"
}
},
"@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"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": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
@@ -564,6 +647,12 @@
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
@@ -576,6 +665,30 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"axios": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"requires": {
"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": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -965,6 +1078,15 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"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": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -1116,6 +1238,15 @@
"text-hex": "1.0.x"
}
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -1410,6 +1541,12 @@
}
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"des.js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@@ -1485,6 +1622,15 @@
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
"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": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@@ -2316,6 +2462,12 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"eventemitter3": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
"dev": true
},
"events": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
@@ -2622,6 +2774,12 @@
"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": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
@@ -2670,12 +2828,46 @@
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
"dev": true
},
"form-data": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@@ -3413,8 +3605,7 @@
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-types": {
"version": "1.0.0",
@@ -3577,8 +3768,7 @@
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lodash.get": {
"version": "4.4.2",
@@ -3659,6 +3849,22 @@
"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": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -3691,8 +3897,7 @@
"map-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz",
"integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==",
"dev": true
"integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g=="
},
"map-visit": {
"version": "1.0.0",
@@ -3792,6 +3997,39 @@
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"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": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz",
@@ -3866,6 +4104,21 @@
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"dev": true
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"dev": true,
"requires": {
"mime-db": "1.44.0"
}
},
"mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
@@ -3994,6 +4247,15 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"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": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
@@ -4321,12 +4583,28 @@
"p-limit": "^2.2.0"
}
},
"p-queue": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-2.4.2.tgz",
"integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==",
"dev": true
},
"p-reduce": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz",
"integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==",
"dev": true
},
"p-retry": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz",
"integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==",
"dev": true,
"requires": {
"@types/retry": "^0.12.0",
"retry": "^0.12.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -4392,6 +4670,15 @@
"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": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -4476,6 +4763,15 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
@@ -4616,6 +4912,11 @@
"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": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -4890,6 +5191,12 @@
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"dev": true
},
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"dev": true
},
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -5043,6 +5350,15 @@
"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": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -5638,6 +5954,14 @@
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
"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": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
@@ -5664,8 +5988,7 @@
"tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
"dev": true
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
},
"tsutils": {
"version": "3.17.1",
@@ -5894,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": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
@@ -5953,6 +6281,14 @@
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"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": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+5 -1
View File
@@ -1,6 +1,6 @@
{
"name": "nvidia-snatcher",
"version": "1.0.0",
"version": "1.4.0",
"description": "🔮 For all your Nvidia needs",
"main": "src/index.ts",
"scripts": {
@@ -22,15 +22,19 @@
"homepage": "https://github.com/jef/nvidia-snatcher#readme",
"dependencies": {
"dotenv": "^8.2.0",
"messaging-api-telegram": "^1.0.0",
"nodemailer": "^6.4.11",
"open": "^7.2.1",
"puppeteer": "^5.3.0",
"pushover-notifications": "^1.2.2",
"winston": "^3.3.3"
},
"devDependencies": {
"@slack/web-api": "^5.12.0",
"@types/node": "^14.11.1",
"@types/nodemailer": "^6.4.0",
"@types/puppeteer": "^3.0.2",
"play-sound": "^1.1.3",
"rimraf": "^3.0.2",
"typescript": "^4.0.2",
"xo": "^0.33.1"
+44 -13
View File
@@ -3,27 +3,58 @@ import {config} from 'dotenv';
config({path: resolve(__dirname, '../.env')});
const email = {
username: process.env.EMAIL_USERNAME,
password: process.env.EMAIL_PASSWORD
};
const notifications = {
email: email.username && email.password
email: {
username: process.env.EMAIL_USERNAME ?? '',
password: process.env.EMAIL_PASSWORD ?? ''
},
phone: {
availableCarriers: new Map([
['att', 'txt.att.net'],
['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: {
channel: process.env.SLACK_CHANNEL ?? '',
token: process.env.SLACK_TOKEN ?? ''
},
telegram: {
accessToken: process.env.TELEGRAM_ACCESS_TOKEN ?? '',
chatId: process.env.TELEGRAM_CHAT_ID ?? ''
},
test: process.env.NOTIFICATION_TEST ?? 'false'
};
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
capture: process.env.SCREENSHOT ?? 'true',
width: 1920,
height: 1080,
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'
};
const stores = process.env.STORES ?? 'nvidia';
const openBrowser = process.env.OPEN_BROWSER ?? 'true';
const rateLimitTimeout = Number(process.env.RATE_LIMIT_TIMEOUT) ?? 5000;
const stores = process.env.STORES ? process.env.STORES.split(',') : ['nvidia'];
const showOnlyBrands = process.env.SHOW_ONLY_BRANDS ? process.env.SHOW_ONLY_BRANDS.split(',') : [];
export const Config = {
email,
notifications,
rateLimitTimeout,
page,
rateLimitTimeout: 5000,
stores
stores,
openBrowser,
showOnlyBrands
};
+11 -63
View File
@@ -1,86 +1,34 @@
import {Config} from './config';
import {Store, Stores} from './store';
import puppeteer from 'puppeteer';
import open from 'open';
import sendNotification from './notification';
import {Stores} from './store/model';
import {Logger} from './logger';
import {sendNotification} from './notification';
import {lookup} from './store';
import puppeteer from 'puppeteer';
/**
* Starts the bot.
*/
async function main() {
const results = [];
const browser = await puppeteer.launch();
for (const store of Stores) {
Logger.debug(store.links);
results.push(lookup(store));
results.push(lookup(browser, store));
}
await Promise.all(results);
await browser.close();
Logger.info('↗ trying stores again');
setTimeout(main, Config.rateLimitTimeout);
}
/**
* 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.
* Send test email.
*/
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({
height: Config.page.height,
width: Config.page.width
});
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);
}
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();
let result = false;
for (const oosLabel of oosLabels) {
result = domTextLowerCase.includes(oosLabel.toLowerCase());
}
return result;
if (Config.notifications.test === 'true') {
sendNotification('test');
}
/**
+3 -1
View File
@@ -1,11 +1,13 @@
import winston, {format} from 'winston';
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 `[${timestamp}] ${info.level} :: ${info.message}`;
});
export const Logger = winston.createLogger({
+6 -5
View File
@@ -3,23 +3,24 @@ import Mail from 'nodemailer/lib/mailer';
import {Config} from '../config';
import {Logger} from '../logger';
const email = Config.notifications.email;
const subject = 'NVIDIA - BUY NOW';
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: Config.email.username,
pass: Config.email.password
user: email.username,
pass: email.password
}
});
const mailOptions: Mail.Options = {
from: Config.email.username,
to: Config.email.username,
from: email.username,
to: email.username,
subject
};
export default function sendEmail(text: string) {
export function sendEmail(text: string) {
mailOptions.text = text;
transporter.sendMail(mailOptions, (error, info) => {
+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';
+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}`);
}
});
}
+23
View File
@@ -0,0 +1,23 @@
import {WebClient} from '@slack/web-api';
import {Config} from '../config';
import {Logger} from '../logger';
const channel = Config.notifications.slack.channel;
const token = Config.notifications.slack.token;
const web = new WebClient(token);
export function sendSlackMessage(text: string) {
(async () => {
try {
const result = await web.chat.postMessage({text, channel});
if (!result.ok) {
Logger.error(result.error);
return;
}
Logger.info(`✔ slack message sent to '${channel}': ${text}`);
} catch (error) {
Logger.error(error);
}
})();
}
+41
View File
@@ -0,0 +1,41 @@
import nodemailer from 'nodemailer';
import Mail from 'nodemailer/lib/mailer';
import {Config} from '../config';
import {Logger} from '../logger';
const subject = 'NVIDIA - BUY NOW';
const [email, phone] = [Config.notifications.email, Config.notifications.phone];
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: email.username,
pass: email.password
}
});
const mailOptions: Mail.Options = {
from: Config.notifications.email.username,
to: generateAddress(),
subject
};
export function sendSMS(text: string) {
mailOptions.text = text;
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
Logger.error(error);
} else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.info(`✔ sms sent: ${info.response}`);
}
});
}
function generateAddress() {
const carrier = phone.carrier.toLowerCase();
if (carrier && phone.availableCarriers.has(carrier)) {
return [phone.number, phone.availableCarriers.get(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'
};
-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'
};
+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 -32
View File
@@ -1,32 +1,2 @@
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';
export * from './includes-labels';
+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'
};
+63
View File
@@ -0,0 +1,63 @@
import {Store} from './store';
export const Amazon: Store = {
links: [
{
brand: 'pny',
model: 'xlr8',
url: 'https://www.amazon.com/PNY-GeForce-Gaming-Epic-X-Graphics/dp/B08HBR7QBM?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'msi',
model: 'gaming trio',
url: 'https://www.amazon.com/MSI-GeForce-RTX-3080-10G/dp/B08HR7SV3M?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'ftw3',
url: 'https://www.amazon.com/EVGA-10G-P5-3897-KR-GeForce-Technology-Backplate/dp/B08HR3Y5GQ?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'evga',
model: 'xc3',
url: 'https://www.amazon.com/EVGA-10G-P5-3885-KR-GeForce-Cooling-Backplate/dp/B08HR55YB5?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'gigabyte',
model: 'windforce',
url: 'https://www.amazon.com/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.com/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.com/ASUS-Graphics-DisplayPort-Military-Grade-Certification/dp/B08HH5WF97?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
},
{
brand: 'msi',
model: 'ventus',
url: 'https://www.amazon.com/MSI-GeForce-RTX-3080-10G/dp/B08HR5SXPS?ref_=ast_sto_dp',
oosLabels: ['currently unavailable'],
captchaLabels: ['enter the characters you see below']
}
],
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'
};
+49
View File
@@ -0,0 +1,49 @@
import {Store} from './store';
export const BestBuy: Store = {
links: [
{
brand: 'asus',
model: 'rog strix',
url: 'https://www.bestbuy.com/site/asus-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-strix-graphics-card-black/6432445.p?skuId=6432445',
oosLabels: ['sold out', 'coming soon']
},
{
brand: 'evga',
model: 'xc3 black',
url: 'https://www.bestbuy.com/site/evga-geforce-rtx-3080-10gb-gddr6x-pci-express-4-0-graphics-card/6432399.p?skuId=6432399',
oosLabels: ['sold out', 'coming soon']
},
{
brand: 'evga',
model: 'xc3 ultra',
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']
},
{
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', 'coming soon']
},
{
brand: 'gigabyte',
model: 'eagle',
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']
},
{
brand: 'msi',
model: 'ventus 3x',
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',
oosLabels: ['sold out', 'coming soon']
},
{
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', 'coming soon']
}
],
name: 'bestbuy'
};
+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';
+43
View File
@@ -0,0 +1,43 @@
import {Store} from './store';
export const MicroCenter: Store = {
links: [
{
brand: 'evga',
model: 'xc3 ultra gaming',
url: 'https://www.microcenter.com/product/628344/evga-geforce-rtx-3080-xc3-ultra-gaming-triple-fan-10gb-gddr6x-pcie-40-graphics-card',
oosLabels: ['sold out']
},
{
brand: 'msi',
model: 'ventus 3x overclocked',
url: 'https://www.microcenter.com/product/628331/msi-geforce-rtx-3080-ventus-3x-overclocked-triple-fan-10gb-gddr6x-pcie-40-graphics-card',
oosLabels: ['sold out']
},
{
brand: 'asus',
model: 'tuf gaming',
url: 'https://www.microcenter.com/product/628303/asus-geforce-rtx-3080-tuf-gaming-triple-fan-10gb-gddr6x-pcie-40-graphics-card',
oosLabels: ['sold out']
},
{
brand: 'msi',
model: 'gaming x trio',
url: 'https://www.microcenter.com/product/628330/msi-geforce-rtx-3080-gaming-x-trio-triple-fan-10gb-gddr6x-pcie-40-graphics-card',
oosLabels: ['sold out']
},
{
brand: 'evga',
model: 'xc3 black',
url: 'https://www.microcenter.com/product/628340/evga-geforce-rtx-3080-xc3-black-triple-fan-10gb-gddr6x-pcie-40-graphics-card',
oosLabels: ['sold out']
},
{
brand: 'zotac',
model: 'trinity overclocked',
url: 'https://www.microcenter.com/product/628607/zotac-geforce-rtx-3080-trinity-overclocked-triple-fan-10gb-gddr6x-pcie-40-graphics-card',
oosLabels: ['sold out']
}
],
name: 'microcenter'
};
@@ -1,37 +1,41 @@
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']
oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
},
{
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']
oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
},
{
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']
oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
},
{
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']
oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
},
{
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']
oosLabels: ['auto notify', 'out of stock'],
captchaLabels: ['are you a human?']
}
],
name: 'newegg'
+13
View File
@@ -0,0 +1,13 @@
import {Store} from './store';
export const Nvidia: Store = {
links: [
{
brand: 'nvidia',
model: 'founders edition',
url: 'https://api.digitalriver.com/v1/shoppers/me/products/5438481700/inventory-status?apiKey=9485fa7b159e42edb08a83bde0d83dia',
oosLabels: ['product_inventory_out_of_stock']
}
],
name: 'nvidia'
};
@@ -3,10 +3,11 @@ interface Link {
model: string;
url: string;
oosLabels: string[];
captchaLabels?: string[];
}
export interface Store {
cartUrl: string;
cartUrl?: string;
links: Link[];
name: string;
}
-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'
};
+1
View File
@@ -0,0 +1 @@
declare module 'play-sound';
+1
View File
@@ -0,0 +1 @@
declare module 'pushover-notifications';