FreeSWITCH用Lua脚本实现IVR功能

什么是IVR

在我们的日常生活中,我们拨打10000或者10086后经常会出现一段提示音,该提示音告诉我们按0会进入什么功能,按1会进入什么功能,这就是IVR,在下面的示例中我们使用FreeSWITCH的Lua脚本实现这个IVR功能。

示例

我们接下来实现一个充值业务,流程如下图所示。

录音

我们录制了几个声音文件,用于在lua脚本中提示用户时使用

Lua脚本如下

lua 复制代码
error_prompt = "/root/ivr_audio/input_error.wav" -- 用户按键错误提示
account = ""  -- 账号
password = ""  -- 密码
digits = ""   -- 按键数字
balance = 100  -- 余额
charge = 100 -- 充值卡上的金额
card = ""  -- 卡号
checkPress1_chargePress2 = "/root/ivr_audio/checkPress1_chargePress2.wav"  -- 查询请按1,充值请按2语音文件
input_account = "/root/ivr_audio/inputAccount.wav"  -- 请输入账号,以井号结束
input_password = "/root/ivr_audio/inputPassword.wav" -- 请输入密码,以井号结束
input_charge_cardnumber = "/root/ivr_audio/inputChargeCardNumber.wav"  -- 请输入卡号, 以井号结束
confirmPress1_returnPress2 = "/root/ivr_audio/confirmPress1_returnPress2.wav"  -- 确认请按1,返回请按2

-- 充值操作
function do_charge(account, charge) 
  balance = balance + charge -- 将充值的金额加到了余额上
  return balance
end

-- 主菜单
function main_menu()
  -- 在进入主菜单前判断下当前通话是否还存在
  if not session:ready() then return end

  -- playAndGetDigits参数解析
  -- 第一个1表示最少输入1位数字
  -- 第二个1表示最多输入1位数字
  -- 第三个参数3表示最多可以尝试输入3次
  -- 5000表示收集所有号的超市时间
  -- # 表示输入结束提示符,当我们输入一位数字以后按#结束
  -- checkPress1_chargePress2 为提示用户的提示音
  -- error_prompt 为当用户输入错误以后的提示音
  -- "^1|2$" 为正则匹配用户的输入按键,匹配下是否符合我们的正则表达式
  digits = session:playAndGetDigits(1, 1, 3, 5000, "#", checkPress1_chargePress2, error_prompt, "^1|2$")

  session:execute("log", "INFO main_menu:" .. digits)  -- 打印日志

  if not (digits == "") then
    ask_account(digits)  -- 用户输入按键了
  else
    goodbye()  -- 没有输入按键,再见
  end
end

-- 查询账户
function ask_account(service_type)
  if not session:ready() then return end -- 检查通话是否还是存在的

  -- 获取用户的账户
  digits = session:playAndGetDigits(4, 5, 3, 5000, "#", input_account, error_prompt, "\\d{4}$")

  session:execute("log", "INFO account:"  .. digits);

  if not (digits == "") then
    account = digits
    if (service_type == "1") then  -- 查询业务
      ask_account_password()
    else
      ask_card()  -- 充值业务
    end
  else
    goodbye()
  end
end

function ask_account_password()
  if not session:ready() then return end
  
  -- 获取密码
  digits = session:playAndGetDigits(4, 5, 3, 5000, "#", input_password, error_prompt, "\\d{4}$")

  session:execute("log", "INFO account password: " .. digits)

  if not (digits == "") then  -- 用户输入了密码,检查下密码是否正确
    password = digits
    check_account_password()
  else
    goodbye()
  end
end

-- 充值业务
function ask_card()
  if not session:ready() then return end

  -- 获取卡号
  digits = session:playAndGetDigits(4, 5, 3, 5000, "#", input_charge_cardnumber, error_prompt, "\\d{4}$")

  session:execute("log", "INFO card number: " .. digits)

  if not (digits == "") then  -- 用户输入了卡号
    card = digits
    check_account_card()
  else
    goodbye()
  end
