📜 ⬆️ ⬇️

We make a Cloud IVR with intellectual redirection and recognition in a few minutes

The standard scenario that many businesses need to implement is an IVR menu for an incoming call that allows you to either get some information or contact a specific employee or operator of the company. The caller can control the menu either by pressing the buttons on the phone (DTMF), or even voice (ASR). Since the VoxImplant platform allows you to quickly write and debug call processing scripts in Javascript, we decided to tell you how you can improve your business experience in a few minutes by making a convenient and technological IVR menu. In addition, you can intelligently distribute the load on your telephone system and employees. For details, as usual, welcome under cat.
As usual, we will need a free developer account VoxImplant ( you can take here ) and some free time. So, in order to demonstrate more possibilities of VoxImplant, let's make an IVR, which asks for a call to call the city of Russia and tells the weather forecast for the selected city. For weather information, we use the weather.yandex.ru service. So, go to the control panel VoxImplant and in the Applications section create a new application, you can call it ivr. Go to the Scenarios section and create a new script of the following form:

//     require(Modules.ASR); var call, asr, city, cityId, Weather = {}, // Yandex  Weather.cities tts_voice = Language.RU_RUSSIAN_FEMALE; //   text-to-speech //    function getCities() { var opts = new Net.HttpRequestOptions(); opts.rawOutput = true; Net.httpRequest("http://weather.yandex.ru/static/cities.json", function (e) { if (e.code == 200) { // json   octet-stream var response = bytes2str(e.data, 'utf-8'); //   Weather.cities eval(response); } else Logger.write("Couldn't load cities. Status: " + e.code); }, opts); } //  id        - function getCityId(name) { for (var i in Weather.cities) { var country = Weather.cities[i]; for (var k in country) { if (k.toLowerCase() == name.toLowerCase()) return country[k]; } } return -1; //   } //      ( ),   ,     ,        VoxEngine.addEventListener(AppEvents.Started, function (e) { getCities(); call = VoxEngine.callPSTN("  ", "    "); //   ,    ,    Voximplant      ,    caller id call.addEventListener(CallEvents.Connected, handleCallConnected); //    call.addEventListener(CallEvents.Failed, cleanup); call.addEventListener(CallEvents.Disconnected, cleanup); }); //    (      ) VoxEngine.addEventListener(AppEvents.CallAlerting, function (e) { getCities(); call = e.call; call.answer(); //    call.addEventListener(CallEvents.Connected, handleCallConnected); //    call.addEventListener(CallEvents.Disconnected, cleanup); }); //   function handleCallConnected(e) { call.say("!   ,      .", tts_voice); enableASR(); } //   function enableASR() { //   ASR,  ,  -      ( ) asr = VoxEngine.createASR(ASRLanguage.RUSSIAN_RU, ASRDictionary.ADDRESS_RU); //  ,        asr.addEventListener(ASREvents.CaptureStarted, function (e) { call.stopPlayback(); }); asr.addEventListener(ASREvents.Result, handleRecognitionResult); //      ASR call.sendMediaTo(asr); } //    function handleRecognitionResult(e) { //  ASR asr.stop(); //    50% if (e.confidence >= 50) { city = e.text; if (city.toLowerCase() == "") city = "-"; //         cityId = getCityId(city); if (cityId == -1 && /\s/g.test(city)) { Logger.write("Replacing whitespace"); city = city.replace(/\s/g, "-"); cityId = getCityId(city); } if (cityId !== -1) { call.say("   " + city, tts_voice); call.addEventListener(CallEvents.PlaybackFinished, handleCityChosen); } else { call.say(" ,     . ,  ,        .", tts_voice); enableASR(); } } else { call.say(" ,     . ,  ,        .", tts_voice); enableASR(); } } //    function declOfNum(number, titles) { cases = [2, 0, 1, 1, 1, 2]; return titles[(number % 100 > 4 && number % 100 < 20) ? 2 : cases[(number % 10 < 5) ? number % 10 : 5]]; } //        function handleCityChosen(e) { call.removeEventListener(CallEvents.PlaybackFinished, handleCityChosen); // cityId Net.httpRequest("http://export.yandex.ru/weather-ng/forecasts/" + cityId + ".xml", function (e) { if (e.code == 200) { Logger.write("Weather info has been loaded"); var data = e.text.split("\n").slice(1).join("\n"), forecast = new XML(data), ns = new Namespace('w', 'http://weather.yandex.ru/forecast'), temperature = forecast.ns::fact.ns::temperature, humidity = forecast.ns::fact.ns::humidity, pressure = forecast.ns::fact.ns::pressure, wind_speed = forecast.ns::fact.ns::wind_speed; wind_speed = parseFloat(wind_speed).toFixed(1).replace(/\.0{1}$/, ""); var forecast_string = " " + parseNumber(temperature) + " " + declOfNum(Math.abs(temperature), ['', '', '']) + ", " + forecast.ns::fact.ns::weather_type + ",  " + parseNumber(humidity) + " " + declOfNum(humidity, ['', '', '']) + ", " + "  " + parseNumber(pressure) + " " + declOfNum(pressure, ['', '', '']) + "  , " + "  " + parseNumber(wind_speed) + " " + declOfNum(wind_speed, ['', '', '']) + "  "; //     call.say(forecast_string, tts_voice); call.addEventListener(CallEvents.PlaybackFinished, VoxEngine.terminate); } else { Logger.write("Couldn't load weather info. Status: " + e.code); call.say(" ,    . ,   .", tts_voice); call.addEventListener(CallEvents.PlaybackFinished, VoxEngine.terminate); } }); } //   function cleanup(e) { Logger.write("Cleanup"); VoxEngine.terminate(); } 

