1 Commits
main ... french

Author SHA1 Message Date
d7b891abad French version 2025-06-03 13:06:37 +00:00
6 changed files with 186 additions and 180 deletions

137
README.md
View File

@ -1,148 +1,150 @@
<h1 align="center">Nvidia Stock Bot</h1>
<div align="center">
<a href="https://discord.gg/gxffg3GA96">
<img src="https://img.shields.io/badge/JV%20hardware-join-green?style=flat-square&logo=discord&logoColor=%23fff" alt="JV Hardware">
<img src="https://img.shields.io/badge/JV%20hardware-rejoindre-green?style=flat-square&logo=discord&logoColor=%23fff" alt="JV Hardware">
<a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">
<img src="https://img.shields.io/badge/License-CC%20BY--NC%204.0-8E44AD?style=flat-square" alt="License: CC BY-NC 4.0">
</a>
</div>
<div align="center" >
<img src="https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/nvidia-stock-bot-logo.png" alt="Nvidia Stock Bot" width="300">
</div>
**🤖 Nvidia Stock Bot** - A bot that alerts you in real-time about **Nvidia RTX FE** GPU stock availability through Discord notifications.
**🤖 Nvidia Stock Bot** - Un robot qui permet d'être alerté en temps réel des stocks de cartes graphiques **Nvidia RTX FE** grâce à des notifications Discord.
*The code was partially written and structured using a generative AI.*
*Le code a été en partie rédigé et structuré à l'aide d'une IA générative.*
## 📌 Table of Contents
## 📌 Sommaire
- [✨ Features](#features)
- [🐳 Docker Installation without cloning the repo (quick)](#docker-installation-without-the-repo-quick)
- [🐙 Docker Installation with the repo (developer)](#docker-installation-with-the-repo)
- [🐍 Python Installation (developer)](#python-installation)
- [🖼️ Screenshots](#screenshots)
- [🧑‍💻 Contributors](#contributors)
- [✨ Fonctionnalités](#fonctionnalit%C3%A9s)
- [🐳 Installation docker sans le dépot (rapide)](#installation-sans-le-d%C3%A9pot-avec-docker-compose)
- [🐙 Installation docker avec le dépot (développeur)](#installation-avec-le-d%C3%A9pot)
- [🐍 Installation avec Python (développeur)](#installation-avec-python)
- [🖼️ Captures d'écran](#captures-d%C3%A9cran)
- [🧑‍💻 Contributeurs](#contributeurs)
## ✨ Features
## ✨ Fonctionnalités
- Discord `@everyone` notification on SKU change (possible imminent drop)
- Discord `@everyone` notification when stock is detected, including model, price, and link
- Silent Discord notification when no stock is detected
- Selectable check frequency
- Selectable GPU model
- Notification Discord `@everyone` en cas de changement du SKU (potentiel drop imminent)
- Notification Discord `@everyone` en cas de stock détecté avec modèle, prix, et lien
- Notification Discord silencieuse en cas d'absence de stock détécté
- Choix de la fréquence de la vérification
- Choix du modèle à surveiller
## 🐳 Docker Installation without the repo (quick)
<img src="https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/nvbot_schematics.png" align="center">
Below are the instructions to set up the container using our pre-built image. With this setup, your bot will run independently as long as the container is active.
## 🐳 Installation sans le dépot avec docker compose
**Requirements**
Vous trouverez-ci dessous les instructions pour configurer le conteneur avec notre image pré-compilée. Avec cette solution, votre bot tournera tout seul tant que le conteneur est actif.
**Pré-requis**
- [Docker](https://docs.docker.com/engine/install/)
**Configuration**
- Create a folder named `nvidia-stock-bot`
- Create a `compose.yaml` file inside that folder with the following content:
- Créez un dossier `nvidia-stock-bot`
- Créez le fichier `compose.yaml` dans ce dossier avec la configuration ci-dessous :
```yaml
services:
nvidia-stock-bot:
image: git.djeex.fr/djeex/nvidia-stock-bot:latest
image: git.djeex.fr/djeex/nvidia-stock-bot:latest-french
container_name: nvidia-stock-bot
restart: unless-stopped
environment:
- DISCORD_WEBHOOK_URL= # Your Discord webhook URL
- PRODUCT_NAME= # Exact GPU name like "RTX 5080"
- PYTHONUNBUFFERED=1 # Enables real-time log output
- DISCORD_WEBHOOK_URL= # URL de votre webhook Discord
- PRODUCT_NAME= # Le nom exact du GPU que vous recherchez comme "RTX 5080"
- PYTHONUNBUFFERED=1 # Permet d'afficher les logs en temps réel
command: python nvidia-stock-bot.py
```
**Environment Variables:**
**Variables d'environnements :**
| Variable | Description | Possible Values | Default Value |
|---------------------|-------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| DISCORD_WEBHOOK_URL | Your Discord webhook URL | A valid URL | |
| REFRESH_TIME | Script refresh interval in seconds | `60`, `30`, etc. | `30` |
| API_URL_SKU | API listing the product | A URL | `https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia` |
| API_URL_STOCK | API providing stock data | A URL | `https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus=` |
| PRODUCT_URL | GPU purchase URL | A URL | `https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&manufacturer=NVIDIA` |
| PRODUCT_NAME | The exact GPU name you're searching for | `RTX 5090`, `RTX 5080`, `RTX 5070` | |
| TEST_MODE | For testing without sending notifications | `True`, `False` | `False` |
| PYTHONUNBUFFERED | Enables real-time log output | `1`, `0` | `1` |
| Variables | Explications | Valeurs possibles | Valeur par défaut |
|---------------------|-------------------------------------------------|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| DISCORD_WEBHOOK_URL | URL de votre webhook Discord | Une URL | |
| REFRESH_TIME | Durée de rafraichissement du script en secondes | `60`, `30`, etc... | `30` |
| API_URL_SKU | API listant le produit | Une URL | `https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia` |
| API_URL_STOCK | API donnant le stock | Une URL | `https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus=` |
| PRODUCT_URL | URL d'achat du GPU | Une URL | `https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&manufacturer=NVIDIA` |
| PRODUCT_NAME | Le nom exact du GPU que vous recherchez | `RTX 5090`, `RTX 5080` ou `RTX 5070`. | |
| TEST_MODE | Pour tester sans envoyer de notifs | `True`, `False` | `False` |
| PYTHONUNBUFFERED | #Permet d'afficher les logs en temps réel | `1`, `0` | `1` |
**Run the image**
**Lancer l'image**
Navigate to the `nvidia-stock-bot` folder and launch the container:
Rendez-vous dans le dossier `nvidia-stock-bot` et lancez le conteneur :
```sh
docker compose up -d
```
**Check logs to verify operation**
**Voir les logs pour vérifier le bon fonctionnement**
```sh
docker logs -f nvidia-stock-bot
```
## 📦 Docker Installation with the repo
## 📦 Installation avec le dépot
Instructions below show how to install the repo, build the Docker image, and launch the container. Your bot will run independently as long as the container is active.
Vous trouverez-ci dessous les instructions pour installer le dépot, compiler l'image docker, et lancer le conteneur. Avec cette solution, votre bot tournera tout seul tant que le conteneur est actif.
**Requirements**
**Pré-requis**
- [Git](https://git-scm.com/docs)
- [Docker](https://docs.docker.com/engine/install/)
**Clone and configure**
**Cloner et paramétrer**
- Clone the repo:
- Clonez le repo :
```sh
git clone https://git.djeex.fr/Djeex/nvidia-stock-bot.git
```
- Navigate to `nvidia-stock-bot` and build the Docker image:
- Rendez vous dans le dossier `nvidia-stock-bot` et compilez l'image docker :
```sh
docker build -t nvidia-stock-bot .
```
- Then go to `nvidia-stock-bot/docker` and edit the `.env` file with:
- Your Discord webhook URL
- The API and product URLs
- Stock checking frequency (default: 60s; lowering too much may get your IP blocked by Nvidia)
- Puis rendez-vous dans le dossier `nvidia-stock-bot/docker` et éditez le fichier `.env` avec :
- l'url de votre webhook discord
- les différents liens API et produits
- la fréquence de consultation des stock (par défaut 60s, attention à ne pas trop descendre sous peine de blocage de votre adresse IP par nVidia)
**Run the image**
**Lancer l'image**
Navigate to `nvidia-stock-bot/docker` and launch the container:
Rendez-vous dans le dossier `nvidia-stock-bot/docker` et lancez le conteneur :
```sh
docker compose up -d
```
**Check logs to verify operation**
**Voir les logs pour vérifier le bon fonctionnement**
```sh
docker logs -f nvidia-stock-bot
```
## 🐍 Python Installation
## 🐍 Installation avec Python
Instructions to directly run the Python script. Note: the bot stops when you close the terminal.
Vous trouverez ci-dessous comment exécuter directement le script Python. Avec cette solution, le bot s'arretera si vous fermez votre terminal.
**Requirements**
**Pré-requis**
- Python 3.11 or newer
- Python 3.11 ou plus
- requests : `pip install requests`
**Configuration**
- Create a virtual environment (e.g., `python3 -m venv env_name`)
- Create and navigate into a folder
- Download the Python script:
- Créez un environnement virtuel (exemple : `python3 -m venv nom_de_l_environnement` )
- Créez un dossier et aller dedans
- Téléchargez le script python :
```sh
curl -o nvidia-stock-bot.py -# https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/nvidia-stock-bot.py
```
- Export the environment variables with your webhook and refresh time:
- Exportez les variables d'environnement avec votre webhook discord et le temps de rafraichissement en secondes, par exemple :
```sh
export DISCORD_WEBHOOK_URL="https://your_discord_url"
export DISCORD_WEBHOOK_URL="https://votre_url_discord"
export REFRESH_TIME="60"
export API_URL_SKU="https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia&gpu=RTX%205080"
export API_URL_STOCK="https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus="
@ -150,22 +152,21 @@ export PRODUCT_URL="https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards
export PRODUCT_NAME="RTX 5080"
export TEST_MODE=false
```
- Run the script
- Lancez le script
```sh
python nvidia-stock-bot.py
```
## 🖼️ Screenshots
## 🖼️ Captures d'écran
<div align="center" >
<img src="https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/nvidia-stock-bot-discord.png" alt="Nvidia Stock Bot - screenshots">
<img src="https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/nvidia-stock-bot-discord.png" alt="Nvidia Stock Bot - captures">
</div>
## 🧑‍💻 Contributors
## 🧑‍💻 Contributeurs
Thanks for their contributions:
On remercie pour leurs contributions :
- Djeex
- KevOut

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,6 +1,6 @@
DS_HOOK= # Your Discord webhook URL
FREQ= # Refresh frequency in seconds, default is 30
API_URL_SKU= # API listing the product, default is https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia
API_URL_STOCK= # API used to check stock without specifying the SKU, default is https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus=
PRODUCT_URL= # Purchase URL of the GPU
PRODUCT_NAME= # The exact name of the GPU you're looking for, e.g., "RTX 5080"
DS_HOOK= # Votre url du webhook Discord
FREQ= # Frequence de rafraichissement en secondes, par défaut 30
API_URL_SKU= # API listant le produit, par défaut https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia
API_URL_STOCK= # API appelant le stock sans préciser la valeur du sku, par défaut https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus=
PRODUCT_URL= # URL d'achat du GPU
PRODUCT_NAME= # Le nom exact du GPU que vous recherchez comme : "RTX 5080"

View File

@ -12,5 +12,5 @@ services:
- API_URL_STOCK=${API_URL_STOCK}
- PRODUCT_URL=${PRODUCT_URL}
- PRODUCT_NAME=${PRODUCT_NAME}
- PYTHONUNBUFFERED=1 # Allow to display log in real-time
command: python nvidia-stock-bot.py # Run the script
- PYTHONUNBUFFERED=1 # Permet d'afficher les logs en temps réel
command: python nvidia-stock-bot.py # Lance le script Python

View File

@ -5,14 +5,14 @@ import os
import re
from requests.adapters import HTTPAdapter, Retry
# Logger configuration
# Configuration du logger
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logging.info("Script started")
logging.info("Démarrage du script")
# Retrieve environment variables
# Récupération des variables d'environnement
try:
DISCORD_WEBHOOK_URL = os.environ.get('DISCORD_WEBHOOK_URL')
API_URL_SKU = os.environ.get('API_URL_SKU', 'https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia')
@ -22,48 +22,49 @@ try:
PRODUCT_URL = os.environ.get('PRODUCT_URL', 'https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&manufacturer=NVIDIA')
PRODUCT_NAME = os.environ.get('PRODUCT_NAME')
# Error logging
# Logging des erreurs
if not DISCORD_WEBHOOK_URL:
logging.error("❌ DISCORD_WEBHOOK_URL is required but not defined.")
logging.error("❌ DISCORD_WEBHOOK_URL est requis mais non défini.")
exit(1)
if not PRODUCT_NAME:
logging.error("❌ PRODUCT_NAME is required but not defined.")
logging.error("❌ PRODUCT_NAME est requis mais non défini.")
exit(1)
# Regex to extract ID and token
# Regex pour extraire l'ID et le token
match = re.search(r'/(\d+)/(.*)', DISCORD_WEBHOOK_URL)
if match:
webhook_id = match.group(1)
webhook_token = match.group(2)
# Mask last characters of the ID
# Masquer derniers caractères de l'ID
masked_webhook_id = webhook_id[:len(webhook_id) - 10] + '*' * 10
# Mask last characters of the token
# Masquer derniers caractères du token
masked_webhook_token = webhook_token[:len(webhook_token) - 120] + '*' * 10
# Rebuild masked URL
# Reconstruction de l'url masquée
wh_masked_url = f"https://discord.com/api/webhooks/{masked_webhook_id}/{masked_webhook_token}"
# Error logging
# Logging des erreurs
except KeyError as e:
logging.error(f"Missing environment variable: {e}")
logging.error(f"Variable d'environnement manquante : {e}")
exit(1)
except ValueError:
logging.error("REFRESH_TIME must be a valid integer.")
logging.error("REFRESH_TIME doit être un entier valide.")
exit(1)
# Display URLs and configurations
# Affichage des URLs et configurations
logging.info(f"GPU: {PRODUCT_NAME}")
logging.info(f"Discord Webhook URL: {wh_masked_url}")
logging.info(f"API URL SKU: {API_URL_SKU}")
logging.info(f"API URL Stock: {API_URL_STOCK}")
logging.info(f"Product URL: {PRODUCT_URL}")
logging.info(f"Refresh time: {REFRESH_TIME} seconds")
logging.info(f"Test Mode: {TEST_MODE}")
logging.info(f"URL Webhook Discord: {wh_masked_url}")
logging.info(f"URL API SKU: {API_URL_SKU}")
logging.info(f"URL API Stock: {API_URL_STOCK}")
logging.info(f"URL produit: {PRODUCT_URL}")
logging.info(f"Temps d'actualisation: {REFRESH_TIME} secondes")
logging.info(f"Mode Test: {TEST_MODE}")
# HTTP headers
# Entêtes HTTP
HEADERS = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
@ -82,30 +83,30 @@ HEADERS = {
"Sec-GPC": "1",
}
# Session with retries
# Session avec retries
session = requests.Session()
retries = Retry(total=5, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
session.mount('https://', HTTPAdapter(max_retries=retries))
# Store stock status
# Stockage de l'état des stocks
global_stock_status = {}
# Store the last known SKU
# Stocke le dernier SKU connu
last_sku = None
first_run = True # Before calling check_rtx_50_founders
# Discord notifications
# Notifications Discord
def send_discord_notification(gpu_name: str, product_link: str, products_price: str):
# Get current UNIX timestamp
# Récupérer le timestamp UNIX actuel
timestamp_unix = int(time.time())
if TEST_MODE:
logging.info(f"[TEST MODE] Discord Notification: {gpu_name} available!")
logging.info(f"[TEST MODE] Notification Discord: {gpu_name} disponible !")
return
embed = {
"title": f"🚀 {PRODUCT_NAME} IN STOCK!",
"title": f"🚀 {PRODUCT_NAME} EN STOCK !",
"color": 3066993,
"thumbnail": {
"url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/RTX5000.jpg"
@ -116,18 +117,18 @@ def send_discord_notification(gpu_name: str, product_link: str, products_price:
"fields": [
{
"name": "Price",
"name": "Prix",
"value": f"`{products_price}€`",
"inline": True
},
{
"name": "Time",
"name": "Heure",
"value": f"<t:{timestamp_unix}:d> <t:{timestamp_unix}:T>",
"inline": True
},
],
"description": f"**:point_right: [Buy now]({product_link})**",
"description": f"**:point_right: [Acheter maintenant]({product_link})**",
"footer": {
"text": "NviBot • JV Hardware 2.0",
"icon_url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/ds_wh_pp.jpg"
@ -137,24 +138,24 @@ def send_discord_notification(gpu_name: str, product_link: str, products_price:
try:
response = requests.post(DISCORD_WEBHOOK_URL, json=payload)
if response.status_code == 204:
logging.info("✅ Notification sent to Discord.")
logging.info("✅ Notification envoyée sur Discord.")
else:
logging.error(f"❌ Webhook error: {response.status_code} - {response.text}")
logging.error(f" Erreur Webhook : {response.status_code} - {response.text}")
except Exception as e:
logging.error(f"🚨 Error sending webhook: {e}")
logging.error(f"🚨 Erreur lors de l'envoi du webhook : {e}")
def send_out_of_stock_notification(gpu_name: str, product_link: str, products_price: str):
# Get current UNIX timestamp
# Récupérer le timestamp UNIX actuel
timestamp_unix = int(time.time())
if TEST_MODE:
logging.info(f"[TEST MODE] Discord Notification: {gpu_name} out of stock!")
logging.info(f"[TEST MODE] Notification Discord: {gpu_name} hors stock !")
return
embed = {
"title": f"{PRODUCT_NAME} is out of stock",
"color": 15158332, # Red for out of stock
"title": f"{PRODUCT_NAME} n'est plus en stock",
"color": 15158332, # Rouge pour hors stock
"thumbnail": {
"url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/RTX5000.jpg"
},
@ -170,7 +171,7 @@ def send_out_of_stock_notification(gpu_name: str, product_link: str, products_pr
"fields": [
{
"name": "Time",
"name": "Heure",
"value": f"<t:{timestamp_unix}:d> <t:{timestamp_unix}:T>",
"inline": True
}
@ -180,26 +181,26 @@ def send_out_of_stock_notification(gpu_name: str, product_link: str, products_pr
try:
response = requests.post(DISCORD_WEBHOOK_URL, json=payload)
if response.status_code == 204:
logging.info("'Out of stock' notification sent to Discord.")
logging.info("Notification 'hors stock' envoyée sur Discord.")
else:
logging.error(f"❌ Webhook error: {response.status_code} - {response.text}")
logging.error(f" Erreur Webhook : {response.status_code} - {response.text}")
except Exception as e:
logging.error(f"🚨 Error sending webhook: {e}")
logging.error(f"🚨 Erreur lors de l'envoi du webhook : {e}")
def send_sku_change_notification(old_sku: str, new_sku: str, product_link: str):
# Get current UNIX timestamp
# Récupérer le timestamp UNIX actuel
timestamp_unix = int(time.time())
if TEST_MODE:
logging.info(f"[TEST MODE] SKU change detected: {old_sku}{new_sku}")
logging.info(f"[TEST MODE] Changement de SKU détecté : {old_sku}{new_sku}")
return
embed = {
"title": f"🔄 {PRODUCT_NAME} SKU change detected",
"title": f"🔄 {PRODUCT_NAME} Changement de SKU détecté",
"url": f"{product_link}",
"description": f"**Old SKU** : `{old_sku}`\n**New SKU** : `{new_sku}`",
"color": 16776960, # Yellow
"description": f"**Ancien SKU** : `{old_sku}`\n**Nouveau SKU** : `{new_sku}`",
"color": 16776960, # Jaune
"footer": {
"text": "NviBot • JV Hardware 2.0",
@ -208,7 +209,7 @@ def send_sku_change_notification(old_sku: str, new_sku: str, product_link: str):
"fields": [
{
"name": "Time",
"name": "Heure",
"value": f"<t:{timestamp_unix}:d> <t:{timestamp_unix}:T>",
"inline": True
}
@ -216,7 +217,7 @@ def send_sku_change_notification(old_sku: str, new_sku: str, product_link: str):
}
payload = {
"content": "@everyone ⚠️ Possible imminent drop!",
"content": "@everyone ⚠️ Potentiel drop imminent !",
"username": "NviBot",
"avatar_url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/ds_wh_pp.jpg",
"embeds": [embed]
@ -225,88 +226,90 @@ def send_sku_change_notification(old_sku: str, new_sku: str, product_link: str):
try:
response = requests.post(DISCORD_WEBHOOK_URL, json=payload)
if response.status_code == 204:
logging.info("SKU change notification sent to Discord.")
logging.info("Notification de changement de SKU envoyée sur Discord.")
else:
logging.error(f"❌ Webhook error: {response.status_code} - {response.text}")
logging.error(f" Erreur Webhook : {response.status_code} - {response.text}")
except Exception as e:
logging.error(f"🚨 Error sending webhook: {e}")
logging.error(f"🚨 Erreur lors de l'envoi du webhook : {e}")
# Stock search
# Recherche du stock
def check_rtx_50_founders():
global global_stock_status, last_sku, first_run
# Call product API to retrieve SKU and UPC
# Appel vers l'API produit pour récupérer le sku et l'upc
try:
response = session.get(API_URL_SKU, headers=HEADERS, timeout=10)
logging.info(f"SKU API response: {response.status_code}")
logging.info(f"Réponse de l'API SKU : {response.status_code}")
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
logging.error(f"SKU API error: {e}")
logging.error(f"Erreur API SKU : {e}")
return
# Look for product whose GPU matches PRODUCT_NAME
# Recherche du produit dont le GPU correspond à PRODUCT_NAME
product_details = None
for p in data['searchedProducts']['productDetails']:
gpu_name = p.get("gpu", "").strip()
# If the GPU matches exactly PRODUCT_NAME
# Si le GPU correspond exactement à PRODUCT_NAME
if gpu_name == PRODUCT_NAME.strip():
product_details = p
break # Exit as soon as the correct product is found
break # Sortir dès qu'on trouve le bon produit
if not product_details:
logging.warning(f"⚠️ No product with GPU '{PRODUCT_NAME}' found.")
logging.warning(f"⚠️ Aucun produit avec le GPU '{PRODUCT_NAME}' trouvé.")
return
# Retrieve the SKU for the found GPU
# Récupérer le SKU pour le GPU trouvé
product_sku = product_details['productSKU']
product_upc = product_details.get('productUPC', "")
# Check if this is the first execution
# Vérifier si c'est la première exécution
if last_sku is not None and product_sku != last_sku:
if not first_run: # Prevent sending notification on first run
if not first_run: # Évite d'envoyer une notification au premier appel
product_link = PRODUCT_URL
logging.warning(f"⚠️ SKU changed: {last_sku}{product_sku}")
logging.warning(f"⚠️ SKU modifié : {last_sku}{product_sku}")
send_sku_change_notification(last_sku, product_sku, product_link)
# Update stored SKU
# Mettre à jour le SKU stocké
last_sku = product_sku
first_run = False # Disable first-run protection
first_run = False # Désactive la protection après la première exécution
if not isinstance(product_upc, list):
product_upc = [product_upc]
# Build stock API URL and call to check status
# Construction de l'url de l'API de stock et appel pour vérifier le statut
API_URL = API_URL_STOCK + product_sku
logging.info(f"Stock API URL called: {API_URL}")
logging.info(f"URL de l'API de stock appelée : {API_URL}")
try:
response = session.get(API_URL, headers=HEADERS, timeout=10)
logging.info(f"Stock API response: {response.status_code}")
logging.info(f"Réponse de l'API : {response.status_code}")
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Stock API error: {e}")
logging.error(f"Erreur API Stock : {e}")
return
products = data.get("listMap", [])
products_price = 'Price not available' # Default value
products_price = 'Prix non disponible' # Valeur par défaut
# Check product list and retrieve price
# Vérification de la liste des produits et récupération du prix
if isinstance(products, list) and len(products) > 0:
for product in products:
price = product.get("price", 'Price not available')
if price != 'Price not available':
products_price = price # Use found price
break # Stop at first found price
price = product.get("price", 'Prix non disponible')
if price != 'Prix non disponible':
products_price = price # Utiliser le prix trouvé
break # Sortir dès qu'on trouve un prix
else:
logging.error("Product list is empty or malformed.")
logging.error("La liste des produits est vide ou mal formée.")
found_in_stock = set()
# Check status and send notifications accordingly
# Recherche du statut et notifications selon le statut
for p in products:
gpu_name = p.get("fe_sku", "").upper()
is_active = p.get("is_active") == "true"
@ -322,18 +325,20 @@ def check_rtx_50_founders():
product_link = PRODUCT_URL
send_discord_notification(gpu_upper, product_link, products_price)
global_stock_status[gpu_upper] = True
logging.info(f"{gpu} is now in stock!")
logging.info(f"{gpu} est maintenant en stock!")
elif not currently_in_stock and previously_in_stock:
product_link = PRODUCT_URL
send_out_of_stock_notification(gpu_upper, product_link, products_price)
global_stock_status[gpu_upper] = False
logging.info(f"{gpu} is no longer in stock.")
elif currently_in_stock and previously_in_stock:
logging.info(f"{gpu} is currently in stock.")
else:
logging.info(f"{gpu} is currently out of stock.")
logging.info(f"{gpu} n'est plus en stock.")
# Loop
elif currently_in_stock and previously_in_stock:
logging.info(f"{gpu} est actuellement en stock.")
else:
logging.info(f"{gpu} est actuellement hors stock.")
# Boucle
if __name__ == "__main__":
while True:
check_rtx_50_founders()