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

Настройка уведомления о записи разговора для исходящих и входящих вызовов в 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.

Контроль спама: *