📅 Calendar Workflows
🎯 Mục tiêu bài học
Sau bài học này, bạn sẽ:
✅ Kết nối Google Calendar với n8n
✅ Tạo multi-stage meeting reminders tự động
✅ Xây dựng meeting prep automation và auto meeting notes
✅ Implement post-meeting follow-up và calendar analytics
Calendar là trung tâm của productivity. Bài này hướng dẫn automate meeting prep, reminders, và follow-ups để bạn luôn chuẩn bị tốt nhất.
🔍 Calendar Automation Overview
What Can Be Automated:
Checkpoint
Có bao nhiêu giai đoạn trong meeting lifecycle có thể automate?
🛠️ Google Calendar Setup
Connect Google Calendar:
11. Add Google Calendar node22. Create new credential33. OAuth2 authentication44. Required scopes:5 - calendar.readonly6 - calendar.events7 - calendar.settings.readonlyCalendar Trigger Options:
Checkpoint
Những trigger options nào có sẵn cho Google Calendar trong n8n?
⚡ Workflow 1: Meeting Reminder System
Smart Reminders:
Multi-Stage Reminders
Reminder Logic Code:
1const events = $input.all();2const now = new Date();3const reminders = [];45for (const event of events) {6 const startTime = new Date(event.json.start.dateTime);7 const hoursUntil = (startTime - now) / (1000 * 60 * 60);8 9 // 24-hour reminder10 if (hoursUntil > 23 && hoursUntil <= 24) {11 reminders.push({12 event: event.json,13 type: '24h',14 message: `📅 Tomorrow: ${event.json.summary}`,15 includeAgenda: true16 });17 }18 19 // 1-hour reminder20 if (hoursUntil > 0.9 && hoursUntil <= 1) {21 reminders.push({22 event: event.json,23 type: '1h',24 message: `⏰ In 1 hour: ${event.json.summary}`,25 includeJoinLink: true26 });27 }28 29 // 15-minute reminder30 if (hoursUntil > 0.2 && hoursUntil <= 0.25) {31 reminders.push({32 event: event.json,33 type: '15m',34 message: `🔔 Starting soon: ${event.json.summary}`,35 includeJoinLink: true,36 urgent: true37 });38 }39}4041return reminders;Send Slack Reminder:
1{2 "name": "Send Slack Reminder",3 "type": "n8n-nodes-base.slack",4 "parameters": {5 "channel": "@me",6 "text": "={{ $json.message }}",7 "attachments": [8 {9 "color": "#4CAF50",10 "fields": [11 {12 "title": "Time",13 "value": "={{ $json.event.start.dateTime }}",14 "short": true15 },16 {17 "title": "Location",18 "value": "={{ $json.event.location || $json.event.hangoutLink || 'TBD' }}",19 "short": true20 }21 ],22 "actions": [23 {24 "type": "button",25 "text": "Join Meeting",26 "url": "={{ $json.event.hangoutLink }}"27 }28 ]29 }30 ]31 }32}Checkpoint
Multi-stage reminder system gửi reminders ở những thời điểm nào?
🔧 Workflow 2: Meeting Prep Automation
Auto-Generate Prep Materials:
Meeting Prep Generator
Attendee Research Code:
1const event = $input.item.json;2const attendees = event.attendees || [];34// Extract external attendees (not from your company)5const externalAttendees = attendees.filter(a => 6 !a.email.includes('@yourcompany.com') &&7 !a.self8);910const prepInfo = {11 eventTitle: event.summary,12 eventTime: event.start.dateTime,13 agenda: event.description || 'No agenda provided',14 attendees: externalAttendees.map(a => ({15 email: a.email,16 name: a.displayName || a.email.split('@')[0],17 company: a.email.split('@')[1]?.replace('.com', '')18 }))19};2021return prepInfo;Create Prep Document:
1const prep = $input.item.json;23const template = `4# Meeting Prep: ${prep.eventTitle}56## �� Details7- **When:** ${new Date(prep.eventTime).toLocaleString()}8- **Attendees:** ${prep.attendees.map(a => a.name).join(', ')}910## �� Agenda11${prep.agenda}1213## �� Attendee Research14${prep.attendees.map(a => `15### ${a.name} (${a.company})16- **Email:** ${a.email}17- **Recent interactions:** [Check email/CRM]18- **LinkedIn:** [Research needed]19- **Notes:** 20`).join('\n')}2122## ❓ Questions to Ask231. 242. 253. 2627## �� Meeting Objectives281. 292. 3031## �� Notes32(To be filled during meeting)3334## ✅ Action Items35(To be filled after meeting)36`;3738return { content: template, title: `Prep: ${prep.eventTitle}` };Checkpoint
Meeting prep automation thu thập thông tin attendees từ những nguồn nào?
🏗️ Workflow 3: Auto Meeting Notes
Create Notes Template:
Meeting Notes Creator
Notion Meeting Notes Template:
1{2 "name": "Create Notion Page",3 "type": "n8n-nodes-base.notion",4 "parameters": {5 "operation": "create",6 "databaseId": "meetings-database-id",7 "properties": {8 "Title": {9 "title": [{ "text": { "content": "={{ $json.summary }}" }}]10 },11 "Date": {12 "date": { "start": "={{ $json.start.dateTime }}" }13 },14 "Attendees": {15 "multi_select": "={{ $json.attendees.map(a => ({name: a.email})) }}"16 },17 "Status": {18 "select": { "name": "In Progress" }19 }20 },21 "content": {22 "type": "callout",23 "content": "Meeting in progress..."24 }25 }26}Checkpoint
Auto meeting notes template bao gồm những sections nào?
📊 Workflow 4: Post-Meeting Follow-Up
Automated Follow-Up:
Post-Meeting Actions
Extract Action Items:
1const notes = $input.item.json.content;23// Find action items (lines starting with - [ ] or TODO:)4const actionItemRegex = /(?:- \[ \]|TODO:?)\s*(.+?)(?:\n|$)/gi;5const matches = [...notes.matchAll(actionItemRegex)];67const actionItems = matches.map((match, index) => {8 const text = match[1].trim();9 10 // Try to extract assignee11 const assigneeMatch = text.match(/@(\w+)/);12 const assignee = assigneeMatch ? assigneeMatch[1] : 'unassigned';13 14 // Try to extract due date15 const dueDateMatch = text.match(/due:?\s*(\d{1,2}\/\d{1,2})/i);16 const dueDate = dueDateMatch ? dueDateMatch[1] : null;17 18 return {19 id: index + 1,20 task: text.replace(/@\w+/, '').replace(/due:?\s*\d{1,2}\/\d{1,2}/i, '').trim(),21 assignee,22 dueDate23 };24});2526return { actionItems };Send Summary Email:
1const meeting = $input.item.json;23const emailBody = `4Hi everyone,56Thank you for attending ${meeting.title}. Here's a quick summary:78## Key Decisions9${meeting.decisions || 'None recorded'}1011## Action Items12${meeting.actionItems.map(a => 13 `- ${a.task} (${a.assignee})${a.dueDate ? ` - Due: ${a.dueDate}` : ''}`14).join('\n')}1516## Next Steps17${meeting.nextSteps || 'Follow up scheduled for next week'}1819Full meeting notes: ${meeting.notesLink}2021Best regards,22[Auto-generated by n8n]23`;2425return {26 to: meeting.attendees.map(a => a.email).join(','),27 subject: `Meeting Summary: ${meeting.title}`,28 body: emailBody29};Checkpoint
Post-meeting follow-up extract action items bằng regex pattern nào?
💡 Workflow 5: Calendar Analytics
Weekly Meeting Report:
Weekly Calendar Analysis
Calculate Meeting Stats:
1const events = $input.all();23let stats = {4 totalMeetings: 0,5 totalMinutes: 0,6 byType: {},7 byDay: {Mon: 0, Tue: 0, Wed: 0, Thu: 0, Fri: 0},8 longestMeeting: null,9 busiestDay: ''10};1112for (const event of events) {13 const e = event.json;14 15 // Skip all-day events16 if (!e.start.dateTime) continue;17 18 stats.totalMeetings++;19 20 // Calculate duration21 const start = new Date(e.start.dateTime);22 const end = new Date(e.end.dateTime);23 const duration = (end - start) / (1000 * 60);24 stats.totalMinutes += duration;25 26 // Track longest meeting27 if (!stats.longestMeeting || duration > stats.longestMeeting.duration) {28 stats.longestMeeting = { title: e.summary, duration };29 }30 31 // By day32 const day = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][start.getDay()];33 stats.byDay[day] = (stats.byDay[day] || 0) + 1;34 35 // By type (based on title keywords)36 let type = 'Other';37 if (e.summary.toLowerCase().includes('1:1')) type = '1:1';38 else if (e.summary.toLowerCase().includes('standup')) type = 'Standup';39 else if (e.summary.toLowerCase().includes('review')) type = 'Review';40 41 stats.byType[type] = (stats.byType[type] || 0) + 1;42}4344// Find busiest day45stats.busiestDay = Object.entries(stats.byDay)46 .sort((a, b) => b[1] - a[1])[0][0];4748stats.avgMeetingLength = Math.round(stats.totalMinutes / stats.totalMeetings);49stats.hoursInMeetings = (stats.totalMinutes / 60).toFixed(1);5051return stats;Generate Weekly Report:
1const stats = $input.item.json;23const report = `4# �� Weekly Calendar Report56## Overview7- **Total Meetings:** ${stats.totalMeetings}8- **Hours in Meetings:** ${stats.hoursInMeetings}h9- **Average Meeting Length:** ${stats.avgMeetingLength} min10- **Busiest Day:** ${stats.busiestDay}1112## By Type13${Object.entries(stats.byType).map(([type, count]) => 14 `- ${type}: ${count}`15).join('\n')}1617## By Day18| Day | Meetings |19|-----|----------|20${Object.entries(stats.byDay).map(([day, count]) => 21 `| ${day} | ${count} |`22).join('\n')}2324## Longest Meeting25�� ${stats.longestMeeting.title} (${stats.longestMeeting.duration} min)2627*Generated automatically by n8n*28`;2930return { report };Checkpoint
Calendar analytics tính toán những metrics nào?
🌟 Workflow 6: Smart Scheduling
Find Optimal Meeting Time:
1// Find free slots for meeting2const busySlots = $input.all().map(e => ({3 start: new Date(e.json.start.dateTime),4 end: new Date(e.json.end.dateTime)5}));67const workDayStart = 9; // 9 AM8const workDayEnd = 18; // 6 PM9const meetingDuration = 30; // 30 minutes1011const freeSlots = [];12const today = new Date();13today.setHours(workDayStart, 0, 0, 0);1415// Check next 5 business days16for (let day = 0; day < 5; day++) {17 const checkDate = new Date(today);18 checkDate.setDate(checkDate.getDate() + day);19 20 // Skip weekends21 if (checkDate.getDay() === 0 || checkDate.getDay() === 6) continue;22 23 // Check each 30-min slot24 for (let hour = workDayStart; hour < workDayEnd; hour++) {25 for (let min = 0; min < 60; min += 30) {26 const slotStart = new Date(checkDate);27 slotStart.setHours(hour, min, 0, 0);28 29 const slotEnd = new Date(slotStart);30 slotEnd.setMinutes(slotEnd.getMinutes() + meetingDuration);31 32 // Check if slot conflicts with any busy time33 const isConflict = busySlots.some(busy => 34 (slotStart >= busy.start && slotStart < busy.end) ||35 (slotEnd > busy.start && slotEnd <= busy.end)36 );37 38 if (!isConflict) {39 freeSlots.push({40 start: slotStart.toISOString(),41 end: slotEnd.toISOString(),42 display: slotStart.toLocaleString()43 });44 }45 }46 }47}4849return { freeSlots: freeSlots.slice(0, 10) }; // Return top 10 optionsCheckpoint
Smart scheduling kiểm tra những điều kiện gì khi tìm available slots?
📋 Best Practices
Do:
- ✅ Test reminders with your own events first
- ✅ Respect time zones
- ✅ Keep notifications concise
- ✅ Allow opt-out from automated emails
Don't:
- ❌ Send too many reminders (max 2-3)
- ❌ Auto-create events without confirmation
- ❌ Process declined events
- ❌ Ignore private/sensitive events
Checkpoint
Tối đa bao nhiêu reminders nên gửi cho một meeting?
📝 Bài Tập Thực Hành
Build your calendar system:
- Create multi-stage reminder workflow
- Build meeting prep automation
- Set up post-meeting follow-up
- Implement weekly calendar report
- Design smart scheduling assistant
Master your calendar! 📅
Checkpoint
Bạn đã hoàn thành những challenges nào trong bài tập?
🧠 Key Takeaways
- 📅 Prep is key - Automate pre-meeting preparation
- ⏰ Smart reminders - Stage them appropriately
- 📝 Capture everything - Automate note templates
- 📊 Track time - Know where your time goes
- 🔄 Close the loop - Automate follow-ups
Checkpoint
Tại sao prep automation quan trọng cho meetings?
🚀 Bài tiếp theo
Note-Taking Automation — Save content to Notion, Obsidian và organize knowledge automatically.
