Новости
Обзоры и тесты
Техно
Советы
Разное
Главная » Техно

Настройка уведомления о записи разговора для исходящих и входящих вызовов в Asterisk

Добавлено на 11.04.2017 – 10:03

В прошлой статье мы перевели соответствия внешних и внутренних номеров на АТС Asterisk в базу данных MySQL. В данном материале мы рассмотрим пример добавления уведомления о записи разговоров для входящей и исходящей связи. Причём уведомление может быть любым, ведь по сути мы добавляем воспроизведение произвольного аудиофайла перед соединением с клиентом. Это очень востребованная функция, так как если разговор записывается, то клиент, по закону, должен быть уведомлён об этом.
Итак, сценарий работы у нас будет следующий – для исходящей связи:
В базе данных мы проставляем для каждого внутреннего номера признак проигрывать или не проигрывать уведомление о записи разговора. При исходящем вызове в базу данных передаётся внутренний номер абонента станции (в примере ниже 650), который совершает исходящий вызов, в ответ возвращается значение которое определят проигрывать или не проигрывать аудиофайл вызываемому абоненту.
Для входящей связи – при входящем вызове на городской номер АТС, вызываемый городской номер (в примере 78121234567) передаётся в базу данных, откуда возвращается значение, на основании которого АТС определяет проигрывать или не проигрывать аудиофайл уведомления о записи разговора.
Причём, мы рассмотрим два случая: первый – проигрывание уведомления о записи разговора после ответа АТС – состояния “сonnected” и старта тарификации вызова, второй – проигрывание уведомления о записи разговора до ответа АТС и состояния “connected”, соответственно до тарификации. Таким образом, абонент не будет платить за вызов если абонент АТС не ответил.
Теперь рассмотрим техническую реализацию описанных выше сценариев.

Исходящая связь

Чтобы проиграть аудиофайл /var/lib/asterisk/sounds/ivr/razgovor_record в приложении Dial используем параметр A(имя_аудиофайла). После соединения указанный в скобках аудифайл будет проигран.
Но нам необходимо управлять через базу данных, для исходящих вызовов, с каких внутренних номеров аудиофайл уведомления о записи разговора нужно проигрывать, а для каких не нужно.
Для этого добавим новый столбец в существующей таблице outbound.
Добавим этот новый столбец после столбца callerid с именем notification_enabled, значения в столбце могут принимать следующие значения:
1 – Проиграть аудиофайл уведомления
0 – Не проигрывать аудиофайл уведомления
Добавим столбец в notification_enabled таблицу outbound, далее приведён пример запроса в СУБД MySQL.

mysql> use astcdrdb;
mysql> ALTER TABLE `outbound` ADD `notification_enabled` INT( 1 ) NOT NULL DEFAULT '0' AFTER `callerid`;

Теперь таблица outbound будет выглядеть следующим образом:

mysql> select * from outbound where internal=650;
+-----+----------+-------------+----------------------+-------+
| id | internal | callerid | notification_enabled | Notes |
+-----+----------+-------------+----------------------+-------+
| 210 | 650 | 78121234567 | 0 | |
+-----+----------+-------------+----------------------+-------+
1 row in set (0.00 sec)

Добавим функцию, которая будет проверять значение столбца notification_enabled в файл func_odbc.conf.

[CHECK_NOTIF_OUT_ENABLE]
dsn=asterisk
readsql=SELECT notification_enabled FROM outbound where internal='${ARG1}'

В консоли Asterisk применим функцию:

aster*CLI> module reload func_odbc.so

В выводе консоли Asterisk увидим:

  == Registered custom function 'ODBC_CHECK_NOTIF_OUT_ENABLE'

Теперь модифицируем дайлплан, файл extensions.conf.
Чтобы проиграть аудиофайл /var/lib/asterisk/sounds/ivr/razgovor_record в приложении Dial используем параметр A(имя_аудиофайла). После соединение указанный в параметре аудиофайл будет проигран.
Начнём с контекста [mg-out]. Исходный вид:

