Как в 1С определить входящий или исходящий звонок при интеграции с Asterisk через AMI и настроить команду Originate?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Интеграция учетной системы 1С с IP-телефонией Asterisk через протокол AMI (Asterisk Manager Interface) позволяет фиксировать телефонные переговоры, открывать карточки клиентов при входящем звонке и совершать звонки в один клик. Однако при разборе «сырого» потока AMI-пакетов разработчики часто сталкиваются с трудностями: в самом ядре Asterisk нет жесткого понятия «входящий» или «исходящий» звонок — существуют только каналы связи, соединяемые мостами. Разберем по шагам, как корректно определять направление вызова, связывать события в единую цепочку и правильно формировать команду Originate для совершения звонков прямо из 1С.

Архитектурная логика вызовов и определение направления звонка

Рассмотрим, почему нельзя опираться на простое предположение о том, что первый канал в событии соединения всегда принадлежит клиенту. При звонках через АТС коммутация может происходить в различных направлениях, сопровождаться переводами звонков, вызовом групп и перехватами. Выясним, какие подходы позволяют надежно классифицировать вызов на стороне 1С.

  1. Анализ контекстов диалплана (Context): Наиболее надежный способ. В настройках Asterisk входящие и исходящие маршруты разносятся по разным контекстам (например, входящие попадают в контекст с префиксом incall или from-trunk, а исходящие от сотрудников — в office или from-internal). Анализируя поле Context в событии создания канала, можно однозначно зафиксировать тип звонка.
  2. Сравнение номеров с внутренним номером сотрудника: В базе 1С для каждого пользователя фиксируется его короткий внутренний номер. При анализе события соединения моста (Bridge) мы сопоставляем CallerID1 и CallerID2 с номером текущего пользователя.
  3. Анализ длины номера: Если номер источника длинный (городской/мобильный), а назначение — короткий внутренний номер, звонок классифицируется как входящий. Если наоборот — как исходящий. Однако этот метод дает сбои при внутренних звонках между сотрудниками.

Обработка события Newchannel в 1С

Проанализируем первый этап жизненного цикла вызова. Событие Newchannel возникает в момент создания канала связи. Если в диалплане АТС контексты четко разделены, зафиксировать направление звонка в таблице значений или регистре сведений можно уже на этом этапе.

Посмотрим на пример обработки пакета Newchannel в кодовой базе 1С:


Если Событие = "Newchannel" Тогда
    НовЗапись = ТЗСобытий.Добавить();
    НовЗапись.Uniqueid = СтруктураПолей.Получить("Uniqueid");
    
    // Проверяем контекст обработки вызова в диалплане
    Если СтруктураПолей.Получить("Context") = "incall" Тогда 
        НовЗапись.Телефон = "8" + СтруктураПолей.Получить("CallerIDNum");
        НовЗапись.ВидЗвонка = Перечисления.ВидЗвонка.Входящий;
    Иначе
        НовЗапись.Телефон = СтруктураПолей.Получить("Exten");
        НовЗапись.ВидЗвонка = Перечисления.ВидЗвонка.Исходящий;
    КонецЕсли;
КонецЕсли;

Определение направления звонка по событию коммутации Bridge

Рассмотрим ситуацию, когда контексты диалплана могут меняться или являются общими. В этом случае точкой фиксации разговора становится событие Bridge (или BridgeEnter в современных версиях Asterisk). Это событие информирует о том, что два абонента сняли трубки и начался разговор.

Разберем пример алгоритма, который сопоставляет участников моста с внутренним номером сотрудника, закрепленным в 1С:


