深度解析:基于 Asterisk 与 SIP 协议的私有化语音验证码呼叫原理

在实现了使用VOIP客户端拨打8000号码后,播报语音验证码的功能后,我发现了一个最大的缺点,就是这需要用户主动去操作。这对于想使用API集成无法实现。 在思考之后,我决定使用一个可以调用API就呼叫VOIP客户端,当用户接通后,播报语音验证码的功能。当实现这个功能后,它的好处是显而易见的。比如,可以集成到嵌入式,集成到第三方网站。 那么该如何实现它呢? 一、 系统原理 传统的拨号方案(Dialplan)是静态的,而 ARI 允许我们动态控制。整个“API 触发呼叫并播报”的流程如下: 触发阶段:第三方系统通过 API 向 Go 服务发送呼叫请求(包含目标 ID 和验证码)。 呼叫发起(Originate):Go 服务调用 Asterisk ARI 的 /channels 接口。此时 Asterisk 会尝试向 PJSIP 终端(或通过中继向手机)发起呼叫。 接通监听(Stasis Start):一旦用户接起电话,该通道会被移交给一个名为 Stasis 的应用。此时 Go 服务会收到一个“通道已接通”的 WebSocket 事件。 语音合成与播放:Go 服务识别到接通后,调用播报指令(可以播放预录音文件,或对接 TTS 引擎生成的语音流)。 挂断处理:播报完毕后,服务发送挂断指令,释放资源。 二、系统架构 [第三方API] --> [Go 后端服务] --(REST API)--> [Asterisk ARI] | | (WebSocket) (PJSIP/IMS) | | [接通状态回调] <--- [用户终端接听] 三、 核心代码实现 (Golang) 假设你使用了 GitHub 上的 go-ari 库。 1. 初始化 ARI 客户端 import ( "github.com/v5" "github.com/v5/client/native" ) // 连接到 Asterisk ARI cl, err := native.Connect(&native.Options{ Application: "voice-verify", // 必须与 asterisk.conf 配置一致 Username: "admin", Password: "password", URL: "http://localhost:8088/ari", }) 2. 实现呼叫并播报逻辑 这是核心逻辑:接收参数 -> 发起呼叫 -> 监听接通 -> 播放语音。 ...

2026-01-13 · 3 min · Eagle

私有化语音验证码方案:基于 Asterisk 与 Go 的 SIP 通信及 AGI 脚本实战

