From 4cd15d9bccac194d919ac0241410e9955f6a8890 Mon Sep 17 00:00:00 2001 From: Djeex Date: Sat, 12 Jul 2025 17:36:58 +0000 Subject: [PATCH] New COUNTRY variable. It sets notifications language, currency, and API localization. Removed DISCORD_NOTIFICATION_CURRENCY and DISCORD_NOTIFICATION_LANGUAGE --- README.md | 27 ++-- app/env_config.py | 76 +++++++--- app/gpu_checker.py | 7 +- app/languages.json | 266 +++++++++++++++++++++++++++++++++ app/localization.json | 336 +++++++++++------------------------------- app/notifier.py | 3 +- 6 files changed, 424 insertions(+), 291 deletions(-) create mode 100644 app/languages.json diff --git a/README.md b/README.md index 2511a8a..ff18383 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,13 @@ ## ✨ Features - Selectable GPU models +- Selectable country marketplace to check. It will set notifications language and currency accordingly - Discord `@everyone` or specified role on SKU change (possible imminent drop) - Discord `@everyone` or specified role notification when stock is detected, including model, price, and link - Silent Discord notification when no stock is detected -- Selectable notification language -- Selectable notification server name in footer +- Selectable notification server name in notification footer - Selectable check frequency +- Test mode to check without sending notifications ## 🐳 Docker Installation without the repo (quick) @@ -55,12 +56,9 @@ services: restart: unless-stopped environment: # Minimal environment variables - - PRODUCT_NAMES= # Exact GPU name (e.g. "RTX 5080, RTX 5090") + - PRODUCT_NAMES= # Exact GPU name (e.g. 'RTX 5080, RTX 5090') - DISCORD_WEBHOOK_URL= # Your Discord webhook URL - - DISCORD_NOTIFICATION_CURRENCY= # Set your country currency - - API_URL_SKU= # API listing the product for your country - - API_URL_STOCK= # API providing stock data for your country - - PRODUCT_URL= # GPU purchase URL for your country + - COUNTRY= # Set your country (e.g. 'US') - PYTHONUNBUFFERED=1 # Enables real-time log output ``` @@ -70,14 +68,14 @@ services: |---------------------|-------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| | `PRODUCT_NAMES` | The exact GPU names you're searching for | `RTX 5080, RTX 5090` | | | `DISCORD_WEBHOOK_URL` | Your Discord webhook URL | A valid URL | | -| `DISCORD_NOTIFICATION_CURRENCY` | Your country currency | A text, e.g. `$`, `€`, `£`... | `€` | +| `COUNTRY` | Your country (only one). API localization, Discord notification currency and language will be set accordingly | `GB`, `US`, `CA`, `BE`, `NL`, `DK`, `DE`, `ES`, `FR`, `IT`, `NO`, `AT`, `PL`, `FI`, `SE`, `KR`, `JP` + | `US` | | `DISCORD_SERVER_NAME` | The name of your server, displayed in notification's footer | A text | Shared for free | -| `DISCORD_NOTIFICATION_LANGUAGE` | Your language for notification's content | `bg`, `cs`, `da`, `de`, `el`, `en`, `es`, `et`, `fi`, `fr`, `ga`, `hr`, `hu`, `it`, `lt`, `lv`, `mt`, `nl`, `pl`, `pt`, `ro`, `sk`, `sl`, `sv` | `en` | | `DISCORD_ROLES` | List of Discord roles ID in the same order than `PRODUCT_NAMES` values, found in your discord server settings (with user profile developer mode enabled) | `<@&12345><@&6789>` | @everyone | | `REFRESH_TIME` | Script refresh interval in seconds | `60`, `30`, etc. | `30` | -| `API_URL_SKU` | API listing the product | A URL. API url can change over time. For now, you can use the default one and change the `locale` parameter to yours (e.g. `locale=en-gb`) | `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. API url can change over time. For now, you can use the default one and change the `locale` parameter to yours (e.g. `locale=en-gb`) | `https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus=` | -| `PRODUCT_URL` | GPU purchase URL. There isn't any direct link workinf right now, so put the generic marketplace url listing all FE products | A URL. API url can change over time. For now, you can use the default one and change the locale parameter to yours (e.g. `/en-gb/`) | `https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&manufacturer=NVIDIA` | +| `API_URL_SKU` | API listing the product. Use it for development purpose. __Warning__ : it will override the locale set by `COUNTRY`. | An URL | https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale={locale}&Manufacturer=Nvidia` | +| `API_URL_STOCK` | API providing stock data. Use it for development purpose. __Warning__ : it will override the locale set by `COUNTRY`. | A URL | `https://api.store.nvidia.com/partner/v1/feinventory?locale={locale}&skus=` | +| `PRODUCT_URL` | GPU purchase URL. Use it for development purpose. __Warning__ : it will override the locale set by `COUNTRY`. | A URL | `https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale={locale}&page=1&limit=12&manufacturer=NVIDIA` | | `TEST_MODE` | For testing without sending notifications | `True`, `False` | `False` | | `PYTHONUNBUFFERED` | Enables real-time log output | `1`, `0` | `1` | @@ -153,13 +151,10 @@ git clone https://git.djeex.fr/Djeex/nvidia-stock-bot.git ```sh export DISCORD_WEBHOOK_URL="https://your_discord_url" -export DISCORD_NOTIFICATION_CURRENCY=€ +export COUNTRY=€ export PRODUCT_NAMES=RTX 5080, RTX 5090 export DISCORD_ROLES=<@&12345>, <@&6789> 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=" -export PRODUCT_URL="https://marketplace.nvidia.com/fr-fr/consumer/graphics-cards/?locale=fr-fr&page=1&limit=12&gpu=RTX%205080&manufacturer=NVIDIA" export TEST_MODE=false export PYTHONUNBUFFERED=1 ``` diff --git a/app/env_config.py b/app/env_config.py index 00fbcf2..7d7996e 100644 --- a/app/env_config.py +++ b/app/env_config.py @@ -15,13 +15,10 @@ try: # Env variables DISCORD_WEBHOOK_URL = os.environ['DISCORD_WEBHOOK_URL'] DISCORD_SERVER_NAME = os.environ.get('DISCORD_SERVER_NAME', 'Shared for free') - DISCORD_NOTIFICATION_CURRENCY = os.environ.get('DISCORD_NOTIFICATION_CURRENCY') or '€' - API_URL_SKU = os.environ.get('API_URL_SKU') or 'https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale=fr-fr&Manufacturer=Nvidia' - API_URL_STOCK = os.environ.get('API_URL_STOCK') or 'https://api.store.nvidia.com/partner/v1/feinventory?locale=fr-fr&skus=' + DISCORD_ROLES = os.environ.get('DISCORD_ROLES') + COUNTRY = os.environ.get('COUNTRY') or 'US' REFRESH_TIME = int(os.environ.get('REFRESH_TIME') or 30) TEST_MODE = os.environ.get('TEST_MODE', 'False').lower() == 'true' - 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') - DISCORD_ROLES = os.environ.get('DISCORD_ROLES') PRODUCT_NAMES = os.environ['PRODUCT_NAMES'] except KeyError as e: logging.error(f"Missing environment variable: {e}") @@ -90,30 +87,57 @@ HEADERS = { "Sec-GPC": "1", } -# Localization loading -language = os.environ.get("DISCORD_NOTIFICATION_LANGUAGE", "en").lower() +# Load country setting and localization config +country_code = os.environ.get("COUNTRY", "US").upper() + try: with open("localization.json", "r", encoding="utf-8") as f: - localization = json.load(f) + localization_config = json.load(f) except FileNotFoundError: logging.error("❌ localization.json file not found.") sys.exit(1) +# Find country entry +country_entry = next((entry for entry in localization_config if entry["country_code"].upper() == country_code), None) + +if not country_entry: + logging.warning(f"⚠️ Country '{country_code}' not found in localization.json. Defaulting to US.") + country_entry = next((entry for entry in localization_config if entry["country_code"].upper() == "US"), None) + if not country_entry: + logging.error("❌ US fallback not found in localization.json.") + sys.exit(1) + +# Extract language code and currency +full_lang_code = country_entry["language_code"] +language = full_lang_code[:2].lower() # e.g., "en-US" -> "en" +currency = country_entry["currency"] + +# Load language file +try: + with open("languages.json", "r", encoding="utf-8") as f: + loc_lang = json.load(f) +except FileNotFoundError: + logging.error("❌ languages.json file not found.") + sys.exit(1) + +loc = loc_lang.get(language, loc_lang.get("en", {})) +logging.info(f"Notification language: {language} (from country {country_code})") +logging.info(f"Currency symbol: {currency}") + +if not loc: + logging.warning(f"⚠️ Language '{language}' not found. Falling back to English.") + loc = loc_lang.get("en", {}) + language = "en" + +# Ensure all required keys are present required_keys = [ "in_stock_title", "out_of_stock_title", "sku_change_title", "buy_now", "price", "time", "footer", "sku_description", "imminent_drop" ] -loc = localization.get(language, localization.get("en", {})) -logging.info(f"Notification language: {language}") - -if not loc: - logging.warning(f"⚠️ Language '{language}' not found. Falling back to English.") - loc = localization.get("en", {}) - language = "en" - missing_keys = [key for key in required_keys if key not in loc] -fallback = localization.get("en", {}) +fallback = loc_lang.get("en", {}) + for key in missing_keys: if key in fallback: loc[key] = fallback[key] @@ -121,6 +145,23 @@ for key in missing_keys: logging.error(f"❌ Missing required key '{key}' in both '{language}' and fallback 'en'.") sys.exit(1) +# Localized API - Env var can be overrided for development purposes +locale = full_lang_code.lower() +API_URL_SKU = os.getenv( + "API_URL_SKU", + f"https://api.nvidia.partners/edge/product/search?page=1&limit=100&locale={locale}&Manufacturer=Nvidia" +) + +API_URL_STOCK = os.getenv( + "API_URL_STOCK", + f"https://api.store.nvidia.com/partner/v1/feinventory?locale={locale}&skus=" +) + +PRODUCT_URL = os.getenv( + "PRODUCT_URL", + f"https://marketplace.nvidia.com/{locale}/consumer/graphics-cards/?locale={locale}&page=1&limit=12&manufacturer=NVIDIA" +) + # Public constants in_stock_title = loc["in_stock_title"] out_of_stock_title = loc["out_of_stock_title"] @@ -136,7 +177,6 @@ imminent_drop = loc["imminent_drop"] logging.info(f"GPU: {PRODUCT_NAMES}") logging.info(f"Discord Webhook URL: {wh_masked_url}") logging.info(f"Discord Role Mention: {DISCORD_ROLES}") -logging.info(f"Currency used for notifications: {DISCORD_NOTIFICATION_CURRENCY}") 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}") diff --git a/app/gpu_checker.py b/app/gpu_checker.py index 817d4af..90a099c 100644 --- a/app/gpu_checker.py +++ b/app/gpu_checker.py @@ -1,8 +1,7 @@ import requests import logging -from env_config import API_URL_SKU, API_URL_STOCK, HEADERS, PRODUCT_NAMES, PRODUCT_URL +from env_config import HEADERS, PRODUCT_NAMES, API_URL_SKU, API_URL_STOCK, PRODUCT_URL from notifier import send_discord_notification, send_out_of_stock_notification, send_sku_change_notification - from requests.adapters import HTTPAdapter, Retry session = requests.Session() @@ -25,7 +24,6 @@ def check_rtx_50_founders(): logging.error(f"SKU API error: {e}") return - # All available products all_products = data['searchedProducts']['productDetails'] for product_name in PRODUCT_NAMES: @@ -44,7 +42,6 @@ def check_rtx_50_founders(): if not isinstance(product_upc, list): product_upc = [product_upc] - # Check SKU change old_sku = last_sku_dict.get(product_name) if old_sku and old_sku != product_sku and not first_run_dict[product_name]: logging.warning(f"⚠️ SKU changed for {product_name}: {old_sku} → {product_sku}") @@ -53,7 +50,6 @@ def check_rtx_50_founders(): last_sku_dict[product_name] = product_sku first_run_dict[product_name] = False - # Stock check api_stock_url = API_URL_STOCK + product_sku logging.info(f"[{product_name}] Checking stock: {api_stock_url}") @@ -101,4 +97,3 @@ def check_rtx_50_founders(): logging.info(f"[{product_name}] {upc} still in stock.") else: logging.info(f"[{product_name}] {upc} still out of stock.") - \ No newline at end of file diff --git a/app/languages.json b/app/languages.json new file mode 100644 index 0000000..20cefc7 --- /dev/null +++ b/app/languages.json @@ -0,0 +1,266 @@ +{ + "en": { + "in_stock_title": "🚀 {gpu_name} IN STOCK!", + "out_of_stock_title": "❌ {gpu_name} is out of stock", + "sku_change_title": "🔄 SKU change detected for {gpu_name}", + "buy_now": "**:point_right: [Buy now]({product_link})**", + "price": "Price", + "time": "Time", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Old SKU** : `{old_sku}`\n**New SKU** : `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Possible imminent drop!" + }, + "bg": { + "in_stock_title": "🚀 {gpu_name} ИМА НАЛИЧНОСТ!", + "out_of_stock_title": "❌ {gpu_name} е изчерпан", + "sku_change_title": "🔄 Засечена промяна на SKU за {gpu_name}", + "buy_now": "**:point_right: [Купи сега]({product_link})**", + "price": "Цена", + "time": "Час", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Стар SKU**: `{old_sku}`\n**Нов SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Възможен наближаващ спад!" + }, + "cs": { + "in_stock_title": "🚀 {gpu_name} SKLADEM!", + "out_of_stock_title": "❌ {gpu_name} je vyprodán", + "sku_change_title": "🔄 Změna SKU nalezena pro {gpu_name}", + "buy_now": "**:point_right: [Koupit nyní]({product_link})**", + "price": "Cena", + "time": "Čas", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Starý SKU**: `{old_sku}`\n**Nový SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Možný blížící se pokles!" + }, + "da": { + "in_stock_title": "🚀 {gpu_name} PÅ LAGER!", + "out_of_stock_title": "❌ {gpu_name} er udsolgt", + "sku_change_title": "🔄 SKU-ændring fundet for {gpu_name}", + "buy_now": "**:point_right: [Køb nu]({product_link})**", + "price": "Pris", + "time": "Tid", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Gammelt SKU**: `{old_sku}`\n**Nyt SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Muligt snarligt drop!" + }, + "de": { + "in_stock_title": "🚀 {gpu_name} AUF LAGER!", + "out_of_stock_title": "❌ {gpu_name} ist ausverkauft", + "sku_change_title": "🔄 SKU-Änderung erkannt für {gpu_name}", + "buy_now": "**:point_right: [Jetzt kaufen]({product_link})**", + "price": "Preis", + "time": "Zeit", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Alte SKU**: `{old_sku}`\n**Neue SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Möglicher bevorstehender Drop!" + }, + "el": { + "in_stock_title": "🚀 {gpu_name} ΔΙΑΘΕΣΙΜΟ!", + "out_of_stock_title": "❌ {gpu_name} εξαντλήθηκε", + "sku_change_title": "🔄 Ανίχνευση αλλαγής SKU για {gpu_name}", + "buy_now": "**:point_right: [Αγόρασε τώρα]({product_link})**", + "price": "Τιμή", + "time": "Ώρα", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Παλαιό SKU**: `{old_sku}`\n**Νέο SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Πιθανή επικείμενη πτώση!" + }, + "es": { + "in_stock_title": "🚀 {gpu_name} ¡EN STOCK!", + "out_of_stock_title": "❌ {gpu_name} está agotado", + "sku_change_title": "🔄 Cambio de SKU detectado para {gpu_name}", + "buy_now": "**:point_right: [Comprar ahora]({product_link})**", + "price": "Precio", + "time": "Hora", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**SKU antiguo**: `{old_sku}`\n**SKU nuevo**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ ¡Posible drop inminente!" + }, + "et": { + "in_stock_title": "🚀 {gpu_name} LAOS!", + "out_of_stock_title": "❌ {gpu_name} on välja müüdud", + "sku_change_title": "🔄 Tuvastatud SKU muudatus {gpu_name}", + "buy_now": "**:point_right: [Osta kohe]({product_link})**", + "price": "Hind", + "time": "Aeg", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Vana SKU**: `{old_sku}`\n**Uus SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Võimalik varsti tulemas drop!" + }, + "fi": { + "in_stock_title": "🚀 {gpu_name} VARASTOSSA!", + "out_of_stock_title": "❌ {gpu_name} on loppunut", + "sku_change_title": "🔄 SKU-muutos havaittu {gpu_name}", + "buy_now": "**:point_right: [Osta nyt]({product_link})**", + "price": "Hinta", + "time": "Aika", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Vanha SKU**: `{old_sku}`\n**Uusi SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Mahdollinen lähestyvä drop!" + }, + "fr": { + "in_stock_title": "🚀 {gpu_name} EN STOCK !", + "out_of_stock_title": "❌ {gpu_name} est en rupture", + "sku_change_title": "🔄 Changement de SKU détecté pour {gpu_name}", + "buy_now": "**:point_right: [Acheter maintenant]({product_link})**", + "price": "Prix", + "time": "Heure", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Ancien SKU** : `{old_sku}`\n**Nouveau SKU** : `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Éventuel drop imminent!" + }, + "hr": { + "in_stock_title": "🚀 {gpu_name} NA ZALIHI!", + "out_of_stock_title": "❌ {gpu_name} je rasprodan", + "sku_change_title": "🔄 Promjena SKU za {gpu_name}", + "buy_now": "**:point_right: [Kupi sada]({product_link})**", + "price": "Cijena", + "time": "Vrijeme", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Stari SKU**: `{old_sku}`\n**Novi SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Mogući nadolazeći drop!" + }, + "hu": { + "in_stock_title": "🚀 {gpu_name} RAKTÁRON!", + "out_of_stock_title": "❌ {gpu_name} elfogyott", + "sku_change_title": "🔄 SKU változás észlelve: {gpu_name}", + "buy_now": "**:point_right: [Vásárlás most]({product_link})**", + "price": "Ár", + "time": "Idő", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Régi SKU**: `{old_sku}`\n**Új SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Lehetséges közelgő drop!" + }, + "ga": { + "in_stock_title": "🚀 {gpu_name} I STOC!", + "out_of_stock_title": "❌ níl {gpu_name} ar fáil", + "sku_change_title": "🔄 SKU athraithe aimsithe do {gpu_name}", + "buy_now": "**:point_right: [Ceannaigh anois]({product_link})**", + "price": "Praghas", + "time": "Am", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Sean SKU**: `{old_sku}`\n**SKU Nua**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ I bhfad is gaire drop féideartha!" + }, + "it": { + "in_stock_title": "🚀 {gpu_name} DISPONIBILE!", + "out_of_stock_title": "❌ {gpu_name} esaurito", + "sku_change_title": "🔄 Modifica SKU rilevata per {gpu_name}", + "buy_now": "**:point_right: [Acquista ora]({product_link})**", + "price": "Prezzo", + "time": "Ora", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Vecchio SKU**: `{old_sku}`\n**Nuovo SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Possibile drop imminente!" + }, + "lv": { + "in_stock_title": "🚀 {gpu_name} KRĀJUMĀ!", + "out_of_stock_title": "❌ {gpu_name} nav pieejams", + "sku_change_title": "🔄 SKU izmaiņas: {gpu_name}", + "buy_now": "**:point_right: [Pirkt tagad]({product_link})**", + "price": "Cena", + "time": "Laiks", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Vecais SKU**: `{old_sku}`\n**Jaunais SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Iespējams gaidāms drop!" + }, + "lt": { + "in_stock_title": "🚀 {gpu_name} SANDĖLYJE!", + "out_of_stock_title": "❌ {gpu_name} išparduotas", + "sku_change_title": "🔄 SKU pakeitimas aptiktas: {gpu_name}", + "buy_now": "**:point_right: [Pirkti dabar]({product_link})**", + "price": "Kaina", + "time": "Laikas", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Senas SKU**: `{old_sku}`\n**Naujas SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Rugsėjo drop'as gali artėti!" + }, + "mt": { + "in_stock_title": "🚀 {gpu_name} F'ĠESTA!", + "out_of_stock_title": "❌ {gpu_name} mhux disponibbli", + "sku_change_title": "🔄 Bidla SKU skoperta għal {gpu_name}", + "buy_now": "**:point_right: [Ixtri issa]({product_link})**", + "price": "Prezz", + "time": "Ħin", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**SKU Antik**: `{old_sku}`\n**SKU Ġdid**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Possibbli drop imminenti!" + }, + "nl": { + "in_stock_title": "🚀 {gpu_name} OP VOORRAAD!", + "out_of_stock_title": "❌ {gpu_name} is uitverkocht", + "sku_change_title": "🔄 SKU-wijziging gedetecteerd voor {gpu_name}", + "buy_now": "**:point_right: [Nu kopen]({product_link})**", + "price": "Prijs", + "time": "Tijd", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Oude SKU**: `{old_sku}`\n**Nieuwe SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Mogelijke aanstaande drop!" + }, + "pl": { + "in_stock_title": "🚀 {gpu_name} DOSTĘPNE!", + "out_of_stock_title": "❌ {gpu_name} jest niedostępny", + "sku_change_title": "🔄 Wykryto zmianę SKU dla {gpu_name}", + "buy_now": "**:point_right: [Kup teraz]({product_link})**", + "price": "Cena", + "time": "Czas", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Stare SKU**: `{old_sku}`\n**Nowe SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Możliwy nadchodzący drop!" + }, + "pt": { + "in_stock_title": "🚀 {gpu_name} EM STOCK!", + "out_of_stock_title": "❌ {gpu_name} esgotado", + "sku_change_title": "🔄 Alteração de SKU detectada para {gpu_name}", + "buy_now": "**:point_right: [Comprar agora]({product_link})**", + "price": "Preço", + "time": "Hora", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**SKU antigo**: `{old_sku}`\n**SKU novo**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Possível drop iminente!" + }, + "ro": { + "in_stock_title": "🚀 {gpu_name} ÎN STOC!", + "out_of_stock_title": "❌ {gpu_name} este epuizat", + "sku_change_title": "🔄 Schimbare SKU detectată pentru {gpu_name}", + "buy_now": "**:point_right: [Cumpără acum]({product_link})**", + "price": "Preț", + "time": "Timp", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**SKU vechi**: `{old_sku}`\n**SKU nou**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Posibil drop iminent!" + }, + "sk": { + "in_stock_title": "🚀 {gpu_name} NA SKLADE!", + "out_of_stock_title": "❌ {gpu_name} je vypredaný", + "sku_change_title": "🔄 Zmena SKU zistená pre {gpu_name}", + "buy_now": "**:point_right: [Kúp teraz]({product_link})**", + "price": "Cena", + "time": "Čas", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Starý SKU**: `{old_sku}`\n**Nový SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Možný nadchádzajúci drop!" + }, + "sl": { + "in_stock_title": "🚀 {gpu_name} NA ZALOGI!", + "out_of_stock_title": "❌ {gpu_name} je razprodan", + "sku_change_title": "🔄 Sprememba SKU zaznana za {gpu_name}", + "buy_now": "**:point_right: [Kupi zdaj]({product_link})**", + "price": "Cena", + "time": "Čas", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Stari SKU**: `{old_sku}`\n**Novi SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Možen prihajajoči drop!" + }, + "sv": { + "in_stock_title": "🚀 {gpu_name} I LAGER!", + "out_of_stock_title": "❌ {gpu_name} är slut", + "sku_change_title": "🔄 SKU-ändring upptäckt för {gpu_name}", + "buy_now": "**:point_right: [Köp nu]({product_link})**", + "price": "Pris", + "time": "Tid", + "footer": "NviBot • {DISCORD_SERVER_NAME}", + "sku_description": "**Gammalt SKU**: `{old_sku}`\n**Nytt SKU**: `{new_sku}`", + "imminent_drop": "{DISCORD_ROLE} ⚠️ Möjlig förestående drop!" + } +} diff --git a/app/localization.json b/app/localization.json index 20cefc7..f7e463b 100644 --- a/app/localization.json +++ b/app/localization.json @@ -1,266 +1,104 @@ -{ - "en": { - "in_stock_title": "🚀 {gpu_name} IN STOCK!", - "out_of_stock_title": "❌ {gpu_name} is out of stock", - "sku_change_title": "🔄 SKU change detected for {gpu_name}", - "buy_now": "**:point_right: [Buy now]({product_link})**", - "price": "Price", - "time": "Time", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Old SKU** : `{old_sku}`\n**New SKU** : `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Possible imminent drop!" +[ + { + "country": "United Kingdom", + "country_code": "GB", + "language_code": "en-GB", + "currency": "£" }, - "bg": { - "in_stock_title": "🚀 {gpu_name} ИМА НАЛИЧНОСТ!", - "out_of_stock_title": "❌ {gpu_name} е изчерпан", - "sku_change_title": "🔄 Засечена промяна на SKU за {gpu_name}", - "buy_now": "**:point_right: [Купи сега]({product_link})**", - "price": "Цена", - "time": "Час", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Стар SKU**: `{old_sku}`\n**Нов SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Възможен наближаващ спад!" + { + "country": "United States", + "country_code": "US", + "language_code": "en-US", + "currency": "$" }, - "cs": { - "in_stock_title": "🚀 {gpu_name} SKLADEM!", - "out_of_stock_title": "❌ {gpu_name} je vyprodán", - "sku_change_title": "🔄 Změna SKU nalezena pro {gpu_name}", - "buy_now": "**:point_right: [Koupit nyní]({product_link})**", - "price": "Cena", - "time": "Čas", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Starý SKU**: `{old_sku}`\n**Nový SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Možný blížící se pokles!" + { + "country": "Canada", + "country_code": "CA", + "language_code": "en-CA", + "currency": "$" }, - "da": { - "in_stock_title": "🚀 {gpu_name} PÅ LAGER!", - "out_of_stock_title": "❌ {gpu_name} er udsolgt", - "sku_change_title": "🔄 SKU-ændring fundet for {gpu_name}", - "buy_now": "**:point_right: [Køb nu]({product_link})**", - "price": "Pris", - "time": "Tid", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Gammelt SKU**: `{old_sku}`\n**Nyt SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Muligt snarligt drop!" + { + "country": "Belgium", + "country_code": "BE", + "language_code": "nl-BE", + "currency": "€" }, - "de": { - "in_stock_title": "🚀 {gpu_name} AUF LAGER!", - "out_of_stock_title": "❌ {gpu_name} ist ausverkauft", - "sku_change_title": "🔄 SKU-Änderung erkannt für {gpu_name}", - "buy_now": "**:point_right: [Jetzt kaufen]({product_link})**", - "price": "Preis", - "time": "Zeit", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Alte SKU**: `{old_sku}`\n**Neue SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Möglicher bevorstehender Drop!" + { + "country": "Netherlands", + "country_code": "NL", + "language_code": "nl-NL", + "currency": "€" }, - "el": { - "in_stock_title": "🚀 {gpu_name} ΔΙΑΘΕΣΙΜΟ!", - "out_of_stock_title": "❌ {gpu_name} εξαντλήθηκε", - "sku_change_title": "🔄 Ανίχνευση αλλαγής SKU για {gpu_name}", - "buy_now": "**:point_right: [Αγόρασε τώρα]({product_link})**", - "price": "Τιμή", - "time": "Ώρα", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Παλαιό SKU**: `{old_sku}`\n**Νέο SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Πιθανή επικείμενη πτώση!" + { + "country": "Denmark", + "country_code": "DK", + "language_code": "da-DK", + "currency": "kr" }, - "es": { - "in_stock_title": "🚀 {gpu_name} ¡EN STOCK!", - "out_of_stock_title": "❌ {gpu_name} está agotado", - "sku_change_title": "🔄 Cambio de SKU detectado para {gpu_name}", - "buy_now": "**:point_right: [Comprar ahora]({product_link})**", - "price": "Precio", - "time": "Hora", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**SKU antiguo**: `{old_sku}`\n**SKU nuevo**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ ¡Posible drop inminente!" + { + "country": "Germany", + "country_code": "DE", + "language_code": "de-DE", + "currency": "€" }, - "et": { - "in_stock_title": "🚀 {gpu_name} LAOS!", - "out_of_stock_title": "❌ {gpu_name} on välja müüdud", - "sku_change_title": "🔄 Tuvastatud SKU muudatus {gpu_name}", - "buy_now": "**:point_right: [Osta kohe]({product_link})**", - "price": "Hind", - "time": "Aeg", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Vana SKU**: `{old_sku}`\n**Uus SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Võimalik varsti tulemas drop!" + { + "country": "Spain", + "country_code": "ES", + "language_code": "es-ES", + "currency": "€" }, - "fi": { - "in_stock_title": "🚀 {gpu_name} VARASTOSSA!", - "out_of_stock_title": "❌ {gpu_name} on loppunut", - "sku_change_title": "🔄 SKU-muutos havaittu {gpu_name}", - "buy_now": "**:point_right: [Osta nyt]({product_link})**", - "price": "Hinta", - "time": "Aika", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Vanha SKU**: `{old_sku}`\n**Uusi SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Mahdollinen lähestyvä drop!" + { + "country": "France", + "country_code": "FR", + "language_code": "fr-FR", + "currency": "€" }, - "fr": { - "in_stock_title": "🚀 {gpu_name} EN STOCK !", - "out_of_stock_title": "❌ {gpu_name} est en rupture", - "sku_change_title": "🔄 Changement de SKU détecté pour {gpu_name}", - "buy_now": "**:point_right: [Acheter maintenant]({product_link})**", - "price": "Prix", - "time": "Heure", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Ancien SKU** : `{old_sku}`\n**Nouveau SKU** : `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Éventuel drop imminent!" + { + "country": "Italy", + "country_code": "IT", + "language_code": "it-IT", + "currency": "€" }, - "hr": { - "in_stock_title": "🚀 {gpu_name} NA ZALIHI!", - "out_of_stock_title": "❌ {gpu_name} je rasprodan", - "sku_change_title": "🔄 Promjena SKU za {gpu_name}", - "buy_now": "**:point_right: [Kupi sada]({product_link})**", - "price": "Cijena", - "time": "Vrijeme", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Stari SKU**: `{old_sku}`\n**Novi SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Mogući nadolazeći drop!" + { + "country": "Norway", + "country_code": "NO", + "language_code": "nb-NO", + "currency": "kr" }, - "hu": { - "in_stock_title": "🚀 {gpu_name} RAKTÁRON!", - "out_of_stock_title": "❌ {gpu_name} elfogyott", - "sku_change_title": "🔄 SKU változás észlelve: {gpu_name}", - "buy_now": "**:point_right: [Vásárlás most]({product_link})**", - "price": "Ár", - "time": "Idő", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Régi SKU**: `{old_sku}`\n**Új SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Lehetséges közelgő drop!" + { + "country": "Austria", + "country_code": "AT", + "language_code": "de-AT", + "currency": "€" }, - "ga": { - "in_stock_title": "🚀 {gpu_name} I STOC!", - "out_of_stock_title": "❌ níl {gpu_name} ar fáil", - "sku_change_title": "🔄 SKU athraithe aimsithe do {gpu_name}", - "buy_now": "**:point_right: [Ceannaigh anois]({product_link})**", - "price": "Praghas", - "time": "Am", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Sean SKU**: `{old_sku}`\n**SKU Nua**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ I bhfad is gaire drop féideartha!" + { + "country": "Poland", + "country_code": "PL", + "language_code": "pl-PL", + "currency": "zł" }, - "it": { - "in_stock_title": "🚀 {gpu_name} DISPONIBILE!", - "out_of_stock_title": "❌ {gpu_name} esaurito", - "sku_change_title": "🔄 Modifica SKU rilevata per {gpu_name}", - "buy_now": "**:point_right: [Acquista ora]({product_link})**", - "price": "Prezzo", - "time": "Ora", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Vecchio SKU**: `{old_sku}`\n**Nuovo SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Possibile drop imminente!" + { + "country": "Finland", + "country_code": "FI", + "language_code": "fi-FI", + "currency": "€" }, - "lv": { - "in_stock_title": "🚀 {gpu_name} KRĀJUMĀ!", - "out_of_stock_title": "❌ {gpu_name} nav pieejams", - "sku_change_title": "🔄 SKU izmaiņas: {gpu_name}", - "buy_now": "**:point_right: [Pirkt tagad]({product_link})**", - "price": "Cena", - "time": "Laiks", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Vecais SKU**: `{old_sku}`\n**Jaunais SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Iespējams gaidāms drop!" + { + "country": "Sweden", + "country_code": "SE", + "language_code": "sv-SE", + "currency": "kr" }, - "lt": { - "in_stock_title": "🚀 {gpu_name} SANDĖLYJE!", - "out_of_stock_title": "❌ {gpu_name} išparduotas", - "sku_change_title": "🔄 SKU pakeitimas aptiktas: {gpu_name}", - "buy_now": "**:point_right: [Pirkti dabar]({product_link})**", - "price": "Kaina", - "time": "Laikas", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Senas SKU**: `{old_sku}`\n**Naujas SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Rugsėjo drop'as gali artėti!" + { + "country": "South Korea", + "country_code": "KR", + "language_code": "ko-KR", + "currency": "₩" }, - "mt": { - "in_stock_title": "🚀 {gpu_name} F'ĠESTA!", - "out_of_stock_title": "❌ {gpu_name} mhux disponibbli", - "sku_change_title": "🔄 Bidla SKU skoperta għal {gpu_name}", - "buy_now": "**:point_right: [Ixtri issa]({product_link})**", - "price": "Prezz", - "time": "Ħin", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**SKU Antik**: `{old_sku}`\n**SKU Ġdid**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Possibbli drop imminenti!" - }, - "nl": { - "in_stock_title": "🚀 {gpu_name} OP VOORRAAD!", - "out_of_stock_title": "❌ {gpu_name} is uitverkocht", - "sku_change_title": "🔄 SKU-wijziging gedetecteerd voor {gpu_name}", - "buy_now": "**:point_right: [Nu kopen]({product_link})**", - "price": "Prijs", - "time": "Tijd", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Oude SKU**: `{old_sku}`\n**Nieuwe SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Mogelijke aanstaande drop!" - }, - "pl": { - "in_stock_title": "🚀 {gpu_name} DOSTĘPNE!", - "out_of_stock_title": "❌ {gpu_name} jest niedostępny", - "sku_change_title": "🔄 Wykryto zmianę SKU dla {gpu_name}", - "buy_now": "**:point_right: [Kup teraz]({product_link})**", - "price": "Cena", - "time": "Czas", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Stare SKU**: `{old_sku}`\n**Nowe SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Możliwy nadchodzący drop!" - }, - "pt": { - "in_stock_title": "🚀 {gpu_name} EM STOCK!", - "out_of_stock_title": "❌ {gpu_name} esgotado", - "sku_change_title": "🔄 Alteração de SKU detectada para {gpu_name}", - "buy_now": "**:point_right: [Comprar agora]({product_link})**", - "price": "Preço", - "time": "Hora", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**SKU antigo**: `{old_sku}`\n**SKU novo**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Possível drop iminente!" - }, - "ro": { - "in_stock_title": "🚀 {gpu_name} ÎN STOC!", - "out_of_stock_title": "❌ {gpu_name} este epuizat", - "sku_change_title": "🔄 Schimbare SKU detectată pentru {gpu_name}", - "buy_now": "**:point_right: [Cumpără acum]({product_link})**", - "price": "Preț", - "time": "Timp", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**SKU vechi**: `{old_sku}`\n**SKU nou**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Posibil drop iminent!" - }, - "sk": { - "in_stock_title": "🚀 {gpu_name} NA SKLADE!", - "out_of_stock_title": "❌ {gpu_name} je vypredaný", - "sku_change_title": "🔄 Zmena SKU zistená pre {gpu_name}", - "buy_now": "**:point_right: [Kúp teraz]({product_link})**", - "price": "Cena", - "time": "Čas", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Starý SKU**: `{old_sku}`\n**Nový SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Možný nadchádzajúci drop!" - }, - "sl": { - "in_stock_title": "🚀 {gpu_name} NA ZALOGI!", - "out_of_stock_title": "❌ {gpu_name} je razprodan", - "sku_change_title": "🔄 Sprememba SKU zaznana za {gpu_name}", - "buy_now": "**:point_right: [Kupi zdaj]({product_link})**", - "price": "Cena", - "time": "Čas", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Stari SKU**: `{old_sku}`\n**Novi SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Možen prihajajoči drop!" - }, - "sv": { - "in_stock_title": "🚀 {gpu_name} I LAGER!", - "out_of_stock_title": "❌ {gpu_name} är slut", - "sku_change_title": "🔄 SKU-ändring upptäckt för {gpu_name}", - "buy_now": "**:point_right: [Köp nu]({product_link})**", - "price": "Pris", - "time": "Tid", - "footer": "NviBot • {DISCORD_SERVER_NAME}", - "sku_description": "**Gammalt SKU**: `{old_sku}`\n**Nytt SKU**: `{new_sku}`", - "imminent_drop": "{DISCORD_ROLE} ⚠️ Möjlig förestående drop!" + { + "country": "Japan", + "country_code": "JP", + "language_code": "ja-JP", + "currency": "¥" } -} +] diff --git a/app/notifier.py b/app/notifier.py index b28d342..ad668c0 100644 --- a/app/notifier.py +++ b/app/notifier.py @@ -2,7 +2,7 @@ import time import logging import requests from env_config import ( - DISCORD_WEBHOOK_URL, DISCORD_SERVER_NAME, DISCORD_ROLE_MAP, TEST_MODE, DISCORD_NOTIFICATION_CURRENCY, + DISCORD_WEBHOOK_URL, DISCORD_SERVER_NAME, DISCORD_ROLE_MAP, TEST_MODE, currency, in_stock_title, out_of_stock_title, sku_change_title, buy_now, price_label, time_label, footer, sku_description, imminent_drop ) @@ -13,7 +13,6 @@ THUMBNAIL = "https://git.djeex.fr/Djeex/nvidia-stock-bot/raw/branch/main/assets/ # In stock def send_discord_notification(gpu_name, product_link, products_price): timestamp = int(time.time()) - currency = DISCORD_NOTIFICATION_CURRENCY if TEST_MODE: logging.info(f"[TEST MODE] Notification: {gpu_name} available!") return