samuelm2/Nvidia-Notify

nvidia website gives false positives

samuelm2 opened this issue · 13 comments

recently the nvidia website started giving false positives. I haven't had time to figure out a way around these false positives, so if someone has time to figure it out and wants to submit a PR that'd be great

I was getting false positives on the 3080 search page, but am no longer when using main 3080 page on nvidia.

Picking up a false positive on best buy after the recent restock. cart.svg doesn't appear in the html.

If that's replaced with the css classes for the active button it seems to work correctly again:
"btn btn-primary btn-sm btn-block btn-leading-ficon add-to-cart-button"

EKMN commented

One quite robust way could be to read directly from their store API.

Example below (5438481700 is the product ID for 3080)

GET https://api-prod.nvidia.com/direct-sales-shop/DR/products/en_us/USD/5438481700

{
  "products": {
    "product": [
      {
        "id": 5438481700,
        "name": "NVIDIA GEFORCE RTX 3080 - US",
        "displayName": "NVIDIA GEFORCE RTX 3080",
        "sku": "900-1G133-2530-000",
        "displayableProduct": "false",
        "manufacturerPartNumber": "900-1G133-2530-000",
        "maximumQuantity": 1,
        "thumbnailImage": "https://drh1.img.digitalriver.com/DRHM/Storefront/Company/nvidia/images/product/thumbnail/geforce-rtx-3080-shop-thumbnail-100x100.png",
        "customAttributes": {
          "attribute": [
            {
              "name": "paypalOff",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "nonSolr",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "isGFNBundle",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "needsRestrictedShippingOption",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "shippingCountries",
              "type": "String",
              "value": "US,CA,PR,VI"
            },
            {
              "name": "shipmentAgreement",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "CategorySelection",
              "type": "String",
              "value": "Other"
            },
            {
              "name": "usps",
              "type": "String",
              "value": "false"
            },
            {
              "name": "additionalCartMessage",
              "type": "String",
              "value": "Limit 1 per customer"
            },
            {
              "name": "isGFN",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "preorderOverride",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "originalIsViewable",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "clonedFromStaging",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "landedCost",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "originalIsOrderable",
              "type": "Boolean",
              "value": "true"
            },
            {
              "name": "length",
              "type": "Quantity",
              "value": "357.000 mm"
            },
            {
              "name": "upc",
              "type": "String",
              "value": "812674024509"
            },
            {
              "name": "weight",
              "type": "Quantity",
              "value": "2.500 kg"
            },
            {
              "name": "disablePayPal",
              "type": "Boolean",
              "value": "true"
            },
            {
              "name": "hideInfo",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "hideQty",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "nvidiaPVDecoder",
              "type": "String",
              "value": "PureVideo Decoder Bronze"
            },
            {
              "name": "width",
              "type": "Quantity",
              "value": "166.000 mm"
            },
            {
              "name": "ProductDescription",
              "type": "String",
              "value": "GeForce"
            },
            {
              "name": "privateStoreOnly",
              "type": "Boolean",
              "value": "false"
            },
            {
              "name": "shipsInTimeFrameOverrideEnabled",
              "type": "Boolean",
              "value": "false"
            }
          ]
        },
        "pricing": {
          "uri": "https://dispatch-nvidia.digitalriver.com/v1/shoppers/me/products/5438481700/pricing",
          "listPrice": {
            "currency": "USD",
            "value": 699
          },
          "listPriceWithQuantity": {
            "currency": "USD",
            "value": 699
          },
          "salePriceWithQuantity": {
            "currency": "USD",
            "value": 699
          },
          "formattedListPrice": "$699.00",
          "formattedListPriceWithQuantity": "$699.00",
          "formattedSalePriceWithQuantity": "$699.00",
          "totalDiscountWithQuantity": {
            "currency": "USD",
            "value": 0
          },
          "formattedTotalDiscountWithQuantity": "$0.00",
          "listPriceIncludesTax": "false",
          "tax": {
            "vatPercentage": 0
          },
          "feePricing": {
            "salePriceWithFeesAndQuantity": {
              "currency": "USD",
              "value": 699
            },
            "formattedSalePriceWithFeesAndQuantity": "$699.00"
          }
        },
        "inventoryStatus": {
          "uri": "https://dispatch-nvidia.digitalriver.com/v1/shoppers/me/products/5438481700/inventory-status",
          "availableQuantityIsEstimated": "false",
          "productIsInStock": "true",
          "productIsAllowsBackorders": "false",
          "productIsTracked": "true",
          "requestedQuantityAvailable": "true",
          "status": "PRODUCT_INVENTORY_OUT_OF_STOCK",
          "statusIsEstimated": "false"
        },
        "relatedProducts": [
          {
            "id": 5440852400,
            "name": "GeForce NOW 1 Year Membership (RTX 30 Series Offer)",
            "image": "https://drh1.img.digitalriver.com/DRHM/Storefront/Company/nvidia/images/product/thumbnail/geforce-rtx-bundle-geforce-now-thumbnail-100x100.png"
          },
          {
            "id": 5440851900,
            "name": "Watch Dogs: Legion",
            "image": "https://drh1.img.digitalriver.com/DRHM/Storefront/Company/nvidia/images/product/thumbnail/geforce-rtx-bundle-watch-dogs-legion-thumbnail-100x100.png"
          }
        ],
        "viewStyle": "ampere"
      }
    ]
  }
}

