iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0

晚上八點,系統的排程介面顯示「投資晨報:執行成功 ✅」,容器的 log 翻到底沒有半行紅字,甚至 Telegram API 回傳的也是 HTTP 200 OK。

但我的手機螢幕就是沒亮,該跳出來的訊息硬生生消失了。

這種錯最磨人——不是會在你面前爆炸的崩潰,而是一切看起來都對,結果什麼都沒發生。我為了一則消失的投資晨報,查了整整三天。也是因為這個坑,我學到一件事:通訊層「建立」只是一半,另一半是「測試」——因為它的失敗是無聲的,你不主動驗證就不會知道它壞了。

今天就照「建立 → 測試」走一遍。


為什麼是 Telegram,不是 LINE?

在台灣問這個問題很合理——身邊的人都用 LINE,做一個 LINE 官方帳號來推播看起來更自然。我也真的先做了。

卡住我的是免費額度的推播則數上限

LINE 官方帳號的免費方案,每個月能主動推播的訊息數是有上限的;超過就要升級付費方案。對一般的商業帳號來說這很合理——你推播是為了做生意。但我的用途完全相反:

  • 每天早上兩份晨報
  • 白天每兩小時的環境巡檢,只要有異常就推
  • 排程失敗、快取過期、設備離線的告警
  • 加上臨時的提醒

這些全部都是主動推播,而且我希望「該叫的時候一定要叫得出來」。一個會因為額度用完而安靜的告警系統,比沒有告警更危險——這正是後面 Day 20 監控篇的核心主題。

所以取捨很清楚:**我需要的是推播無上限、而非互動體驗最好的通道。**Telegram 的 Bot API 在這一點上完全免費、沒有則數限制(只有頻率限制,見文末),這對一個每天要推幾十則的個人系統來說是決定性的。

補充一個公平的說法:如果你的用途是「偶爾通知一下」,LINE 官方帳號的免費額度很可能夠用,而且家人不用另外裝 App。選通道要看你推播的頻率,不是看哪個比較多人用。

為什麼用 Telegram 群組 + Topic

架構上,六個 Agent 每天會產出不同類型的訊息:投資晨報、家居警示、備考提醒、生活規劃……全部塞進同一個對話視窗會變成洗版災難。

解法是用 Telegram 的群組 + Topic(主題)——一個群組底下開多個 Topic 分頻道,每個 Agent 各走各的:

Topic ID 頻道用途 對應 Job 類型
2 投資 盤前/收盤推播、持倉異動告警
4 碩士備考 進度提醒、截止倒數
29 命理 每日能量檢查
97 智慧家庭 環境感知、設備離線告警
102 程式開發 開發看板、部署驗收
103 生活規劃 週行程、每日收尾回顧

⚠️ 群組 ID 已遮蔽(文中一律寫成 -100XXXXXXXXXX)。Topic 編號是實際值,但它單獨沒有用——沒有群組 ID 和 Bot Token,任何人都打不進來。這裡列真值是為了讓後面「哪個 Job 該去哪個頻道」的例子對得起來。

概念很乾淨:Job 設定裡指定 chat_id(群組)和 thread_id(Topic),訊息就會落到對的頻道。魔鬼藏在「怎麼建」跟「怎麼確認它真的動了」。

先分清楚:發訊息和收訊息是兩條不同的路

這件事我一開始沒搞懂,白繞了不少路——Bot 的「發」和「收」用的是完全不同的機制,而且對網路的要求天差地遠

方向 怎麼運作 需要對外開東西嗎
發訊息 我的系統 → Telegram 對 Telegram 的 API 發一個 HTTPS 請求 完全不用——這是我主動打出去的
收訊息 Telegram → 我的系統 兩種選擇(見下) ,或者要一直輪詢

收訊息的兩種做法:

  • Webhook:跟 Telegram 註冊一個網址,有人傳訊息給 Bot 時,它主動 POST 到你這裡。即時、不浪費資源,但需要一個公網可達的 HTTPS 端點
  • Long polling:你的程式反覆去問 Telegram「有新訊息嗎?」。不需要對外網址,但要一直輪詢。

我選的是 webhook。而註冊 webhook 時可以一併設一組 secret token——之後 Telegram 每次 POST 都會帶著它,我這邊驗證這組值,才知道「這真的是 Telegram 打來的,不是別人假冒」。對外開一個端點,就一定要有辦法驗證來源。

這個不對稱有個實際的好處,也解釋了明天那篇的必要性:

如果我只要推播,家裡的網路什麼都不用動。
是因為我想「跟它對話」,才需要一個對外可達的入口。

