Welcome Message

Hello my dear reader,

Welcome to my blog, which is dedicated to Cisco technologies. On its pages we will talk about the limitless world of telephony and networking.

We will focus mostly on Cisco collaboration solutions and technologies. These are IP PBX based on Cisco Unified Communications Manager and Cisco Unified Communications Manager Express, Cisco contact centers, Cisco Voice Gateways, etc. Also, I will introduce you the education news: Cisco authorized courses, my own developed training programs, our upcoming events, online learning.

If you have any questions regarding my posts, job or activities, please feel free to ask your questions. I will try to answer them when I have time.

If you are satisfied with the content of my blog, isn’t that worth a beer or coffee? Donations help me to continue supporting the blog and creating new posts here — things for which I spend hours of my free time! Thank you very much!

Sincerely, Dmytro Benda

Wednesday, May 1, 2013

A sample prompt recording script for UCCX

Good morning!

As you know, scripting for UCCX or Cisco IP IVR always requires some pre-recorded wav files. These are all kinds of greetings, instructions for performing certain actions, voice menus, etc. Typically, these wav files are recorded with an external sound editor and then uploaded to the UCCX / IP IVR repository.

However, you can record audio messages and save them directly to the repository using the UCCX / IP IVR itself, i.e. with the help of a script. The user calls to the application, and then the application  plays the instructions for recording. The user pronounces the required phrase, it is recorded and placed in the repository. This post describes how to make such a script for prompt recording.
An example of our prompt recording script is shown in the figure below. Of course, we will discuss the basic idea of it or template there. The script can be modified and customized to the desired functionality:


As usual, first it is necessary to create variables. Here are their names, types and values:

1. recordMsg - used to store a recorded message temporarily while the script is still running
2. recordPrompt - an audio message containing instructions for recording a prompt (“Please record your message after the tone”)
3. recordUser - it is used to store the user account which will be used to save  the file the Repository (for authentication reasons).
4. recordUserID - it carries user login. In our example, the username and password of the user are the same, so the same variable will also be used as a password.
5. successfulPrompt - a prompt confirming successful file recording
6. welcomeMsg - a prompt containing a greeting.

Now let's talk about the structure of the script. Its beginning is very simple: the call is accepted and the greeting is played. Then, using the Recording step, the script plays the instruction for recording, records user's voice and stores it in the variable named recordMsgRecording step properties are as follows:





After the user's message has been recorded into the recordMsg variable, we proceed to saving the contents of this variable as the wav file to the repository. This procedure requires а UCCX user with administrator role or privilleges to save the file. We define the user name (instructor) using the string variable recordUserID, but the UCCX must check if this user really exists and it must convert the username into a variable with type User (recordUser). The Get User step is used for this purpose:


But besides this, authentication of this user is also required, that is, verification of his password and rights. In our example, the username and password are the same (ie, instructor / instructor), so we use the same recordUserID variable as the password. User authentication is done with the Authenticate User step. UCCX checks if the user recordUser really has the password recordUserID:


And then we upload the recorded message into the repository using the Upload Prompt step. In its properties we specify the variable with the user's phrase (recordMsg), the user name for the save procedure (recordUser), the language of Repository's root folder (en_US) and the name for the new file (New \ MsgfromCaller.wav, it means that a completely new folder named New appears in the root en_US folder and it will already contain the MsgfromCaller.wav file):


After the file has been successfully uploaded to the repository, the user hears a message about the successful recording and the recorded message itself. In order to make the script logic like that, in the Successful branch of the Upload Prompt step, two steps of Play Prompt are set.
 
That's actually the whole story. :)  For those who prefer to still record their prompts using the audio editor, I provide the format of prompts for UCCX / IP IVR:

- codec G.711 or G.729
- ITU-T
- 8 bit
- 8 kb/s sample rate
- mono
- mu-Law
- .wav file extension

