{
  "id": "F1ngpzitCtWY5ox2",
  "name": "Automate professional replies to tweets from X (formerly Twitter)",
  "tags": [],
  "nodes": [
    {
      "id": "9638802e-00ea-4682-8a99-f559a9aa901b",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        1560,
        960
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "f33c9d90-c193-4e07-9631-3a0bcda37d75",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        3520,
        740
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 15
      },
      "typeVersion": 1.1
    },
    {
      "id": "6be8d0b6-d78c-4c65-8d33-56c54dce6d93",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        4140,
        740
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 15
      },
      "typeVersion": 1.1
    },
    {
      "id": "745cdd54-689c-4736-be3f-b97eb2fde6d8",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        3300,
        720
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "8d07ff84-970b-411e-95c4-b49586d80b73",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1560,
        740
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "daysInterval": 3,
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "87b7abc7-5636-4d84-aa51-197e8c14ffb2",
      "name": "Set search Terms",
      "type": "n8n-nodes-base.set",
      "position": [
        1800,
        740
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f6484cd1-f15d-4ac0-9d61-aa322f1f4c02",
              "name": "searchTerms",
              "type": "array",
              "value": "=[\"AI Engineer\",  \"Fullstack developer\",  \"As a CEO\",  \"JavaScript developer\",  \"React developer\",  \"Tech recruiter\",  \"TypeScript developer\",  \"Workflow automation\",  \"Automation engineer\",  \"n8n\"]"
            },
            {
              "id": "a12d0bf5-5cf4-4d4f-af90-e2ee949e05d0",
              "name": "role",
              "type": "string",
              "value": "Fullstack Developer and AI Engineer"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "28b30e1e-d168-4602-8042-fd01080e7a5f",
      "name": "Rotating Slot Index Generator",
      "type": "n8n-nodes-base.code",
      "position": [
        2020,
        740
      ],
      "parameters": {
        "jsCode": "// Set a fixed base date to start counting from (can be any date)\nconst baseDate = new Date(\"2025-01-01\");\n\n// Get today's date\nconst today = new Date();\n\n// Calculate the total number of days passed since the base date\nconst diffInDays = Math.floor((today - baseDate) / (1000 * 60 * 60 * 24));\n\n// Each \"slot\" lasts 3 days. Divide the days passed by 3, then get the current slot index (0 to 9)\nconst index = Math.floor(diffInDays / 3) % 10;\n\n// Return the index in the required format (e.g., for n8n or similar workflow tools)\nreturn [{ json: { index } }];\n}\n\n\n/*\n    Example: This script shows how to calculate a daily rotating index based on the number of days since a base date.\n    The index will increase by one each day, and cycle from 0 to 9.\n    You can use this logic for workflows that need to assign a slot, code, or value that changes every day in a repeating pattern.\n*/\n\n// const baseDate = new Date(\"2025-01-01\"); // Set a fixed base date to start counting from (can be any date)\n// const today = new Date(); // Get today's date\n\n// Calculate the total number of days passed since the base date\n// const diffInDays = Math.floor((today - baseDate) / (1000 * 60 * 60 * 24));\n\n// Each slot lasts 1 day. Get the current index (from 0 to 9), rotating every 10 days\n// const index = diffInDays % 10;\n\n// Return the index in the required format (for example, for n8n or similar workflow tools)\n// return [{ json: { index } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "0601e643-0439-4ab3-9748-a69cb30a1ca2",
      "name": "X (Formerly Twitter) Search terms",
      "type": "n8n-nodes-base.twitter",
      "position": [
        2260,
        740
      ],
      "parameters": {
        "limit": 10,
        "operation": "search",
        "searchText": "={{ $('Set search Terms').item.json.searchTerms[$json.index] }}",
        "additionalFields": {}
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "50033bf2-737c-43d6-9d28-ffb2fc33091b",
      "name": "GPT Topic Selector",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2740,
        740
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1",
          "cachedResultName": "GPT-4.1"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "=You are an AI assistant helping a {{ $('Set search Terms').item.json.role }} select tweets to engage with professionally.\n\nYou will receive a JSON array of tweets, each with:\n{\n  \"id\": \"tweet_id\",\n  \"text\": \"tweet content\"\n}\n\nYour job:\n1. Discard tweets that are:\n   - not written in English\n   - memes, jokes, political content, personal replies, spam, or conspiracy theories\n   - about internships or entry-level student programs\n   - Discard tweets generated by AI bot Grok\n\n2. Return only the tweets that are relevant to the topic\n{{ $('Set search Terms').item.json.searchTerms[$json.index] }}.\n\n3. Return only the filtered tweets in this exact format. The property name must always be `results`:\n\n```json\n{\n  \"results\": [\n    {\n      \"id\": \"1234567890\",\n      \"text\": \"tweet content\"\n    }\n  ]\n}"
            },
            {
              "content": "={{ JSON.stringify($json[\"tweets\"], null, 2) }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "4dd2f7f7-edee-4703-9309-94d3c1a8d1c1",
      "name": "Extract Tweets from JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        3060,
        740
      ],
      "parameters": {
        "jsCode": "// Get the array of tweet results from the JSON response\nconst tweets = $json.message.content.results;\n\n// Map through each tweet and return a simplified object with only the id and text\nreturn tweets.map(tweet => {\n  return {\n    json: {\n      id: tweet.id,     // The unique identifier of the tweet\n      text: tweet.text  // The text content of the tweet\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "9069c483-e146-4e67-9204-ea93a31b0631",
      "name": "Group Tweets into a Single Array",
      "type": "n8n-nodes-base.code",
      "position": [
        2540,
        740
      ],
      "parameters": {
        "jsCode": "// Create and return a single object containing an array of tweets\nreturn [\n  {\n    json: {\n      // The 'tweets' array will contain objects with 'id' and 'text' for each tweet\n      tweets: items.map(item => ({\n        id: item.json.id,    // The unique identifier of the tweet\n        text: item.json.text // The text content of the tweet\n      }))\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "8d351836-cf9b-4fca-bc74-1bf2ed99312c",
      "name": "GPT Tweet Response",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        3840,
        740
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1",
          "cachedResultName": "GPT-4.1"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "=You are an expert {{ $('Set search Terms').item.json.role }} and friendly Twitter user. You write short, professional replies to tweets that encourage conversation and add value.\n\nAlways reply in the same language as the original tweet.\nBe thoughtful, professional, and insightful. Keep your tone direct and to the point \u2014 avoid phrases like \"by the way\" or unnecessary fluff.\n\nYour reply must be no longer than 280 characters.\nOnly return the reply text \u2014 no formatting, no explanation, no prefixes.\n\nDo not include links unless those exact conditions are met.\n\nDo not use repetitive or natural phrases or words like \"curious\" or \"I\u2019d love to hear\"\n\nDo not use dashes or hyphens\n"
            },
            {
              "content": "=Tweet: {{ $('Extract Tweets from JSON').item.json.text }}\nWrite a reply that could spark a professional conversation or catch the author\u2019s attention in a natural way."
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "b8579485-bf04-4b3e-8481-d2dc410c8841",
      "name": "X (Formerly Twitter) Like Tweet",
      "type": "n8n-nodes-base.twitter",
      "onError": "continueRegularOutput",
      "position": [
        3680,
        740
      ],
      "parameters": {
        "tweetId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Extract Tweets from JSON').item.json.id }}"
        },
        "operation": "like"
      },
      "typeVersion": 2
    },
    {
      "id": "13121377-4750-4a3f-88e5-c4478fecdf59",
      "name": "X (Formerly Twitter) Post Reply",
      "type": "n8n-nodes-base.twitter",
      "position": [
        4280,
        740
      ],
      "parameters": {
        "text": "={{ $('GPT Tweet Response').item.json.message.content }}",
        "additionalFields": {
          "inReplyToStatusId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $('Extract Tweets from JSON').item.json.id }}"
          }
        }
      },
      "typeVersion": 2
    },
    {
      "id": "c7be5433-e176-476b-80f5-e301b306e090",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1520,
        0
      ],
      "parameters": {
        "width": 640,
        "height": 920,
        "content": "# Configure Your Variables\n\n**The flow currently runs every 3 days and considers 10 topics (10 per month).**\nIf you want to change the schedule (for example, to run daily), you must adjust both the trigger and the code. Alternative code is provided in comments for daily scheduling.\n\n## Tools Overview\n\n### 1. Schedule Trigger\n\n* Triggers the workflow every 3 days.\n* To run the workflow daily, update the schedule trigger and uncomment the relevant code lines in the index calculation node.\n\n### 2. Set Variables (Search Terms and Role)\n\n* Here you define the array of `searchTerms` (the topics or keywords you want to cycle through).\n* You can also set the role or any other variables needed for downstream steps.\n\n### 3. Rotating Slot Index Generator (Code Node)\n\n* This node contains code that calculates an index based on the current day of the month.\n* The index determines which topic to select from the `searchTerms` array.\n* The code is flexible and includes a commented-out version for daily execution if needed.\n\n**This approach ensures your workflow selects a different topic each time it runs, based on your defined frequency.**\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d3ea38df-42af-440e-a89a-22bb924b8375",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2200,
        380
      ],
      "parameters": {
        "color": 3,
        "width": 480,
        "height": 540,
        "content": "# X (Twitter) Search Terms Node\n\n* *Make sure to configure your X (Twitter) API credentials before using this node.**\n* Uses the `index` to select a search term from your `searchTerms` array, which is then used to search tweets.\n\n* **Extract and Format Tweets Node**\n\nProcesses the search results, returning an array of objects with only `id` and `text` for each tweet.\nKeeps just the essential tweet data for the next steps.\n\n---\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b500ec65-8e8e-42ff-bf19-1fad94132082",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2700,
        240
      ],
      "parameters": {
        "color": 4,
        "width": 540,
        "height": 680,
        "content": "# GPT Topic Selector\n\n* **Requires your OpenAI API key to be configured.**\n* Uses the defined role (from the `Set search Terms` node) to guide tweet selection.\n* Receives a JSON array of tweets, each with an `id` and `text`.\n* Filters tweets according to these instructions:\n  Discard tweets that are:\n     * Not written in English\n     * Memes, jokes, political content, personal replies, spam, or conspiracy theories\n     * About internships or entry-level student programs\n* Only tweets that meet the criteria will move on to the next step.\n\n## Organizing Tweets\n\n* The following code node organizes the filtered tweets, returning an array where each object contains just the `id` and `text`.\n\n---\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c63431a7-2426-4d83-8f9b-f98edcab8091",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3500,
        240
      ],
      "parameters": {
        "color": 4,
        "width": 940,
        "height": 680,
        "content": "# Loop: Like, Generate, and Reply to Tweets\n\n**Make sure to configure your X (Twitter) API credentials before using this node.**\n**Requires your OpenAI API key to be configured.**\n\n## How It Works\n\n* The loop processes each selected tweet by:\n\n  1. **Liking** the post.\n  2. **Generating a reply:**\n     For each tweet, the workflow sends the tweet\u2019s content and the selected role to GPT (OpenAI) to generate a short, professional, and relevant reply. The generated reply is tailored to the language and context of the original tweet, following best practices for engagement.\n  3. **Posting the reply** to the original tweet.\n\n* **Wait nodes** are included to comply with the rate limits of the free Twitter API.\n  If you have a paid API plan, you may reduce the wait times to just a few seconds.\n\n**This structure ensures your workflow interacts with tweets in a compliant and professional way.**\n\n---\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5ea891d9-05a1-481d-a6de-8c37dae0b020",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 6,
        "width": 1480,
        "height": 1220,
        "content": "# Professional Tweet Engagement Automation\n\n## Description\n\nAutomate the process of discovering, filtering, and engaging with relevant tweets on X (formerly Twitter) using GPT and n8n. This workflow is designed for professionals who want to consistently interact with targeted topics in a scalable and compliant way.\n\n---\n\n## How It Works\n\n**This workflow runs on a customizable schedule and follows these four main steps:**\n\n1. **Topic Selection:**\n   Every cycle, the workflow picks a new search term from your custom list of topics, using a rotating index. This ensures balanced coverage of all your key interests over time.\n\n2. **Tweet Search & Extraction:**\n   The workflow uses the selected topic to search X (Twitter) for recent tweets. It then extracts only the essential tweet information (`id` and `text`) for further processing.\n\n3. **GPT-Based Filtering:**\n   Using your OpenAI API key, the workflow leverages GPT to filter tweets, keeping only those that match your professional criteria (for example: English, relevant content, no spam or memes, and excluding entry-level opportunities or AI-generated posts).\n\n4. **Engage: Like, Generate & Post Replies:**\n   For each approved tweet, the workflow automatically:\n\n   * Likes the post,\n   * Generates a tailored, professional reply using GPT based on the tweet\u2019s content and your defined role,\n   * Posts the reply directly on X (Twitter).\n\n   Wait nodes are included to respect Twitter\u2019s API rate limits. You can shorten these if you use a paid API tier.\n\n---\n\n## Requirements\n\n* **X (Twitter) API credentials:** Required for searching tweets, liking, and posting replies.\n* **OpenAI API key:** Needed to run all GPT-powered steps.\n\n---\n\n## Customization\n\n* **Topics:** Edit your list of search terms and roles to match your industry or personal interests.\n* **Frequency:** Adjust the schedule to run daily, weekly, or at any interval you prefer.\n* **Filtering logic:** Tweak the GPT prompt or filtering instructions to better match your brand or professional standards.\n\n---\n\n**This workflow helps you engage with relevant conversations on X (Twitter) efficiently and professionally, automating the process end-to-end while maintaining quality and compliance.**\n\n---\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ccf9c3dd-623b-495d-90f7-5d5a3f3536e8",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "X (Formerly Twitter) Like Tweet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "X (Formerly Twitter) Post Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set search Terms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set search Terms": {
      "main": [
        [
          {
            "node": "Rotating Slot Index Generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT Topic Selector": {
      "main": [
        [
          {
            "node": "Extract Tweets from JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT Tweet Response": {
      "main": [
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Tweets from JSON": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rotating Slot Index Generator": {
      "main": [
        [
          {
            "node": "X (Formerly Twitter) Search terms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "X (Formerly Twitter) Like Tweet": {
      "main": [
        [
          {
            "node": "GPT Tweet Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "X (Formerly Twitter) Post Reply": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Group Tweets into a Single Array": {
      "main": [
        [
          {
            "node": "GPT Topic Selector",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "X (Formerly Twitter) Search terms": {
      "main": [
        [
          {
            "node": "Group Tweets into a Single Array",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Set search Terms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}