換句話說,Day 6 那條 Cloudflare Tunnel 只服務「收」這個方向。如果你的需求純粹是「系統有事通知我」,可以完全跳過那一篇——這也是我建議大家從推播開始做的原因:先享受單向的好處,需要雙向時再處理網路。


建立:五步把通訊層接起來

https://ithelp.ithome.com.tw/upload/images/20260801/20182865jCrS7dOpxH.png

實際串接沒幾步,但最後「查 ID」那步最容易卡住,先把流程走一遍:

  1. 建 Bot:在 Telegram 找 @BotFather,輸入 /newbot 一路建到底,拿到一組 Bot Token——這把就是對外發訊的鑰匙,只進 .env、不進 git。
  2. 開群組並啟用 Topic:建一個群組,到群組設定把「主題(Topics)」打開,它會升級成 supergroup,才能分頻道。
  3. 把 Bot 拉進群組並給管理員:Bot 一定要設成管理員,否則發文、讀訊息都會被擋——這本身就是很多「靜默失敗」的根因之一。
  4. chat_idthread_id:這兩個值介面上看不到,得用 Bot API 撈。在目標 Topic 隨手發一則訊息,呼叫 https://api.telegram.org/bot<TOKEN>/getUpdates,回應裡的 chat.id 就是群組 ID(負號大數字),message_thread_id 就是那個 Topic 的 ID。
  5. 填進 Job 設定:把 chat_id 和各 Topic 的 thread_id 填進每個 Job 的 delivery 設定。

thread_id 還有個偷吃步:Telegram 桌面版對著 Topic「複製連結」,網址最後一段數字就是 thread_id。

接好之後,先別急著上線——通訊層的失敗是無聲的,建立完一定要測。


測試:怎麼確認訊息真的有送到

通訊層最反直覺的一點:API 回你 ok: true,不代表訊息真的出現在頻道裡。 一般程式錯了會丟例外,Telegram 這裡很多失敗是「成功地什麼都沒做」。所以建好之後第一件事不是上線,是測試——而且要人眼確認。我的三步測試法:

  1. 逐頻道發測試訊息:對每個要用的 Topic,各發一則帶時間戳的測試訊息,親眼到那個頻道確認它真的出現、而且落在對的頻道。
  2. 狀態列不能信,要看原始 log:排程的狀態列只給你一個騙人的綠燈。真正查得下去的地方是 OpenClaw 的 Control UI——把那次執行的原始 log 整段抓出來。
  3. 撈 log 餵給 AI 定位:我把 log 直接丟給 AI 分析,讓它幫我比對 API 回應、定位問題。這也是自架 AI 系統有趣的地方:除錯不是埋頭翻 log,而是 Control UI 撈 log → 丟 AI 分析。

測試時你會踩到的,正好是這三個典型的坑——把它們當成上線前的檢查清單

坑一:群組 ID 沒帶負號(這個還算佛心,會報錯)

第一次串接時我把群組 ID 直接填數字進去,結果 API 回 chat not found。Telegram 的群組(supergroup)ID 是負數大數字-100XXXXXXXXXX),個人對話才是正數。少了那個負號,API 就當你在找一個不存在的個人聊天室。至少它會出聲——後面兩個就不會了。

坑二:topic:1(General)的靜默失敗

群組剛建好時,預設會有一個「General(通用)」主題,它的 thread_id1。我很自然地把第一個 Job 指到 topic:1,結果狀態 ✅、log 無錯誤,訊息卻沒出現

一開始我以為是 Docker 網路或 DNS 出問題。為了排除框架的干擾,我退回最原始的工具,直接用 curl 手動打 API:

curl -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
     -d "chat_id=-100XXXXXXXXXX" \
     -d "message_thread_id=1" \
     -d "text=Hello Test"

猜猜 API 回了什麼?{"ok":true,"result":{...}}。Telegram 明確告訴我「成功送達」,但手機依然毫無動靜。查了無數論壇才搞懂:Bot 無法以 thread_id=1 發文到 General 主題——這是 Telegram 端的限制,但 API 不報錯,默默把訊息丟進黑洞,回你一個大大的 true

這就是「靜默失敗」最危險的地方:監控全部綠燈,但功能其實是壞的。 教訓一句話:任何 Job 都不准用 topic:1

坑三:不存在的 Topic,一樣靜默失敗

修掉 topic:1 之後,命理頻道的能量檢查又消失了。這次是 Job 指到 topic:3,但這個 Topic 根本不存在(我中途調整頻道時刪掉了舊的)。照理說該回 404 或 Bad Request,但 Telegram 一樣靜默吞掉訊息、回傳 ok:true。改指到實際存在的 topic:29 才恢復。

