Не так давно у одного из наших клиентов возникла необходимость переводить звонки (входящие и исходящие) по клику из браузера. Логика такова: группа операторов колл-центра принимает и осуществляет звонки, и после разговора и выяснения потребностей им нужно соединить клиента с одним из заказчиков или другим специалистом организации.

Казалось бы, оператор может перевести звонок классическим способом — “слепым” переводом на нужный номер со своего телефонного аппарата или софтфона, но на деле оказалось, что номеров, на которые требуется осуществлять перевод, более пяти сотен и они постоянно добавляются и меняются, так что даже централизованное автоматическое заполнение телефонной книги софтфона не дает возможности комфортной и быстрой работы операторов: сначала нужно выяснить потребность клиента, потом найти в 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 для них. При этом, скрипт одинаково работает как с входящими, так и с исходящими вызовами.

Подобная реализация будет полезна для аутсорс-колл-центров, справочных и иных служб, где требуется быстро переводить звонки на большое число номеров, а также может применяться для облегчения труда секретаря в крупной организации.
Поделиться с друзьями
-->

Комментарии (6)


  1. agic
    01.12.2016 10:10
    +1

    я просто напишу, а почему не ami?

    ACTION: Redirect
    Channel: SIP/x7065558529-8f54
    Context: default
    Exten: 5558530
    Priority: 1

    Или почему не ARI пример

    Мне одному кажется, что подобная реализация с вызовом bash скрипта, есть слегка костыль?

    Ведь гораздо правильные дать необходимой доступ арм или crm системе через ami, Rest. А далее уже делать нужное на логике.


    1. sfw
      01.12.2016 10:18

      Да, вы правы, можно сделать и на ami, логика отличаться не будет. На bash проще выделить нужный канал — 2 grep и все, а в случае с ami все будет чуть сложнее, и потребуется минимальное знание хотя бы php


    1. shadowalone
      01.12.2016 23:33

      Мало того что это костыль, так еще и костыль с костылём внутри:
      зачем 2 раза использовать

      asterisk -rx 'core show channels concise'
      

      если можно один раз, и результат держать в переменной.


    1. QDeathNick
      02.12.2016 08:51
      -1

      Наверное людям так противен php, что готовы к костылям на bash.

      А потом ни до кого не дозвонится, перевели звонок и черт с ним, что там никто не берет или вообще сорвалось, это уже не наша проблема.


      1. agic
        05.12.2016 11:08

        полностью с вами солидарен


  1. zzuz
    02.12.2016 08:51
    -1

    Ждем очередной наплыв «крутых астерисковедов» на форумы с уникальными темами «Я сделал по статье, но почему-то не работает».