Связка корпоративной телефонии Asterisk и учетной системы 1С:Предприятие — это классический инструмент повышения эффективности отдела продаж. При входящем звонке менеджер должен мгновенно видеть, кто ему звонит, чтобы обращаться к клиенту по имени и сразу иметь перед глазами историю сделок. Однако разработчики часто сталкиваются с проблемой: старая и некогда популярная бесплатная библиотека ROM-Asterisk.dll, работающая по технологии COM/ActiveX, безнадежно устарела. На современных платформах 1С 8.3.13, 8.3.14, 8.3.15 и новее она вызывает регулярные зависания сеансов, утечки памяти и аварийные завершения работы закрытого клиента.
Использовать постоянный опрос сервера Asterisk через HTTP-запросы (механизм AJAM) — тоже плохая идея, поскольку это создает колоссальную паразитную нагрузку на сеть и базы данных. В этой статье мы подробно разберем три современных, надежных и полностью бесплатных архитектурных пути интеграции Asterisk с 1С, которые не требуют платных лицензий и работают в режиме реального времени.
Проанализируем ситуацию и пошагово разберем каждое решение, чтобы вы могли выбрать наиболее подходящий вариант для вашей инфраструктуры.
---
Если вы хотите сохранить привычную логику работы через внешнюю компоненту, но избавиться от зависаний COM-технологий, лучшим выбором будет использование Native API. Сообщество разработчиков переписало старые библиотеки на современный стандарт 1С. В открытом доступе на GitHub вы можете найти бесплатные проекты, такие как BROM-Asterisk-Native или InnovaIT-Asterisk-Native.
Эти компоненты используют технологию Native API, благодаря чему они стабильно работают как в 32-битных, так и в 64-битных клиентах 1С, а также не требуют регистрации в реестре Windows. Компонента напрямую подключается к порту AMI Asterisk (обычно 5038), слушает TCP-сокет в фоновом потоке и при возникновении события генерирует стандартное внешнее событие в 1С.
Разберем по шагам, как настроить такую интеграцию в коде 1С:
Рассмотрим подробнее пример кода для инициализации компоненты в модуле управляемого приложения:
Процедура ПриНачалеРаботыСистемы()
// Подключаем компоненту из макета конфигурации
РезультатПодключения = ПодключитьВнешнююКомпоненту("ОбщийМакет.КомпонентаAsteriskNative", "AsteriskAddIn", ТипВнешнейКомпоненты.Native);
Если РезультатПодключения Тогда
Попытка
// Создаем объект компоненты
ОбъектAsterisk = Новый("AddIn.AsteriskAddIn.AMI");
// Задаем параметры подключения к Asterisk Manager Interface
ОбъектAsterisk.Хост = "192.168.1.100";
ОбъектAsterisk.Порт = 5038;
ОбъектAsterisk.ИмяПользователя = "manager1c";
ОбъектAsterisk.Пароль = "super_secret_password";
// Устанавливаем соединение в асинхронном режиме
ОбъектAsterisk.Подключиться();
Исключение
ЗаписатьЛогОшибок("Ошибка инициализации AMI Asterisk: " + ОписаниеОшибки());
КонецПопытки;
Иначе
ЗаписатьЛогОшибок("Не удалось загрузить внешнюю компоненту Asterisk Native API.");
КонецЕсли;
КонецПроцедуры
После успешного подключения компонента начинает слушать поток событий Asterisk. При поступлении входящего вызова на телефон сотрудника сработает предопределенная процедура ОбработкаВнешнегоСобытия в модуле управляемого приложения или в форме. Нам остается лишь перехватить ее и открыть форму нужного контрагента.
Посмотрим на пример обработки события:
Процедура ОбработкаВнешнегоСобытия(Источник, Событие, Данные)
// Проверяем, что событие пришло именно от нашей компоненты телефонии
Если Источник = "AsteriskAddIn" И Событие = "NewState" Тогда
// Переменная Данные обычно содержит строку с параметрами (например, в формате JSON или CSV)
// Предположим, компонента передает строку: "CallerID:79991112233;Extension:101;State:Ringing"
ПараметрыСобытия = РаспарситьДанныеСобытия(Данные);
Если ПараметрыСобытия.Получить("State") = "Ringing" Тогда
НомерТелефона = ПараметрыСобытия.Получить("CallerID");
ВнутреннийНомер = ПараметрыСобытия.Получить("Extension");
// Проверяем, назначен ли этот звонок текущему пользователю 1С
ТекущийПользователь = Пользователи.ТекущийПользователь();
Если ПолучитьВнутреннийНомерПользователя(ТекущийПользователь) = ВнутреннийНомер Тогда
// Находим контрагента в базе по номеру телефона
СсылкаНаКлиента = НайтиКонтрагентаПоТелефону(НомерТелефона);
Если ЗначениеЗаполнено(СсылкаНаКлиента) Тогда
// Открываем карточку найденного контрагента
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаКлиента);
ОткрытьФорму("Справочник.Контрагенты.ФормаОбъекта", ПараметрыФормы);
Иначе
// Если клиент не найден, открываем форму создания нового контрагента с заполненным телефоном
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("ТелефонКонтрагента", НомерТелефона);
ПараметрыОткрытия = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
ОткрытьФорму("Справочник.Контрагенты.ФормаОбъекта", ПараметрыОткрытия);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
---
Если в вашей компании используется современная клиент-серверная архитектура, вы можете полностью отказаться от использования DLL-файлов на компьютерах пользователей. Это особенно актуально, если сотрудники работают на разных операционных системах (Windows, macOS, Linux) или используют веб-клиент в браузере. Данное решение базируется на штатных механизмах платформы 1С — HTTP-сервисах и встроенной Системе взаимодействия (доступна начиная с версии платформы 8.3.10).
Давайте разберем по шагам, как работает эта элегантная и отказоустойчивая архитектурная схема:
curl или встроенная функция Asterisk curl.Рассмотрим, как настроить отправку запроса из плана набора Asterisk (файл extensions.conf):
; Настройка диалплана Asterisk при входящем звонке
[incoming-calls]
exten => _X.,1,NoOp(Входящий звонок от ${CALLERID(num)} на внутренний ${EXTEN})
; Вызываем HTTP-сервис 1С в фоновом режиме, передавая номер звонящего и получателя
exten => _X.,n,System(curl -s -X POST "http://1c-server/trade/hs/telephony/incoming" -d "caller=${CALLERID(num)}&called=${EXTEN}" &)
exten => _X.,n,Dial(SIP/${EXTEN},30)
exten => _X.,n,Hangup()
Теперь проанализируем реализацию HTTP-сервиса на стороне 1С. Создадим HTTP-сервис с именем telephony и шаблоном URL /incoming. Напишем обработчик метода POST:
Функция ШаблонЗапросаIncomingPOST(Запрос)
// Извлекаем тело POST-запроса
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
ПараметрыЗвонка = РаспарситьПараметрыЗапроса(ТелоЗапроса);
НомерЗвонящего = ПараметрыЗвонка.Получить("caller");
ВнутреннийНомер = ПараметрыЗвонка.Получить("called");
// Выполняем поиск пользователя 1С по его внутреннему номеру телефона
ЦелевойПользователь = НайтиПользователяПоНомерам(ВнутреннийНомер);
Если ЦелевойПользователь <> Неопределено Тогда
// Находим интеграционное обсуждение Системы взаимодействия для передачи события
ПараметрыСобытия = Новый Структура;
ПараметрыСобытия.Вставить("Событие", "ВходящийЗвонок");
ПараметрыСобытия.Вставить("НомерТелефона", НомерЗвонящего);
// Отправляем системное push-сообщение пользователю в 1С
СистемаВзаимодействия.ОтправитьСистемноеСообщениеПользователю(ЦелевойПользователь, ПараметрыСобытия);
КонецЕсли;
// Быстро возвращаем АТС ответ 200 OK, чтобы не задерживать диалплан
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки("OK");
Возврат Ответ;
КонецФункции
На стороне клиента в модуле управляемого приложения нам остается только подписаться на получение этих сообщений через Систему взаимодействия и открыть форму справочника. Этот метод абсолютно надежен, не нагружает рабочие станции и работает со скоростью света.
---
Начиная с версии платформы 8.3.8, в 1С появился мощный встроенный объект КлиентWebSocket. Благодаря этому нововведению отпала необходимость писать или искать сложные внешние компоненты, работающие сокетами. Мы можем подключить 1С напрямую к интерфейсу Asterisk REST Interface (ARI) по протоколу WebSockets.
При таком подходе клиентское приложение 1С при запуске открывает постоянное сетевое WebSocket-соединение с Asterisk. Сервер телефонии асинхронно отправляет JSON-пакеты событий прямо в сеанс 1С. Платформа парсит эти данные встроенными средствами и реагирует на звонки.
Рассмотрим по шагам, как запустить и использовать встроенный WebSocket-клиент в коде 1С:
Перем WSСоединение Экспорт;
Процедура ИнициализироватьТелефониюWebSocket() Экспорт
// Формируем адрес подключения к ARI Asterisk
// Замените хост, порт, имя приложения (app) и авторизационные данные на свои
АдресПодключения = "ws://192.168.1.100:8088/ari/events?api_key=ari_user:ari_password&app=1c_telephony";
WSСоединение = Новый КлиентWebSocket(АдресПодключения);
// Назначаем обработчики событий для асинхронной работы сокета
ДобавлениеОбработчика WSСоединение.ПриПолученииТекстовогоСообщения, ОбработкаСообщенияWS;
ДобавлениеОбработчика WSСоединение.ПриЗакрытии, ОбработкаЗакрытияWS;
ДобавлениеОбработчика WSСоединение.ПриОшибке, ОбработкаОшибкиWS;
Попытка
WSСоединение.ПодключитьАсинхронно();
Исключение
ЗаписатьЛогОшибок("Ошибка асинхронного подключения к WebSocket: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
При поступлении нового события от Asterisk сработает назначенный нами обработчик текстового сообщения. Платформа 1С получит структурированный JSON-пакет. Нам необходимо распарсить его с помощью объекта ЧтениеJSON и вытащить данные о входящем вызове:
Процедура ОбработкаСообщенияWS(Соединение, ТекстСообщения)
// Читаем входящий JSON-пакет от ARI
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстСообщения);
ДанныеСобытия = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
// Анализируем тип события ARI (например, начало отслеживания звонка в приложении Stasis)
Если ДанныеСобытия.Свойство("type") И ДанныеСобытия.type = "StasisStart" Тогда
// Извлекаем детальную информацию о канале звонка
ДанныеКанала = ДанныеСобытия.channel;
НомерЗвонящего = ДанныеКанала.caller.number;
// Выполняем открытие карточки
СсылкаНаКонтрагента = НайтиКонтрагентаПоТелефону(НомерЗвонящего);
Если ЗначениеЗаполнено(СсылкаНаКонтрагента) Тогда
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаКонтрагента);
ОткрытьФорму("Справочник.Контрагенты.ФормаОбъекта", ПараметрыФормы);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Использование встроенного КлиентWebSocket позволяет разработчику полностью контролировать логику взаимодействия с телефонией на уровне платформенного кода 1С, обеспечивая максимальную гибкость и кроссплатформенность.
---
Мы подробно проанализировали три отличных архитектурных подхода. Чтобы сделать правильный выбор, ориентируйтесь на следующие критерии:
ROM-Asterisk без переписывания архитектуры конфигурации.