Привет, Хабр! (И тебе, случайный читатель, который зашёл сюда просто просто потому, что заскучал в корпоративном чате.)

Сегодня я расскажу вам историю о том, какая задача посетила меня на этот раз и как я сделал «корпоративного бота с возможностью оценки сотрудников» — казалось бы, простая задача, но… Нас ждёт много удивительных вещей :-)

Изначально мой план был такой:

  1. Сделать бота в Телеграме.

  2. Дать боту ролевую модель и базовый функционал для дальнейшего простого расширения возможностей (по сути, я пришёл к тому, что сделал свой конструктор бота для Bitrix24, но об этом не в этой статье).

  3. Научить его собирать фидбэк о коллегах.

  4. Добавить машинное обучение, чтобы он сам выявлял, кто у нас в офисе трудяга, а кто мастерски имитирует бурную деятельность, ну и вообще эмоциональный фон в коллективе).

Но, как это часто бывает в IT, реальность внесла коррективы.

Отдел информационной безопасности посмотрел на мой прототип и сказал:
— «Персональные данные в Телеграме? Ну уж нет, это не по-нашему, не по-православному!»

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

Глава 1. Выбор мессенджера: три пути в никуда

Мне предложили три варианта отечественных мессенджеров:

  1. VK – «Круто, есть API!» Но…

    • Половина сотрудников им не пользуется.

    • Безопасность? Ха! То аккаунты взламывают, то сам VK их теряет.

  2. MAX (тот самый «патриотичный» мессенджер) – «Звучит гордо!» Но…

    • Из 250 сотрудников им пользуется ровно один.

    • API выглядит кстати очень здорово (приятно удивило).

  3. Битрикс24 – «Он у нас уже есть, сервера свои, все сотрудники уже там!» Идеально, подумал я и начал изучать API? Как бы не так…

Глава 2. Документация Битрикса: игра в угадайку

API Битрикса – это как квест с сюрпризами, болью, слезами и страданием, а ещё в конце появляется скример.

Bitrix скримеры которых я боюсь
Bitrix скримеры которых я боюсь

Написано конечно всё классно, плюс/минус понятным языком, жаль конечно, что из примера реализации каждого метода то JS, то PHP, причём реально где-то откроешь метод и там сидит ПХП, а где-то полный набор с cURL, походу зависело от познаний разработчиков. Но вот вопрос, а где Python как можно было забыть этого красавчика.

В Bitrix забыли python
В Bitrix забыли python

Ладно, перейдём к примерам…

Открываем метод imbot.message.add – это отправка сообщений от лица бота, здесь у нас PHP

Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/

Открываем метод по созданию структуры компании (department.add) и здесь у нас уже полный набор.

Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/

И как я понял тот, кто делал API для бота владел одним стеком (PHP), а в остальных разделах API были разносторонние ребята, ну или те, кто умели пользоваться ГПТ, но это на самом деле не очень, важно…

Важно это:

Берём к примеру метод imbot.message.add – простой и понятный метод, отвечающий за отправку сообщений от лица бота. Смотрим на него детальнее…

Документация APi Bitrix24 - https://apidocs.bitrix24.ru/
Документация APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/

Красиво? Понятно? – Мне показалось, что да. А теперь хотите узнать, как оно на самом деле?

Истинная структура
Истинная структура

Да, в KEYBOARD у нас вложился BUTTONS. В BUTTONS вообще массив с перечислением кнопок. CLIENT_ID у нас так же появился из неоткуда, без него вообще запрос возвращал ошибку запрета доступа (скрин со скримером). А MENU где? Его нет.

Как я это понял?

  • 5 часов гугления.

  • 10 часов перебора вариантов (просто тупо перебора вариантов как бы это могло работать...).

  • 1 нервный срыв.

Но самое весёлое – документация врёт нам во многом.

Документация APi Bitrix24 - https://apidocs.bitrix24.ru/
Документация APi Bitrix24 - https://apidocs.bitrix24.ru/

Например, параметр BG_COLOR якобы меняет цвет кнопки. На деле – нет. Есть только четыре предустановленных варианта и то они в BG_COLOR_TOKEN:

primary (синий)
primary (синий)
secondary (белый с синей обводкой)
secondary (белый с синей обводкой)
alert (красный)
alert (красный)
base (прозрачный + серая обводка)
base (прозрачный + серая обводка)

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

Документация APi Bitrix24 - https://apidocs.bitrix24.ru/ (с моим пояснением: Синяя стрелочка то что не влияет на цвет кнопки, а красная стрелочка то что влияет)
Документация APi Bitrix24 - https://apidocs.bitrix24.ru/ (с моим пояснением: Синяя стрелочка то что не влияет на цвет кнопки, а красная стрелочка то что влияет)