音频文件处理 我们使用手机上的录音机来录制音频文件。 Android 录音机录制的音频文件格式为 mp3,如果是 amr 格式,请使用豆子工具音频格式转换功能,转成 mp3 格式文件。 IOS 录音机录制的音频文件格式为 m4a,请使用豆子工具音频格式转换功能,转成 mp3 格式文件。 我们还需要使用 ffmpeg 将 mp3 文件转成 g711a 格式文件。这个 mp3 转 g711a 功能后续会集成到豆子工具中。 mp3 转 g711a 命令: ffmpeg -i test.mp3 -acodec pcm_alaw -f alaw -ac 1 -ar 8000 -vn test.alaw 使用 ffplay 播放测试 ffplay -i test.alaw -f alaw -ac 1 -ar 8000 将制作好的音频文件存放在 asterisk sounds 目录,就可以在拨号计划中使用 Playback 应用调用它了。 配置实时数据库 今天讲解 Asterisk 如何实时将 SIP 用户写入 sqlite3 数据库。 先定义数据库表结构,我当前使用的 PJSIP 协议。 CREATE TABLE ps_endpoints ( id VARCHAR(40) NOT NULL, transport VARCHAR(40), aors VARCHAR(200), auth VARCHAR(40), context VARCHAR(40), disallow VARCHAR(200), allow VARCHAR(200), direct_media varchar(5) check(direct_media in ('yes','no')), connected_line_method varchar(10) check(connected_line_method in ('invite','reinvite','update')), direct_media_method varchar(10) check(direct_media_method in ('invite','reinvite','update')), direct_media_glare_mitigation varchar(20) check(direct_media_glare_mitigation in ('none','outgoing','incoming')), disable_direct_media_on_nat varchar(5) check(disable_direct_media_on_nat in ('yes','no')), dtmf_mode varchar(20) check(dtmf_mode in ('rfc4733','inband','info')), external_media_address VARCHAR(40), force_rport varchar(5) check(force_rport in ('yes','no')), ice_support varchar(5) check(ice_support in ('yes','no')), identify_by varchar(10) check(identify_by in ('username')), mailboxes VARCHAR(40), moh_suggest VARCHAR(40), outbound_auth VARCHAR(40), outbound_proxy VARCHAR(40), rewrite_contact varchar(5) check(rewrite_contact in ('yes','no')), rtp_ipv6 varchar(5) check(rtp_ipv6 in ('yes','no')), rtp_symmetric varchar(5) check(rtp_symmetric in ('yes','no')), send_diversion varchar(5) check(send_diversion in ('yes','no')), send_pai varchar(5) check(send_pai in ('yes','no')), send_rpid varchar(5) check(send_rpid in ('yes','no')), timers_min_se INTEGER, timers varchar(20) check(timers in ('forced','no','required','yes')), timers_sess_expires INTEGER, callerid VARCHAR(40), callerid_privacy varchar(40) check(callerid_privacy in ('allowed_not_screened','allowed_passed_screened','allowed_failed_screened','allowed','prohib_not_screened','prohib_passed_screened','prohib_failed_screened','prohib','unavailable')), callerid_tag VARCHAR(40), `100rel` varchar(20) check(`100rel` in ('no','required','yes')), aggregate_mwi varchar(5) check(aggregate_mwi in ('yes','no')), trust_id_inbound varchar(5) check(trust_id_inbound in ('yes','no')), trust_id_outbound varchar(5) check(trust_id_outbound in ('yes','no')), use_ptime varchar(5) check(use_ptime in ('yes','no')), use_avpf varchar(5) check(use_avpf in ('yes','no')), media_encryption varchar(10) check(media_encryption in ('no','sdes','dtls')), inband_progress varchar(5) check(inband_progress in ('yes','no')), call_group VARCHAR(40), pickup_group VARCHAR(40), named_call_group VARCHAR(40), named_pickup_group VARCHAR(40), device_state_busy_at INTEGER, fax_detect varchar(5) check(fax_detect in ('yes','no')), t38_udptl varchar(5) check(t38_udptl in ('yes','no')), t38_udptl_ec varchar(20) check(t38_udptl_ec in ('none','fec','redundancy')), t38_udptl_maxdatagram INTEGER, t38_udptl_nat varchar(5) check(t38_udptl_nat in ('yes','no')), t38_udptl_ipv6 varchar(5) check(t38_udptl_ipv6 in ('yes','no')), tone_zone VARCHAR(40), language VARCHAR(40), one_touch_recording varchar(5) check(one_touch_recording in ('yes','no')), record_on_feature VARCHAR(40), record_off_feature VARCHAR(40), rtp_engine VARCHAR(40), allow_transfer varchar(5) check(allow_transfer in ('yes','no')), allow_subscribe varchar(5) check(allow_subscribe in ('yes','no')), sdp_owner VARCHAR(40), sdp_session VARCHAR(40), tos_audio INTEGER, tos_video INTEGER, cos_audio INTEGER, cos_video INTEGER, sub_min_expiry INTEGER, from_domain VARCHAR(40), from_user VARCHAR(40), mwi_fromuser VARCHAR(40), dtls_verify VARCHAR(40), dtls_rekey VARCHAR(40), dtls_cert_file VARCHAR(200), dtls_private_key VARCHAR(200), dtls_cipher VARCHAR(200), dtls_ca_file VARCHAR(200), dtls_ca_path VARCHAR(200), dtls_setup varchar(20) check(dtls_setup in ('active','passive','actpass')), srtp_tag_32 varchar(5) check(srtp_tag_32 in ('yes','no')), UNIQUE (id) ); CREATE INDEX ps_endpoints_id ON ps_endpoints (id); CREATE TABLE ps_auths ( id VARCHAR(40) NOT NULL, auth_type varchar(10) check(auth_type in ('md5','userpass')), nonce_lifetime INTEGER, md5_cred VARCHAR(40), password VARCHAR(80), realm VARCHAR(40), username VARCHAR(40), UNIQUE (id) ); CREATE INDEX ps_auths_id ON ps_auths (id); CREATE TABLE ps_aors ( id VARCHAR(40) NOT NULL, contact VARCHAR(40), default_expiration INTEGER, mailboxes VARCHAR(80), max_contacts INTEGER, minimum_expiration INTEGER, remove_existing varchar(5) check(remove_existing in ('yes','no')), qualify_frequency INTEGER, authenticate_qualify varchar(5) check(authenticate_qualify in ('yes','no')), UNIQUE (id) ); CREATE INDEX ps_aors_id ON ps_aors (id); PJSIP 测试数据: ...

2022-08-21 · 5 min · Eagle