语音验证码我想多数人都有接触过,和普通短信验证码类似,只是是用电话的方式播报验证码,好处主要有以下几点:

  1. 无内容过滤和审查,不像短信可能会触发关键字检测
  2. 过滤掉非正常用户,如接码平台等
  3. 送达率高,不容易被客户手机防骚扰等安全软件屏蔽

当然也有缺点,主要是通道接通率的问题:

  1. 可能因为使用viop供应商的通道问题,导致主叫显示号码是全球各国家的号码,被供应商和手机安全软件屏蔽或标记风险
  2. 同一主叫显示号码过于频繁会导致接通率低

下面就用FreePBX为例,说一下如何用asterisk做一个语音验证码服务,主要分为以下几步:

  1. 使用voip通道拨打客户电话号码
  2. 接通后播放欢迎语音和验证码,验证码循环播放一次,然后播放再见语音,并挂机

1. 制作音频文件

首先需要制作欢迎语音和再见语音,还有数字0-9的语音,可以自己录制或者使用在线TTS服务,如科大讯飞等,我用的是这个:http://tools.bugscaner.com/tts/
制作好语音之后,格式需要转换为gsm格式,否则asterisk无法播放,可以使用sox命令

sox hello.mp3 -r 8000 -c1 hello.gsm

将转换完成后的欢迎语、数字、再见等gsm文件放到目录 /var/lib/asterisk/sounds/cn/

2. 定制asterisk拨号计划
dianplan是定制呼叫流程,可以自己定制,编辑文件:/etc/asterisk/extensions_custom.conf,加入:

[app-code]
;先把时间、手机号码、验证码打到日志中
exten = _XX.,1,NoOp(app Voice Verify Code ${TRUNK_RING_TIMER},${DIAL_TRUNK_OPTIONS},${EXTEN} ---- ${code} )
exten = _XX.,n,Wait(1)
;设置语言为中文
exten = _XX.,n,Set(CHANNEL(language)=cn)
;播放hello.gsm 语音内容为:尊敬的XX网客户您好
exten = _XX.,n,Playback(hello)
;播放before.gsm,语音内容为:你的验证码是
exten = _XX.,n,Playback(before)
;播放验证码数字
exten = _XX.,n,SayDigits(${code})
;循环播放一次
exten = _XX.,n,Wait(1)
exten = _XX.,n,Playback(before)
exten = _XX.,n,SayDigits(${code})
;播放goodby.gsm 语音内容为:您的验证码播放完毕,再见
exten = _XX.,n,Playback(goodbye)
exten = _XX.,n,Hangup

需要reload asterisk的dianplan :

asterisk -rx "dianplan reload"

3.定制call files脚本测试
call files是最简单的方式,不需要写复杂代码实现,需要根据自己的trunk进行编写,例如我的是这样:

channel: SIP/Skype/+86136xxxxxxxx
;channel: IAX2/to196/+86136xxxxxxxx

Extension: +86136xxxxxxxx
MaxRetries: 0
RetryTime: 30000
Context: app-code
Setvar: code=6789

将以上内容保存为voicecode.call,移动到/var/spool/asterisk/outgoing/,正常情况系统就会自己拨打电话并播放验证码,主要流程就跑通了。当然这是手动的情况,程序调用需要自己写个api服务,按上面的模板生成call file脚本,替换里面的电话号码和验证码即可。

4.随机callerID设置

上面的流程跑通后,实际测试会发现接通率可能会不高,主要原因是国外的viop服务商的主叫号码有限,如果同一主叫号码非常频繁拨打国内号码,且通话时长很短,就会被供应商当成骚扰电话拉黑。这时候可以多买一些国外的主叫号码,做一个随机的主叫显示号码轮询。

FreePBX中具体设置方法,语音验证码使用的trunk选择允许所有cid,并设置Outbound CallerID为一个固定的字符,如:LEVEL3_CID,然后和上面的做法一样,通过自定义dialplan,根据Outbound CallerID的字符进行随机选择号码呼出。

先定制一个有所有主叫号码的呼叫计划并编号,编辑: /etc/asterisk/extensions_custom.conf ,加入以下内容:

[AllLevel3Cid]

exten => PhoneNo1,1,Set(CALLERID(num)=+19366661001)
  same => n,Return
exten => PhoneNo2,1,Set(CALLERID(num)=+19366661002)
  same => n,Return
exten => PhoneNo3,1,Set(CALLERID(num)=+19366661003)
  same => n,Return
exten => PhoneNo4,1,Set(CALLERID(num)=+19366661004)
  same => n,Return
exten => PhoneNo5,1,Set(CALLERID(num)=+19366661005)
  same => n,Return

第二步使用asterisk的随机函数挑选主叫号码,并更改系统自带的拨号方案,使用挑选的号码作为trunk主叫显示号码,这一步需要hook一下trunk-predial呼叫计划,在[AllLevel3Cid]这个下面继续添加:

[macro-dialout-trunk-predial-hook]
;打印一些日志
exten => s,1,NoOp(The caller id name is: ${CALLERID(name)})
exten => s,n,NoOp(The caller id number is: ${CALLERID(num)})
exten => s,n,NoOp(The ampuser is: ${AMPUSER})
exten => s,n,NoOp(The real caller id is: ${REALCALLERIDNUM})
;判断trunk的CID是否是LEVEL3_CID
exten => s,n,GotoIf($[ "${CALLERID(name)}" = "LEVEL3_CID" ]?AllLevel3Cid)
;使用随机主叫号码,共5个号码,随机选一个做为主叫号码
exten => s,n(AllLevel3Cid),Gosub(AllLevel3Cid,PhoneNo${RAND(1,5)},1))
exten => s,n,MacroExit()
exten => s,n(noselectcid),NoOp(not selectcid ))
exten => s,n,MacroExit()

这样就可以使用随机的主叫显示号码了,同样需要reload生效:

asterisk -rx "dianplan reload"

为了提高接通率主叫号码是越多越好,也可以选择那些可以CID透传的VOIP供应商,咳咳,不敢多说 :)

参考文章:

https://www.voip-info.org/asterisk-func-rand/