Кстати, перед тем как, к примеру создавать command в KEYBOARD её ещё надо зарегистрировать в системе Битрикса. Это делается через метод imbot.command.register. Смотрим на пример.

Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/

А делаем вот так:

Истинная структура
Истинная структура

Вывод: Документация Битрикса — как квантовая физика: если кажется, что вы её поняли, значит, вы точно что-то упустили.

Глава 3. Локальный Битрикс? Не смешите мои серверы

Для начала давайте посмотрим, как вообще создаётся бот.

Из раздела "Разработчикам" мы можем попасть в меню выбора разработки.

Переход в раздел Разработчики
Переход в раздел Разработчики

Для создания бота мы выбираем «Добавить чат-бот», но на самом деле если вы будете делать нечто большее чем просто чат бот с быстрыми командами, то придется попотеть. Но пока не об этом.

После того как мы перешли в «Добавить чат-бот» и перешли следующий пункт (кстати который может у всех отличаться и как я понимаю вы можете брать понравившийся вам) у меня «Информировать сотрудников в чате». Переходим и видим панель создания бота.

Переход в Создание бота
Переход в Создание бота

Вебхук для вызова rest api – это механизм уведомления одного сервиса другим о произошедших событиях. Но простыми словами и в данном случае это просто API ключ. Иногда когда вы будете менять права доступа своему боту, вам может понадобиться перегенерировать данный ключ.

Создание бота:

Название бота* - Здесь у нас будет название бота таким каким пользователи будут его видеть и находить через поиск по чатам битрикса.

Название бота в нашем случае
Название бота в нашем случае

Кстати, если вы хотите изменить картинку боту, то это "ТИПА" можно сделать через запрос imbot.update в формате base64 - но у меня сколько я не пытался не получилось, возможно здесь таже ситуация с неправильной или не актуально докой, я сделал это через «Панель администратора Битрикс», о ней будет в следующем разделе.

Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/
Пример из документации APi Bitrix24 - https://apidocs.bitrix24.ru/

URL обработчика бота* - это, пожалуй, самая важная штука. Сюда мы указываем адрес нашего сервера, на котором мы будем слушать обращения. Это нужно, чтобы, к примеру, когда пользователь пишет боту сообщение, или запускает команду, бот нам слал Сообщение.

А теперь перейдём к самой теме…

Мы столкнулись с интересной проблемой: несмотря на то, что сервер с Битрикс и мой локальный компьютер разработки находились в одной сети, сервер не мог достучаться до URL обработчика, указанного на моей машине. Это вылилось в долгий процесс поиска причины и решения.

Игра «Кто хочет стать разработчиком Bitrix-бота»:

Игра «Кто хочет стать разработчиком Bitrix бота»
Игра «Кто хочет стать разработчиком Bitrix бота»

И тут «A» понятно, что шутка. «С» – порт мы пробросили через NAT, что бы он пинговал мою машину. «D». Тут как бы не столь критично, но самогенерированный (псевдо) сертификат hpps мы сделали. А верный ответ «B».

У нас on-premise версия Битрикса (чтобы всё было на наших серверах).

Но вот загадка:

Данный скриншот после команды "tcpdu"
Данный скриншот после команды "tcpdu"

Почему запросы от бота идут через облако сторонней компании?

Разгадка:

  • Бот отправляет сообщение → запрос улетает на Corp Soft, Seleznevskaya street, 32 (это облачный провайдер Битрикса) и он такой не один, мы так же поймали как минимум пять ip VK (походу он так сильно хотел нашего бота).

  • Вопрос: Зачем гонять трафик через третьи лица, если у нас локальная версия?

Теории:

  • Кто-то накосячил в архитектуре.

  • Это фича, а не баг (чтобы мы купили облако).

Это весьма интересный вопрос, от которого вообще теряется весь смысл on-prem поставки продукта. Ведь нельзя было использовать мощности сервера для отправки запроса, зачем гонять трафик через 3-х лиц. Короче мне кажется, что здесь кто-то просто сильно накосячил.

Глава 4. Бот, который боится файлов

Хочу обратить внимание на проблему: ваш бот по умолчанию не может читать файлы — он их "боится". В целом, это логично, но не до конца.

Вы создали бота под себя — вы его автор и владелец, царь и бог. Однако Битрикс сохраняет все файлы на Битрикс Диск в каталоге чата. Но бот — это не совсем вы. Он должен самостоятельно обрабатывать переданные файлы через свой вебхук, а не полагаться на ваши права.

