N
n8n Store
Workflow Market
Decodo Instant Shopping Insights - Amazon Product Recommender

Decodo Instant Shopping Insights - Amazon Product Recommender

by khmuhtadin1 views

描述

分类

📢 Marketing🤖 AI & Machine Learning

使用的节点

n8n-nodes-base.ifn8n-nodes-base.setn8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.waitn8n-nodes-base.telegramn8n-nodes-base.telegramn8n-nodes-base.telegramn8n-nodes-base.telegramn8n-nodes-base.telegram
价格免费
浏览量1
最后更新2/3/2026
workflow.json
{
  "id": "hKrx6Vbl4FlVrO0G",
  "meta": {
    "instanceId": "c2650793f644091dc80fb900fe63448ad1f4b774008de9608064d67294f8307c"
  },
  "name": "Decodo Instant Shopping Insights - Amazon Product Recommender",
  "tags": [],
  "nodes": [
    {
      "id": "27d87950-96a2-4fe5-9e42-ca1106a93831",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        752,
        528
      ],
      "webhookId": "3c2e5166-ae50-4765-9e90-b6d9dc691e9a",
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "QUapgouHFfRVQShy",
          "name": "Piranusa_assistantbot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f150d08d-0690-4980-9288-2b5ae06e61f4",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        768,
        1024
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "624ba616-679c-4ede-822f-a2f83cc6a146",
      "name": "Extract Chat & Query",
      "type": "n8n-nodes-base.set",
      "position": [
        1536,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "97558f42-95e0-4069-a0dd-797082ab30ca",
              "name": "chatId",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            },
            {
              "id": "f5221be7-a62c-43dd-8753-72db6062d98b",
              "name": "message",
              "type": "string",
              "value": "={{ $json.output.keyword.replaceAll(' ','+') }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "2f2cfc04-08d9-49f0-aa25-decbcef3db59",
      "name": "Decodo API",
      "type": "@decodo/n8n-nodes-decodo.decodo",
      "position": [
        1808,
        480
      ],
      "parameters": {
        "url": "=https://www.amazon.com/s?k={{ $json.message }}",
        "operation": "amazon"
      },
      "credentials": {
        "decodoApi": {
          "id": "um6rsqlbtrY3NRQB",
          "name": "[email protected]"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "252f89f4-54a2-4ea1-9ed4-92773c3d9b1b",
      "name": "Process Product Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2000,
        480
      ],
      "parameters": {
        "jsCode": "// Enhanced: Extract, Clean, and Format for AI with Sales Volume + CLEAN URLS\nconst inputData = $input.all();\nconst products = [];\n\n// Helper: Clean Amazon URL - remove UTM parameters\nfunction cleanAmazonURL(url) {\n  if (!url) return '';\n  \n  // Remove everything after \"?\" (UTM parameters)\n  const cleanUrl = url.split('?')[0];\n  \n  // Extract ASIN for shortest possible URL\n  const asinMatch = cleanUrl.match(/\\/dp\\/([A-Z0-9]{10})/i) || \n                    cleanUrl.match(/\\/gp\\/product\\/([A-Z0-9]{10})/i) ||\n                    cleanUrl.match(/\\/([A-Z0-9]{10})\\//i);\n  \n  if (asinMatch) {\n    return `https://www.amazon.com/dp/${asinMatch[1]}`;\n  }\n  \n  // If no ASIN found, return cleaned URL without params\n  return cleanUrl.startsWith('http') ? cleanUrl : `https://www.amazon.com${cleanUrl}`;\n}\n\n// Helper function to parse sales volume into numeric value for sorting\nfunction parseSalesVolume(salesText) {\n  if (!salesText) return 0;\n  \n  const text = salesText.toLowerCase();\n  \n  // Extract number\n  const match = text.match(/(\\d+\\.?\\d*)([km]?)\\+?/i);\n  if (!match) return 0;\n  \n  let value = parseFloat(match[1]);\n  const multiplier = match[2];\n  \n  // Apply multiplier\n  if (multiplier === 'k') value *= 1000;\n  if (multiplier === 'm') value *= 1000000;\n  \n  return Math.floor(value);\n}\n\n// Helper function to format sales volume for display\nfunction formatSalesVolume(salesText) {\n  if (!salesText) return 'N/A';\n  return salesText.replace(/bought in past month/gi, '').trim();\n}\n\n// Calculate product score (for smart ranking)\nfunction calculateProductScore(product, salesNumeric) {\n  const rating = product.rating || 0;\n  const reviews = product.reviews_count || 0;\n  const discount = product.price_strikethrough ? \n    ((product.price_strikethrough - product.price) / product.price_strikethrough) : 0;\n  \n  // Weighted scoring\n  const ratingScore = rating * 20; // Max 100\n  const reviewScore = Math.min(reviews / 100, 50); // Max 50\n  const salesScore = Math.min(salesNumeric / 1000, 30); // Max 30\n  const discountScore = discount * 20; // Max 20\n  \n  // Bonus for badges\n  const badgeBonus = (product.is_amazons_choice ? 10 : 0) + \n                     (product.best_seller ? 10 : 0);\n  \n  return ratingScore + reviewScore + salesScore + discountScore + badgeBonus;\n}\n\n// Extract all products\nfor (const item of inputData) {\n  const results = item.json.results?.[0]?.content?.results?.results;\n  if (!results) continue;\n  \n  // Combine paid and organic products\n  const allProducts = [\n    ...(results.paid || []),\n    ...(results.organic || []),\n    ...(results.amazons_choices || [])\n  ];\n  \n  allProducts.forEach(p => {\n    // Skip if missing critical data\n    if (!p.title || !p.price) return;\n    \n    const salesVolume = parseSalesVolume(p.sales_volume);\n    \n    products.push({\n      title: p.title,\n      price: p.price,\n      original_price: p.price_strikethrough || null,\n      rating: p.rating || 0,\n      reviews: p.reviews_count || 0,\n      \n      // Sales data\n      sales_volume_raw: p.sales_volume || 'N/A',\n      sales_volume_display: formatSalesVolume(p.sales_volume),\n      sales_volume_numeric: salesVolume,\n      \n      // Discount calculation\n      discount: p.price_strikethrough ? \n        Math.round(((p.price_strikethrough - p.price) / p.price_strikethrough) * 100) : 0,\n      savings: p.price_strikethrough ? \n        (p.price_strikethrough - p.price).toFixed(2) : 0,\n      \n      // CLEAN URL - remove UTM parameters and extract ASIN\n      url: cleanAmazonURL(p.url?.startsWith('http') ? p.url : `https://www.amazon.com${p.url}`),\n      \n      // Badges\n      is_amazons_choice: p.is_amazons_choice || false,\n      is_bestseller: p.best_seller || false,\n      is_sponsored: p.is_sponsored || false,\n      \n      // Image\n      image: p.url_image || null,\n      \n      // Manufacturer\n      brand: p.manufacturer || 'Generic',\n      \n      // Shipping\n      shipping: p.shipping_information || 'See Amazon',\n      \n      // Calculated score for ranking (weighted)\n      score: calculateProductScore(p, salesVolume)\n    });\n  });\n}\n\n// Remove duplicates (same ASIN)\nconst uniqueProducts = products.reduce((acc, product) => {\n  const exists = acc.find(p => p.title === product.title && p.price === product.price);\n  if (!exists) acc.push(product);\n  return acc;\n}, []);\n\n// Sort by calculated score (best overall value)\nuniqueProducts.sort((a, b) => b.score - a.score);\n\n// Get query\nconst query = $('Extract Chat & Query').first().json.message.replaceAll('+', ' ');\n\n// Calculate statistics\nconst avgPrice = uniqueProducts.reduce((sum, p) => sum + p.price, 0) / uniqueProducts.length;\nconst avgRating = uniqueProducts.reduce((sum, p) => sum + p.rating, 0) / uniqueProducts.length;\nconst totalSales = uniqueProducts.reduce((sum, p) => sum + p.sales_volume_numeric, 0);\n\n// Create AI-ready output with enhanced categorization\nreturn [{\n  json: {\n    query: query,\n    total: uniqueProducts.length,\n    \n    // Statistics\n    stats: {\n      avg_price: avgPrice.toFixed(2),\n      min_price: Math.min(...uniqueProducts.map(p => p.price)),\n      max_price: Math.max(...uniqueProducts.map(p => p.price)),\n      avg_rating: avgRating.toFixed(2),\n      total_reviews: uniqueProducts.reduce((sum, p) => sum + p.reviews, 0),\n      total_sales: totalSales\n    },\n    \n    // Top picks (by overall score)\n    top_picks: uniqueProducts.slice(0, 10).map(p => ({\n      title: p.title,\n      price: p.price,\n      rating: p.rating,\n      reviews: p.reviews,\n      sales: p.sales_volume_display,\n      discount: p.discount > 0 ? `${p.discount}% off` : null,\n      badges: {\n        choice: p.is_amazons_choice,\n        bestseller: p.is_bestseller\n      },\n      url: p.url  // Already cleaned by cleanAmazonURL function\n    })),\n    \n    // Budget options (under $30, sorted by rating)\n    budget_options: uniqueProducts\n      .filter(p => p.price < 30)\n      .sort((a, b) => b.rating - a.rating || b.reviews - a.reviews)\n      .slice(0, 5)\n      .map(p => ({\n        title: p.title,\n        price: p.price,\n        rating: p.rating,\n        reviews: p.reviews,\n        sales: p.sales_volume_display,\n        url: p.url\n      })),\n    \n    // Premium options ($50+, sorted by rating)\n    premium_options: uniqueProducts\n      .filter(p => p.price >= 50)\n      .sort((a, b) => b.rating - a.rating)\n      .slice(0, 5)\n      .map(p => ({\n        title: p.title,\n        price: p.price,\n        rating: p.rating,\n        reviews: p.reviews,\n        sales: p.sales_volume_display,\n        url: p.url\n      })),\n    \n    // Best rated (4.5+ stars, sorted by reviews)\n    best_rated: uniqueProducts\n      .filter(p => p.rating >= 4.5)\n      .sort((a, b) => b.reviews - a.reviews)\n      .slice(0, 5)\n      .map(p => ({\n        title: p.title,\n        price: p.price,\n        rating: p.rating,\n        reviews: p.reviews,\n        sales: p.sales_volume_display,\n        url: p.url\n      })),\n    \n    // Best value (highest discount with good rating)\n    best_value: uniqueProducts\n      .filter(p => p.discount > 0 && p.rating >= 4.0)\n      .sort((a, b) => b.discount - a.discount)\n      .slice(0, 5)\n      .map(p => ({\n        title: p.title,\n        price: p.price,\n        original_price: p.original_price,\n        discount: `${p.discount}%`,\n        savings: `$${p.savings}`,\n        rating: p.rating,\n        reviews: p.reviews,\n        url: p.url\n      })),\n    \n    // Most popular (by sales volume)\n    most_popular: uniqueProducts\n      .filter(p => p.sales_volume_numeric > 0)\n      .sort((a, b) => b.sales_volume_numeric - a.sales_volume_numeric)\n      .slice(0, 5)\n      .map(p => ({\n        title: p.title,\n        price: p.price,\n        rating: p.rating,\n        sales: p.sales_volume_display,\n        sales_count: p.sales_volume_numeric,\n        url: p.url\n      })),\n    \n    // Amazon's Choice products\n    amazons_choice: uniqueProducts\n      .filter(p => p.is_amazons_choice)\n      .slice(0, 3)\n      .map(p => ({\n        title: p.title,\n        price: p.price,\n        rating: p.rating,\n        reviews: p.reviews,\n        sales: p.sales_volume_display,\n        url: p.url\n      })),\n    \n    // All products (for reference)\n    all_products: uniqueProducts\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "ad548326-555c-41e0-b406-5a6cce340b84",
      "name": "Generate Recommendations",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        2224,
        480
      ],
      "parameters": {
        "text": "=You are a product recommendation expert analyzing {{ $json.query }}.\n\nAVAILABLE DATA:\n- Total Products: {{ $json.total }}\n- Price Range: ${{ $json.stats.min_price }} - ${{ $json.stats.max_price }} (avg: ${{ $json.stats.avg_price }})\n- Average Rating: {{ $json.stats.avg_rating }}/5\n- Total Reviews: {{ $json.stats.total_reviews }}\n\nCATEGORIES:\n- Top Picks: {{ $json.top_picks.length }}\n- Budget (<$30): {{ $json.budget_options.length }}\n- Premium ($50+): {{ $json.premium_options.length }}\n- Best Value: {{ $json.best_value.length }}\n- Most Popular: {{ $json.most_popular.length }}\n\nCRITICAL RULES:\n1. MAX 2500 characters\n2. Use TELEGRAM MARKDOWN (legacy):\n   - *bold* for headers only\n   - No special escaping needed\n   - Plain URLs on new lines\n3. SHORTEN product names to max 50 chars\n4. Each product MUST include its Amazon URL\n5. Be DYNAMIC - adapt language to product type\n6. VARIED symbols: 💵📊🔥💰⚡🎒📱🍎✓\n\nOUTPUT FORMAT:\n\n*🏆 TOP 3 PICKS*\n\n*1. [Short Product Name]*\n💵 $[XX] | 📊 [X.X]/5 ([XX]K reviews) | 🔥 [Sales]\n✓ [Key unique feature in 5-8 words]\n✓ [Value reason in 10-15 words]\n🔗 [Full Amazon URL]\n\n*2. [Short Product Name]*\n💵 $[XX] | 📊 [X.X]/5 ([XX]K reviews) | 🔥 [Sales]\n✓ [Key unique feature in 5-8 words]\n✓ [Value reason in 10-15 words]\n🔗 [Full Amazon URL]\n\n*3. [Short Product Name]*\n💵 $[XX] | 📊 [X.X]/5 ([XX]K reviews) | 🔥 [Sales]\n✓ [Key unique feature in 5-8 words]\n✓ [Value reason in 10-15 words]\n🔗 [Full Amazon URL]\n\n━━━━━━━━━━━━━━\n\n*💰 BEST BUDGET*\n[Product Name - shortened]\n💵 $[XX] (was $[Original]) | 📊 [X.X]/5 | 💰 Save $[XX]\nPerfect for: [User type in 8-12 words]\nTrade-off: [What's missing vs premium in 6-10 words]\n🔗 [Full Amazon URL]\n\n*💎 BEST PREMIUM*\n[Product Name - shortened]\n💵 $[XX] | 📊 [X.X]/5 ([XX] reviews)\nWorth it: [Premium features in 12-15 words]\nBest for: [Specific user scenario in 8-10 words]\n🔗 [Full Amazon URL]\n\n━━━━━━━━━━━━━━\n\n*📊 QUICK STATS*\n\n💸 Cheapest: [Name] - $[XX]\n⭐ Top Rated: [Name] - [X.X]/5\n🔥 Most Popular: [Name] - [Sales]\n💰 Best Deal: [Name] - [XX]% off\n\n━━━━━━━━━━━━━━\n\n*🎯 PICK BY NEED*\n\n⚡ [Primary feature need] → [Product]\n🔗 [URL]\n\n🎒 [Secondary feature need] → [Product]\n🔗 [URL]\n\n📱 [Third feature need] → [Product]\n🔗 [URL]\n\n💵 Best Value → [Product]\n🔗 [URL]\n\n━━━━━━━━━━━━━━\n\nDYNAMIC LANGUAGE RULES:\n- For electronics: mention wattage, ports, compatibility\n- For accessories: mention material, compatibility, durability\n- For apparel: mention fit, material, style\n- For home goods: mention size, material, features\n- Adapt \"need\" categories to product type\n- Use product-specific terminology\n\nFORMATTING CHECKLIST:\n✓ Product names shortened to 50 chars max\n✓ Every product has Amazon URL with 🔗\n✓ Use *bold* only for section headers\n✓ Plain text for everything else\n✓ URLs on separate lines after product\n✓ Real data from JSON (prices, ratings, reviews, sales)\n✓ Specific features, not generic \"high quality\"\n✓ Total output under 2500 characters\n\nEXTRACT FROM DATA:\n- Use {{ $json.top_picks[0].title }} for product names\n- Use {{ $json.top_picks[0].url }} for Amazon links\n- Use {{ $json.top_picks[0].price }} for prices\n- Use {{ $json.top_picks[0].rating }} for ratings\n- Use {{ $json.top_picks[0].reviews }} for review counts\n- Use {{ $json.top_picks[0].sales }} for sales volume\n\nStart writing now. Be concise, specific, and dynamic!",
        "batching": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "50f0c091-0af8-4256-8431-795e9cc17ce6",
      "name": "Send Final Response",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2576,
        480
      ],
      "webhookId": "2cb3ea86-e1ec-430c-9267-13a5a79eaf40",
      "parameters": {
        "text": "={{ $json.text }}",
        "chatId": "={{ $('Extract Chat & Query').item.json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "QUapgouHFfRVQShy",
          "name": "Piranusa_assistantbot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9208513e-437e-4274-8715-87cb477323cf",
      "name": "Validate Product Input",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        928,
        528
      ],
      "parameters": {
        "text": "={{ $json.message.text }}",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "=You are a product validator.  \nCheck if the user input contains a valid product name.  \n- If the input is ambiguous or includes extra descriptive words (e.g. color, speed, or adjectives), extract only the **core product name**.  \n- If there is no identifiable product, mark it as invalid.  \n\nExample:  \nInput: \"I want a blue fast charging iPhone 11 256GB\"  \nOutput:  \n{\n  \"keyword\": \"iPhone 11 256GB\",\n  \"status\": \"VALID\"\n}\n\nExample:  \nInput: \"I want something nice\"  \nOutput:  \n{\n  \"keyword\": \"\",\n  \"status\": \"INVALID\"\n}\n\nReturn the result **exactly** in this JSON structure:\n{\n  \"keyword\": \"<product_name_or_empty>\",\n  \"status\": \"VALID\" or \"INVALID\"\n}\n"
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "02a37df9-d6ff-434e-843d-933ad2bdd6c5",
      "name": "Parse Validation Output",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1072,
        688
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"keyword\": \"<product_name_or_empty>\",\n  \"status\": \"VALID or INVALID\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "116fcd7b-6fa1-44e4-bb30-05d6a45a1ce1",
      "name": "Check Product Validity",
      "type": "n8n-nodes-base.if",
      "position": [
        1280,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1753216c-c3c5-4617-a940-f5049cf2679e",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.output.status }}",
              "rightValue": "=VALID"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "16886532-c324-4c53-9341-6d3484ca2ec9",
      "name": "Send Invalid Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1536,
        672
      ],
      "webhookId": "2cb3ea86-e1ec-430c-9267-13a5a79eaf40",
      "parameters": {
        "text": "=no product detected on your input, please try again",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "QUapgouHFfRVQShy",
          "name": "Piranusa_assistantbot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4c71756c-42d1-436b-8583-2aa39e20376d",
      "name": "Send Processing Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1168,
        352
      ],
      "webhookId": "2cb3ea86-e1ec-430c-9267-13a5a79eaf40",
      "parameters": {
        "text": "=Processing your input...",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "QUapgouHFfRVQShy",
          "name": "Piranusa_assistantbot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f913fe74-be48-4a9a-8f04-0f7cf536f5c3",
      "name": "Send Valid Confirmation",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1536,
        288
      ],
      "webhookId": "2cb3ea86-e1ec-430c-9267-13a5a79eaf40",
      "parameters": {
        "text": "=Input <b>Valid</b> | keyword: <b>{{ $json.output.keyword }}</b>\n\n<i>processing your request...</i>",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "QUapgouHFfRVQShy",
          "name": "Piranusa_assistantbot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "636f1157-1f2a-44d8-b6c7-8e6cb4aea604",
      "name": "Gemini 2.5 Flash",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2368,
        640
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "id": "KMIkjDkgwnQFlQ9j",
          "name": "dev.uratalier"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "07556ea8-f88a-4720-84b2-cfbc651c9a05",
      "name": "Give Delay",
      "type": "n8n-nodes-base.wait",
      "position": [
        976,
        352
      ],
      "webhookId": "02c14419-de38-4993-bc63-8d68dc734828",
      "parameters": {
        "amount": 2
      },
      "typeVersion": 1.1
    },
    {
      "id": "fd75b467-fc1f-4389-a169-4dee1e67c218",
      "name": "Format Error Notification",
      "type": "n8n-nodes-base.code",
      "position": [
        992,
        1024
      ],
      "parameters": {
        "jsCode": "// Optimized error handler - clean & informative\nconst errorData = $input.first().json;\n\nfunction getNestedValue(obj, path, defaultValue = 'Unknown') {\n  return path.split('.').reduce((curr, prop) => \n    curr?.[prop], obj) || defaultValue;\n}\n\nconst workflowName = getNestedValue(errorData, 'workflow.name', 'Unknown Workflow');\nconst nodeName = getNestedValue(errorData, 'execution.error.node.name', 'Unknown Node');\nconst errorMessage = getNestedValue(errorData, 'execution.error.message', 'No error message');\nconst timestamp = getNestedValue(errorData, 'execution.startedAt', new Date().toISOString());\nconst executionId = getNestedValue(errorData, 'execution.id', 'Unknown');\n\n// Tangkap error details\nconst errorObj = errorData.execution?.error || {};\nconst errorDescription = errorObj.description || '';\nconst errorHttpCode = errorObj.httpCode || '';\n\n// Stack trace - ambil HANYA baris pertama yang paling penting\nconst errorStack = errorObj.stack || '';\nconst mainError = errorStack.split('\\n')[0] || '';\n\n// Format detail yang clean\nlet errorDetails = [];\nif (errorDescription && errorDescription !== errorMessage) {\n  errorDetails.push(errorDescription);\n}\nif (errorHttpCode) {\n  errorDetails.push(`HTTP ${errorHttpCode}`);\n}\n\nconst detailText = errorDetails.length > 0 \n  ? errorDetails.join(' | ') \n  : 'No additional details';\n\n// Main error dari stack (tanpa \"NodeApiError: \" prefix)\nconst cleanMainError = mainError.replace(/^(NodeApiError|Error):\\s*/, '').trim();\n\n// Build message - COMPACT VERSION\nconst message = `🚨 <b>WORKFLOW ERROR</b>\\n\\n` +\n  `📋 <b>${workflowName}</b>\\n` +\n  `⚙️ Node: <code>${nodeName}</code>\\n\\n` +\n  `❌ <b>${errorMessage}</b>\\n` +\n  `💡 ${detailText}\\n\\n` +\n  `🕐 ${new Date(timestamp).toLocaleString('id-ID', { \n    day: '2-digit', \n    month: 'short', \n    hour: '2-digit', \n    minute: '2-digit' \n  })}\\n` +\n  `🔗 Exec: <code>${executionId}</code>`;\n\nreturn {\n  json: {\n    message: message\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "2cca63a2-4999-48c6-8f7b-c8871a8a9ccb",
      "name": "Notify Admin",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1216,
        1024
      ],
      "webhookId": "13c616bf-a3ad-4035-998a-1dfe6021b89f",
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "YOUR-CHAT-ID",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "xFZJ5T4Mt6or4Fui",
          "name": "Khairunnisa Money BOT"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f6a35c5d-e285-43e2-af4a-62d890977c1c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1440,
        896
      ],
      "parameters": {
        "color": 7,
        "width": 496,
        "height": 288,
        "content": "## DECODO CREDENTIALS SETUP\n\n1. Go to: https://decodo.com\n2. Sign up / Login\n3. Navigate to: **Dashboard → Scraping APIs → Web Advanced**\n4. Click: **BASIC AUTH. TOKEN** (Automated copy)\n\n**In n8n:**\n- Go to: Credentials → Add Credential\n- Search: \"Decodo\"\n- Paste API key → Save"
      },
      "typeVersion": 1
    },
    {
      "id": "0b0e83e4-8a0b-4c8e-a49f-4038fddbb092",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        720,
        896
      ],
      "parameters": {
        "color": 3,
        "width": 688,
        "height": 288,
        "content": "## Error Handling\n\nCatches workflow failures, formats error details, sends Telegram alerts to admin."
      },
      "typeVersion": 1
    },
    {
      "id": "ee7dadc9-d140-4d2f-8473-ce3114526735",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1760,
        416
      ],
      "parameters": {
        "color": 5,
        "width": 1024,
        "height": 368,
        "content": "## Process Data"
      },
      "typeVersion": 1
    },
    {
      "id": "3d4464c7-2f04-4093-ab3d-5752cead363d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        240
      ],
      "parameters": {
        "width": 500,
        "height": 622,
        "content": "## Decodo Instant Amazon Recommender\n\n### How it works\n1. User sends a product query in Telegram; an LLM validates the input and confirms a clean search keyword.\n2. The workflow calls Decodo to scrape Amazon search results for the validated query.\n3. A processing step cleans URLs, parses sales volume, removes duplicates, computes scores, and groups products into categories (top picks, budget, premium, best value).\n4. An LLM composes a Telegram-ready recommendation (top 3 picks, budget/premium highlights, quick stats, pick-by-need) following formatting rules.\n5. The workflow returns the formatted recommendation to the user; failures generate an admin alert via Telegram.\n\n### Setup\n- [ ] Connect your Telegram bot credential to the workflow\n- [ ] Add and verify your Decodo API key in n8n credentials\n- [ ] Add LLM credentials (LangChain/Gemini/OpenAI) used for validation and message generation\n- [ ] Set the Telegram webhook so the bot forwards messages to this workflow\n- [ ] Configure an admin Telegram chat ID to receive error notifications\n- [ ] (Optional) Adjust ranking thresholds or scoring logic in the product processing code to tune recommendations\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2ea0f5fe-02a5-43a8-9bc9-3f087bd3a52f",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        256
      ],
      "parameters": {
        "color": 5,
        "width": 544,
        "height": 576,
        "content": "## Validate Input"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "cf1555ce-e5cf-4e0e-a4e4-cd1beb01b5d0",
  "connections": {
    "Decodo API": {
      "main": [
        [
          {
            "node": "Process Product Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Give Delay": {
      "main": [
        [
          {
            "node": "Send Processing Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Format Error Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini 2.5 Flash": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Recommendations",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Validate Product Input",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Validate Product Input",
            "type": "main",
            "index": 0
          },
          {
            "node": "Give Delay",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Chat & Query": {
      "main": [
        [
          {
            "node": "Decodo API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Product Data": {
      "main": [
        [
          {
            "node": "Generate Recommendations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Product Validity": {
      "main": [
        [
          {
            "node": "Extract Chat & Query",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Valid Confirmation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Invalid Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Product Input": {
      "main": [
        [
          {
            "node": "Check Product Validity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Validation Output": {
      "ai_outputParser": [
        [
          {
            "node": "Validate Product Input",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Generate Recommendations": {
      "main": [
        [
          {
            "node": "Send Final Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Error Notification": {
      "main": [
        [
          {
            "node": "Notify Admin",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

相关工作流