ИначеЕсли стрСобытие = "Bridge" Тогда
    State = сзПакет.Получить("Bridgestate");
    CallerID1 = сзПакет.Получить("CallerID1");
    CallerID2 = сзПакет.Получить("CallerID2");
    ИД = сзПакет.Получить("Uniqueid1");
    Channel1 = сзПакет.Получить("Channel1");
    Channel2 = сзПакет.Получить("Channel2");
    
    // Проверяем состояние соединения
    Если (State = "Link") И (CallerID2 = МойТелефон) Тогда 
        // Входящий звонок: сотрудник является вторым участником связки
        Если (идСоединение <> ИД) ИЛИ (стрСостояние = "") Тогда
            Channel = Channel2;
            идСоединение = ИД;
            стрСостояние = сост_Входящий;
            НомерКлиента = CallerID1;
            НомерСотрудника = МойТелефон;
        КонецЕсли;
        
    ИначеЕсли (State = "Link") И ((CallerID1 = МойТелефон) ИЛИ (Channel1 = Channel)) Тогда 
        // Исходящий звонок: сотрудник инициировал соединение
        Если (идСоединение <> ИД) ИЛИ (стрСостояние = "") Тогда
            Channel = Channel1;
            идСоединение = ИД;
            стрСостояние = сост_Исходящий;
            НомерСотрудника = МойТелефон;
            НомерКлиента = CallerID2;
        КонецЕсли;
    КонецЕсли;

Как отследить завершение звонка и избежать преждевременной очистки данных

Частая ошибка при разработке интеграции — удаление связки по первому же событию Hangup. Если вызов идет одновременно на группу номеров или завершается один из служебных каналов перевода, событие Hangup генерируется несколько раз подряд.

Для надежного отслеживания звонка применим следующую цепочку событий:

  1. DialBegin: фиксация попытки дозвона. Если после него сразу следует Hangup без события моста, звонок считается пропущенным (недозвон).
  2. Bridge / BridgeEnter: фактический ответ и начало разговора. Именно здесь фиксируются идентификаторы Uniqueid и Linkedid.
  3. BridgeLeave: окончание разговора абонентов.
  4. Hangup: закрытие канала. Завершать обработку звонка в 1С следует только по событию канала самого сотрудника (по его CallerIDNum или Uniqueid основного канала), игнорируя промежуточные служебные сбросы.

Инициация исходящего звонка из 1С через действие Originate

Выясним причину частой проблемы при организации исходящего звонка по клику из 1С: софтфон сотрудника звонит, сотрудник поднимает трубку, но дальше вызов на номер клиента не идет, а в трубке слышна тишина.

Посмотрим на структуру команды, отправляемой через интерфейс AMI:


ТекстКоманды = "Action: Originate" + Символы.CRLF + 
    "Channel: SIP/" + СокрЛП(ВнутреннийНомер) + Символы.CRLF + 
    "Context: office" + Символы.CRLF + 
    "Exten: " + СокрЛП(НомерКлиента) + Символы.CRLF +
    "Priority: 1" + Символы.CRLF +
    "Callerid: " + СокрЛП(ВнутреннийНомер) + Символы.CRLF + 
    "Timeout: 30000" + Символы.CRLF + 
    "Async: true" + Символы.CRLF + Символы.CRLF;

АстерискВыполнитьКоманду(ТекстКоманды);

Разберем критически важные параметры команды:

  1. Обязательный двойной перенос строк в конце: Протокол AMI требует пустую строку (дважды CRLF) для завершения блока команды. Без этого сервер Asterisk не начнет выполнение.
  2. Правильный Context: Контекст должен точно совпадать с тем, в котором зарегистрирован внутренний пир сотрудника (например, office или from-internal), и этот контекст в диалплане должен иметь право совершать вызовы наружу через транки. Если указать несуществующий или закрытый контекст, после поднятия трубки произойдет мгновенный обрыв.
  3. Параметр Callerid: В нем необходимо передавать простой короткий номер сотрудника (например, 104). Использование стрелок и сложных строк вроде 104->89144004946 может нарушить парсинг заголовков на софтфоне или оборудовании провайдера.
  4. Timeout: Время ожидания ответа сотрудника в миллисекундах (например, 30000). Если параметр не задан, вызов может сброситься до того, как менеджер успеет среагировать.
  5. Пустой Exten при событии Newchannel: При выполнении Originate в первом событии Newchannel поле Exten всегда будет пустым. Это штатное поведение: сначала генерируется канал до софтфона оператора. Целевой номер клиента начнет фигурировать в диалплане только после того, как оператор снимет трубку и Asterisk приступит к маршрутизации второй части звонка. Ориентироваться для сквозного отслеживания вызова в этом случае необходимо на идентификатор Uniqueid.
← На главную