К сожалению, стандартного решения нет. В интернете я нашёл только информацию о платных подписках на "супер-пупер бота", который решает эту проблему. Но мы же разработчики-экспериментаторы!

Решение?

  • Получаем права администратора (каждый делает это по-своему). И заходим в администрирование Bitrix.

  • Находим бота среди пользователей (извините, скриншотов не будет — это конфиденциально).

  • Выдаём ему права обычного сотрудника (чтобы он не выделялся).

  • Авторизуемся под ботом через кнопку «Авторизоваться под сотрудником» в Панеле администрования Bitrix.

  • Настраиваем персональный вебхук для бота (как показано на скриншотах).

Инструкция создания Вебхука
Инструкция создания Вебхука
Инструкция создания Вебхука
Инструкция создания Вебхука
Инструкция создания Вебхук
Инструкция создания Вебхук

Даём права в «Настройка прав». А лучше сразу дать всё чтобы больше не лазить сюда. Копируем сформированный Вебхук. Закрываем и забываем как страшный сон.

И получается у нас смешная картина. Мы сделали бота, наделили его правами создателя, а затем воскресили его (превратив в сотрудника) и выдали ему ещё токен под его правами, и теперь у нас есть 2 токена (1 супер крутой – принадлежит боту, другой простенький и принадлежит нам), и пишем код, который будет маршрутизировать использование прав в зависимости от ситуаций. Не хотите писать, я уже написал, правда не судите строго))

import json
import requests
import itertools
from typing import Dict, Any, List, Optional