28 comments:

  1. Подскажите пожалуйста, а как заставить PlayPromt, читать из других каталогов, а не только из языковых (например C:\1.wav) и как указать путь до файла, если в пути присутсвуют переменные... Заранее спасибо

    ReplyDelete
    Replies
    1. Добрый вечер,

      Думаю, что штатными способами проиграть файл не из репозитория невозможно. Дело в том, что система считает корневой папкой папку репозитория для выбранного языка (он устанавливается в настройках триггера). В этой корневой папке для выбранного языка можно создавать дополнительные папки (например, по названию ваших приложений, для которых пишутся скрипты) и упорядоченно размещать там звуковые файлы.

      Например, пусть мы создали папку Test и поместили туда файл Test.wav. Тогда путь к такому файлу будет Test/Test.wav. Путь можно задавать в виде переменных или просто прописывать его в шаге PlayPromt.

      Пример переменной типа Prompt (имя переменной testPrompt) - P[Test/Test.wav]. Тогда в шаге Play Prompt Вы должны указать, что проигрывается переменная testPrompt. Можете писать в этом шаге и полный путь к файлу (без переменной) - как Вам будет удобно. Оба варианта работают.

      Delete
  2. Подскажите, плз. Во вкладке Document есть компонент Cashe Document. Как высвобождать его память перед прочтением следующего файла.

    ReplyDelete
  3. Добрый день,

    Расскажите, пожалуйста, где Вы нашли такую вкладку: это какой-то step, создание переменных?

    ReplyDelete
    Replies
    1. Cashe Document - это step, находится во вкладке Document в раскрывающемся списке слева.

      Delete
    2. К сожалению, мне не доводилось работать с таким step'ом ранее. Посмотрел его описание в доке Cisco Unified Contact Center Express Editor Step Reference Guide. Этот шаг кэширует файл, на который дана ссылка в step'е Create File Document в память UCCX.

      В свойствах Cashe Document нет никаких настроек, чтобы обнулить память. Можно попробовать обнулить саму переменную типа DOC, в которую производится кэширование. Например, сначала Вы создаете переменную с именем testDoc с нулевым значением (DOC[]). Потом кэшируете файл в эту переменную с помощью Cashe Document, а далее с помощью шага Set снова эту переменную обнуляете, т.е снова присваиваете ей значение DOC []. Потом снова кэшируете следующее значение, итд.

      Предложенный мной способ является всего-лишь предположением, все это нужно проверить. Буду Вам признателен, если отпишетесь, работает ли это или нет.

      Delete
  4. Добрый день, Дмитрий.
    У нас такая задача: есть скрипт обеспечивающийй функционал Help Desk. Операторы просят предусмотреть возможность частой замены мелодии MOH (на новый год-новогодняя, день победы-патриотическая и т.д.) впринципе идея не плохая но менять каждый раз мелодию в настройках CTI группы - это не вариант. Хотелось бы что бы MOH игрался из скрипта, к сожалению формат Вашего блога не позволяет добавить картинки по скрипту или сам скрипт. Задавал этот вопрос на форуме CISCO там есть вся подробная информация по скрипту. К сожалению решить эту проблему там не удалось. Думаю проблема актуальна для многих. Может у Вас был подобный опыт и поможете советом. Поробная информация о скрипте https://supportforums.cisco.com/thread/2240847

    ReplyDelete
  5. Пояснения по скрипту:
    1. Изначально скрипт работал так - входящий звонок попадает в очередь, там на свободного оператора, если после 10 сек ожидания свободный оператор не появился и не принял звонок то срабатывает редирект на фиксированный номер диспетчера. В качестве MOH играется стандартный мох с CUCM
    2. По совету в форуме поменял CALL HOLD на PLAY PROMT
    3. Новый скрипт стал работать так: входящий звонок попадает в очередь, по прежнему играется MOH с CUCM, если в течении 10 сек оператор не принимает звонок, то начинает играть PROMT, причем проигрывается полностью звуковой файл, потом срабатывает редирект.

    ReplyDelete
  6. Добрый день,

    Думаю, что эту проблему решить можно, постараюсь заняться этим вопросом после 25го октября. Сейчас я нахожусь в пути, а потом у меня 2 курса подряд.

    Напишите, пожалуйста, в коменте адрес своей почты (публиковать его не буду). По почте легче пересылать скрипты, итд.

    ReplyDelete
  7. А можно вопрос чуть в сторону? конкретно про IVR на CUCME.
    Есть 2901 с платой E&M, которая используется как линейный выход для усилителя системы громкоговорящего оповещения.
    конфиг:
    =====
    voice-port 0/1/0
    auto-cut-through
    type 5
    signal immediate
    !
    dial-peer voice 199 pots
    destination-pattern 199
    port 0/1/0
    forward-digits 0
    !
    =====
    Всё отлично работает, есть маленький нюанс: очень хотелось бы после соединения проигрывать в громкую связь для привлечения внимания звуковой файл (музычку там или трехтональный гонг - найду или запишу сам).
    Все примеры IVR, что я видел, проигрывают файл ВЫЗЫВАЮЩЕМУ абоненту.
    Как проиграть его ВЫЗЫВАЕМОМУ?

    ReplyDelete
    Replies
    1. Думаю, что на CUCME такое не удастся сделать, увы :( Подобные вещи можно без проблем сделать на контакт-центрах.

      А на CUCМЕ вызывающему абоненту проигрывается сообщение потому, что скрипт запускается после выбора входящего диал-пира, но ДО выбора исходящего. После выбора исходящего диалпира скрипт отключается, стало быть нет возможности проиграть музыку/сообщение вызываемому.

      Delete
    2. А ещё вопрос по выбору диалпира: если у меня есть исходящий диалпир описанный, как приводил выше, на порт:
      ==
      dial-peer voice 199 pots
      destination-pattern 199
      port 0/1/0
      forward-digits 0
      ==
      то если я впишу входящий диалпир примерно так
      ==
      dial-peer voice 198 voip
      incoming called-number 199
      service time_limited_calls
      voice-class codec 1
      !
      ==
      Это сработает? в смысле попадёт вызов на диалпир 198 по incoming called-number? или уйдёт на 199-й как более узкий?

      Delete
    3. И ещё вопрос: я правильно понял, что на настольном стенде (2801, два телефона и E&M модуль), реализующем описанную выше схему с проключением на аналоговый выход, подключить скрипт к входящему диалпиру не уластся, поскольку выбирается автоматически созданный при регистрации SIP телефона?
      Ведь к voice register pool скрипт не подключишь? а судя по логам входящий диалпир автоматически выбран при поднятии трубки, и выбирается дальше только исходящий?

      и ещё: не понимаю, почему я вижу ещё секунд 10-15 после проключения (разговор уже идёт) вот такие сообщения в дебаге
      ==
      Dec 4 00:20:44.259: //-1/xxxxxxxxxxxx/DPM/dpMatchPeersCore:
      Calling Number=, Called Number=10, Peer Info Type=DIALPEER_INFO_SPEECH
      *Dec 4 00:20:44.259: //-1/xxxxxxxxxxxx/DPM/dpMatchPeersCore:
      Match Rule=DP_MATCH_DEST; Called Number=10
      *Dec 4 00:20:44.259: //-1/xxxxxxxxxxxx/DPM/dpMatchPeersCore:
      Result=Success(0) after DP_MATCH_DEST
      *Dec 4 00:20:44.259: //-1/xxxxxxxxxxxx/DPM/dpMatchSafModulePlugin:
      dialstring=10, saf_enabled=0, saf_dndb_lookup=1, dp_result=0
      *Dec 4 00:20:44.259: //-1/xxxxxxxxxxxx/DPM/dpMatchPeersMoreArg:
      Result=SUCCESS(0)
      List of Matched Outgoing Dial-peer(s):
      1: Dial-peer Tag=10
      ===

      Delete
    4. Доброе утро, Сергей!

      Прежде всего приношу извинения за задержку с ответом - веду сейчас курс...

      "Это сработает? в смысле попадёт вызов на диалпир 198 по incoming called-number?" - нет, 198 диалпир не будет выбран как входящий. При звонке с телефона всегда выбирается соответствующий ему виртуальный диал-пир.

      "подключить скрипт к входящему диалпиру не уластся, поскольку выбирается автоматически созданный при регистрации SIP телефона?" - да, именно так. Это происходит из-за причины, описанной в ответе на предыдущий вопрос.

      По вопросу по дебагу не могу пока ничего сказать - конфиги полные надо бы глянуть, топологию полностью представлять, знать, какие цифры набираются... А так, наощупь... это как лечить больного по фотографии :)

      Delete
  8. Добрый день, Дмитрий. Подскажите, а возможно ли запускать скрипт без звонка на определенный номер, т.е. не обрабатывать входящие звонки, а наоборот сам звонил. Например: чтобы в определенный день скрипт сам автоматически звонил на определенные номера и проговаривал сообщение.

    ReplyDelete
    Replies
    1. Добрый день,

      Да, такое можно реализовать на UCCX, начиная с релиза 8.5, если не ошибаюсь (9ка поддерживает точно, 8.0 однозначно нет, 8.5 вроде тоже поддерживает). Такой функционал теперь обеспечивает Outbound Dialer. Ранее он обеспечивал только дозвон до клиента и подключение агента, а теперь можно проигрывать и сообщения с IVR.

      Delete
  9. Добрый день!
    Есть задача - сделать автоматиеское оповещение клиентов об отрицательном балансе. Автоответчик должен автоматически проверять базу данных (для упрощения Microsoft SQL), выбирать значения с отрицательным балансом и оповещать данных сотрудников. Реализуемо ли это средствами UCCX?

    ReplyDelete
    Replies
    1. Доброе утро, Михаил!

      Да, это реализуемо. UCCX умеет подключаться к MS SQL и брать из него данные. Данные переносятся в переменные скрипта, и затем используются скриптом согласно задаче.

      Delete
    2. А есть способ автоматизировать загрузку CSV-файла для исходящих компаний? Т.е. номера и данные клиентов берутся из базы данных и исходя из этих данных составляется файл для обзвона. Как можно автоматически загрузить этот файл?
      Или функционал Outbound Dialer подразумевает, что процесс загрузки информации об клиентах из внешних источников автоматизирован?

      Delete
    3. Думаю, что такой возможности нет. Файл с контактами добавляется вручную на UCCX, увы (по крайней мере, до релиза 9 включительно, а 10ку я не крутил еще).

      Такая возможность есть в "большом" контакт-центре, т.е в UCCE.

      Delete
    4. Здравствуйте!
      А расскажите, пожалуйста, как это сделать " в "большом" контакт-центре, т.е в UCCE"?

      Delete
    5. Доброе утро! В UCCE файл с контактами просто выкладывается в одну из папок на диске UCCE. Далее в настройках Outbound Dialer указывается само имя файла с контактами и путь к нему.

      Ваша задача сведется к тому, чтобы обращаться к своей внешней БД, из нее брать данные, формировать текстовый файл и автоматически выкладывать его на диск UCCE в нужную папку. Но это не задача контакт-центра.

      Delete
  10. Здравствуйте Дмитрий. Подскажите как можно реализовать следующее(если можно пошагово в картинках). Задача состоит в следующем. Написал скрипт. при звонке на внутренний номер если никто трубку не поднял, то на почтовый ящик вызываемого абонента приходит письмо с тел.номером кто звонил и происходит редирект звонка на сотовый номер. Как реализовать подобное для нескольких номеров. Что бы при звонке на какой либо номер отдела происходила сортировка кому именно звонили и на его почту уходило письмо и происходил редирект звонка на его сотовый.

    ReplyDelete
    Replies
    1. Добрый день! Здесь, думаю, нужно использовать шаг Switch. Swtich позволяет делать сравнение значения переменной и далее выполнить те или иные действия.

      Можно рассматривать Switch как составной If. В шаге Switch описываются возможные значения переменной, после чего в Switch появляются, соответственно, ветки. Количество веток равно количеству значений переменных. Далее логика такова: переменная = а -> идем в ветку а и прописываем нужные действия, переменная = b -> идем в ветку b и прописываем нужные действия, итд, если переменная не равна ни одному из описанных значений, то попадаем в ветку Default, где также задаем требуемую далее логику.

      Вот таким образом Вы выполните сортировку.

      Delete
  11. Здравствуйте Дмитрий!
    Спасибо за статью, очень помогла при написании подобного скрипта.
    У меня возникла следующая проблема - при Upload файла, дает записать только в папку репозитория ru_RU, тогда как само считывание происходит из en_US, т.е. нет возможности прослушать файл после записи, надо перезаливать его из ru_RU в en_US.

    ReplyDelete
    Replies
    1. Здравствуйте,

      Записывает в папку ru_RU даже в случае, если Вы в шаге Upload указываете L[en_US]???

      Delete
  12. Дмитрий, здравствуйте!

    Мне нужно, чтоб UCCX записывал wav файлы в формате CCITT a-law. Как это сделать?

    С уважением, Владимир

    ReplyDelete
    Replies
    1. Здравствуйте, Владимир!

      Думаю, что это нельзя сделать. Продукт американский, а посему он записывает на диск файлы только в mu-law :( Доступными средствами администрирования, насколько я знаю, это изменить невозможно, увы.

      Delete