Suivi automatisé des offres de jeux multiplateformes

Intermédiaire

Ceci est unPersonal Productivityworkflow d'automatisation du domainecontenant 11 nœuds.Utilise principalement des nœuds comme If, Cron, Gmail, Sqlite, Function. Suivi automatique des offres de jeux multiplateformes avec Deku Deals et les rappels Gmail

Prérequis
  • Compte Google et informations d'identification Gmail API
  • Peut nécessiter les informations d'identification d'authentification de l'API cible
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "nodes": [
    {
      "name": "Vérification Quotidienne (8h)",
      "type": "n8n-nodes-base.cron",
      "notes": {
        "text": "### 1. Daily Check (8 AM)\n\nThis `Cron` node triggers the workflow automatically every **day at 8:00 AM** (based on your n8n server's local time zone). This allows for frequent checks for new deals.\n\n**To change the schedule:** Adjust the 'Hour' and 'Minute' fields to your preferred time and frequency.",
        "position": "right"
      },
      "position": [
        240,
        300
      ],
      "parameters": {
        "mode": "everyDay",
        "value": {
          "hour": [
            8
          ],
          "minute": [
            0
          ]
        },
        "options": {}
      },
      "typeVersion": 1,
      "id": "V-rification-Quotidienne-8h--0"
    },
    {
      "name": "Récupération de la Page Deku Deals",
      "type": "n8n-nodes-base.httpRequest",
      "notes": {
        "text": "### 2. Fetch Deku Deals Page\n\nThis `HTTP Request` node downloads the HTML content of Deku Deals' 'Most Popular' page.\n\n**Setup:**\n1.  **URL:** The URL is pre-filled for Deku Deals' 'Most Popular' deals. You can change this to `/deals` or another specific section if you prefer.\n2.  **Response Format:** Ensure this is set to `string` (for HTML content).\n\n**Considerations:**\n* Deku Deals is generally well-structured for scraping. If their layout changes, the `HTML Extract` node will need updates.",
        "position": "right"
      },
      "position": [
        480,
        300
      ],
      "parameters": {
        "url": "https://www.dekudeals.com/most-popular",
        "options": {},
        "responseFormat": "string"
      },
      "typeVersion": 3,
      "id": "R-cup-ration-de-la-Page-Deku-Deals-1"
    },
    {
      "name": "Extraction de Chaque Carte de Promotion",
      "type": "n8n-nodes-base.htmlExtract",
      "notes": {
        "text": "### 3. Extract Each Deal Card\n\nThis `HTML Extract` node first extracts each individual 'game card' HTML block. This makes it easier to process each deal independently in the next step.\n\n**Setup:**\n1.  **HTML:** This field is already set to `{{ $node[\"Fetch Deku Deals Page\"].json.data }}`.\n2.  **Selector:** `div.game-card` (This targets the main container for each deal).\n3.  **Attribute:** `html` (to get the full HTML content of each card).\n\n**If the layout changes:** You might need to update `div.game-card` to the new wrapper for individual game deals.",
        "position": "right"
      },
      "position": [
        720,
        300
      ],
      "parameters": {
        "html": "={{ $node[\"Fetch Deku Deals Page\"].json.data }}",
        "extractOperations": [
          {
            "options": {},
            "selector": "div.game-card",
            "attribute": "html",
            "operation": "extract",
            "propertyName": "dealHtml"
          }
        ]
      },
      "typeVersion": 1,
      "id": "Extraction-de-Chaque-Carte-de-Promotion-2"
    },
    {
      "name": "Analyse des Détails de la Promotion (Code Fonction)",
      "type": "n8n-nodes-base.function",
      "notes": {
        "text": "### 4. Parse Deal Details (Function Code)\n\nThis `Function` node dives into the HTML of each deal card to extract the specific details (title, price, link, platform, etc.). It also creates a `dealUniqueId` for tracking.\n\n**Setup:**\n* This node uses JavaScript with `jsdom` (an n8n built-in library for parsing HTML) to select individual elements within each `dealHtml` item.\n* **Crucially, the selectors here (`div.name > a`, `div.price.current`, etc.) are also subject to website changes.** You might need to update them if the layout of `div.game-card` itself changes.\n\n**Output:** Each item will now have fields like `dealUniqueId`, `gameTitle`, `gameLink`, `platforms`, `currentPrice`, `originalPrice`, `discount`.",
        "position": "right"
      },
      "position": [
        960,
        300
      ],
      "parameters": {
        "options": {},
        "function": "const { JSDOM } = require('jsdom');\n\nconst deals = [];\n\nfor (const item of items) {\n  const dom = new JSDOM(item.json.dealHtml);\n  const doc = dom.window.document;\n\n  const titleElement = doc.querySelector('div.name > a');\n  const linkElement = doc.querySelector('div.name > a');\n  const currentPriceElement = doc.querySelector('div.price-wrapper > div.price.current');\n  const originalPriceElement = doc.querySelector('div.price-wrapper > div.price.original');\n  const discountElement = doc.querySelector('div.price-wrapper > div.price.discount');\n  const platformElements = doc.querySelectorAll('div.platforms > span.platform');\n\n  const gameTitle = titleElement ? titleElement.textContent.trim() : 'N/A';\n  const gameLink = linkElement ? `https://www.dekudeals.com${linkElement.getAttribute('href')}` : 'N/A';\n  const currentPrice = currentPriceElement ? currentPriceElement.textContent.trim() : 'N/A';\n  const originalPrice = originalPriceElement ? originalPriceElement.textContent.trim() : 'N/A';\n  const discount = discountElement ? discountElement.textContent.trim() : 'N/A';\n  const platforms = Array.from(platformElements).map(p => p.textContent.trim()).join(', ');\n\n  // Create a unique ID for the deal for tracking in SQLite\n  // Combination of title, platform, and current price should be fairly unique\n  const dealUniqueId = `${gameTitle}|${platforms}|${currentPrice}`;\n\n  deals.push({\n    json: {\n      dealUniqueId: dealUniqueId,\n      gameTitle: gameTitle,\n      gameLink: gameLink,\n      platforms: platforms,\n      currentPrice: currentPrice,\n      originalPrice: originalPrice,\n      discount: discount\n    }\n  });\n}\n\nreturn deals;"
      },
      "typeVersion": 1,
      "id": "Analyse-des-D-tails-de-la-Promotion-Code-Fonction--3"
    },
    {
      "name": "SQLite : Vérifier l'Existence de la Table",
      "type": "n8n-nodes-base.sqlite",
      "notes": {
        "text": "### 5. SQLite: Ensure Table Exists\n\nThis `SQLite` node ensures that a local database table named `notified_deals` exists. This table will store the unique IDs of deals you've already been notified about.\n\n**Setup:**\n* **Database:** `dekudeals` (this creates a file `dekudeals.db` in your n8n data directory).\n* **Query:** `CREATE TABLE IF NOT EXISTS notified_deals (...)` as shown.\n\n**No further action needed**; this node will run automatically. On subsequent runs, it will simply confirm the table exists.",
        "position": "right"
      },
      "position": [
        1200,
        220
      ],
      "parameters": {
        "query": "CREATE TABLE IF NOT EXISTS notified_deals (deal_id TEXT PRIMARY KEY, game_title TEXT, platforms TEXT, current_price TEXT, original_price TEXT, discount TEXT, deal_link TEXT, notified_date TEXT)",
        "database": "dekudeals"
      },
      "typeVersion": 1,
      "id": "SQLite-V-rifier-l-Existence-de-la-Table-4"
    },
    {
      "name": "SQLite : Vérifier si Notifié",
      "type": "n8n-nodes-base.sqlite",
      "notes": {
        "text": "### 6. SQLite: Check if Deal Already Notified\n\nThis `SQLite` node checks if each extracted deal (using its `dealUniqueId`) is already present in your `notified_deals` database.\n\n**Setup:**\n* **Database:** `dekudeals`\n* **Query:** `SELECT deal_id FROM notified_deals WHERE deal_id = '{{ $json.dealUniqueId }}'` (It looks for a match for the current deal's unique ID).\n\n**Output:** If the deal is found, this node will output an item. If not, it will output no item, which is crucial for the 'Item Lists' node below.",
        "position": "right"
      },
      "position": [
        1440,
        300
      ],
      "parameters": {
        "query": "SELECT deal_id FROM notified_deals WHERE deal_id = '{{ $json.dealUniqueId }}'",
        "database": "dekudeals"
      },
      "typeVersion": 1,
      "id": "SQLite-V-rifier-si-Notifi--5"
    },
    {
      "name": "Diviser en Notifiés/Nouveaux",
      "type": "n8n-nodes-base.itemLists",
      "notes": {
        "text": "### 7. Split into Notified/New\n\nThis `Item Lists` node takes the results from the 'SQLite: Check if Notified' node and splits them into two paths:\n* **Original items without a matching ID in the database:** These are **NEW** deals (`Output 1`).\n* **Original items with a matching ID in the database:** These are **ALREADY NOTIFIED** deals (`Output 2`).\n\n**No configuration needed**; it automatically separates the items based on whether the `SQLite` query found a match or not.",
        "position": "right"
      },
      "position": [
        1680,
        300
      ],
      "parameters": {
        "mode": "splitInBatches",
        "property": "dealUniqueId"
      },
      "typeVersion": 1,
      "id": "Diviser-en-Notifi-s-Nouveaux-6"
    },
    {
      "name": "Si (Nouvelles Promotions Trouvées)",
      "type": "n8n-nodes-base.if",
      "notes": {
        "text": "### 8. If (New Deals Found)\n\nThis `If` node checks if there are any *new* deals (i.e., items coming from the 'New' path of the 'Split into Notified/New' node).\n\n* **'True' branch:** If new deals are found, the workflow proceeds to insert them into the database and send a notification.\n* **'False' branch:** If no new deals are found, the workflow ends here (no notification needed).\n\n**No configuration needed**; it automatically checks if there are any items.",
        "position": "right"
      },
      "position": [
        1920,
        220
      ],
      "parameters": {
        "conditions": [
          {
            "value1": "={{ $json.length }}",
            "value2": "0",
            "operation": "notEqual"
          }
        ]
      },
      "typeVersion": 1,
      "id": "Si-Nouvelles-Promotions-Trouv-es--7"
    },
    {
      "name": "SQLite : Insérer les Nouvelles Promotions",
      "type": "n8n-nodes-base.sqlite",
      "notes": {
        "text": "### 9. SQLite: Insert New Deals\n\nThis `SQLite` node inserts the details of the newly found deals into your `notified_deals` database. This ensures you won't be notified about them again on subsequent runs.\n\n**Setup:**\n* **Database:** `dekudeals`\n* **Query:** The `INSERT INTO` query is pre-filled, saving all the extracted deal details.\n\n**No further action needed**; it automatically stores the new deal information.",
        "position": "right"
      },
      "position": [
        2160,
        140
      ],
      "parameters": {
        "query": "INSERT INTO notified_deals (deal_id, game_title, platforms, current_price, original_price, discount, deal_link, notified_date) VALUES ('{{ $json.dealUniqueId }}', '{{ $json.gameTitle }}', '{{ $json.platforms }}', '{{ $json.currentPrice }}', '{{ $json.originalPrice }}', '{{ $json.discount }}', '{{ $json.gameLink }}', '{{ new Date().toISOString() }}')",
        "database": "dekudeals"
      },
      "typeVersion": 1,
      "id": "SQLite-Ins-rer-les-Nouvelles-Promotions-8"
    },
    {
      "name": "Formater le Message de Notification",
      "type": "n8n-nodes-base.function",
      "notes": {
        "text": "### 10. Format Notification Message\n\nThis `Function` node takes the new deal details and formats them into a clear, readable message for your notification (e.g., email or Telegram).\n\n**Customization:**\n* You can change the introductory text, add more emojis, or adjust the display format of each deal. It dynamically adds 'Was' and 'Discount' info only if available.\n\n**No configuration needed if your property names match the previous node's output.**",
        "position": "right"
      },
      "position": [
        2400,
        140
      ],
      "parameters": {
        "options": {},
        "function": "let message = \"🎮 **New Game Deals on Deku Deals!** 🎮\\n\\n\";\n\nfor (const item of items) {\n  message += `**${item.json.gameTitle}** (${item.json.platforms})\\n` +\n             `  Current Price: **${item.json.currentPrice}**` +\n             (item.json.originalPrice !== 'N/A' ? ` (Was: ${item.json.originalPrice})` : '') +\n             (item.json.discount !== 'N/A' ? ` | Discount: ${item.json.discount}` : '') +\n             `\\n  Claim/View Deal: ${item.json.gameLink}\\n\\n`;\n}\n\nmessage += \"Check out all deals: https://www.dekudeals.com/most-popular\";\n\nreturn [{ json: { notificationMessage: message } }];"
      },
      "typeVersion": 1,
      "id": "Formater-le-Message-de-Notification-9"
    },
    {
      "name": "Envoyer une Notification par Email",
      "type": "n8n-nodes-base.gmail",
      "notes": {
        "text": "### 11. Send Email Notification\n\nThis `Gmail` node sends the formatted notification message about the new game deals.\n\n**Setup:**\n1.  **Gmail Credential:** Select your Gmail API credential.\n2.  **From Email:** Enter your Gmail address (must match the authenticated account).\n3.  **To Email:** **IMPORTANT: Change `YOUR_RECIPIENT_EMAIL@example.com` to your actual email address!**\n4.  **Subject & Text:** These fields pull the formatted message from the previous node.\n\n**To switch to Telegram/Slack/Discord:** Simply replace this node with your preferred notification service's node, and map the `notificationMessage` to its text field. You'll need to set up credentials for that service.",
        "position": "right"
      },
      "position": [
        2640,
        140
      ],
      "parameters": {
        "text": "={{ $json.notificationMessage }}",
        "options": {},
        "subject": "🎮 New Game Deals Alert! (Deku Deals)",
        "toEmail": "YOUR_RECIPIENT_EMAIL@example.com",
        "fromEmail": "YOUR_GMAIL_EMAIL@gmail.com"
      },
      "credentials": {
        "gmailApi": {
          "id": "YOUR_GMAIL_CREDENTIAL_ID",
          "resolve": false
        }
      },
      "typeVersion": 2,
      "id": "Envoyer-une-Notification-par-Email-10"
    }
  ],
  "pinData": {},
  "version": 1,
  "connections": {
    "V-rification-Quotidienne-8h--0": {
      "main": [
        [
          {
            "node": "R-cup-ration-de-la-Page-Deku-Deals-1",
            "type": "main"
          }
        ]
      ]
    },
    "Si-Nouvelles-Promotions-Trouv-es--7": {
      "main": [
        [
          {
            "node": "SQLite-Ins-rer-les-Nouvelles-Promotions-8",
            "type": "main"
          }
        ],
        []
      ]
    },
    "R-cup-ration-de-la-Page-Deku-Deals-1": {
      "main": [
        [
          {
            "node": "Extraction-de-Chaque-Carte-de-Promotion-2",
            "type": "main"
          }
        ]
      ]
    },
    "Extraction-de-Chaque-Carte-de-Promotion-2": {
      "main": [
        [
          {
            "node": "Analyse-des-D-tails-de-la-Promotion-Code-Fonction--3",
            "type": "main"
          }
        ]
      ]
    },
    "Diviser-en-Notifi-s-Nouveaux-6": {
      "main": [
        [
          {
            "node": "Si-Nouvelles-Promotions-Trouv-es--7",
            "type": "main"
          }
        ],
        []
      ]
    },
    "SQLite-Ins-rer-les-Nouvelles-Promotions-8": {
      "main": [
        [
          {
            "node": "Formater-le-Message-de-Notification-9",
            "type": "main"
          }
        ]
      ]
    },
    "SQLite-V-rifier-si-Notifi--5": {
      "main": [
        [
          {
            "node": "Diviser-en-Notifi-s-Nouveaux-6",
            "type": "main"
          }
        ]
      ],
      "output": [
        {
          "type": "item",
          "toIndex": 0,
          "fromIndex": 0,
          "destination": [
            {
              "node": "Diviser en Notifiés/Nouveaux",
              "input": "input2"
            }
          ]
        }
      ]
    },
    "Formater-le-Message-de-Notification-9": {
      "main": [
        [
          {
            "node": "Envoyer-une-Notification-par-Email-10",
            "type": "main"
          }
        ]
      ]
    },
    "SQLite-V-rifier-l-Existence-de-la-Table-4": {
      "main": [
        [
          {
            "node": "SQLite-V-rifier-si-Notifi--5",
            "type": "main"
          }
        ]
      ]
    },
    "Analyse-des-D-tails-de-la-Promotion-Code-Fonction--3": {
      "main": [
        [
          {
            "node": "SQLite-V-rifier-si-Notifi--5",
            "type": "main"
          }
        ]
      ]
    }
  }
}
Foire aux questions

Comment utiliser ce workflow ?

Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.

Dans quelles scénarios ce workflow est-il adapté ?

Intermédiaire - Productivité personnelle

Est-ce payant ?

Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.

Informations sur le workflow
Niveau de difficulté
Intermédiaire
Nombre de nœuds11
Catégorie1
Types de nœuds8
Description de la difficulté

Adapté aux utilisateurs expérimentés, avec des workflows de complexité moyenne contenant 6-15 nœuds

Auteur
Piotr Sobolewski

Piotr Sobolewski

@piotrsobolewski

AI PhD with 7 years experience as a game dev CEO, currently teaching, helping others and building something new.

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34