package tencentNlu import ( "encoding/json" "fmt" "net/http" "net/url" "speech-nlu-parse/global" "speech-nlu-parse/model" "speech-nlu-parse/pkg/logger" "speech-nlu-parse/pkg/proto" "time" "github.com/gorilla/websocket" ) // 腾讯 nlp websocket type TencentNlpWs struct { NlpWsConn *websocket.Conn AppKey string AccessToken string Auth string Dsn string Qua string HomeId string } func (this *TencentNlpWs) GetTencentDialogHandler(appkey, accessToken, tRequestId, ip, auth, dsn string) (*websocket.Conn, error) { wsUrl := fmt.Sprintf("%s/dialog", global.TencentGwSetting.Gw.GwWs) global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"wsUrl": wsUrl}, // "requestId": requestId, }).Info("GetTencentDialogHandler wsUrl.") // wsUrl = "wss://gwgray.tvs.qq.com/v2/ws/dialog" dialogUrl, err := url.Parse(wsUrl) if err != nil { // log global.Logger.WithFields(logger.Fields{ // "requestId": this.semanticeReq.Re questId, }).Errorf("GetTencentDialogHandler error. %v", err) return nil, err } timeStamp := time.Now().Unix() signature := HmacSha256AndBase64(fmt.Sprintf("appkey=%s&requestid=%s×tamp=%d", appkey, tRequestId, timeStamp), accessToken) v := url.Values{} v.Add("appkey", appkey) v.Add("requestid", tRequestId) v.Add("signature", signature) v.Add("timestamp", fmt.Sprintf("%d", timeStamp)) dialogUrl.RawQuery = v.Encode() d := websocket.Dialer{ HandshakeTimeout: 30 * time.Second, } header := http.Header{} header.Add("Content-Type", "application/json;charset=utf-8") header.Add("X-Forwarded-For", ip) header.Add("Authorization", fmt.Sprintf("Bearer %s", auth)) header.Add("DSN", dsn) conn, _, err := d.Dial(dialogUrl.String(), header) if err != nil { global.Logger.WithFields(logger.Fields{ // "requestId": this.semanticeReq.RequestId, }).Errorf("GetTencentDialogHandler Dial error. %v", err) return nil, err } return conn, nil } func (this *TencentNlpWs) Send(data interface{}) error { this.NlpWsConn.WriteJSON(data) return nil } func (this *TencentNlpWs) Recv() ([]byte, error) { _, msg, err := this.NlpWsConn.ReadMessage() return msg, err } func TansTencentNlpWsReq(appkey, dsn, qua, auth string, semanticReq *model.SemanticReq) *model.TencentNlpWs { reqBody := model.TencentNlpWs{} reqBody.Header.RequestID = semanticReq.TRequestId reqBody.Header.DSN = dsn reqBody.Header.QUA = qua reqBody.Header.AppKey = appkey var location *model.Location if semanticReq.Lbs != nil { location = &model.Location{ Longitude: float32(semanticReq.Lbs.Longitude), Latitude: float32(semanticReq.Lbs.Latitude), } // 仅使用city字段 if semanticReq.AddressInfo != nil { location.City = semanticReq.AddressInfo.City } } reqBody.Header.Location = location reqBody.Payload.CommType = semanticReq.CommType var sdkExtend *model.SDKExtend if semanticReq.SDKExtend != nil { sdkExtend = &model.SDKExtend{ CarDOA: semanticReq.SDKExtend.CarDOA, LastRspType: semanticReq.SDKExtend.LastRspType, LastRspResult: semanticReq.SDKExtend.LastRspResult, LastSessionStatus: semanticReq.SDKExtend.LastSessionStatus, LastCompleteCmdResult: semanticReq.SDKExtend.LastCompleteCmdResult, } reqBody.Payload.SDKExtend = sdkExtend } requestType := semanticReq.RequestType reqBody.Payload.RequestType = requestType // 解析responseType switch requestType { case "semantic.text": // 请求的 RequestType 标识需为 semantic.text // Common reqBody.Payload.Common.GUID = "" // 传dsn就不传guid reqBody.Payload.Common.QUA = qua reqBody.Payload.Common.AppeKey = appkey reqBody.Payload.Common.DSN = dsn reqBody.Payload.Common.CharacterID = semanticReq.CharacterID reqBody.Payload.Semantic.Query = semanticReq.Query reqBody.Payload.Semantic.Language = semanticReq.Language case "semantic.specific": // 请求的 RequestType 标识需为 semantic.specific reqBody.Payload.Semantic.Event = semanticReq.Event case "native.api.data": // Native API响应是由客户端发起的请求,通过请求向云端返回Native API数据,对应向云端的请求中 // RequestType 需指定为 native.api.data 。 reqBody.Payload.Common.GUID = "" // 传dsn就不传guid reqBody.Payload.Common.QUA = qua reqBody.Payload.Common.AppeKey = appkey reqBody.Payload.Common.DSN = dsn reqBody.Payload.Common.CharacterID = semanticReq.CharacterID // Native var native *model.Native if semanticReq.Nativate != nil { native = &model.Native{ Name: semanticReq.Nativate.Name, SeqID: semanticReq.Nativate.SeqID, Code: semanticReq.Nativate.Code, Message: semanticReq.Nativate.Message, DataVersion: semanticReq.Nativate.DataVersion, // Data 由云端和客户端一起定 } } reqBody.Payload.Native = native default: // 未知 requestType global.Logger.Errorf("error TansTencentNlpWsReq unknown requestType %v", requestType) } return &reqBody } func (this *TencentNlpWs) ParseTNluData(jsonData []byte, mid, midType, requestId, sessionId, mac string) []byte { tencentNlpWsResp := model.TencentNlpWsResp{} err := json.Unmarshal(jsonData, &tencentNlpWsResp) if err != nil { global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData Unmarshal error.") return replyWithChat(error_reply, "doudi") } if !tencentNlpWsResp.CheckHeader() { // Header不存在 global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData response Header is not exist.") return replyWithChat(error_reply, "doudi") } code := tencentNlpWsResp.GetCode() // 解析 if code != 0 { // 状态码异常 打印log global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData response code error.") return replyWithChat(error_reply, "doudi") } // 解析 ResponseType native.api.request、semantic.close if !tencentNlpWsResp.CheckPayload() { // Payload不存在 global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData response Payload is not exist.") return replyWithChat(error_reply, "doudi") } responseType := tencentNlpWsResp.GetResponseType() if responseType == "native.api.request" || responseType == "asr.stream.data" { if !tencentNlpWsResp.CheckData() { // Data不存在 global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData response Data is not exist.") return replyWithChat(error_reply, "doudi") } // 解析 params := &model.DomainParamsWs{ TencentNlpResp: &tencentNlpWsResp, // TokenSearchResponse: this.tokenSearchResponse, Mid: mid, Mac: mac, MidType: midType, Query: "", RequestId: requestId, SessionId: sessionId, HomeId: this.HomeId, AppKey: this.AppKey, } return this.ParseNativeApiRequest(params) } // 对domain进行划分 domainStr := tencentNlpWsResp.GetDomain() if domainStr == "" { // domain不存在, 打印log global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData domain is not exist.") return replyWithChat(error_reply, "doudi") } if !tencentNlpWsResp.CheckSemantic() { global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData semantic is not exist.") } var jsonByte []byte reply_when_tencent_empty = replyMessage[getRandom(len(replyMessage))] query := tencentNlpWsResp.GetQuery() params := &model.DomainParamsWs{ TencentNlpResp: &tencentNlpWsResp, // TokenSearchResponse: this.tokenSearchResponse, Query: query, Mid: mid, Mac: mac, MidType: midType, RequestId: requestId, TencentNlpWs: this, SessionId: sessionId, HomeId: this.HomeId, AppKey: this.AppKey, } if IsContain(domainStr, global.GetLimitedSetting(mid).ShieldedDomainList) { jsonByte = ShieldedDomainWs(params) return jsonByte } if value, ok := getHandlerWs(domainStr); ok { jsonByte = value(params) } else { // 其他domain进行兜底 global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{"requestBody": string(jsonData)}, "requestId": requestId, }).Error("ParseTNluData domain handler is not exist.") jsonByte = OtherDomainWs(params) } return jsonByte } // 屏蔽处理 func ShieldedDomainWs(params *model.DomainParamsWs) []byte { domainStr := params.TencentNlpResp.Payload.Data.Semantic.Results[0].Nlu.Domain intentStr := params.TencentNlpResp.Payload.Data.Semantic.Results[0].Nlu.Intent // 暂不支持此功能 resultTextStr := shielded_reply return replyWithChat(resultTextStr, domainStr+"."+intentStr) } // 不进行register func OtherDomainWs(params *model.DomainParamsWs) []byte { if !params.TencentNlpResp.CheckDm() || !params.TencentNlpResp.CheckNlu() { global.Logger.WithFields(logger.Fields{ "requestId": params.RequestId, }).Error("OtherDomainWs Dm is not exist.") return replyWithChat(error_reply, "doudi") } text := params.TencentNlpResp.GetText() domain := params.TencentNlpResp.GetDomain() intent := params.TencentNlpResp.GetIntent() var resultText string if text != "" { resultText = text return replyWithChatV2(resultText, domain, intent) } else { resultText = reply_when_tencent_empty return replyWithChat(resultText, domain) } } func (this *TencentNlpWs) GetTencentWsReq(name, seqId string, resp *proto.TencentNlpSkillResponse) (*model.TencentNlpWs, error) { var req *model.TencentNlpWs req = &model.TencentNlpWs{} req.Header.AppKey = this.AppKey req.Header.DSN = this.Dsn req.Header.QUA = this.Qua req.Payload.RequestType = "native.api.data" var native *model.Native native = &model.Native{ Name: name, SeqID: seqId, Code: 0, Message: "", DataVersion: "2.0", // 理论上由技能服务返回 } native.Data.DataArr = make([]map[string]string, 0) result := resp.GetResult() for i := 0; i < len(result); i++ { // result[i].TypeUrl, 后续考虑一下标识 var jsonData map[string]string err := json.Unmarshal(result[i].Value, &jsonData) if err != nil { return nil, err } native.Data.DataArr = append(native.Data.DataArr, jsonData) } req.Payload.Native = native return req, nil } func (this *TencentNlpWs) WsSend(body *model.TencentNlpWs) { this.Send(body) } func (this *TencentNlpWs) ParseNativeApiRequest(params *model.DomainParamsWs) []byte { var res model.ResponseBody if !params.TencentNlpResp.CheckHeader() || !params.TencentNlpResp.CheckNative() { // 响应体内容缺少 global.Logger.WithFields(logger.Fields{ "requestId": params.RequestId, }).Error("ParseNativeApiRequest error Header or CheckNative not exist.") return replyWithChat(error_reply, "doudi") } native := params.TencentNlpResp.GetNativate() if IsNativateClondHandle(params) { nativeName := native.Name switch nativeName { case "Native://Tencent.Alarm.SearchAlarm", "Native://Tencent.Reminder.CollectPermissionInfo", "Native://Tencent.Reminder.SearchNote": tencentNlpSkill := TencentNlpSkill{} nativeReq, err := tencentNlpSkill.GetNativeReq(params.Mac, params.HomeId, params.RequestId, params.TencentNlpResp) if err != nil { global.Logger.WithFields(logger.Fields{ "requestId": params.RequestId, }).Errorf("tencentNlpSkill.GetNativeReq error. %v", err) return replyWithChat(error_reply, "doudi") } resp, err := tencentNlpSkill.Req(params, nativeReq) if err != nil { global.Logger.WithFields(logger.Fields{ "requestId": params.RequestId, }).Errorf("tencentNlpSkill.Req error. %v", err) return replyWithChat(error_reply, "doudi") } if !CheckStatusCode(resp) { global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{ "tencentNlpSkillRespBody": resp, // "query": data.Query, }, "requestId": params.RequestId, "mac": params.Mac, "mid": params.Mid, "vender": params.MidType, "sessionId": params.SessionId, }).Info("tencentNlpSkill.Req code error.") return replyWithChat(error_reply, "doudi") } wsReqBody, err := this.GetTencentWsReq(native.Name, native.SeqID, resp) if err != nil { global.Logger.WithFields(logger.Fields{ "requestId": params.RequestId, }).Errorf("this.GetTencentWsReq error. %v", err) return replyWithChat(error_reply, "doudi") } this.WsSend(wsReqBody) tRequestBody, _ := json.Marshal(wsReqBody) global.Logger.WithFields(logger.Fields{ "data": map[string]interface{}{ "tencentNluParseStreamBody": string(tRequestBody), // "query": data.Query, }, "requestId": params.RequestId, "mac": params.Mac, "mid": params.Mid, "vender": params.MidType, "sessionId": params.SessionId, }).Info("TencentNluParseStream tencentNluWs.Send.") // 查询并返回 // this.Send() return []byte("") } } code := params.TencentNlpResp.GetCode() msg := params.TencentNlpResp.GetMessage() res.Header.Semantic.Code = code res.Header.Semantic.Msg = msg payloadData := &model.PayloadData{} payloadData.Class = params.TencentNlpResp.GetClass() payloadData.Native = model.PayloadDataNative{ Name: native.Name, SeqID: native.SeqID, Timeout: native.Timeout, Param: native.Param, } res.Header.Semantic.Data = payloadData resByte, err := json.Marshal(res) if err != nil { global.Logger.WithFields(logger.Fields{ "requestId": params.RequestId, }).Errorf("Marshal error. %v", err) return replyWithChat(error_reply, "doudi") } return resByte } // 根据机型和appkey判断 func IsNativateClondHandle(params *model.DomainParamsWs) bool { var flag bool flag = false if params.Mid == "10f05" && params.MidType == "7e000024" && params.AppKey == "a8814080829e11eda5c8e38bb1008e50" { flag = true } else if params.Mid == "10f05" { if params.MidType == "87654321" { flag = true } } return flag } func CheckStatusCode(resp *proto.TencentNlpSkillResponse) bool { if resp == nil { return false } if resp.GetStatus().GetCode() != 0 { return false } return true }