專案筆記

claude_linebot 部署教學:LINE channel、Cloudflare Tunnel 與開機自動啟動

2026-08-09 #教學#Claude Code#LINE#Cloudflare Tunnel#macOS

系列最後一篇,把 claude_linebot 從零部署起來:申請 LINE channel、設定 .env、用 Cloudflare Tunnel 把本機 webhook 掛上公網(quick tunnel 與 named tunnel 兩種),最後註冊成 macOS LaunchAgent 開機自動跑。(前情提要:動機架構

前置需求

  • macOS(防睡眠用 caffeinate;Windows 也能跑,自動走 Win32 分支)
  • Python 3.10+
  • 已安裝、登入完成的 claude CLI
  • LINE Messaging API channel(下面申請)
  • cloudflaredbrew install cloudflared

LINE 是 webhook 模式——LINE 的伺服器要能主動連到你的電腦,所以需要一個公開 HTTPS 網址。Cloudflare Tunnel 用一條 outbound 連線解決這件事:不開防火牆 port、不需要固定 IP。

一、申請 LINE channel

  1. LINE Developers Console 建立 Provider(沒有的話)
  2. 建立 Messaging API channel
  3. 記下兩樣東西:Channel secret(Basic settings 分頁)和 Channel access token(Messaging API 分頁,按 Issue)

二、設定並啟動 bot

1
2
3
git clone https://github.com/murmur-wu/claude_linebot.git ~/claude/lineBot
cd ~/claude/lineBot
cp .env.example .env

.env 至少填:

1
2
3
4
5
LINE_CHANNEL_SECRET=...
LINE_CHANNEL_ACCESS_TOKEN=...
ALLOWED_USER_IDS= # 先留空,等下用 bot 的回覆拿自己的 id
PROJECTS=blog:/Users/you/projects/blog;work:/Users/you/projects/work
DEFAULT_PROJECT=blog

啟動(首次會自動建 .venv、裝套件):

1
./start.sh

專案路徑建議放 ~/claude/ 這類一般目錄。放在 ~/Desktop~/Documents~/Downloads 的話,之後服務化會撞上 macOS 的保護目錄限制(見最後一節)。

三、Cloudflare Tunnel

先用 quick tunnel 打通流程

1
./tunnel.sh          # 會印出 https://xxxx.trycloudflare.com

quick tunnel 不用註冊、不用網域,適合驗證整條流程。缺點是網址每次重啟都會變,變了就要回 Console 更新 Webhook URL——所以打通後建議直接升級 named tunnel。

設定 Webhook URL

LINE Developers Console → 你的 channel → Messaging API 分頁:

  1. Webhook URLhttps://xxxx.trycloudflare.com/callback
  2. Use webhook 打開
  3. Auto-reply messages 關掉(不然官方罐頭回覆會跟 bot 打架)
  4. Verify → 應該回 Success

拿白名單 id

用手機加 bot 好友、傳任意訊息,bot 會直接回你的 userId(一串 U 開頭 33 字)。填進 .envALLOWED_USER_IDS./restart.sh 重啟。這是 fail-closed 設計:名單空著時 bot 拒絕所有人,不存在「忘了設定所以全開」的狀態。

升級 named tunnel(固定網址)

前提:你有一個網域託管在 Cloudflare(免費方案即可)。

1
2
cloudflared tunnel login          # 瀏覽器授權,選你的 zone
cloudflared tunnel create linebot

建立 ~/.cloudflared/config.yml

1
2
3
4
5
6
7
tunnel: linebot
credentials-file: /Users/<你>/.cloudflared/<tunnel-uuid>.json

ingress:
- hostname: linebot.example.com
service: http://127.0.0.1:8000
- service: http_status:404

把 DNS 指到 tunnel、再啟動:

1
2
cloudflared tunnel route dns --overwrite-dns linebot linebot.example.com
./tunnel.sh # 偵測到 config.yml 就自動跑 named tunnel

回 Console 把 Webhook URL 改成 https://linebot.example.com/callback,Verify 一次。從此網址固定,重啟也不用再碰 Console。

安全上的細節:bot 的 HOST 預設綁 127.0.0.1,只有 tunnel 進得來,不對區網開放;webhook 已內建 X-Line-Signature HMAC 驗簽,驗不過直接 400。

四、日常操作

1
2
./restart.sh              # 改完 .env 重啟(自動判斷 launchd / nohup 模式)
tail -f state/bot.log # 看 log

重啟不會斷對話——session_id 存在 state/sessions.json。改 .env 不用重啟 tunnel(除非改了 PORT)。

五、開機自動啟動(LaunchAgent)

1
./services/install-service.sh     # 註冊 bot 與 tunnel 兩個 LaunchAgent

用的是使用者層的 LaunchAgent 而不是系統層的 LaunchDaemon——原因跟 Windows 版不能用 LocalSystem 一樣:claude CLI 的認證在 ~/.claude/,以 root 跑找不到。代價是「登入後」才啟動;要真正開機即跑,到系統設定開自動登入。

macOS 保護目錄的坑

如果專案放在 ~/Desktop~/Documents~/Downloads(TCC 保護目錄),launchd 會遇到一連串限制:不能 chdir 進去、不能直接 exec 裡面的執行檔、不能開啟由 Terminal 建立的既有 log 檔——而且失敗時只給 exit code 78 (EX_CONFIG),零錯誤輸出,極難排查。

最陰險的是第三項:你先用 ./start.sh 手動跑過,state/bot.log 的存取權歸屬 Terminal;改成服務後 launchd 開不了那個檔案,服務永遠起不來。

install-service.sh 已經把能繞的都繞了(不設 WorkingDirectory、透過 /bin/sh -c 'exec …' 啟動、自動把舊 log 改名),但「launchd 啟動的程序讀取保護目錄檔案」這關必須手動授權:系統設定 → 隱私權與安全性 → 完全磁碟取用權 → 加入 <專案>/.venv/bin/python

不想開這麼大的權限?把專案移出保護目錄(例如 ~/claude/lineBot)就完全不需要任何授權——這也是本文開頭建議 clone 到 ~/claude/ 的原因。

常見坑速查

症狀 原因
Console 按 Verify 回 504 tunnel 沒跑,或 ingress 沒指到 bot。先 curl 127.0.0.1:8000/healthz 確認本機活著
自己的測試腳本收到 403 Cloudflare 擋部分 User-Agent(如 Python-urllib),換個 UA 就好,LINE 本身不受影響
userId 填了還是被拒 LINE userId 固定 33 字,手抄常漏字——直接從 state/bot.log 的 rejection log 複製
群組裡 bot 完全沒反應 依序查:ALLOW_GROUPS=true 且重啟過、訊息帶 claude 前綴、發話者在白名單
.env 沒生效 bot 不會自動重讀設定,要 ./restart.sh
LaunchAgent exit 78 且無 log 保護目錄問題,見上一節

系列回顧

  • 第一篇:動機——為什麼是 LINE,三大平台難題
  • 第二篇:架構——webhook 管線、回覆經濟學、兩段式附件
  • 本篇:部署——LINE channel、Cloudflare Tunnel、LaunchAgent

到這裡,家裡的 Mac 就成了一台隨身可指揮的開發機:LINE 傳個訊息,Claude Code 改 code、跑指令、commit,結果回到手機上。

留言
分享