exten => _9[78][123456780]XXXXXXXXX,2,Set(__CALLER=${CALLERID(ANI)})
exten => _9[78][123456780]XXXXXXXXX,3,Set(__CALLED=7${EXTEN:2})
exten => _9[78][123456780]XXXXXXXXX,4,Set(__UID=${UNIQUEID})
exten => _9[78][123456780]XXXXXXXXX,5,Dial(SIP/RTU/7${EXTEN:2},,tTM(startrec))

Модифицируем контекст, новые строки выделены жирным текстом:

exten => _9[78][123456780]XXXXXXXXX,2,Set(__CALLER=${CALLERID(ANI)})
exten => _9[78][123456780]XXXXXXXXX,3,Set(__CALLED=7${EXTEN:2})
exten => _9[78][123456780]XXXXXXXXX,4,Set(__UID=${UNIQUEID})
exten => _9[78][123456780]XXXXXXXXX,5,NoOp(${ODBC_CHECK_NOTIF_OUT_ENABLE(${CALLER})})
exten => _9[78][123456780]XXXXXXXXX,6,Set(NOTIFICATION=${IF($["${ODBC_CHECK_NOTIF_OUT_ENABLE(${CALLER})}" = "1"]?/var/lib/asterisk/sounds/ivr/razgovor_record)})
exten => _9[78][123456780]XXXXXXXXX,7,Dial(SIP/RTU/7${EXTEN:2},,tTM(startrec)A(${NOTIFICATION}))

Далее, перечитываем дайлплан командой из консоли Asterisk: dialplan reload.

При вызове, если notification_enabled=0, отладочный вывод в консоли Asterisk будет следующим:

   -- Executing [981116275136@mn:1] Set("SIP/650-00003bb2", "CALLERID(num)=78121234567") in new stack
    -- Executing [981116275136@mn:2] Set("SIP/650-00003bb2", "__CALLER=650") in new stack
    -- Executing [981116275136@mn:3] Set("SIP/650-00003bb2", "__CALLED=71116275136") in new stack
    -- Executing [981116275136@mn:4] Set("SIP/650-00003bb2", "__UID=1490019065.32064") in new stack
    -- Executing [981116275136@mn:5] NoOp("SIP/650-00003bb2", "0") in new stack
    -- Executing [981116275136@mn:6] Set("SIP/650-00003bb2", "NOTIFICATION=") in new stack
    -- Executing [981116275136@mn:7] Dial("SIP/650-00003bb2", "SIP/RTU/71116275136,,tTM(startrec)A()") in new stack

При вызове, если notification_enabled=1:

  == Using SIP RTP CoS mark 5
    -- Executing [981116275136@mn:1] Set("SIP/650-00003b70", "CALLERID(num)=78121234567") in new stack
    -- Executing [981116275136@mn:2] Set("SIP/650-00003b70", "__CALLER=650") in new stack
    -- Executing [981116275136@mn:3] Set("SIP/650-00003b70", "__CALLED=71116275136") in new stack
    -- Executing [981116275136@mn:4] Set("SIP/650-00003b70", "__UID=1490018964.31913") in new stack
    -- Executing [981116275136@mn:5] NoOp("SIP/650-00003b70", "1") in new stack
    -- Executing [981116275136@mn:6] Set("SIP/650-00003b70", "NOTIFICATION=/var/lib/asterisk/sounds/ivr/razgovor_record") in new stack
    -- Executing [981116275136@mn:7] Dial("SIP/650-00003b70", "SIP/RTU/71116275136,,tTM(startrec)A(/var/lib/asterisk/sounds/ivr/razgovor_record)") in new stack

Исходящая связь

В предыдущем материале посвященном Asterisk мы переводили входящую связь через БД, MySQL и запрашивали параметры входящего вызова из БД, существующие строки в дайлплане:

