使用GPT-4自动化从ClickUp到Slack和Gmail的每日晨报生成
高级
这是一个自动化工作流,包含 27 个节点。主要使用 If, Code, Gmail, Slack, ClickUp 等节点。 使用GPT-4o从ClickUp生成AI驱动的晨报并发送至Slack和Gmail
前置要求
- •Google 账号和 Gmail API 凭证
- •Slack Bot Token 或 Webhook URL
- •OpenAI API Key
使用的节点 (27)
分类
-
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "GUsJXoE0d02fP7ii",
"meta": {
"instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
"templateCredsSetupCompleted": true
},
"name": "使用 GPT-4 自动化从 ClickUp 到 Slack 和 Gmail 的每日晨报生成",
"tags": [],
"nodes": [
{
"id": "2bf648b3-58dc-4003-b682-9d35749f3c9f",
"name": "注意:工作流概览",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4032,
-384
],
"parameters": {
"color": 4,
"width": 454,
"height": 794,
"content": "## 🌅 使用 GPT-4 自动化从 ClickUp 到 Slack 和 Gmail 的每日晨报生成"
},
"typeVersion": 1
},
{
"id": "92a7dff2-bade-4ad8-884c-c6d6d78276c8",
"name": "注意:计划触发器",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3488,
-512
],
"parameters": {
"width": 276,
"height": 362,
"content": "## ⏰ 计划触发器"
},
"typeVersion": 1
},
{
"id": "351c23ff-37f5-4305-888b-9d41cd6af400",
"name": "触发器:早晨计划",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-3344,
-80
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "15 9 * * *"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "788b9c85-c74a-4533-8552-7f1b6523516b",
"name": "注意:获取冲刺",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3168,
-576
],
"parameters": {
"width": 285,
"height": 451,
"content": "## 📋 获取冲刺列表"
},
"typeVersion": 1
},
{
"id": "22bc18f9-01ab-4674-ba53-c72f7c71685c",
"name": "获取所有列表(冲刺)",
"type": "n8n-nodes-base.clickUp",
"position": [
-3120,
-80
],
"parameters": {
"team": "YOUR_TEAM_ID",
"limit": 100,
"space": "YOUR_SPACE_ID",
"folder": "YOUR_FOLDER_ID",
"filters": {},
"resource": "list",
"operation": "getAll",
"authentication": "oAuth2"
},
"credentials": {
"clickUpOAuth2Api": {
"id": "6UikDwF6BMx3ln9O",
"name": "ClickUp account"
}
},
"typeVersion": 1
},
{
"id": "cfc16d55-b75a-4801-8620-cc5aab3acf46",
"name": "注意:验证数据",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2816,
-512
],
"parameters": {
"width": 249,
"height": 367,
"content": "## ✅ 验证检查"
},
"typeVersion": 1
},
{
"id": "ff0eb47a-246d-4aed-8760-a09020487bbb",
"name": "检查列表是否存在",
"type": "n8n-nodes-base.if",
"position": [
-2896,
-80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "boolean",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.isNotEmpty() }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2
},
{
"id": "4c9545e5-eda5-444e-bceb-6d61d7f06648",
"name": "注意:冲刺逻辑",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2736,
96
],
"parameters": {
"width": 263,
"height": 388,
"content": "## 🔍 查找活跃冲刺"
},
"typeVersion": 1
},
{
"id": "997dcb26-240f-4ed0-86bb-9bcb302aaa47",
"name": "查找最新冲刺",
"type": "n8n-nodes-base.code",
"position": [
-2672,
-80
],
"parameters": {
"jsCode": "// Get currently active sprint or the latest one\nconst lists = $input.all();\nconst now = Date.now();\n\n// First, try to find active sprints (current time between start and due date)\nconst activeSprints = lists.filter(list => {\n const startDate = parseInt(list.json.start_date);\n const dueDate = parseInt(list.json.due_date);\n return startDate <= now && now <= dueDate;\n});\n\nlet latestSprint;\n\nif (activeSprints.length > 0) {\n // If we have active sprints, get the most recent one by start date\n latestSprint = activeSprints.sort((a, b) => \n parseInt(b.json.start_date) - parseInt(a.json.start_date)\n )[0];\n} else {\n // No active sprints, get the most recent by start date (could be future sprint)\n latestSprint = lists.sort((a, b) => \n parseInt(b.json.start_date) - parseInt(a.json.start_date)\n )[0];\n}\n\nreturn {\n json: {\n latest_sprint_id: latestSprint.json.id,\n latest_sprint_name: latestSprint.json.name,\n latest_sprint_start_date: latestSprint.json.start_date,\n latest_sprint_due_date: latestSprint.json.due_date,\n is_currently_active: activeSprints.length > 0,\n total_sprints_found: lists.length\n }\n};"
},
"typeVersion": 2
},
{
"id": "46cc6963-ea8a-4bb5-ac09-de10816754e9",
"name": "注意:任务检索",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2544,
-560
],
"parameters": {
"width": 259,
"height": 390,
"content": "## 📥 获取冲刺任务"
},
"typeVersion": 1
},
{
"id": "18dd7a8f-caad-4a73-b334-6d6140638834",
"name": "从最新冲刺获取任务",
"type": "n8n-nodes-base.clickUp",
"position": [
-2448,
-80
],
"parameters": {
"list": "={{ $json.latest_sprint_id }}",
"team": "YOUR_TEAM_ID",
"space": "YOUR_SPACE_ID",
"folder": "={{ $json.latest_sprint_name }}",
"filters": {
"subtasks": true,
"dueDateGt": "={{ new Date().setHours(0, 0, 0, 0) }}",
"dueDateLt": "={{ new Date().setHours(23, 59, 59, 999) }}"
},
"operation": "getAll",
"authentication": "oAuth2"
},
"credentials": {
"clickUpOAuth2Api": {
"id": "6UikDwF6BMx3ln9O",
"name": "ClickUp account"
}
},
"typeVersion": 1
},
{
"id": "2d279459-7101-408e-8a79-10030d627705",
"name": "注意:数据处理",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2304,
96
],
"parameters": {
"width": 264,
"height": 313,
"content": "## 📊 格式化任务数据"
},
"typeVersion": 1
},
{
"id": "d76873cc-ba60-4cbe-9163-05e7b90baf26",
"name": "格式化:撰写简报数据",
"type": "n8n-nodes-base.code",
"position": [
-2224,
-80
],
"parameters": {
"jsCode": "// Get all inputs dynamically\nconst allInputs = $input.all();\n\n// Extract ClickUp data from all items\nconst clickupData = allInputs.map(item => item.json).filter(item => item && item.id);\n\nconst highPriority = [];\nconst normalPriority = [];\nconst blockers = [];\nconst urgentTasks = [];\nconst inProgress = [];\n\nclickupData.forEach(task => {\n // Extract checklist items as subtasks\n const subtasks = [];\n if (task.checklists && task.checklists.length > 0) {\n task.checklists.forEach(checklist => {\n if (checklist.items && checklist.items.length > 0) {\n checklist.items.forEach(item => {\n subtasks.push({\n name: item.name,\n resolved: item.resolved,\n assignee: item.assignee ? 'Assigned' : 'Unassigned'\n });\n });\n }\n });\n }\n\n const taskInfo = {\n name: task.name || 'Untitled',\n status: task.status?.status || 'Unknown',\n statusType: task.status?.type || 'unknown',\n priority: task.priority?.priority || 'normal',\n url: task.url || '',\n assignees: task.assignees?.map(a => a.username).join(', ') || 'Unassigned',\n list: task.list?.name || '',\n folder: task.folder?.name || task.project?.name || '',\n dueDate: task.due_date ? new Date(parseInt(task.due_date)).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : 'No due date',\n timeSpent: task.time_spent ? `${Math.floor(task.time_spent / 60000)} min` : null,\n description: task.description ? (task.description.length > 100 ? task.description.substring(0, 100) + '...' : task.description) : null,\n subtasks: subtasks,\n subtasksSummary: subtasks.length > 0 ? `${subtasks.filter(s => s.resolved).length}/${subtasks.length} completed` : null\n };\n \n // Categorize by status first (in progress)\n if (task.status?.status?.toLowerCase().includes('progress')) {\n inProgress.push(taskInfo);\n }\n // Check for blockers\n else if (task.status?.status?.toLowerCase().includes('block')) {\n blockers.push(taskInfo);\n } \n // Then by priority\n else if (task.priority?.priority === 'urgent') {\n urgentTasks.push(taskInfo);\n } \n else if (task.priority?.priority === 'high') {\n highPriority.push(taskInfo);\n } \n else {\n normalPriority.push(taskInfo);\n }\n});\n\n// Dynamically get board URL from first task\nconst firstTask = clickupData[0];\nconst boardUrl = firstTask?.list?.id \n ? `https://app.clickup.com/${firstTask.team_id}/v/li/${firstTask.list.id}`\n : firstTask?.space?.id \n ? `https://app.clickup.com/${firstTask.team_id}/v/s/${firstTask.space.id}`\n : 'https://app.clickup.com';\n\n// Count status types\nconst statusSummary = {\n backlogs: clickupData.filter(t => t.status?.status === 'backlogs').length,\n inProgress: clickupData.filter(t => t.status?.status?.toLowerCase().includes('progress')).length,\n completed: clickupData.filter(t => t.status?.type === 'closed').length\n};\n\nconst summary = {\n date: new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),\n inProgressTasks: inProgress,\n urgentTasks: urgentTasks,\n highPriorityTasks: highPriority,\n normalPriorityTasks: normalPriority,\n blockers: blockers,\n clickupBoardUrl: boardUrl,\n totalTasks: clickupData.length,\n listName: firstTask?.list?.name || 'Tasks',\n folderName: firstTask?.folder?.name || firstTask?.project?.name || 'Board',\n statusSummary: statusSummary,\n teamId: firstTask?.team_id || null\n};\n\nreturn { json: summary };"
},
"typeVersion": 2
},
{
"id": "d1a1920b-f54d-4172-9b8c-b17054b3a39d",
"name": "注意:AI 处理",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2080,
-480
],
"parameters": {
"width": 385,
"height": 396,
"content": "## 🤖 AI 简报生成"
},
"typeVersion": 1
},
{
"id": "e5fae6c6-9533-46b3-be91-0441dc52ad08",
"name": "OpenAI:生成简报",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-1936,
-80
],
"parameters": {
"text": "=Create a comprehensive Morning Brief for {{ $json.date }}.\n\n## CURRENT DATE\n{{ $json.date }}\n\n## SPRINT OVERVIEW\n- **List:** {{ $json.listName }}\n- **Total Tasks:** {{ $json.totalTasks }}\n- **Status Breakdown:** {{ $json.statusSummary.backlogs }} Backlogs | {{ $json.statusSummary.inProgress }} In Progress | {{ $json.statusSummary.completed }} Completed\n\n## IN PROGRESS TASKS ({{ $json.inProgressTasks.length }})\n{{ $json.inProgressTasks.map(t => `- **${t.name}** (${t.assignees}) - Due: ${t.dueDate}${t.timeSpent ? ' | Time Spent: ' + t.timeSpent : ''}${t.subtasksSummary ? ' | Subtasks: ' + t.subtasksSummary : ''}`).join('\\n') || 'None' }}\n\n## HIGH PRIORITY TASKS\n{{ $json.highPriorityTasks.map(t => `- **${t.name}** (${t.assignees}) - Due: ${t.dueDate} - ${t.priority.toUpperCase()}`).join('\\n') || 'None' }}\n\n## NORMAL PRIORITY TASKS\n{{ $json.normalPriorityTasks.map(t => `- **${t.name}** (${t.assignees}) - Due: ${t.dueDate}${t.subtasksSummary ? ' | ' + t.subtasksSummary : ''}`).join('\\n') || 'None' }}\n\n## BLOCKERS ({{ $json.blockers.length }})\n{{ $json.blockers.map(t => `- **${t.name}** (${t.assignees}) - Status: ${t.status}`).join('\\n') || 'None - All Clear!' }}\n\n## ALL TEAM MEMBERS\n{{ [...new Set([...$json.inProgressTasks, ...$json.highPriorityTasks, ...$json.normalPriorityTasks].map(t => t.assignees).join(', ').split(', '))].join(', ') }}\n\n---\n\n**Instructions:**\nToday's date is: {{ $json.date }}\n\nAnalyze the tasks above and identify which ones are due TODAY based on the dueDate field.\n\nReturn ONLY valid JSON with this exact structure:\n{\n \"title\": \"Morning Brief - {{ $json.date }}\",\n \"executiveSummary\": \"2-3 sentence overview highlighting key priorities and team focus for TODAY\",\n \"inProgressHighlights\": \"Brief summary of active in-progress tasks and their status\",\n \"todaysPriorities\": \"List ALL tasks due TODAY. Format as bullet points with task name and assignee. If no tasks due today, state 'No tasks due today - focus on in-progress items'\",\n \"teamFocus\": \"For each team member, list what they are working on TODAY. Focus on tasks due today or in-progress tasks. Group by person name.\",\n \"blockers\": \"{{ $json.blockers.length > 0 ? 'List blockers' : 'All Clear' }}\",\n \"clickupBoardUrl\": \"{{ $json.clickupBoardUrl }}\",\n \"metrics\": {\n \"totalTasks\": {{ $json.totalTasks }},\n \"dueToday\": \"COUNT how many tasks are due today\",\n \"inProgress\": {{ $json.statusSummary.inProgress }},\n \"backlogs\": {{ $json.statusSummary.backlogs }}\n }\n}\n\nCRITICAL: \n- Look at the dueDate field for each task\n- In \"todaysPriorities\", list ONLY tasks due today\n- In \"teamFocus\", mention what each person is working on\n- Be specific and actionable",
"options": {
"systemMessage": "=You are a professional executive assistant creating detailed morning briefs for a team.\nYour response must:\n- Be structured, actionable, and team-focused\n- Highlight in-progress tasks and blockers prominently\n- Group tasks by assignee/team member\n- Show clear priorities with due dates\n- Include subtask progress where available\n- Be concise but comprehensive\n- Focus on TODAY's tasks and priorities\n- Use the exact date provided in the input data\n\nAlways output valid JSON matching the exact schema provided.\nNever add markdown fences, code blocks, or explanatory text."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2.1
},
{
"id": "357b93df-c039-4fde-90f3-6fec91193d32",
"name": "结构化输出解析器",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-1744,
160
],
"parameters": {
"jsonSchemaExample": "{\n \"title\": \"\",\n \"executiveSummary\": \"\",\n \"inProgressHighlights\": \"\",\n \"todaysPriorities\": \"\",\n \"teamFocus\": \"\",\n \"blockers\": \"\",\n \"clickupBoardUrl\": \"\"\n}"
},
"typeVersion": 1.3
},
{
"id": "d1af24e4-dd4f-4e6b-bca7-340f1280624c",
"name": "Azure OpenAI聊天模型",
"type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
"position": [
-2000,
160
],
"parameters": {
"model": "gpt-4o",
"options": {}
},
"credentials": {
"azureOpenAiApi": {
"id": "C3WzT18XqF8OdVM6",
"name": "Azure Open AI account"
}
},
"typeVersion": 1
},
{
"id": "f336c158-207b-4ec0-8bf9-917a1ad7aabc",
"name": "简单记忆",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
-1872,
160
],
"parameters": {
"sessionKey": "\"lead_intent_classifier\"",
"sessionIdType": "customKey",
"contextWindowLength": 7
},
"typeVersion": 1.3
},
{
"id": "1d5d6d60-31ef-4cb8-bee0-c781bf54a0ca",
"name": "注意:电子邮件构建器",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1584,
192
],
"parameters": {
"width": 256,
"height": 349,
"content": "## 📧 电子邮件格式化"
},
"typeVersion": 1
},
{
"id": "0a16b85f-c088-48f4-a4a5-4f236a0a875b",
"name": "注意:Slack 集成",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1488,
-480
],
"parameters": {
"width": 246,
"height": 299,
"content": "## 💬 Slack 通知"
},
"typeVersion": 1
},
{
"id": "e62daaeb-4688-42d1-92d1-663418816d80",
"name": "Slack:发布简报",
"type": "n8n-nodes-base.slack",
"position": [
-1536,
-176
],
"webhookId": "f9ae2b0d-06b5-46e2-9e4f-6133473fc6e4",
"parameters": {
"text": "=**Morning Brief** - {{ $('Format: Compose Brief Data').item.json.date }}**\n\n**Summary** - {{ $json.output.executiveSummary }}\n\n**Today's Priority** - {{ $json.output.todaysPriorities }} \n\n**Team's Focus** - {{ $json.output.teamFocus }}\n\nlink: <{{ $('Format: Compose Brief Data').item.json.clickupBoardUrl }}|View ClickUp Today Board>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SLACK_CHANNEL_ID"
},
"otherOptions": {
"mrkdwn": true
}
},
"credentials": {
"slackApi": {
"id": "rNqvWj9TfChPVRYY",
"name": "Slack account vivek"
}
},
"typeVersion": 2.2
},
{
"id": "03f81896-e5e6-42a5-9b1f-cd7102b0091c",
"name": "注意:邮件发送",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1168,
48
],
"parameters": {
"width": 256,
"height": 421,
"content": "## 📨 Gmail 投递"
},
"typeVersion": 1
},
{
"id": "518dbe59-a863-444c-b34f-fa15529ade86",
"name": "注意:错误管理",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3472,
416
],
"parameters": {
"width": 379,
"height": 329,
"content": "## 🚨 错误处理系统"
},
"typeVersion": 1
},
{
"id": "6213ae5c-d856-4db2-9dd2-fece957df490",
"name": "错误触发器:捕获失败",
"type": "n8n-nodes-base.errorTrigger",
"position": [
-3344,
256
],
"parameters": {},
"typeVersion": 1
},
{
"id": "865225c0-7954-4202-b893-f798b465d889",
"name": "Slack:错误警报",
"type": "n8n-nodes-base.slack",
"position": [
-3120,
256
],
"webhookId": "9625246c-14d5-41a7-a11f-3162e1ad650b",
"parameters": {
"text": "=🚨 **Morning Brief Workflow Failed**\n\n*Error:* {{ $json.error.message }}\n*Node:* {{ $json.error.node.name }}\n*Time:* {{ $json.error.timestamp }}\n\nPlease check the workflow and retry manually.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_ERROR_CHANNEL_ID",
"cachedResultName": "errors"
},
"otherOptions": {
"mrkdwn": true
}
},
"credentials": {
"slackApi": {
"id": "rNqvWj9TfChPVRYY",
"name": "Slack account vivek"
}
},
"typeVersion": 2.2
},
{
"id": "952212b7-d531-4d9e-ba24-49101e309aaa",
"name": "为电子邮件格式化数据",
"type": "n8n-nodes-base.code",
"position": [
-1536,
32
],
"parameters": {
"jsCode": "// N8N Code Node - Enhanced Morning Brief HTML Email Formatter\nconst inputData = $input.all()[0].json.output;\n\n// Helper function to parse priorities into structured data\nfunction parsePriorities(text) {\n const lines = text.split('\\n').filter(line => line.trim().startsWith('-'));\n return lines.map(line => {\n const cleanLine = line.replace(/^-\\s*/, '').trim();\n const match = cleanLine.match(/^(.+?)\\s*\\(([^)]+)\\)$/);\n if (match) {\n return { task: match[1].trim(), assignee: match[2].trim() };\n }\n return { task: cleanLine, assignee: '' };\n });\n}\n\n// Helper function to parse team focus by person\nfunction parseTeamFocus(text) {\n const sections = text.split(/(?=[A-Z][a-z]+\\s+[A-Z][a-z]+:)/);\n return sections.filter(s => s.trim()).map(section => {\n const [name, ...tasks] = section.split(':');\n return {\n name: name.trim(),\n tasks: tasks.join(':').trim()\n };\n });\n}\n\nconst priorities = parsePriorities(inputData.todaysPriorities);\nconst teamMembers = parseTeamFocus(inputData.teamFocus);\n\nconst html = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${inputData.title}</title>\n <!--[if mso]>\n <style type=\"text/css\">\n body, table, td {font-family: Arial, sans-serif !important;}\n </style>\n <![endif]-->\n</head>\n<body style=\"margin: 0; padding: 0; background-color: #f4f4f4;\">\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"background-color: #f4f4f4;\">\n <tr>\n <td align=\"center\" style=\"padding: 20px 0;\">\n \n <!-- Main Container -->\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"700\" style=\"background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);\">\n \n <!-- Header Section -->\n <tr>\n <td style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 8px 8px 0 0;\">\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\">\n <tr>\n <td>\n <h1 style=\"margin: 0; color: #ffffff; font-size: 28px; font-weight: bold; font-family: Arial, sans-serif; text-shadow: 0 2px 4px rgba(0,0,0,0.2);\">\n 📋 ${inputData.title}\n </h1>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- Executive Summary Section -->\n <tr>\n <td style=\"padding: 30px 30px 20px 30px;\">\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"border-left: 4px solid #667eea; background-color: #f8f9ff; border-radius: 4px;\">\n <tr>\n <td style=\"padding: 20px;\">\n <h2 style=\"margin: 0 0 12px 0; color: #667eea; font-size: 16px; font-weight: bold; font-family: Arial, sans-serif; text-transform: uppercase; letter-spacing: 0.5px;\">\n 📊 EXECUTIVE SUMMARY\n </h2>\n <p style=\"margin: 0; color: #2d3748; font-size: 14px; line-height: 1.7; font-family: Arial, sans-serif;\">\n ${inputData.executiveSummary}\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- Today's Priorities Section -->\n <tr>\n <td style=\"padding: 10px 30px 20px 30px;\">\n <h2 style=\"margin: 0 0 15px 0; color: #2d3748; font-size: 18px; font-weight: bold; font-family: Arial, sans-serif; border-bottom: 3px solid #f56565; padding-bottom: 10px;\">\n 🎯 TODAY'S PRIORITIES (${priorities.length} Tasks)\n </h2>\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; border: 1px solid #e2e8f0; border-radius: 4px; overflow: hidden;\">\n <tr>\n <td width=\"60\" align=\"center\" style=\"background-color: #f56565; color: #ffffff; font-weight: bold; padding: 12px 8px; font-family: Arial, sans-serif; font-size: 13px;\">\n #\n </td>\n <td style=\"background-color: #f56565; color: #ffffff; font-weight: bold; padding: 12px 15px; font-family: Arial, sans-serif; font-size: 13px;\">\n Task Description\n </td>\n <td width=\"150\" style=\"background-color: #f56565; color: #ffffff; font-weight: bold; padding: 12px 15px; font-family: Arial, sans-serif; font-size: 13px;\">\n Assigned To\n </td>\n </tr>\n ${priorities.map((priority, index) => `\n <tr>\n <td align=\"center\" style=\"background-color: ${index % 2 === 0 ? '#ffffff' : '#f7fafc'}; padding: 14px 8px; font-family: Arial, sans-serif; font-size: 14px; border-top: 1px solid #e2e8f0; font-weight: bold; color: #f56565;\">\n ${index + 1}\n </td>\n <td style=\"background-color: ${index % 2 === 0 ? '#ffffff' : '#f7fafc'}; padding: 14px 15px; font-family: Arial, sans-serif; font-size: 13px; line-height: 1.5; border-top: 1px solid #e2e8f0; color: #2d3748;\">\n ${priority.task}\n </td>\n <td style=\"background-color: ${index % 2 === 0 ? '#ffffff' : '#f7fafc'}; padding: 14px 15px; font-family: Arial, sans-serif; font-size: 13px; border-top: 1px solid #e2e8f0; color: #4a5568; font-weight: 600;\">\n ${priority.assignee}\n </td>\n </tr>\n `).join('')}\n </table>\n </td>\n </tr>\n \n <!-- In Progress Highlights Section -->\n <tr>\n <td style=\"padding: 10px 30px 20px 30px;\">\n <h2 style=\"margin: 0 0 15px 0; color: #2d3748; font-size: 18px; font-weight: bold; font-family: Arial, sans-serif; border-bottom: 3px solid #ed8936; padding-bottom: 10px;\">\n ⚡ IN PROGRESS HIGHLIGHTS\n </h2>\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"border-left: 4px solid #ed8936; background-color: #fffaf0; border-radius: 4px;\">\n <tr>\n <td style=\"padding: 18px;\">\n <p style=\"margin: 0; color: #2d3748; font-size: 14px; line-height: 1.7; font-family: Arial, sans-serif;\">\n ${inputData.inProgressHighlights}\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- Team Focus Section -->\n <tr>\n <td style=\"padding: 10px 30px 20px 30px;\">\n <h2 style=\"margin: 0 0 15px 0; color: #2d3748; font-size: 18px; font-weight: bold; font-family: Arial, sans-serif; border-bottom: 3px solid #48bb78; padding-bottom: 10px;\">\n 👥 TEAM FOCUS & ASSIGNMENTS\n </h2>\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse;\">\n ${teamMembers.map((member, index) => `\n <tr>\n <td style=\"padding: ${index > 0 ? '15' : '0'}px 0 0 0;\">\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"border: 2px solid #48bb78; border-radius: 6px; overflow: hidden; background-color: #f0fff4;\">\n <tr>\n <td style=\"background-color: #48bb78; padding: 12px 18px;\">\n <p style=\"margin: 0; color: #ffffff; font-size: 14px; font-weight: bold; font-family: Arial, sans-serif;\">\n 👤 ${member.name}\n </p>\n </td>\n </tr>\n <tr>\n <td style=\"padding: 15px 18px;\">\n <p style=\"margin: 0; color: #2d3748; font-size: 13px; line-height: 1.7; font-family: Arial, sans-serif;\">\n ${member.tasks}\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n `).join('')}\n </table>\n </td>\n </tr>\n \n <!-- Blockers Section -->\n <tr>\n <td style=\"padding: 10px 30px 20px 30px;\">\n <h2 style=\"margin: 0 0 15px 0; color: #2d3748; font-size: 18px; font-weight: bold; font-family: Arial, sans-serif; border-bottom: 3px solid ${inputData.blockers.toLowerCase().includes('all clear') ? '#48bb78' : '#f56565'}; padding-bottom: 10px;\">\n ${inputData.blockers.toLowerCase().includes('all clear') ? '✅' : '⚠️'} BLOCKERS & ISSUES\n </h2>\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" style=\"border-left: 4px solid ${inputData.blockers.toLowerCase().includes('all clear') ? '#48bb78' : '#f56565'}; background-color: ${inputData.blockers.toLowerCase().includes('all clear') ? '#f0fff4' : '#fff5f5'}; border-radius: 4px;\">\n <tr>\n <td style=\"padding: 18px;\">\n <p style=\"margin: 0; color: #2d3748; font-size: 15px; font-weight: ${inputData.blockers.toLowerCase().includes('all clear') ? 'bold' : 'normal'}; font-family: Arial, sans-serif; line-height: 1.6;\">\n ${inputData.blockers}\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- Action Button -->\n <tr>\n <td align=\"center\" style=\"padding: 25px 30px 35px 30px;\">\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n <tr>\n <td align=\"center\" style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 16px 45px; border-radius: 6px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);\">\n <a href=\"${inputData.clickupBoardUrl}\" style=\"color: #ffffff; text-decoration: none; font-weight: bold; font-size: 14px; font-family: Arial, sans-serif; display: block; letter-spacing: 0.5px;\">\n 🔗 VIEW FULL BOARD ON CLICKUP\n </a>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- Footer -->\n <tr>\n <td style=\"background-color: #f7fafc; padding: 25px 30px; border-top: 1px solid #e2e8f0; border-radius: 0 0 8px 8px;\">\n <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\">\n <tr>\n <td align=\"center\">\n <p style=\"margin: 0; color: #718096; font-size: 12px; line-height: 1.6; font-family: Arial, sans-serif;\">\n 🤖 This brief was automatically generated by N8N Automation<br>\n <strong style=\"color: #4a5568;\">Have a productive day! 🚀</strong>\n </p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n </table>\n \n </td>\n </tr>\n </table>\n</body>\n</html>\n`;\n\n// Enhanced plain text version\nconst plainText = `${inputData.title}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n📊 EXECUTIVE SUMMARY\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${inputData.executiveSummary}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n🎯 TODAY'S PRIORITIES (${priorities.length} Tasks)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${priorities.map((p, i) => `${i + 1}. ${p.task} → ${p.assignee}`).join('\\n')}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n⚡ IN PROGRESS HIGHLIGHTS\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${inputData.inProgressHighlights}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n👥 TEAM FOCUS & ASSIGNMENTS\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${teamMembers.map(m => `👤 ${m.name}\\n ${m.tasks}`).join('\\n\\n')}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${inputData.blockers.toLowerCase().includes('all clear') ? '✅' : '⚠️'} BLOCKERS & ISSUES\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${inputData.blockers}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nView Full Board: ${inputData.clickupBoardUrl}\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`;\n\nreturn {\n json: {\n html: html,\n subject: `📋 ${inputData.title}`,\n plainText: plainText\n }\n};"
},
"typeVersion": 2
},
{
"id": "78494f7e-40da-4c02-aa89-62dc1a3783e4",
"name": "发送晨报电子邮件",
"type": "n8n-nodes-base.gmail",
"position": [
-1312,
32
],
"webhookId": "9e36fe20-bb0c-4c0a-8421-2f57096e79d7",
"parameters": {
"sendTo": "YOUR_EMAIL@example.com",
"message": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "gEIaWCTvGfYjMSb3",
"name": "Gmail credentials"
}
},
"typeVersion": 2.1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "36a5ec42-fd1c-4215-9a66-eb98c6bc60c3",
"connections": {
"Simple Memory": {
"ai_memory": [
[
{
"node": "OpenAI: Generate Brief",
"type": "ai_memory",
"index": 0
}
]
]
},
"Check Lists Exist": {
"main": [
[
{
"node": "Find Latest Sprint",
"type": "main",
"index": 0
}
]
]
},
"Find Latest Sprint": {
"main": [
[
{
"node": "Get Task From Latest Sprint",
"type": "main",
"index": 0
}
]
]
},
"Format Data For Email": {
"main": [
[
{
"node": "Send Morning Brief Email",
"type": "main",
"index": 0
}
]
]
},
"OpenAI: Generate Brief": {
"main": [
[
{
"node": "Slack: Post Brief",
"type": "main",
"index": 0
},
{
"node": "Format Data For Email",
"type": "main",
"index": 0
}
]
]
},
"Azure OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "OpenAI: Generate Brief",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Get All Lists (Sprints)": {
"main": [
[
{
"node": "Check Lists Exist",
"type": "main",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "OpenAI: Generate Brief",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Trigger: Morning Schedule": {
"main": [
[
{
"node": "Get All Lists (Sprints)",
"type": "main",
"index": 0
}
]
]
},
"Format: Compose Brief Data": {
"main": [
[
{
"node": "OpenAI: Generate Brief",
"type": "main",
"index": 0
}
]
]
},
"Get Task From Latest Sprint": {
"main": [
[
{
"node": "Format: Compose Brief Data",
"type": "main",
"index": 0
}
]
]
},
"Error Trigger: Catch Failures": {
"main": [
[
{
"node": "Slack: Error Alert",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
客户入职帮助请求(Typeform 到 Gmail 和 Sheets)
客户入职帮助请求(Typeform 到 Gmail 和 Sheets)
If
Code
Gmail
+10
28 节点Rahul Joshi
内容创作
自动化从ClickUp和GoHighLevel生成每日结束报告到Slack、Email和Google云端硬盘
从ClickUp和GoHighLevel生成AI驱动的每日结束报告到多渠道
If
Code
Merge
+12
29 节点Rahul Joshi
从Monday.com和Jira到Outlook的AI驱动反馈分类与报告
使用Azure GPT-4、Jira任务和Outlook报告分析来自Monday.com的客户反馈
Set
Code
Jira
+12
27 节点Rahul Joshi
使用 Slack、Gmail 和 AI 自动化 Jira 待办事项整理和报告
通过 Slack、Gmail 和 GPT-4 实现 Jira 待办事项整理与报告的自动化
If
Set
Jira
+9
31 节点Rahul Joshi
发布说明转常见问题存根(Jira/ClickUp)
使用GPT-4o从ClickUp自动生成发布说明到Notion和Slack
If
Code
Gmail
+8
20 节点Rahul Joshi
工程
从 Stripe 支付自动交付模板给客户
使用Stripe、GPT-4o和Gmail的自动化模板交付系统
If
Code
Gmail
+12
44 节点Rahul Joshi
客户关系管理
工作流信息
难度等级
高级
节点数量27
分类-
节点类型12
作者
Rahul Joshi
@rahul08Rahul Joshi is a seasoned technology leader specializing in the n8n automation tool and AI-driven workflow automation. With deep expertise in building open-source workflow automation and self-hosted automation platforms, he helps organizations eliminate manual processes through intelligent n8n ai agent automation solutions.
外部链接
在 n8n.io 查看 →
分享此工作流