使用Gemini AI为Discord生成精选本地新闻摘要
高级
这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 20 个节点。主要使用 Code, Sort, Limit, Merge, Discord 等节点。 使用Gemini AI为Discord生成精选本地新闻摘要
前置要求
- •Discord Bot Token 或 Webhook
- •可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"meta": {
"instanceId": "105694f414213a0eca348284005921253960bd1b0223294a4970522d0da53055",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "e5629011-82f2-4107-967f-91659ee7455f",
"name": "每日 8 点触发器",
"type": "n8n-nodes-base.scheduleTrigger",
"notes": "Change the cron expression to adjust schedule. Current: 8AM daily",
"position": [
-400,
48
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * *"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "fb7df187-44df-4e20-bc35-64ef353e6ee0",
"name": "RSS Phoenix New Times",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-192,
-256
],
"parameters": {
"url": "https://www.phoenixnewtimes.com/phoenix/Rss.xml",
"options": {}
},
"typeVersion": 1
},
{
"id": "9d60cd62-960a-451b-bde1-5543eaef63ae",
"name": "RSS AZ Free News",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-192,
-96
],
"parameters": {
"url": "https://azfreenews.com/feed/",
"options": {}
},
"typeVersion": 1
},
{
"id": "6dbf6940-9f45-48be-87f2-1d03727c2f71",
"name": "RSS Reddit Phoenix",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-192,
48
],
"parameters": {
"url": "https://www.reddit.com/r/Phoenix/.rss",
"options": {}
},
"typeVersion": 1
},
{
"id": "6633710a-8742-45f6-b21b-07c800067043",
"name": "RSS Reddit Scottsdale",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-192,
192
],
"parameters": {
"url": "https://www.reddit.com/r/Scottsdale/.rss",
"options": {}
},
"typeVersion": 1
},
{
"id": "b5248dc3-2541-4ee1-8ae8-342c363963f6",
"name": "RSS Reddit Arizona",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-192,
336
],
"parameters": {
"url": "https://www.reddit.com/r/Arizona/.rss",
"options": {}
},
"typeVersion": 1
},
{
"id": "834fa6ed-9b2d-433f-b121-f58ff9dba58b",
"name": "合并所有 RSS 源",
"type": "n8n-nodes-base.merge",
"position": [
16,
48
],
"parameters": {
"numberInputs": 5
},
"typeVersion": 3
},
{
"id": "588b18cf-44a7-42b8-940e-219bb5176cfb",
"name": "去重与准备",
"type": "n8n-nodes-base.code",
"position": [
208,
48
],
"parameters": {
"jsCode": "// Deduplicate articles based on URL and prepare for Gemini scoring\nconst items = $input.all();\nconst uniqueArticles = new Map();\nconst locations = ['Phoenix', 'Scottsdale', 'Paradise Valley', 'Arizona', 'Tempe', 'Mesa', 'Chandler'];\n\n// Process and deduplicate\nfor (const item of items) {\n const url = item.json.link || item.json.url || '';\n const title = item.json.title || '';\n const description = item.json.description || item.json.content || item.json.summary || '';\n const source = item.json.source || 'Unknown';\n const pubDate = item.json.pubDate || item.json.published || new Date().toISOString();\n \n // Create unique key based on URL or title\n const key = url || title;\n \n if (key && !uniqueArticles.has(key)) {\n // Check if article mentions any of our locations\n const fullText = `${title} ${description}`.toLowerCase();\n const mentionsLocation = locations.some(loc => fullText.includes(loc.toLowerCase()));\n \n uniqueArticles.set(key, {\n title: title.substring(0, 200),\n url: url,\n description: description.substring(0, 500),\n source: source,\n pubDate: pubDate,\n mentionsLocation: mentionsLocation\n });\n }\n}\n\n// Convert to array and return\nconst deduplicatedArticles = Array.from(uniqueArticles.values());\n\n// Sort by date (newest first) and filter to most recent 50 for processing\nconst sortedArticles = deduplicatedArticles\n .sort((a, b) => new Date(b.pubDate) - new Date(a.pubDate))\n .slice(0, 50);\n\nreturn sortedArticles.map(article => ({ json: article }));"
},
"typeVersion": 2,
"continueOnFail": true
},
{
"id": "add1c53d-bfb6-4ae7-898d-3205404f96b6",
"name": "为 Gemini 准备",
"type": "n8n-nodes-base.code",
"position": [
416,
48
],
"parameters": {
"jsCode": "// Prepare batch of articles for Gemini scoring - ONLY TITLES for efficiency\nconst articles = $input.all().map(item => item.json);\n\n// Create a text summary for Gemini to score - ONLY TITLES to save tokens\nconst titlesText = articles.map((article, index) => {\n return `${index + 1}. ${article.title}`;\n}).join('\\n');\n\n// Return both the articles and the formatted text\nreturn [{\n json: {\n articles: articles,\n titlesText: titlesText,\n articlesCount: articles.length\n }\n}];"
},
"typeVersion": 2,
"continueOnFail": true
},
{
"id": "8e245ef2-dada-4bdf-b0cf-82f36732ae7e",
"name": "Gemini 相关性评分",
"type": "n8n-nodes-base.httpRequest",
"notes": "REPLACE YOUR_GEMINI_API_KEY_HERE with your actual Gemini API key",
"position": [
608,
48
],
"parameters": {
"url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent?key=YOUR_KEY_HERE",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({\n \"contents\": [{\n \"parts\": [{\n \"text\": \"You are a local news relevance scorer for Phoenix, Scottsdale, and Paradise Valley area. Score the following \" + $json.articlesCount + \" article TITLES based on their local relevance.\\n\\nScore each from 0-100 based on:\\n- Local relevance (50%): Is it about Phoenix/Scottsdale/Paradise Valley?\\n- Community impact (30%): Will locals care?\\n- News value (20%): Is it newsworthy?\\n\\nArticle titles:\\n\" + $json.titlesText + \"\\n\\nReturn ONLY a JSON array with scores in the same order. Example: [85, 72, 45, 90, 33]\\nBe strict - only highly local news should score above 70.\"\n }]\n }],\n \"generationConfig\": {\n \"temperature\": 0.3,\n \"topK\": 40,\n \"topP\": 0.95,\n \"maxOutputTokens\": 512\n }\n}) }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"executeOnce": true,
"typeVersion": 4.2
},
{
"id": "8d17b168-d628-403e-8d02-7c28ef911b04",
"name": "处理分数",
"type": "n8n-nodes-base.code",
"position": [
816,
48
],
"parameters": {
"jsCode": "// Process Gemini response and add scores to articles\nconst geminiResponse = $input.first().json;\nconst preparedData = $('Prepare for Gemini').first().json;\nconst articles = preparedData.articles;\n\n// Extract scores from Gemini response\nlet scores = [];\ntry {\n // Parse the response from Gemini\n const responseText = geminiResponse.candidates[0].content.parts[0].text;\n \n // Try to extract JSON array from the response\n const jsonMatch = responseText.match(/\\[.*\\]/s);\n if (jsonMatch) {\n scores = JSON.parse(jsonMatch[0]);\n } else {\n // Fallback: assign default scores based on location mentions\n scores = articles.map(article => article.mentionsLocation ? 75 : 50);\n }\n} catch (error) {\n console.error('Error parsing Gemini response:', error);\n // Fallback scoring based on location mentions\n scores = articles.map(article => article.mentionsLocation ? 75 : 50);\n}\n\n// Combine articles with scores\nconst scoredArticles = articles.map((article, index) => ({\n ...article,\n relevanceScore: scores[index] || 50\n}));\n\nreturn scoredArticles.map(article => ({ json: article }));"
},
"typeVersion": 2,
"continueOnFail": true
},
{
"id": "339e1098-65ba-4e89-85e1-9edcd6049ec9",
"name": "按相关性排序",
"type": "n8n-nodes-base.sort",
"position": [
1008,
48
],
"parameters": {
"options": {},
"sortFieldsUi": {
"sortField": [
{
"order": "descending",
"fieldName": "relevanceScore"
}
]
}
},
"typeVersion": 1
},
{
"id": "03a00299-fa97-4cb2-8265-d1b0962344a3",
"name": "限制为前 5 条",
"type": "n8n-nodes-base.limit",
"notes": "Change maxItems to adjust how many articles to send (e.g., 15)",
"position": [
1216,
48
],
"parameters": {
"maxItems": 5
},
"typeVersion": 1
},
{
"id": "1e31fe92-f998-4e72-a8a6-388d0b3db8e1",
"name": "格式化 Discord 消息",
"type": "n8n-nodes-base.code",
"notes": "Using RSS descriptions directly - no additional AI summarization to save tokens",
"position": [
1408,
48
],
"parameters": {
"jsCode": "// Collect all incoming articles\nconst articles = $input.all().map(item => item.json);\n\n// Get today's date formatted\nconst today = new Date().toLocaleDateString('en-US', { \n weekday: 'long', \n year: 'numeric', \n month: 'long', \n day: 'numeric' \n});\n\n// Utility: strip HTML tags from text\nfunction stripHtml(html) {\n return html\n .replace(/<[^>]*>/g, '') // remove HTML tags\n .replace(/\\s+/g, ' ') // collapse whitespace\n .trim();\n}\n\n// Build the full digest string\nlet discordMessage = `🌵 **LOCAL NEWS DIGEST** 🌵\\n📅 ${today}\\n${'━'.repeat(40)}\\n\\n`;\n\narticles.forEach((article, index) => {\n const emoji = index === 0 ? '🥇' : index === 1 ? '🥈' : index === 2 ? '🥉' : `${index + 1}.`;\n \n const description = stripHtml(article.description || 'No description available');\n\n discordMessage += `${emoji} **${article.title}**\\n`;\n discordMessage += `📍 ${description}\\n`;\n discordMessage += `🔗 ${article.url}\\n\\n`;\n});\n\ndiscordMessage += `${'━'.repeat(40)}\\n`;\ndiscordMessage += `_Powered by n8n automation | Phoenix • Scottsdale • Paradise Valley_`;\n\n// Discord has a 2000 character limit, so split into multiple messages if necessary\nconst maxLength = 1900; // leave buffer for safety\nconst messages = [];\n\nfor (let i = 0; i < discordMessage.length; i += maxLength) {\n messages.push(discordMessage.slice(i, i + maxLength));\n}\n\n// Return multiple items (Discord node will send each separately)\nreturn messages.map(msg => ({\n json: { content: msg }\n}));\n"
},
"typeVersion": 2,
"continueOnFail": true
},
{
"id": "6b23ead3-4d35-451f-b0f2-7e5146dd26a1",
"name": "发送到 Discord",
"type": "n8n-nodes-base.discord",
"notes": "REPLACE with your actual Discord webhook URL",
"position": [
1616,
48
],
"webhookId": "5565d2a2-5e59-43c4-8733-d6b1ca51f509",
"parameters": {
"content": "={{ $json.content }}",
"options": {},
"authentication": "webhook"
},
"credentials": {
"discordWebhookApi": {
"id": "6S341y8tVCvOIO8r",
"name": "All Ops Notis Discord Webhook"
}
},
"typeVersion": 2,
"continueOnFail": true
},
{
"id": "9983e297-ee82-4fc9-bd87-74f46857afaf",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
528,
-336
],
"parameters": {
"height": 304,
"content": "## 设置您的 Gemini API 密钥。免费获取!"
},
"typeVersion": 1
},
{
"id": "0cb7e7cd-9818-40fd-9444-7a040fe054bf",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-256,
-496
],
"parameters": {
"height": 208,
"content": "## 在此处设置您的新闻源"
},
"typeVersion": 1
},
{
"id": "5e83db58-6f66-4e74-b355-e7d32ff6649d",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1136,
272
],
"parameters": {
"height": 112,
"content": "## 设置您想要多少篇文章。"
},
"typeVersion": 1
},
{
"id": "ef2042de-6862-4981-b195-2dfc11d68df6",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1328,
-224
],
"parameters": {
"height": 192,
"content": "## 对于其他发送位置(如 Telegram、WhatsApp 等)可能需要更改。"
},
"typeVersion": 1
},
{
"id": "023096cc-74b9-4c66-91ad-9102f6ba209c",
"name": "便签4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
224
],
"parameters": {
"height": 112,
"content": "## 接收您的相关新闻文章"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"Limit to Top 5": {
"main": [
[
{
"node": "Format Discord Message",
"type": "main",
"index": 0
}
]
]
},
"Process Scores": {
"main": [
[
{
"node": "Sort by Relevance",
"type": "main",
"index": 0
}
]
]
},
"RSS AZ Free News": {
"main": [
[
{
"node": "Merge All RSS Feeds",
"type": "main",
"index": 1
}
]
]
},
"Daily 8AM Trigger": {
"main": [
[
{
"node": "RSS Phoenix New Times",
"type": "main",
"index": 0
},
{
"node": "RSS AZ Free News",
"type": "main",
"index": 0
},
{
"node": "RSS Reddit Phoenix",
"type": "main",
"index": 0
},
{
"node": "RSS Reddit Scottsdale",
"type": "main",
"index": 0
},
{
"node": "RSS Reddit Arizona",
"type": "main",
"index": 0
}
]
]
},
"Sort by Relevance": {
"main": [
[
{
"node": "Limit to Top 5",
"type": "main",
"index": 0
}
]
]
},
"Prepare for Gemini": {
"main": [
[
{
"node": "Gemini Relevance Scoring",
"type": "main",
"index": 0
}
]
]
},
"RSS Reddit Arizona": {
"main": [
[
{
"node": "Merge All RSS Feeds",
"type": "main",
"index": 4
}
]
]
},
"RSS Reddit Phoenix": {
"main": [
[
{
"node": "Merge All RSS Feeds",
"type": "main",
"index": 2
}
]
]
},
"Merge All RSS Feeds": {
"main": [
[
{
"node": "Deduplicate & Prepare",
"type": "main",
"index": 0
}
]
]
},
"Deduplicate & Prepare": {
"main": [
[
{
"node": "Prepare for Gemini",
"type": "main",
"index": 0
}
]
]
},
"RSS Phoenix New Times": {
"main": [
[
{
"node": "Merge All RSS Feeds",
"type": "main",
"index": 0
}
]
]
},
"RSS Reddit Scottsdale": {
"main": [
[
{
"node": "Merge All RSS Feeds",
"type": "main",
"index": 3
}
]
]
},
"Format Discord Message": {
"main": [
[
{
"node": "Send to Discord",
"type": "main",
"index": 0
}
]
]
},
"Gemini Relevance Scoring": {
"main": [
[
{
"node": "Process Scores",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 内容创作, 多模态 AI
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
ETH钱包监测器
使用Etherscan、CoinGecko价格和Discord提醒的每日ETH钱包监测
Code
Discord
Http Request
+2
13 节点Kaden Reese
内容创作
从 Mortgage News Daily 发送每日抵押贷款利率更新到消息平台
从 Mortgage News Daily 发送每日抵押贷款利率更新到消息平台
Code
Discord
Http Request
+3
10 节点Kaden Reese
内容创作
灵活新闻聚合器 - 多源集成、AI分析和可设置频道
多源新闻策展系统,集成Mistral AI分析、摘要和自定义频道
If
Set
Xml
+32
120 节点Hybroht
内容创作
AI驱动视频创作与上传至Instagram、TikTok和YouTube
从云端硬盘进行AI驱动视频创作并上传至Instagram、TikTok和YouTube
If
Set
Code
+14
53 节点DevCode Journey
内容创作
内容生成器 v3
AI驱动博客自动化:使用GPT-4生成并发布SEO文章至WordPress和Twitter
If
Set
Code
+25
144 节点Jay Emp0
内容创作
使用Groq、Gemini和Slack审批系统自动化RSS到Medium发布
通过Groq、Gemini和Slack审批系统实现RSS到Medium发布的自动化流程
If
Set
Code
+16
41 节点ObisDev
内容创作
工作流信息
难度等级
高级
节点数量20
分类2
节点类型9
作者
Kaden Reese
@kadenreeseI started automating with Python in 2020 and still use it in workflows when needed, but I’ve recently leaned into n8n for client-facing solutions. Lately I’ve focused on real estate automations, though I also build workflows for email, scraping, and other use cases. Currently Building 👇🏻
外部链接
在 n8n.io 查看 →
分享此工作流