end

-- 查询余额
function check_account_password()
  if not session:ready() then return end

  if (account == "1111" and password == "1111") then  -- 检查用户的账户和密码是否正确,正确的查询出现在的余额告诉用户
    session:speak("The balance is" .. balance)
    session:sleep(500)  -- sleep 500ms后返回主菜单
    main_menu()
  else
    session:speak("Input error, please re-enter")  -- 输入错误,返回主菜单
    main_menu()
  end
end

-- 充值

function check_account_card()
  if not session:ready() then return end

  if (account == "1111" and card == "2222") then 
    session:speak("You want to charge" .. charge)
    
    -- 获取用户是否充值的按键, 确认充值按1,返回按2
    digits = session:playAndGetDigits(1, 1, 3, 10000, "#", confirmPress1_returnPress2, error_prompt, "^[12]$")

    if digits == "1" then
      balance = do_charge(account, charge)
      session:speak("charge successful, charge is" ..  charge .. ", balance is" .. balance ); 
      main_menu()  -- 充值成功,返回主菜单
    else
      if digits == "2" then
        session:sleep(500)
        main_menu()  -- 用户按2取消充值, 返回主菜单
      else 
        goodbye()
      end
    end

  else
    session:speak("Input error, please re-enter")  -- 输入错误,返回充值业务
    ask_account(2)
  end
end

-- 挂机函数
function goodbye()
  if not session:ready() then return end

  session:speak("Goodbye")
  session:hangup()  -- 挂机
end

-- 设置TTS语音参数
session:set_tts_params("cepstral", "David")

session:answer()  -- 应答
session:speak("welcome")

main_menu()

加载TTS模块

在测试之前我们先要加载TTS模块,具体参考FreeSWITCH配置TTS模块,这一步也可省略,使用session:streamFile播放声音也可以。

测试

添加一个extension,该分机匹配6666号码, 并且修改Dialplan

xml 复制代码
<extension name="ivr-test">
  <condition field="destination_number" expression="^6666$">
     <action application="lua" data="/usr/local/freeswitch/test/ivr.lua"/>
  </condition>
</extension>

重新扫描配置文件

sh 复制代码
sofia profile internal rescan 

拨打6666号码测试

可以看到呼叫走我们写的Lua脚本了

输入1,我们查询下账号余额

sh 复制代码
freeswitch@debianh61> 2025-04-06 17:34:57.564651 99.37% [WARNING] switch_core_file.c:463 File has 2 channels, muxing to 1 channel will occur.
2025-04-06 17:35:01.664651 99.40% [INFO] switch_channel.c:528 RECV DTMF 1:960
EXECUTE [depth=0] sofia/internal/[email protected] log(INFO main_menu:1)
2025-04-06 17:35:01.664651 99.40% [INFO] mod_dptools.c:1865 main_menu:1
2025-04-06 17:35:01.664651 99.40% [WARNING] switch_core_file.c:463 File has 2 channels, muxing to 1 channel will occur.
2025-04-06 17:35:07.384652 99.30% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:07.724620 99.30% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:08.144621 99.30% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:08.444653 99.30% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:09.624621 99.30% [INFO] switch_channel.c:528 RECV DTMF #:960
EXECUTE [depth=0] sofia/internal/[email protected] log(INFO account:1111)
2025-04-06 17:35:09.624621 99.30% [INFO] mod_dptools.c:1865 account:1111
2025-04-06 17:35:09.624621 99.30% [WARNING] switch_core_file.c:463 File has 2 channels, muxing to 1 channel will occur.
2025-04-06 17:35:14.464653 99.23% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:14.764651 99.23% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:15.064653 99.23% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:15.444621 99.23% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:35:16.264651 99.23% [INFO] switch_channel.c:528 RECV DTMF #:960
EXECUTE [depth=0] sofia/internal/[email protected] log(INFO account password: 1111)
2025-04-06 17:35:16.264651 99.23% [INFO] mod_dptools.c:1865 account password: 1111

