企业在线形象监测器
高级
这是一个Market Research, AI Summarization领域的自动化工作流,包含 17 个节点。主要使用 Set, Cron, Gmail, OpenAi, Reddit 等节点。 使用AI情感分析和多平台追踪的每日企业在线形象监测
前置要求
- •Google 账号和 Gmail API 凭证
- •OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
无法加载工作流预览
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"nodes": [
{
"name": "每日早晨触发器(上午9点)",
"type": "n8n-nodes-base.cron",
"notes": {
"text": "### 1. Daily Morning Trigger (9 AM)\n\nThis `Cron` node triggers the workflow automatically every **day at 9:00 AM** (based on your n8n server's local time zone). This provides a regular daily digest of activity.\n\n**To change the schedule:** Adjust the 'Hour' and 'Minute' fields to your preferred time for receiving the report.",
"position": "right"
},
"position": [
240,
300
],
"parameters": {
"mode": "everyDay",
"value": {
"hour": [
9
],
"minute": [
0
]
},
"options": {}
},
"typeVersion": 1
},
{
"name": "设置公司详情",
"type": "n8n-nodes-base.set",
"notes": {
"text": "### 2. Set Company Details\n\nThis `Set` node defines the company you want to monitor and the keywords to search for.\n\n**Setup (CRITICAL!):**\n1. **`companyName`:** Change `YourCompanyNameHere` to the exact name of the company you are monitoring (e.g., 'Google', 'Coca-Cola'). This is used for the email subject and AI prompts.\n2. **`searchKeywords`:** List all relevant keywords, including common misspellings, product names, hashtags, or related terms. These will be used for searching news, Reddit, and YouTube. Example: `[\"Acme Corp\", \"AcmeCo\", \"#AcmeCorp\", \"AcmeSoftware\"]`",
"position": "right"
},
"position": [
480,
300
],
"parameters": {
"values": [
{
"name": "companyName",
"value": "YourCompanyNameHere"
},
{
"name": "searchKeywords",
"value": [
"YourCompanyNameHere",
"YourCompanyProduct",
"#YourCompanyTag"
]
}
],
"options": {}
},
"typeVersion": 2
},
{
"name": "获取Google新闻RSS",
"type": "n8n-nodes-base.rssFeed",
"notes": {
"text": "### 3.1. Fetch Google News RSS\n\nThis `RSS Feed` node fetches news articles from Google News that mention your `companyName`.\n\n**Setup:**\n1. **URL:** It's pre-configured to search Google News using your `companyName`. You can adjust the `hl` (host language) and `gl` (geo location) parameters if needed.\n\n**Note:** Google News RSS is a good general news source, but it might not capture every single article across all sites.",
"position": "right"
},
"position": [
720,
100
],
"parameters": {
"url": "=https://news.google.com/rss/search?q={{ encodeURIComponent($node[\"Set Company Details\"].json.companyName) }}&hl=en-US&gl=US&ceid=US:en",
"options": {}
},
"typeVersion": 1
},
{
"name": "准备合并的新闻",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 3.2. Prepare News for Merge\n\nThis `Function` node standardizes the news articles' data into a consistent format (`source`, `title`, `text`, `link`, `publishedAt`) for later merging and AI processing.\n\n**No configuration needed.**",
"position": "right"
},
"position": [
960,
100
],
"parameters": {
"options": {},
"function": "const preparedItems = [];\n\nfor (const item of items) {\n preparedItems.push({\n json: {\n source: 'News Article',\n title: item.json.title || 'N/A',\n text: item.json.contentSnippet || item.json.description || 'N/A',\n link: item.json.link || '#',\n publishedAt: item.json.isoDate || new Date().toISOString()\n }\n });\n}\n\nreturn preparedItems;"
},
"typeVersion": 1
},
{
"name": "搜索 Reddit 帖子",
"type": "n8n-nodes-base.reddit",
"notes": {
"text": "### 3.3. Search Reddit Posts\n\nThis `Reddit` node searches for posts containing your `searchKeywords`.\n\n**Setup:**\n1. **Reddit Credential:** Click 'Credentials' and select 'New Credential'. Choose 'Reddit OAuth2 API'. Follow n8n documentation for detailed steps to create a Reddit app (type 'script') and get your client ID and secret. You'll also need your Reddit username/password for authentication.\n2. **Query:** It's pre-configured to search using your `searchKeywords` joined by ' OR '.\n3. **Limit & Sort:** Adjust the number of results (`limit`) and `sort` order as needed.\n\n**Note:** Reddit API has rate limits, so be mindful of how often you run this if you increase the limit significantly.",
"position": "right"
},
"position": [
720,
300
],
"parameters": {
"query": "={{ $node[\"Set Company Details\"].json.searchKeywords.join(' OR ') }}",
"options": {
"sort": "hot",
"limit": 20
},
"resource": "post",
"operation": "search"
},
"credentials": {
"redditOAuth2Api": {
"id": "YOUR_REDDIT_CREDENTIAL_ID",
"resolve": false
}
},
"typeVersion": 1
},
{
"name": "准备合并的 Reddit",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 3.4. Prepare Reddit for Merge\n\nThis `Function` node standardizes the Reddit posts' data, ensuring consistent `source`, `title`, `text`, `link`, and `publishedAt` fields for merging.\n\n**No configuration needed.**",
"position": "right"
},
"position": [
960,
300
],
"parameters": {
"options": {},
"function": "const preparedItems = [];\n\nfor (const item of items) {\n preparedItems.push({\n json: {\n source: 'Reddit Post',\n title: item.json.title || 'N/A',\n text: item.json.selftext || item.json.body || 'N/A',\n link: item.json.url || '#',\n publishedAt: new Date(item.json.created_utc * 1000).toISOString() // Convert Unix timestamp to ISO string\n }\n });\n}\n\nreturn preparedItems;"
},
"typeVersion": 1
},
{
"name": "搜索 YouTube 视频",
"type": "n8n-nodes-base.youTube",
"notes": {
"text": "### 3.5. Search YouTube Videos\n\nThis `YouTube` node searches for videos mentioning your `searchKeywords`.\n\n**Setup:**\n1. **Google Credential:** Click 'Credentials' and select 'New Credential'. Choose 'Google OAuth2 API'. You'll need to enable the YouTube Data API v3 in your Google Cloud Project and configure OAuth2 credentials (Client ID/Secret) for desktop app/other. This can be complex; search n8n docs for 'Google OAuth2' for detailed steps.\n2. **Search:** Pre-configured to use your `searchKeywords`.\n3. **Limit & Order:** Adjust the number of results and order as needed.\n\n**Note:** YouTube API has quotas, so excessive searches might lead to temporary limits.",
"position": "right"
},
"position": [
720,
500
],
"parameters": {
"search": "={{ $node[\"Set Company Details\"].json.searchKeywords.join(' ') }}",
"options": {
"limit": 10,
"order": "relevance"
},
"resource": "video",
"operation": "list"
},
"credentials": {
"googleApi": {
"id": "YOUR_GOOGLE_API_CREDENTIAL_ID",
"resolve": false
}
},
"typeVersion": 1
},
{
"name": "准备合并的 YouTube",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 3.6. Prepare YouTube for Merge\n\nThis `Function` node standardizes YouTube video data into the common format (`source`, `title`, `text`, `link`, `publishedAt`) for merging.\n\n**No configuration needed.**",
"position": "right"
},
"position": [
960,
500
],
"parameters": {
"options": {},
"function": "const preparedItems = [];\n\nfor (const item of items) {\n preparedItems.push({\n json: {\n source: 'YouTube Video',\n title: item.json.snippet.title || 'N/A',\n text: item.json.snippet.description || 'N/A',\n link: `https://www.youtube.com/watch?v=${item.json.id.videoId}` || '#',\n publishedAt: item.json.snippet.publishedAt || new Date().toISOString()\n }\n });\n}\n\nreturn preparedItems;"
},
"typeVersion": 1
},
{
"name": "合并所有提及",
"type": "n8n-nodes-base.itemLists",
"notes": {
"text": "### 4. Merge All Mentions\n\nThis `Item Lists` node combines all prepared data from Google News, Reddit, and YouTube into a single, unified list. This is the stream of all potential mentions.\n\n**No configuration needed.**",
"position": "right"
},
"position": [
1200,
300
],
"parameters": {
"mode": "merge",
"options": {}
},
"typeVersion": 1
},
{
"name": "SQLite:确保表存在",
"type": "n8n-nodes-base.sqlite",
"notes": {
"text": "### 5.1. SQLite: Ensure Table Exists\n\nThis `SQLite` node ensures a local database table named `processed_mentions` exists. This table will store a hash of every mention seen, preventing duplicate notifications over time.\n\n**Setup:**\n* **Database:** `company_monitor` (this creates a file `company_monitor.db` in your n8n data directory).\n* **Query:** The `CREATE TABLE IF NOT EXISTS` query is pre-filled.\n\n**No further action needed**; this node runs automatically.",
"position": "right"
},
"position": [
1440,
220
],
"parameters": {
"query": "CREATE TABLE IF NOT EXISTS processed_mentions (link_hash TEXT PRIMARY KEY, source TEXT, title TEXT, link TEXT, processed_date TEXT)",
"database": "company_monitor"
},
"typeVersion": 1
},
{
"name": "过滤新提及(去重)",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 5.2. Filter New Mentions (Deduplication)\n\nThis `Function` node generates a unique hash for each mention (based on its link/title) and then checks the `processed_mentions` database to see if it's already been seen. **Only truly new mentions are passed on.**\n\n**Setup:**\n* It uses `crypto` (built-in) for hashing.\n* It executes the 'SQLite: Check If Processed' node for each item.\n\n**Important:** If you change the `SQLite: Check If Processed` node's name, update it here. Also, ensure 'Run Once Per Item' is OFF for this Function node.",
"position": "right"
},
"position": [
1440,
380
],
"parameters": {
"options": {
"runOncePerItem": false
},
"function": "const crypto = require('crypto');\n\nconst newItems = [];\n\nfor (const item of items) {\n const link = item.json.link || item.json.title; // Use link or title as primary unique identifier\n const linkHash = crypto.createHash('md5').update(link).digest('hex');\n\n // Add the hash to the item for later use\n item.json.linkHash = linkHash;\n\n // Check if this item (based on linkHash) is already in the database\n const result = await n8n.getNodeParameter('SQLite: Check If Processed', 'query', { json: item.json }, { execute: true });\n\n if (result.length === 0) {\n // Item not found in DB, it's new\n newItems.push(item);\n }\n}\n\nreturn newItems;"
},
"typeVersion": 1
},
{
"name": "SQLite:检查是否已处理",
"type": "n8n-nodes-base.sqlite",
"notes": {
"text": "### (Helper) SQLite: Check If Processed\n\nThis helper `SQLite` node is called by the 'Filter New Mentions' Function node to check if a specific `link_hash` already exists in the database.\n\n**No direct configuration needed**; it's managed by the Function node.",
"position": "right"
},
"position": [
1700,
380
],
"parameters": {
"query": "SELECT link_hash FROM processed_mentions WHERE link_hash = '{{ $json.linkHash }}'",
"database": "company_monitor"
},
"typeVersion": 1
},
{
"name": "AI:分析情感并总结",
"type": "n8n-nodes-base.openAi",
"notes": {
"text": "### 6. AI: Analyze Sentiment & Summarize\n\nThis `OpenAI` node processes each *new* mention to determine its sentiment and generate a brief summary.\n\n**Setup:**\n1. **OpenAI Credential:** Select your OpenAI API Key credential.\n2. **Model:** `gpt-3.5-turbo` is selected. For more robust analysis, consider `gpt-4o`.\n3. **Prompt:** The system prompt guides the AI to output sentiment and summary in a structured JSON format, making it easy to parse in the next step.\n\n**Output:** Each item will have a `choices` field containing the AI's JSON output.",
"position": "right"
},
"position": [
1920,
300
],
"parameters": {
"model": "gpt-3.5-turbo",
"options": {},
"messages": [
{
"role": "system",
"content": "You are a sentiment analyzer and summarizer for company mentions. For the following text related to '{{ $node[\"Set Company Details\"].json.companyName }}', determine its overall sentiment (Positive, Negative, or Neutral) and provide a concise 2-sentence summary. Output in JSON format:\n\n{\n \"sentiment\": \"[Positive/Negative/Neutral]\",\n \"summary\": \"[Concise summary]\"\n}"
},
{
"role": "user",
"content": "Source: {{ $json.source }}\\nTitle: {{ $json.title }}\\nText: {{ $json.text }}"
}
]
},
"credentials": {
"openAiApi": {
"id": "YOUR_OPENAI_CREDENTIAL_ID",
"resolve": false
}
},
"typeVersion": 1
},
{
"name": "处理 AI 结果并分类",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 7. Process AI Results & Categorize\n\nThis `Function` node parses the AI's JSON output and categorizes all *new* mentions by their sentiment (Positive, Negative, Neutral). It also handles potential AI parsing errors.\n\n**Output:** A single item containing categorized lists of new mentions (e.g., `positive: [...]`, `negative: [...]`).\n\n**No configuration needed.**",
"position": "right"
},
"position": [
2160,
300
],
"parameters": {
"options": {},
"function": "const results = {\n positive: [],\n neutral: [],\n negative: [],\n noAnalysis: []\n};\n\nfor (const item of items) {\n try {\n const aiOutput = JSON.parse(item.json.choices[0].message.content);\n const sentiment = aiOutput.sentiment ? aiOutput.sentiment.toLowerCase() : 'unknown';\n const summary = aiOutput.summary || 'No summary provided.';\n\n const data = {\n source: item.json.source,\n title: item.json.title,\n link: item.json.link,\n summary: summary,\n publishedAt: item.json.publishedAt,\n linkHash: item.json.linkHash // Pass the hash for database insertion\n };\n\n if (sentiment === 'positive') {\n results.positive.push(data);\n } else if (sentiment === 'negative') {\n results.negative.push(data);\n } else if (sentiment === 'neutral') {\n results.neutral.push(data);\n } else {\n results.noAnalysis.push(data);\n }\n } catch (e) {\n results.noAnalysis.push({ ...item.json, summary: `AI analysis failed: ${e.message}` });\n }\n}\n\nreturn [{ json: results }];"
},
"typeVersion": 1
},
{
"name": "SQLite:记录已处理的提及",
"type": "n8n-nodes-base.sqlite",
"notes": {
"text": "### 8. SQLite: Record Processed Mentions\n\nThis `SQLite` node records all the *newly processed* mentions into your `processed_mentions` database. This is crucial for the deduplication step in future runs.\n\n**Setup:**\n* **Database:** `company_monitor`\n* **Query:** The `INSERT INTO` query is pre-filled, storing the unique hash and other details of the processed mention.\n\n**No further action needed**; it automatically stores the data.",
"position": "right"
},
"position": [
2400,
220
],
"parameters": {
"query": "INSERT INTO processed_mentions (link_hash, source, title, link, processed_date) VALUES ('{{ $json.linkHash }}', '{{ $json.source }}', '{{ $json.title }}', '{{ $json.link }}', '{{ new Date().toISOString() }}')",
"database": "company_monitor"
},
"typeVersion": 1
},
{
"name": "格式化报告邮件",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 9. Format Report Email\n\nThis `Function` node constructs the final email body. It groups mentions by sentiment and provides summaries and links for each. It also handles the case where no *new* mentions were found.\n\n**Customization:**\n* You can adjust the headings, introduction, or the level of detail for each mention.\n* The email body uses Markdown for formatting (bolding, lists).\n\n**No configuration needed.**",
"position": "right"
},
"position": [
2400,
380
],
"parameters": {
"options": {},
"function": "const data = items[0].json;\nconst companyName = $node[\"Set Company Details\"].json.companyName;\n\nlet emailBody = `Good morning! Here's your daily online presence report for **${companyName}**.\\n\\n`;\n\n// Helper to format a list of mentions\nconst formatMentions = (mentions, heading) => {\n if (mentions.length === 0) return '';\n\n let formatted = `### ${heading} (${mentions.length} mentions)\\n`;\n mentions.forEach(m => {\n formatted += `* **[${m.source}]** ${m.title}\\n` +\n ` Summary: ${m.summary}\\n` +\n ` Link: ${m.link}\\n` +\n ` (Published: ${new Date(m.publishedAt).toLocaleDateString()})\\n\\n`;\n });\n return formatted;\n};\n\nemailBody += formatMentions(data.positive, 'Positive Mentions');\nemailBody += formatMentions(data.neutral, 'Neutral Mentions');\nemailBody += formatMentions(data.negative, 'Negative Mentions');\nemailBody += formatMentions(data.noAnalysis, 'Mentions with No Analysis / Error');\n\n\n// Check if any new mentions were processed\nif (data.positive.length + data.neutral.length + data.negative.length + data.noAnalysis.length === 0) {\n emailBody = `Good morning! No *new* online mentions found for **${companyName}** today.`;\n}\n\nreturn [{ json: { emailSubject: `📊 Daily Online Presence Report for ${companyName}`, emailBody: emailBody } }];"
},
"typeVersion": 1
},
{
"name": "发送报告邮件",
"type": "n8n-nodes-base.gmail",
"notes": {
"text": "### 10. Send Report Email\n\nThis `Gmail` node sends the compiled daily online presence report to your mailbox.\n\n**Setup:**\n1. **Gmail Credential:** Select your Gmail API credential.\n2. **From Email:** Enter your Gmail address (must match the authenticated account).\n3. **To Email:** **IMPORTANT: Change `YOUR_RECIPIENT_EMAIL@example.com` to your actual email address!**\n4. **Subject & Text:** These fields pull the formatted subject and body from the previous 'Format Report Email' node.\n\n**Test this node by running the workflow** to ensure you receive the email.",
"position": "right"
},
"position": [
2640,
300
],
"parameters": {
"text": "={{ $json.emailBody }}",
"options": {},
"subject": "={{ $json.emailSubject }}",
"toEmail": "YOUR_RECIPIENT_EMAIL@example.com",
"fromEmail": "YOUR_GMAIL_EMAIL@gmail.com"
},
"credentials": {
"gmailApi": {
"id": "YOUR_GMAIL_CREDENTIAL_ID",
"resolve": false
}
},
"typeVersion": 2
}
],
"pinData": {},
"version": 1,
"connections": {
"Merge All Mentions": {
"main": [
[
{
"node": "Filter New Mentions (Deduplication)",
"type": "main"
}
]
]
},
"Format Report Email": {
"main": [
[
{
"node": "Send Report Email",
"type": "main"
}
]
]
},
"Search Reddit Posts": {
"main": [
[
{
"node": "Prepare Reddit for Merge",
"type": "main"
}
]
]
},
"Set Company Details": {
"main": [
[
{
"node": "Fetch Google News RSS",
"type": "main"
},
{
"node": "Search Reddit Posts",
"type": "main"
},
{
"node": "Search YouTube Videos",
"type": "main"
}
]
]
},
"Fetch Google News RSS": {
"main": [
[
{
"node": "Prepare News for Merge",
"type": "main"
}
]
]
},
"Search YouTube Videos": {
"main": [
[
{
"node": "Prepare YouTube for Merge",
"type": "main"
}
]
]
},
"Prepare News for Merge": {
"main": [
[
{
"node": "Merge All Mentions",
"type": "main"
}
]
]
},
"Prepare Reddit for Merge": {
"main": [
[
{
"node": "Merge All Mentions",
"type": "main"
}
]
]
},
"Prepare YouTube for Merge": {
"main": [
[
{
"node": "Merge All Mentions",
"type": "main"
}
]
]
},
"SQLite: Ensure Table Exists": {
"main": [
[
{
"node": "Filter New Mentions (Deduplication)",
"type": "main"
}
]
]
},
"Daily Morning Trigger (9 AM)": {
"main": [
[
{
"node": "Set Company Details",
"type": "main"
}
]
]
},
"Process AI Results & Categorize": {
"main": [
[
{
"node": "SQLite: Record Processed Mentions",
"type": "main"
},
{
"node": "Format Report Email",
"type": "main"
}
]
]
},
"AI: Analyze Sentiment & Summarize": {
"main": [
[
{
"node": "Process AI Results & Categorize",
"type": "main"
}
]
]
},
"SQLite: Record Processed Mentions": {
"main": [
[]
]
},
"Filter New Mentions (Deduplication)": {
"main": [
[
{
"node": "AI: Analyze Sentiment & Summarize",
"type": "main"
}
]
],
"output": [
{
"type": "item",
"toIndex": 0,
"fromIndex": 0,
"destination": [
{
"node": "SQLite: Check If Processed",
"input": "input1"
}
]
}
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 市场调研, AI 摘要总结
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
使用OpenAI和Gmail的每日积极新闻摘要
使用OpenAI和Gmail的每日积极新闻摘要
If
Cron
Gmail
+3
9 节点Piotr Sobolewski
个人效率
AI驱动的产品研究与价格比较 - Google搜索和OpenAI
基于Google搜索和OpenAI的AI驱动产品研究与价格比较
Set
Gmail
Open Ai
+3
8 节点Piotr Sobolewski
市场调研
使用Gmail和GPT摘要自动发送每日邮件摘要
使用Gmail和GPT摘要自动发送每日邮件摘要,每天下午发送
Cron
Gmail
Open Ai
+1
6 节点Piotr Sobolewski
个人效率
自动化多平台游戏优惠追踪
使用 Deku Deals 和 Gmail 提醒自动追踪多平台游戏优惠
If
Cron
Gmail
+5
11 节点Piotr Sobolewski
个人效率
自动化网页爬虫:细分职位/产品监控与Telegram警报
自动化网页爬虫:细分职位/产品监控与Telegram警报
If
Cron
Function
+3
6 节点Piotr Sobolewski
市场调研
使用OpenAI和Gmail生成播客转录摘要和关键词
使用OpenAI和Gmail生成播客转录摘要和关键词
Set
Gmail
Open Ai
+1
6 节点Piotr Sobolewski
内容创作
工作流信息
难度等级
高级
节点数量17
分类2
节点类型10
作者
Piotr Sobolewski
@piotrsobolewskiAI PhD with 7 years experience as a game dev CEO, currently teaching, helping others and building something new.
外部链接
在 n8n.io 查看 →
分享此工作流