same => n,NoOp(GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after ==  ${ODBC_GET_FORWARD_AFTER_HG(${EXTEN})})
same => n,Set(ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER)=${ODBC_GET_FORWARD_AFTER_HG(${EXTEN})})

В данные строки нужно добавить получение ещё одного параметра notification_enabled из таблицы inbound, который будет указывать на то, нужно ли проигрывать уведомление или нет.
Добавим новый столбец запросом в mysql:

mysql> use astcdrdb;
mysql> ALTER TABLE `inbound` ADD `notification_enabled` INT( 1 ) NULL DEFAULT '0' AFTER `timeout_ring_after`

Модифицируем функцию в ODBC_GET_FORWARD_AFTER_HG в файле func_odbc.conf, то что добавлено выделено жирным шрифтом:

[GET_FORWARD_AFTER_HG]
dsn=asterisk
readsql=SELECT ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after, notification_enabled from inbound where did='${ARG1}'

Затем строки в extensions.conf:

same => n,NoOp(GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after, notification_enabled ==  ${ODBC_GET_FORWARD_AFTER_HG(${EXTEN})})
same => n,Set(ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER,NOTIFICATION_ENABLED)=${ODBC_GET_FORWARD_AFTER_HG(${EXTEN})})

Перечитаем файл с функциями odbc:

aster*CLI> module reload func_odbc.so
Module 'func_odbc.so' reloaded successfully.
    -- Reloading module 'func_odbc.so' (ODBC lookups)
  == Parsing '/etc/asterisk/func_odbc.conf': Found
…
  == Registered custom function 'ODBC_GET_FORWARD_AFTER_HG'
…

Далее, переходим в контекст [HG-NEW]. Закомментируем первую строку и добавляю NoOp, также две строки выделенные жирным цветом. Здесь проверяется разрешено ли уведомление – значение NOTIFICATION_ENABLED равно 1. Если 0, то переменная NOTIFICATION принимает значение skip, в опции приложения Playback из переменой NOTIFICATION добавляется данный ключ, который указывает что если канал не в ответном состоянии, то приложение Playback не будет проигрывать указанный файл /var/lib/asterisk/sounds/ivr/razgovor_record. Первой строкой мы закомментировали приложение Answer, которое переводит канал в ответное состояние Connected в самом начале контекста. Поэтому эта комбинация будет работать.

Получится вот так:

;exten => _X.,1, Answer
exten => _X.,1,NoOp
same => n,Set(_MONITOR_FILENAME=IN-${EXTEN}-${STRFTIME(${EPOCH},,%Y_%m_%d[%H_%M])}-${CALLERID(num)}-${EXTEN}-${UNIQUEID})
same => n, NoOp(!!!!CALLED=${CALLED})
same => n, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})})
same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})})
same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})})
same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})})
same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})})
same => n, Set(H6=${IF($["${m6}" != ""]?&SIP/${m6})})
same => n, Set(H7=${IF($["${m7}" != ""]?&SIP/${m7})})
same => n, Set(H8=${IF($["${m8}" != ""]?&SIP/${m8})})
same => n, Set(H9=${IF($["${m9}" != ""]?&SIP/${m9})})
same => n, Set(H11=${IF($["${m11}" != ""]?&SIP/${m11})})
same => n, Set(H12=${IF($["${m12}" != ""]?&SIP/${m12})})
same => n, Set(H13=${IF($["${m13}" != ""]?&SIP/${m13})})
same => n, Set(H14=${IF($["${m14}" != ""]?&SIP/${m14})})
same => n, Set(H15=${IF($["${m15}" != ""]?&SIP/${m15})})

same => n,Set(NOTIFICATION=${IF($["${NOTIFICATION_ENABLED}" = "1"]?:skip)})
same => n,Playback(/var/lib/asterisk/sounds/ivr/razgovor_record,${NOTIFICATION})

same => n,Dial(${H1}${H2}${H3}${H4}${H5}${H6}${H7}${H8}${H9}${H10}${H11}${H12}${H13}${H14}${H15},${RING_TIMEOUT},TtrM(monext))
...

