Не так давно у одного из наших клиентов возникла необходимость переводить звонки (входящие и исходящие) по клику из браузера. Логика такова: группа операторов колл-центра принимает и осуществляет звонки, и после разговора и выяснения потребностей им нужно соединить клиента с одним из заказчиков или другим специалистом организации.
Казалось бы, оператор может перевести звонок классическим способом — “слепым” переводом на нужный номер со своего телефонного аппарата или софтфона, но на деле оказалось, что номеров, на которые требуется осуществлять перевод, более пяти сотен и они постоянно добавляются и меняются, так что даже централизованное автоматическое заполнение телефонной книги софтфона не дает возможности комфортной и быстрой работы операторов: сначала нужно выяснить потребность клиента, потом найти в CRM номер, с которым его требуется соединить, затем или вручную ввести номер, или найти тот же номер в телефонной книге софтфона. Это приводит к нерациональному использованию рабочего времени операторов, высокой вероятности ошибки при переводе и снижению качества обслуживания клиентов в целом.
В некоторых случаях подобную задачу решают следующим образом: по событию в CRM в некую базу данных заносится номер оператора, и номер, с которым нужно соединить его собеседника. После этого оператор осуществляет перевод звонка на служебный номер, в контексте перевода осуществляется запрос к базе данных и звонок наконец переводится на нужный номер. Диалплан для исходящих в таком случае выглядит примерно так:
[operator-out]
exten => _X.,1,Noop(Outgoing call from ${CALLERID(num)} to ${EXTEN})
exten => _X.,n,Set(__TRANSFER_CONTEXT=transfer)
exten => _X.,n,Dial(SIP/provider/${EXTEN},30,T)
[transfer]
exten => 800,1,Set(num=ODBC_GETXFERNUM(${CALLERID(num)}
exten => 800,n,Noop(Transfer to ${num})
exten => 800,n,Goto(some-context,${num},1)
При исходящем звонке устанавливается переменная канала TRANSFER_CONTEXT, которая переопределяет контекст для перевода звонков, и если звонок будет переведен на номер 800, используя ODBC, мы получаем номер, на который нужно на самом деле осуществить перевод. Однако, до самого перевода в соответствующие поля таблицы должно быть занесено однозначное соответствие внутреннего номера оператора и номера, на который именно сейчас нужно переводить звонок, что требует как минимум одного лишнего действия со стороны работника (в CRM ему требуется выбрать номер, куда он хочет перевести звонок, а потом на телефоне или софтфоне выполнить сам перевод), да еще и нужно следить за тем, чтобы данные в таблице были верны: удалять запись сразу после перевода, проверять и обрабатывать в диалплане пустые записи.
При всех недостатках подобный метод можно применять в том случае, если заранее известно, на какой номер может потребоваться перевести звонок. Предположим, колл-центр работает на исходящие звонки по клиентам нескольких заказчиков. Звонок формируется автоматически из CRM(с использованием callfile или через originate в AMI), где оператор нажимает кнопку “следующий звонок” и видит скрипт разговора с клиентом. Тогда можно просто добавить наследуемую переменную с номером, и переводить звонок на него:
В callfile добавить наследуемую переменную __num:
Channel: Local/101@from-internal
Callerid: 74950000000
MaxRetries: 2
RetryTime: 600
WaitTime: 30
Context: from-internal
Extension: 74950000000
Priority: 1
Set: __num=79991112233
А контекст перевода будет выглядеть еще проще:
exten => 800,1,Noop(Transfer to ${num})
exten => 800,n,Goto(some-context,${num},1)
Однако, в нашем случае, как было сказано выше, такой метод применять нежелательно.
К счастью, разработчики asterisk позаботились о нас, и через cli можно осуществить перевод определенного канала в заданный контекст:
channel redirect <[[context,]exten,]priority>
Аналогичный функционал есть и в AMI:
Action: Redirect
[ActionID:] value
Channel: value
[ExtraChannel:] value
Exten: value
[ExtraExten:] value
Context: value
[ExtraContext:] value
Priority: value
[ExtraPriority:] value>
Остался только один вопрос: как определить канал, который нам нужно перевести, ведь мы знаем только номер оператора.
Нужно понимать, что обычный разговор есть соединение двух каналов. В консоли мы можем выполнить bridge show all и увидеть все идентификаторы соединений.
> bridge show all
Bridge-ID Chans Type Technology
14418b64-0635-46e7-bd48-f4b820461eaa 2 basic simple_bridge
А командой bridge show <bridge-id> посмотреть информацию о том, какие именно каналы участвуют в соединении.
Понятно, что перебирать все bridge-id в поисках нужного канала неудобно, и проще получить подробную информацию о каналах и найти в выдаче bridge-id, на основании которого можно будет идентифицировать нужный нам канал:
> core show channels concise
SIP/RT-00000453!incoming!!1!Up!AppDial!(Outgoing Line)!89063448810!!!3!134!14418b64-0635-46e7-bd48-f4b820461eaa!1479984681.1659
SIP/67-00000452!macro-dialout-trunk!s!23!Up!Dial!SIP/RT/89063448810,300,TtL(7200000)!67!!!3!134!14418b64-0635-46e7-bd48-f4b820461eaa!1479984681.1658
Мы видим, что на текущий момент у нас есть 2 канала с одинаковым bridge-id(14418b64-0635-46e7-bd48-f4b820461eaa), и мы знаем внутренний номер оператора. Остается только получить имя второго канала и осуществить перевод. Рассмотрим пример bash-скрипта, который реализует нужный функционал (протестировано на FreePBX 13):
#!/bin/bash
context="from-internal" # задаем контекст, в который будем переводить вызов
exten=$1 # получаем номер оператора
num=$2 # получаем внешний или внутренний номер, на который осуществим перевод
bridge=$(asterisk -rx 'core show channels concise' | grep SIP/$exten- | cut -d '!' -f13 ) # получаем bridge-id для канала название которого имеет вид SIP/xx-aaaaa
channel=$(asterisk -rx 'core show channels concise' | grep $bridge | grep -v SIP/$exten- | cut -d '!' -f1) # получаем channel соединенного с ним канала вида SIP/provider-000000
result=$(asterisk -rx "channel redirect $channel $context,$num,1") # переводим полученный канал в нужный контекст
echo $result
exit 0
Скрипт вызывается с двумя параметрами - номер оператора и номер, на который требуется перевести звонок. Остается только “научить” CRM его корректно вызывать.
Что же мы получили в итоге? Возможность перевода звонка оператора по одному клику без необходимости использования баз данных и дополнительных действий со стороны работника, не используя готовые CRM и модули для работы с asterisk для них. При этом, скрипт одинаково работает как с входящими, так и с исходящими вызовами.
Подобная реализация будет полезна для аутсорс-колл-центров, справочных и иных служб, где требуется быстро переводить звонки на большое число номеров, а также может применяться для облегчения труда секретаря в крупной организации.