Do not forget to substitute your phone number in
 call = VoxEngine.callPSTN("  ", "    "); 
, number format - 7xxxxxxxxxxx, well, or another country code, if you are not in Russia. Also indicate the number from which the call will be made; You can confirm the number you own through the Voximplant control panel. Save, you can call the SmartIVR script. If you carefully looked at the code, you noticed that there is a function parseNumber, which is not declared anywhere. When calling, the executed scripts can be combined, so we can make a separate parseNumber script and insert the code of this function there:

 var parseNumber = function () { var dictionary = [ ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" ], ["", "", "", "", "", "", "", "", "", ""], ["", "", "", "", "", "", "", "", "", ""], ["|||", "|||", "|||", "|||"] ]; function getNumber(number, limit) { var temp = number.match(/^\d{1,3}([,|\s]\d{3})+/); if (temp) return temp[0].replace(/[,|\s]/g, ""); temp = Math.abs(parseInt(number)); if (temp !== temp || temp > limit) return null; return String(temp); }; function setEnding(variants, number) { variants = variants.split("|"); number = number.charAt(number.length - 2) === "1" ? null : number.charAt(number.length - 1); switch (number) { case "1": return variants[0] + variants[1]; case "2": case "3": case "4": return variants[0] + variants[2]; default: return variants[0] + variants[3]; }; }; function getPostfix(postfix, number) { if (typeof postfix === "string" || postfix instanceof String) { if (postfix.split("|").length < 3) return " " + postfix; return " " + setEnding(postfix, number); }; return ""; }; return function (number, postfix) { if (typeof number === "undefined") return "999" + new Array(dictionary[3].length + 1).join(" 999"); number = String(number); var minus = false; number.replace(/^\s+/, "").replace(/^-\s*/, function () { minus = true; return ""; }); number = getNumber(number, Number(new Array(dictionary[3].length + 2).join("999"))); if (!number) return ""; postfix = getPostfix(postfix, number); if (number === "0") return "" + postfix; var position = number.length, i = 0, j = 0, result = []; while (position--) { result.unshift(dictionary[i++][number.charAt(position)]); if (i === 2 && number.charAt(position) === "1") result.splice(0, 2, dictionary[0][number.substring(position, position + 2)]); if (i === 3 && position !== 0) { i = 0; if (position > 3 && number.substring(position - 3, position) === "000") { j++; continue; }; result.unshift(setEnding(dictionary[3][j++], number.substring(0, position))); }; }; position = result.length - 5; switch (result[position]) { case "": result[position] = ""; break; case "": result[position] = ""; break; }; if (minus) result.unshift(""); return result.join(" ").replace(/\s+$/, "").replace(/\s+/g, " ") + postfix; }; }(); 

Save the script, we can call it by the function name parseNumber. If someone has a desire, then you can modify the function so that it can also translate fractional numbers into words :) It remains to attach our scripts to the application using rules (Rule), after which you can make the first test call to your phone. To do this, go to the Applications section and edit our previously created ivr application - go to the Rules section and click Add Rule. You can call the rule as you wish, since we are now making the rule for an outgoing call, we can call it OutboundTest, and leave it in. * In the Pattern, since the script is not taken into account when running scripts via the HTTP API API (the id is specified in the request). Drag parseNumber and SmartIVR into Assigned and save (see screenshot)
')


If after that you hover the cursor over the newly created rule, we will see 3 buttons - to start, edit and delete. We are interested in the first.



A dialog appears that allows, in fact, conveniently to call the StartScenarios method described in the HTTP API .



If you entered the phone number correctly, then after pressing the Run button you will receive an incoming call and will be asked to name the city of Russia, if the system recognizes the city and finds it in the Yandex Weather database, then they will tell you about the current weather in this city. Of course, you can take a phone number and attach it to this scenario so that the IVR works for incoming calls, in this case, do not forget to comment out the AppEvents.Started handler, and then every time you call to IVR, the system will also make an outgoing call to your number :) The number is attached to the application in the Phone Numbers> My phone numbers section, after which you need to make a rule (Rule) of the application that incoming calls to the number will send to our scripts, you can simply write a phone number to the Pattern.

PS You can read about creating ordinary IVRs from the menu using VoxImplant in our blog.

Source: https://habr.com/ru/post/231319/


All Articles