The interesting bit there is this:

{
   "inventoryStatus":{
      "uri":"https://dispatch-nvidia.digitalriver.com/v1/shoppers/me/products/5438481700/inventory-status",
      "availableQuantityIsEstimated":"false",
      "productIsInStock":"true",
      "productIsAllowsBackorders":"false",
      "productIsTracked":"true",
      "requestedQuantityAvailable":"true",
      "status":"PRODUCT_INVENTORY_OUT_OF_STOCK",
      "statusIsEstimated":"false"
   }
}

Specifically:

{
  "status":"PRODUCT_INVENTORY_OUT_OF_STOCK",
}

this is what I did to get around nvidia false positives. Not ideal but it seems to be working... for now. Above approach is miles better. @EKMN how do I run GET in python? Thanks

def got_stock(url, info):
    try:
        if info[2]:
            html = seleniumGet(url)
        else:
            html = urllibGet(url)
    except Exception as e:
        print("Connection failed...")
        print(e)
        return False
    keyWord = info[0]
    alertOnFound = info[1]
    index = html.find(keyWord)
    index = html.upper().find(keyWord.upper())
    gotStock=False
    if alertOnFound and index != -1 and "show-out-of-stock" not in html[index-150:index+150]:
        print(index)
        print(html[index-150:index+150])
        #alert(url)
        gotStock = True
    elif not alertOnFound and index == -1:
        #print(html[index-50:index+50])
        gotStock = True
    return gotStock

def main():
    numSearches = 0
    while True:
        now = datetime.now()
        current_time = now.strftime("%H:%M:%S")
        print("Starting search {} at {}".format(numSearches, current_time))
        numSearches += 1
        for url, info in urlKeyWords.items():
            print("\tChecking {}...".format(info[3]))
            if got_stock(url, info):
                print("........confirming in stock signal")
                baseSleepAmt = 1
                totalSleep = baseSleepAmt + random.uniform(0, 4)
                time.sleep(totalSleep)
                if got_stock(url, info):
                    alert(url)
        baseSleepAmt = 1
        totalSleep = baseSleepAmt + random.uniform(6, 20)
        # print("Sleeping for {} seconds".format(totalSleep))
        time.sleep(totalSleep)


if __name__ == '__main__':
    main()

how often are you guys getting them?

under def seleniumGet i added:
driver.set_page_load_timeout(60)

so far so good. so it either helped, or i've just been extremely lucky.

i've also adjusted the sleep timings as i didn't want to get IP banned by nvidia/bbuy etc.
baseSleepAmt = 30
totalSleep = baseSleepAmt + random.uniform(0, 10)

@dereckhall
bestbuy - havent gotten any yet
nvidia - was getting it on every 10-ish request. Now it was running for 2 hours no issues.

Good point on ban, would suck to also switch vpn every now and then. Might increase wait time to be a little longer

EKMN commented

this is what I did to get around nvidia false positives. Not ideal but it seems to be working... for now. Above approach is miles better. @EKMN how do I run GET in python? Thanks

