iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Modern Web

教練看不到的那六天|從 FIT 檔到 3D 軌跡,馬拉松訓練資料 Dashboard系列 第 11

Day 11|Telegram Bot 從零開始:為什麼選 long polling,及讓電腦持續開著

  • 分享至 

  • xImage
  •  

第一階段結束,資料都進來了,也出了第一個畫面,但目前 dashboard 有個問題:它必須在自己的電腦上運行。

而重訓是在健身房記的、體重是在房間量的、跑完步是在路邊看錶的,這些時刻都是用手機在處理,因為不可能隨時帶著筆電。

所以第二階段的第一件事,是找一個隨身的入口


為什麼是 Telegram,不是自己寫 App

寫個 App 當然可以,但那是另一個成本的專案,而聊天軟體本來就滿足了幾個關鍵條件:手機上可下載使用、可以打字也可以傳照片,且支援按鈕介面、而且有現成的 Bot API

選 Telegram 的好處:Telegram 的 Bot API 對個人開發者友善,不需要企業帳號、不需要審核,申請完直接拿到 token 就能開始寫。


webhook vs long polling

Bot 要收到訊息有兩種方式,這是動手前第一個要決定的事。

Webhook:自己提供一個網址給 Telegram,有新訊息時它主動打你的伺服器。優點是即時、省資源;缺點是你必須有一個公開、有 HTTPS 憑證的網址

Long polling:自己的程式主動去問 Telegram「有新訊息嗎」,沒有就把連線掛著,有了就回。優點是不需要任何對外的網址,在自己電腦上跑就行。

補充說明token 對兩種方式都需要,跟 BotFather 申請完拿到的那串 token 是呼叫 Bot API 的通行證,不管你是主動去 getUpdates,還是被動等 Telegram 打過來,都得帶著它。

webhook 真正多出來的是三樣東西:一個公開的 HTTPS 網址、一次 setWebhook 把網址註冊給 Telegram、以及自己跑一個能接 POST 的 web server。

而且因為那個網址是公開的,任何人都能對它發假訊息,所以 setWebhook 還可以帶一個 secret_token 參數—之後 Telegram 每次打過來都會在 X-Telegram-Bot-Api-Secret-Token 這個 header 帶上它,讓你確認請求真的是 Telegram 發的。long polling 沒有這層顧慮,連線是自己主動發起的。

另外,兩者是互斥的:設了 webhook 之後 getUpdates 就會失敗,要先 deleteWebhook 才能切回 polling。

這個專案選了 long polling,程式碼就一行:

app.run_polling()

理由跟架構有關,而且是刻意的取捨。


這台 bot 為什麼一定得跑在自己電腦上

一般的 bot 會部署到雲端,24 小時在線。但這個 bot 不行—它需要存取本機的東西

它會做這些事:讀寫本機 coach/logs/ 底下的訓練紀錄檔、透過藍牙讀家裡的體重機、呼叫本機安裝的 claude CLI。

這三件事全部離不開這台電腦:體重機的藍牙訊號抵達不了雲端機房,訓練紀錄的 markdown 檔也在本機。

既然程式本來就得跑在自己電腦上,webhook 那個「公開網址」的門檻就變成純粹的麻煩—需要買網域架 HTTPS,或是開 ngrok 之類的隧道。而 long polling 完全不需要,插上電就能跑。

不過代價就是:你的電腦要一直開著,bot 才會在線。

因為服務需要跑在自己的設備上,變成一個硬體需求,但對一個自己用的工具來說,還可以接受—剛好桌機每天就都開著。


六個指令,各自對應一個資料來源

Bot 註冊了這些指令:

app.add_handler(CommandHandler("start", cmd_start))
app.add_handler(CommandHandler("new", cmd_new))
app.add_handler(CommandHandler("run", cmd_run))       # Garmin
app.add_handler(CommandHandler("sync", cmd_sync))
app.add_handler(CommandHandler("weight", cmd_weight)) # 體重機
app.add_handler(CommandHandler("workout", cmd_workout)) # Notion

前十天做的那些腳本,在這裡各自變成一個指令:/run 去 Garmin 撈活動、/weight 開藍牙監聽、/workout 爬 Notion 重訓紀錄。

也做了一個小東西,讓使用上更方便—啟動時把指令註冊進 Telegram 的選單:

async def _post_init(app: Application):
    await app.bot.set_my_commands([
        BotCommand("run", "從 Garmin 同步跑步/活動"),
        BotCommand("workout", "從 Notion 同步重訓"),
        BotCommand("weight", "讀體重機"),
        BotCommand("new", "開新對話、清空記憶"),
    ])

https://ithelp.ithome.com.tw/upload/images/20260827/20183598TMuuW0xO63.jpg

這樣在輸入框旁邊點一下就有清單,不用記指令怎麼拼,能少動手就少^0^


只有自己能用

這是一支能讀寫本機檔案、能呼叫本機 CLI 的 bot,如果任何人都能對它下指令,等於把自己的電腦開放給全世界。

所以每個 handler 的第一行都是同一件事:

if not _authorized(update):
    await update.message.reply_text("未授權的使用者。")
    return

比對的是 .env 裡設定的 Telegram 數字 ID,沒設等於讓任何人遠端驅動你本機的 claude

Telegram bot 的 token 一旦洩漏,任何人都能找到你的 bot 並對它下指令,這道檢查非常必要。


明天:賦予 bot 智慧

到目前為止,這個 bot 只是一組指令的遙控器—/run 去撈資料、/workout 爬紀錄。

但我們想要的不是遙控器,是一個能理解「今天 12K 有點喘」這種對話的東西,明天要處理的就是這一層!



上一篇
Day 10|訓練 Dashboard 的初畫面:九天之後,終於有東西可以看
下一篇
Day 12|讓 bot 變聰明:Agent SDK vs 自幹 subprocess
系列文
教練看不到的那六天|從 FIT 檔到 3D 軌跡,馬拉松訓練資料 Dashboard14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言