Разработка кастомного коннектора для открытых линий Битрикс24 – из опыта

В двух предыдущих постах мы писали о том, как интегрировали Битрикс24 и WhatsApp, о том, почему для облака и для коробки были выбраны различные варианты реализации этой интеграции и о том, как данная интеграция была реализована нами для облачного Битрикс24.

В данном посте я расскажу о том, как мы реализовали кастомный коннектор для интеграции WhatsApp в открытые линии Битрикс24, используя API сервиса Chat2desk.

Как мы уже писали в предыдущих постах, мы отказались от использования приложения, написанного под облачные версии Битрикс в коробке. Главная причина – для коробки можно сделать интеграцию на более глубоком уровне, из-за доступности API Bitrix Framework. В отличии от облачной версии – в коробке мы можем создавать свои собственные коннекторы для открытых линий. Что позволит интегрировать мессенджеры через механизм открытых линий максимально близко к тому, как это делают «стандартные» коннекторы.

Существует официальный курс

https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=48&LESSON_ID=2184&LESSON_PATH=3918.4635.8395.2184 ).

по разработке кастомных коннекторов для Битрикс24. Однако не все технические моменты освещены в нем подробно.

Начинаем писать код с создания нового модуля под Битрикс Маркетплейс.

Снимок экрана 2018-09-12 в 15.38.46

Рис 1. Модуль в списке Доступных решений.

Создаем библиотеку модуля, в котором будет храниться логика его работы. Данный класс будет подключаться каждый раз при вызове модуля. Для отображения коннектора в списке доступных конекторов открытых линий – необходимо подписаться на событие OnImConnectorBuildList модуля imconnector, и по нему вернуть информацию о нашем новом коннекторе.

В библиотеке модуля реализовываем обработчик события OnImConnectorBuildList. Привязку обработчика к событию производим при установке модуля. Соответственно при удалении модуля – привязку снимаем.

Снимок экрана 2018-09-12 в 15.39.04

Рис 2. Коннектор с списке подключенных каналов открытой линии

В массиве информации о коннекторе, мы должны указать компонент, который будет отображаться в качестве формы настроек коннектора. Этот компонент создаем на основе стандартного компонента imconnector.baseconnector и помещаем в состав нашего модуля. 

Снимок экрана 2018-09-12 в 15.14.44

Рис 3. Форма настроек коннектора

В нашем случае нам необходимо хранить токен для доступа к агрегатору. Здесь сталкиваемся с первой проблемой. Стандартный механизм не предлагает нам способа хранения настроек. Вернее, компонент imconnector.baseconnector нам предлагает хранить настройки в именованном кэше. У нас этот кэш очищался раз в сутки, соответственно настройки сбрасывались. Поэтому мы таким способом не воспользовались и сохранили настройки, воспользовавшись COption::SetOptionString().

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

Начнем с приема сообщений в линию. Для этого нам понадобиться иметь точку входа – доступный извне скрипт, который будет принимать вебхуки от агрегатора, подключать наш модуль и передавать ему данные.

В самом общем виде код приема сообщений выглядит так:

Как видите, за отправку сообщений в линию отвечает функция \Bitrix\ImConnector\CustomConnectors::sendMessages. При ее выполнении Битрикс сам создаст новый чат (если нужно), пригласит туда ответственного, опубликует в чате сообщение.

Здесь всплывает следующий подводный камень. Как видно – в массиве message_data в поле user можно задать name (имя, с которым пользователь мессенджера будет добавлен в чат). При его задании – Битрикс будет проводить поиск по базе CRM и пытаться привязать диалог к сущностям CRM. Вроде бы все ничего, но делает он это по имени, которое может быть совсем не уникально. Соответственно диалог может прикрепиться не туда, куда вы можете ожидать. В тоже время он не ищет по номерам телефонов, email и значениям полей мессенджеров, что странно. Обычно внешняя система может прислать вам номер телефона, или id мессенджера, который зачастую является email. Намного больше смысла искать по ним.

Собственно сама привязка заключается в том, что Битрикс добавляет открытую линию в контакты сущности, и создает дела у этой сущности.

В нашем случае, было необходимо привязывать диалог к сделкe в CRM по номеру телефона. Для этого мы написали функцию, которая ищет номера телефонов в CRM сущностях (контактах, лидах, компаниях) и возвращает список сделок связанных с найденными сущностями, к которым нужно осуществить привязку. Тут важно учитывать то, что телефоны могут храниться в разных форматах, поэтому поиск нужно вести с помощью Bitrix\Crm\Integrity\DuplicateCommunicationCriterion

