Skip to content

Commit 0099d52

Browse files
init: Hits the stock API every 5 seconds and then loads up a page with the item added to cart.
Signed-off-by: Hari-Nagarajan <[email protected]>
1 parent 2300f5f commit 0099d52

10 files changed

+284
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/dist/
2+
/.idea/

Pipfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[[source]]
2+
name = "pypi"
3+
url = "https://pypi.org/simple"
4+
verify_ssl = true
5+
6+
[dev-packages]
7+
pyinstaller = "*"
8+
black = "*"
9+
10+
[packages]
11+
requests = "*"
12+
click = "*"
13+
14+
[requires]
15+
python_version = "3.7"

Pipfile.lock

+111
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from cli import cli
2+
3+
if __name__ == "__main__":
4+
cli.main()

app.spec

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# -*- mode: python ; coding: utf-8 -*-
2+
3+
block_cipher = None
4+
5+
6+
a = Analysis(['app.py'],
7+
pathex=['C:\\Users\\hari\\PycharmProjects\\nvidia-bot'],
8+
binaries=[],
9+
datas=[],
10+
hiddenimports=[],
11+
hookspath=[],
12+
runtime_hooks=[],
13+
excludes=[],
14+
win_no_prefer_redirects=False,
15+
win_private_assemblies=False,
16+
cipher=block_cipher,
17+
noarchive=False)
18+
pyz = PYZ(a.pure, a.zipped_data,
19+
cipher=block_cipher)
20+
exe = EXE(pyz,
21+
a.scripts,
22+
a.binaries,
23+
a.zipfiles,
24+
a.datas,
25+
[],
26+
name='app',
27+
debug=False,
28+
bootloader_ignore_signals=False,
29+
strip=False,
30+
upx=True,
31+
upx_exclude=[],
32+
runtime_tmpdir=None,
33+
console=True )

cli/__init__.py

Whitespace-only changes.

cli/cli.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import click
2+
3+
from cli.gpu import GPU
4+
from stores.nvidia import NvidiaBuyer
5+
6+
7+
@click.group()
8+
def main():
9+
pass
10+
11+
12+
@click.command()
13+
@click.argument("gpu", type=GPU())
14+
def buy(gpu):
15+
nv = NvidiaBuyer()
16+
nv.buy(gpu)
17+
18+
19+
main.add_command(buy)

cli/gpu.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import click
2+
3+
from stores.nvidia import GPU_DISPLAY_NAMES
4+
5+
6+
class GPU(click.ParamType):
7+
name = "api-key"
8+
9+
def convert(self, value, param, ctx):
10+
if value.upper() not in GPU_DISPLAY_NAMES.keys():
11+
self.fail(
12+
f"{value} is not a valid GPU, valid GPUs are {list(GPU_DISPLAY_NAMES.keys())}",
13+
param,
14+
ctx,
15+
)
16+
17+
return value.upper()

stores/__init__.py

Whitespace-only changes.

stores/nvidia.py

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import logging
2+
import webbrowser
3+
4+
import requests
5+
6+
log = logging.getLogger(__name__)
7+
formatter = logging.Formatter(
8+
"%(asctime)s : %(message)s : %(levelname)s -%(name)s", datefmt="%d%m%Y %I:%M:%S %p"
9+
)
10+
handler = logging.StreamHandler()
11+
handler.setFormatter(formatter)
12+
log.setLevel(10)
13+
log.addHandler(handler)
14+
15+
DIGITAL_RIVER_OUT_OF_STOCK_MESSAGE = "PRODUCT_INVENTORY_OUT_OF_STOCK"
16+
DIGITAL_RIVER_API_KEY = "9485fa7b159e42edb08a83bde0d83dia"
17+
DIGITAL_RIVER_PRODUCT_LIST_URL = "https://api.digitalriver.com/v1/shoppers/me/products"
18+
DIGITAL_RIVER_STOCK_CHECK_URL = "https://api.digitalriver.com/v1/shoppers/me/products/{product_id}/inventory-status?"
19+
20+
NVIDIA_CART_URL = "https://store.nvidia.com/store/nvidia/en_US/buy/productID.{product_id}/clearCart.yes/nextPage.QuickBuyCartPage"
21+
22+
GPU_DISPLAY_NAMES = {
23+
"2060S": "NVIDIA GEFORCE RTX 2060 SUPER",
24+
"3080": "NVIDIA GEFORCE RTX 3080",
25+
"3090": "NVIDIA GEFORCE RTX 3090",
26+
}
27+
28+
29+
def add_to_cart(product_id):
30+
log.info(f"Adding {product_id} to cart!")
31+
webbrowser.open_new(NVIDIA_CART_URL.format(product_id=product_id))
32+
33+
34+
def is_in_stock(product_id):
35+
payload = {
36+
"apiKey": DIGITAL_RIVER_API_KEY,
37+
}
38+
39+
url = DIGITAL_RIVER_STOCK_CHECK_URL.format(product_id=product_id)
40+
41+
log.debug(f"Calling {url}")
42+
response = requests.get(url, headers={"Accept": "application/json"}, params=payload)
43+
log.debug(f"Returned {response.status_code}")
44+
response_json = response.json()
45+
product_status_message = response_json["inventoryStatus"]["status"]
46+
log.info(f"Stock status is {product_status_message}")
47+
return product_status_message != DIGITAL_RIVER_OUT_OF_STOCK_MESSAGE
48+
49+
50+
class NvidiaBuyer:
51+
def __init__(self):
52+
self.product_data = {}
53+
self.get_product_ids()
54+
print(self.product_data)
55+
56+
def get_product_ids(self, url=DIGITAL_RIVER_PRODUCT_LIST_URL):
57+
log.debug(f"Calling {url}")
58+
payload = {
59+
"apiKey": DIGITAL_RIVER_API_KEY,
60+
"expand": "product",
61+
"fields": "product.id,product.displayName,product.pricing",
62+
}
63+
response = requests.get(
64+
url, headers={"Accept": "application/json"}, params=payload
65+
)
66+
67+
log.debug(response.status_code)
68+
response_json = response.json()
69+
for product_obj in response_json["products"]["product"]:
70+
if product_obj["displayName"] in GPU_DISPLAY_NAMES.values():
71+
self.product_data[product_obj["displayName"]] = {
72+
"id": product_obj["id"],
73+
"price": product_obj["pricing"]["formattedListPrice"],
74+
}
75+
if response_json["products"].get("nextPage"):
76+
self.get_product_ids(url=response_json["products"]["nextPage"]["uri"])
77+
78+
def buy(self, gpu):
79+
product_id = self.product_data.get(GPU_DISPLAY_NAMES[gpu])["id"]
80+
log.info(f"Checking stock for {GPU_DISPLAY_NAMES[gpu]}...")
81+
while not is_in_stock(product_id):
82+
sleep(5)
83+
add_to_cart(product_id)

0 commit comments

Comments
 (0)