IVR(Interactive Voice Response)即交互式语音应答,也就是我们说的电话语音菜单,可以使用预先录制的语音或者TTS进行自动应答,提供菜单导航,主要用于呼叫中心系统。我们主要介绍FreeSWITCH提供的IVR功能。
FreeSWITCH的IVR系统默认的配置文件为conf/autoload_configs/ivr.conf.xml
,它包含了conf/ivr_menus/目录下所有的XML文件,下面我们创建一个XML配置文件conf/ivr_menus/ivr.xml,内容如下:
<include>
<menus>
<menu name="welcome"
greet-long="ivr/welcome.wav"
greet-short="ivr/welcom_short.wav"
invalid-sound="ivr/ivr-that_was_an_invalid_entry.wav"
exit-sound="voicemail/vm-goodbye.wav"
timeout="15000"
max-failures="3"
max-timeouts="3"
inter-digit-timeout="2000"
digit-len="4">
<entry action="menu-exec-app" digits="0" param="transfer 1000 XML default"/>
<entry action="menu-exec-app" digits="/^(10[01][0-9])$/" param="transfer $1 XML default"/>
</menu>
</menus>
</include>
在上述配置中,首先,我们指定菜单的名字(name)是welcome,其他各项的含义如下:
可以看到ivr的动作主要是在entry项里配置完成的,在上述例子中,第一个entry里配置了按键0,通过menu-exec-app执行一个FreeSWITCH的App(transfer),再次通过Dialplan路由,将通话分配到被叫是1000的路由规则上,默认该规则是1000的分机号码。
菜单中的另一个entry的按键规则是一个正则表达式,表示匹配按键是1001~1019的输入,匹配成功后,会将按键赋值给$1,然后再次进行路由。比如用户输入了1019的按键,会通过执行transfer,将通话分配到被叫是1019的路由规则上,默认该规则是1019的分机号码。
如果来电用户按其他按键,则由于找不到匹配的菜单项进而提示错误(invalid-sound指定的声音),并提示用户重新输入。
以上菜单设定好后,需要在控制台中执行reloadxml使配置生效。
配置完成后就可以在控制台上进行如下测试(呼叫1001,接听后进入ivr菜单):
freeswitch> originate user/1001 &ivr(welcome)
测试成功后,你就可以配置Dialplan把并户来话转接到菜单了,在Dialplan中加入一个extension(请注意,你需要加到正确的Dialplan Context中,如果不确定应该加到哪个Context中的话,在default和public中都加上会比较保险。):
<extension name="incoming_call">
<condition field="destination_number" expression="^777$">
<action application="answer" data=""/>
<action application="sleep" data="1000"/>
<action application="ivr" data="welcome"/>
</condition>
</extension>
接下来呼叫777进行测试。
通过上面的ivr.xml的配置,我们已经知道如何配置一个简单的IVR了,接下来我们配置一个带有二级菜单的IVR。
<include>
<menus>
<menu name="main"
greet-long="ivr/main_welcome.wav"
greet-short="ivr/main_welcome_short.wav"
invalid-sound="ivr/ivr-that_was_an_invalid_entry.wav"
exit-sound="voicemail/vm-goodbye.wav"
timeout ="10000"
max-failures="3"
digit-len="4">
<entry action="menu-exit" digits="*"/>
<entry action="menu-sub" digits="2" param="sub"/>
<entry action="menu-exec-app" digits="0" param="transfer 1000 XML default"/>
</menu>
</menus>
</include>
<include>
<menus>
<menu name="sub"
greet-long="ivr/sub_welcome.wav"
greet-short="ivr/web_welcome_short.wav"
invalid-sound="ivr/ivr-that_was_an_invalid_entry.wav"
exit-sound="voicemail/vm-goodbye.wav"
timeout="15000"
max-failures="3"
max-timeouts="3"
inter-digit-timeout="2000"
digit-len="4">
<entry action="menu-exit" digits="*"/>
<entry action="menu-back" digits="6"/>
<entry action="menu-top" digits="7"/>
<entry action="menu-exec-app" digits="/^(10[01][0-9])$/" param="transfer $1 XML default"/>
</menu>
</menus>
</include>
上面配置了两个IVR,名字分别是main
、sub
,顾名思义,main是主菜单,sub是子菜单, 下面先介绍下entry里的action:
配置了XML后,同样需要在控制台中执行reloadxml使配置生效。
配置完成后就可以在控制台上进行如下测试(依然呼叫1001,接听后进入ivr菜单):
freeswitch> originate user/1001 &ivr(main)
进入主菜单后,我们可以按2进入子菜单,在子菜单中如果我们可以按6返回上一级菜单,按7返回主菜单,不过由于我们只有一级子菜单,因此这里按键6和7的效果是一样的。如果读者感兴趣,可以自己配置多个子菜单来验证下menu-back和menu-top的区别。
不过我们也看到了,我们上面的XML IVR极其简单,在实际的业务中,我们可能需要和外面的一些服务做交互,比如查询数据库,请求一个Web服务,等等,因此我们需要一种更灵活的方式来配置IVR应用,在此,我们介绍下使用Lua方式实现的IVR:
FreeSWITCH的mod_lua模块支持Lua语言,由于Lua是一种嵌入式语言,可以很容易嵌入到程序中,因此使用Lua给我们带来很多便捷。最新的模块已经支持Lua 5.2。下面我们用Lua来实现一遍上面的welcomeIVR。
local tts_engine = "tts_commandline"
local tts_voice = "zh-CN-XiaoxiaoNeural"
session:set_tts_params(tts_engine, tts_voice)
session:setVariable("tts_engine", tts_engine)
session:setVariable("tts_voice", tts_voice)
session:answer()
session:sleep(1000)
local digits = session:playAndGetDigits(1, 4, 3, 15000, "#", "say:欢迎使用小樱桃智能语音产品,请直拨分机号,查号请拨0", "say:输入错误", "^(0|10[0-1][0-9]$)", "digits", 2000)
if digits ~= "" and digits ~= nil then
if digits == "0" then
session:transfer("1000 XML default")
else
session:transfer(digits .. " XML default")
end
else
session:speak("再见")
end
我们可以保存上述lua到FreeSWITCH的scripts目录下,命名为welcome.lua,配置完成后就可以在控制台上进行如下测试(依然呼叫1001,接听后进入ivr菜单)
freeswitch> originate user/1001 &lua(welcome.lua)
电话接听后,我们会听到“欢迎使用小樱桃智能语音产品,请直拨分机号,查号请拨0”这样的欢迎词,可以看到,欢迎词这次我们没有使用录制好的语音文件,而是使用了TTS,上述的TTS使用的是edge-tts。
我们按0#可以实现和IVR XML一样的效果,相应的按1001~1019就可以转到相应的分机路由上去。需要注意的是上述我们讲到的是要按0#,当然我们也可以只按一个0,但是需要等2秒超时,按#是为了告诉程序,我们的按键结束,可以不用等超时,程序继续往下走
下面我们介绍playAndGetDigits
digits = session:playAndGetDigits (
min_digits, max_digits, max_attempts, timeout, terminators,
prompt_audio_files, input_error_audio_files,
digit_regex, variable_name, digit_timeout,
transfer_on_failure)
需要注意的一点是在Lua中使用playAndGetDigits和在XML Dialplan中使用play_and_get_digits功能一样,只是参数稍有不同,前者参数digit_regex在variable_name之前,后者反之,读者注意不要弄反了。
对于上面的例子,可能会有读者问,上面的Lua我们可以不可以只按0,同时又不用等2秒超时,答案是肯定的。下面我们简单优化下上面的Lua脚本。
local tts_engine = "tts_commandline"
local tts_voice = "zh-CN-XiaoxiaoNeural"
session:set_tts_params(tts_engine, tts_voice)
session:setVariable("tts_engine", tts_engine)
session:setVariable("tts_voice", tts_voice)
session:answer()
session:sleep(1000)
local first_digit = session:playAndGetDigits(1, 1, 3, 15000, "#", "say:欢迎使用小樱桃智能语音产品,请直拨分机号,查号请拨0", "say:输入错误,请重新输入", "[0-1]", "first_digit", 2000)
if first_digit ~= "" and first_digit ~= nil then
if first_digit == "0" then
session:transfer("1000 XML default")
else
local remain_digits = session:playAndGetDigits(3, 3, 3, 2000, "#", "silence_stream://1000", "say:输入错误,请重新输入", "^(0[0-1][0-9]$)", "remain_digits", 2000)
session:transfer(first_digit .. remain_digits .. " XML default")
end
else
session:speak("再见")
end
上述优化过的脚本,我们可以看到,分两步来收集按键,先收第一个按键,因此min_digits和min_digits设置成1即可,这样可以避免按井号和等待按键超时。第一个按键收集之后,可以根据实际再收余下的按键。
上面我们实现了一个很简单常见的IVR场景,学会了简单的流程,读者可以结合实际,写出功能更强大的IVR脚本,好记性不如烂笔头,现在就来动手来写一个吧。
文献参考:
本文分享自 FreeSWITCH中文社区 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!