三個坑合起來一句話:Telegram 對「無效的 thread_id」一律靜默吞掉。你不能靠 API 回應判斷訊息有沒有送出去——只能靠主動測試。


防呆:把有效 Topic 清單寫進品質閘道

查了三天的代價,總要換成制度。既然 API 靠不住,那就在 Job 進入系統前先擋下無效設定。我在排程層做了一個「品質閘道(Quality Gate)」,任何要新增或修改的推播任務都得先過這層驗證:

// 品質閘道:Telegram Topic 防呆驗證
const VALID_TOPIC_IDS = [2, 4, 29, 97, 102, 103];

function validateTelegramDelivery(config) {
  const topicId = parseInt(config.thread_id, 10);

  if (topicId === 1) {
    throw new Error("🚨 [品質閘道拒絕] 嚴禁使用 topic:1 (General),會導致靜默失敗!");
  }
  if (!VALID_TOPIC_IDS.includes(topicId)) {
    throw new Error(`🚨 [品質閘道拒絕] 未知的 Topic ID: ${topicId}。請確保頻道已建立並加入白名單。`);
  }
  return true;
}

topicId 不在白名單就在建立階段直接拋 Error 擋下——我把 Telegram 那個「不報錯」的行為,用自己的閘道補成「會大聲報錯」。這是自架系統很核心的心法:當底層工具會靜默失敗,你的責任就是在上層幫它補一道會出聲的防線。


同場加映:兩個 LLM 管家常踩的隱藏限制

既然講到 Telegram,再補兩個自架時一定會遇到的雷點:

1. 單則訊息 4096 字元上限
有時候投資長分析財報太熱情,回傳一大篇 Markdown,API 就報 MESSAGE_TOO_LONG。Telegram 單則訊息上限是 4096 字元。解法是在 Gateway 層加攔截器:超長就自動切塊(chunking)分多次發,或強制截斷並附上「…(字數超過,請至 md-server 查看)」。

2. 頻率限制(Rate Limit, HTTP 429)
如果六個 Agent 都設在早上 8:00 準時發晨報,有機率觸發 429 Too Many Requests(同群組廣播大約每分鐘 20 則上限)。排程器要加重試機制(Exponential Backoff),被擋了就等幾秒再傳,別 fire-and-forget。


小結

通訊層的功課,一半在「建立」、一半在「測試」:

  • 選通道:看推播頻率而不是看誰多人用——免費額度有則數上限的通道,不適合當告警管道。
  • 收發不對稱:發訊息不需要對外開任何東西;收訊息才需要 webhook(或輪詢)。想清楚你只要單向還是要雙向。
  • 建立:BotFather 拿 token → 開群組啟用 Topic → Bot 給管理員 → getUpdateschat_id / thread_id → 填進 Job。
  • 測試:逐頻道發測試訊息、人眼確認;狀態列會騙人,要從 Control UI 撈 log 丟 AI 分析。
  • 三個必驗的坑:群組 ID 負號、topic:1、不存在的 thread_id——Bot 發文會靜默失敗(回成功但訊息消失)。
  • 別忘了:4096 字元上限、429 頻率限制。

靜默失敗是維運自架系統最該警惕的一類錯——它不會吵你,只會讓你在最需要那則訊息時,發現它從來沒來過。所以通訊層沒有「建好就好」,只有「測過才算數」。

明天談對外服務:怎麼讓家裡的 NAS 對公網提供服務,但路由器一個 port 都不用開——Cloudflare Tunnel。


🔑 這篇的關鍵字
選通道:LINE 官方帳號免費方案有推播則數上限(告警用途不適合)· Telegram Bot API 推播免費無則數限制
收發兩條路:發=對 API 發 HTTPS 請求(不需對外開埠)· 收=webhook(要公網 HTTPS 端點 + secret token 驗來源)或 long polling(不需對外但要輪詢)
建立@BotFather · /newbot · supergroup + Topics · Bot 需設為管理員 · getUpdateschat.idmessage_thread_id
必踩的坑:群組 ID 是負數(-100...)· thread_id=1(General)Bot 無法發文且不報錯 · 不存在的 thread_id 一樣靜默吞掉
限制:單則 4096 字元(MESSAGE_TOO_LONG)· 429 Too Many Requests → Exponential Backoff
防呆:把有效 Topic 白名單寫進建立階段的驗證,別依賴 API 回應


我是一名金融業資訊工程師,這是我半年來在家自架 AI Agent 系統的實錄。


上一篇
Day 4:投資秘書、家居管家、備考顧問——六個 Agent 怎麼不打架
系列文
生活中的 AI 應用:我在家用 NAS 養了一隻 Agent,幫我看盤、顧家、盯備考——30 天自架實錄5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言