XCC API
XCC入门
在使用XCC接口控制时,请先确保本地安装的系统已经启动了 NATS,使用以下命令可以启动 NATS。默认端口:4222。调用地址:nats://xswitch-nats:4222。
make nats
NATS 简介
NATS[^nats]是一个消息队列,它实现了 Pub/Sub(生产者-消费者)消息机制,通过它也可以实现“请求-响应”式的 RPC(Remote Procedure Call,远程过程调用)交互。
[^nats]: 参见 https://nats.io/ 。
系统配置检查
在使用该模块时需要先确认XCC 模块是否已经开启,点击【高级】⇨【模块配置】⇨【XCC】,点进去默认配置页面,如果页面详情显示“模块未加载”,则点击右侧“重载”按钮,启用该模块。
了解基本参数
XCC模块配置主要包含以下几个部分。
XCC-SETTINGS
主要参数说明
nats-url
:程序调用的NATS 的地址,如nats://127.0.0.1:4222
,如果不配置则无法连接 NATS。publish-events-subject-prefix
:事件的 Subject 前缀,如cn.xswitch.ctrl.event
,比如,程序调用应答事件将会变为cn.xswitch.ctrl.event.channel_answer
之类。cdr-subject
:CDR 专门的 Subject,用于将 CDR 送到专门的 Subject 上,比如,程序获取话单事件可直接订阅变为cn.xswitch.cdr
。debug
:调试日志级别,0~7。cdr-format
:CDR的格式,可以在CDR中选择一种格式,目前支持CUSTOM。xml‑handler‑bindings
:XML 绑定,不同的Section以│分隔,如directory│config。
XCC-BINDINGS
事件绑定。绑定的事件会向 NATS 发送,ALL
会绑定所有事件。程序如果想要订阅事件时,可如下所示:
cn.xswitch.ctrl.event.channel_answer
通话相关事件可绑定如下事件:
CHANNEL_CREATE
//通话创建事件CHANNEL_STATE
//状态CHANNEL_ANSWER
//应答CHANNEL_HANGUP_COMPLETE
//挂机结束
XCC-SUBS
向 NATS 订阅。事件订阅可以直接使用 NATS 提供的 subscribe
功能实现,可以订阅 FreeSWITCH 中原生的事件,如,订阅所有原生事件:cn.xswitch.ctrl.event.>
。事件的 Subject 以及所需的事件类型可以在系统的 xcc
配置界面上指定。原生事件以 JSON-RPC 格式发出,可以在各种语言中很简单的解析其中的内容。
XCC-CHANNEL
在此页面添加更多通道变量。
user_uuid
//当前 User 的 UUIDuser_domain
//当前 User 的 Domain
XCC-DIALPLAN
XSwitch 的mod_xcc模块中实现一个XCC Dialplan 接口,使用该 Dialplan 后,XSwitch 在每次查询 Dialplan 时都会向 Ctrl 发送request请求。
- subject:cn.xswitch.ctrl
- method:XCtrl.Dialplan
配置页面字段参数说明:
cid_name
//主叫名称cid_number
//主叫号码dst_number
//被叫号码context
//呼叫源direction
//方向,inbound 为向内,outbound 向外
XCC-CDR
订阅 cn.xswitch.cdr
时可获取到的字段,如果想要更多字段,可在此页面添加。比如开启了 cdr-subject
,则订阅 cn.xswitch.cdr
即可收到话单事件。
在通话完毕后发出。每一个 Channel 都会有一个 CDR 事件,如果参与通话的是两条腿(aleg
、bleg
),则会有两个 CDR 事件,并分别有 leg
标志。CDR 事件一般会在 Event.Destroy
之前发出。
CDR 事件默认会送到与 Event.Channel
相同的 Subject 上,但也可以通过全局配置参数 cdr-subject
配置单独的 Subject。
挂机结束后话单指定事件,默认如下字段参数说明:
caller_id_number
//主叫号码destination_number
//被叫号码uuid
//通话 uuidstart_stamp
//开始时间answer_stamp
//应答时间end_stamp
//结束时间duration
//总时长billsec
//计费时长hangup_cause
//挂机原因context
//呼叫源direction
//方向,inbound 为向内,outbound 向外caller_id_name
//主叫名称
呼入示例
场景描述:
新建一条xcc路由,执行ivr.js代码,代码无报错,打开软电话,登录已注册后的本地分机用户的账号,输入被叫号码1234,点击呼叫,呼入电话进来,开始应答,播放欢迎音"hello,你好,欢迎致电,请按1"。根据提示音进行操作,会返回相对应的json数据。
创建路由:
进入【xswitch】页面,到【呼叫】⇨【路由】中,点击【新建】
名称:xcc
被叫字冠:1234
目的地类型选择:系统
目的地:xcc
点击提交,路由就创建好了。当呼叫1234时,路由到xcc,它会固定往cn.xswitch.ctrl上发Event.Channel消息。如果想让消息发到特定的 Subject 上,目的地中可以填 xcc cn.xswitch.ctrl.你自己的Ctroller的UUID 。
呼入工作流程:
在有电话呼入时,Ctrl 在收到START消息后应该在 10 秒内调用Accept或Answer接口接管呼叫。
除了一些特殊应用(如回彩铃等),应该尽快应答。某些 PSTN 网管在应答前不允许与对方语音交互,未应答的呼叫一般也会被对方超时拆线(如 60 秒)。
应答后,在有延迟的中继网管情况下,可能出现放音的前几个字对方听不到的情况,这时可以在应答后延迟 1-2 秒再发送后续指令
为了防止 Node 崩溃时来不及发送注销消息,Node 每 20 秒发一个Event.NodeUpdate消息,该消息可以做为心跳保活消息使用。此外,该消息还携带节点当前的活动 Channel 数以及负载信息。Ctrl 可以根据该信息向“负载最轻”的 Node 发送新任务。
Ctrl 监听cn.xswitch.ctrl
Ctrl 收到Event.Channel(state = START)
Ctrl 执行Accept或Answer
Ctrl 调用 Play 或 TTS 播放欢迎音
Ctrl 调用ReadDTMF或DetectSpeech检测按键或语音
Ctrl 检查到语音后调用后续操作
在 xui/xcc-examples/nodejs 路径下,执行一个简单的来话 ivr.js 脚本
如:
js xui/xcc-examples/nodejs/ivr.js
输入node ivr.js,执行代码,确保代码执行没有错误,打开软电话,登录有本地分机用户的账号。输入1234,点击呼叫,就打出呼入的电话了。
呼入返回数据示例:
{ "jsonrpc": "2.0", "method": "Event.Channel", "params": { "node_uuid": "dd1d43df-71d8-4182-9054-9c2695cca17d", "uuid": "018ec6a3-216d-7906-a873-31d36115300b", "state": "START", "domain": "xswitch.cn", "cid_name": "1001", "cid_number": "1001", "dest_number": "1234", "bridged": false, "answered": false, "held": false, "video": false, "direction": "inbound", "create_epoch": 1712729563, "ring_epoch": 0, "caller_source": "mod_sofia", "context": "context-1", "params": { "create_epoch": "1712729563", "ring_epoch": "0" } } } { "jsonrpc": "2.0", "id": "fake-uuid-answer", "result": { "node_uuid": "dd1d43df-71d8-4182-9054-9c2695cca17d", "uuid": "018ec6a3-216d-7906-a873-31d36115300b", "code": 200, "message": "OK" } } { "jsonrpc": "2.0", "id": "fake-tts", "result": { "node_uuid": "dd1d43df-71d8-4182-9054-9c2695cca17d", "code": 202, "message": "OK", "uuid": "018ec6a3-216d-7906-a873-31d36115300b", "dtmf": "" } }
ASR测试
场景描述:
如上xcc路由已建好,就不需要建,执行asr.js代码,代码执行无报错,打开软电话,登录已注册后的本地分机用户的账号,输入被叫号码1234,点击呼叫,呼入电话进来,开始应答,播放欢迎音"您好,欢迎致电,请直接说出您想咨询的业务,或按键选择"。根据提示音进行操作,会返回相对应的json数据。
在 xui/xcc-examples/nodejs 路径下,执行 asr.js 脚本
如:
js xui/xcc-examples/nodejs/asr.js
测试ASR:输入node asr.js,确保代码执行没有错误,执行代码,打开软电话,登录有本地分机用户的账号。输入1234,点击呼叫,听到提示音后,对着话筒说"今天天气怎么样",返回如下数据。
返回数据示例:
{ "jsonrpc": "2.0", "id": "fake-tts", "result": { "data": { "engine": "ali", "text": "今天天气怎么样", "confidence": 0.863, "segment_recording_path": "/usr/local/freeswitch/storage/recordings/segment-recording-2020-ce3261b3e3c146f1832028708902749a.wav", "segment_recording_duration": 3540, "engine_data": { "header": { "namespace": "SpeechTranscriber", "name": "SentenceEnd", "status": 20000000, "message_id": "ce3261b3e3c146f1832028708902749a", "task_id": "a426f3d4618447519c9d85d1a0d15bf6", "status_text": "Gateway:SUCCESS:Success." }, "payload": { "index": 2, "time": 23500, "result": "今天天气怎么样", "confidence": 0.863, "words": [], "status": 0, "gender": "", "begin_time": 21880, "fixed_result": "", "unfixed_result": "", "stash_result": { "sentenceId": 3, "beginTime": 23500, "text": "", "fixedText": "", "unfixedText": "", "currentTime": 23500, "words": [] }, "audio_extra_info": "", "sentence_id": "2444da4bbb4a496f95e7f0b93e01faab", "gender_score": 0 }, "segment_recording_path": "/usr/local/freeswitch/storage/recordings/segment-recording-2020-ce3261b3e3c146f1832028708902749a.wav", "segment_recording_duration": 3540 }, "is_final": true, "uuid": "018ea277-d6ea-7d47-936e-c71259695ceb", "type": "Speech.End" }, "code": 200, "message": "OK", "node_uuid": "72f48b59-520c-4b93-89b7-4898adf4810f", "node_ip": "172.18.0.2", "uuid": "018ea277-d6ea-7d47-936e-c71259695ceb" } }
呼出示例
场景描述:
更换代码里面的分机号和分机,更换为自己本地已注册的分机和分机号。更换完后,执行call.js代码,代码执行无报错,会弹出应答框,有三个选项,视频应答、应答、拒绝,选择其中一个,外呼接通。
注意事项:
- 在外呼时(尤其是在对接 PSTN 网关时),Dial 接口会在收到媒体时返回(如 SIP 中的 183 消息),如果需要在应答后返回,收需要加ignore_early_media=true参数。
更改代码内容:
分机注册到 FreeSWITCH,FreeSWITCH 可以直接呼叫。需要更换自己本地的分机
user/分机号
示例:
- user/1001
更改分机号
举例: cid_number: '1001'
const dial_string = 'user/1001'; //更改为本地的分机 var rpc = default_rpc(); rpc.method = "XNode.Dial" rpc.id = 'call1'; rpc.params.destination = { global_params: { ignore_early_media: "true" }, call_params: [{ uuid: uuid, dial_string: dial_string, cid_number: '1001', //加一个分机号 params: { leg_timeout: "20", } }] }
在 xui/xcc-examples/nodejs 路径下,执行一个 call.js 脚本
如:
js xui/xcc-examples/nodejs/call.js
输入node call.js,执行代码,代码执行没有错误,外呼接通,返回如下json数据。
呼出返回数据示例:
{ "jsonrpc": "2.0", "method": "XNode.Dial", "id": "call1", "params": { "ctrl_uuid": "4cf999d4-777c-429d-ad52-be758e8e001e", "destination": { "global_params": { "ignore_early_media": "true" }, "call_params": [ { "uuid": "a78bac98-1c96-4d24-b631-efba23c5803a", "dial_string": "user/1001", "cid_number": "1001", "params": { "leg_timeout": "20" } } ] } } } { "jsonrpc": "2.0", "id": "call1", "result": { "node_uuid": "981adb0e-9776-4cb2-b61d-f4c5b86a65e7", "code": 200, "message": "OK", "cause": "SUCCESS", "uuid": "a78bac98-1c96-4d24-b631-efba23c5803a" } }
呼出场景示例
场景描述:
更换本地分机号,执行call-and-bridge2.js代码,代码执行无报错,出现"Bridge Success",代表桥接成功。
注意事项:
桥接两个uuid,两个 Channel 必须为未Bridge状态,且处于PARK状态(没有执行其它 App)。 当桥接完成后马上返回 api 调用的回复消息
- uuid:当前uuid,为a-leg。
- peer_uuid:对方uuid,为b-leg。
- flow_control:桥接成功后是否自动挂机
桥接:将两条独立的腿接到一起,使双方可以通话。
更改代码内容:
填下两个桥接的分机的分机号
const dial_string = "null/1000"; const dial_string2 = "null/1001";
在 xui/xcc-examples/nodejs 路径下,执行一个 call-and-bridge2.js 脚本
如:
js xui/xcc-examples/nodejs/call-and-bridge2.js
输入node call-and-bridge2.js,执行代码,代码没有报错
返回数据示例:
{"jsonrpc":"2.0","method":"XNode.Dial","id":"call1","params":{"ctrl_uuid":"bbae36ef-62f4-476c-a8ed-c3bffef6586c","sync":true,"destination":{"global_params":{"ignore_early_media":"true"},"call_params":[{"uuid":"dfce488d-039b-4676-8064-a2e1abaa7c16","dial_string":"null/1000"}]}}} { "jsonrpc": "2.0", "id": "call1", "result": { "node_uuid": "082b2994-48ff-4a29-85e8-8bdfe38d229e", "code": 200, "message": "OK", "cause": "SUCCESS", "uuid": "dfce488d-039b-4676-8064-a2e1abaa7c16" } } {"jsonrpc":"2.0","method":"XNode.ChannelBridge2","id":"channel-bridge","params":{"ctrl_uuid":"bbae36ef-62f4-476c-a8ed-c3bffef6586c","uuid":"dfce488d-039b-4676-8064-a2e1abaa7c16","peer_uuid":"4e37c96c-1173-4cb5-b8ed-f9f0b89e89af"}} { "jsonrpc": "2.0", "id": "channel-bridge", "result": { "node_uuid": "082b2994-48ff-4a29-85e8-8bdfe38d229e", "code": 200, "message": "Bridge Success" } }
发起一个呼叫,队列分配坐席,接收话单事件,获取话单信息
场景描述:
更换本地分机号和服务器,执行call-and-acd.js代码,代码执行无报错,出现"Success",代表呼出成功。
更改代码内容:
分机注册到 FreeSWITCH,FreeSWITCH 可以直接呼叫。需要更换自己本地的分机
user/分机号
示例:
user/1001
更改服务器
点击【XCC】⇨【XCC-SUBS】,选择订阅 FreeSWITCH 中原生的事件。
subs
向 NATS 订阅。
- name:Topic,可以是任意合法的 NATS Subject 或 Kafka Topic。其中 NATS 支持queue订阅。
- queue:如果参数存在则在 Queue 方式订阅,即在集群订阅时,同一个 Queue 的订阅只会有一个订阅者收到。
示例:
service = "cn.xswitch.node.test"
在 xui/xcc-examples/nodejs 路径下,执行一个 call-and-acd.js 脚本
如:
js xui/xcc-examples/nodejs/call-and-acd.js
输入node call-and-acd.js,执行代码,代码没有报错
返回数据示例:
{ "jsonrpc": "2.0", "id": "call1", "result": { "node_uuid": "e6378c49-fdcd-4bc1-8593-b586b86c7c32", "code": 200, "message": "OK", "cause": "SUCCESS", "uuid": "a1" } }