Webhook 媒體相簿:彙整照片而不遺失子訊息
透過 webhook 接收媒體相簿時,應先獨立儲存每則子訊息,再使用相簿識別碼建立彙整檢視。不要以相簿 ID 對子訊息去重,否則同一相簿中的不同照片會遭到捨棄。當酬載沒有預期總數時,等待一段沒有新資料的時間可以作為處理條件,但不能證明所有照片都已抵達。
重點
- 訊息識別碼用於逐則儲存,相簿識別碼用於建立關聯。
- 兩者都應限定在接收帳號及對話範圍內。
- 先持久化子訊息,再確認接收;彙整工作交給背景程序。
- 計時器觸發後仍保留群組狀態,讓延遲項目可以加入。
相簿 ID 不等於訊息 ID
Telegram 官方 Bot API 文件將 Message.media_group_id 定義為選用欄位,識別訊息所屬、在該聊天內唯一的媒體群組。這是訊息間的關係,不是每則訊息身分的替代品。
UnifyPort 採用不同契約。標準 webhook 參考記載了選用的 data.message.album,其中有 id、index 及選用的 total。相簿子項仍保有自己的 data.message.id。文件中的相簿範例來自 WhatsApp;不要因此假定所有平台都會提供相簿中繼資料,或認為 Telegram 原始欄位會直接出現在標準酬載中。
若尚未完成附件解析,可先閱讀媒體附件對應指南。彙整只是額外的檢視層,不是新的檔案下載協定。
| 識別方式 | 建議範圍 | 用途 |
|---|---|---|
| 一般事件 ID | 工作區及事件流 | 辨識同一事件的重複傳送 |
| 子訊息 ID | 工作區、平台、訊息帳號、對話 | 儲存或更新單則訊息 |
| 相簿 ID | 工作區、平台、訊息帳號、對話 | 關聯多個子項 |
| 附件 URL | 所屬子訊息紀錄 | 定位媒體,不作為群組鍵值 |
以假設的三張照片提交為例,B 抵達兩次、C 抵達一次,代表只有兩個不同子項,而非三個。HTTP 請求次數不能代表相簿完整度。
先建立子訊息紀錄
下列 JavaScript 從已驗證的事件建立應用程式儲存鍵值。回傳屬性是本地設計,並非新 API 欄位;程式也不負責驗證簽章或持久化。
function albumKeys(workspaceKey, event) {
if (event.type !== 'message.received') return null;
const conversation = event.data?.conversation;
const message = event.data?.message;
if (!event.provider || !event.account_id ||
!conversation?.id || !message?.id) {
throw new Error('Missing message identity');
}
const scope = [workspaceKey, event.provider,
event.account_id, conversation.id];
return {
childKey: JSON.stringify([...scope, message.id]),
albumKey: message.album?.id
? JSON.stringify([...scope, message.album.id])
: null
};
}
建議使用以下交易流程:
- 依投遞契約驗證原始請求。啟用
signing_secret後,以時間戳記、句點及原始本文驗證 HMAC-SHA256,並檢查時間戳記的新鮮度。 - 儲存事件,對完整訊息身分建立唯一約束並新增或更新子訊息。保留說明文字、附件、
sent_at與相簿中繼資料。 - 將子項連結至對應相簿。若沒有相簿 ID,就保留為獨立訊息,不憑相似文字或相近抵達時間猜測關係。
- 提交紀錄與持久化工作項目後才回傳 2xx,讓背景 worker 更新檢視及下載媒體。
使用同一交易或持久化 outbox,避免程序中斷造成「子訊息已存,但彙整工作遺漏」。每則子訊息的說明文字都要保留,不讓最後一次傳送覆蓋唯一的相簿說明。
決定處理時機,不宣稱資料一定完整
以下是應用程式狀態建議,不是平台事件或 API 欄位。
| 觀察 | 建議處理 |
|---|---|
| total 一致,且不同子項數量已達該值 | 標記預期數量已達成,另行追蹤檔案就緒狀態 |
| 缺少 total | 經過本地設定的靜默時間後處理暫定快照 |
| total 衝突或 index 重複 | 保留子訊息,標記中繼資料待查 |
| 處理後又收到新子項 | 更新檢視並執行延遲項目策略 |
| 同一子項再次傳送 | 冪等更新,不增加不同子項數量 |
同一相簿的更新應循序執行,或使用交易版本檢查,避免多個 worker 同時讀到舊狀態、重複建立下游工作。保存每次處理決定與納入的子訊息識別碼。延遲照片出現後,明確選擇刷新畫面、補充分析或人工審核,不要自動再次傳送客戶訊息。
保留 album.index 作為呈現資訊,但不要從單一範例推論未記載的索引起點。索引缺少或衝突不應導致子項消失。等待時間只是應用程式的延遲政策,不是平台的完成訊號。
將檔案就緒狀態分開
即使已收到預期數量的訊息,仍可能有檔案無法使用。UnifyPort 文件說明附件 URL 是暫時網址,過大檔案也可能沒有 URL。應及時排入下載工作,分別顯示附件成功與失敗狀態。
失效下載連結復原指南說明了為何不能直接用 Telegram Bot API 的檔案更新方法處理 UnifyPort 附件 URL。不要為了等待相簿完整而無限延後保存已可取得的檔案。
UnifyPort 是非官方介面,標準化事件流不保證各平台中繼資料完全一致。它不提供 REST 訊息歷史讀取 API,也不保證重播遺漏酬載。需要原生機器人契約時,應使用官方 Bot API,不要混用兩種結構。
驗收與常見問題
建議測試重複子項、亂序抵達、不同對話中的相同相簿 ID、缺少總數、資料衝突及處理後抵達的照片。這是建議測試,並非已執行結果。
可以用 album.id 對子訊息去重嗎?
不行。多則不同訊息會共用它,應使用帶範圍的子訊息 ID 儲存。
total 達成是否代表檔案已下載?
不是。total 在提供時描述預期群組大小,檔案持久化是另一個階段。
沒有 total 的相簿應該捨棄嗎?
不應捨棄。保留子項,依明確策略產生暫定檢視,並允許後續補入。
下一步與來源
先依標準事件參考完成子訊息儲存,再啟用相簿自動化。
核對日期:2026-09-29。
讓訊息接入變成一條穩定的產品管線。
先用統一 API 跑通傳送,再用標準事件把所有入站訊息接回業務系統。