Если переменная NOTIFICATION принимает значение пусто(“”), то Playback посылает 200OK с SDP (Connect) , переводит канал в ответное состояние и проигрывает файл /var/lib/asterisk/sounds/ivr/razgovor_record.
Таким образом аудиофайл будет воспроизводиться вне зависимости поддерживает ли операторское оборудование early media.
Ниже SIP-трейс обмена сообщениями:

Capturing on eth0
  2.329733 192.168.103.45 -> 192.168.126.210 SIP/SDP 1235 Request: INVITE sip:78121234567@192.168.126.210;user=phone, with session description
  2.332353 192.168.126.210 -> 192.168.103.45 SIP 631 Status: 100 Trying
  2.365530 192.168.126.210 -> 192.168.103.45 SIP/SDP 913 Status: 200 OK, with session description
  2.368216 192.168.103.45 -> 192.168.126.210 SIP 502 Request: ACK sip:78121234567@192.168.126.210:5060
<проигрывается уведомление, состояние ответа, тарификация вызова началась>
<Далее вызов на внутреннего абонента>
  6.840698 192.168.126.210 -> 192.168.85.148 SIP/SDP 955 Request: INVITE sip:350@192.168.85.148:64141;rinstance=6372aa4716dea7bf, with session description
  6.961023 192.168.85.148 -> 192.168.126.210 SIP 462 Status: 180 Ringing
 10.469290 192.168.85.148 -> 192.168.126.210 SIP/SDP 841 Status: 200 OK, with session description
 10.469761 192.168.126.210 -> 192.168.85.148 SIP 505 Request: ACK sip:350@192.168.85.148:64141;rinstance=6372aa4716dea7bf
 11.479193 192.168.85.148 -> 192.168.126.210 SIP 578 Request: BYE sip:78121234567@192.168.126.210:5060
 11.480282 192.168.126.210 -> 192.168.85.148 SIP 563 Status: 200 OK
 11.481937 192.168.126.210 -> 192.168.103.45 SIP 574 Request: BYE sip:78121234567@192.168.103.45:5063;user=phone
 11.484585 192.168.103.45 -> 192.168.126.210 SIP 503 Status: 200 OK

Если встречный оператор поддерживает режим early media или отправку вызывающей стороне аудио в голосовом канале, то cтроку с приложением Playback можно модифицировать вот так:

same => n,Set(NOTIFICATION=${IF($["${NOTIFICATION_ENABLED}" = "1"]?noanswer:skip)})

То есть, добавить ключ noanswer, вместо “”(пусто), тогда Playback будет проигрывать файл в предответном состоянии, то есть до состояния Connect:

same => n,Playback(/var/lib/asterisk/sounds/razgovor_zapis1,${NOTIFICATION})

В sip.conf нужно
Выставить в общих настройках(секция [general]) параметр

prematuremedia=no

SIP-трейсировка будет следующий:

0.000000 192.168.103.45 -> 192.168.126.210 SIP/SDP 1235 Request: INVITE sip:78121234567@192.168.126.210;user=phone, with session description
  0.000815 192.168.126.210 -> 192.168.103.45 SIP 631 Status: 100 Trying
  0.031701 192.168.126.210 -> 192.168.103.45 SIP/SDP 929 Status: 183 Session Progress, with session description
  0.031775 192.168.126.210 -> 192.168.103.45 RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6439ADF6, Seq=43650, Time=160, Mark
  0.032872 192.168.103.45 -> 192.168.126.210 RTCP 122 Sender Report   Source description
  0.032922 192.168.103.45 -> 192.168.126.210 SIP 640 Request: OPTIONS sip:78121234567@192.168.126.210;user=phone
  0.033428 192.168.126.210 -> 192.168.103.45 SIP 668 Status: 200 OK
  0.051935 192.168.126.210 -> 192.168.103.45 RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6439ADF6, Seq=43651, Time=320
  0.071893 192.168.126.210 -> 192.168.103.45 RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6439ADF6, Seq=43652, Time=480
  0.091958 192.168.126.210 -> 192.168.103.45 RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6439ADF6, Seq=43653, Time=640
