[AI 自動化] 用 n8n 做刷卡消費記錄(富邦、國泰、匯豐)
上次 n8n 試用結束後,因為發現 Make 比較便宜,就跳槽過去了。後來有網友留言,推薦我試試自架 n8n,我馬上心動就也來自架了,結果真的一試上癮。
目前已經把上次 Make 的流程搬過來,要拋棄 Make 了。Productivity tool 的世界真殘酷。哪個稍微更 productive,使用者馬上就跳走...
喜歡 n8n 因為:
- 內建 node 種類多
- HTTP Request 和 Webhook 非常彈性且強大,基本上等於是快速串 API 的方式
- 介面回饋速度更快、效能高
- Open Source
- Workflow 設定本身就是一個 json,非常容易共享
- 社群討論和 templates 分享還算活躍
n8n 架設真的很簡單。我架了一個在我的 AWS,使用 docker compose,教學可以看這篇。有遇到一些小卡,主要是我用 nginx 做 redirect,所以沒用到 traefik,一開始沒發現而相衝。但沒有什麼是 AI 回答不出來的,順利裝好。
後來又幫公司裝一個在 heroku,看這篇教學。也有一些 .env 設定的小卡住,但後來也解決了。如果你對於自架 n8n 有遇到問題,歡迎私訊問我。我的 IG 是 @hanamizuki。
接下來開始建立吧!
建立 Workflow
在 Make 叫做 Scenario,在 n8n 是 Workflow。下圖是原本 Make 的畫面以及 n8n 的畫面。
基本邏輯一樣,
- Trigger: Gmail 收到交易通知
- Router: 根據信件類型要有不同的文字處理
- 請 AI 根據信件內容回傳欄位內容
- 送到 Google Sheet
因為已經用 Make 自動記帳兩週,有發現可以優化的地方,轉移到 n8n 的過程中又做了一些優化,如下。
- 調整富邦信用卡消費記錄的 search query
- AI prompt更完整,比如美金要到小數點後兩位,台幣不用等等
- 改用免費的 gemini 1.5 flash(也可以快速的自己換成其他 model)
- 優化了 Google Sheet 格式
建立 n8n 流程的第一步:Trigger
Trigger 選擇 Gmail,設定如下:
- Pool Times: 我選每小時抓
- Event: Message Recieved
- Simplify: Off
- 原本是希望打開的,打開的話輸出的格式比較乾淨。可是的,可恨的是,有幾家銀行的信件太過傳統,沒有 snippet、沒有純文字版本,只有落落長的 html。因此這邊還是必須打開,然後再用 parser 去整理內容。
- Filters: 選 Search
- 可以參考上篇 Search Query 教學
提供我的 Search Query 當參考
{from:(noreply@messaging.hsbc.com.tw) AND subject:(匯豐(台灣) "刷卡消費通知”)} OR {from:(hello@crypto.com) AND subject:(purchase complete)} OR {from:(@taipeifubon.com.tw) AND subject:(富邦 即時消費通知)} OR {from:(service@pxbillrc01.cathaybk.com.tw) AND subject:(國泰世華銀行消費彙整通知)} OR {from:(noreply@uber.com) AND {subject:(透過 Uber Eats 系統送出的訂單) OR subject:(在 Uber 平台上搭乘的行程)}} OR {from:(no_reply@email.apple.com) AND subject:(Your invoice)}
OR {from:(bot@heroku.com) AND subject:([billing] Heroku Invoice)}
OR {from:(invoice+statements) AND subject:receipt}
OR {from:(@stripe.com) AND subject:receipt}
這個範例可以記帳的範圍包含:
- 信用卡刷卡記錄:匯豐、富邦、國泰、crypto.com
- 消費記錄:Uber、Uber Eats、Apple、Stripe、Heroku
記帳時,「2. 消費記錄」純粹是為了補充「1. 信用卡刷卡記錄」的資訊,因為刷卡記錄通常沒有細項。
使用 Switch
Switch 是一個 router,能根據前面 node(也就是 trigger)傳過來的資訊,開出多條後續流程。因為每種資料要整理的方式可能不同。我的案例:
- 國泰世華的通知是一天一封,並且只有肥大 html,裡面還有多筆消費,會需要從信件拆多筆出來
- 其他信用卡交易通知都是一個交易一封
- 消費記錄(像是 uber)算第三種,送到 google sheet 時要有不同處理,以免 double accounnting
設定如下:
- Mode: Rules
- Routing Rules:
- 寄件者(
{{ $json.from.value[0].address }}
) = service@pxbillrc01.cathaybk.com.tw 時,是國泰刷卡記錄 - 寄件者是
\b(?:noreply@messaging\.hsbc\.com\.tw|hello@crypto\.com|taipeifubon\.com\.tw)\b
時,是其他信用卡刷卡記錄(匯豐、富邦、crypto.com) - 寄件者「不是」以上者些時,則是消費記錄
- 寄件者(
處理國泰消費彙整通知信
這部份我處理起來其實有點意興闌珊,要不是最近愛著 n8n ~~,國泰這樣的信件(和越來越爛又麻煩的優惠 ~~... 每次都忘記切換。對我就是用 Cube)看著看著我就想剪卡。
國泰的信件長這樣:
而這是一大包 table,如果直接 strip html 也不太行,因為輸出的格式會變得很亂,即使有四種 AI model,我還是不想給他們這麼大的閱讀挑戰。
好在看起來這包 table 每一筆資料都有一個 .spend_table,料想他們工程師應該不會一直改這個名稱,就這樣用了。
如果你的 Trigger Node 已經試跑過而且有成功抓到你想測試的內容,可以按一下 pin。這樣即使之後重新整理,這個 node 不用重跑也會有資料可以測試。(不過全部完成要 enable 這個 workflow 時記得要 unpin)
如果你想直接在 gmail 裡面看信件的 html 長怎樣,也可以在 gmail 介面使用 Inspect 選擇「Show Original 」。
回到 n8n 設定,我們可以使用 n8n 內建的 HTML node。
- Operation: Extract HTML Content
- Source Data:JSON
- JSON property: html
- Extraction Values:
- Key: spend
- CSS selector: .spend-table
- Return value: Text
- Return array: True
這樣就可以把雜亂的 html 變成乾淨的兩筆資料。
接下來還有一個步驟,因為剛才回傳的是一筆裡面有 array 的資料,但 node 執行的是 line item,所以要用 split 把他變成兩筆資料。
我們用內建的 Split Out Node 即可。
Field to split out: spend
再來試跑,就會看到他變成右邊那樣兩筆資料。雖然這串文字很亂,但我們就丟給 AI 吧!
整理資料
在丟給 AI 前,我們先把我們要的資料整理好。可以用內建的 Set data node。
(2024/10/6 更新如下,主要是三種狀況的資料可能不同,要先整理好,不能只用一個 Set Data)
Set Data 0
- Mode: Manual Mapping
- Fields to Set:
- email_date:
{{ $('Switch').item.json.date }}
這邊抓信件的日期給 AI 當參考,因為信件內容有可能沒有日期 - email_subject:
{{ $('Switch').item.json.subject }}
這邊抓信件標題,一樣是給 AI 當參考,讓他知道他在閱讀的是這封信 - email_content:
{{ $ifEmpty($json.spend, $ifEmpty( $json.text, $json.html)) }}
這邊抓國泰資料,如果不是國泰的,就直接抓信件的 text,若是也沒有 text,就抓 html。 - email_type:
0
從 router 出去目前有三條路,給個編號,後面會用到。
- email_date:
Set Data 1
- Mode: Manual Mapping
- Fields to Set:
- email_date:
{{ $json.date }}
- email_subject:
{{ $json.subject }}
- email_content:
{{ $ifEmpty( $json.text, $json.html) }}
直接抓信件的 text,若是也沒有 text,就抓 html。 - email_type:
1
- email_date:
Set Data 2
- Mode: Manual Mapping
- Fields to Set:
- email_date:
{{ $json.date }}
- email_subject:
{{ $json.subject }}
- email_content:
{{ $ifEmpty( $json.text, $json.html) }}
直接抓信件的 text,若是也沒有 text,就抓 html。 - email_type:
2
- email_date:
請 AI 閱讀信件並回傳帳務資訊
(2024/10/6 更新:需要新增 Merge 步驟,並把上面三個 Merge Data 分別透過 Input 1~3 整合進去)
資料都整理好,就可以給 AI 看了。可以用 n8n 的 Basic LLM Chain node,可以取名叫做「Extract details」。然後選擇 model。n8n 也已經幫大家串好很多 ai model 了。
目前我用過這幾種:
- OpenAI: 記得一定要用 4o-mini,4o 和最新的 o1 都超貴,上次選錯,一天就噴好多錢,真的要小心
- Gemini: 免費
- Claude: 最近幾個月開始訂閱的,覺得還不錯
- Groq: 剛開始用
至少以上四種都很好串,OpenAI 和 Claude 需要先支付一筆錢購買 credit,Gemini 和 Groq 則是免費的。一樣都是取的 API key 就可以 plug and play。大家可以自己考量看看。
我現在用 Gemini 是因為免費。
接下來進入設定。
- Prompt: Define below
- Text:
這是我的消費明細信件中的單筆消費內容,請幫我擷取內容來記帳。其中信件來源有 0, 1, 2 三種選項。0 是國泰卡明細,1 是其他信用卡明細,2 是消費記錄。如果是 2,card 欄位回傳 null。
這部份大家可以自己調整成你要的,就跟和 ChatGPT 說話一樣。 - Require Specific Output Format: True
- Chat Message:
- Type Name or ID: User
- Message Type: Text(也許之後可以試試截圖給他看?但目前先用文字)
- Message:
信件日期:{{ $json.email_date }} 信件標題:{{ $json.email_subject }} 信件內容: {{ $json.email_content }} 信件來源:{{ $json.email_type }}
因為我們希望有輸出固定欄位,所以要加上一個 Output Parser。
- Schema Type: define below
- Input Schema:
{
"title": "消費記錄 Schema",
"description": "用於解析消費記錄信件的 Schema,包括日期、服務名稱、交易詳情、金額、類別、貨幣和卡片。",
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "交易日期,可參考信件日期或內容內的消費日期。若有多個日期,以最早的日期為主。格式為 'YYYY-MM-DD',例如 '2024-09-02'。",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"examples": ["2024-09-02"]
},
"service": {
"type": ["string", "null"],
"description": "服務或商店的名稱,例如 'GOOGLE'、'Uber' 等。",
"examples": ["GOOGLE", "Uber Eats", "Uber", "CLAUDE.AI"]
},
"details": {
"type": ["string", "null"],
"description": "交易的詳細訊息,如海外刷卡、網路刷卡、餐廳名稱或消費細節。若無可留空或為 null。",
"examples": ["Uber:從復興北路到民權東路", "餐廳名稱", null]
},
"amount": {
"type": "number",
"description": "交易金額。若為美元,保留小數點後兩位(如 50.12);若為台幣則使用整數(如 550)。",
"examples": [50.12, 550]
},
"category": {
"type": "string",
"description": "交易類別",
"enum": ["飲食", "交通", "日用品", "居住", "3C", "美容妝髮",
"examples": ["飲食", "交通"]
},
"currency": {
"type": "string",
"description": "交易使用的貨幣代碼,如果金額是 NT$ 開頭,curreny 就是 TWD。",
"enum": ["TWD", "USD", "JPY", "EUR", "SGD"],
"examples": ["USD", "TWD"]
},
"card": {
"type": ["string", "null"],
"description": "交易使用的信用卡。",
"enum": ["匯豐", "富邦", "Crypto.com Card", "國泰", null],
"examples": ["匯豐", "國泰"]
}
},
"required": ["date", "amount", "category", "currency"]
}
交易類別的部分可以自訂。
送到 Google Sheet
這部份相對單純,先準備好 Google Sheet。我這邊有一個範本。
https://docs.google.com/spreadsheets/d/1_IhdHj8bxtsfH2MRqKuU2LzJuzm4DaeKSw46eFcyYts/edit?gid=1617968863#gid=1617968863
是長這樣的:
在 n8n 建立一個 Google Sheet node。設定如下:
- Credential to connect with: 選一個
- Resource: Sheet Within Document
- Operation: Append Row
- Document: 貼上 Google Sheet 網址
- Sheet: from list 選擇 raw data
- Mapping Column Mode: Map Each Column Manually(如果選自動的話,他會去找 n8n 資料的欄位名稱和 Google 第一個 row 的名稱做比對,也是一種選擇,但我怕我腦殘去改 Google Sheet,所以這邊先手動寫死)
- Value to send: 選擇剛才的欄位名稱,如
{{ $json.output.date }}
哇啦!這樣就完成啦!
其實如果你有興趣,上面這些步驟都可以省略直接複製下面這段 JSON 貼到 Workflow,就可以把上面提到的設定都一次匯入。這也是我很喜歡 n8n 的地方。把分享做的這麼方便,大家還不狂分享嗎? 那我為何不直接貼下面的 JSON?還要打這麼長的內容?
最後可以來 Test 一下,看看資料有沒有出現在你的 Google Sheet 上。沒有的話,可以到 Executions 看錯誤資訊,貼給 AI 看,通常很快可以獲得答案。如果有卡住也歡迎跟我說。
對帳方式
上面的流程基本把這些信用卡的紀錄都包含進去了,若 n8n 跑的順利,你每天進來 Google Sheet,什麼都不用做,直接在「book」這個 sheet 可以看到今天的交易記錄,是已經整合刷卡記錄和消費記錄的資訊放一起的。
有個 check 欄位則是方便大家確認這筆消費沒問題可以打勾的,Note 則是手動寫個筆記。像這樣:
結語
可以用 n8n 做 AI 自動化真的是很幸福的事情,打開了很多可能性。我現在還有用 n8n + AI 做 Twitter 分析、Segment、Amplitude、Databricks、Asana ... 等自動化,節省了不少時間,實在太快樂了!