{
  "id": "4poK6alRCRmCQ2lo",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Comment Analyse and Reporter",
  "tags": [],
  "nodes": [
    {
      "id": "311c70f0-4471-400f-8aa1-02d5410a30a2",
      "name": "Run Every 15 Minutes",
      "type": "n8n-nodes-base.cron",
      "position": [
        -1040,
        -320
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a03df491-a798-4992-8838-39574fd04f3f",
      "name": "Search LinkedIn Posts via SerpAPI",
      "type": "n8n-nodes-serpapi.serpApi",
      "position": [
        -816,
        -320
      ],
      "parameters": {
        "q": "site:linkedin.com/posts (\"Techdome\" OR \"Employee of techdome\")",
        "requestOptions": {},
        "additionalFields": {}
      },
      "credentials": {
        "serpApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4f832192-6aab-472c-9415-6c2a8971eb9a",
      "name": "Fetch Post Comments via ConnectSafely",
      "type": "n8n-nodes-connectsafely-ai.connectSafelyLinkedIn",
      "position": [
        -368,
        -320
      ],
      "parameters": {
        "postUrl": "={{ $json.post_url }}",
        "accountId": "695ce64a09c18d6bbbe90ed0",
        "operation": "getAllPostComments"
      },
      "credentials": {
        "connectSafelyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4dcb09ba-2ccc-4207-9aa7-08d563fff0c8",
      "name": "Parse & Filter Search Results",
      "type": "n8n-nodes-base.code",
      "position": [
        -592,
        -320
      ],
      "parameters": {
        "jsCode": "// ---------------------------\n// COLLECT ALL POSSIBLE RESULTS\n// ---------------------------\nconst data = items[0].json;\n\nconst results = [\n  ...(data.organic_results || []),\n  ...(data.inline_results || []),\n  ...(data.discussions || []),\n  ...(data.forums || []),\n];\n\nconst problemKeywords = [\n  'problem',\n  'issue',\n  'manual',\n  'not working',\n  'struggling',\n  'pain',\n  'challenge',\n  'empty',\n  'ghosted',\n  'ghosting',\n  'not enough',\n  'low',\n  'hard',\n  'difficult',\n  'miss',\n  'failing'\n];\n\nconst output = results.map(post => {\n  const postUrl = post.link || '';\n  let platform = 'Unknown';\n  let username = null;\n  let postId = null;\n  let authorName = null;\n\n  // ---------------------------\n  // LINKEDIN DETECTION\n  // ---------------------------\n  if (/linkedin\\.com\\/posts\\//i.test(postUrl)) {\n    platform = 'LinkedIn';\n\n    const match = postUrl.match(/linkedin\\.com\\/posts\\/([^_]+).*?(\\d{8,})/i);\n    if (match) {\n      username = match[1];\n      postId = match[2];\n    }\n\n    authorName = post.source\n      ? post.source.replace('LinkedIn \u00b7 ', '').trim()\n      : null;\n  }\n\n  // ---------------------------\n  // REDDIT DETECTION (ROBUST)\n  // ---------------------------\n  if (/reddit\\.com/i.test(postUrl)) {\n    platform = 'Reddit';\n\n    const redditMatch = postUrl.match(\n      /reddit\\.com\\/r\\/([^/]+)\\/comments\\/([^/]+)/i\n    );\n\n    if (redditMatch) {\n      username = `r/${redditMatch[1]}`;\n      postId = redditMatch[2];\n    }\n\n    authorName = post.source\n      ? post.source.replace('Reddit \u00b7 ', '').trim()\n      : username;\n  }\n\n  // ---------------------------\n  // PROBLEM DETECTION\n  // ---------------------------\n  const text = `${post.title || ''} ${post.snippet || ''}`.toLowerCase();\n\n  const detectedProblems = problemKeywords.filter(k =>\n    text.includes(k)\n  );\n\n  return {\n    json: {\n      platform,\n      author_name: authorName,\n      username,\n      post_id: postId,\n      post_url: postUrl || null,\n      post_title: post.title || null,\n      post_snippet: post.snippet || null,\n      engagement_hint: post.displayed_link || null,\n      problem_keywords_detected: detectedProblems,\n      is_problem_post: detectedProblems.length > 0\n    }\n  };\n});\n\n// REMOVE EMPTY / UNKNOWN URL RESULTS\nreturn output.filter(item => item.json.post_url);\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ba32dca4-ff48-4310-8a8a-86003adaf7af",
      "name": "AI Intent Detection Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        80,
        -320
      ],
      "parameters": {
        "text": "=Analyze the following LinkedIn comment and identify lead intent.\n\nPost URL:\n{{ $json.postUrl }}\n\nPost Content:\n{{ $json.postContent }}\n\nCommenter Name:\n{{ $json.commenterName }}\n\nComment Text:\n{{ $json.commentText }}\n\nReturn the result in the following JSON format ONLY:\n\n{\n  \"postUrl\": \"{{ $json.postUrl }}\",\n  \"leadName\": \"{{ $json.commenterName }}\",\n  \"comment\": \"{{ $json.commentText }}\",\n  \"intentScore\": number,\n  \"intentLabel\": \"no-intent | passive-interest | problem-aware | solution-aware | high-intent\"\n}\n",
        "options": {
          "systemMessage": "=You are a B2B sales intent detection agent.\n\nYour job is to analyze LinkedIn post context and comment text to identify whether the commenter shows buying intent, problem awareness, or decision-maker interest.\n\nYou must:\n- Use ONLY the data provided in the input\n- Infer intent from wording, tone, and relevance to the post\n- Be conservative: do not hallucinate intent\n- Prefer precision over recall\n\nScoring rules:\n- 0\u201320 \u2192 No intent (generic praise, agreement, emojis)\n- 21\u201340 \u2192 Passive interest (thoughtful comment, but no problem ownership)\n- 41\u201360 \u2192 Problem-aware (mentions pain points, challenges, or agreement with problem)\n- 61\u201380 \u2192 Solution-aware (implies need for solution, improvement, or optimization)\n- 81\u2013100 \u2192 High buying intent (clear ownership, decision role, desire to act)\n\nIf intent is below 30, still return the record but mark it as \"low-intent\".\n\nOutput MUST be valid JSON and follow the exact schema provided.\nDo not add explanations or extra text.\n"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "32e43c34-820d-419f-9197-7daa3f985722",
      "name": "Flatten Comments into Rows",
      "type": "n8n-nodes-base.code",
      "position": [
        -144,
        -320
      ],
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of items) {\n  const post = item.json;\n\n  const postUrl = post.postUrl || '';\n  const postContent = post.postDetails?.content || '';\n  const activityUrn = post.postDetails?.activityUrn || null;\n\n  const comments = post.comments || [];\n\n  for (const comment of comments) {\n    results.push({\n      json: {\n        // Post info\n        postUrl,\n        activityUrn,\n        postContent,\n\n        // Comment info\n        commentId: comment.commentId,\n        commentText: comment.commentText,\n        commenterName: comment.authorName,\n        commenterProfileUrl: comment.profileUrl || null,\n        commenterUrn: comment.commenterUrn || null,\n\n        // Metadata\n        createdAt: comment.createdAt,\n        likeCount: comment.likeCount,\n        replyCount: comment.replyCount,\n        isProfile: comment.hasProfile\n      }\n    });\n  }\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "356016ed-0627-4f62-bd71-8e0a329d7b38",
      "name": "Parse AI Intent Output",
      "type": "n8n-nodes-base.code",
      "position": [
        432,
        -320
      ],
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of items) {\n  // Skip empty or invalid outputs safely\n  if (!item.json.output) continue;\n\n  let parsed;\n  try {\n    parsed = JSON.parse(item.json.output);\n  } catch (e) {\n    // If parsing fails, skip this record\n    continue;\n  }\n\n  results.push({\n    json: {\n      postUrl: parsed.postUrl || null,\n      leadName: parsed.leadName || null,\n      comment: parsed.comment || null,\n      intentScore: parsed.intentScore ?? null,\n      intentLabel: parsed.intentLabel || null\n    }\n  });\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7f0af878-ec31-4116-b5e6-eb97c45ea8a9",
      "name": "Save Leads to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        656,
        -320
      ],
      "parameters": {
        "columns": {
          "value": {
            "comment": "={{ $json.comment }}",
            "post_url": "={{ $json.postUrl }}",
            "lead name": "={{ $json.leadName }}",
            "intent label": "={{ $json.intentLabel }}",
            "intent score": "={{ $json.intentScore }}"
          },
          "schema": [
            {
              "id": "post_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "post_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lead name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "comment",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "comment",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "intent score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "intent score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "intent label",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "intent label",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "75ef2c71-7b3c-475d-9337-c7d061301fbb",
      "name": "Azure OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        160,
        -96
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2df8d882-61e5-42f3-95f7-dfd248c19d5d",
      "name": "Google Sheets Trigger",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -1040,
        112
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "df24c01b-5a8b-47de-b76d-44ec84289b04",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -816,
        16
      ],
      "parameters": {
        "text": "Draft a suitable reply for the commentes for proper sentiments",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "bdebac9a-51a7-46a4-a39d-d8af16e2bce7",
      "name": "Azure OpenAI Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        -752,
        240
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7cdc0bec-cb28-4240-b948-11c55e1f20b2",
      "name": "Code in JavaScript2",
      "type": "n8n-nodes-base.code",
      "position": [
        -464,
        112
      ],
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n  item.json.myNewField = 1;\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "31d68c55-0e83-4100-9665-5f8356bc4656",
      "name": "Append or update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -240,
        112
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "91de8c1f-3442-4edc-951d-e0907da31fa0",
      "name": "Google Sheets Trigger1",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -1040,
        448
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "baf4b709-326d-4a29-a172-cdccfeee6ece",
      "name": "Code in JavaScript3",
      "type": "n8n-nodes-base.code",
      "position": [
        -816,
        448
      ],
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n  item.json.myNewField = 1;\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "0b752ec9-cf45-4406-9cb2-41419f925a8e",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -592,
        448
      ],
      "parameters": {
        "sendTo": "info@example.com",
        "message": "<HTML File>",
        "options": {},
        "subject": "<Your Subject>"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "94cf4921-5e78-4f51-bf0c-5dc9aac3448d",
      "name": "Trigger Every 15 Minutes",
      "type": "n8n-nodes-base.cron",
      "position": [
        -992,
        1024
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a4caaaa9-92f6-4b3e-93d6-912cc95ea5a4",
      "name": "AI Sentiment & Intent Detection Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        128,
        1024
      ],
      "parameters": {
        "text": "=Analyze the following LinkedIn comment and identify lead intent.\n\nPost URL:\n{{ $json.postUrl }}\n\nPost Content:\n{{ $json.postContent }}\n\nCommenter Name:\n{{ $json.commenterName }}\n\nComment Text:\n{{ $json.commentText }}\n\nReturn the result in the following JSON format ONLY:\n\n{\n  \"postUrl\": \"{{ $json.postUrl }}\",\n  \"leadName\": \"{{ $json.commenterName }}\",\n  \"comment\": \"{{ $json.commentText }}\",\n  \"intentScore\": number,\n  \"intentLabel\": \"no-intent | passive-interest | problem-aware | solution-aware | high-intent\"\n}\n",
        "options": {
          "systemMessage": "=You are a B2B sales intent detection agent.\n\nYour job is to analyze LinkedIn post context and comment text to identify whether the commenter shows buying intent, problem awareness, or decision-maker interest.\n\nYou must:\n- Use ONLY the data provided in the input\n- Infer intent from wording, tone, and relevance to the post\n- Be conservative: do not hallucinate intent\n- Prefer precision over recall\n\nScoring rules:\n- 0\u201320 \u2192 No intent (generic praise, agreement, emojis)\n- 21\u201340 \u2192 Passive interest (thoughtful comment, but no problem ownership)\n- 41\u201360 \u2192 Problem-aware (mentions pain points, challenges, or agreement with problem)\n- 61\u201380 \u2192 Solution-aware (implies need for solution, improvement, or optimization)\n- 81\u2013100 \u2192 High buying intent (clear ownership, decision role, desire to act)\n\nIf intent is below 30, still return the record but mark it as \"low-intent\".\n\nOutput MUST be valid JSON and follow the exact schema provided.\nDo not add explanations or extra text.\n"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "987fd12f-c41d-482b-b84e-b0cf834af7fe",
      "name": "Parse Sentiment & Score Output",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        1024
      ],
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of items) {\n  // Skip empty or invalid outputs safely\n  if (!item.json.output) continue;\n\n  let parsed;\n  try {\n    parsed = JSON.parse(item.json.output);\n  } catch (e) {\n    // If parsing fails, skip this record\n    continue;\n  }\n\n  results.push({\n    json: {\n      postUrl: parsed.postUrl || null,\n      leadName: parsed.leadName || null,\n      comment: parsed.comment || null,\n      intentScore: parsed.intentScore ?? null,\n      intentLabel: parsed.intentLabel || null\n    }\n  });\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "8e5d8fe6-e2a8-4861-ae28-9ac1fddb8ea9",
      "name": "Save Comments & Sentiment to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        704,
        1024
      ],
      "parameters": {
        "columns": {
          "value": {
            "comment": "={{ $json.comment }}",
            "post_url": "={{ $json.postUrl }}",
            "lead name": "={{ $json.leadName }}",
            "intent label": "={{ $json.intentLabel }}",
            "intent score": "={{ $json.intentScore }}"
          },
          "schema": [
            {
              "id": "post_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "post_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lead name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "comment",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "comment",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "intent score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "intent score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "intent label",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "intent label",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "fa815af1-60b8-4761-bfb6-aa6f8f23b413",
      "name": "Watch Sheet for New Comment Rows",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -928,
        1600
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "265c661c-bda8-4632-abe7-6536ceafc25a",
      "name": "AI Reply Drafting Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -704,
        1488
      ],
      "parameters": {
        "text": "Draft a suitable reply for the commentes for proper sentiments",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "6c0598e9-0259-4e0a-8d75-3a6e895c741c",
      "name": "Format Reply for Sheet Update",
      "type": "n8n-nodes-base.code",
      "position": [
        -352,
        1600
      ],
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n  item.json.myNewField = 1;\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "5f1bd6ef-2474-4b7a-8b0d-9a7cbb6b4292",
      "name": "Write AI Reply Back to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -128,
        1600
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4486ac1e-5322-46d0-ad9f-31c1500a8f46",
      "name": "Watch Sheet for Approved Replies",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -960,
        2016
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1996970241,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit#gid=1996970241",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hXDNE90IFbXLgyLt2XwSHgI_9sASzTSM9LAA4cUDFj4/edit?usp=drivesdk",
          "cachedResultName": "Post Scraping"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ee63d50c-9c00-4231-a302-a7d0d0b1fd4b",
      "name": "Build Media Team Email Report",
      "type": "n8n-nodes-base.code",
      "position": [
        -736,
        2016
      ],
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n  item.json.myNewField = 1;\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "c83ea93c-6d0d-43a9-b029-68335b83f135",
      "name": "Send Email Report to Media Team",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -512,
        2016
      ],
      "parameters": {
        "sendTo": "info@example.com",
        "message": "<HTML File>",
        "options": {},
        "subject": "<Your Subject>"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "660a6302-4e05-41fe-89c6-6cbb9fbedc3c",
      "name": "\ud83d\udccb Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2048,
        1072
      ],
      "parameters": {
        "color": 3,
        "width": 520,
        "height": 734,
        "content": "## \ud83d\udccb Workflow Overview \u2014 Comment Analyse & Reporter\n\n### How it works\nThis workflow automates the full lifecycle of LinkedIn comment monitoring and response for Techdome. It runs across **three sequential stages**:\n\n**Stage 1 \u2014 Comment Scraping & Sentiment Scoring:** A cron trigger fires every 15 minutes. SerpAPI searches LinkedIn for posts mentioning Techdome. Results are parsed and filtered, then ConnectSafely fetches the actual comments for each post. An Azure OpenAI-powered AI agent analyzes each comment for sentiment and assigns an intent score and label. The structured output is saved to a Google Sheet (Sheet2) with columns for post URL, commenter name, comment text, sentiment score, and intent label.\n\n**Stage 2 \u2014 AI Reply Generation:** A Google Sheets trigger watches for new rows. For each comment, an AI Reply Drafting Agent generates a contextually appropriate reply based on the sentiment and comment content. The drafted reply is written back to the same Google Sheet row.\n\n**Stage 3 \u2014 Media Team Email Report:** A second Google Sheets trigger detects updated rows with replies. A code node compiles a structured HTML email report and sends it via Gmail to the media team so they can review and post the replies on LinkedIn.\n\n### Setup steps\n1. Add credentials: **SerpAPI**, **ConnectSafely**, **Azure OpenAI**, **Google Sheets**, and **Gmail**\n2. Set the target Google Sheet ID in all Sheets nodes\n3. Update the Gmail recipient address in the Send Email node\n4. Activate all three trigger nodes"
      },
      "typeVersion": 1
    },
    {
      "id": "2437fbdc-b371-4fc4-8e41-a1a8480d4e5d",
      "name": "Section: Comment Scraping",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1280,
        800
      ],
      "parameters": {
        "width": 1900,
        "content": "## \ud83d\udd0d Stage 1 \u2014 Comment Scraping & Sentiment Scoring\nSerpAPI searches LinkedIn for Techdome-related posts every 15 minutes. Comments are fetched, flattened, and scored by an AI agent for sentiment and buyer intent. Results are saved to Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "3cdd5de0-9ded-494a-b0f8-55859f7a4c9c",
      "name": "Section: Reply Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1280,
        1392
      ],
      "parameters": {
        "width": 1000,
        "content": "## \ud83d\udcac Stage 2 \u2014 AI Reply Generation\nWatches Google Sheets for new comment rows. The AI Reply Drafting Agent generates a tone-matched reply per comment using sentiment context, then writes the reply back to the sheet."
      },
      "typeVersion": 1
    },
    {
      "id": "a5b1d617-e8bb-4b46-b005-6c4c7414c675",
      "name": "Section: Email Report",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1392,
        1808
      ],
      "parameters": {
        "width": 680,
        "content": "## \ud83d\udce7 Stage 3 \u2014 Media Team Email Report\nWatches the sheet for rows with completed replies. Compiles a formatted HTML email report and delivers it to the media team via Gmail so they can act on the replies."
      },
      "typeVersion": 1
    },
    {
      "id": "c41c87f1-a2c1-43de-8704-d202100e2fa0",
      "name": "Warning: SerpAPI",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -704,
        1216
      ],
      "parameters": {
        "color": 2,
        "width": 260,
        "height": 140,
        "content": "\u26a0\ufe0f **SerpAPI Credentials Required**\nThis node requires a valid SerpAPI key. Without it, no LinkedIn posts will be found and the workflow will fail silently. Watch for rate limit errors on high-volume runs."
      },
      "typeVersion": 1
    },
    {
      "id": "4538dc4b-d7c0-42ba-9d2a-18ff15c5d306",
      "name": "Warning: Gmail",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        2000
      ],
      "parameters": {
        "color": 2,
        "width": 280,
        "height": 140,
        "content": "\u26a0\ufe0f **Gmail Credentials & Recipient Required**\nSet the correct recipient email for the media team. Misconfigured Gmail OAuth will silently fail \u2014 ensure the Gmail account has Send permission enabled."
      },
      "typeVersion": 1
    },
    {
      "id": "eb0adcb0-ded9-475d-a8c3-d537ea663e74",
      "name": "Search LinkedIn Posts via SerpAPI1",
      "type": "n8n-nodes-serpapi.serpApi",
      "position": [
        -768,
        1024
      ],
      "parameters": {
        "q": "site:linkedin.com/posts (\"Techdome\" OR \"Employee of techdome\")",
        "requestOptions": {},
        "additionalFields": {}
      },
      "credentials": {
        "serpApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d2e234ea-7582-4644-9ddd-3899b6a8a528",
      "name": "Fetch Post Comments via ConnectSafely1",
      "type": "n8n-nodes-connectsafely-ai.connectSafelyLinkedIn",
      "position": [
        -320,
        1024
      ],
      "parameters": {
        "postUrl": "={{ $json.post_url }}",
        "accountId": "695ce64a09c18d6bbbe90ed0",
        "operation": "getAllPostComments"
      },
      "credentials": {
        "connectSafelyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dee58d4c-1220-4690-8e80-625fa2cbd30e",
      "name": "Parse & Filter Search Results1",
      "type": "n8n-nodes-base.code",
      "position": [
        -544,
        1024
      ],
      "parameters": {
        "jsCode": "// ---------------------------\n// COLLECT ALL POSSIBLE RESULTS\n// ---------------------------\nconst data = items[0].json;\n\nconst results = [\n  ...(data.organic_results || []),\n  ...(data.inline_results || []),\n  ...(data.discussions || []),\n  ...(data.forums || []),\n];\n\nconst problemKeywords = [\n  'problem',\n  'issue',\n  'manual',\n  'not working',\n  'struggling',\n  'pain',\n  'challenge',\n  'empty',\n  'ghosted',\n  'ghosting',\n  'not enough',\n  'low',\n  'hard',\n  'difficult',\n  'miss',\n  'failing'\n];\n\nconst output = results.map(post => {\n  const postUrl = post.link || '';\n  let platform = 'Unknown';\n  let username = null;\n  let postId = null;\n  let authorName = null;\n\n  // ---------------------------\n  // LINKEDIN DETECTION\n  // ---------------------------\n  if (/linkedin\\.com\\/posts\\//i.test(postUrl)) {\n    platform = 'LinkedIn';\n\n    const match = postUrl.match(/linkedin\\.com\\/posts\\/([^_]+).*?(\\d{8,})/i);\n    if (match) {\n      username = match[1];\n      postId = match[2];\n    }\n\n    authorName = post.source\n      ? post.source.replace('LinkedIn \u00b7 ', '').trim()\n      : null;\n  }\n\n  // ---------------------------\n  // REDDIT DETECTION (ROBUST)\n  // ---------------------------\n  if (/reddit\\.com/i.test(postUrl)) {\n    platform = 'Reddit';\n\n    const redditMatch = postUrl.match(\n      /reddit\\.com\\/r\\/([^/]+)\\/comments\\/([^/]+)/i\n    );\n\n    if (redditMatch) {\n      username = `r/${redditMatch[1]}`;\n      postId = redditMatch[2];\n    }\n\n    authorName = post.source\n      ? post.source.replace('Reddit \u00b7 ', '').trim()\n      : username;\n  }\n\n  // ---------------------------\n  // PROBLEM DETECTION\n  // ---------------------------\n  const text = `${post.title || ''} ${post.snippet || ''}`.toLowerCase();\n\n  const detectedProblems = problemKeywords.filter(k =>\n    text.includes(k)\n  );\n\n  return {\n    json: {\n      platform,\n      author_name: authorName,\n      username,\n      post_id: postId,\n      post_url: postUrl || null,\n      post_title: post.title || null,\n      post_snippet: post.snippet || null,\n      engagement_hint: post.displayed_link || null,\n      problem_keywords_detected: detectedProblems,\n      is_problem_post: detectedProblems.length > 0\n    }\n  };\n});\n\n// REMOVE EMPTY / UNKNOWN URL RESULTS\nreturn output.filter(item => item.json.post_url);\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d1ca6bba-4cd5-4898-804a-a3c00ff81c48",
      "name": "Flatten Comments into Rows1",
      "type": "n8n-nodes-base.code",
      "position": [
        -96,
        1024
      ],
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of items) {\n  const post = item.json;\n\n  const postUrl = post.postUrl || '';\n  const postContent = post.postDetails?.content || '';\n  const activityUrn = post.postDetails?.activityUrn || null;\n\n  const comments = post.comments || [];\n\n  for (const comment of comments) {\n    results.push({\n      json: {\n        // Post info\n        postUrl,\n        activityUrn,\n        postContent,\n\n        // Comment info\n        commentId: comment.commentId,\n        commentText: comment.commentText,\n        commenterName: comment.authorName,\n        commenterProfileUrl: comment.profileUrl || null,\n        commenterUrn: comment.commenterUrn || null,\n\n        // Metadata\n        createdAt: comment.createdAt,\n        likeCount: comment.likeCount,\n        replyCount: comment.replyCount,\n        isProfile: comment.hasProfile\n      }\n    });\n  }\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "8d8385ec-d04f-4391-b715-180f64db8d18",
      "name": "Azure OpenAI Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        208,
        1248
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "671d467f-7943-43a9-be71-260bfdd14aff",
      "name": "Azure OpenAI Chat Model3",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        -624,
        1712
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "02436b7c-4c02-4eb3-965e-f5819b4b3b5d",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Code in JavaScript2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript2": {
      "main": [
        [
          {
            "node": "Append or update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript3": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Search LinkedIn Posts via SerpAPI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets Trigger": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets Trigger1": {
      "main": [
        [
          {
            "node": "Code in JavaScript3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Intent Output": {
      "main": [
        [
          {
            "node": "Save Leads to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Reply Drafting Agent": {
      "main": [
        [
          {
            "node": "Format Reply for Sheet Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Azure OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Intent Detection Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Azure OpenAI Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Azure OpenAI Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "AI Sentiment & Intent Detection Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Azure OpenAI Chat Model3": {
      "ai_languageModel": [
        [
          {
            "node": "AI Reply Drafting Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Trigger Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Search LinkedIn Posts via SerpAPI1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Intent Detection Agent": {
      "main": [
        [
          {
            "node": "Parse AI Intent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flatten Comments into Rows": {
      "main": [
        [
          {
            "node": "AI Intent Detection Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flatten Comments into Rows1": {
      "main": [
        [
          {
            "node": "AI Sentiment & Intent Detection Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Media Team Email Report": {
      "main": [
        [
          {
            "node": "Send Email Report to Media Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Reply for Sheet Update": {
      "main": [
        [
          {
            "node": "Write AI Reply Back to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse & Filter Search Results": {
      "main": [
        [
          {
            "node": "Fetch Post Comments via ConnectSafely",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse & Filter Search Results1": {
      "main": [
        [
          {
            "node": "Fetch Post Comments via ConnectSafely1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Sentiment & Score Output": {
      "main": [
        [
          {
            "node": "Save Comments & Sentiment to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Watch Sheet for Approved Replies": {
      "main": [
        [
          {
            "node": "Build Media Team Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Watch Sheet for New Comment Rows": {
      "main": [
        [
          {
            "node": "AI Reply Drafting Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search LinkedIn Posts via SerpAPI": {
      "main": [
        [
          {
            "node": "Parse & Filter Search Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search LinkedIn Posts via SerpAPI1": {
      "main": [
        [
          {
            "node": "Parse & Filter Search Results1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Sentiment & Intent Detection Agent": {
      "main": [
        [
          {
            "node": "Parse Sentiment & Score Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Post Comments via ConnectSafely": {
      "main": [
        [
          {
            "node": "Flatten Comments into Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Post Comments via ConnectSafely1": {
      "main": [
        [
          {
            "node": "Flatten Comments into Rows1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}