參加 iTHome 鐵人賽最怕的是什麼?除了沒靈感、或是寫不完,甚至到最後忘記在午夜前按下「送出」按鈕,導致前功盡棄 QQ
今天,就讓我們一起動手,用自動化工具 n8n 打造一個忠實的發文提醒小助理。它會在每天晚上檢查你是否已發文,如果還沒,就立刻發送 Discord 通知,確保你不會因為一時疏忽而斷賽
這個流程的邏輯是: 「每天在指定時間觸發 -> 抓取你的鐵人賽 RSS -> 檢查最新一篇文章的發布日期 -> 如果最新文章不是今天發的 -> 就發送通知提醒自己」
這樣可以確保在午夜 12 點前,如果還沒發文,就會收到警告,避免斷賽的悲劇發生
在開始動手前,我們先用一張流程圖來理解整個自動化腳本的樣貌
graph TD
A[開始] --> B[1. Schedule 排程觸發];
B --> C[2. RSS Feed Read 讀取 RSS];
C --> D[3. Aggregate 合併資料];
D --> E{4. IF 判斷是否已發文};
E -- 否/False --> F[5a. Discord 發送提醒];
E -- 是/True --> G[5b. NoOp 結束流程];
來到儀表板,新增一個流程「Create Workflow」
初始節點選擇排程「On a schedule」
時間設定為晚上的 9pm
接著到設定的地方
設定為台灣時區
下個節點選擇「RSS Read」
URL 填入你鐵人賽的 RSS 網址
接著切換到「Settings」的分頁,把「Always Output Data」打開,避免第一天沒有資料就不會進行下去
為了方便後續的判斷,我們需要將 RSS 抓回來的多篇文章資料合併成一個陣列
下個節點選擇「Aggregate」來合併資料
選擇「All Item Data」會把列表的資料都放進「data」的變數裡面
下個節點選擇「IF」,用來判斷「最新文章的日期」是否等於「今天的日期」
判斷式內容填以下的內容,格式選擇「Boolean」的「is true」
{
{
$json.data.some(
(item) =>
new Date(item.isoDate).toISOString().split("T")[0] ===
$now.toFormat("yyyy-MM-dd")
);
}
}
當 IF
節點判斷為 false
時,就傳送 Discord 通知,這邊的下個節點可以選擇「Discord」的「Send a message」來傳送訊息
「Connection Type」選擇「Webhook」,憑證的串接在之前的文章有撰寫過,這邊就不重複惹
Message 可以設定為
**🚨 iTHome 鐵人賽警告 🚨**
@everyone 今天還沒有發文喔!再不寫就要斷賽啦!
而當 IF
節點判斷為 true
時,我們什麼事都不用做,可以連接一個 NoOp
(No Operation) 節點,讓流程圖看起來更完整、語意更清晰
可以點選最下方的「Execute workflow」來試跑看看,而完整的流程會長這樣,這邊也要記得到上方把流程切換為「Active」啟用
Discord 會收到這樣的訊息
最後也附上完整的流程 JSON 內容
{
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 21
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"id": "4e275616-c21c-411e-b510-bc2510aef7ca",
"name": "Schedule Trigger"
},
{
"parameters": {
"url": "https://ithelp.ithome.com.tw/rss/series/8496",
"options": {}
},
"type": "n8n-nodes-base.rssFeedRead",
"typeVersion": 1.1,
"position": [220, 0],
"id": "9f72e3cb-8f20-4e5f-8732-79667a2cecbd",
"name": "RSS Read",
"alwaysOutputData": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "b59baac4-6a94-434d-a9cb-ccbe13ab557b",
"leftValue": "={{ $json.data.some(item => new Date(item.isoDate).toISOString().split('T')[0] === $now.toFormat('yyyy-MM-dd')) }}",
"rightValue": "={{ $now.toFormat('yyyy-MM-dd') }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [660, 0],
"id": "fdf179a3-e78f-4d53-9cb4-069871445fed",
"name": "If"
},
{
"parameters": {
"authentication": "webhook",
"content": "**🚨 iTHome 鐵人賽警告 🚨**\n@everyone \n<@YOUR_DISCORD_USER_ID>\n今天還沒有發文喔!再不寫就要斷賽啦!",
"options": {}
},
"type": "n8n-nodes-base.discord",
"typeVersion": 2,
"position": [880, 100],
"id": "80fdff87-081c-4fe4-8f23-c7387985eb54",
"name": "Discord",
"webhookId": "<REDACTED_WEBHOOK_ID>",
"credentials": {}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [880, -100],
"id": "2e8a21ad-34db-4bd8-8391-3685b3f0e054",
"name": "No Operation, do nothing"
},
{
"parameters": {
"aggregate": "aggregateAllItemData",
"options": {}
},
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [440, 0],
"id": "2e31e8b0-7b45-463d-b117-7f5f4934a48c",
"name": "Aggregate"
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "RSS Read",
"type": "main",
"index": 0
}
]
]
},
"RSS Read": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
],
[
{
"node": "Discord",
"type": "main",
"index": 0
}
]
]
},
"Aggregate": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "<REDACTED_INSTANCE_ID>"
}
}
這樣就完成了一個可靠的鐵人賽發文通知小助理,再也不用擔心忘記發文而斷賽惹。也祝大家順利完賽!