AutomationFlowsSlack & Telegram › Daily Schedule Alert: Google Sheets to Slack

Daily Schedule Alert: Google Sheets to Slack

Original n8n title: Jen Daily Schedule Alert

Jen Daily Schedule Alert. Uses googleSheets, slack. Scheduled trigger; 4 nodes.

Cron / scheduled trigger★★★★☆ complexity4 nodesGoogle SheetsSlack
Slack & Telegram Trigger: Cron / scheduled Nodes: 4 Complexity: ★★★★☆ Added:

This workflow follows the Google Sheets → Slack recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Jen Daily Schedule Alert",
  "nodes": [
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1B19nehOp-PMweCWRcsyEne5Ih1dztN6Zc4JEp2ZgxsY",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Sheet1",
          "mode": "name"
        },
        "options": {}
      },
      "id": "cad75dff-5113-4b13-a352-83d579d3967a",
      "name": "\u8b80\u53d6\u6392\u73ed\u8868",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        220,
        0
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Jen \u6392\u73ed\u901a\u77e5\u8655\u7406\u908f\u8f2f - \u4fee\u6b63\u6642\u5340\u5224\u65b7\u908f\u8f2f\uff08\u542b\u590f\u4ee4\u6642\uff09\nconst items = $input.all();\n\nconsole.log('=== \u8cc7\u6599\u7d50\u69cb\u5206\u6790 ===');\nconsole.log('items \u9577\u5ea6:', items.length);\n\n// \ud83d\udd50 \u52d5\u614b\u6642\u5340\u504f\u79fb\u51fd\u6578\uff08\u8655\u7406\u590f\u4ee4\u6642\uff09\nfunction getCalgaryOffset(date) {\n  const year = date.getFullYear();\n  \n  // \u8a08\u7b97\u590f\u4ee4\u6642\u958b\u59cb\uff1a3\u6708\u7b2c\u4e8c\u500b\u9031\u65e5\n  const marchFirst = new Date(year, 2, 1); // 3\u67081\u65e5\n  const firstSunday = 7 - marchFirst.getDay();\n  const secondSunday = firstSunday + 7;\n  const dstStart = new Date(year, 2, secondSunday, 2, 0, 0); // 3\u6708\u7b2c\u4e8c\u500b\u9031\u65e5 2:00 AM\n  \n  // \u8a08\u7b97\u590f\u4ee4\u6642\u7d50\u675f\uff1a11\u6708\u7b2c\u4e00\u500b\u9031\u65e5\n  const novemberFirst = new Date(year, 10, 1); // 11\u67081\u65e5\n  const novFirstSunday = 7 - novemberFirst.getDay();\n  const dstEnd = new Date(year, 10, novFirstSunday, 2, 0, 0); // 11\u6708\u7b2c\u4e00\u500b\u9031\u65e5 2:00 AM\n  \n  // \u5224\u65b7\u662f\u5426\u5728\u590f\u4ee4\u6642\u671f\u9593\n  if (date >= dstStart && date < dstEnd) {\n    console.log('\ud83c\udf1e \u5361\u52a0\u5229\u590f\u4ee4\u6642\u671f\u9593 (MDT): UTC-6');\n    return -6 * 60; // UTC-6 (Mountain Daylight Time)\n  } else {\n    console.log('\u2744\ufe0f \u5361\u52a0\u5229\u6a19\u6e96\u6642\u671f\u9593 (MST): UTC-7');\n    return -7 * 60; // UTC-7 (Mountain Standard Time)\n  }\n}\n\n// \u6642\u5340\u8a2d\u7f6e\nconst taipeiOffset = 8 * 60; // UTC+8 (\u5206\u9418)\nconst now = new Date();\nconst calgaryOffset = getCalgaryOffset(now); // \u52d5\u614b\u8a08\u7b97\u5361\u52a0\u5229\u6642\u5340\n\n// \u53d6\u5f97\u7576\u524d\u6642\u9593\nconst taipeiNow = new Date(now.getTime() + (taipeiOffset * 60000));\nconst calgaryNow = new Date(now.getTime() + (calgaryOffset * 60000));\n\nconsole.log('\ud83c\udf0d \u6642\u5340\u8cc7\u8a0a:');\nconsole.log('\u53f0\u5317\u6642\u5340\u504f\u79fb:', taipeiOffset / 60, '\u5c0f\u6642');\nconsole.log('\u5361\u52a0\u5229\u6642\u5340\u504f\u79fb:', calgaryOffset / 60, '\u5c0f\u6642');\n\n// \ud83c\udfaf \u65b0\u7684\u6642\u9593\u5224\u65b7\u908f\u8f2f\nconst taipeiHour = taipeiNow.getHours();\nlet targetDate;\nlet displayDay;\nlet isShowingTomorrow;\n\nif (taipeiHour >= 8 && taipeiHour <= 19) {\n  // \u53f0\u5317 08:00-19:59 \u2192 \u986f\u793a\u5361\u52a0\u5229\u300c\u660e\u5929\u300d\u73ed\u8868\n  const calgaryTomorrow = new Date(calgaryNow);\n  calgaryTomorrow.setDate(calgaryTomorrow.getDate() + 1);\n  targetDate = calgaryTomorrow.toISOString().split('T')[0];\n  displayDay = '\u660e\u5929';\n  isShowingTomorrow = true;\n  console.log('\ud83c\udf05 \u53f0\u5317\u767d\u5929\u6642\u6bb5 (08:00-19:59) - \u986f\u793a\u5361\u52a0\u5229\u660e\u5929\u73ed\u8868');\n} else {\n  // \u53f0\u5317 20:00-07:59 \u2192 \u986f\u793a\u5361\u52a0\u5229\u300c\u4eca\u5929\u300d\u73ed\u8868\n  targetDate = calgaryNow.toISOString().split('T')[0];\n  displayDay = '\u4eca\u5929';\n  isShowingTomorrow = false;\n  console.log('\ud83c\udf19 \u53f0\u5317\u591c\u9593\u6642\u6bb5 (20:00-07:59) - \u986f\u793a\u5361\u52a0\u5229\u4eca\u5929\u73ed\u8868');\n}\n\nconsole.log('\u53f0\u5317\u7576\u524d\u6642\u9593:', taipeiNow.toISOString());\nconsole.log('\u5361\u52a0\u5229\u7576\u524d\u6642\u9593:', calgaryNow.toISOString());\nconsole.log('\u76ee\u6a19\u65e5\u671f:', targetDate);\nconsole.log('\u986f\u793a\u5929\u6578:', displayDay);\nconsole.log('\u53f0\u5317\u5c0f\u6642:', taipeiHour);\n\n// \u6536\u96c6\u6240\u6709\u8cc7\u6599\nconst scheduleData = [];\n\nconsole.log('\u958b\u59cb\u8655\u7406\u6240\u6709 items...');\n\n// \u76f4\u63a5\u8655\u7406\u6bcf\u500b item\uff08\u6bcf\u500b item \u4ee3\u8868\u4e00\u884c\u8cc7\u6599\uff09\nfor (let i = 0; i < items.length; i++) {\n  const item = items[i];\n  const row = item.json;\n  \n  console.log(`\u8655\u7406\u7b2c ${i+1} \u500b item:`, JSON.stringify(row, null, 2));\n  \n  // \u8df3\u904e\u7121\u6548\u8cc7\u6599\n  if (!row || !row.Employee || !row.Date) {\n    console.log(`\u8df3\u904e\u7b2c ${i+1} \u500b item (\u7121\u54e1\u5de5\u540d\u7a31\u6216\u65e5\u671f)`);\n    continue;\n  }\n  \n  const employee = row.Employee;\n  const dateStr = row.Date;\n  const day = row.Day;\n  const shiftTime = row.Shift_Time;\n  const status = row.Status;\n  const location = row.Manager_Location;\n  \n  console.log(`\u8655\u7406\u54e1\u5de5: ${employee}, \u65e5\u671f: ${dateStr}, \u73ed\u6b21: ${shiftTime}, \u72c0\u614b: ${status}, \u4f4d\u7f6e: ${location}`);\n  \n  // \u65e5\u671f\u683c\u5f0f\u8f49\u63db\n  let formattedDate = '';\n  if (dateStr && dateStr.includes('/')) {\n    try {\n      const parts = dateStr.split('/');\n      if (parts.length === 3) {\n        const month = parts[0].padStart(2, '0');\n        const day = parts[1].padStart(2, '0');\n        const year = parts[2];\n        formattedDate = `${year}-${month}-${day}`;\n        console.log(`\u65e5\u671f\u8f49\u63db: ${dateStr} \u2192 ${formattedDate}`);\n      }\n    } catch (e) {\n      console.log('\u65e5\u671f\u89e3\u6790\u932f\u8aa4:', dateStr);\n      continue;\n    }\n  }\n  \n  if (formattedDate && employee) {\n    const processedRow = {\n      employee: employee.trim(),\n      date: formattedDate,\n      day: day,\n      shiftTime: shiftTime || '',\n      status: status || '',\n      location: location || ''\n    };\n    \n    scheduleData.push(processedRow);\n    console.log(`\u2705 \u6210\u529f\u8655\u7406:`, processedRow);\n  }\n}\n\nconsole.log('\u8655\u7406\u5f8c\u7684 scheduleData \u9577\u5ea6:', scheduleData.length);\nconsole.log('\u524d5\u7b46\u8655\u7406\u5f8c\u7684\u8cc7\u6599:', scheduleData.slice(0, 5));\n\n// \u627e\u51fa\u76ee\u6a19\u65e5\u671f\u7684\u73ed\u8868\nconst targetSchedules = scheduleData.filter(item => \n  item.date === targetDate\n);\n\nconsole.log(`${displayDay}\u7684\u73ed\u8868\u7b46\u6578:`, targetSchedules.length);\nconsole.log(`${displayDay}\u7684\u6240\u6709\u73ed\u8868:`, targetSchedules);\n\n// \u627e\u51fa Jen \u7684\u73ed\u8868\nconst jenTargetSchedule = targetSchedules.find(item => \n  item.employee && item.employee.toLowerCase().includes('jen')\n);\n\n// \u627e\u51fa\u5176\u4ed6\u540c\u4e8b\u7684\u73ed\u8868\nconst othersTargetSchedules = targetSchedules.filter(item => \n  item.employee && !item.employee.toLowerCase().includes('jen')\n);\n\nconsole.log(`Jen ${displayDay}\u73ed\u8868:`, jenTargetSchedule);\nconsole.log(`\u5176\u4ed6\u540c\u4e8b${displayDay}\u73ed\u8868\u6578\u91cf:`, othersTargetSchedules.length);\n\n// \u683c\u5f0f\u5316\u901a\u77e5\u8a0a\u606f - \u4fee\u6b63\u4f5c\u7528\u57df\u554f\u984c\nfunction formatScheduleMessage(taipeiNow, calgaryNow, jenSchedule, othersSchedules, displayDay, targetDate, isShowingTomorrow, calgaryOffset) {\n  const taipeiDateStr = taipeiNow.toLocaleDateString('zh-TW', {\n    year: 'numeric',\n    month: 'numeric', \n    day: 'numeric',\n    weekday: 'short'\n  });\n  \n  const taipeiTimeStr = taipeiNow.toLocaleTimeString('zh-TW', {\n    hour: '2-digit',\n    minute: '2-digit',\n    hour12: false\n  });\n  \n  const calgaryDateStr = calgaryNow.toLocaleDateString('en-CA', {\n    year: 'numeric',\n    month: 'numeric',\n    day: 'numeric', \n    weekday: 'short'\n  });\n  \n  const calgaryTimeStr = calgaryNow.toLocaleTimeString('en-CA', {\n    hour: '2-digit',\n    minute: '2-digit',\n    hour12: true\n  });\n  \n  // \u8a08\u7b97\u76ee\u6a19\u65e5\u671f\u7684\u986f\u793a\u683c\u5f0f\n  const targetDateObj = new Date(targetDate + 'T12:00:00');\n  const targetDateStr = targetDateObj.toLocaleDateString('zh-TW', {\n    month: 'numeric',\n    day: 'numeric', \n    weekday: 'short'\n  });\n  \n  // \u986f\u793a\u6642\u5340\u8cc7\u8a0a\n  const timeZoneInfo = calgaryOffset === -6 * 60 ? 'MDT (UTC-6)' : 'MST (UTC-7)';\n  \n  let message = `\ud83c\udf41 Jen ${displayDay}\u73ed\u8868\u63d0\u9192\\n\\n`;\n  message += `\u26a0\ufe0f \u6642\u5340\u63d0\u9192\uff1a\u9019\u662f Jen \u5728\u5361\u52a0\u5229\u7684\u300c${displayDay}\u300d\u73ed\u8868\uff01\\n`;\n  message += `\ud83d\udcc5 \u53f0\u5317\u73fe\u5728\uff1a${taipeiDateStr} ${taipeiTimeStr}\\n`;\n  message += `\ud83d\udcc5 \u5361\u52a0\u5229\u73fe\u5728\uff1a${calgaryDateStr} ${calgaryTimeStr} (${timeZoneInfo})\\n\\n`;\n  \n  // Jen \u7684\u73ed\u8868\n  if (jenSchedule) {\n    message += `\ud83d\udc64 Jen \u7684\u5361\u52a0\u5229${displayDay}\u73ed\u6b21 (${targetDateStr})\uff1a\\n`;\n    \n    if (jenSchedule.status === 'OFF') {\n      message += `\ud83c\udfe0 \u4f11\u5047\\n`;\n    } else {\n      const timeInfo = jenSchedule.shiftTime;\n      if (timeInfo) {\n        message += `\u23f0 ${timeInfo} (\u5361\u52a0\u5229\u6642\u9593)\\n`;\n      }\n      message += `\ud83d\udccd \u72c0\u614b: ${jenSchedule.status}\\n`;\n      if (jenSchedule.location) {\n        message += `\ud83d\udccd \u4f4d\u7f6e: ${jenSchedule.location}\\n`;\n      }\n    }\n  } else {\n    message += `\ud83d\udc64 Jen \u7684\u5361\u52a0\u5229${displayDay}\u73ed\u6b21 (${targetDateStr})\uff1a\\n`;\n    message += `\u2753 \u67e5\u7121\u8cc7\u6599\\n`;\n  }\n  \n  message += `\\n\ud83d\udc65 \u540c\u65e5\u5176\u4ed6\u540c\u4e8b\uff1a\\n`;\n  \n  if (othersSchedules.length > 0) {\n    othersSchedules.forEach(schedule => {\n      const employeeName = schedule.employee;\n      \n      // Karen \u7684\u908f\u8f2f\uff1a\u6839\u64da Manager_Location \u986f\u793a\u4f4d\u7f6e\n      if (employeeName.toLowerCase().includes('karen')) {\n        if (schedule.location) {\n          message += `\u2022 ${employeeName}: Manager (${schedule.location})\\n`;\n        } else {\n          message += `\u2022 ${employeeName}: Manager\\n`;\n        }\n      } else {\n        // \u5176\u4ed6\u54e1\u5de5\u7684\u908f\u8f2f\n        if (schedule.status === 'OFF') {\n          message += `\u2022 ${employeeName}: \u4f11\u5047\\n`;\n        } else {\n          const timeInfo = schedule.shiftTime || schedule.status;\n          const locationInfo = schedule.location ? ` (${schedule.location})` : '';\n          message += `\u2022 ${employeeName}: ${timeInfo}${locationInfo}\\n`;\n        }\n      }\n    });\n  } else {\n    message += `\u2022 \u67e5\u7121\u5176\u4ed6\u540c\u4e8b\u8cc7\u6599\\n`;\n  }\n  \n  return message;\n}\n\n// \u547c\u53eb\u683c\u5f0f\u5316\u51fd\u5f0f\uff0c\u50b3\u5165\u6240\u9700\u7684\u53c3\u6578\nconst notificationMessage = formatScheduleMessage(\n  taipeiNow, \n  calgaryNow, \n  jenTargetSchedule, \n  othersTargetSchedules,\n  displayDay,\n  targetDate,\n  isShowingTomorrow,\n  calgaryOffset\n);\n\nconsole.log('\u6700\u7d42\u901a\u77e5\u8a0a\u606f:', notificationMessage);\n\n// \u56de\u50b3\u8655\u7406\u7d50\u679c\nreturn {\n  json: {\n    message: notificationMessage,\n    jenSchedule: jenTargetSchedule,\n    othersSchedules: othersTargetSchedules,\n    targetDate: targetDate,\n    displayDay: displayDay,\n    isShowingTomorrow: isShowingTomorrow,\n    taipeiTime: taipeiNow.toISOString(),\n    calgaryTime: calgaryNow.toISOString(),\n    timeZoneInfo: {\n      taipeiOffset: taipeiOffset / 60,\n      calgaryOffset: calgaryOffset / 60,\n      isDST: calgaryOffset === -6 * 60,\n      timeZoneDisplay: calgaryOffset === -6 * 60 ? 'MDT (UTC-6)' : 'MST (UTC-7)'\n    },\n    debugInfo: {\n      totalItems: items.length,\n      processedDataLength: scheduleData.length,\n      targetSchedulesLength: targetSchedules.length,\n      jenFound: !!jenTargetSchedule,\n      taipeiHour: taipeiHour\n    }\n  }\n};"
      },
      "id": "f5dc57ad-22cd-48fc-abea-5ea9c5cbd196",
      "name": "\u8655\u7406\u6392\u73ed\u8cc7\u6599",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "channel": "#n8n-work-schedule",
        "text": "={{$json.message}}",
        "otherOptions": {},
        "attachments": []
      },
      "id": "bd7cea7e-3893-4e1d-8142-1ee6051f83fc",
      "name": "\u767c\u9001 Slack \u901a\u77e5",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [
        660,
        0
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8,20 * * *"
            }
          ]
        }
      },
      "id": "9ad9e047-9354-4488-b891-0fd997553866",
      "name": "\u6bcf\u65e5\u65e9\u665a 8:00 \u89f8\u767c",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ]
    }
  ],
  "connections": {
    "\u8b80\u53d6\u6392\u73ed\u8868": {
      "main": [
        [
          {
            "node": "\u8655\u7406\u6392\u73ed\u8cc7\u6599",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u8655\u7406\u6392\u73ed\u8cc7\u6599": {
      "main": [
        [
          {
            "node": "\u767c\u9001 Slack \u901a\u77e5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u6bcf\u65e5\u65e9\u665a 8:00 \u89f8\u767c": {
      "main": [
        [
          {
            "node": "\u8b80\u53d6\u6392\u73ed\u8868",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d6d750dc-23d3-4723-9a5e-9773b89132d5",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "IpjZzAXLRwSjcLza",
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Jen Daily Schedule Alert. Uses googleSheets, slack. Scheduled trigger; 4 nodes.

Source: https://github.com/lettucebo/N8N-Flows/blob/52ab732270953f61117544747dae01a81fdde982/flows/JenDailyScheduleAlert/Jen_Daily_Schedule_Alert.json — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

This workflow continuously monitors the Meta Ads Library for new creatives from a specific competitor pages, logs them into Google Sheets, and sends a concise Telegram notification with the number of

HTTP Request, Telegram, Google Sheets +1
Slack & Telegram

Enhance financial oversight with this automated n8n workflow. Triggered every 5 minutes, it fetches real-time bank transactions via an API, enriches and transforms the data, and applies smart logic to

HTTP Request, Email Send, Google Sheets +1
Slack & Telegram

This workflow automates competitive price intelligence using Bright Data's enterprise web scraping API. On a scheduled basis (default: daily at 9 AM), the system loops through configured competitor pr

HTTP Request, Google Sheets, Slack +1
Slack & Telegram

Ensure your customer SLAs never slip with this n8n automation template. The workflow runs on a schedule, fetching open tickets from Zendesk, calculating SLA time remaining, and sending proactive alert

Zendesk, Slack, Google Sheets
Slack & Telegram

&gt; n8n, Binance API, Google Sheets, Slack, Telegram, Jira & Email

HTTP Request, Google Sheets, Slack +3