Files
GraphicsCardScripts/src/StockTrackingBot.py
T
wuliaozhiji f1c7da5a0e [O] Finish
2022-02-28 22:56:55 -05:00

125 lines
3.9 KiB
Python

import os
import re
import sys
import time
import traceback
import requests
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
# Config
# Price increase ratio threshold (ignore everything higher than this ratio)
INCR_MAX = 0.2
# Telegram chat ID that receives update messages (could be a channel in @channel_id format)
# TG_RECEIVER = 1770239825
TG_RECEIVER = '@toronto_bestbuy_gpu'
# Telegram bot token
TG_TOKEN = os.environ['TG_TOKEN']
# Constants
CSS = By.CSS_SELECTOR
MODELS = [
['3060 ti', 400, 132],
['3070 ti', 600, 167],
['3080 ti', 1200, 233],
['3050', 250, 73],
['3060', 330, 98],
['3070', 500, 154],
['3080', 700, 204],
['3090', 1500, 236],
]
USD_TO_CAD = 1.27
AVAIL_TABLE: dict[str, bool] = {}
TITLE_SHORTEN = re.compile('(rtx|nvidia|geforce|edition|gddr[56]x*|video|card)', flags=re.IGNORECASE)
def shorten_title(title: str):
short_title = TITLE_SHORTEN.sub('', title)
while ' ' in short_title:
short_title = short_title.replace(' ', ' ')
return short_title.strip()
def send_message(msg: str):
r = requests.get(f'https://api.telegram.org/bot{TG_TOKEN}/sendMessage',
params={'chat_id': TG_RECEIVER, 'parse_mode': 'Markdown', 'text': msg})
if r.status_code != 200:
print('Request not OK:', r.status_code, r.text)
def parse_page(browser: Chrome):
# Parse page
for item in browser.find_elements(By.CLASS_NAME, 'x-productListItem'):
title = item.find_element(CSS, 'div[data-automation="productItemName"]').get_attribute('innerHTML')
title = shorten_title(title)
# Check availability
avail = item.find_elements(CSS, 'div[data-automation="store-availability-messages"] span[data-automation="store-availability-checkmark"]')
# Not available, check if it was previously available
if len(avail) == 0:
if title in AVAIL_TABLE:
send_message(f'Sold out: `{title}`')
del AVAIL_TABLE[title]
continue
# Already sent availability message
if title in AVAIL_TABLE:
continue
# Get price
price = item.find_element(CSS, 'div[data-automation="product-pricing"] > span > div').get_attribute('innerHTML')
price = round(float(price.replace(',', '')[1:]))
price_usd = price / USD_TO_CAD
# Find model
lower = title.lower()
model = [c for c in MODELS if c[0] in lower]
if not model:
continue
model = model[0]
# Calculate price increase
price_incr = (price_usd - model[1]) / model[1]
value = model[2] / price_usd * 100
# Check incr threshold
if price_incr > INCR_MAX:
print(' '.join(title.split()[:2]), f'is in stock but price increase {price_incr * 100:.0f}% '
f'is larger than defined threshold.')
# continue
# Find link
link = item.find_element(CSS, 'a').get_attribute('href')
# Available and meets threshold criteria, notify user
AVAIL_TABLE[title] = True
send_message(f'{model[0].upper()} Became Available!\n'
f'\n'
f'${price:.0f} | {price_incr * 100:.0f}% Incr | Value: {value:.0f}\n'
f'- [{title}]({link})')
if __name__ == '__main__':
web_options = Options()
# web_options.headless = True
browser = Chrome(options=web_options)
browser.get('https://www.bestbuy.ca/en-ca/collection/graphics-cards-with-nvidia-chipset/349221')
# parse_page(browser)
# browser.close()
# Refresh indefinitely
while True:
try:
parse_page(browser)
except Exception as e:
traceback.print_exc()
time.sleep(5)
browser.refresh()
time.sleep(2)