Интеграция учетной системы 1С с IP-телефонией Asterisk через протокол AMI (Asterisk Manager Interface) позволяет фиксировать телефонные переговоры, открывать карточки клиентов при входящем звонке и совершать звонки в один клик. Однако при разборе «сырого» потока AMI-пакетов разработчики часто сталкиваются с трудностями: в самом ядре Asterisk нет жесткого понятия «входящий» или «исходящий» звонок — существуют только каналы связи, соединяемые мостами. Разберем по шагам, как корректно определять направление вызова, связывать события в единую цепочку и правильно формировать команду Originate для совершения звонков прямо из 1С.
Рассмотрим, почему нельзя опираться на простое предположение о том, что первый канал в событии соединения всегда принадлежит клиенту. При звонках через АТС коммутация может происходить в различных направлениях, сопровождаться переводами звонков, вызовом групп и перехватами. Выясним, какие подходы позволяют надежно классифицировать вызов на стороне 1С.
incall или from-trunk, а исходящие от сотрудников — в office или from-internal). Анализируя поле Context в событии создания канала, можно однозначно зафиксировать тип звонка.Bridge) мы сопоставляем CallerID1 и CallerID2 с номером текущего пользователя.Проанализируем первый этап жизненного цикла вызова. Событие Newchannel возникает в момент создания канала связи. Если в диалплане АТС контексты четко разделены, зафиксировать направление звонка в таблице значений или регистре сведений можно уже на этом этапе.
Посмотрим на пример обработки пакета Newchannel в кодовой базе 1С:
Если Событие = "Newchannel" Тогда
НовЗапись = ТЗСобытий.Добавить();
НовЗапись.Uniqueid = СтруктураПолей.Получить("Uniqueid");
// Проверяем контекст обработки вызова в диалплане
Если СтруктураПолей.Получить("Context") = "incall" Тогда
НовЗапись.Телефон = "8" + СтруктураПолей.Получить("CallerIDNum");
НовЗапись.ВидЗвонка = Перечисления.ВидЗвонка.Входящий;
Иначе
НовЗапись.Телефон = СтруктураПолей.Получить("Exten");
НовЗапись.ВидЗвонка = Перечисления.ВидЗвонка.Исходящий;
КонецЕсли;
КонецЕсли;
Рассмотрим ситуацию, когда контексты диалплана могут меняться или являются общими. В этом случае точкой фиксации разговора становится событие 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 генерируется несколько раз подряд.
Для надежного отслеживания звонка применим следующую цепочку событий:
Hangup без события моста, звонок считается пропущенным (недозвон).Uniqueid и Linkedid.CallerIDNum или Uniqueid основного канала), игнорируя промежуточные служебные сбросы.Выясним причину частой проблемы при организации исходящего звонка по клику из 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;
АстерискВыполнитьКоманду(ТекстКоманды);
Разберем критически важные параметры команды:
CRLF) для завершения блока команды. Без этого сервер Asterisk не начнет выполнение.office или from-internal), и этот контекст в диалплане должен иметь право совершать вызовы наружу через транки. Если указать несуществующий или закрытый контекст, после поднятия трубки произойдет мгновенный обрыв.104). Использование стрелок и сложных строк вроде 104->89144004946 может нарушить парсинг заголовков на софтфоне или оборудовании провайдера.30000). Если параметр не задан, вызов может сброситься до того, как менеджер успеет среагировать.Originate в первом событии Newchannel поле Exten всегда будет пустым. Это штатное поведение: сначала генерируется канал до софтфона оператора. Целевой номер клиента начнет фигурировать в диалплане только после того, как оператор снимет трубку и Asterisk приступит к маршрутизации второй части звонка. Ориентироваться для сквозного отслеживания вызова в этом случае необходимо на идентификатор Uniqueid.