Webhookのメディアアルバム:写真を失わずにまとめる設計
Webhookからメディアアルバムを組み立てるときは、各子メッセージを個別に保存し、アルバム識別子は表示をまとめるためだけに使います。アルバムIDで子メッセージを重複排除すると、同じアルバムに属する別の写真まで失われます。予定総数がない場合、一定の無通信時間を処理開始条件にできますが、全項目が届いた証明にはなりません。
要点
- メッセージの識別とアルバムの識別を分けます。
- どちらのキーにも受信アカウントと会話の範囲を含めます。
- 子メッセージを永続化してから受信確認を返し、集約は後で行います。
- タイマー発火後も状態を残し、遅れて届く項目を追加できるようにします。
アルバムIDはメッセージIDではない
Telegramの公式Bot APIリファレンスでは、Message.media_group_id は、そのメッセージが属するメディアグループをそのチャット内で識別する任意フィールドです。メッセージ同士の関係を表すもので、個々のメッセージIDの代わりではありません。
UnifyPortは別の契約を使います。標準Webhookリファレンスには、任意の data.message.album と、その id、index、任意の total が記載されています。子メッセージには固有の data.message.id が残ります。掲載されているアルバム例はWhatsAppです。LINEを含む全チャネルで必ずアルバム情報が得られる、あるいはTelegramの生フィールドがそのまま届くとは考えないでください。
添付ファイルの抽出から実装する場合は、先にメディア添付の対応ガイドを参照してください。アルバム集約は追加の表示モデルであり、別のダウンロード方式ではありません。
| 識別子 | 推奨する範囲 | 用途 |
|---|---|---|
| 通常イベントのID | ワークスペースとイベントストリーム | 同じイベントの再配信を識別 |
| 子メッセージID | ワークスペース、プロバイダー、アカウント、会話 | 1件の保存・更新 |
| アルバムID | ワークスペース、プロバイダー、アカウント、会話 | 複数の子メッセージを関連付け |
| 添付URL | 所属する子メッセージ | メディアの場所。集約キーにはしない |
仮の3枚投稿でBが2回、Cが1回届いても、異なる子メッセージは2件です。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と、タイムスタンプの鮮度を確認します。 - イベントを保存し、範囲付きメッセージIDに一意制約を設けて子メッセージを追加・更新します。キャプション、添付、
sent_at、アルバム情報を残します。 - 子メッセージを対応するアルバムに関連付けます。アルバムIDがなければ単独メッセージとして保存し、似たキャプションや到着時刻から所属を推測しません。
- 受信記録と永続的なジョブをコミットしてから2xxを返します。表示更新とメディア取得はワーカーに任せます。
同じトランザクション、または永続的なoutboxを使い、「子メッセージは保存されたが集約ジョブがない」という中断状態を防ぎます。各子メッセージのキャプションは個別に保持し、最後に届いた内容だけでアルバム全体の説明を上書きしないでください。
完全性を断定せずに処理時点を決める
以下はアプリケーション側の状態設計案です。プラットフォームのイベントやAPIフィールドではありません。
| 観測 | アプリケーションの判断 |
|---|---|
| totalが一貫し、その数の異なる子メッセージを保存済み | 予定件数に到達。ファイル準備状況は別管理 |
| totalがない | ローカルに決めた待機時間後、暫定スナップショットを処理 |
| totalが矛盾する、またはindexが重なる | 子メッセージを保持してメタデータを調査 |
| 処理後に新しい子メッセージが届く | 表示を更新し、遅延項目の方針を適用 |
| 同じ子メッセージが再配信される | 冪等に更新し、件数を増やさない |
同じアルバムの更新を直列化するか、トランザクションのバージョン確認を使います。複数ワーカーが同じ古い件数を読んで、下流処理を重複登録するのを防ぐためです。処理判断と、その時点で含めた子メッセージIDを保存してください。後から写真が増えたら、画面更新、追加分析、人による確認のどれを行うか明示し、顧客への送信を勝手に繰り返さないようにします。
album.index は表示用に保存しますが、単一の例から未記載の開始番号を推測しないでください。欠落や競合があっても子メッセージを捨てません。タイマーは遅延の運用方針であり、公式の完了通知ではありません。
ファイルの準備状況を分ける
予定件数がそろっていても、ファイルが利用可能とは限りません。UnifyPortは一時的な添付URLと、大きすぎるファイルではURLが省略される場合を文書化しています。取得可能なファイルは速やかにダウンロードし、成功・失敗を添付ごとに表示します。
期限切れダウンロードリンクの復旧ガイドでは、Telegram Bot APIの更新手順をUnifyPortの添付URLへそのまま適用できない理由を説明しています。相簿がそろうまで、取得できるファイルの保存を無期限に待たせないでください。
UnifyPortは非公式インターフェースです。正規化されたイベントでも、チャネル間で同じメタデータを保証するわけではありません。RESTのメッセージ履歴読み取りAPIや、欠落したペイロードの再配信保証もありません。ネイティブなボット契約が必要なら公式Bot APIを使い、両方の構造を混ぜないでください。
テストとFAQ
重複、順不同、別会話で同じアルバムID、総数欠落、メタデータ競合、処理後の遅延到着をテストします。これは推奨テストであり、実施済みの結果ではありません。
album.idで子メッセージを重複排除できますか?
できません。複数メッセージが共有する値です。保存には範囲付きの子メッセージIDを使います。
totalに達したらダウンロード完了ですか?
いいえ。totalは提供された場合の予定件数です。ファイルの永続化は別段階です。
totalがないアルバムは破棄しますか?
破棄しません。子メッセージを保存して明示的な方針で暫定表示を処理し、後から追加できるようにします。
次のステップと出典
アルバム単位の自動化を有効にする前に、標準イベントリファレンスに沿って子メッセージの保存を実装してください。
確認日:2026-09-29。
メッセージ連携を安定したプロダクトパイプラインへ。
まずは 1 つの API で送信を始め、標準イベントですべての inbound メッセージを業務システムへ戻しましょう。