← Все статьи
Руководство

Медиаальбомы в 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 создаёт ключи хранения приложения из проверенного события UnifyPort. Названия возвращаемых свойств относятся к локальному коду, а не к новым полям 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
  };
}

Рекомендуемый порядок транзакционной обработки:

  1. Проверьте исходный запрос по контракту доставки. При включённом signing_secret проверяйте HMAC-SHA256 от временной метки, точки и исходного тела запроса, а также допустимую давность метки.
  2. Сохраните событие и выполните upsert дочернего сообщения с ограничением уникальности на полный ключ. Сохраняйте подписи к фото, вложения, sent_at и доступные метаданные альбома.
  3. Свяжите сообщение с альбомом в той же области. Если ID альбома отсутствует, оставьте сообщение самостоятельным. Не угадывайте принадлежность по одинаковым подписям или близкому времени поступления.
  4. Зафиксируйте входящую запись и устойчивое задание до ответа 2xx. Обновление представления и загрузку файлов выполняйте в фоне.

Используйте одну транзакцию либо устойчивый outbox, чтобы сбой процесса не оставил сохранённое сообщение без задания на группировку. Подпись каждого элемента храните отдельно: последнее поступление не должно перезаписывать единственную подпись всего альбома.

Выберите момент обработки, не обещая полноту

Ниже приведены рекомендуемые состояния приложения, а не события провайдера или поля API.

НаблюдениеРешение приложения
Значения total согласованы и столько разных элементов уже сохраненоОтметить достижение ожидаемого количества; готовность файлов учитывать отдельно
total отсутствуетОбработать предварительный снимок после выбранного периода ожидания
total противоречив или индексы совпадаютСохранить элементы и отправить метаданные на проверку
Новый элемент поступил после обработкиОбновить проекцию и применить явную политику поздних поступлений
Элемент доставлен повторноОбновить идемпотентно, не увеличивая число уникальных элементов

Сериализуйте изменения одного альбома или применяйте транзакционную проверку версий. Иначе несколько обработчиков могут прочитать старое состояние и создать одинаковые downstream-задания. Сохраняйте решение об обработке вместе с точными ID включённых сообщений. Для поздней фотографии заранее выберите обновление интерфейса, дополнительный анализ либо ручную проверку. Не запускайте автоматически повторную отправку клиенту.

Сохраняйте полученный album.index для отображения, но не выводите недокументированное начало нумерации из одного примера. Отсутствующий или конфликтующий индекс не должен приводить к потере элемента. Таймер задаёт вашу политику задержки, а не сигнал завершения от платформы.

Готовность файлов — отдельное состояние

Все ожидаемые записи сообщений могут быть на месте, хотя часть файлов ещё недоступна. UnifyPort документирует временные URL вложений и случаи слишком больших файлов без URL. Ставьте доступные файлы на скачивание своевременно и показывайте успех или ошибку для каждого вложения.

В руководстве по восстановлению истёкших ссылок объясняется, почему обновление файла через Telegram Bot API нельзя напрямую применять к URL вложения UnifyPort. Не откладывайте сохранение доступных файлов на неопределённый срок ради ожидания полного альбома.

UnifyPort предоставляет неофициальный интерфейс. Нормализованный поток не гарантирует одинаковые метаданные на всех каналах. REST API чтения истории сообщений и гарантированного воспроизведения пропущенных payload нет. Если нужен нативный контракт Telegram-бота, используйте официальный Bot API и не смешивайте две схемы в одном парсере.

Проверки и FAQ

Проверьте повторную доставку, нарушение порядка, одинаковые ID альбомов в разных чатах, отсутствие total, конфликт метаданных и поступление элемента после обработки. Это план проверок, а не результаты выполненных тестов.

Можно ли удалять дубликаты по album.id?

Не для дочерних сообщений: разные сообщения могут иметь общий ID альбома. Используйте ID сообщения с полной областью идентификации.

Достижение total означает, что файлы скачаны?

Нет. При наличии total описывает ожидаемый размер группы. Сохранение файлов — отдельный этап.

Нужно ли отбрасывать альбом без total?

Нет. Сохраняйте элементы, обрабатывайте предварительное представление по явной политике и оставляйте возможность дополнить его позже.

Следующий шаг и источники

Реализуйте хранение отдельных сообщений по справочнику стандартных событий до включения автоматизации на уровне альбома.

Документация проверена 2026-09-29:

UnifyPort API

Превратите интеграцию сообщений в стабильный продуктовый pipeline.

Начните с отправки через единый API, затем возвращайте входящие сообщения в бизнес-систему стандартными событиями.