class Bitrix24API:
    """Класс для работы с REST API Bitrix24 через вебхук(и)"""
    def __init__(self, webhook_url: str, array_webhooks: Optional[List[str]] = None, logger=None, fetch_all_pages: bool = True, page_size: int = 50):
        """
        Инициализация класса с URL вебхука(ов) Bitrix24
        ---
        :param webhook_url: URL вебхука Bitrix24
        :param array_webhooks: Список альтернативных вебхуков для перебора
        :param logger: Объект логирования от библиотеки logging
        :param fetch_all_pages: Автоматически загружать все страницы при пагинации
        :param page_size: Количество элементов на странице (по умолчанию 50)
        """
        self.webhook_url = webhook_url
        self.array_webhooks = array_webhooks or []
        self.logger = logger
        self.specifically = None
        self.fetch_all_pages = fetch_all_pages
        self.page_size = page_size

    # ---Внутрение методы---
    def _try_call_method(self, webhook_url: str, method: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """Внутренний метод для попытки вызова метода через конкретный вебхук"""
        url = f"{webhook_url}{method}"
        try:
            response = requests.post(url, json=params)
            response.raise_for_status()
            result = response.json()
            if 'error' in result:
                if self.logger: self.logger.error(f"Bitrix API ошибка в '{webhook_url}': {result['error']}")
                return None
            if self.logger:  self.logger.info(f"Запрос к Bitrix API выполнен успешно - method: {method} - 200")
            return result
        except requests.exceptions.RequestException as e:
            if self.logger:  self.logger.error(f"Ошибка запроса в '{webhook_url}': {e}")
            return None
        except json.JSONDecodeError as e:
            if self.logger: self.logger.error(f"Ошибка декодирования JSON в '{webhook_url}': {e}")
            return None

    def _call_with_pagination(self, webhook_url: str, method: str, params: Dict[str, Any], start: int = 0) -> Optional[Dict[str, Any]]:
        """
        Внутренний метод для обработки пагинации
        ---
        :param webhook_url: URL вебхука
        :param method: Метод API
        :param params: Параметры запроса
        :param start: Смещение для пагинации
        :return: Объединенный результат или None при ошибке
        """
        params = params.copy()
        params['start'] = start
        result = self._try_call_method(webhook_url, method, params)
        if not result: return None
        if not self.fetch_all_pages or 'next' not in result or start > 0: return result.get('result')
        # Если нужно получить все страницы и это первый запрос
        total = result.get('total', 0)
        if total <= self.page_size:
            return result.get('result')
        # Вычисляем количество дополнительных запросов
        count_pages = (total // self.page_size) - (1 if total % self.page_size == 0 else 0)
        # Собираем результаты со всех страниц
        all_results = [result.get('result', [])]
        for page in range(1, count_pages + 1):
            next_result = self._call_with_pagination( webhook_url, method, params, page * self.page_size)
            if next_result is not None: all_results.append(next_result)
        # Объединяем результаты
        if isinstance(all_results[0], list):
            return list(itertools.chain.from_iterable(all_results))
        elif isinstance(all_results[0], dict):
            merged = {}
            for res in all_results: merged.update(res)
            return merged
        return all_results[0]

    # ---Внешние методы---
    def callMethod(self, method: str, params: Optional[Dict[str, Any]] = None, 
                  specifically: Optional[str] = None) -> Any:
        """
        Отправляет запрос к Bitrix24 REST API с поддержкой пагинации
        ---
        :param method: Метод API (например, "crm.deal.list")
        :param params: Параметры запроса в виде словаря
        :param specifically: Конкретный вебхук для использования
        :return: Ответ от сервера Bitrix24 или None в случае ошибки
        """
        if params is None: params = {}
        webhooks_to_try = []
        if specifically is not None: webhooks_to_try = [specifically]
        else:
            if self.webhook_url: webhooks_to_try.append(self.webhook_url)
            webhooks_to_try.extend(self.array_webhooks)
        for webhook in webhooks_to_try:
            result = self._call_with_pagination(webhook, method, params)
            if result is not None: return result
        if self.logger:
            self.logger.error(f"Запрос к Bitrix API никак не отработал {method}, {params}")
        return None


# # ====================================
# # ----------Пример использования------
# if __name__ == "__main__":
#     # Инициализация клиента с несколькими вебхуками
#     main_webhook = "https://...Токен_Бота"
#     backup_webhooks = ["https://...Токен_Разработчика", "https://...Токен_Васи_Пупкина", "https://...Токен_ФигЗнаетКого"]
    
#     bitrix = Bitrix24API(webhook_url=main_webhook, array_webhooks=backup_webhooks, logger=C.LOGGER_BITRIX_API)
#     result = bitrix.callMethod("calendar.section.get", {"type": "user", "ownerId": "0"}, specifically="https://...Токен_Разработчика")
#     print(result)
# # ====================================

Итоговая схема:

Вот такую я нарисовал схему взаимодействия, на первый взгляд возможно не очевидную.

Схема того как работает наш бот на on-premise Bitrix24
Схема того как работает наш бот на on-premise Bitrix24

Вывод: кто виноват и что делать?

  • Документация Битрикса – это квест. Гугл и метод тыка спасают.

  • «Локальный» Битрикс иногда ведёт себя как облачный.

  • Боты боятся файлов, пока не дашь самостоятельность боту (а это может быть страшно).

Мораль:
Если ваш бот в Битриксе работает с первого раза – проверьте, не снится ли вам это.

P.S. В следующей статье расскажу, как сделать боту ролевую модель, научить бота отвечать на любые произвольные команды пользователя, подружить его с СУБД , заставить этого бота анализировать сотрудников (и не сойти при этом с ума).

Комментарии (4)


  1. vacoo
    12.08.2025 09:16

    Подтверждаю. Битрикс это лютая дичь. Код очень низкого качества


  1. Pluzh
    12.08.2025 09:16

    В битриксе так устроено буквально всё, не только боты.

    Их документация:
    А) Разбросана как попало по множеству страниц.
    Б) Неполная
    В) Недостоверная, то есть часто там вообще описано что-то не то, что реально есть в битриксе.

    Можно было бы применять вместо документации глаза и логику. Но элементы внутри самой системы:
    А) Разбросаны внутри с грубыми нарушениями логики
    Б) Не содержат функций, которые ожидаешь увидеть в любом интерфейсе.
    В) Делают не то, что на них написано.
    Г) Имеют незадокументированные исключения в применимости.
    Д) Однотипные элементы работают по разной логике.
    Обычная фильтрация может в одном месте действительно фильтровать, как ожидается, а в другом месте фильтровать, но совсем не так.

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


  1. serg-vostrikov
    12.08.2025 09:16

    Спасибо за статью! Разобраться с особенностями ботов - не самая простая история. Это достаточно старый функционал, который по мере развития, оброс рядом костылей в REST API, к сожалению. И пока мы еще не до конца актуализировали материалы по ботам в доке. Как раз в ближайших планах обновление по этому поводу от команды коммуникаций.

    И да, текущая версия REST API - это зоопарк, получившийся в силу интенсивного наращивания пользовательского функционала, прежде всего. Битрикс24, признаем, не API first продукт. Но мы стараемся исправиться в новой версии REST API, которая уже жестко регламентирована для внутренней разработки. Думаю, скоро первые методы уже выкатим.

    Было бы очень круто, если бы вы этот материал в виде туториала закинули в доку - https://github.com/bitrix-tools/b24-rest-docs. Уверен, что много людей были бы очень благодарны за подробный разбор особенностей bot api в Битрикс24 :).


    1. MrSotnik Автор
      12.08.2025 09:16

      Отлично, появиться свободное время попробую туда изложить проблемные места и то как с этим можно и нужно бороться))