From 037f5bebf88aef19453a9a06355703eeec595003 Mon Sep 17 00:00:00 2001 From: Djeex Date: Fri, 14 Mar 2025 10:53:59 +0000 Subject: [PATCH] SKU changes & better notif --- README.md | 6 ++- docker/.env | 4 +- nvidia-stock-bot.py | 115 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a7a80c2..eb8eb47 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Nvidia Stock Bot +# Nvidia Stock Bot - WIP Par KevOut & Djeex [![](https://img.shields.io/badge/JV%20hardware-rejoindre-green?style=flat-square&logo=discord&logoColor=%23fff&label=JV%20hardware&link=https%3A%2F%2Fdiscord.gg%2Fgxffg3GA96)](https://discord.gg/gxffg3GA96) @@ -69,7 +69,7 @@ Vous trouverez-ci dessous les instructions pour configurer le conteneur avec not version: "3.8" services: nvidia-stock-bot: - image: git.djeex.fr/djeex/nvidia-stock-bot:latest + image: git.djeex.fr/djeex/nvidia-stock-bot:wip container_name: nvidia-stock-bot restart: always # Le conteneur redémarrera automatiquement en cas d'échec environment: @@ -77,6 +77,8 @@ services: - REFRESH_TIME= # Durée de rafraichissement du script en secondes - API_URL_SKU= # API listant le produit par exemple https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia&gpu=RTX%205090 - API_URL_STOCK= # API appelant le stock sans préciser la valeur du sku, par exemple https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus= + - PRODUCT_URL= # URL d'achat du GPU + - PRODUCT_NAME= #Le nom du GPU qui s'affiche dans les notifications - TEST_MODE= #true pour tester les notifications discord. false par défaut. - PYTHONUNBUFFERED=1 # Permet d'afficher les logs en temps réel command: python nvidia-stock-bot.py # Lance le script Python au démarrage du conteneur diff --git a/docker/.env b/docker/.env index 558e1ff..7f0dbad 100644 --- a/docker/.env +++ b/docker/.env @@ -1,4 +1,6 @@ DS_HOOK= #votre url du webhook Discord FREQ= #frequence de rafraichissement en secondes API_URL_SKU= # API listant le produit par exemple https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia&gpu=RTX%205090 -API_URL_STOCK= # API appelant le stock sans préciser la valeur du sku, par exemple https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus= \ No newline at end of file +API_URL_STOCK= # API appelant le stock sans préciser la valeur du sku, par exemple https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus= +PRODUCT_URL= # URL d'achat du GPU +PRODUCT_NAME= #Le nom du GPU qui s'affiche dans les notifications diff --git a/nvidia-stock-bot.py b/nvidia-stock-bot.py index b1485f1..a64b0f9 100644 --- a/nvidia-stock-bot.py +++ b/nvidia-stock-bot.py @@ -19,6 +19,8 @@ try: API_URL_STOCK = os.environ['API_URL_STOCK'] REFRESH_TIME = int(os.environ['REFRESH_TIME']) # Convertir en entier TEST_MODE = os.environ.get('TEST_MODE', 'False').lower() == 'true' + PRODUCT_URL = os.environ['PRODUCT_URL'] + PRODUCT_NAME = os.environ['PRODUCT_NAME'] # Regex pour extraire l'ID et le token match = re.search(r'/(\d+)/(.*)', DISCORD_WEBHOOK_URL) @@ -43,12 +45,15 @@ except ValueError: exit(1) # Affichage des URLs et configurations +logging.info(f"GPU: {PRODUCT_NAME}") 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}") + # Entêtes HTTP HEADERS = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " @@ -76,19 +81,26 @@ session.mount('https://', HTTPAdapter(max_retries=retries)) # Stockage de l'état des stocks global_stock_status = {} +# Stocke le dernier SKU connu +last_sku = None +first_run = True # Before calling check_rtx_50_founders + # Notifications Discord def send_discord_notification(gpu_name: str, product_link: str, products_price: str): + + # Récupérer le timestamp UNIX actuel + timestamp_unix = int(time.time()) + if TEST_MODE: logging.info(f"[TEST MODE] Notification Discord: {gpu_name} disponible !") return embed = { - "title": f"🚀 {gpu_name} EN 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" }, - "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()), "author": { "name": "Nvidia Founder Editions" }, @@ -98,9 +110,21 @@ def send_discord_notification(gpu_name: str, product_link: str, products_price: "name": "Prix", "value": f"`{products_price} €`", "inline": True + }, + + { + "name": "Heure", + "value": f" ", + "inline": True + }, + + { + "name": "Lien", + "value": f"{PRODUCT_URL}" } ], - "description": "**:point_right: [Acheter maintenant](https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&gpu=RTX%205090,RTX%205080)**", + "description": f"**:point_right: [Acheter maintenant]({product_link})**", + "url": f"{product_link}" "footer": { "text": "Par KevOut & Djeex" } @@ -116,25 +140,35 @@ def send_discord_notification(gpu_name: str, product_link: str, products_price: 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): + + # Récupérer le timestamp UNIX actuel + timestamp_unix = int(time.time()) + if TEST_MODE: logging.info(f"[TEST MODE] Notification Discord: {gpu_name} hors stock !") return embed = { - "title": f"❌ {gpu_name} n'est plus en stock", - "description": f":disappointed_relieved:", + "title": f"❌ {PRODUCT_NAME} n'est plus en stock", "color": 15158332, # Rouge pour hors stock - "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()), "thumbnail": { "url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/RTX5000.jpg" }, - "url": "https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&gpu=RTX%205090,RTX%205080", + "url": f"{product_link}", "author": { "name": "Nvidia Founder Editions" }, "footer": { "text": "Par KevOut & Djeex" - } + }, + + "fields": [ + { + "name": "Heure", + "value": f" ", + "inline": True + } + ] } payload = {"username": "NviBot", "avatar_url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/RTX5000_pp.jpg", "embeds": [embed]} try: @@ -146,20 +180,77 @@ def send_out_of_stock_notification(gpu_name: str, product_link: str, products_pr except Exception as e: logging.error(f"🚨 Erreur lors de l'envoi du webhook : {e}") +def send_sku_change_notification(old_sku: str, new_sku: str): + + # Récupérer le timestamp UNIX actuel + timestamp_unix = int(time.time()) + + if TEST_MODE: + logging.info(f"[TEST MODE] Changement de SKU détecté : {old_sku} → {new_sku}") + return + + embed = { + "title": f"🔄 {PRODUCT_NAME} Changement de SKU détecté", + "description": f"**Ancien SKU** : `{old_sku}`\n**Nouveau SKU** : `{new_sku}`", + "color": 16776960, # Jaune + + "footer": { + "text": "Par KevOut & Djeex" + }, + + "fields": [ + { + "name": "Heure", + "value": f" ", + "inline": True + } + ] + } + + payload = { + "content": "@everyone ⚠️ Potentiel drop imminent !", + "username": "NviBot", + "avatar_url": "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/img/RTX5000_pp.jpg", + "embeds": [embed] + } + + try: + response = requests.post(DISCORD_WEBHOOK_URL, json=payload) + if response.status_code == 204: + logging.info("✅ Notification de changement de SKU envoyée sur Discord.") + else: + logging.error(f"❌ Erreur Webhook : {response.status_code} - {response.text}") + except Exception as e: + logging.error(f"🚨 Erreur lors de l'envoi du webhook : {e}") + # Recherche du stock def check_rtx_50_founders(): - global global_stock_status + global global_stock_status, last_sku, first_run # 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"Réponse de l'API : {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"Erreur API SKU : {e}") return + product_details = data['searchedProducts']['productDetails'][0] + product_sku = product_details['productSKU'] + + # 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: # Évite d'envoyer une notification au premier appel + logging.warning(f"⚠️ SKU modifié : {last_sku} → {product_sku}") + send_sku_change_notification(last_sku, product_sku) + + # Mettre à jour le SKU stocké + last_sku = product_sku + first_run = False # Désactive la protection après la première exécution + product_details = data['searchedProducts']['productDetails'][0] product_sku = product_details['productSKU'] product_upc = product_details.get('productUPC', "") @@ -207,12 +298,12 @@ def check_rtx_50_founders(): previously_in_stock = global_stock_status.get(gpu_upper, False) if currently_in_stock and not previously_in_stock: - product_link = "https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&gpu=RTX%205090,RTX%205080" + product_link = PRODUCT_URL send_discord_notification(gpu_upper, product_link, products_price) global_stock_status[gpu_upper] = True logging.info(f"{gpu} est maintenant en stock!") elif not currently_in_stock and previously_in_stock: - product_link = "https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&gpu=RTX%205090,RTX%205080" + 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} n'est plus en stock.")