Здесь снова возникает проблема. Дело в том, что \Bitrix\ImConnector\CustomConnectors::sendMessages не возвращает данных о том, что за сообщение она создала, и самое главное – в какой чат. Она возвращает только id пользователя который написал сообщение. А не зная идентификатор чата – мы не сможем его привязать к CRM. Получить id чата можно через само сообщение. В \Bitrix\ImConnector\CustomConnectors::sendMessages мы передавали уникальный идентификатор из внешней системы. По нему можно получить информацию о сообщение, в том числе и чат:

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

Получить идентификатор сессии можно таким образом:

В функцию \CCrmActivity::Add нужно передать параметр ORIGIN_ID со значением вида  IMOL_#ид_сессии#


Наконец-то, мы создаем «Дело» на каждый новый чат и привязываем его к найденным сделкам.  В результате в карточке отображаются наши диалоги:

Снимок экрана 2018-09-12 в 15.22.03

Рис 4. Диалог в карточке CRM

Для добавления ссылки на чат в контакт или лид можно воспользоваться таким кодом:

Снимок экрана 2018-09-12 в 15.24.44

Рис 5. Ссылка на чат в карточке CRM

Далее, выясняется, что \Bitrix\ImConnector\CustomConnectors::sendMessages не запускает триггеры автоматизации. Запустить их принудительно можно с помощью Bitrix\ImOpenLines\Crm ->executeAutomationTrigger();

Дополнительно к вышеописанным проблемам на нашем сервере – не отрабатывали нотификации по новым сообщениям. Push-and-Pull был настроен верно, его события создавались, но не запускались «в фоне», а только по перезагрузке страницы. То есть нотификации не приходили не в мобильное приложение, не в десктопное, ни в браузер. Проблема решилась принудительным вызовом:

Теперь прием сообщений работает как надо. Можно переходить к передаче.

Для отправки сообщений из открытой линии во внешнюю систему нужно подписаться на событие OnSendMessageCustomHandler. В обработчик попадет информация о сообщении, которую вы передадите уже во внешнюю систему, например через curl.


Здесь также не обошлось без «особенностей». Мы столкнулись с такой проблемой: внешней системе, для передачи файлов (картинок, pdf) мессенджерам, требуются прямые ссылки на файл.  Битрикс, при отправке файла во внешнюю линию передает такой массив данных о файле:

Пример данных, приходящих в обработчик:

Как видно, Битрикс отдает ссылку. Но это не ссылка на файл, а ссылка на html страницу, с которой файл можно скачать. Естественно, внешняя система не может этого сделать. Поэтому нам нужна прямая ссылка на файл. Чтобы ее получить – нужно знать ид файла. Но этой информации у нас нет. Поэтому будем в очередной раз изобретать велосипед:

Имея короткую ссылку, можно найти информацию о полной ссылке на файл:

Полная ссылка на файл содержит в себе хэш, по которому можно найти файл:

То есть, помимо ид файла, нам еще нужен и токен загрузки. Формируется он следующим образом:

Так мы получим прямую ссылку на файл. Однако как видно – токен хранится в сессии

И доступ  к файлу по этой ссылке, скорее всего, будет только у пользователя, отправившего файл.   Такое решение  нам не подходит ,  нам нужно иметь возможность отдавать файл всегда, вне зависимости от сессии. Для этого можно  создать собственный  скрипт, который по короткой ссылке будет вычислять id файла, и отдавать его содержимое   с помощью:

Теперь мы можем отдавать файл без проверки токена (со всеми вытекающими предосторожностями). Это уже что-то, но все таки, хочется иметь возможность не только отправлять прямую ссылку на файл (всегда доступную), но и отправлять сам файл. И здесь мы сталкиваемся с неожиданной проблемой: Дело в том, что во время отработки события OnSendMessageCustom пересылаемый файл еще не занял своего положения на Битрикс.Диске, а до окончания выполнения обработчика функции OnSendMessageCustom существует как запись в бд вида:

Как видите размер у него равен нулю, content-type равен application/octet-stream. При попытке в обработчике найти файл /upload/disk/a8a/a8a71448e3092e5af419e9fb853d5ea5 с помощью Bitrix\Main\IO\File – сообщает что файл не существует. На самом деле – сразу после выполнения обработчика события OnSendMessageCustom – файл перемещается на Диск в другое хранилище (что-то вроде хранилища файлов открытых линий). И тогда становится нормальным, доступным. Но, это все происходит уже после окончания работы обработчика OnSendMessageCustom. Соответственно, внешняя система не сможет скачать файл пока мы находимся в обработчике.

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

Вот такие интересные особенности разработки кастомного коннектора мы открыли для себя, когда писали интеграцию коробочной версии Битрикс24 с WhatsApp.

Об авторе:

Сертифицированный Битрикс-разработчик. Ведущий программист Студии Юлии Бедросовой.

bedrosova3

Подпишитесь на рассылку!

Fields marked with an * are required

Комментарии

 

Комментировать

 

Подписаться на рассылку:

Fields marked with an * are required