preloader

  呼入需在FSGUI的路由配置中,将某个路由指向ai use mrcp类型,则会直接进入到对话流程中,其余部分,呼入呼出在接口中无区别.3.4,3.5,3.6三个service为通用service

有关智能外呼的接口如下:

1 注册获取token接口login

` curl -d ‘{“username”: “admin”, “password”: “admin”}’ -H “Content-Type:application/json” http://192.168.0.200:8085/api/login

返回值

正常:

{

  "token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MjIwNzM2ODksImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTUyMjA3MDA4OX0.x2KXWs5KNdSu2t3teGBfZMVs5oUp2zPPlgWpnfyp1pk"

}

即意味着返回了一个带token的json串

非正常返回:

{

  "Error": "Not Authorized"

}

2 发起呼叫的接口

2.1 不带最长呼叫时间,默认20分钟

curl -H "Content-Type: application/json" -d '{"Job_uuid":"feew32223dd3e32re32e32","Callernum":"18621575908",  "Calleenum":"999888","A_gatewayname":"nway1","A_effective_caller_id_num":"021168686868", "A_external":"true","Cdr_url":"http://127.0.0.1:8085/fs/cdr","Event_url":"http://127.0.0.1:8085/fs/callstate","Record_file":"mytest.wav"}'  -H "Authorization:Nway TOKEN" http://127.0.0.1:8085/api/originate_i

用前边的获取到的token来替代TOEKN这几个字符,Authorization:Nway是必须要有的。

Job_uuid: 每通呼叫都不一样的一个uuid值,最长64位
Callernum:要呼叫的号码
Calleenum:在此项目中必须为999888
A_gatewayname:如果号码需要出局,则指定这个gateway为出局的落地网关
A_effective_caller_id_num:外显号码
A_external:是否呼外线,如果为false,则只呼内线,true呼外线
Cdr_url:由系统回推话单的url
Event_url:由系统回推事件的url
Record_file:录音文件名,建议采用Job_uuid+”_”+Callernum,不需要带.wav

返回值

正常:

{
  "code": "0",
  "msg": "successed",
  "sessionid": "feew32223dd3e32re32e92"
}

非正常:

{
  "Error": "Not Authorized"
}

2.2 带最长呼叫时间长度的呼叫

curl-H"Content-Type:application/json"-d'{"Job_uuid":"1234","Callernum":"68018621575908",  "Calleenum":"999888","A_gatewayname":"yn1","A_effective_caller_id_num":"057156215110","A_external":"true","Cdr_url":"http://182.254.137.78:8090/ningweifs/cdr","Event_url":"http://182.254.137.78:8090/ningweifs/callstate","Record_file":"kdksltest.wav","Max_call_time":"0" ,"Call_timeout":"30"}'  -H"Authorization:Nway" http://127.0.0.1:8085/api/originate_i_ext

Max_call_time为最长通话时长,秒数

Call_timeout为呼叫时超时长度,秒数,比如呼30秒不接听就不呼

用前边的获取到的token来替代TOEKN这几个字符,Authorization:Nway是必须要有的。

返回值

正常:

{
  "code": "0",
  "msg": "successed",
  "sessionid": "feew32223dd3e32re32e92"
}

非正常:

{
  "Error": "Not Authorized"
}

3 需要实现的三个接口

cdr接口,event接口,人机处理接口

cdr接口用于接收通话清单

event接口用于推送通话过程中产生的各类事件

人机处理接口用于进行ASR、TTS、通信、放音等等

3.1 notify定义

      enter : 表明有电话刚进入,则我们需要进行开场白了,呼入的为:您好,有什么能帮您?    呼出的为:您好,我是xx公司,做xx的!
 
      asr_result:是由nway_power通过采集数据后送给识别引擎后识别结果送过来了
 
      vad_short_sentence_file:在mode为2时,进行放音时采集,用于更精准的打断功能
      bridge_result:转接回应
      playback_result:放音回应结果
      getdtmf_result:获取按键的回应结果

3.2 action定义

      asr:用于播放tts合成语音或放a.wav等预录制的语音
 
      hangup:用于放音后挂机
 
      bridge:转到某个座席,可为内线也可为外线
 
      vad_stop:用于在播放一个长语音时,由于识别引擎识别到人工部分说语音而中止当前的流程(内置于nway_power,不需要调用方直接调用,当有打断需求时,直接调用asr/bridge等action)
      skip: 用于在nway_power告知业务系统有vad_short_sentence_file或asr_result时,但是业务不需要打断; 或因为打断,而nway_power回了一个带 use_recognize=false的asr_result ,那么我们不需要让系统再重新去执行一个事件,使用skip

3.3 response params定义

      prompt:用于将交互时的要合成的tts或预录的语音文件传给系统,用.wav文件直接则播放和sounddir为主路径下的该预录制文件,如果是不带.wav则先tts合成再播放,如果是file_string:则顺序播放以”,”分隔的多个.wav录音文件
 
      cause:用于标明挂机原因
 
      usermsg:用户定义参数
 
      number:用于标明要呼转的号码
 
      callerid:用于标明呼出的号码,即外显号码,如果是呼入,则是呼入号码
 
     calleeid:用于标明被叫号码,如果是呼入,则是呼入的落地号码
 
      gateway:用于标明是通过哪个网关呼出,如果为空,则是呼转给内线号码
 
      max_waiting_ms:最长等待时长
 
      retry:尝试次数
 
      mode:0为只要有响动就打断,1为不打断一直到放音结束,2为做实时采集用户人工语音,并识别后送给业务层处理,按用户命令来走一下步
 
      profile: 指定使用的mrcp的profile
 
      grammar: 指定使用的mrcp服务的grammar

3.4 request params定义

message: 识别后的文字内容
errorcode:识别后的错误码等
record: 录音文件存放路径
callid:呼叫的唯一标识符
notify:通知的消息,看3.1
flowdata: 随路数据,暂时不需考虑
use_recognize:是否要识别处理,用于打断后,此句做了识别,但还是不需要回应让处理,检测到此为false,直接回应skip
file_exist: exist/lose  采集的文件存在与否,如果是送的vad_short_sentence_file,那么无此项

3.5 golang业务处理示例

这里的示例是由nway_speech来请求httpserver,即可以用java、php、c#、golang等开发一个http service,由语音系统部分来请求。

/*
名称:智能语音自动处理和应答
开发者:李浩
所有权:上海宁卫信息技术有限公司
时间:2017-10-21
*/
 
package main
 
import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
 
    "strings"
)
 
/*
if (prompt.find(".wav,")   第二个接口增加就是这里,如果有a.wav,b.wav,c.wav就会自动的认为是多条语音播放
*/
func AsrServer(w http.ResponseWriter, req *http.Request) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
            return
        }
    }()
    con, _ := ioutil.ReadAll(req.Body)
    if req != nil {
        defer req.Body.Close()
    }
 
    //fmt.Println(string(con))
    var dat map[string]interface{}
    fmt.Println("-------------------------------------------------------------------------------------------")
    fmt.Println(string(con))
    err := json.Unmarshal(con, &dat)
    if err == nil {
        if dat["notify"] != nil && dat["calleeid"] != nil && dat["callerid"] != nil && dat["callid"] != nil {
            notify := dat["notify"].(string)
            //calleeid := dat["calleeid"].(string)
            //callerid := dat["callerid"].(string)
            //callid := dat["callid"].(string)
            errorcode := 0
            if dat["errorcode"] != nil {
                errorcode = int(dat["errorcode"].(float64))
            }
            message := ""
            if dat["message"] != nil {
                message = dat["message"].(string)
            }
            use_recognize := "true"
            if dat["use_recognize"] != nil {
                use_recognize = dat["use_recognize"].(string) //如果是打断模式打断触发中断当前通话,则这里会为fasle
            }
            file_exist := "exist"
            if dat["file_exist"] != nil {
                file_exist = dat["file_exist"].(string) //当messsage为空时,可以通过这个参数来确认是因为没产生文件还是没识别内容
            }
            prompt := "您好!在吗?"
            if notify == "enter" {
                jsonStr := `{"action":"asr","flowdata":"","params":{"prompt":"/opt/nway/welcome.wav","max_waiting_ms":7000,"retry":0,"mode":2}}`
                //w.WriteHeader()
                w.Write([]byte(jsonStr))
            } else if notify == "asr_result" && use_recognize == "false" {
                jsonStr := `{"action":"skip","flowdata":"","params":{"cause":0,"usermsg":"caller response"}}`
 
                w.Write([]byte(jsonStr))
            } else if notify == "asr_result" {
 
                if errorcode == 0 {
 
                    if strings.Contains(message, "智能") {
                        prompt = "/opt/nway/ai.wav"
 
                        jsonStr := `{"action":"hangup","flowdata":"","params":{"prompt":"` + prompt + `","cause":0,"usermsg":"caller request"}}`
 
                        w.Write([]byte(jsonStr))
 
                    } else if strings.Contains(message, "呼叫") {
                        prompt = "/opt/nway/cc.wav"
 
                        jsonStr := `{"action":"hangup","flowdata":"","params":{"prompt":"` + prompt + `","cause":0,"usermsg":"caller request"}}`
 
                        w.Write([]byte(jsonStr))
                    } else if strings.Contains(message, "再见") || strings.Contains(message, "好的") {
 
                        prompt = "/opt/nway/goodbye.wav"
 
                        jsonStr := `{"action":"hangup","flowdata":"","params":{"prompt":"` + prompt + `","cause":0,"usermsg":"caller request"}}`
 
                        w.Write([]byte(jsonStr))
                    } else {
                        fmt.Println(message)
                    }
                } else if errorcode == -1 {
 
                    //prompt = "您在吗?"
                } else if errorcode == -2 || file_exist == "lose" {
                    //是不是磁盘满了
                    prompt = "/opt/nway/unknown.wav"
                }
 
                if prompt != "" {
                    jsonStr := `{"action":"asr","params":{"prompt":"` + prompt + `","max_waiting_ms":10000,"retry":0,"mode":2}}`
                    fmt.Println(jsonStr)
                    w.Write([]byte(jsonStr))
                }
            }
        } else if dat["notify"] != nil && dat["callid"] != nil {
            notify := dat["notify"].(string)
            message := ""
            if dat["message"] != nil {
                message = dat["message"].(string)
            }
            prompt := "您好!在吗?"
            if notify == "vad_short_sentence_file" {
                fmt.Println(notify)
                if strings.Contains(message, "再见") || strings.Contains(message, "拜拜") {
 
                    prompt = "/opt/nway/goodbye.wav"
 
                    jsonStr := `{"action":"hangup","flowdata":"","params":{"prompt":"` + prompt + `","cause":0,"usermsg":"caller request"}}`
 
                    w.Write([]byte(jsonStr))
                } else if strings.Contains(message, "不用") || strings.Contains(message, "好的") {
 
                    jsonStr := `{"action":"skip","flowdata":"","params":{"cause":0,"usermsg":"caller response"}}`
 
                    w.Write([]byte(jsonStr))
                } else {
                    prompt = "/opt/nway/unknown.wav"
                    jsonStr := `{"action":"asr","params":{"prompt":"` + prompt + `","max_waiting_ms":10000,"retry":0,"mode":2}}`
                    fmt.Println(jsonStr)
                    w.Write([]byte(jsonStr))
                }
 
            }
        }
 
    } else {
        fmt.Println("unmarshal event message error:", err, "  &&&    message:", string(con))
 
    }
}
 
func Start() {
    http.HandleFunc("/asr", AsrServer)
 
    err := http.ListenAndServe(":10086", nil) // http.ListenAndServeTLS(ipport, nway_path.GetCurrentDirectory()+crtfile, nway_path.GetCurrentDirectory()+keyfile, nil)
    if err != nil {
        fmt.Println("ListenAndServe: ", err)
    }
}
func main() {
    Start()
}

3.6 CDR Service

func cdr(w http.ResponseWriter, req *http.Request) {
       con, _ := ioutil.ReadAll(req.Body)
       if req != nil {
              defer req.Body.Close()
       }
 
       var dat map[string]interface{}
       err := json.Unmarshal(con, &dat)
       if err != nil {
              logs.Error(err)
              return
       }
       var dataCd map[string]interface{}
       data, err := json.Marshal(dat["callRecordCdr"])
       if err != nil {
 
              logs.Error(err)
              return
       }
       json.Unmarshal(data, &dataCd)
       var dataCdr map[string]interface{}
       datas, err := json.Marshal(dataCd["callerCdr"])
       if err != nil {
              logs.Error(err)
              return
       }
       json.Unmarshal(datas, &dataCdr)
       var cdr = ai_db.DBCdr{}
       var cdrModel = ai_db.AiCdr{}
       var runTime = ai_db.DBRunTime{}
       var rtModel = ai_db.RunTimeModel{}
       user_data := dataCdr["user_data"].(string)
       rtModel, err = runTime.RunTimeListByCallId(dataCdr["sessionid"].(string))
 
       admin, err := cdr.QueryUserInfoByOwenr(rtModel.Tpl_id)
 
       cdrModel.Account_id = admin.Id                   
       cdrModel.Callee = dataCdr["dst"].(string)        
       cdrModel.Caller = dataCdr["calleridnum"].(string)
       if cdrModel.Caller == "0000000000" {
              cdrModel.Caller = dataCdr["dst"].(string)
       }
       cdrModel.Start_time = dataCdr["calldate"].(string)
       cdrModel.End_time = dataCdr["endtime"].(string)
       cdrModel.Route_id = rtModel.Gateway_id
       cdrModel.Fee_rate = admin.Org_fee_rate
       cdrModel.Hangup_dispostion = dataCdr["hangup_dispostion"].(string)
       a, err := strconv.Atoi(dataCdr["billsec"].(string))
       cdrModel.Duration = a
       var duration = a / 60
       if a%60 > 0 {
              duration += 1
       }
       cdrModel.Bill_balance = cdrModel.Fee_rate * float32(duration)
       cdrModel.Record_base = Record_base  
       cdrModel.Record_path = dataCd["recordurl"].(string)
       cdrModel.Task_id = rtModel.Taskid
       cdrModel.Intention = rtModel.Intention
       cdrModel.Call_id = dataCdr["sessionid"].(string)
       cdrModel.Term_cause = dataCd["term_cause"].(string)
       cdrModel.Term_status = dataCd["term_status"].(string)
       cdrModel.Talk_crycle = strconv.Itoa(rtModel.Talk_crycle)
       currentTime:=time.Now()
       err = cdr.InsertCdr(cdrModel, user_data)
       if err != nil {
              logs.Error(err)
       }
       intdata,err:= runTime.SelctRunTimeByCallToInterval(rtModel.Taskid)
       time.Sleep(time.Duration(intdata) * time.Millisecond)
       err = runTime.DeleteRunTimeByCall(dataCdr["sessionid"].(string))
       if err != nil {
              logs.Error(err)
              return
       }
 
}

3.7 EVENT Service

func event(w http.ResponseWriter, req *http.Request) {
       con, _ := ioutil.ReadAll(req.Body)
       if req != nil {
              defer req.Body.Close()
       }
 
       var data map[string]interface{}
 
       err := json.Unmarshal(con, &data)
       if err != nil {
              fmt.Println(err)
       }
       var runtime = ai_db.DBRunTime{}
       model, err := runtime.RunTimeListByCallId(data["sessionid"].(string))
        
       if err != nil {
              logs.Error(err)
              return
       }
       if data["status"] == "caller-start" {
              model.Call_state = 1
       } else if data["status"] == "caller-ringing" {
              model.Call_state = 1
              err = runtime.UpdateRunTimeByCall2(model)
              if err != nil {
                     logs.Error(err)
              }
       } else if data["status"] == "caller-answered" {
              model.Call_state = 2
              err = runtime.UpdateRunTimeByCall2(model)
              if err != nil {
                     logs.Error(err)
              }
       } else if data["status"] == "caller-hangup" {
              model.Call_state = 3
              err = runtime.UpdateRunTimeByCall2(model)
              if err != nil {
                     logs.Error(err)
              }
       }
        
       var dbnumber = ai_db.DBNumber{}
       err = dbnumber.UpdateNumberByGroupId(model.Call_state, model.Number_map_id)
       if err != nil {
              logs.Error(err)
              return
       }
 
}