Xツイートの提案
上級
これはAI, Marketing分野の自動化ワークフローで、21個のノードを含みます。主にCode, Html, SplitOut, Aggregate, FormTriggerなどのノードを使用、AI技術を活用したスマート自動化を実現。 プロフィールに基づいて関連するXのツイートを見つけて返信の提案を提供
前提条件
- •ターゲットAPIの認証情報が必要な場合あり
- •OpenAI API Key
使用ノード (21)
ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
"id": "kMKlrWsoDftOxsqE",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "X Tweet Suggestions",
"tags": [],
"nodes": [
{
"id": "5fc895f6-b188-4900-81c7-b0eeebd1d571",
"name": "ユーザー投稿取得",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1220,
240
],
"parameters": {
"url": "https://api.twitterapi.io/twitter/user/last_tweets",
"options": {
"timeout": 30000
},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "userName",
"value": "={{ $json[\"What's your name on X (it's within your url)\"] }}"
},
{
"name": "count",
"value": "20"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "ptKPKaEhgzwgwNXi",
"name": "Header Auth account"
}
},
"typeVersion": 4.2,
"continueOnFail": true
},
{
"id": "299a1ada-646c-453c-85e5-c340e6df2dfc",
"name": "投稿をマッピング",
"type": "n8n-nodes-base.code",
"position": [
-1220,
440
],
"parameters": {
"jsCode": "\n return { tweets: $input.first().json.data.tweets };\n \n\n"
},
"typeVersion": 2
},
{
"id": "72b79ff2-055b-4842-8a60-7b8cef9dedd0",
"name": "フォーム送信時",
"type": "n8n-nodes-base.formTrigger",
"position": [
-1220,
0
],
"webhookId": "cf0416df-72fb-4660-9f6a-1b29d03a18ec",
"parameters": {
"options": {},
"formTitle": "Perfect Niche Tweets",
"formFields": {
"values": [
{
"fieldLabel": "What's your name on X (it's within your url)",
"placeholder": "OnePromptMagic"
},
{
"fieldType": "dropdown",
"fieldLabel": "What are your current goals on X",
"multiselect": true,
"fieldOptions": {
"values": [
{
"option": "Increase followers"
},
{
"option": "Boost impressions or reach"
},
{
"option": "Boost engagement"
},
{
"option": "Write a viral tweet"
},
{
"option": "Just be helpful and motivating"
}
]
}
},
{
"fieldLabel": "Additional Infos (Optional)",
"placeholder": "I'm recently deep into n8n flows, publishing one flow a week, incorporate that where it fits"
}
]
},
"formDescription": "Enter your details and goals, we will then analyse your profile, analyse trendings topics that might be a good fit for you and finally draft some perfect tweets for you"
},
"typeVersion": 2.2
},
{
"id": "ce05506b-fba3-40ff-a28f-24434175f6b7",
"name": "分割",
"type": "n8n-nodes-base.splitOut",
"position": [
-320,
240
],
"parameters": {
"options": {},
"fieldToSplitOut": "queries"
},
"typeVersion": 1
},
{
"id": "445f99d3-95ec-4c94-95b7-c64bb0783da1",
"name": "集約",
"type": "n8n-nodes-base.aggregate",
"position": [
120,
0
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "tweets"
}
]
}
},
"typeVersion": 1
},
{
"id": "0511ce11-d316-4598-9e49-9a4df3e8dc95",
"name": "投稿作成",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
60,
320
],
"parameters": {
"text": "=you are an expert on X conversions and brand building. the user has the following goals: {{ $('On form submission').item.json[\"What are your current goals on X\"] }}\nhere is the user's detailed user profile: {{ $('User Profile Analyser').item.json.output.toJsonString() }}\nhere is the user's bio: {{ $('Map Tweets').item.json.tweets[0].author.profile_bio.toJsonString()[\"description\"] }}\nHere are trending tweets that might be interesting for the user to interact with: \n{{ $json.tweets.toJsonString() }}\ncraft 10 tweets for the user, either interact with one of the listed tweets or craft an original tweet, based on what you think will bring the user closer to their goal. You can only write up to 2 original tweet, all others have to be responses. When responding, try to engage with the author, not self promote. Never write hashtags within the tweets. Use exclamation marks sparingly. Keep text short and authentic. some sarcasm can help, as long as it's not cringeworthy.\n\n🚫 Words to Avoid (AI Red Flags)\nNever overuse these AI-typical words:\nCrucial, delve, dive, tapestry\nFurthermore, consequently, moreover\n\"In today's [adjective] world of...\"\n\"Not only... but also\"\n\"By [doing X]...\" (especially in conclusions)\n✅ AI Writing Instructions\n1. Vary Sentence Structure\nMix short sentences (5-10 words) with longer ones (15-25 words)\nAvoid starting consecutive sentences with the same word patterns\nUse contractions naturally (don't, won't, it's, we're)\nBreak up long paragraphs with single-sentence paragraphs for emphasis\n2. Simulate Personal Voice\nUse first-person occasionally: \"I've noticed...\", \"In my view...\"\nInclude hypothetical scenarios: \"Imagine if...\", \"Picture this...\"\nReference common experiences: \"We've all been there when...\"\n3. Break Formulaic Patterns\nAvoid repetitive paragraph structures (intro-body-conclusion format)\nDon't restate the same concept multiple times in different words\nVary transition words instead of always using \"however,\" \"therefore,\" \"additionally\"\n4. Natural Language Patterns\nUse incomplete sentences for emphasis. Like this.\nInclude rhetorical questions to engage readers\nAdd mild interruptions with em-dashes--like this--for natural flow\nUse casual connectors: \"Plus,\" \"And here's the thing,\" \"Look,\"\n5. Content Structure Variation\nStart some paragraphs with questions\nUse specific numbers instead of vague terms (say \"73%\" not \"most\")\nInclude conversational asides in parentheses (because who doesn't love those?)\nVary list formats: bullets, numbers, and embedded lists within paragraphs\n6. Tone Authenticity\nMatch the complexity level to the audience consistently\nUse industry jargon appropriately but sparingly\nInclude mild opinions and stance-taking language\nWrite with confident assertions rather than hedging every statement\nKey instruction: Simulate the natural inconsistencies and stylistic quirks that make human writing distinctive.\n\ntweets to ignore: anything crypto related, politics\nif in the relevant tweets array there are several tweets of the same user, only respond to up to 1 one of those.\n\nHere is additional info by the user for this request:\n{{ $('On form submission').item.json[\"Additional Infos (Optional)\"] }}",
"options": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2
},
{
"id": "8d2da454-3561-4ddc-8df7-b5d535a4411f",
"name": "ユーザープロフィール分析",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-840,
0
],
"parameters": {
"text": "=You will provide a detailed user profile of an X user based on their recent tweets. Your output will serve to create new tweets for this user, that have the same feel, punctuation patterns, language hickups, emoji usage, use of abbreviation, humor and flow of story as the user. so go as detailed as possible on what you can find out, so that the next agent can work with your ouput to craft a tweet.\nhere are the user's last tweets: \n{{ $json.tweets.toJsonString() }}\n",
"options": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2
},
{
"id": "59bfbd43-e08b-4369-9db5-b0a24fa7312d",
"name": "キーワード分析",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-840,
320
],
"parameters": {
"text": "=You are an expert Twitter trend analyst. Analyze the user's profile and generate 3 groups of related keywords that will find relevant, high-engagement tweets for their goals.\n\nUser Profile Analysis:\n{{ $json.output.toJsonString() }}\n\nUser Additional Input: \n{{ $('On form submission').item.json[\"Additional Infos (Optional)\"] || \"none\" }}\n\nYour Mission:\nGenerate 3 groups of keywords (3-5 keywords per group) that align with the user's expertise and goals. Each group should target different aspects of their interests.\n\nFocus on:\n- Specific terminology in their field\n- Related concepts and synonyms \n- Industry jargon and buzzwords\n- Trending topics in their niche\n- Tools and technologies they use\n\nGuidelines:\n- Use exact phrases that people actually tweet about\n- Include both formal and casual variations\n- Consider abbreviations and acronyms\n- Think about what thought leaders in their field discuss\n- Focus on actionable, engaging content\n\nExample:\nIf user is into AI automation:\nGroup 1: [\"AI agents\", \"autonomous AI\", \"AI automation\", \"agent orchestration\"] \nGroup 2: [\"no code\", \"workflow automation\", \"AI tools\", \"automation tools\"]\nGroup 3: [\"cursor\", \"AI coding\", \"AI development\", \"LLM integration\"]\n\nOutput exactly 3 keyword groups that will find the most relevant tweets for engagement opportunities.",
"options": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2
},
{
"id": "98f3a6f6-397b-48b8-8770-0aa89720ed4b",
"name": "関連投稿取得",
"type": "n8n-nodes-base.httpRequest",
"position": [
-320,
460
],
"parameters": {
"url": "https://api.twitterapi.io/twitter/tweet/advanced_search",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "query",
"value": "={{ $json[\"queries\"] }}"
},
{
"name": "queryType",
"value": "Top"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "ptKPKaEhgzwgwNXi",
"name": "Header Auth account"
}
},
"typeVersion": 4.2
},
{
"id": "8c513d94-3aec-4094-8989-572cb019a258",
"name": "クリックで結果表示",
"type": "n8n-nodes-base.html",
"position": [
600,
440
],
"parameters": {
"html": "{{ $json.html }}"
},
"typeVersion": 1.2
},
{
"id": "0d75fcdf-83d4-4eed-b5b7-84521948022e",
"name": "全角ダッシュ置換",
"type": "n8n-nodes-base.code",
"position": [
600,
0
],
"parameters": {
"jsCode": "const items = [];\n\nfor (const item of $input.all()) {\n const data = item.json;\n \n if (!data.output || !data.output.tweets || !Array.isArray(data.output.tweets)) {\n items.push({\n json: data\n });\n continue;\n }\n \n const processedTweets = data.output.tweets.map(tweet => {\n return {\n ...tweet,\n text: tweet.text ? tweet.text.replace(/--/g, ' - ') : tweet.text\n };\n });\n \n items.push({\n json: {\n ...data,\n output: {\n ...data.output,\n tweets: processedTweets\n }\n }\n });\n}\n\nreturn items;"
},
"typeVersion": 2
},
{
"id": "4478ff3c-b653-4e4d-bd03-2b10b13b8265",
"name": "キーワード結合",
"type": "n8n-nodes-base.code",
"position": [
-320,
0
],
"parameters": {
"jsCode": "const items = [];\n\nfor (const item of $input.all()) {\n const data = item.json;\n const userGoals = $('On form submission').item.json[\"What are your current goals on X\"];\n \n const keywordGroups = data.output.keyword_groups || [];\n \n const wantsViral = userGoals && userGoals.includes(\"Write a viral tweet\");\n const wantsFollowers = userGoals && userGoals.includes(\"Increase followers\");\n \n const queries = [];\n \n keywordGroups.forEach((group, index) => {\n const keywordQuery = group.map(keyword => `\"${keyword}\"`).join(\" OR \");\n \n let searchQuery = `(${keywordQuery})`;\n \n searchQuery += \" within_time:2d\";\n \n if (wantsViral) {\n searchQuery += \" min_faves:200 min_retweets:20\";\n } else if (wantsFollowers) {\n searchQuery += \" min_faves:100 min_retweets:10\";\n } else {\n searchQuery += \" min_faves:50\";\n }\n \n searchQuery += \" lang:en -$ -filter:retweets\";\n \n if (index === 0) {\n searchQuery += \" filter:verified\";\n } else if (index === 1) {\n searchQuery += \" filter:blue_verified\";\n }\n \n queries.push(searchQuery);\n });\n \n items.push({\n json: {\n queries: queries,\n user_goals: userGoals,\n wants_viral: wantsViral,\n original_keyword_groups: keywordGroups\n }\n });\n}\n\nreturn items;"
},
"typeVersion": 2
},
{
"id": "61f53128-9ef7-4638-81ff-a752a62510a4",
"name": "o3-mini",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-840,
180
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "o3-mini",
"cachedResultName": "o3-mini"
},
"options": {}
},
"credentials": {
"openAiApi": {
"id": "q4srYaXuPZeNbtbt",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "de12f5b8-38b5-41b8-bfb8-756f40e98668",
"name": "プロフィールスキーマ",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-660,
180
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"examplePrompt\": {\n \"type\": \"string\",\n \"description\": \"This should be one of their best tweets (just as is, nothing changed)\"\n },\n \"punctuation\": {\n \"type\": \"string\",\n \"description\": \"User's punctuation style and patterns\"\n },\n \"personalityType\": {\n \"type\": \"string\",\n \"enum\": [\n \"Extraverted sensation\",\n \"Introverted sensation\", \n \"Extraverted intuition\",\n \"Introverted intuition\",\n \"Extraverted thinking\",\n \"Introverted thinking\",\n \"Extraverted feeling\",\n \"Introverted feeling\"\n ],\n \"description\": \"Dominant cognitive function based on user's communication style\"\n },\n \"humorType\": {\n \"type\": \"string\",\n \"enum\": [\n \"Physical comedy or slapstick comedy\",\n \"Aggressive humor\",\n \"Self-enhancing humor\", \n \"Self-defeating humor\",\n \"Affiliative humor\",\n \"Self-deprecating humor\",\n \"Observational humor\",\n \"Dry humor\"\n ],\n \"description\": \"Primary humor style used by the user\"\n },\n \"fieldOfExpertise\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"maxItems\": 5,\n \"description\": \"User's areas of expertise (up to 5)\"\n },\n \"recognizedPatterns\": {\n \"type\": \"string\",\n \"description\": \"This could be something like 'user always starts with this', or 'user often uses the pray emoji', etc.\"\n }\n },\n \"required\": [\"examplePrompt\"],\n \"additionalProperties\": false\n}"
},
"typeVersion": 1.2
},
{
"id": "3ed5bc68-5456-4fd5-a1d6-d3c2fa05fc94",
"name": "4.1-mini",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-840,
500
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini",
"cachedResultName": "gpt-4.1-mini"
},
"options": {}
},
"credentials": {
"openAiApi": {
"id": "q4srYaXuPZeNbtbt",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "592ede7f-1a72-480a-8090-740906cb9d21",
"name": "キーワードスキーマ",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-660,
500
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"keyword_groups\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\", \n \"items\": {\n \"type\": \"string\"\n },\n \"minItems\": 3,\n \"maxItems\": 5,\n \"description\": \"A group of 3-5 related keywords that will be combined with OR\"\n },\n \"minItems\": 3,\n \"maxItems\": 3,\n \"description\": \"3 groups of related keywords, each targeting different aspects of user's interests\"\n }\n },\n \"required\": [\"keyword_groups\"],\n \"additionalProperties\": false\n}"
},
"typeVersion": 1.2
},
{
"id": "debdaf4b-4c3e-4797-b1e4-1b0f118175e7",
"name": "o3-mini1",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
60,
500
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "o3-mini",
"cachedResultName": "o3-mini"
},
"options": {}
},
"credentials": {
"openAiApi": {
"id": "q4srYaXuPZeNbtbt",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "8745739b-1a0f-4390-accb-8127c5602f0e",
"name": "投稿スキーマ",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
260,
500
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"tweets\": {\n \"type\": \"array\",\n \"minItems\": 5,\n \"maxItems\": 10,\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"priority\": {\n \"type\": \"integer\",\n \"minimum\": 1,\n \"maximum\": 5,\n \"description\": \"Priority level from 1-5 (1 is highest priority, 5 is lowest priority)\"\n },\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The tweet text content\"\n },\n \"isAnswer\": {\n \"type\": \"boolean\",\n \"description\": \"If true, the tweet is a response to one of the listed tweets; if false, it's an original tweet by the user\"\n },\n \"tweetUrl\": {\n \"type\": \"string\",\n \"description\": \"The URL to the tweet to respond to (required only if isAnswer is true)\"\n },\n \"reasoning\": {\n \"type\": \"string\",\n \"description\": \"Concise reason why this tweet will bring the user closer to their goal\"\n }\n },\n \"required\": [\"priority\", \"text\", \"isAnswer\", \"reasoning\"],\n \"additionalProperties\": false,\n \"if\": {\n \"properties\": {\n \"isAnswer\": {\n \"const\": true\n }\n }\n },\n \"then\": {\n \"required\": [\"priority\", \"text\", \"isAnswer\", \"tweetUrl\", \"reasoning\"]\n }\n },\n \"description\": \"Array of 1-5 tweet suggestions with priority and context\"\n }\n },\n \"required\": [\"tweets\"],\n \"additionalProperties\": false\n}"
},
"typeVersion": 1.2
},
{
"id": "fb0151fe-e279-4a7e-b60c-0c318c16bc91",
"name": "結果を準備",
"type": "n8n-nodes-base.code",
"position": [
600,
220
],
"parameters": {
"jsCode": "const items = [];\n\nfor (const item of $input.all()) {\n const data = item.json;\n \n const aggregateNodeData = $('Aggregate').all();\n \n const originalTweetsMap = {};\n \n let debugUrls = [];\n \n if (aggregateNodeData && aggregateNodeData.length > 0) {\n aggregateNodeData.forEach((nodeItem, nodeIndex) => {\n if (nodeItem.json && nodeItem.json.tweets) {\n \n if (Array.isArray(nodeItem.json.tweets)) {\n nodeItem.json.tweets.forEach((tweetGroup, groupIndex) => {\n if (Array.isArray(tweetGroup)) {\n tweetGroup.forEach((tweet, tweetIndex) => {\n if (tweet && typeof tweet === 'object') {\n let username = 'unknown';\n let authorName = 'Unknown User';\n \n if (tweet.author) {\n username = tweet.author.username || tweet.author.screen_name || tweet.author.handle || 'unknown';\n authorName = tweet.author.name || tweet.author.display_name || 'Unknown User';\n }\n \n if (!username || username === 'unknown') {\n username = tweet.username || tweet.screen_name || tweet.handle || 'unknown';\n }\n if (!authorName || authorName === 'Unknown User') {\n authorName = tweet.author_name || tweet.name || 'Unknown User';\n }\n \n const tweetData = {\n text: tweet.text,\n author: username,\n authorName: authorName\n };\n \n if (tweet.url && tweet.text) {\n originalTweetsMap[tweet.url] = tweetData;\n debugUrls.push(tweet.url);\n }\n if (tweet.twitterUrl && tweet.text) {\n originalTweetsMap[tweet.twitterUrl] = tweetData;\n debugUrls.push(tweet.twitterUrl);\n }\n \n if (tweet.url) {\n const normalizedUrl = tweet.url.replace('https://x.com', 'https://twitter.com');\n originalTweetsMap[normalizedUrl] = tweetData;\n }\n if (tweet.twitterUrl) {\n const normalizedUrl = tweet.twitterUrl.replace('https://twitter.com', 'https://x.com');\n originalTweetsMap[normalizedUrl] = tweetData;\n }\n }\n });\n } else if (tweetGroup && typeof tweetGroup === 'object') {\n let username = 'unknown';\n let authorName = 'Unknown User';\n \n if (tweetGroup.author) {\n username = tweetGroup.author.username || tweetGroup.author.screen_name || tweetGroup.author.handle || 'unknown';\n authorName = tweetGroup.author.name || tweetGroup.author.display_name || 'Unknown User';\n }\n \n if (!username || username === 'unknown') {\n username = tweetGroup.username || tweetGroup.screen_name || tweetGroup.handle || 'unknown';\n }\n if (!authorName || authorName === 'Unknown User') {\n authorName = tweetGroup.author_name || tweetGroup.name || 'Unknown User';\n }\n \n const tweetData = {\n text: tweetGroup.text,\n author: username,\n authorName: authorName\n };\n \n if (tweetGroup.url && tweetGroup.text) {\n originalTweetsMap[tweetGroup.url] = tweetData;\n debugUrls.push(tweetGroup.url);\n }\n if (tweetGroup.twitterUrl && tweetGroup.text) {\n originalTweetsMap[tweetGroup.twitterUrl] = tweetData;\n debugUrls.push(tweetGroup.twitterUrl);\n }\n }\n });\n }\n }\n });\n }\n \n if (!data.output || !data.output.tweets || !Array.isArray(data.output.tweets)) {\n items.push({\n json: {\n html: '<div>No tweets data found</div>'\n }\n });\n continue;\n }\n \n const tweets = data.output.tweets;\n const responseOpportunities = tweets.filter(t => t.isAnswer);\n const originalTweets = tweets.filter(t => !t.isAnswer);\n \n // Debug: track which URLs we're looking for vs what we found\n const lookingFor = responseOpportunities.map(t => t.tweetUrl);\n const notFound = [];\n const skippedUnknown = [];\n \n let tweetsHtml = '';\n \n responseOpportunities.forEach((tweet, index) => {\n let originalTweetData = originalTweetsMap[tweet.tweetUrl];\n \n if (!originalTweetData) {\n const normalizedUrl1 = tweet.tweetUrl.replace('https://x.com', 'https://twitter.com');\n const normalizedUrl2 = tweet.tweetUrl.replace('https://twitter.com', 'https://x.com');\n originalTweetData = originalTweetsMap[normalizedUrl1] || originalTweetsMap[normalizedUrl2];\n }\n \n if (!originalTweetData || \n originalTweetData.authorName === 'Unknown User' || \n originalTweetData.text === 'Original tweet text not available') {\n skippedUnknown.push(tweet.tweetUrl);\n notFound.push(tweet.tweetUrl);\n return;\n }\n \n const originalTweetText = originalTweetData.text;\n const authorInfo = originalTweetData.authorName;\n \n const isAnswerBadge = '<span style=\"background: #8b5cf6; color: white; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; margin-bottom: 16px; display: inline-block;\">💬 Response Opportunity</span>';\n \n const tweetId = `tweet-response-${index}`;\n const reasoningId = `reasoning-response-${index}`;\n \n tweetsHtml += `\n <div style=\"border-bottom: 2px solid #e5e7eb; padding: 24px;\">\n ${isAnswerBadge}\n \n <!-- Original Tweet -->\n <div style=\"margin-bottom: 20px;\">\n <div style=\"display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;\">\n <div style=\"color: #6b7280; font-size: 14px;\">\n <strong>${authorInfo}</strong>\n </div>\n <button onclick=\"toggleReasoning('${reasoningId}')\" style=\"background: #6b7280; color: white; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 10px; transition: background 0.2s;\" onmouseover=\"this.style.background='#4b5563'\" onmouseout=\"this.style.background='#6b7280'\">\n ℹ️\n </button>\n </div>\n \n <div onclick=\"window.open('${tweet.tweetUrl}', '_blank')\" style=\"background: #f9fafb; padding: 16px; border-radius: 8px; border-left: 4px solid #1da1f2; margin-bottom: 12px; cursor: pointer; transition: background 0.2s;\" onmouseover=\"this.style.background='#f3f4f6'\" onmouseout=\"this.style.background='#f9fafb'\">\n ${originalTweetText}\n </div>\n \n <div style=\"background: #fafafa; padding: 8px 12px; border-radius: 6px; font-family: monospace; font-size: 11px; word-break: break-all; margin-bottom: 8px;\">\n <strong>URL:</strong> <a href=\"${tweet.tweetUrl}\" target=\"_blank\" style=\"color: #1da1f2; text-decoration: none;\">${tweet.tweetUrl}</a>\n </div>\n \n <!-- Hidden Reasoning -->\n <div id=\"${reasoningId}\" style=\"display: none; background: #fff3cd; padding: 12px; border-radius: 6px; margin-top: 12px; border-left: 4px solid #ffc107;\">\n <strong>Strategic Analysis:</strong> ${tweet.reasoning}\n </div>\n </div>\n\n <!-- Suggested Response -->\n <div>\n <h3 style=\"color: #374151; margin: 0 0 12px 0; font-size: 16px;\">✨ Your Response</h3>\n <div id=\"${tweetId}\" onclick=\"copyResponseText('${tweetId}')\" style=\"background: #f0fdf4; padding: 16px; border-radius: 8px; border-left: 4px solid #10b981; margin-bottom: 12px; cursor: pointer; transition: all 0.2s;\" onmouseover=\"this.style.background='#dcfce7'; this.style.transform='translateY(-1px)'; this.style.boxShadow='0 4px 12px rgba(16, 185, 129, 0.15)'\" onmouseout=\"this.style.background='#f0fdf4'; this.style.transform='translateY(0)'; this.style.boxShadow='none'\">\n ${tweet.text}\n </div>\n </div>\n \n </div>\n `;\n });\n \n originalTweets.forEach((tweet, index) => {\n const originalBadge = '<span style=\"background: #f59e0b; color: white; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; margin-bottom: 16px; display: inline-block;\">✨ Original Tweet</span>';\n \n const tweetId = `tweet-original-${index}`;\n const reasoningId = `reasoning-original-${index}`;\n \n tweetsHtml += `\n <div style=\"border-bottom: 2px solid #e5e7eb; padding: 24px;\">\n ${originalBadge}\n \n <!-- Original Tweet Content -->\n <div style=\"margin-bottom: 20px;\">\n <div style=\"display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;\">\n <h3 style=\"color: #374151; margin: 0; font-size: 16px;\">🎯 Your Original Tweet</h3>\n <button onclick=\"toggleReasoning('${reasoningId}')\" style=\"background: #6b7280; color: white; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 10px; transition: background 0.2s;\" onmouseover=\"this.style.background='#4b5563'\" onmouseout=\"this.style.background='#6b7280'\">\n ℹ️\n </button>\n </div>\n \n <div id=\"${tweetId}\" onclick=\"copyResponseText('${tweetId}')\" style=\"background: #fef3c7; padding: 16px; border-radius: 8px; border-left: 4px solid #f59e0b; margin-bottom: 12px; cursor: pointer; transition: all 0.2s;\" onmouseover=\"this.style.background='#fde68a'; this.style.transform='translateY(-1px)'; this.style.boxShadow='0 4px 12px rgba(245, 158, 11, 0.15)'\" onmouseout=\"this.style.background='#fef3c7'; this.style.transform='translateY(0)'; this.style.boxShadow='none'\">\n ${tweet.text}\n </div>\n \n <!-- Hidden Reasoning for Original Tweet -->\n <div id=\"${reasoningId}\" style=\"display: none; background: #ede9fe; padding: 12px; border-radius: 6px; margin-top: 12px; border-left: 4px solid #8b5cf6;\">\n <strong>Strategic Reasoning:</strong> ${tweet.reasoning}\n </div>\n </div>\n \n </div>\n `;\n });\n\n let debugSection = '';\n if (notFound.length > 0) {\n debugSection = `\n <div style=\"padding: 20px; background: #fef2f2; border-top: 1px solid #fecaca; margin-top: 20px;\">\n <details style=\"font-size: 12px; color: #dc2626;\">\n <summary style=\"cursor: pointer; font-weight: 600;\">🔍 Debug: ${notFound.length} tweets not shown</summary>\n <div style=\"margin-top: 8px;\">\n <p><strong>Total response opportunities:</strong> ${responseOpportunities.length}</p>\n <p><strong>Shown:</strong> ${responseOpportunities.length - notFound.length}</p>\n <p><strong>Hidden (unknown users/missing data):</strong> ${notFound.length}</p>\n <p><strong>Original tweets shown:</strong> ${originalTweets.length}</p>\n <br>\n <p><strong>URLs we're looking for:</strong></p>\n <div style=\"font-family: monospace; font-size: 10px; background: white; padding: 8px; border-radius: 4px; margin: 4px 0;\">\n ${lookingFor.map(url => `• ${url}`).join('<br>')}\n </div>\n <p><strong>URLs we found in data:</strong></p>\n <div style=\"font-family: monospace; font-size: 10px; background: white; padding: 8px; border-radius: 4px; margin: 4px 0;\">\n ${debugUrls.length > 0 ? debugUrls.map(url => `• ${url}`).join('<br>') : 'No URLs found in aggregate data'}\n </div>\n <p><strong>Skipped URLs (unknown/missing):</strong></p>\n <div style=\"font-family: monospace; font-size: 10px; background: white; padding: 8px; border-radius: 4px; margin: 4px 0;\">\n ${skippedUnknown.map(url => `• ${url}`).join('<br>')}\n </div>\n </div>\n </details>\n </div>\n `;\n }\n\n const totalShown = (responseOpportunities.length - notFound.length) + originalTweets.length;\n \n const fullHtml = `\n <div style=\"max-width: 800px; margin: 20px auto; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8fafc; padding: 20px;\">\n <div style=\"background: white; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); overflow: hidden;\">\n \n <div style=\"background: #000000; color: white; padding: 20px; text-align: center;\">\n <h1 style=\"margin: 0; font-size: 24px;\">🚀 X Engagement Opportunities</h1>\n <p style=\"margin: 10px 0 0 0; opacity: 0.9;\">Strategic responses and original content (${totalShown} suggestions)</p>\n </div>\n\n ${tweetsHtml}\n ${debugSection}\n\n </div>\n </div>\n\n <script>\n function copyResponseText(elementId) {\n const element = document.getElementById(elementId);\n const text = element.textContent || element.innerText;\n \n if (navigator.clipboard && window.isSecureContext) {\n navigator.clipboard.writeText(text.trim()).then(function() {\n showNotification('✅ Tweet copied to clipboard!');\n flashCopySuccess(elementId);\n }).catch(function(err) {\n fallbackCopy(text.trim());\n });\n } else {\n fallbackCopy(text.trim());\n }\n }\n \n function fallbackCopy(text) {\n const textArea = document.createElement('textarea');\n textArea.value = text;\n textArea.style.position = 'fixed';\n textArea.style.left = '-999999px';\n textArea.style.top = '-999999px';\n document.body.appendChild(textArea);\n textArea.focus();\n textArea.select();\n \n try {\n document.execCommand('copy');\n showNotification('✅ Tweet copied to clipboard!');\n } catch (err) {\n showNotification('❌ Copy failed - please select and copy manually');\n }\n \n document.body.removeChild(textArea);\n }\n \n function flashCopySuccess(elementId) {\n const element = document.getElementById(elementId);\n const originalBg = element.style.backgroundColor;\n element.style.backgroundColor = '#10b981';\n element.style.color = 'white';\n \n setTimeout(() => {\n element.style.backgroundColor = originalBg;\n element.style.color = '';\n }, 200);\n }\n\n function toggleReasoning(elementId) {\n const element = document.getElementById(elementId);\n if (element.style.display === 'none') {\n element.style.display = 'block';\n } else {\n element.style.display = 'none';\n }\n }\n\n function showNotification(message) {\n const notification = document.createElement('div');\n notification.textContent = message;\n notification.style.cssText = 'position:fixed;top:20px;right:20px;background:#10b981;color:white;padding:12px 20px;border-radius:6px;z-index:1000;font-size:14px;box-shadow:0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;';\n document.body.appendChild(notification);\n \n setTimeout(() => {\n if (document.body.contains(notification)) {\n notification.style.animation = 'slideOut 0.3s ease';\n setTimeout(() => {\n if (document.body.contains(notification)) {\n document.body.removeChild(notification);\n }\n }, 300);\n }\n }, 2000);\n }\n \n // Add CSS for animations\n const style = document.createElement('style');\n style.textContent = \\`\n @keyframes slideIn {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n @keyframes slideOut {\n from { transform: translateX(0); opacity: 1; }\n to { transform: translateX(100%); opacity: 0; }\n }\n \\`;\n document.head.appendChild(style);\n </script>\n `;\n\n items.push({\n json: {\n html: fullHtml,\n tweet_count: totalShown,\n response_opportunities: responseOpportunities.length - notFound.length,\n original_tweets: originalTweets.length,\n original_data: data,\n debug_missing_tweets: notFound,\n debug_skipped_unknown: skippedUnknown\n }\n });\n}\n\nreturn items;"
},
"typeVersion": 2
},
{
"id": "9306ec98-229c-44df-9be4-eb642eb49a35",
"name": "付箋",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1840,
0
],
"parameters": {
"color": 7,
"width": 500,
"height": 660,
"content": "## 📋 Setup Instructions\n\n### 1️⃣ Configure API Keys\n- Add your OpenAI credentials to the nodes\n\n- Get your twitterapi key from https://twitterapi.io?ref=1PROMPT (:pray:)\n- Add to \"Header Auth account\" credential\n- Used for fetching user tweets and trending content\n\n### 2️⃣ How to Use\n1. **Execute** the workflow\n2. **Fill out the form** with:\n - Your X username (from your URL)\n - Your goals on X (multiple selection)\n - Optional additional info\n3. **Submit** the form\n4. **Wait** for processing (2-3 minutes)\n5. **Double-click** the \"Click to show Result\" node\n\n### 3️⃣ What This Workflow Does\n\n**Step 1:** Analyzes your recent tweets for personality & style\n**Step 2:** Generates strategic keywords based on your profile\n**Step 3:** Searches for trending tweets in your niche\n**Step 4:** Creates personalized responses & original tweets\n**Step 5:** Displays results in beautiful HTML format\n\nFollow https://x.com/OnePromptMagic for more free n8n templates\n"
},
"typeVersion": 1
},
{
"id": "39c40eba-dbf9-449e-b80c-147625ff4acc",
"name": "付箋1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-580,
-240
],
"parameters": {
"color": 7,
"width": 600,
"height": 80,
"content": "# 🚀 X Tweet Suggestions Workflow"
},
"typeVersion": 1
}
],
"active": false,
"scopes": [
"workflow:create",
"workflow:delete",
"workflow:execute",
"workflow:list",
"workflow:move",
"workflow:read",
"workflow:share",
"workflow:update"
],
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"createdAt": "2025-06-07T04:31:44.088Z",
"updatedAt": "2025-06-10T05:43:37.000Z",
"versionId": "27278b7e-2af0-47d1-b904-8d25bbd300e2",
"isArchived": false,
"staticData": null,
"connections": {
"61f53128-9ef7-4638-81ff-a752a62510a4": {
"ai_languageModel": [
[
{
"node": "8d2da454-3561-4ddc-8df7-b5d535a4411f",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"3ed5bc68-5456-4fd5-a1d6-d3c2fa05fc94": {
"ai_languageModel": [
[
{
"node": "59bfbd43-e08b-4369-9db5-b0a24fa7312d",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"debdaf4b-4c3e-4797-b1e4-1b0f118175e7": {
"ai_languageModel": [
[
{
"node": "0511ce11-d316-4598-9e49-9a4df3e8dc95",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"445f99d3-95ec-4c94-95b7-c64bb0783da1": {
"main": [
[
{
"node": "0511ce11-d316-4598-9e49-9a4df3e8dc95",
"type": "main",
"index": 0
}
]
]
},
"ce05506b-fba3-40ff-a28f-24434175f6b7": {
"main": [
[
{
"node": "98f3a6f6-397b-48b8-8770-0aa89720ed4b",
"type": "main",
"index": 0
}
]
]
},
"299a1ada-646c-453c-85e5-c340e6df2dfc": {
"main": [
[
{
"node": "8d2da454-3561-4ddc-8df7-b5d535a4411f",
"type": "main",
"index": 0
}
]
]
},
"0511ce11-d316-4598-9e49-9a4df3e8dc95": {
"main": [
[
{
"node": "0d75fcdf-83d4-4eed-b5b7-84521948022e",
"type": "main",
"index": 0
}
]
]
},
"8745739b-1a0f-4390-accb-8127c5602f0e": {
"ai_outputParser": [
[
{
"node": "0511ce11-d316-4598-9e49-9a4df3e8dc95",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"fb0151fe-e279-4a7e-b60c-0c318c16bc91": {
"main": [
[
{
"node": "8c513d94-3aec-4094-8989-572cb019a258",
"type": "main",
"index": 0
}
]
]
},
"de12f5b8-38b5-41b8-bfb8-756f40e98668": {
"ai_outputParser": [
[
{
"node": "8d2da454-3561-4ddc-8df7-b5d535a4411f",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"5fc895f6-b188-4900-81c7-b0eeebd1d571": {
"main": [
[
{
"node": "299a1ada-646c-453c-85e5-c340e6df2dfc",
"type": "main",
"index": 0
}
]
]
},
"0d75fcdf-83d4-4eed-b5b7-84521948022e": {
"main": [
[
{
"node": "fb0151fe-e279-4a7e-b60c-0c318c16bc91",
"type": "main",
"index": 0
}
]
]
},
"592ede7f-1a72-480a-8090-740906cb9d21": {
"ai_outputParser": [
[
{
"node": "59bfbd43-e08b-4369-9db5-b0a24fa7312d",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"59bfbd43-e08b-4369-9db5-b0a24fa7312d": {
"main": [
[
{
"node": "4478ff3c-b653-4e4d-bd03-2b10b13b8265",
"type": "main",
"index": 0
}
]
]
},
"4478ff3c-b653-4e4d-bd03-2b10b13b8265": {
"main": [
[
{
"node": "ce05506b-fba3-40ff-a28f-24434175f6b7",
"type": "main",
"index": 0
}
]
]
},
"72b79ff2-055b-4842-8a60-7b8cef9dedd0": {
"main": [
[
{
"node": "5fc895f6-b188-4900-81c7-b0eeebd1d571",
"type": "main",
"index": 0
}
]
]
},
"98f3a6f6-397b-48b8-8770-0aa89720ed4b": {
"main": [
[
{
"node": "445f99d3-95ec-4c94-95b7-c64bb0783da1",
"type": "main",
"index": 0
}
]
]
},
"8d2da454-3561-4ddc-8df7-b5d535a4411f": {
"main": [
[
{
"node": "59bfbd43-e08b-4369-9db5-b0a24fa7312d",
"type": "main",
"index": 0
}
]
]
}
},
"triggerCount": 0
}よくある質問
このワークフローの使い方は?
上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。
このワークフローはどんな場面に適していますか?
上級 - 人工知能, マーケティング
有料ですか?
このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。
関連ワークフロー
AIベースのYouTubeメタデータジェネレーター(GPT-4o、Geminiとコンテンツ強化)
AIによるYouTubeメタデータジェネレーター(GPT-4o、Geminiとコンテンツ強化)
Set
Xml
Code
+
Set
Xml
Code
37 ノードAmjid Ali
人工知能
WordPress コンテンツジェネレータ v3
WordPress コンテンツジェネレーター v3
If
Set
Code
+
If
Set
Code
102 ノードAlex Kim
人工知能
AI Logoテーブルエクスプローラー to Airtable
フォーム、AI、Google Sheet、Airtableを使用してLogoテーブルから情報を抽出
Set
Code
Html
+
Set
Code
Html
44 ノードMarcel Claus-Ahrens
営業
✨🤖X + Facebook + Instagram + LinkedIn向けの自動化AI駆動型ソーシャルメディアコンテンツファクトリー
✨🤖 AIを使用してマルチプラットフォームでソーシャルメディアコンテンツ作成を自動化
If
Set
Code
+
If
Set
Code
57 ノードJoseph LePage
人工知能
[テンプレート] AIペットショップ v8
AIペットショップアシスタント - GPT-4o、Googleカレンダー、WhatsApp/Instagram/Facebookを統合
If
N8n
Set
+
If
N8n
Set
244 ノードAmanda Benks
営業
🗞️ AIドライブの持続可能性マーケティングブリーフィング(Gmail、GPT-4o使用)
🗞️ AI駆動型持続可能性マーケティングブリーフィング(Gmail、GPT-4oを使用)
If
Set
Code
+
If
Set
Code
21 ノードSamir Saci
人工知能
ワークフロー情報
難易度
上級
ノード数21
カテゴリー2
ノードタイプ10
作成者
@OnePromptMagic
@one-prompt-magicdegree in computer science software dev, specialised in AI/LLM integrations CEO psquared GmbH (psquared.dev) -> we do custom Agent Implementation and process automation (e.g using n8n) not every flow needs AI in it -> only using it very it really provides value.
外部リンク
n8n.ioで表示 →
このワークフローを共有