Давайте напишем VXML-скрипт, который будет работать на голосовом шлюзе Cisco в качестве IVR по приведенной схеме.
Алгоритм работы: при звонке на 2100 проиграть файл (Нажмите 1, 2 или 3). Если звонящий ничего не нажимает или нажимает, но не то – проигрываем приглашение снова и терпеливо ждем адекватных действий со стороны звонящего.
Если нажали 1, то переводим звонок на номер 9991, если 2, то предлагаем набрать внутренний номер абонента (четыре цифры, начинающиеся с 2 или 3), ну а если 3, то разрываем соединение, не забыв предварительно попрощаться.
Начало скрипта простое до неприличия:
<?xml version="1.0" encoding="UTF-8"?> <vxml version = "2.1" > <var name="DestNumber"/> <!-- Variable used to carry the destination number -->
В качестве эксперимента создал отдельную форму (form), которая используется только для проигрывания приветствия, которое нельзя "перебивать" вводом DTMF и переходом к следующей форме (id="Main").
<form id="Start"> <block> <prompt bargein="false"><!-- You cannot interrupt this prompt --> <audio src="tftp://192.168.2.213/quickstart/audio/welcome.au"/> </prompt> <goto next="#Main"/><!-- Goto оно и в Африке Goto --> </block> </form>
В качестве места хранения голосовых сообщений использовал TFTP, чтобы не копировать все файлы на лабораторный маршрутизатор. Следует учитывать задержку на копирование по TFTP при первом проигрывании, а также то, что роутер кэширует файлик у себя (в RAM?) и при замене файла на стороне TFTP-сервера, роутер просто так перекачивать файлик не будет (если знаете как сбросить такой кэш для того, чтобы роутер перезалил файл – напишите, пожалуйста в комментариях).
Очень полезно дебажить скрипт командой
debug voip application vxml error
Ниже приведен вывод дебага, когда я использовал тег <prompt> сразу после тега <form>, методом наблюдения за другими скриптами оказалось, что в этом случаем <prompt> нужно "обернуть" с помощью <block> (пример выше).
Lab_BB1_CME(config-app-param)#do deb voip app vxml erro vxml software & call error debugging are on Lab_BB1_CME(config-app-param)# Feb 1 10:53:38.966: //0/77A7A02681C2/VXML:/vxml_start_element_handler: CALL_ERROR; tftp://192.168.2.213/hello.vxml at line 8: Element <prompt> is not used according to DTD Feb 1 10:53:38.970: //-1//VXML:/vxml_create: CALL_ERROR; code=ERROR vapp=VAPP_SUCCESS vxml= Lab_BB1_CME(config-app-param)#
Итак, повторим корректную структуру для проигрывания голосового приветствия:
<form> <block> <prompt> <audio/> </prompt> </block> </form>
Теперь посмотрим на структуру VXML-скрипта для интерактивного диалога с пользователем:
<form> <field> <grammar></grammar><!-- Здесь будут описываться RegExp'ы --> <noinput> <!-- Пользователь ничего не ввёл --> </noinput> <nomatch> <!-- Пользователь брутфорсит --> </nomatch> <prompt> <!-- Расскажем пользователю что делать --> <audio/> </prompt> <filled> <!-- Обрабатываем ввод пользователя --> </filled> </field> </form>
Короткий пример того, как при вводе цифры 1, 2 или 3 происходит присвоение значения переменной и переход к другой форме (TransferToDestNumber):
<form id="Main"> <field name="getdigit" type="digits?length=1"><!-- Expect one digit to be entered --> <grammar type="application/grammar+regex">[123]</grammar><!-- Expect 1, 2, or 3 as user input --> <filled> <!-- Right digits were caught --> <assign name="DestNumber" expr="'phone://9991'"/> <goto next="#TransferToDestNumber"/> </filled> </field> </form>
Осталось посмотреть на форму, которая используется для перевода звонка на внутренний номер:
<form id="TransferToDestNumber"> <transfer connecttimeout="20s" name="mycall" destexpr="DestNumber" bridge="false"> </transfer> <block> <prompt bargein="true"> <audio src="tftp://192.168.2.213/quickstart/audio/busy.au" caching="fast"/> <audio src="tftp://192.168.2.213/quickstart/audio/goodbye.au" caching="fast"/> </prompt> <disconnect/> </block> </form>
И тут (барабанная дробь) на сцене появляется завершающая часть скрипта :)
</vxml>
Скрипт целиком
Нажав на кнопку вы сможете просмотреть мой лабораторный скрипт целиком:А теперь закономерный вопрос: что же делать с этим скриптом?
Ответ: нужно скопировать скрипт и все прилагающиеся аудио-файлы на flash голосового шлюза и "привязать" скрипт к dial-peer.
Есть много примеров того, как привязать VXML-скрипт к dial-peer типа POTS, но вот как быть, если в моём распоряжении вообще нет аналоговых портов? Оказалось, что можно привязать и к dial-peer типа VOIP.
Конфигурация для случая с dial-peer типа VOIP:
! application service NAME_OF_SERVICE flash:/vxml_script.vxml ! dial-peer voice 1 voip service NAME_OF_SERVICE out-bound destination-patter ^2100$ session target ipv4:1.1.1.1
Немного ссылок:
Cisco IOS Tcl IVR and VoiceXML Application Guide - 12.3(14)T and later (Outbound Voice Applications)Cisco IOS VoiceXML Quick Start Guide
Voice Gateway API (VGAPI) Developer Center (можно скачать Sample Scripts для QSG)
Cisco IOS Voice Troubleshooting and Monitoring -- Cisco VoiceXML Troubleshooting
VoiceXML Development Guide
Cisco VoiceXML Programmer's Guide
Простейшее голосовое меню на Cisco VoiceXML
VXML IVR на IOS гейтвее
Простой IVR или автоответчик на маршрутизаторе
P.S.
Получилось немного сумбурно и не последовательно, но если будет время, то я постараюсь реорганизовать материал для более удобного усвоения.Всем успехов! :)
А напоследок небольшое видео (4,2 МБ) с попыткой объяснением алгоритма работы скрипта (удачно или нет, решать Вам, уважаемые читатели):
Крайне полезная информация. Спасибо, Саня!
ОтветитьУдалитьВсегда пожалуйста ;)
УдалитьАлександр, спасибо за статью! Все получилось очень неплохо.
ОтветитьУдалитьСогласен с Сашей Левичевым - информация очень полезная, да еще и наглядно показано, как написать скрипт. Думаю, что она будет полезна всем, кто хочет без особых денежных затрат создать автосекретарь на базе циско-шлюза.
Так держать! :) Молодец!
Спасибо :)
УдалитьДобрый день Александр, спасибо за скрипт и доступное описание.
ОтветитьУдалитьИнформация которая может помочь у кого cisco 2801, скрипт не работал пока не поменял кодировку.
Звуковые промты не обязательно хранить на tftp сервере, их можно хранить и во флеш. Для того, чтобы это работало необходимо изменить encoding с UTF8 на iso-8859-1 пример без скобок <>, так как не позволяет и писать:
?xml version="1.0" encoding="iso-8859-1"?
vxml version="2.0",
а в переменной промт источник аудио файла указать следующий параметр flash:имя звукового файла
пример ниже:
prompt
audio src="flash:no_input.au"/
/prompt
еще полезная команда sh call application voice имя сервиса VXML
с помощью данной команды можно посмотреть статистику : сколько было входящих вызовов на сркипт,сколько раз проигрывался промт, сколько записанных сообщений, а также что за скрипт в данный момент времени используется.
к примеру:
application
service VXML flash:vxml_script.vxml
в нашем случае комманда просмотра будет следующая
sh call application voice VXML
Вопрос, а как сделать переменную, которая бы собирала счетчик сесии, к примеру когда человек не вводит никаких цифр скажем 3 раза после проигрывания звукового файла меню ему проигрывался промт досвидание и происходил разрыв линии. В выше приведенном примере скрипта vxml_script.vxml, если абонент не набирает никаких цифр ему постоянно проигрываются промты bip и меню, тем самым абонент дозвонившийся на скрипт занимает линию и не уходит, так сказать некая dos атака, если таких абонентов будет несколько.
Спасибо.
Доброго времени суток!
УдалитьНасчет хранения на флешке: насколько я помню у меня на 2811 не нужно было менять кодировку, работало и для flash. и для tftp. Просто с tftp мне было удобнее скрипт подгонять до нужной кондиции :). Но всё равно спасибо за информацию: как появится время – добавлю Ваши комментарии в статью.
Насчет счетчика проигрываний: посмотрите скрипт начинающийся на странице 3-12 этого документа от Cisco (catch event ="noinput nomatch error" count="3").
Более удобная ссылка на catch event ="noinput nomatch error" count="3".
УдалитьВо время дозвона включается аудио файл) кнопки 1 2 становятся активными только после прослушивания аудио записи сразу нельзя! как можно поправить чтобы когда идёт аудио сразу нажать допутим 1
ОтветитьУдалитьПроверьте параметр bargein:
УдалитьПараметр bargein тега prompt запрещаете (false) или разрешает (true) прерывать проигрывание аудио-файлов вводом DTMF.
ага) уже настроил пасиб)
УдалитьА как насчёт проиграть файл ВЫЗВАННОМУ абоненту? типа при звонке с определённого номера "С вами будет говорить ХХХХ" или при звонке с внешней линии "Вам звонок из филиала NNNN"
ОтветитьУдалитьПосле загрузка новой редакции - сброс старой и загрузка новой делается вот так:
ОтветитьУдалитьapplication
no service бла-бла
service бла-бла
мне руководитель поставил в задачу чтобы случайным образом или по очереди проигрывались два разных приветствия. ума не приложу как это реализовать. буду признателен за подсказку.
ОтветитьУдалитьДоброго времени суток!
УдалитьЯ бы копал в сторону Math.random и prompt cond (вот ссылка на ресурс, где нашел этот пример):
<_var name="r" expr="Math.random()"/>
<_prompt cond="r < .50">
<_audio src="flash:prompt_01.au"/>
<_prompt cond="r >= .50">
<_audio src="flash:prompt_02.au"/>
Посъедались теги, ещё раз...
Удалить<_var name="r" expr="Math.random()"/>
<_prompt cond="r < .50">
<_audio src="flash:prompt_01.au"/>
<_/prompt>
<_prompt cond="r >= .50">
<_audio src="flash:prompt_02.au"/>
<_/prompt>
спасибо за помощь, но решил уже вопрос самостоятельно.
Удалитьсоздал переменную, которая получает миллисекунды времени звонка
<_var name="time_ms" expr="new Date().getUTCMilliseconds()"/>
если значение больше 500 то один файл, если меньше то другой
Доброго времени суток.
ОтветитьУдалитьВозможно вопрос не в тему. Если есть возможность натолкните на мысль.
Требуется отображение фото и данных с AD звонящего.
Нарисовал скрипт который запрашивает данные и генерит png файл в виде визитки.
Каким образом сделать запрос к скрипту с Cisco ума не дам. Имеет ли решение рыть в сторону VXML?
День добрый!
ОтветитьУдалитьтакая проблема скрипт работает, если все лежит на flash
если на tftp то сам скрипт с tftp подгружается а prompt нет... хотя в ручную с этого адреса промт выкачивается...
есть также скрипт, похожий, который запускается на этом же роутере с tftp без проблем...
Лучше поздно, чем никогда :)
УдалитьЯ бы подебажил как на стороне Cisco, так и на стороне TFTP-сервера (есть ли обращения на TFTP от Cisco? приходят ли запросы на TFTP? в общем с помощью debug и Wireshark)
Добрый день!
ОтветитьУдалитьДелал все по инструкции на CISCO 881 voice.
При попытке звонка на пир, сервер отвечает 500 internal server error.
Подскажите, в чем может быть проблема?
Здравствуйте!
ОтветитьУдалитьСпасибо за статью.
Подскажите пожалуйста, какая у вас модель маршрутизатора и ИОС, на котором работает IVR ?
Д.д. Маршрутизатор был 2811, а вот IOS.. Скорее всего это был c2800nm-adventerprisek9-mz.151-3.T3.bin (отыскал в старых логах).
УдалитьАлександр, не подскажете, как реализовать продолжение диалплана после того, как вторая сторона положила трубку?
ОтветитьУдалитьПопробуйте вот этот вариант: http://www.w3.org/TR/voicexml20/#dml1.5.4
УдалитьБольшое спасибо за статью. Сам только второй день, как начал ковырять vxml. Помогло.)
ОтветитьУдалитьПо поводу Вашего вопроса о сбросе кэша с аудиозаписью в оперативке.
У меня запись хранится на флеше и так же столкнулся с описанной проблемой при ее замене.
Для моего случая получилось так:
1. Заливаю новую запись на флешку
2. Включаю стриминг с флеша добавлением в конфиг команды: ivr prompt streamed flash
3. Отключаю стриминг: ivr prompt streamed none
И при следующем звонке играет новая запись.
Надеюсь, поможет)
Дополнение. Между пунктами 2 и 3 нужно позвонить на этот ivr.
УдалитьЭтот комментарий был удален автором.
ОтветитьУдалитьЭтот комментарий был удален автором.
ОтветитьУдалитьДобрый день,
ОтветитьУдалитьНедавно столкнулся с такой проблемой - как проигрывание IVR скрипта на H323 и SIP транках, на cisco.com в основном описываются решения, когда клиенты звонят на cisco с POTS,FXO,FXS или cisco phones зарегистрированных на этом же CME, где находится IVR скрипт. Но как быть когда клиенты звонят на cisco через SIP или H323 транк? Я потратил на решение этой проблемы 3 недели, пытался поднимать транскодинг, кодировать файлы в другой кодек, так как при входящем вызове на dial-peer voip в h323 согласовывался кодек g729r8 скрипт отрабатывал, но ничего не было слышно, при звонках через sip транк кодек согласовывался g711ulaw, но в ответ так же тишина. В итоге, после долгих тестирований и поисков ответа в интернете решение все таки было найдено, на cisco.com в форуме я его описал подробней - ссылка на форум https://supportforums.cisco.com/ru/discussion/11841966. Здесь же я оставлю краткое решение на всякий случай.
Итак для корректной работы скрипта IVR на SIP и H323 транках необходимо использовать только dial-peer voip как входящий интерфейс по аналогии dial-peer pots.
т.е. рабочий dial-peer должен выглядеть следующим образом:
dial-peer voice 8892 voip
incoming called-number 8892
session target ipv4:192.168.100.121
destination-pattern 8892
service test
В этом пире главное условие применение сервиса service test , а не service test outbound, и обязательное условие указать ,что этот пир является входящим incoming called-number.
На мысль сделать voip как pots подтолкнула вот эта статья http://www.cisco.com/c/en/us/td/docs/routers/access/as5350/software/feature/guide/pull_ivr.pdf Configuring Interactive Voice Response for Cisco Access Platforms и вот этот абзац:
Dial-Peer Application Field
Use the application field in the inbound dial peer to associate an application with an incoming call.
P.S. После того, как я дошел до этого, нашел вот эту статью https://supportforums.cisco.com/ru/discussion/11858086 - обидно ответ был, но я его не видел.
Александр здравствуйте!
ОтветитьУдалитьподскажите каким xml редактором вы на видео пользовались?
Здравствуйте, Андрей,
УдалитьSublime Text 2
Здравствуйте, Андрей,
УдалитьSublime Text 2
По поводу загрузки новых версий скрипта и аудио: всё очевидно и описано на сайте циски :)
ОтветитьУдалитьcall application voice load
audio-prompt load
Друзья, а ограничение VMXL когда нельзя повторно (после попадания вызова к секретарю, например) переадресовать вызов - ещё актуально?
ОтветитьУдалитьАлександр, здравствуйте!
ОтветитьУдалитьПрошу Вашей помощи вот с какой хитрой проблемой со входящим DTMF.
Буду безмерно благодарен за хоть какие-то мысли как побороть проблему. Своей головы уже совершенно не хватает...
Дано:
Cisco 2900, CME, IVR на входящем dial-peer.
SIP транк от провайдера с вышестоящей HuaweiSoftX3000
DTMF Payload type 97 (a=fmtp:97 0-15)
Исходящие DTMF победил с помощью
"rtp payload-type nte 97" и, самое важное: "voice-class sip dtmf-relay force rtp-nte" - без этой команды DTMF ходить не хотел, хоть и payload type согласованный.
А вот со входящим DTMF беда - как только не крутил, но не проходит, хоть ты тресни. Скрипт рабочий - проверял звонком не на внешний номер, а на внутренний, на котором висит IVR. Пересмотрел уже кучу debug'ов, по-разному крутил настройки - безрезультатно.
Единственно, что смущает, если пристально смотреть SDP, то от провайдера приходит, "a=fmtp:97 0-15", а Cisco отдаёт "a=fmtp:97 0-16".
Насколько я понял, это лишь говорит о том, что Cisco поддерживает hook flash и никак влиять не должно. По крайней мере исходящие DTMF работают без проблем.
Озадачил уже провайдера этим вопросом, но пока всё традиционно: "у нас всё хорошо". Может есть ещё какая скрытая команда типа "voice-class sip dtmf-relay force rtp-nte" которая поможет с входящим DTMF при нестандартном payload type?
Добрый день, если ещё актуально, то напишите мне на a@alakin.org
Удалить