Это старая версия документа!
Пишем голосовое IVR меню на языке TCL, с использованием Cisco IVR API
Сегодня речь о голосовом меню (IVR) для маршрутизаторов Cisco, которое мы будем писать на языке TCL, и подключать на Cisco 3845.
Итак, для начала давайте разберемся в азах
Cisco начиная с версии IOS 12 поддерживает как VXML так и TCL скрипты для работы с голосовым меню. Однако, в отличии от VXML, скрипты на TCL имеют гораздо больше возможностей взаимодействия с Cisco IVR API. Так же существует возможность подключать гибридные IVR скрипты, со встроенными кусками VXML кода внутри TCL скрипта.
Все документы, связанные с IVR от Cisco, которые мне довелось получить можно скачать здесь.
FSM
Первое это FSM переходы.
Finite-State Machines — абстрактный автомат, число возможных внутренних состояний которого конечно.
Выглядит это примерно так:
''set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup same_state" ''
Переходов таких может быть сколько угодно, и расположены они в конце TCL скрипта.
Давайте разберемся, что это вообще такое.
Общий синтаксис этой команды таков:
''set array(CURRSTATE, curr_event) “act_proc NEXTSTATE” ''
где:\
array – это имя FSM массива.
CURRSTATE – имя текущего состояния, при котором получено событие curr_event .
act_proc – имя функции, которую необходимо выполнить при поступлении события curr_event .
NEXTSTATE – имя состояния, которое установится после выполнения act_proc .
Другими словами, FSM это маркер, по которому Cisco сравнивает полученное от API событие с curr_event и текущий статус с CURRSTATE, если в каком либо FSM переходе они описаны, вызывается процедура act_proc и состояние изменяется на NEXTSTATE.
Самое главное в этом — это то, что текущее событие и состояние сравниваются со всеми описанными FSM переходами одновременно. Т.е. для Cisco не имеет значения порядок, в котором расположены FSM переходы, все они обрабатываются асинхронно.
Функции
Второй момент, это сами функции, которые должны быть описаны до инициализации скрипта.
Назначение всех команд и состояний подробно описано в файле tcl_ivr_2.0_programming_guide, который вы можете скачать здесь, я поподробнее остановлюсь только на тех, которые буду использовать непосредственно в скрипте
1) Инициализация скрипта
Начало любого TCL IVR скрипта содержит процедуру init , в моем примере эта функция выглядит так:
''proc init { } { puts "\n proc Init start" global param } ''
Здесь по сути выполняется вывод на экран командой puts «…« и определение глобальной переменной param
Инициализация скрипта происходит после описания всех функций, и начинается с запуска функции init . На этом простые вещи закончились, дальше все гораздо интереснее.
Последней исполняемой строкой скрипта должна быть строка определения стартового FSM перехода и стартового состояния. В нашем случае это:
''fsm define ivr_fsm CALLCOMES ''
Это значит, что имя FSM массива задано как ivr_fsm, и стартовое состояние CALLCOMES. С инициализацией закончим, дальше будет понятнее, что происходит (я надеюсь).
2) Приветствие
''proc Play_Welcome { } { puts "\n\n IVR - proc Play_Welcome start \n\n" global playng_files global param global pattern global numbers global workingtime #Вызываем процедуру, где описаны все переменные init_perCallVars #Получаем время GetDate #В зависимости рабочее сейчас время или нет, устанавливаем приветствие if {$workingtime} { set after_welcome $playng_files(takenumber) } else { set after_welcome $playng_files(noworking) } #Устанавливаем параметры подключения входящего вызова set param(interruptPrompt) true set param(abortKey) * set param(terminationKey) # #Подключаем входящий вызов leg setupack leg_incoming leg proceeding leg_incoming leg connect leg_incoming #Запускаем процедуру сбора нажатых цифр со стороны звонящего leg collectdigits leg_incoming param pattern #Запускаем проигрыш файлов звонящему абоненту, после их окончания #начнет действовать таймер param(interDigitTimeout), по истечении которого #Произойдет событие ev_collectdigits_done media play leg_incoming %s500 $playng_files(welcome) $after_welcome $playng_files(onhold) #Запускаем таймер, по истечении которого произойдет событие ev_named_timer timer start named_timer $numbers(waiting_time) t1 } '' </file> Здесь довольно подробно все описано.\ Результатом выполнения данной процедуры будет подключение входящей линии к Cisco за счет команд //**leg setupack, leg proceeding, leg connect,**// и проигрыш музыкальных файлов по очереди во входящую линию за счет команды //**media play leg_incoming**// .\\ Тут же запускается процесс сбора нажатых клавиш **//leg collectdigit//** и таймер командой //**timer start**// .\\ \\ И проверяется рабочее сейчас время или нет, вызывая функцию //**GetDate**// : <code>''proc GetDate { } { global workingtime #Час set houris [clock format [clock seconds] -format %H] #День недели set dayis [clock format [clock seconds] -format %A] #Проверяем рабочее время if {$houris > 17 || $houris < 8 || $dayis=="Sunday" || $dayis=="Saturday"} { set workingtime 0 } else { set workingtime 1 } } ''
В зависимости от того рабочее время или нет мы меняем музыкальный файл, который будет проигран звонящему абоненту.\
Так как стартовое состояние задано в нашем случае как fsm define ivr_fsm CALLCOMES , в него попадают сразу 3 FSM:
''set ivr_fsm(CALLCOMES,ev_setup_indication) "Play_Welcome, same_state" set ivr_fsm(CALLCOMES,ev_collectdigits_done) "CheckDestanation, same_state" set ivr_fsm(CALLCOMES,ev_named_timer) "GoToReception, same_state" ''
Событие ev_setup_indication произойдет при поступлении звонка, и будет запущена процедура Play_Welcome , в которой описан старт процесса сбора нажатых цифр и старт таймера.
После окончания проигрывания музыки абоненту, начнется обратный отчет таймера, который задается параметром param(initialDigitTimeout) (который можно было задать чуть выше строкой set param(initialDigitTimeout) 15 и установить значение 15 секунд), т.к. он у нас не указан, его стандартное значение 10 секунд, после чего скрипт получит событие ev_collectdigits_done , при наступлении которого, как мы описали в FSM переходе, будет выполнена функция CheckDestanation .
Таймер, запущенный в Play_Welcome командой:
''#Тип таймера named_timer, длительность, взята из переменной numbers(waiting_time), имя таймера t1 timer start named_timer $numbers(waiting_time) t1 ''
После своего окончания сгенерирует событие ev_named_timer , которое будет обработано следующим FSM переходом:
''set ivr_fsm(CALLCOMES,ev_named_timer) "GoToReception, same_state" ''
и вызовется процедура GoToReception .
3) Проверка введенного номера
''proc CheckDestanation { } { puts "\n\n IVR - proc CheckDestanation start \n\n" global playng_files global numbers global digit #Останавливаем проигрыш медиа media stop leg_incoming #Определяем значение переменным set status [infotag get evt_status] set digit [infotag get evt_dcdigits] #Сравниваем полученные цифры и статусы #Если введенная цифра соответствует той, что задана в $numbers(fast_reception), #изменяем digit на номер ресепшн и передаем $digit в функцию CheckCallersAndConnect, # предварительно изменив статус на CALLCONNECTED, # благодаря которому, при наступлении события ev_setup_done (подключение к номеру секретаря) # будет отработана процедура CallIsConnect if {$digit == $numbers(fast_reception)} { puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n" fsm setstate CALLCONNECTED set digit $numbers(reception) #Передаем $digit в функцию CheckCallersAndConnect CheckCallersAndConnect $digit #Если введенная цифра соответствует той, что задана в $numbers(fast_ckp), подключаем на ЦКП #через CheckCallersAndConnect } elseif {$digit == $numbers(fast_ckp)} { puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next CKP \n\n" fsm setstate CALLCONNECTED set digit $numbers(ckp) #Передаем $digit в функцию CheckCallersAndConnect CheckCallersAndConnect $digit #Если введенная цифра соответствует той, что задана в $numbers(fast_fax), подключаем на факс #через CheckCallersAndConnect } elseif {$digit == $numbers(fast_fax)} { puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next fax \n\n" fsm setstate CALLCONNECTED set digit $numbers(fax) #Передаем $digit в функцию CheckCallersAndConnect CheckCallersAndConnect $digit #Если статус = cd_004 (введены корректные цифры номера) - подключаем к нужному номеру #через CheckCallersAndConnect } elseif {$status == "cd_004"} { puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n" fsm setstate CALLCONNECTED #Передаем $digit в функцию CheckCallersAndConnect CheckCallersAndConnect $digit #Если статус = cd_005 (совпадение с dial plan) - подключаем к нужному номеру #через CheckCallersAndConnect } elseif {$status == "cd_005"} { puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n" fsm setstate CALLCONNECTED #Передаем $digit в функцию CheckCallersAndConnect CheckCallersAndConnect $digit #Если статус = cd_006 (набран не существующий номер) - играем в линию $playng_files(noexist) # и изменяем статус на TORECEPTION, при действии которого и наступлении события #ev_media_done (конец проигрывания звукового файла) вызовется процедура Play_TakeNumber } elseif {$status == "cd_006"} { puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n" fsm setstate TRYAGAIN media play leg_incoming $playng_files(noexist) #Во всех остальных случаях изменяем статус на TORECEPTION, при действии которого #и наступлении события ev_media_done (конец проигрывания звукового файла) вызовется #процедура GoToReception } else { #Проигрываем "Ваш вызов переадресовывается на секретаря" fsm setstate TORECEPTION media play leg_incoming $playng_files(toreception) puts "\n\n IVR - proc CheckDestanation status = $status \n\n" } } '' </file> В процедуре CheckDestanation, которая будет вызвана после набора номера звонящим абонентом, мы сравниваем полученные при наборе цифры с настройками и переводим скрипт в соответствующее состояние командой //**fsm setstate**// .\\ \\ Все состояния, попавшие в функцию, попадают под следующие FSM переходы: <code>''set ivr_fsm(CALLCONNECTED,ev_setup_done) "CallIsConnect, same_state" set ivr_fsm(TORECEPTION,ev_media_done) "GoToReception, same_state" set ivr_fsm(TRYAGAIN,ev_media_done) "Play_TakeNumber, TRYING" set ivr_fsm(TRYING,ev_collectdigits_done) "CheckDestanation, same_state" set ivr_fsm(TRYING,ev_named_timer) "GoToReception, same_state" ''
Давайте по порядку.\
1) Итак, изначально функция CheckDestanation вызывается после окончания процедуры сбора нажатия клавиш.
2) Информацию о нажатых клавишах мы записываем в переменную digit с помощью команды set digit [infotag get evt_dcdigits]
Аналогично записываем состояние линии в переменную status
3) Затем сравниваем полученные результаты с заданными переменными и изменяем состояние скрипта при совпадении:
''if {$digit == $numbers(fast_reception)} { puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n" fsm setstate CALLCONNECTED leg setup $numbers(reception) callinfo leg_incoming } ''
4) Проверка номера звонящего абонента
''proc CheckCallersAndConnect {digit} { puts "\n\n IVR - proc CheckCallersAndConnect start \n\n" set callernumber [infotag get leg_ani] switch $callernumber { "9120000000" {set callInfo(displayInfo) "Director(mobile)"} "9130000000" {set callInfo(displayInfo) "Buhgalter(mobile)"} default {} } puts "\n\n IVR - caller is $callernumber connect with $digit\n\n" leg setup $digit callInfo leg_incoming } ''
Данная функция позволяет изменить поле, отвечающее за написание имени звонящего. Просто ради эстетики, будет приятнее, когда на телефоне будет написан не только номер но и ID абонента. После изменения ID абонента происходит подключение линии к требуемому номеру.
5) Подключение номера
''proc CallIsConnect { } { puts "\n\n IVR - proc CallIsConnect start \n\n" global playng_files #Определяем чему равен status set status [infotag get evt_status] #Если статус равен ls_000 (успешное соединение с требуемым номером), изменяем состояние на CALLACTIVE if {$status == "ls_000"} { fsm setstate CALLACTIVE #Если статус равен ls_002 (никто не ответил на звонок), запускаем процедуру запроса номера } elseif {$status == "ls_002"} { fsm setstate TRYAGAIN media play leg_incoming $playng_files(noanswer) #Если статус - неверный номер, запускаем процедуру запроса номера } elseif {$status == "ls_004" || $status == "ls_005" || $status == "ls_006"} { fsm setstate TRYAGAIN media play leg_incoming $playng_files(noexist) #Если статус равен ls_007 (абонент занят), запускаем процедуру запроса номера } elseif {$status == "ls_007"} { fsm setstate TRYAGAIN media play leg_incoming $playng_files(busy) } } '' </file> Данная функция вызывается следующим FSM переходом: <code> ''set ivr_fsm(CALLCONNECTED,ev_setup_done) "CallIsConnect, same_state" ''
Событие ev_setup_done наступает после подключения звонящего к требуемой линии.
6) Повторный запрос номера
''proc Play_TakeNumber { } { puts "\n\n IVR - proc Play_TakeNumber start \n\n" global playng_files global numbers global param global pattern #Проверяем какой раз абонент пытается набрать номер if {$numbers(cur_try) <= $numbers(max_try)} { puts "\n\n IVR - proc Play_TakeNumber current try is: $numbers(cur_try) \n\n" incr numbers(cur_try) #Запускаем процедуру сбора нажатых цифр со стороны звонящего leg collectdigits leg_incoming param pattern #Запускаем проигрыш файлов media play leg_incoming $playng_files(takenumber) #Запускаем таймер, по истечении которого произойдет событие ev_named_timer timer start named_timer $numbers(waiting_time) t1 #Если попытка больше чем $numbers(max_try) - разъединяем } else { fsm setstate CALLDISCONNECTED media play leg_incoming $playng_files(callafter) } } '' </file> Данная функция проверяет какой раз ошибается звонящий, и если значение меньше чем **//$numbers(max_try)//** просит ввести номер еще раз.\\ \\ Данная функция вызывается следующими **FSM**: <code>''set ivr_fsm(TRYAGAIN,ev_media_done) "Play_TakeNumber, TRYING" set ivr_fsm(TRYING,ev_collectdigits_done) "CheckDestanation, same_state" set ivr_fsm(TRYING,ev_named_timer) "GoToReception, same_state" ''
7) Разрыв соединения
''proc AbortCall { } { puts "\n\n IVR - proc AbortCall start \n\n" call close } ''
Вызывается следующими FSM:
''set ivr_fsm(any_state,ev_disconnected) "AbortCall, same_state" set ivr_fsm(CALLACTIVE,ev_disconnected) "AbortCall, CALLDISCONNECTED" set ivr_fsm(CALLDISCONNECTED,ev_disconnected) "AbortCall, same_state" set ivr_fsm(CALLDISCONNECTED,ev_media_done) "AbortCall, same_state" set ivr_fsm(CALLDISCONNECTED,ev_disconnect_done) "AbortCall, same_state" ''
8) Подключение скрипта
Подключение на маршрутизаторе Cisco проходит в 2 этапа.
Первое, что нужно сделать это определить application:
''application service voicemunu flash:voicemenu.tcl param allowed_pattern 5[5-7].. param fastto_reception 1 param reception_number 5501 param fastto_ckp 2 param ckp_number 5604 param fastto_fax 3 param fax_number 5555 param waiting_time 20 param max_try 3 param file_noanswer flash:en_noanswer.au param file_after flash:en_after.au param file_noexist flash:en_noexist.au param file_busy flash:en_busy.au param file_welcome flash:en_welcome.au param file_onhold flash:music-on-hold.au param file_noworking flash:en_takenumber2.au param file_takenumber flash:en_takenumber2.au '' </file> **Второе**, подключить //**service**// к **//dial-peer//** : <code>''dial-peer voice 200 pots description -= ISP Beeline - INcoming call to number 3300100 =- service voicemunu incoming called-number 3300100 ''
Таким образом, при поступлении звонка на номер 3300100, произойдет вызов нашего голосового меню voicemunu .
9) Полная версия скрипта
Выше были рассмотрены только основные функции скрипта, далее полный текст, имейте в виду, это практически самый простой вариант:
<code>#######################################################
# Cisco IVR TCL script by Konovalov D.A. v.2
#######################################################
#
# Для дебага скрипта
# debug voip application script
# Более полный дебага (не рекомендуется, может привести к перегрузке)
# debug voip ivr
#
# Скрипт должен быть запущен со следующими параметрами:
# param allowed_pattern 5[5-7]..
# param fastto_reception 1
# param reception_number 5501
# param fastto_ckp 2
# param ckp_number 5604
# param fastto_fax 3
# param fax_number 5555
# param waiting_time 20
# param max_try 3
# param file_welcome flash:en_welcome.au
# param file_takenumber flash:en_takenumber.au
# param file_after flash:en_after.au
# param file_busy flash:en_busy.au
# param file_noexist flash:en_noexist.au
# param file_noanswer flash:en_noanswer.au
# param file_onhold flash:music-on-hold.au
# param file_noworking flash:music-on-hold.au
#Процедура инициализации скрипта
proc init { } {
puts «\n proc Init start»
global param
}
#Процедура с переменными
proc init_perCallVars { } {
global pattern
global numbers
global playng_files
#####Допустимая нумерация
#Если в параметрах скрипта не указана допустимая нумерация, будет установлено значение …. - 4 любых цифры
if {[infotag get cfg_avpair_exists allowed_pattern]} {
set pattern(1) [string trim [infotag get cfg_avpair allowed_pattern]]
puts «\n\n IVR - Allowed pattern set as: $pattern(1) \n\n»
} else {
set pattern(1) ….
puts «\n\n IVR - Allowed pattern set as DEFAULT: $pattern(1) \n\n»
}
#####Номера
#Секретарь. Если в параметрах скрипта не указан номер секретаря, номер будет установлен в 0000
if {[infotag get cfg_avpair_exists reception_number]} {
set numbers(reception) [string trim [infotag get cfg_avpair reception_number]]
puts «\n\n IVR - reception number set as: $numbers(reception) \n\n»
} else {
set numbers(reception) 0000
puts «\n\n IVR - reception number set as DEFAULT: $numbers(reception) \n\n»
}
#ЦКП
if {[infotag get cfg_avpair_exists ckp_number]} {
set numbers(ckp) [string trim [infotag get cfg_avpair ckp_number]]
puts «\n\n IVR - ckp number set as: $numbers(ckp) \n\n»
} else {
set numbers(ckp) 0000
puts «\n\n IVR - ckp number set as DEFAULT: $numbers(ckp) \n\n»
}
#Факс
if {[infotag get cfg_avpair_exists fax_number]} {
set numbers(fax) [string trim [infotag get cfg_avpair fax_number]]
puts «\n\n IVR - fax number set as: $numbers(fax) \n\n»
} else {
set numbers(fax) 0000
puts «\n\n IVR - fax number set as DEFAULT: $numbers(fax) \n\n»
}
#Быстрый перевод на Ресепшн
if {[infotag get cfg_avpair_exists fastto_reception]} {
set numbers(fast_reception) [string trim [infotag get cfg_avpair fastto_reception]]
puts «\n\n IVR - fast to reception set as: $numbers(fast_reception) \n\n»
} else {
set numbers(fast_reception) 1
puts «\n\n IVR - fast to reception set as DEFAULT: $numbers(fast_reception) \n\n»
}
#Быстрый перевод на ЦКП
if {[infotag get cfg_avpair_exists fastto_ckp]} {
set numbers(fast_ckp) [string trim [infotag get cfg_avpair fastto_ckp]]
puts «\n\n IVR - fast to ckp set as: $numbers(fast_ckp) \n\n»
} else {
set numbers(fast_ckp) 2
puts «\n\n IVR - fast to ckp set as DEFAULT: $numbers(fast_ckp) \n\n»
}
#Быстрый перевод на факс
if {[infotag get cfg_avpair_exists fastto_fax]} {
set numbers(fast_fax) [string trim [infotag get cfg_avpair fastto_fax]]
puts «\n\n IVR - fast to fax set as: $numbers(fast_fax) \n\n»
} else {
set numbers(fast_fax) 3
puts «\n\n IVR - fast to fax set as DEFAULT: $numbers(fast_fax) \n\n»
}
#Время ожидания введения номера (должно быть больше времени проигрыша всех файлов приветствия)
if {[infotag get cfg_avpair_exists waiting_time]} {
set numbers(waiting_time) [string trim [infotag get cfg_avpair waiting_time]]
puts «\n\n IVR - wait number set as: $numbers(waiting_time) \n\n»
} else {
set numbers(waiting_time) 10
puts «\n\n IVR - wait number set as DEFAULT: $numbers(waiting_time) \n\n»
}
#Количество попыток ввести правильный номер, прежде чем звонок будет переведен на секретаря
if {[infotag get cfg_avpair_exists max_try]} {
set numbers(max_try) [string trim [infotag get cfg_avpair max_try]]
puts «\n\n IVR - max try set as: $numbers(max_try) \n\n»
set numbers(cur_try) 0
} else {
set numbers(max_try) 5
puts «\n\n IVR - max try set as DEFAULT: $numbers(max_try) \n\n»
set numbers(cur_try) 0
}
#####Музыкальные файлы, которые будут проигрываться
#Файл приветствия
if {[infotag get cfg_avpair_exists file_welcome]} {
set playng_files(welcome) [string trim [infotag get cfg_avpair file_welcome]]
puts «\n\n IVR - file_welcome set as: $playng_files(welcome) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(welcome) %s1
puts «\n\n IVR - file_welcome set as DEFAULT: $playng_files(welcome) \n\n»
}
#Файл запроса ввести требуемый номер
if {[infotag get cfg_avpair_exists file_takenumber]} {
set playng_files(takenumber) [string trim [infotag get cfg_avpair file_takenumber]]
puts «\n\n IVR - file_takenumber set as: $playng_files(takenumber) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(takenumber) %s1
puts «\n\n IVR - file_takenumber set as DEFAULT: $playng_files(takenumber) \n\n»
}
#Файл «Пожалуйста перезвоните позднее»
if {[infotag get cfg_avpair_exists file_after]} {
set playng_files(callafter) [string trim [infotag get cfg_avpair file_after]]
puts «\n\n IVR - file_after set as: $playng_files(callafter) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(callafter) %s1
puts «\n\n IVR - file_after set as DEFAULT: $playng_files(callafter) \n\n»
}
#Файл «Номер занят»
if {[infotag get cfg_avpair_exists file_busy]} {
set playng_files(busy) [string trim [infotag get cfg_avpair file_busy]]
puts «\n\n IVR - file_busy set as: $playng_files(busy) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(busy) %s1
puts «\n\n IVR - file_busy set as DEFAULT: $playng_files(busy) \n\n»
}
#Файл «Номер не существует»
if {[infotag get cfg_avpair_exists file_noexist]} {
set playng_files(noexist) [string trim [infotag get cfg_avpair file_noexist]]
puts «\n\n IVR - file_noexist set as: $playng_files(noexist) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(noexist) %s1
puts «\n\n IVR - file_noexist set as DEFAULT: $playng_files(noexist) \n\n»
}
#Файл «Соеденяю с секретарем/оператором»
if {[infotag get cfg_avpair_exists file_toreception]} {
set playng_files(toreception) [string trim [infotag get cfg_avpair file_toreception]]
puts «\n\n IVR - file_toreception set as: $playng_files(toreception) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(toreception) %s1
puts «\n\n IVR - file_toreception set as DEFAULT: $playng_files(toreception) \n\n»
}
#Файл «Номер не отвечает, перезвоните позднее»
if {[infotag get cfg_avpair_exists file_noanswer]} {
set playng_files(noanswer) [string trim [infotag get cfg_avpair file_noanswer]]
puts «\n\n IVR - file_noanswer set as: $playng_files(noanswer) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(noanswer) %s1
puts «\n\n IVR - file_noanswer set as DEFAULT: $playng_files(noanswer) \n\n»
}
#Файл музыки, которая будет проигрываться при ожидании
if {[infotag get cfg_avpair_exists file_onhold]} {
set playng_files(onhold) [string trim [infotag get cfg_avpair file_onhold]]
puts «\n\n IVR - file_onhold set as: $playng_files(onhold) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(onhold) %s1
puts «\n\n IVR - file_onhold set as DEFAULT: $playng_files(onhold) \n\n»
}
#Файл музыки, которая будет проигрываться В нерабочее время
if {[infotag get cfg_avpair_exists file_noworking]} {
set playng_files(noworking) [string trim [infotag get cfg_avpair file_noworking]]
puts «\n\n IVR - file_noworking set as: $playng_files(noworking) \n\n»
} else {
#Если файл не найден, он будет заменен на тишину в 1мс
set playng_files(noworking) %s1
puts «\n\n IVR - file_noworking set as DEFAULT: $playng_files(noworking) \n\n»
}
}
proc GetDate { } {
global workingtime
#Час
set houris [clock format [clock seconds] -format %H]
#День недели
set dayis [clock format [clock seconds] -format %A]
#Проверяем рабочее время
if {$houris > 17 || $houris < 8 || $dayis==«Sunday» || $dayis==«Saturday»} {
set workingtime 0
} else {
set workingtime 1
}
}
#Процедура проигрыша приветствия
proc Play_Welcome { } {
puts «\n\n IVR - proc Play_Welcome start \n\n»
global playng_files
global param
global pattern
global numbers
global workingtime
#Вызываем процедуру, где описаны все переменные
init_perCallVars
#Получаем время
GetDate
#В зависимости рабочее сейчас время или нет, устанавливаем приветствие
if {$workingtime} {
set after_welcome $playng_files(takenumber)
} else {
set after_welcome $playng_files(noworking)
}
#Устанавливаем параметры подключения входящего вызова
set param(interruptPrompt) true
set param(abortKey) *
set param(terminationKey) #
#Подключаем входящий вызов
leg setupack leg_incoming
leg proceeding leg_incoming
leg connect leg_incoming
#Запускаем процедуру сбора нажатых цифр со стороны звонящего
leg collectdigits leg_incoming param pattern
#Запускаем проигрыш файлов звонящему абоненту, после их окончания начнет
#действовать таймер param(interDigitTimeout), по истечении которого
#будет событие ev_collectdigits_done
media play leg_incoming %s500 $playng_files(welcome) $after_welcome $playng_files(onhold)
#Запускаем таймер, по истечении которого произойдет событие ev_named_timer
timer start named_timer $numbers(waiting_time) t1
}
#Процедура запроса ввести номер
proc Play_TakeNumber { } {
puts «\n\n IVR - proc Play_TakeNumber start \n\n»
global playng_files
global numbers
global param
global pattern
#Проверяем какой раз абонент пытается набрать номер
if {$numbers(cur_try) ⇐ $numbers(max_try)} {
puts «\n\n IVR - proc Play_TakeNumber current try is: $numbers(cur_try) \n\n»
incr numbers(cur_try)
#Запускаем процедуру сбора нажатых цифр со стороны звонящего
leg collectdigits leg_incoming param pattern
#Запускаем проигрыш файлов
media play leg_incoming $playng_files(takenumber)
#Запускаем таймер, по истечении которого произойдет событие ev_named_timer
timer start named_timer $numbers(waiting_time) t1
#Если попытка больше чем $numbers(max_try) - разъединяем
} else {
fsm setstate CALLDISCONNECTED
media play leg_incoming $playng_files(callafter)
}
}
#Процедура перевода звонка на секретаря
proc GoToReception { } {
puts «\n\n IVR - proc GoToReception start \n\n»
global numbers
#Останавливаем проигрыш медиа
media stop leg_incoming
#Меняем состояние
fsm setstate CALLCONNECTED
set digit $numbers(reception)
#Передаем $digit в функцию CheckCallersAndConnect
CheckCallersAndConnect $digit
}
#Здесь проверяем введенные или не введенные звонящим цифры
proc CheckDestanation { } {
puts «\n\n IVR - proc CheckDestanation start \n\n»
global playng_files
global numbers
global digit
#Останавливаем проигрыш медиа
media stop leg_incoming
#Определяем значение переменным
set status [infotag get evt_status]
set digit [infotag get evt_dcdigits]
#Сравниваем полученные цифры и статусы
#Если введенная цифра соответствует той, что задана в $numbers(fast_reception),
#изменяем digit на номер ресепшн и передаем $digit в функцию CheckCallersAndConnect,
# предварительно изменив статус на CALLCONNECTED,
# благодаря которому, при наступлении события ev_setup_done (подключение к номеру секретаря)
# будет отработана процедура CallIsConnect
if {$digit == $numbers(fast_reception)} {
puts «\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n»
fsm setstate CALLCONNECTED
set digit $numbers(reception)
#Передаем $digit в функцию CheckCallersAndConnect
CheckCallersAndConnect $digit
#Если введенная цифра соответствует той, что задана в $numbers(fast_ckp), подключаем на ЦКП
#через CheckCallersAndConnect
} elseif {$digit == $numbers(fast_ckp)} {
puts «\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next CKP \n\n»
fsm setstate CALLCONNECTED
set digit $numbers(ckp)
#Передаем $digit в функцию CheckCallersAndConnect
CheckCallersAndConnect $digit
#Если введенная цифра соответствует той, что задана в $numbers(fast_fax), подключаем на факс
#через CheckCallersAndConnect
} elseif {$digit == $numbers(fast_fax)} {
puts «\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next fax \n\n»
fsm setstate CALLCONNECTED
set digit $numbers(fax)
#Передаем $digit в функцию CheckCallersAndConnect
CheckCallersAndConnect $digit
#Если статус = cd_004 (введены корректные цифры номера) - подключаем к нужному номеру
#через CheckCallersAndConnect
} elseif {$status == «cd_004»} {
puts «\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n»
fsm setstate CALLCONNECTED
#Передаем $digit в функцию CheckCallersAndConnect
CheckCallersAndConnect $digit
#Если статус = cd_005 (совпадение с dial plan) - подключаем к нужному номеру
#через CheckCallersAndConnect
} elseif {$status == «cd_005»} {
puts «\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n»
fsm setstate CALLCONNECTED
#Передаем $digit в функцию CheckCallersAndConnect
CheckCallersAndConnect $digit
#Если статус = cd_006 (набран не существующий номер) - играем в линию $playng_files(noexist)
#и изменяем статус на TRYAGAIN, при действии которого и наступлении события ev_media_done
#(конец проигрывания звукового файла) вызовется процедура Play_TakeNumber
} elseif {$status == «cd_006»} {
puts «\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n»
fsm setstate TRYAGAIN
media play leg_incoming $playng_files(noexist)
#Во всех остальных случаях изменяем статус на TORECEPTION, при действии которого и
#наступлении события ev_media_done (конец проигрывания звукового файла) вызовется процедура GoToReception
} else {
#Проигрываем «Ваш вызов переадресовывается на секретаря»
fsm setstate TORECEPTION
media play leg_incoming $playng_files(toreception)
puts «\n\n IVR - proc CheckDestanation status = $status \n\n»
}
}
#Проверяем звонящего, если совпадает, будем менять отображаемое имя
proc CheckCallersAndConnect {digit} {
puts «\n\n IVR - proc CheckCallersAndConnect start \n\n»
set callernumber [infotag get leg_ani]
switch $callernumber {
«9120000000» {set callInfo(displayInfo) «Director(mobile)»}
«9130000000» {set callInfo(displayInfo) «Buhgalter(mobile)»}
default {}
}
leg setup $digit callInfo leg_incoming
}
#Процедура проверки состоянии линии после подключения звонящего к требуемому номеру
proc CallIsConnect { } {
puts «\n\n IVR - proc CallIsConnect start \n\n»
global playng_files
#Определяем чему равен status
set status [infotag get evt_status]
#Если статус равен ls_000 (успешное соединение с требуемым номером), изменяем состояние на CALLACTIVE
if {$status == «ls_000»} {
fsm setstate CALLACTIVE
#Если статус равен ls_002 (никто не ответил на звонок), запускаем процедуру запроса номера
} elseif {$status == «ls_002»} {
fsm setstate TRYAGAIN
media play leg_incoming $playng_files(noanswer)
#Если статус - неверный номер, запускаем процедуру запроса номера
} elseif {$status == «ls_004» || $status == «ls_005» || $status == «ls_006»} {
fsm setstate TRYAGAIN
media play leg_incoming $playng_files(noexist)
#Если статус равен ls_007 (абонент занят), запускаем процедуру запроса номера
} elseif {$status == «ls_007»} {
fsm setstate TRYAGAIN
media play leg_incoming $playng_files(busy)
}
}
#Процедура прерывания звонка
proc AbortCall { } {
puts «\n\n IVR - proc AbortCall start \n\n»
call close
}
#Исполнение скрипта
init
#init_perCallVars
#Это набор состояний и возникающих при данных состояних событий
#По сути именно это и описывает работу скрипта
#Если в любом состоянии возникнет событие отключения ev_disconnected, вызвать AbortCall
set ivr_fsm(any_state,ev_disconnected) «AbortCall, same_state»
#Если в состоянии CALLCOMES возникнет событие ev_setup_indication (входящий вызов)
#запускается Play_Welcome, и состояние меняется на same_state (т.е. остается прежним)
set ivr_fsm(CALLCOMES,ev_setup_indication) «Play_Welcome, same_state»
#Если в состоянии CALLCOMES возникнет событие ev_collectdigits_done (закончен ввод цифр)
#запускается CheckDestanation, и состояние остается прежним
set ivr_fsm(CALLCOMES,ev_collectdigits_done) «CheckDestanation, same_state»
#Если в состоянии CALLCOMES возникнет событие ev_named_timer (закончился таймер ожидания ввода цифр)
#запускается GoToReception, и состояние остается прежним
set ivr_fsm(CALLCOMES,ev_named_timer) «GoToReception, same_state»
#Если в состоянии TORECEPTION возникнет событие ev_media_done (закончился проигрыш файла)
#запускается GoToReception, и состояние остается прежним
set ivr_fsm(TORECEPTION,ev_media_done) «GoToReception, same_state»
#Данные настройки описывают поведение скрипта при ошибке в номере
set ivr_fsm(TRYAGAIN,ev_media_done) «Play_TakeNumber, TRYING»
set ivr_fsm(TRYING,ev_collectdigits_done) «CheckDestanation, same_state»
set ivr_fsm(TRYING,ev_named_timer) «GoToReception, same_state»
#Если в состоянии CALLCONNECTED возникнет событие ev_setup_done
#(установлено/неустановлено соединение с требуемым номером) запускается CallIsConnect, и состояние остается прежним
set ivr_fsm(CALLCONNECTED,ev_setup_done) «CallIsConnect, same_state»
#Эти события отрабатывают отключение линии
set ivr_fsm(CALLACTIVE,ev_disconnected) «AbortCall, CALLDISCONNECTED»
set ivr_fsm(CALLDISCONNECTED,ev_disconnected) «AbortCall, same_state»
set ivr_fsm(CALLDISCONNECTED,ev_media_done) «AbortCall, same_state»
set ivr_fsm(CALLDISCONNECTED,ev_disconnect_done) «AbortCall, same_state»
fsm define ivr_fsm CALLCOMES
</file> Источник https://habr.com/ru/articles/265453/