输入2,我们充值

sh 复制代码
freeswitch@debianh61> 2025-04-06 17:36:51.964621 99.40% [INFO] switch_channel.c:528 RECV DTMF 2:960
EXECUTE [depth=0] sofia/internal/[email protected] log(INFO main_menu:2)
2025-04-06 17:36:51.964621 99.40% [INFO] mod_dptools.c:1865 main_menu:2
2025-04-06 17:36:51.964621 99.40% [WARNING] switch_core_file.c:463 File has 2 channels, muxing to 1 channel will occur.
2025-04-06 17:36:56.344627 99.37% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:36:56.744626 99.37% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:36:57.204626 99.37% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:36:57.624651 99.37% [INFO] switch_channel.c:528 RECV DTMF 1:960
2025-04-06 17:36:58.364612 99.37% [INFO] switch_channel.c:528 RECV DTMF #:960
EXECUTE [depth=0] sofia/internal/[email protected] log(INFO account:1111)
2025-04-06 17:36:58.364612 99.37% [INFO] mod_dptools.c:1865 account:1111
2025-04-06 17:36:58.364612 99.37% [WARNING] switch_core_file.c:463 File has 2 channels, muxing to 1 channel will occur.
2025-04-06 17:37:03.604651 99.37% [INFO] switch_channel.c:528 RECV DTMF 2:960
2025-04-06 17:37:03.984651 99.37% [INFO] switch_channel.c:528 RECV DTMF 2:960
2025-04-06 17:37:04.324652 99.33% [INFO] switch_channel.c:528 RECV DTMF 2:960
2025-04-06 17:37:04.664627 99.33% [INFO] switch_channel.c:528 RECV DTMF 2:960
2025-04-06 17:37:05.444621 99.73% [INFO] switch_channel.c:528 RECV DTMF #:960
EXECUTE [depth=0] sofia/internal/[email protected] log(INFO card number: 2222)
2025-04-06 17:37:05.444621 99.73% [INFO] mod_dptools.c:1865 card number: 2222
2025-04-06 17:37:16.084640 99.27% [WARNING] switch_core_file.c:463 File has 2 channels, muxing to 1 channel will occur.
2025-04-06 17:37:22.624621 99.20% [INFO] switch_channel.c:528 RECV DTMF 1:960

参考资料

相关推荐
Code哈哈笑3 小时前
【图书管理系统】用户注册系统实现详解
数据库·spring boot·后端·mybatis
用手手打人3 小时前
SpringBoot(一)--- Maven基础
spring boot·后端·maven
Code哈哈笑4 小时前
【基于Spring Boot 的图书购买系统】深度讲解 用户注册的前后端交互,Mapper操作MySQL数据库进行用户持久化
数据库·spring boot·后端·mysql·mybatis·交互
Javatutouhouduan5 小时前
线上问题排查:JVM OOM问题如何排查和解决
java·jvm·数据库·后端·程序员·架构师·oom
多多*5 小时前
Spring之Bean的初始化 Bean的生命周期 全站式解析
java·开发语言·前端·数据库·后端·spring·servlet
Villiam_AY6 小时前
Go 后端中双 token 的实现模板
开发语言·后端·golang
拾贰_C10 小时前
【SpringBoot】MyBatisPlus(MP | 分页查询操作
java·spring boot·后端·spring·maven·apache·intellij-idea
就叫飞六吧15 小时前
Spring Security 集成指南:避免 CORS 跨域问题
java·后端·spring
冼紫菜17 小时前
[特殊字符]CentOS 7.6 安装 JDK 11(适配国内服务器环境)
java·linux·服务器·后端·centos
秋野酱18 小时前
Spring Boot 项目的计算机专业论文参考文献
java·spring boot·后端