Voximplant 26.700.0: оценку качества связи убрали, у notifyAdmins() появился тип
Ломающее обновление
Удалены или изменены публичные API — прикладной код может перестать работать.
Модуль телефонии voximplant 26.700.0 вышел в коробочный Битрикс24 11 сентября 2026 года, по данным официального канала «Битрикс24 changelog». Из карточки звонка убрали оценку качества связи вместе с её JS-событиями, у Im::notifyAdmins() появился тип параметра, а публичные константы SipStatusInformer теперь указывают на другие языковые фразы. Проверьте свой код, если у вас приложение на плейсменте фоновой карточки звонка, свои уведомления админам через модуль телефонии или внешняя АТС, которая грузит записи через telephony.externalCall.attachRecord.
Уберите подписки на оценку качества связи
В JS-расширении voximplant.phone-calls оценки качества связи больше нет. Из PhoneCallView удалили showQualityPopup(), closeQualityPopup(), _onQualityMeterClick(), колбэк qualityGraded и событие десктопа phoneCallViewOnQualityGraded. Из PhoneCallsController убрали обработчик, который отправлял в звонок команду gradeQuality.
Для приложений на плейсменте фоновой карточки звонка это ломающее изменение. Событие BackgroundCallCard::qualityMeterClick удалено из фонового воркера (src/view/background-worker/base-worker.js), а его десктопный вариант DesktopCallCardQualityMeterClick удалён из desktop-worker.js. Подписанное на них приложение этих событий больше не получит. Код, который сам вызывает showQualityPopup() или closeQualityPopup() у PhoneCallView, упадёт с TypeError, потому что этих методов больше нет.
Индикатор качества в карточке остался, но нажать на него нельзя. У .im-phone-call-btn-signal вместо cursor: pointer теперь стоит pointer-events: none, так что обработчик, повешенный на сам индикатор, тоже не сработает. Стили .im-phone-popup-rating* и фразу IM_PHONE_CALL_VIEW_RATE_QUALITY удалили.
Проверьте, что передаёте в Im::notifyAdmins()
Было notifyAdmins($notification, array $buttons = array()), стало notifyAdmins(string|array|null $notification, array $buttons = array()). Из файла с declare(strict_types=1) число или bool в первый параметр больше не передать, будет TypeError. Кроме строки метод принимает массив с ключами NOTIFY_MESSAGE, NOTIFY_TITLE, SUBJECT и PLAIN_TEXT.
Поменялось и то, что уходит админам:
null,''и[]метод пропускает сразу, пустое уведомление больше не отправляется;- в
CIMNotify::Addвсегда передаётсяNOTIFY_TITLE, а без своего заголовка подставится фразаVOXIMPLANT_NOTIFICATION_TITLE(«Телефония»); - в
PARAMSдобавленыCOMPONENT_ID => 'DefaultEntity'иCOMPONENT_PARAMSсSUBJECTиPLAIN_TEXT. ЕслиPLAIN_TEXTне передан, его получают из сообщения через\Bitrix\Im\Text::removeBbCodes().
Формат DefaultEntity уже используют уведомления других модулей, например mail (lib/integration/im/notification.php) и tasks (lib/integration/im/notification/usecase/taskcreatedv2.php). У Im::notifyChangeSipRegistrationStatus($notification) сигнатура прежняя, а поведение поменялось так же. Пустые сообщения он пропускает, остальные отправляет с заголовком в формате DefaultEntity.
Сам модуль тоже перешёл на массив. Команда контроллера NotifyAdmins в controller_hit.php принимает необязательные SUBJECT и TITLE. TITLE уходит в NOTIFY_TITLE, а если SUBJECT не пришёл, темой становится тот же TITLE.
Уведомление со своим заголовком и темой:
<?php declare(strict_types=1);
namespace Vendor\Telephony\Service;
use Bitrix\Main\Loader;
use Bitrix\Voximplant\Integration\Im;
final class SipLinesAlert
{
public function send(int $linesDown): void
{
if ($linesDown <= 0 || !Loader::includeModule('voximplant')) {
return;
}
// Im::notifyAdmins($linesDown) с 26.700.0 бросит TypeError
Im::notifyAdmins([
'NOTIFY_TITLE' => 'SIP-линии',
'SUBJECT' => 'Часть SIP-линий недоступна',
'NOTIFY_MESSAGE' => sprintf('Недоступно линий: [b]%d[/b]', $linesDown),
// PLAIN_TEXT соберётся из NOTIFY_MESSAGE без BB-кодов
]);
}
}
Читайте SIP-фразы через константы SipStatusInformer
В Bitrix\Voximplant\SipStatusInformer у публичных констант SHORT_STATUS_FAILED, SHORT_STATUS_RECOVERED и LONG_STATUS_FAILED к значениям добавился суффикс _MSGVER_2. Значения констант — это ключи языковых фраз. Старые ключи SIP_SHORT_STATUS_INFORMER_FAILED_WITH_LINK, SIP_SHORT_STATUS_INFORMER_RECOVERED и SIP_LONG_STATUS_INFORMER_FAILED_WITH_LINK удалили из lang/ru и lang/en. Код, который берёт фразу по константе, получит новый текст, а код со старым ключом строкой получит пустое значение.
<?php declare(strict_types=1);
namespace Vendor\Telephony\Sip;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bitrix\Voximplant\SipStatusInformer;
final class SipStatusText
{
/** @param array<string, string> $substitutions */
public function failed(array $substitutions): string
{
Loader::requireModule('voximplant');
// вместо 'SIP_SHORT_STATUS_INFORMER_FAILED_WITH_LINK', которого больше нет
return (string)Loc::getMessage(SipStatusInformer::SHORT_STATUS_FAILED, $substitutions);
}
}
Вместе с ключом поменялся текст короткой фразы о восстановлении подключения. Фраза SIP_SHORT_STATUS_INFORMER_RECOVERED_MSGVER_2 подставляет #PROXY# и #LOGIN# вместо #PHONE_NAME# и по смыслу совпала с длинной SIP_LONG_STATUS_INFORMER_RECOVERED. При этом SipStatusInformer::getStatusRecoveredText() выбирает короткий вариант как раз тогда, когда #PHONE_NAME# не пуст, и имя телефона в текст теперь не попадает. Если собираете текст сами, передавайте в подстановки #PROXY# и #LOGIN#.
Проверьте загрузку записей через attachRecord
Сигнатура Bitrix\Voximplant\Rest\Helper::attachRecordWithUrl($callId, $recordUrl, $fileName = '') не изменилась, но результат вызова стал другим. Его вызывает REST-метод telephony.externalCall.attachRecord, когда передан RECORD_URL, поэтому изменение касается интеграций, которые отдают запись ссылкой.
Тип файла сначала определяется по содержимому через \CFile::GetContentType(). Заголовок Content-Type из ответа удалённого сервера берётся, только если нет функции mime_content_type и по содержимому получилось '' или application/octet-stream. Найденный тип уходит в \CFile::MakeFileArray($tempPath, $fileType). Разработчики объясняют это в комментарии к коду. Сервер за recordUrl выбирает тот, кто вызывает метод, поэтому Content-Type его ответа считается данными под контролем атакующего. Если fileinfo работает, application/octet-stream считается законным результатом для непрозрачных бинарных данных, и заголовок его не перебивает.
Потом новый private-метод ensureRecordFileExtension() дописывает к имени файла расширение по MIME-типу, а неразрешённое расширение заменяет. Раньше запись без .mp3, .flac или .wav в имени не проходила белый список расширений в RecordFile::isCorrectFromArray(), который вызывает \CFile::CheckFile(). Теперь она проходит, если содержимое распознано как audio/mpeg, audio/flac, audio/wav или их варианты. Для типов вне карты имя не меняется, и файл по-прежнему отклоняется.
Проверенное имя записывается и в $fileName. В коде объяснено, что Диск берёт NAME, а не fileArray['name'], так что на Диск ляжет то имя, которое прошло проверку.
Белый список и перевод MIME-типов в расширения теперь доступны снаружи. RecordFile::getAvailableExtensions() возвращает mp3, flac, wav, а RecordFile::resolveExtensionByType() переводит MIME-тип в расширение: audio/mpeg и audio/mp3 в mp3, audio/flac и audio/x-flac в flac, audio/wav, audio/wave и audio/x-wav в wav. Для остальных типов он возвращает null.
<?php declare(strict_types=1);
namespace Vendor\Pbx\Record;
use Bitrix\Main\Loader;
use Bitrix\Voximplant\Security\RecordFile;
final class RecordNameResolver
{
public function resolve(string $callId, string $mimeType): ?string
{
Loader::requireModule('voximplant');
$extension = RecordFile::resolveExtensionByType($mimeType);
// null: тип вне карты, запись без расширения в имени attachRecord отклонит
return $extension === null ? null : $callId . '.' . $extension;
}
}
Триггеры звонков получили номер, направление и статус
CVoxImplantCrmHelper::StartCallTrigger() и StartMissedCallTrigger() теперь передают в \Bitrix\Crm\Automation\Trigger\CallTrigger::execute() и MissedCallTrigger::execute() кроме LINE_NUMBER ещё три ключа:
CALL_PHONE_NUMBERиз$call->getCallerId();CALL_DIRECTION,outgoingдляCVoxImplantMain::CALL_OUTGOINGиincomingдля остальных;CALL_STATUSиз$call->getStatus(), одна из константSTATUS_*вlib/model/call.php:waiting,connecting,connected,enqueued,finished.
Пока эти ключи никто не читает. CallTrigger::checkApplyRules() сравнивает только LINE_NUMBER. Так и в crm 26.500.100, и в более новой crm 26.800.0. Похоже на заготовку под будущую версию crm. Если будете на них опираться, учтите, что incoming получают и обратный звонок (CALL_CALLBACK = 4), и переадресованный входящий (CALL_INCOMING_REDIRECT = 3), и CALL_INFO = 5.
Мелочи и находки
- Ссылка на документацию в компоненте
voximplant.config.editтеперь ведёт на статью helpdesk с кодом 19420080. Если подключён модульui, адрес строит\Bitrix\UI\Util::getArticleUrlByCode('19420080')с доменом по языку интерфейса (дляruэто helpdesk.bitrix24.ru), безuiостаётсяhttps://helpdesk.bitrix24.com/open/code_19420080/. Раньше в зависимости от языка и наличия модуляbitrix24ссылка вела на курсы dev.1c-bitrix.ru или на bitrixsoft.com. - В
install/js/voximplant/voximplant.min.jsобновили Web SDK VoxImplant, строка версии сменилась с4.13.1-3005на4.13.4-3014. phone-calls.bundle.min.cssраньше лежал неминифицированным (1451 строка), теперь он собран в одну строку. Отсюда почти все 1911 удалённых строк в статистике диффа.VERSION_DATEу 26.700.0 (2026-07-27 18:03:09) раньше, чем у предыдущей версии 26.600.100 (2026-08-26 16:43:15).- В русской фразе
SIP_LONG_STATUS_INFORMER_FAILED_WITH_LINK_MSGVER_2после\nостались лишние пробелы перед «Логин» и «Код ошибки». - У 13 файлов сняли бит исполнения (100755 → 100644), среди них
controller_hit.php,lib/integration/im.php,lib/security/recordfile.php,lib/sipstatusinformer.php, изменённые JS-исходникиphone-calls/srcи языковые файлыsipstatusinformer.phpиphone-calls/lang/*/config.php. - Deprecated-пометок в релизе нет, схема БД не менялась.
Что делать
- Поищите в JS своих приложений и шаблонов
qualityMeterClick,DesktopCallCardQualityMeterClick,phoneCallViewOnQualityGraded,qualityGradedиshowQualityPopupи уберите подписки и вызовы. - Найдите вызовы
Im::notifyAdmins()из файлов соstrict_types=1и проверьте, что туда уходит строка, массив илиnull. Для своего заголовка передавайте массив сNOTIFY_TITLE, иначе админ увидит «Телефония». - Замените строки
SIP_SHORT_STATUS_INFORMER_FAILED_WITH_LINK,SIP_SHORT_STATUS_INFORMER_RECOVEREDиSIP_LONG_STATUS_INFORMER_FAILED_WITH_LINKв своём коде на константыSipStatusInformer. - Если внешняя АТС отдаёт записи ссылкой, прогоните
telephony.externalCall.attachRecordна тестовом портале с записью без расширения в имени и посмотрите, под каким именем она легла на Диск.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
voximplant.phone-calls: BackgroundCallCard::qualityMeterClick
|
Удалено | — |
voximplant.phone-calls: DesktopCallCardQualityMeterClick
|
Удалено | — |
voximplant.phone-calls: PhoneCallView.showQualityPopup()
|
Удалено | — |
voximplant.phone-calls: PhoneCallView.closeQualityPopup()
|
Удалено | — |
voximplant.phone-calls: PhoneCallView.setCallback('qualityGraded')
|
Удалено | — |
voximplant.phone-calls: phoneCallViewOnQualityGraded
|
Удалено | — |