Привет, Хабр! (И тебе, страдалец, который три недели смотрит на мёртвого бота в Битриксе. И тебе, админ, который уже устал объяснять руководству, почему «оно перестало работать». И тебе, безопасник, который узнал, что данные компании летают через какой-то curator.pro и чуть не уронил кружку.)
Помните мою прошлую статью про разработку Битрикс-бота? Ту самую, где я рассказывал, как документация врала, облака смеялись, а трафик зачем-то летел через сторонние сервера? Так вот - продолжение банкета.
Спойлер: стало хуже. Но мы справились.
Предыстория, или «Как Битрикс убил нашего бота и попросил за воскрешение 50 тысяч»
Три месяца назад наш корпоративный бот на Python, который честно трудился в коробочном Битрикс24, просто замолчал. Как обиженный кот - смотрит на тебя, но не отвечает. А мы его так любили, столько фич прикрутили...:-(
Начали разбираться. Открыли tcpdump, посмотрели на трафик и увидели знакомую картину из прошлой статьи: запросы от пользователя летят на внешние сервера. Ладно, ок, но... Только раньше это были сервера VK и corp-soft, а теперь - какой-то curator.pro. И если раньше это хотя бы работало (хоть и бесило), то теперь от curator.pro ответ просто… не приходит. До него идёт, а от него нет - блочат. Вообще. Тишина. Пустота. Космический вакуум. Ничего не приходит на наш сервер с ботом…
Написали в поддержку Битрикс. Ответ был в стиле классического 1С-го «это фича, а не баг»:
«Для ра��оты исходящих событий на коробочных установках сейчас понадобится подписка на Маркет.»
Перевод на человеческий: «Заплатите ~50 000 рублей в год, и ваш бот снова оживёт». Как будто взяли наш родненький бот в заложники.

Но подождите. У нас коробочная версия Битрикса. Она стоит на нашем сервере. Бот крутится на нашем сервере. Данные - наши. С чего нам нужно платить за подписку, чтобы один наш сервер мог отправить HTTP-запрос другому нашему серверу?!
Мы так и спросили у поддержки. И тут произошло чудо - они дали полезный ответ. Впервые за всю мою практику общения с поддержкой я получил ссылку, которая реально помогла:
«Можно использовать свой провайдер авторизации» Ссылка: apidocs.bitrix24.ru — Custom Auth Provider
И понеслась…
Глава 1. Что вообще произошло и почему всё сломалось
Давайте разберёмся, что случилось. Я нарисую картинку, потому, что без неё тут можно свихнуться.
Раньше (до новой политики Битрикс):

Когда пользователь писал боту сообщение, Битрикс генерировал событие и отправлял его на URL обработчика бота. Да, трафик зачем-то летел через внешние сервера (я об этом писал), но хотя бы доходил.
Теперь (после изменения политики):

Там где крестик там у нас просят деньги, что бы разблокировать данный маршрут.

Битрикс решил, что отправка событий - это премиум-функция. Без подписки на Маркет события просто не отправляются. Вообще. Ваш бот может стоять хоть в соседней серверной стойке - Битрикс ему ничего не пошлёт.
Это как если бы почтальон пришёл к вашему дому, посмотрел на дверь и сказал: «Извините, без подписки на "Почта+" я вам письма не ношу. Даже если отправитель - ваш сосед по лестничной клетке».
Глава 2. Решение: делаем свой провайдер (с блэкджеком и без curator.pro)
Документация Битрикса предлагает создать собственный провайдер авторизации и событий. Звучит страшно, но на деле - это PHP-модуль из 6 файлов, который говорит Битриксу: «Не надо слать события на внешние сервера. Я сам отправлю, напрямую, по локалке».
На самом деле эту доку можно раз 10 прочитать и ничего не понять. Плюс у нас вообще ни у кого не было опыта работы с PHP, но пришлось учиться. Наверное, при написании этой доки специально закладывали смысл, что тот, кто будет её читать психанёт и купит подписку. Но нас бонусом ко всему напрягал момент прогулки данных по разнообразным серверам никак не предлежащих битриксу. Поэтому вооружившись успокоительным мы начали действовать.
Да, дока даёт понимания, что нужно сделать, но не даёт понимания, где это нужно сделать с первых разов прочтения, плюс ко всему и не даёт представления как это потом всё запустить и протестить…
Что делает модуль (простым языком):
Перехватывает момент отправки события. Когда пользователь пишет боту, Битрикс хочет отправить событие через свои облака. Наш модуль говорит: «Стоп. Я сам».
Отправляет событие напрямую. Вместо curator.pro делает обычный HTTP POST на URL вашего Python-бота.
Управляет авторизацией локально. Вместо проверки токенов через
oauth.bitrix24.techгенерирует и проверяет их сам.
По сути, мы выкидываем из цепочки всех посредников и делаем то, что Битрикс должен был делать с самого начала - отправлять запрос из точки А в точку Б без путешествия через полмира.
Глава 3. Структура модуля (не бойтесь, файлов всего 6)
Сейчас мы начнём с вами максимально простым языком погружаться в то, как всё это реализовать.
В примерах в статье будет изложена реализация урезанного провайдера, так как именно его мы выбрали для реализации бота, но на GitHub вы можете взять по��ную версию провайдера с инструкцией установки.
Весь модуль выглядит так. Структура полного провайдера:
/local/modules/local.authprovider/ ├── install/ │ ├── index.php ← установка/удаление модуля │ ├── step.php ← сообщение "Ура, установлено!" │ ├── unstep.php ← сообщение "Ну и ладно, удалено" │ └── version.php ← версия (1.0.0, мы же оптимисты) ├── include.php ← автозагрузка классов └── lib/ ├── AuthSimple.php ← валидатор авторизации ├── EventProvider.php ← ГЛАВНЫЙ ГЕРОЙ: отправка событий напрямую └── AuthProvider.php ← провайдер токенов
Весь модуль выглядит так. Структура урезанного провайдера (о нём и пойдёт речь дальше):
/local/modules/local.authprovider/ ├── install/ │ ├── index.php ← установка/удаление модуля │ ├── step.php ← сообщение "Ура, установлено!" │ ├── unstep.php ← сообщение "Ну и ладно, удалено" │ └── version.php ← версия (1.0.0, мы же оптимисты) ├── include.php ← автозагрузка классов └── lib/ ├── EventProvider.php ← ГЛАВНЫЙ ГЕРОЙ: отправка событий напрямую
Теперь пройдёмся по каждому файлу. Я постараюсь объяснить максимально понятно, даже если вы, как и я, от PHP держитесь на расстоянии вытянутой руки.

Глава 4. Код. Каждый файл с пояснениями
install/version.php - Версия модуля
Самый простой файл. Даже объяснять стыдно
<?php $arModuleVersion = array( "VERSION" => "1.0.1", "VERSION_DATE" => "2025-01-01 00:00:01" );
install/step.php и install/unstep.php - Сообщения при установке/удалении
<?php if (!check_bitrix_sessid()) return; echo CAdminMessage::ShowNote("Модуль local.chatbot установлен. Провайдер событий активирован.");
<?php if (!check_bitrix_sessid()) return; echo CAdminMessage::ShowNote("Модуль local.chatbot успешно удален.");
install/index.php - Главный файл установки
Этот файл говорит Битриксу: «Привет, я модуль. Вот мои обработчики. Зарегистрируй их, пожалуйста».
<?php use Bitrix\Main\ModuleManager; use Bitrix\Main\EventManager; class local_chatbot extends CModule { var $MODULE_ID = "local.chatbot"; var $MODULE_VERSION; var $MODULE_VERSION_DATE; var $MODULE_NAME; var $MODULE_DESCRIPTION; function __construct() { $arModuleVersion = array(); $path = str_replace("\\", "/", __FILE__); $path = substr($path, 0, strlen($path) - strlen("/index.php")); include($path . "/version.php"); if (is_array($arModuleVersion) && array_key_exists("VERSION", $arModuleVersion)) { $this->MODULE_VERSION = $arModuleVersion["VERSION"]; $this->MODULE_VERSION_DATE = $arModuleVersion["VERSION_DATE"]; } $this->MODULE_NAME = "Локальная отправка сообщений чат-бота"; $this->MODULE_DESCRIPTION = "Позволяет отправлять сообщения из чат-бота напрямую, не используя сервер oauth.bitrix.info"; } function DoInstall() { global $DOCUMENT_ROOT, $APPLICATION; // Регистрируем модуль ModuleManager::registerModule($this->MODULE_ID); // Регистрируем инициализацию провайдера событий EventManager::getInstance()->registerEventHandler( "rest", "onEventManagerInitialize", $this->MODULE_ID, "\\Local\\AuthProvider\\EventProvider", "onEventManagerInitialize" ); $APPLICATION->IncludeAdminFile( "Установка модуля " . $this->MODULE_ID, $DOCUMENT_ROOT . "/local/modules/" . $this->MODULE_ID . "/install/step.php" ); } function DoUninstall() { global $DOCUMENT_ROOT, $APPLICATION; EventManager::getInstance()->unRegisterEventHandler( "rest", "onEventManagerInitialize", $this->MODULE_ID, "\\Local\\AuthProvider\\EventProvider", "onEventManagerInitialize" ); ModuleManager::unRegisterModule($this->MODULE_ID); $APPLICATION->IncludeAdminFile( "Деинсталляция модуля " . $this->MODULE_ID, $DOCUMENT_ROOT . "/local/modules/" . $this->MODULE_ID . "/install/unstep.php" ); } }
include.php - Автозагрузка классов
Говорит Битриксу, где искать наши PHP-классы:
<?php // Автозагрузка классов модуля Bitrix\Main\Loader::registerAutoLoadClasses( "local.chatbot", array( "\\Local\\AuthProvider\\EventProvider" => "lib/EventProvider.php", ) );
lib/EventProvider.php - Главный герой статьи
Это самый важный файл. Именно он решает проблему. Вместо отправки событий через облака Битрикса - делает прямой HTTP POST на ваш локальный сервер.
<?php // Это **самый важный файл** — именно он решает вашу проблему, отправляя события напрямую на ваш Python-сервер вместо внешних серверов Битрикс: namespace Local\AuthProvider; use Bitrix\Rest\Event\ProviderInterface; use Bitrix\Rest\Event\ProviderOAuth; use Bitrix\Rest\Event\Sender; class EventProvider extends ProviderOAuth implements ProviderInterface { /** * Регистрируем себя как провайдер событий */ public static function onEventManagerInitialize() { Sender::setProvider(static::instance()); } /** * Переопределяем отправку событий. * Вместо отправки на внешние сервера Битрикс (curator.pro и т.д.) * делаем прямой HTTP POST запрос на URL обработчика бота. */ public function send(array $queryData) { $http = new \Bitrix\Main\Web\HttpClient(array( 'socketTimeout' => 5, 'streamTimeout' => 10, 'redirect' => true, 'redirectMax' => 3, )); $remainingData = array(); foreach ($queryData as $key => $item) { // Проверяем, есть ли URL для отправки if (!empty($item['query']['QUERY_URL'])) { $url = $item['query']['QUERY_URL']; // Если URL ведёт на наш локальный сервер — отправляем напрямую if ($this->isLocalUrl($url)) { // Логируем для отладки $this->log("Sending event directly to: " . $url); $postData = $item['query']['QUERY_DATA']; $result = $http->post($url, $postData); $this->log("Response code: " . $http->getStatus() . ", body: " . substr($result, 0, 500)); } else { // Не наш URL — собираем для отправки через стандартный механизм $remainingData[] = $item; } } else { $remainingData[] = $item; } } // Если остались события для внешних обработчиков — пробуем отправить стандартно if (count($remainingData) > 0) { try { parent::send(array_values($remainingData)); } catch (\Exception $e) { $this->log("Parent send failed: " . $e->getMessage()); } } } /** * Проверяет, является ли URL локальным (в вашей сети). * НАСТРОЙТЕ ПОД СЕБЯ! */ protected function isLocalUrl($url) { $host = parse_url($url, PHP_URL_HOST); if (empty($host)) { return false; } // Добавьте сюда ваши локальные адреса/домены $localPatterns = array( '127.0.0.1', 'localhost', // И вообще любые адреса с которыми мы хотим дружить ); // Проверяем по IP-диапазонам foreach ($localPatterns as $pattern) { if (strpos($host, $pattern) === 0 || $host === $pattern) { return true; } } return false; } /** * Простое логирование для отладки. * Логи будут в /local/modules/local.chatbot/debug.log */ protected function log($message) { $logFile = $_SERVER['DOCUMENT_ROOT'] . '/local/modules/local.chatbot/debug.log'; $timestamp = date('Y-m-d H:i:s'); file_put_contents($logFile, "[{$timestamp}] {$message}\n", FILE_APPEND); } }
Глава 5. Установка
mkdir -p /home/bitrix/www/local/modules/ # Создание директории local/modules (она может уже быть если есть другие модули) # Учтите, что путь у вас может быть свой chmod -R 755 /home/bitrix/www/local/ chown -R bitrix:bitrix /home/bitrix/www/local cd /home/bitrix/www/local/modules # Перешли в директорию # Далее сюда всё кладём по структуре
В админке Битрикса: Администрирование -> Marketplace -> Установленные решения -> Доступные решения - > Установить нужное (Локальная отправка сообщений чат-бота (local.chatbot).

И всё, если у вас уже всё было сделано для функционирования бота значит всё должно с этого момента заработать как и было, если нет делай бота и читай статью.
И я тебя поздравляю. Твой бот воскрес. Без подписки. Без curator.pro. Без регистрации. Без СМС. Без слёз. Ну ладно, почти без слёз...
Итог

Битрикс решил монетизировать то, что раньше было бесплатным. Это их право. Наше право - использовать документированные механизмы, чтобы наши серверы продолжали общаться друг с другом без посредников.
6 PHP-файлов. 5 минут установки. 0 рублей в год. И ваш бот снова работает.
P.S. Если вам кажется, что я написал целую статью, чтобы сэкономить компании 50 тысяч рублей - вы правы. Но на самом деле я написал её, чтобы наши персональные данные перестали летать через весь интернет, да и вообще меня взбесило, что бота которого я делал убил капитализм.
