跳到內容

Telegram 內的審批詳情卡(Mini App)

完整說明、模擬後果與剩餘倒數一次看完,直接在 Telegram 裡按下同意或拒絕。


高風險動作要人同意時,DuDuClaw 會把一張卡片推到 Telegram,上面兩顆按鈕:同意、拒絕。按鈕夠用,但卡片裝不下判斷需要的東西:完整的動作說明、模擬出來的後果、還剩多久會自動拒絕。以前這些只能到儀表板看,於是「在手機上決定」變成「先切到瀏覽器、先登入、再找到那一筆」。

這一頁講的功能把那三樣東西搬進 Telegram 裡:卡片多一顆「🔎 查看詳情」,按下去在對話上方展開一個小畫面,看完直接按同意或拒絕,整段過程沒有離開 Telegram。

這是試作(spike)功能,預設關閉。 範圍只有審批詳情這一張卡:目標是先驗證「把介面塞進通道」這條路可不可行,再決定要不要推廣到任務、花費、狀態等其他畫面。


高風險動作等待人類同意
|
v
卡片推到你與 bot 的一對一私訊
[同意] [拒絕] [🔎 查看詳情]
| 按下第三顆按鈕
v
Mini App 在對話上方展開
| Telegram 附上簽好章的 initData
v
Gateway 重算 HMAC 驗簽
| 相符 --> 詳情經 POST 送達
v
完整說明+模擬後果+每秒更新的倒數
| 同意 / 拒絕
v
畫面自動關閉,對話裡那張卡片
就地收斂成一行結果
  • 這是哪一類決定:與卡片同一行的標籤(「⚠️ 高風險動作需要你同意」)。
  • 誰要做什麼:AI 員工名稱、動作的白話說明(「執行高風險工具」「安裝新技能/工具」…)。
  • 內容:完整說明,不像聊天氣泡那樣被截斷。
  • 如果同意,接下來會發生什麼:系統事先模擬出來的一到三步後果。沒有模擬結果的審批就不顯示這一段,不會硬編。
  • 剩餘時間:每秒更新的倒數,剩不到三分之一時轉紅。逾時未回覆一律自動拒絕。
  • 兩顆大按鈕:同意這個動作/拒絕這個動作。按下去送出,成功後畫面自動關閉,回到對話。

畫面配色跟著 Telegram 的佈景走(深色模式、淺色模式、自訂主題都會套用),不會在深色聊天室裡刺出一片白。


條件 怎麼設定 不滿足會怎樣
功能開關 config.toml 加上 [miniapp] enabled = true 端點回 404,卡片維持原本兩顆按鈕
對外網址是 https config.toml [dashboard] public_url = "https://你的網域" 不附詳情按鈕(Telegram 硬性規定 Mini App 只吃 https)
卡片送到一對一私訊 由推播目的地決定 不附詳情按鈕(Telegram 規定 web_app 按鈕只能在私訊出現)
~/.duduclaw/config.toml
[miniapp]
enabled = true
[dashboard]
public_url = "https://ai.example.com"

改完重開 gateway。三個條件任何一個沒過,卡片就跟開啟這個功能之前一模一樣,不會少一顆按鈕,也不會送不出去。

http://localhost:18789 這種本機網址是有效的儀表板位置,但不是有效的 Mini App 位置。沒有對外 https 網域的部署(大多數本機安裝屬於這類)不需要做任何事,這個功能對它們是靜默的。


Telegram 開啟 Mini App 時會附一段簽好章的 initData,內容包含使用者 id 與開啟時間,簽章的金鑰是你自己的 bot token。DuDuClaw 收到後照官方文件重算一次:

data_check_string = 除了 hash 以外的所有欄位,依字母排序,用 \n 串起來
secret_key = HMAC_SHA256(<bot_token>, "WebAppData")
hash = hex(HMAC_SHA256(data_check_string, secret_key))

算出來的值與 Telegram 附的 hash 用定值時間比對。對不上就什麼都不回:連這筆審批存不存在都不透露,也不會退回任何精簡版內容。

除了簽章,還有三道:

  1. auth_date 超過 1 小時視為過期(畫面開太久要回對話重開);時間戳離譜地指向未來也一樣擋掉。
  2. user 欄位必須含數字 id,否則拒絕。
  3. 端點有每 IP 每分鐘的請求上限。

網址上的 ?id=<編號> 不是憑證。知道編號不會讓任何人看到內容,因為頁面本身不帶資料,資料一律走驗簽後的 POST 才拿得到。

沒有第二套權限。 Mini App 認出來的 Telegram 使用者 id,跟按下卡片按鈕時回報的是同一個 id,所以決定送出後走的是與按鈕完全相同的那一條路:

  • 儀表板裡有已驗證綁定且身分是管理員/主管的人,可以決定;
  • 一般員工帳號不行(就算他是收件人也不行,這是職責分離);
  • 完全還沒有人綁定通道帳號的部署,只有「卡片送達的那個帳號本人」可以決定。

看詳情套用的是同一組判斷,不會比較寬鬆:不能決定的人也看不到內容。

bot token 只當作 HMAC 的金鑰材料用,不寫日誌、不回應、不進到頁面裡。頁面本身沒有任何後端細節,錯誤訊息一律是「請回到對話重新開啟」這種給人看的話。


  • 群組收不到這顆按鈕。 Telegram 規定 web_app 按鈕只在使用者與 bot 的私訊出現。卡片推到群組時只會有原本的同意/拒絕兩顆。這是刻意的:硬塞會讓整則訊息送不出去。
  • 一定要 https。 這是 Telegram 的規定,不是本專案的選擇。
  • 只有審批這一張卡。 任務、花費、狀態卡都還是走既有的按鈕與儀表板深連結。
  • 只有 Telegram。 LINE LIFF、Teams Dialog、飛書 Web App 都具備對等能力,但這次沒做;Slack、WhatsApp、Google Chat、Discord 沒有對等機制,未來也只會走「卡片按鈕+原生表單」那一軌。
  • 頁面會向 telegram.org 取得平台 SDK。 這是取得 Telegram.WebAppinitData、佈景參數、關閉視窗)的唯一受支援方式。SDK 取不到時頁面不會壞,會改從網址片段(tgWebAppData)讀同一份資料,只是少了自動關閉視窗。除此之外頁面沒有任何外部資源:CSS、JS 全部內嵌,沒有 CDN、沒有字型、沒有圖片。

本機沒有對外 https,webview 開不起來,所以下面這段要在有公網部署的環境上跑:

  1. 設好 [miniapp] enabled = true 與 https 的 [dashboard] public_url,重開 gateway。
  2. 用瀏覽器打開 https://你的網域/miniapp/approval?id=test — 應該看到「請從對話中開啟」,這代表頁面活著而且沒有身分就不給資料。
  3. 觸發一筆需要人核可的動作,讓卡片推到你與 bot 的私訊
  4. 卡片上應該出現第三顆按鈕「🔎 查看詳情」,點開後看到完整內容與倒數。
  5. 在畫面裡按同意或拒絕 → 對話裡原本那張卡片會就地收斂成一行結果(與按按鈕的行為一致)。
  6. 反面測試:把同一個網址傳給一個沒有決定權限的人,他打開應該只看到「您沒有查看這筆決定的權限」。