Webhooks – События в реальном времени
Webhooks BigBlueButton позволяют вашему приложению получать HTTP POST-уведомления в реальном времени при возникновении событий встречи. Вместо постоянного опроса getMeetings или getMeetingInfo вы регистрируете callback URL, и BigBlueButton автоматически отправляет вам события. В этой главе рассматриваются три эндпоинта API webhook, формат callback, типы событий, поведение повторных попыток и конфигурация сервера.
Для webhooks требуется, чтобы пакет bbb-webhooks был установлен на сервере. Некоторые детали, описанные в этой главе, не охвачены официальной документацией BigBlueButton и были определены путём анализа исходного кода. undocumented
Предварительные условия
Пакет bbb-webhooks должен быть установлен на сервере BigBlueButton, прежде чем эндпоинты webhook станут доступны:
sudo apt-get install bbb-webhooks Если приложение webhook не запущено в момент создания встречи, внутреннее сопоставление встречи не создаётся. В результате для этой встречи callback-запросы не будут вызываться.
hooks/create — зарегистрировать webhook
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/create?<parameters>&checksum=replace-with-checksum Официальная документация не указывает HTTP-метод. Роутер Express регистрирует только маршруты GET — запросы POST не принимаются. undocumented
| Параметр | Тип | Обязательно | По умолчанию | Описание |
|---|---|---|---|---|
callbackURL | String | Да | — | URL, который будет получать callback POST. Обнаружение дубликатов основано исключительно на этом URL — один и тот же URL нельзя зарегистрировать дважды, даже с другим meetingID или eventID. |
meetingID | String | Нет | — | Ограничивает webhook конкретной встречей (внешний идентификатор встречи). Если параметр опущен, webhook становится глобальным и срабатывает для всех встреч. |
eventID | String | Нет | — | Список типов событий, разделённых запятыми, для отслеживания (например, user-joined,meeting-ended). Если параметр опущен, доставляются все события. Фильтр нечувствителен к регистру. Доступно начиная с BBB 2.5. |
getRaw | Boolean | Нет | false | Если установлено значение true, callback содержат необработанное сообщение Redis вместо обработанных данных события. |
Успешный ответ
<response>
<returncode>SUCCESS</returncode>
<hookID>replace-with-hook-id</hookID>
<permanentHook>false</permanentHook>
<rawData>false</rawData>
</response> Дублирующая регистрация
Если тот же callbackURL регистрируется снова, ответ всё равно возвращает SUCCESS, но включает duplicateWarning. Существующий webhook остаётся без изменений — meetingID, eventID и getRaw не обновляются.
<response>
<returncode>SUCCESS</returncode>
<hookID>replace-with-hook-id</hookID>
<messageKey>duplicateWarning</messageKey>
<message>There is already a hook for this callback URL.</message>
</response> Ответы с ошибками
messageKey | Описание |
|---|---|
checksumError | Контрольная сумма недействительна или отсутствует. |
missingParamCallbackURL | Параметр callbackURL не был передан. undocumented |
createHookError | Произошла внутренняя ошибка (например, Redis недоступен). Проверьте журналы сервера. |
hooks/list — список зарегистрированных webhook
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/list?<parameters>&checksum=replace-with-checksum | Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
meetingID | String | Нет | Фильтрует по ID встречи. Результат включает как хуки для указанной встречи, так и все глобальные хуки. Если параметр опущен, возвращаются все зарегистрированные хуки. |
Успешный ответ
<response>
<returncode>SUCCESS</returncode>
<hooks>
<hook>
<hookID>replace-with-hook-id</hookID>
<callbackURL><![CDATA[https://api-guide.bbbserver.com/callbacks/webhook]]></callbackURL>
<meetingID><![CDATA[replace-with-meeting-id]]></meetingID>
<eventID>user-joined,meeting-ended</eventID>
<permanentHook>false</permanentHook>
<rawData>false</rawData>
</hook>
</hooks>
</response> Элемент <meetingID> появляется только для хуков, привязанных к встрече. У глобальных хуков элемент <meetingID> отсутствует. Аналогично, <eventID> появляется только тогда, когда при создании был задан фильтр событий.
Ответы с ошибками
messageKey | Описание |
|---|---|
checksumError | Контрольная сумма недействительна или отсутствует. |
listHookError | При получении списка хуков произошла внутренняя ошибка. Проверьте журналы сервера. undocumented |
hooks/destroy — удалить webhook
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/destroy?<parameters>&checksum=replace-with-checksum | Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
hookID | String | Да | ID хука, который нужно удалить (полученный из hooks/create или hooks/list). |
Успешный ответ
<response>
<returncode>SUCCESS</returncode>
<removed>true</removed>
</response> Постоянные хуки (настроенные на стороне сервера через permanentURLs) нельзя удалить через API. Попытка сделать это возвращает ошибку destroyMissingHook, как если бы хук не существовал.
Ответы с ошибками
messageKey | Описание |
|---|---|
checksumError | Контрольная сумма недействительна или отсутствует. |
missingParamHookID | Параметр hookID не был передан. |
destroyMissingHook | ID хука не существует или принадлежит постоянному хуку. |
destroyHookError | При удалении хука произошла внутренняя ошибка. Проверьте журналы сервера. |
hooks/ping — проверка работоспособности
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/ping Этот диагностический эндпоинт не требует контрольной суммы. Если приложение вебхука доступно, он возвращает строку простого текста bbb-webhooks API up! (не XML, тип содержимого text/plain). undocumented
Формат обратного вызова
Обратные вызовы вебхука отправляются как запросы HTTP POST с типом содержимого application/x-www-form-urlencoded. Тело запроса содержит три поля:
| Поле | Описание |
|---|---|
domain | Домен сервера (из конфигурации вебхука). undocumented |
event | Массив JSON, содержащий данные события. Всегда массив, даже для одного события. |
timestamp | Метка времени Unix в миллисекундах на момент отправки. |
Тело обработанного callback (getRaw=false)
domain=myserver.com&event=[{"data":{"type":"event","id":"user-joined","attributes":{...},"event":{"ts":1532718316938}}}]×tamp=1532718316953 Тело необработанного callback (getRaw=true)
domain=myserver.com&event=[{"envelope":{"name":"UserJoinedMeetingEvtMsg","routing":{"sender":"bbb-apps-akka"}},"core":{"header":{...},"body":{...}}}]×tamp=1532718316953 Когда сервер настроен со значением multiEvent больше 1, несколько событий могут быть объединены в массиве event в рамках одного обратного вызова.
Проверка контрольной суммы обратного вызова
В режиме по умолчанию каждый callback URL получает параметр строки запроса checksum, который ваше приложение должно проверить:
https://api-guide.bbbserver.com/callbacks/webhook?checksum=replace-with-checksum Формула проверки:
sha<algo>(<callback-URL-without-checksum> + <URL-encoded-body> + <shared-secret>) Возьмите зарегистрированный URL обратного вызова без параметра checksum.
Добавьте полное URL-кодированное тело запроса (domain=...&event=[...]×tamp=...).
Добавьте общий секрет BBB.
Вычислите хеш получившейся строки с использованием настроенного алгоритма (по умолчанию: sha1, настраивается через hookChecksumAlgorithm).
Режим Auth 2.0 / Bearer
В качестве альтернативы сервер можно настроить с помощью auth2_0: true. В этом режиме параметр checksum не добавляется к callback URL. Вместо этого общий секрет передаётся как токен Bearer в заголовке Authorization: undocumented
Authorization: Bearer <shared-secret> Поведение обратного вызова
- Допустимые HTTP-коды статуса: HTTP
2xxи HTTP401считаются успешной доставкой. Все остальные коды статуса вызывают повторную попытку. undocumented - Перенаправления: Ответы HTTP
3xxобрабатываются с переходом по перенаправлению (до 10 переходов). Только если после 10 перенаправлений не будет достигнут2xx/401, callback считается неудачным. - Тайм-аут: Для каждого запроса обратного вызова установлен тайм-аут 5 секунд (настраивается через
requestTimeout). - Интервалы повторных попыток: При ошибке повторные попытки используют экспоненциальную задержку:
100ms, 500ms, 1s, 2s, 4s, 8s, 10s, 30s, 60s, 60s, 60s, 60s(12 попыток примерно за 5 минут, настраивается черезretryIntervals). - После исчерпания всех повторных попыток: Непостоянные хуки удаляются автоматически. Постоянные хуки продолжают повторные попытки бесконечно с интервалом 60 секунд (настраивается через
permanentIntervalReset).
Типы событий
События встречи
| ID события | Описание |
|---|---|
meeting-created | Встреча была создана. |
meeting-ended | Встреча завершилась. Это также вызывает синтетические события user-left для всех подключённых пользователей. |
meeting-recording-started | Запись началась. |
meeting-recording-stopped | Запись остановлена. |
meeting-recording-unhandled | Необработанное событие записи (резервный вариант, когда статус записи не равен ни true, ни false). |
meeting-screenshare-started | Начат показ экрана (включает данные пользователя-презентатора). |
meeting-screenshare-stopped | Показ экрана остановлен (включает данные пользователя-владельца). |
meeting-presentation-changed | Активная презентация изменилась (включает presentation-id). |
События пользователя
| ID события | Описание |
|---|---|
user-joined | Пользователь присоединился к встрече (включает флаг гостя, пользовательские данные, IP-адрес, User-Agent). |
user-left | Пользователь покинул встречу (включает флаг гостя). Также синтетически генерируется при meeting-ended. |
user-audio-voice-enabled | Аудио включено (включает флаги listening-only, sharing-mic, muted). |
user-audio-voice-disabled | Аудио отключено. |
user-audio-muted | Пользователь был заглушён. |
user-audio-unmuted | Пользователь был разглушён. |
user-audio-unhandled | Необработанное аудиособытие (резервный вариант). |
user-cam-broadcast-start | Веб-камера запущена (включает stream-id). |
user-cam-broadcast-end | Веб-камера выключена. |
user-presenter-assigned | Пользователю была назначена роль ведущего. |
user-presenter-unassigned | Пользователь был лишён роли ведущего. |
user-emoji-changed | Эмодзи или реакция пользователя изменены (включает значение эмодзи). |
user-raise-hand-changed | Пользователь поднял или опустил руку (включает логическое значение raise-hand). В версиях BBB до 2.7 это генерируется синтетически из user-emoji-changed. |
События чата и заметок
| ID события | Описание |
|---|---|
chat-group-message-sent | Сообщение было отправлено в публичный чат. Сообщения в приватном чате не создают событий webhook. |
transcript-updated | Транскрипция обновлена (включает transcript, locale, флаг final). |
pad-content | Содержимое общих заметок изменилось (включает pad-id, rev, text). |
События опросов
| ID события | Описание |
|---|---|
poll-started | Опрос был запущен (включает вопрос и варианты ответов). |
poll-responded | Участник ответил на опрос (включает ID ответов). |
События записи и воспроизведения (RAP)
| ID события | Описание |
|---|---|
rap-archive-started | Архивирование началось. |
rap-archive-ended | Архивирование завершено (включает флаг recorded и длительность). |
rap-sanity-started | Проверка целостности началась. |
rap-sanity-ended | Проверка целостности завершена. |
rap-post-archive-started | Пост-архивирование началось. |
rap-post-archive-ended | Пост-архивирование завершено. |
rap-process-started | Обработка началась. |
rap-process-ended | Обработка завершена. |
rap-post-process-started | Пост-обработка началась. |
rap-post-process-ended | Пост-обработка завершена. |
rap-publish-started | Публикация началась. |
rap-publish-ended | Публикация завершена (включает полный объект записи с метаданными, информацией о воспроизведении и скачивании). |
rap-post-publish-started | Пост-публикация началась. |
rap-post-publish-ended | Пост-публикация завершена. |
rap-published | Запись опубликована. |
rap-unpublished | Публикация записи отменена. |
rap-deleted | Запись удалена. |
Большинство событий RAP включают record-id, success (булево значение) и step-time. События с полем workflow дополнительно содержат имя рабочего процесса обработки.
Конфигурация сервера
Следующие параметры можно настроить в /etc/bigbluebutton/bbb-webhooks.yml или config/default.yml. Большинство из этих параметров не описано в официальной документации. undocumented
| Вариант | По умолчанию | Описание |
|---|---|---|
hookChecksumAlgorithm | sha1 | Алгоритм хеширования для checksum callback (sha1, sha256, sha384, sha512). |
api.supportedChecksumAlgorithms | [sha1, sha256, sha384, sha512] | Допустимые алгоритмы для контрольных сумм входящих API-запросов. |
api.port | 3005 | Порт сервера webhook API. |
api.bind | 127.0.0.1 | Адрес привязки сервера API. |
includeEvents | [] | Глобальный фильтр событий сервера: только эти события доставляются всем хукам. Пустое значение означает все события. |
excludeEvents | [] | Глобальный фильтр событий сервера: эти события подавляются для всех хуков. Пустое значение означает отсутствие исключений. |
permanentURLs | [] | Список постоянных URL-хуков, которые регистрируются при запуске и не могут быть удалены через API. |
retryIntervals | [100,500,1000,...,60000] | Интервалы повторных попыток в миллисекундах для неудачных callback-запросов. |
permanentIntervalReset | 60000 | Интервал повторной попытки в миллисекундах для постоянных хуков после исчерпания всех обычных повторных попыток. |
requestTimeout | 5000 | HTTP-таймаут в миллисекундах для callback-запросов. |
multiEvent | 1 | Если установлено значение больше 1, несколько событий объединяются в массив событий в рамках одного callback-запроса. |
queueSize | 10000 | Максимальный размер внутренней очереди событий. |
bbb.auth2_0 | false | Включает аутентификацию по токену Bearer вместо checksum в URL для callback. |
includeEvents и excludeEvents — это фильтры на уровне всего сервера, которые применяются дополнительно к фильтру eventID для каждого хука. Событие должно пройти оба фильтра, чтобы быть доставленным.
Примечания по эксплуатации
- Постоянство: Хуки хранятся в Redis и сохраняются после перезапуска сервера (повторно синхронизируются при запуске).
- Порядок: Обратные вызовы отправляются последовательно для каждого хука, по одному за раз, с сохранением порядка событий.
- Уникальность URL: Обнаружение дубликатов основано исключительно на callback URL. Один и тот же URL нельзя зарегистрировать с разными фильтрами
meetingIDилиeventID— побеждает первая регистрация. - Очистка данных: Внутренние сопоставления встреч удаляются после одной недели неактивности (настраивается через
mappings.timeout, по умолчанию: 604800000 мс). - Синтетические события: Когда встреча заканчивается, события
user-leftавтоматически генерируются для всех ещё подключённых пользователей, потому что сам BigBlueButton не генерируетUserLeftMeetingEvtMsgпри завершении встречи. - Приватные чаты: Только сообщения публичного чата создают события
chat-group-message-sent. Личные сообщения молча игнорируются.
meetingID — глобальные хуки без meetingID не поддерживаются; (2) персональные данные в событиях автоматически обфусцируются в целях конфиденциальности (например, IP-адреса участников не видны).Часто задаваемые вопросы
bbb-webhooks должен быть установлен через apt-get install bbb-webhooks. Без него эндпоинты hooks/create, hooks/list и hooks/destroy недоступны.duplicateWarning, а существующий хук останется без изменений. Чтобы отдельно отслеживать конкретные встречи, используйте разные callback URL.MAIN-PUBLIC-GROUP-CHAT), вызывают событие chat-group-message-sent. Личные сообщения между участниками не генерируют никаких событий webhook.getRaw=false (по умолчанию) вы получаете обработанные данные события со стандартизированной структурой, содержащей поля type, id и attributes. С getRaw=true вы получаете необработанное сообщение Redis, опубликованное внутренне BigBlueButton, которое включает поля envelope и core. Необработанный формат полезен для отладки или когда вам нужны данные, не включённые в обработанный формат.permanentURLs и не могут быть удалены через API. Вызов hooks/destroy для постоянного хука возвращает ошибку destroyMissingHook, как будто хук не существует.2xx и HTTP 401 оба считаются успешной доставкой. Все остальные коды статуса, включая 3xx (после перехода максимум по 10 перенаправлениям), 4xx (кроме 401) и 5xx, вызывают повторную попытку. Такое поведение не задокументировано в официальной документации BigBlueButton.