{
  "id": "4qrbmY03PKVPsYsO",
  "meta": {
    "instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177"
  },
  "name": "AI-Powered Cross-Platform Audience Sentiment & Trend Analyzer",
  "tags": [],
  "nodes": [
    {
      "id": "d5c1e015-e268-4211-899a-61d939f4047e",
      "name": "When clicking ‘Execute workflow’",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -928,
        224
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a21066f6-08dc-4e9a-aa40-d5baf0896ba7",
      "name": "Configure GPT-4o Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        752,
        432
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "id": "credential-id",
          "name": "azureOpenAiApi Credential"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0dc05bdd-36ef-4856-bdae-63d1b24d2215",
      "name": "Configure GPT-4o Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        1408,
        416
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "id": "credential-id",
          "name": "azureOpenAiApi Credential"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7d0c89e9-8186-4be4-b11b-77a422f86390",
      "name": "Fetch Recent Mentions (X API)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -544,
        80
      ],
      "parameters": {
        "url": "https://api.x.YOUR_AWS_SECRET_KEY_HERE?tweet.fields=created_at,text,public_metrics,author_id",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "twitterOAuth2Api"
      },
      "credentials": {
        "twitterOAuth2Api": {
          "id": "credential-id",
          "name": "twitterOAuth2Api Credential"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b8219630-8153-4edc-a5f8-3c60e434431d",
      "name": " Fetch Recent Facebook Comments ",
      "type": "n8n-nodes-base.facebookGraphApi",
      "position": [
        -544,
        368
      ],
      "parameters": {
        "edge": "posts",
        "node": "230476031170639",
        "options": {
          "fields": {
            "field": [
              {
                "name": "id,message,story,created_time,permalink_url,full_picture,attachments{media_type,media,url,description},shares,likes.limit(100).summary(true){id,name},comments.limit(100).summary(true){id,from,message,created_time,like_count,comment_count,reactions.summary(true)},reactions.limit(100).summary(true){type,id,name},from{id,name,category}"
              }
            ]
          }
        },
        "graphApiVersion": "v23.0"
      },
      "credentials": {
        "facebookGraphApi": {
          "id": "credential-id",
          "name": "facebookGraphApi Credential"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ab16e066-9ae2-4fc6-a53c-c04d119ba763",
      "name": "Merge Platform Datasets",
      "type": "n8n-nodes-base.merge",
      "position": [
        -176,
        208
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "1eaeed88-5862-4a4c-900b-1f53ed3346d0",
      "name": "Consolidate Mentions & Comments ",
      "type": "n8n-nodes-base.code",
      "position": [
        496,
        192
      ],
      "parameters": {
        "jsCode": "// Input: Combined JSON from X and Facebook (like in your example)\nconst allData = $input.all();\n\n// Helper: normalize date safely\nfunction normalizeDate(dateString) {\n  if (!dateString) return null;\n  try {\n    return new Date(dateString).toISOString();\n  } catch {\n    return dateString;\n  }\n}\n\nconst unified = [];\n\n// Iterate over each input item\nfor (const item of allData) {\n  const dataArray = item.json.data || [];\n\n  // --- Case 1: X (Twitter) Mentions ---\n  if (dataArray[0]?.text && dataArray[0]?.public_metrics) {\n    for (const tweet of dataArray) {\n      unified.push({\n        platform: \"Twitter\",\n        post_id: tweet.id,\n        author_id: tweet.author_id,\n        message: tweet.text,\n        created_at: normalizeDate(tweet.created_at),\n        likes: tweet.public_metrics.like_count,\n        replies: tweet.public_metrics.reply_count,\n        impressions: tweet.public_metrics.impression_count,\n        retweets: tweet.public_metrics.retweet_count,\n        link: `https://x.com/i/web/status/${tweet.id}`,\n        sentiment_status: \"\", // can be filled later by AI\n      });\n    }\n  }\n\n  // --- Case 2: Facebook Posts + Comments ---\n  if (dataArray[0]?.message && dataArray[0]?.comments) {\n    for (const post of dataArray) {\n      const comments = post.comments?.data || [];\n      for (const c of comments) {\n        unified.push({\n          platform: \"Facebook\",\n          post_id: post.id,\n          comment_id: c.id,\n          commenter: c.from?.name || \"Unknown\",\n          message: c.message,\n          created_at: normalizeDate(c.created_time),\n          likes: c.like_count || 0,\n          reactions: c.reactions?.summary?.total_count || 0,\n          post_link: post.permalink_url,\n          sentiment_status: \"\", // for AI analysis later\n        });\n      }\n    }\n  }\n}\n\n// --- Output ---\nreturn [\n  {\n    json: {\n      total_items: unified.length,\n      unified_comments: unified,\n      summary: {\n        twitter_mentions: unified.filter(i => i.platform === \"Twitter\").length,\n        facebook_comments: unified.filter(i => i.platform === \"Facebook\").length,\n      },\n    },\n  },\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a7ef2879-6f9c-4dd3-9de8-336b8f966724",
      "name": "Perform Sentiment & Keyword Analysis ",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        752,
        192
      ],
      "parameters": {
        "text": "=Analyze the following social media dataset for sentiment and keyword trends.\n\nInput Data:\n{{ JSON.stringify($json.unified_comments) }}\n\nReturn structured JSON in this format:\n{\n  \"overall_summary\": {\n    \"positive\": 0,\n    \"negative\": 0,\n    \"neutral\": 0,\n    \"dominant_trends\": [\"example\", \"keywords\"]\n  },\n  \"detailed_analysis\": [\n    {\n      \"platform\": \"\",\n      \"message\": \"\",\n      \"sentiment\": \"\",\n      \"keywords\": [],\n      \"reasoning\": \"\"\n    }\n  ]\n}\n",
        "options": {
          "systemMessage": "=You are a social media analytics assistant.\n\nYour job is to analyze combined social media mentions, comments, and posts across platforms such as Twitter (X) and Facebook.\n\nGiven an input dataset of social posts and comments, you must:\n1. Perform **sentiment analysis** on each item (positive / negative / neutral).\n2. Extract **key topics, themes, or keywords** mentioned.\n3. Identify **top recurring trends** across all items.\n4. Calculate **overall sentiment distribution** (e.g., 60% positive, 20% negative, 20% neutral).\n5. Return the output as clean, structured JSON.\n\nEnsure your analysis is concise, data-driven, and easy to visualize later.\nAvoid repeating text or adding unnecessary narrative.\n"
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "e146a4e0-ef7a-4ec0-9da5-1e28b2ab67e9",
      "name": " Parse AI Output to Structured JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        192
      ],
      "parameters": {
        "jsCode": "// Get raw AI output (string)\nconst raw = $json.output;\n\n// Remove markdown wrappers (```json ... ```)\nconst cleaned = raw.replace(/```json|```/g, '').trim();\n\nlet parsed;\ntry {\n  parsed = JSON.parse(cleaned);\n} catch (error) {\n  parsed = { error: \"Failed to parse JSON\", raw: cleaned };\n}\n\n// Extract the main sections for clarity\nconst overall = parsed.overall_summary || {};\nconst details = parsed.detailed_analysis || [];\n\n// Return structured data\nreturn [\n  {\n    json: {\n      overall_summary: overall,\n      detailed_analysis: details,\n      total_posts: details.length\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e3223d09-b24a-4061-a53c-7b021b63f197",
      "name": " Generate Audience Sentiment Summary",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1408,
        192
      ],
      "parameters": {
        "text": "=You are a professional marketing insights writer who transforms structured analytical data into clean, visually engaging reports. \n\nYour task is to summarize social media sentiment data and engagement trends in a polished and human-readable tone.  \n\nFocus on:\n1. Highlighting positive, negative, and neutral sentiment proportions with short insights.\n2. Extracting top recurring topics or keywords and summarizing what they indicate about audience mood.\n3. Giving 3–5 bullet-point insights about the overall tone of discussion (what people liked, disliked, and discussed the most).\n4. Mentioning key examples from the dataset when useful (e.g., “Users praised the movie excitement and visuals.”)\n5. Keeping formatting clean with short paragraphs, emojis, and clear section titles (use Markdown headings like **Overall Summary**, **Top Trends**, **Insights**, etc.)\n\nReturn your response in **HTML format**, suitable for an email body, with light styling (headers, bold text, lists, and emojis).\nAvoid adding JSON or code blocks — just return clean HTML.\n",
        "options": {
          "systemMessage": "=You are a professional marketing insights writer who transforms structured analytical data into clean, visually engaging reports. \n\nYour task is to summarize social media sentiment data and engagement trends in a polished and human-readable tone.  \n\nFocus on:\n1. Highlighting positive, negative, and neutral sentiment proportions with short insights.\n2. Extracting top recurring topics or keywords and summarizing what they indicate about audience mood.\n3. Giving 3–5 bullet-point insights about the overall tone of discussion (what people liked, disliked, and discussed the most).\n4. Mentioning key examples from the dataset when useful (e.g., “Users praised the movie excitement and visuals.”)\n5. Keeping formatting clean with short paragraphs, emojis, and clear section titles (use Markdown headings like **Overall Summary**, **Top Trends**, **Insights**, etc.)\n\nReturn your response in **HTML format**, suitable for an email body, with light styling (headers, bold text, lists, and emojis).\nAvoid adding JSON or code blocks — just return clean HTML.\n"
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "53905e4c-1b06-4e80-a767-ae5d82b858fb",
      "name": "Send Email Summary to Marketing Team",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1840,
        192
      ],
      "webhookId": "0c82c299-6938-42ed-bda6-5007d79af34f",
      "parameters": {
        "sendTo": "=newscctv22@gmail.com",
        "message": "={{ $json.output }}",
        "options": {},
        "subject": "=Audience Sentiment and Keyword Trend Monitoring"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "credential-id",
          "name": "gmailOAuth2 Credential"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "58e719fc-aff0-42c7-8bd0-547997c55bfa",
      "name": "Log Errors in Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        496,
        880
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "error_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "error_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "error",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "error",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "error_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1338537721,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit#gid=1338537721",
          "cachedResultName": "error log sheet"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit?usp=drivesdk",
          "cachedResultName": "Interviewer Brief Pack "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "googleSheetsOAuth2Api Credential"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "3433cb19-924a-40c1-881f-59ea3ebfd528",
      "name": "Validate API Response Integrity",
      "type": "n8n-nodes-base.if",
      "position": [
        112,
        208
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "e2adb005-2b3c-4d1e-8445-442df1fe925a",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.data.id }}",
              "rightValue": ""
            },
            {
              "id": "7dba8105-5f0f-43a9-8483-c1776f1a275f",
              "operator": {
                "type": "number",
                "operation": "equals"
              },
              "leftValue": "={{ 1 }}",
              "rightValue": 1
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a72debb0-2014-4c53-84b2-cb7dee6a3db2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1536,
        -448
      ],
      "parameters": {
        "width": 624,
        "height": 480,
        "content": "## Cross-Platform Audience Sentiment Analyzer 🎯\nThis workflow unifies data from Twitter (X) and Facebook, performs AI-driven sentiment and keyword analysis, and delivers structured marketing summaries by email.  \nIt provides real-time insight into audience tone, trending topics, and engagement behavior across both platforms.\n\n### Key Steps:\n1️⃣ Fetch mentions from Twitter and comments from Facebook.  \n2️⃣ Merge both datasets into a unified comment structure.  \n3️⃣ Run sentiment and keyword extraction using GPT-4o.  \n4️⃣ Summarize audience mood and top trends in HTML format.  \n5️⃣ Email the final report to the marketing team automatically.  \n6️⃣ Log API or runtime errors into Google Sheets for monitoring.\n\n### Setup Checklist:\n- Connect credentials: Twitter, Facebook Graph, Azure OpenAI, Gmail, Google Sheets.  \n- Verify Gmail and Google Sheet IDs.  \n- Adjust API endpoints to your preferred handles or pages.  \n- Optionally schedule a weekly run for automated social insights.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6178296d-3b96-4c7d-8621-0c8a4531bb87",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 800,
        "content": "## Data Collection Layer\nFetches recent mentions from Twitter (X) and recent post comments from Facebook.  \nProvides raw input data for sentiment and trend analysis across both platforms.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "99a214c5-5d48-4aa1-958f-7e4e28fb90d9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -96
      ],
      "parameters": {
        "color": 7,
        "height": 672,
        "content": "## Dataset Merging\nCombines results from both APIs into a unified dataset.  \nEnsures consistent structure for message text, timestamps, reactions, and likes.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9c70b6a7-973a-4b59-a9b4-5717c3e58c91",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        -80
      ],
      "parameters": {
        "color": 7,
        "height": 560,
        "content": "## Data Validation\nChecks API responses to confirm essential fields exist before analysis.  \nInvalid or empty responses are sent to Google Sheets for error tracking.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "43bb63ef-a9ee-4511-8b92-69626ce1a50b",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        416,
        -32
      ],
      "parameters": {
        "color": 7,
        "height": 512,
        "content": "## Data Consolidation\nStandardizes and merges Facebook comments and Twitter mentions into a clean JSON array.  \nAdds key attributes like platform, author, sentiment placeholder, and post URLs.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1d86c84c-e101-4eb8-b4ca-f1fde1a461e9",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        704,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 608,
        "content": "## AI Sentiment & Keyword Analysis\nUses GPT-4o to classify each post/comment as Positive, Negative, or Neutral.  \nExtracts recurring themes and dominant keywords to detect engagement trends.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "11a4865e-1a8d-47eb-ab54-50537084250c",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        -16
      ],
      "parameters": {
        "color": 7,
        "height": 384,
        "content": "## AI Output Parsing\nCleans and parses the AI output JSON.  \nExtracts sentiment distribution, topic keywords, and detailed message-level analysis for downstream summarization.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d6eef13f-ebaa-446a-b61f-542ee194a4ed",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 608,
        "content": "## AI Summary Generation\nGPT-4o converts structured data into a visually rich HTML summary.  \nIncludes sentiment ratios, trending topics, and top insights formatted for email readability.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "df66fc74-e5e8-42e0-a9e6-5ec8f26c203c",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1760,
        -16
      ],
      "parameters": {
        "color": 7,
        "height": 576,
        "content": "## Email Delivery\nSends the final HTML summary via Gmail to marketing stakeholders.  \nIncludes formatted sections with emojis and highlights for quick readability.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f352a4d1-542e-425b-b900-08aabe024b16",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        624
      ],
      "parameters": {
        "color": 7,
        "height": 448,
        "content": "## Error Logging\nCaptures workflow or API-level errors and logs them in Google Sheets.  \nProvides an audit trail for debugging and workflow reliability.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "11fd2dec-9841-45df-a7cb-d553064c9cce",
  "connections": {
    "Configure GPT-4o Model": {
      "ai_languageModel": [
        [
          {
            "node": "Perform Sentiment & Keyword Analysis ",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Configure GPT-4o Model1": {
      "ai_languageModel": [
        [
          {
            "node": " Generate Audience Sentiment Summary",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Merge Platform Datasets": {
      "main": [
        [
          {
            "node": "Validate API Response Integrity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Recent Mentions (X API)": {
      "main": [
        [
          {
            "node": "Merge Platform Datasets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate API Response Integrity": {
      "main": [
        [
          {
            "node": "Consolidate Mentions & Comments ",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Errors in Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Fetch Recent Facebook Comments ": {
      "main": [
        [
          {
            "node": "Merge Platform Datasets",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Consolidate Mentions & Comments ": {
      "main": [
        [
          {
            "node": "Perform Sentiment & Keyword Analysis ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Parse AI Output to Structured JSON": {
      "main": [
        [
          {
            "node": " Generate Audience Sentiment Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Generate Audience Sentiment Summary": {
      "main": [
        [
          {
            "node": "Send Email Summary to Marketing Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": " Fetch Recent Facebook Comments ",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Recent Mentions (X API)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Perform Sentiment & Keyword Analysis ": {
      "main": [
        [
          {
            "node": " Parse AI Output to Structured JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}