<проговаривание аудиофайла>
…
  4.438201 192.168.126.210 -> 192.168.103.45 SIP 597 Status: 180 Ringing
<КПВ вызывающему абоненту>

Вариант с поддержкой early media будет выглядеть вот так:

;exten => _X.,1, Answer
exten => _X.,1, NoOp
same => n,Set(_MONITOR_FILENAME=IN-${EXTEN}-${STRFTIME(${EPOCH},,%Y_%m_%d[%H_%M])}-${CALLERID(num)}-${EXTEN}-${UNIQUEID})
same => n, NoOp(!!!!CALLED=${CALLED})
same => n, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})})
same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})})
same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})})
same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})})
same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})})
same => n, Set(H6=${IF($["${m6}" != ""]?&SIP/${m6})})
same => n, Set(H7=${IF($["${m7}" != ""]?&SIP/${m7})})
same => n, Set(H8=${IF($["${m8}" != ""]?&SIP/${m8})})
same => n, Set(H9=${IF($["${m9}" != ""]?&SIP/${m9})})
same => n, Set(H11=${IF($["${m11}" != ""]?&SIP/${m11})})
same => n, Set(H12=${IF($["${m12}" != ""]?&SIP/${m12})})
same => n, Set(H13=${IF($["${m13}" != ""]?&SIP/${m13})})
same => n, Set(H14=${IF($["${m14}" != ""]?&SIP/${m14})})
same => n, Set(H15=${IF($["${m15}" != ""]?&SIP/${m15})})

same => n,Set(NOTIFICATION=${IF($["${NOTIFICATION_ENABLED}" = "1"]?noanswer:skip)})
same => n,Playback(/var/lib/asterisk/sounds/ivr/razgovor_record,${NOTIFICATION})
same => n,Dial(${H1}${H2}${H3}${H4}${H5}${H6}${H7}${H8}${H9}${H10}${H11}${H12}${H13}${H14}${H15},${RING_TIMEOUT},TtrM(monext))
same => n,GotoIf($["${FORWARD_AFTER_ENABLE}" != "1"]?end)
same => n,GotoIf($[${REGEX("@" ${FORWARD_AFTER_NUMBER})}]?nochangecid)
same => n,Set(CALLERID(num)=${IF($["${REGEX("^7812.......$" ${EXTEN})}"]?${EXTEN}:78120001122)})
same => n(nochangecid),Dial(${FORWARD_AFTER_NUMBER},${TIMEOUT_RING_AFTER},TtrM(monext))
same => n,Goto(CHECK_BUSY_FORWARD,${EXTEN},1)
same => n(end),Hangup

Старое оборудование, которое установлено у операторов связи оможет «криво» поддерживать или не поддерживать  early media вовсе. Например, по SIP-дампу в RTP трафике в плеере wireshark уведомление на участке между АТС и оператором слышно, а у абоненента в сети ТфОП тишина. В таком случае нужно сообщение connect и следует использовать первый вариант.

Таким образом мы добавили воспроизведение аудиофайла, в частности уведомление о записи разговоара, которое всегда проигрывается только одной стороне — клиенту, а не обоим сторонам вызова. Причём включение и отключение уведомления регулируется путём установки параметра в базе данных MySQL. Поэтому сделать интерфейс для абонентов, где они самостоятельно устанавливать этот параметр не составит труда. Это также можно реализовать через служебные комбинации или старкоды.

Комментарии:

Оставить комментарий

Напишите Ваш комментарий ниже. Также Вы можете подписаться на комментарии к материалу через RSS

Вы можете использовать следующие теги:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

Мы поддерживаем Gravatar.

Контроль спама: * Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.