Seguimiento automatizado de ofertas de juegos multiplataforma

Intermedio

Este es unPersonal Productivityflujo de automatización del dominio deautomatización que contiene 11 nodos.Utiliza principalmente nodos como If, Cron, Gmail, Sqlite, Function. Seguimiento automatizado de ofertas de juegos multiplataforma usando Deku Deals y Gmail

Requisitos previos
  • Cuenta de Google y credenciales de API de Gmail
  • Pueden requerirse credenciales de autenticación para la API de destino
Vista previa del flujo de trabajo
Visualización de las conexiones entre nodos, con soporte para zoom y panorámica
Exportar flujo de trabajo
Copie la siguiente configuración JSON en n8n para importar y usar este flujo de trabajo
{
  "nodes": [
    {
      "name": "Verificación Diaria (8 AM)",
      "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": "Verificaci-n-Diaria-8-AM--0"
    },
    {
      "name": "Obtener Página de 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": "Obtener-P-gina-de-Deku-Deals-1"
    },
    {
      "name": "Extraer Cada Tarjeta de Oferta",
      "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": "Extraer-Cada-Tarjeta-de-Oferta-2"
    },
    {
      "name": "Analizar Detalles de la Oferta (Código de Función)",
      "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": "Analizar-Detalles-de-la-Oferta-C-digo-de-Funci-n--3"
    },
    {
      "name": "SQLite: Asegurar que la Tabla Exista",
      "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-Asegurar-que-la-Tabla-Exista-4"
    },
    {
      "name": "SQLite: Verificar si ya se Notificó",
      "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-Verificar-si-ya-se-Notific--5"
    },
    {
      "name": "Dividir en Notificadas/Nuevas",
      "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": "Dividir-en-Notificadas-Nuevas-6"
    },
    {
      "name": "Si (Se Encuentran Ofertas Nuevas)",
      "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-Se-Encuentran-Ofertas-Nuevas--7"
    },
    {
      "name": "SQLite: Insertar Ofertas Nuevas",
      "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-Insertar-Ofertas-Nuevas-8"
    },
    {
      "name": "Formatear Mensaje de Notificación",
      "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": "Formatear-Mensaje-de-Notificaci-n-9"
    },
    {
      "name": "Enviar Notificación por Correo",
      "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": "Enviar-Notificaci-n-por-Correo-10"
    }
  ],
  "pinData": {},
  "version": 1,
  "connections": {
    "Verificaci-n-Diaria-8-AM--0": {
      "main": [
        [
          {
            "node": "Obtener-P-gina-de-Deku-Deals-1",
            "type": "main"
          }
        ]
      ]
    },
    "Si-Se-Encuentran-Ofertas-Nuevas--7": {
      "main": [
        [
          {
            "node": "SQLite-Insertar-Ofertas-Nuevas-8",
            "type": "main"
          }
        ],
        []
      ]
    },
    "Obtener-P-gina-de-Deku-Deals-1": {
      "main": [
        [
          {
            "node": "Extraer-Cada-Tarjeta-de-Oferta-2",
            "type": "main"
          }
        ]
      ]
    },
    "Extraer-Cada-Tarjeta-de-Oferta-2": {
      "main": [
        [
          {
            "node": "Analizar-Detalles-de-la-Oferta-C-digo-de-Funci-n--3",
            "type": "main"
          }
        ]
      ]
    },
    "Dividir-en-Notificadas-Nuevas-6": {
      "main": [
        [
          {
            "node": "Si-Se-Encuentran-Ofertas-Nuevas--7",
            "type": "main"
          }
        ],
        []
      ]
    },
    "SQLite-Insertar-Ofertas-Nuevas-8": {
      "main": [
        [
          {
            "node": "Formatear-Mensaje-de-Notificaci-n-9",
            "type": "main"
          }
        ]
      ]
    },
    "SQLite-Verificar-si-ya-se-Notific--5": {
      "main": [
        [
          {
            "node": "Dividir-en-Notificadas-Nuevas-6",
            "type": "main"
          }
        ]
      ],
      "output": [
        {
          "type": "item",
          "toIndex": 0,
          "fromIndex": 0,
          "destination": [
            {
              "node": "Dividir en Notificadas/Nuevas",
              "input": "input2"
            }
          ]
        }
      ]
    },
    "Formatear-Mensaje-de-Notificaci-n-9": {
      "main": [
        [
          {
            "node": "Enviar-Notificaci-n-por-Correo-10",
            "type": "main"
          }
        ]
      ]
    },
    "SQLite-Asegurar-que-la-Tabla-Exista-4": {
      "main": [
        [
          {
            "node": "SQLite-Verificar-si-ya-se-Notific--5",
            "type": "main"
          }
        ]
      ]
    },
    "Analizar-Detalles-de-la-Oferta-C-digo-de-Funci-n--3": {
      "main": [
        [
          {
            "node": "SQLite-Verificar-si-ya-se-Notific--5",
            "type": "main"
          }
        ]
      ]
    }
  }
}
Preguntas frecuentes

¿Cómo usar este flujo de trabajo?

Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.

¿En qué escenarios es adecuado este flujo de trabajo?

Intermedio - Productividad personal

¿Es de pago?

Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.

Información del flujo de trabajo
Nivel de dificultad
Intermedio
Número de nodos11
Categoría1
Tipos de nodos8
Descripción de la dificultad

Adecuado para usuarios con experiencia intermedia, flujos de trabajo de complejidad media con 6-15 nodos

Autor
Piotr Sobolewski

Piotr Sobolewski

@piotrsobolewski

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

Enlaces externos
Ver en n8n.io

Compartir este flujo de trabajo

Categorías

Categorías: 34