def got_stock(url, info):
    try:
        if info[2]:
            html = seleniumGet(url)
        else:
            html = urllibGet(url)
    except Exception as e:
        print("Connection failed...")
        print(e)
        return False
    keyWord = info[0]
    alertOnFound = info[1]
    index = html.find(keyWord)
    index = html.upper().find(keyWord.upper())
    gotStock=False
    if alertOnFound and index != -1 and "show-out-of-stock" not in html[index-150:index+150]:
        print(index)
        print(html[index-150:index+150])
        #alert(url)
        gotStock = True
    elif not alertOnFound and index == -1:
        #print(html[index-50:index+50])
        gotStock = True
    return gotStock

def main():
    numSearches = 0
    while True:
        now = datetime.now()
        current_time = now.strftime("%H:%M:%S")
        print("Starting search {} at {}".format(numSearches, current_time))
        numSearches += 1
        for url, info in urlKeyWords.items():
            print("\tChecking {}...".format(info[3]))
            if got_stock(url, info):
                print("........confirming in stock signal")
                baseSleepAmt = 1
                totalSleep = baseSleepAmt + random.uniform(0, 4)
                time.sleep(totalSleep)
                if got_stock(url, info):
                    alert(url)
        baseSleepAmt = 1
        totalSleep = baseSleepAmt + random.uniform(6, 20)
        # print("Sleeping for {} seconds".format(totalSleep))
        time.sleep(totalSleep)


if __name__ == '__main__':
    main()

You can basically use any request library that makes a GET request (usually the default option). Then parse the JSON response and evaluate the key-value pair that I highlighted.

Heres a basic example - not sure what possible status codes there are though.

        response = requests.get("https://api-prod.nvidia.com/direct-sales-shop/DR/products/en_us/USD/5438481700")
        item = response.json()

        print(item['products']['product'][0]['inventoryStatus']['status'])
        if item['products']['product'][0]['inventoryStatus']['status'] != "PRODUCT_INVENTORY_OUT_OF_STOCK":
            alert("Nvidia API")

Try this:

def got_stock(url, info):
    try:
        if info[2]:
            html = seleniumGet(url)
        else:
            html = urllibGet(url)
    except Exception as e:
        print("Connection failed...")
        print(e)
        return False
    keyWord = info[0]
    alertOnFound = info[1]
    index = html.find(keyWord)
    index = html.upper().find(keyWord.upper())
    gotStock=False
    if alertOnFound and index != -1 and "show-out-of-stock" not in html[index-150:index+150]:
        print(index)
        print(html[index-150:index+150])
        #alert(url)
        gotStock = True
    elif not alertOnFound and index == -1:
        #print(html[index-50:index+50])
        gotStock = True
    return gotStock


def check_nvidia_api():
    response = requests.get("https://api-prod.nvidia.com/direct-sales-shop/DR/products/en_us/USD/5438481700")
    item = response.json()
    print(item['products']['product'][0]['inventoryStatus']['status'])
    if item['products']['product'][0]['inventoryStatus']['status'] != "PRODUCT_INVENTORY_OUT_OF_STOCK":
        return True
    else:
        return False


def main():
    numSearches = 0
    toast.show_toast("Hello World!!!",
                       "Python is 10 seconds awsm!",
                       icon_path="icon.ico",
                       duration=10)
    while True:
        now = datetime.now()
        current_time = now.strftime("%H:%M:%S")
        print("Starting search {} at {}".format(numSearches, current_time))
        numSearches += 1
        for url, info in urlKeyWords.items():
            print("\tChecking {}...".format(info[3]))
            if got_stock(url, info):
                print("........confirming in stock signal")
                baseSleepAmt = 1
                totalSleep = baseSleepAmt + random.uniform(2, 10)
                time.sleep(totalSleep)
                if got_stock(url, info):
                    if info[3] == "Nvidia":
                        if check_nvidia_api():
                            alert(url)
                    else:
                        alert(url)

        baseSleepAmt = 1
        totalSleep = baseSleepAmt + random.uniform(2, 15)
        # print("Sleeping for {} seconds".format(totalSleep))
        time.sleep(totalSleep)


if __name__ == '__main__':
    main()

@ItsCalebJones i ended up at the same code, just moved

   else:
       alert(url) 

to make it for if info[4] == "Nvidia": statement

That was a derp ;)

Submitted #5 , should handle grabbing nvidia stuff by API instead of by html, and has a few other QOL changes.

Gonna close this out. Thanks for ironing this out, all!