package icshttp import ( "crypto/rand" "encoding/json" "fmt" "io/ioutil" "math/big" "net/http" "strings" "sync" "time" "gitlab.com/cinnamon/voiceagent/icsconf" "gitlab.com/cinnamon/voiceagent/icslog" "gitlab.com/cinnamon/voiceagent/icspacketer" "gitlab.com/cinnamon/voiceagent/icspacketparser" ) const HANDLE_NUM = 1024 const ( TTS_FUNC1 = iota TTS_FUNC2 TTS_FUNC3 TTS_FUNC4 BOT_FUNC ) type Handler struct { r *Router } type handleInfo struct { id int method string pattern string h http.HandlerFunc } type Resquest struct { Token string `json:"token"` OprMngCode string `json:"oprMngCode"` Method string `json:"method"` TalkText string `json:"talkText"` CallId string `json:"callId"` TelNo string `json:"telNo"` MentType string `json:"mentType"` RecordFilePath string `json:"recordFilePath"` } type Response struct { ResultCode int `json:"resultCode"` Token string `json:"token"` Action string `json:"action"` AnounceMents string `json:"announceMents"` Data ResData `json:"DATA"` } type ResData struct { BargeIn string `json:"bargeIn"` RecodingFile string `json:"recodingFile"` SttMaxTime int `json:"sttMaxTime"` MaxDigit int `json:"maxDigit"` MinDigit int `json:"minDigit"` DigitTerm int `json:"digitTerm"` TelNo string `json:"telNo"` } type ResStatus struct { Count int Status string NotUnderstand int } type ScenarioSession struct { session map[string]ResStatus m *sync.Mutex } // var scenarioSession map[string]ResStatus var handles []*handleInfo var session []*ScenarioSession func init() { handles = make([]*handleInfo, HANDLE_NUM) // session := make(map[string]ResStatus) // session init scenarioSession := new(ScenarioSession) scenarioSession.session = make(map[string]ResStatus, 200) scenarioSession.m = &sync.Mutex{} // session = make(map[string]ResStatus) // session init //TTS handles[TTS_FUNC1] = &handleInfo{method: "GET", pattern: "/tts/1", h: TTSFunc1} handles[TTS_FUNC2] = &handleInfo{method: "GET", pattern: "/tts/2", h: TTSFunc2} handles[TTS_FUNC3] = &handleInfo{method: "POST", pattern: "/tts/3", h: TTSFunc3} handles[TTS_FUNC4] = &handleInfo{method: "POST", pattern: "/tts/4", h: TTSFunc4} // bot sinario handles[BOT_FUNC] = &handleInfo{method: "POST", pattern: "/platform/api/call/process", h: scenarioSession.BOTPFunc} } func BuildHandler(r *Router) { for iter, handle := range handles { if handle != nil { //fmt.Println(handle) handle.id = iter r.HandleFunc(handle.method, handle.pattern, handle.h) } else { break } } } ///////////////////////////////////////////// //TTS func TTSFunc1(w http.ResponseWriter, r *http.Request) { h := icspacketparser.NewHTTP() err := icspacketer.PutPacket(r, &h) if err != nil { fmt.Println(err) } fmt.Fprintln(w, "TTS func 1", r) } func TTSFunc2(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "TTS func 2", r) } //post func TTSFunc3(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "TTS func 3", r) body := make([]byte, r.ContentLength) r.Body.Read(body) fmt.Println("TTS func 3", r.Method, r.URL) for iter, v := range r.Header { fmt.Println(iter, v) } fmt.Println(string(body)) } //post func TTSFunc4(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "TTS func 4", r) } ///////////////////////////////////////////// //BOT func (s *ScenarioSession) BOTPFunc(w http.ResponseWriter, r *http.Request) { // get scenario config l := icslog.GetIcsLog() conf := icsconf.GetIcsConfig() scnarioConf := conf.ScenarioConfig // Request json parsing request := Resquest{} resBody, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Println(err) } defer r.Body.Close() err = json.Unmarshal(resBody, &request) if err != nil { l.Printf(icslog.LOG_LEVEL_INFO, -1, "response unmarshal error!!!!! ") } agent := strings.SplitN(request.CallId, "@", 2) fmt.Printf(">>>>>> DATA INFO - method [%s], agent [%s], token [%s]\n", request.Method, agent, request.Token) l.Printf(icslog.LOG_LEVEL_INFO, -1, ">>>>>> DATA INFO - method [%s], agent [%s], token [%s] ", request.Method, agent, request.Token) //////////////////////////////////// //////// SCENARIO MAPPING/////////// //////////////////////////////////// response := new(Response) // 현재 들어온 method로 분기 // next 시나리오로 넘겨주기 fmt.Printf("response %+v", request) if request.Method == "HANGUP" { response.ResultCode = 200 response.Token = request.Token response.Action = "" response.AnounceMents = "" response.Data.BargeIn = "" response.Data.RecodingFile = "" response.Data.SttMaxTime = 10 } else if request.Method != "INIT" && request.TalkText == "" { response.ResultCode = 200 response.Token = request.Token if s.session[request.Token].NotUnderstand == 3 { response.AnounceMents = "잘 이해하지 못했습니다. 정확한 상담을 위해 매장으로 연결해드릴게요." response.Action = "END" } else { response.AnounceMents = "잘 이해하지 못했습니다. 다시 말씀해주세요." response.Action = "STT" s.session[request.Token] = ResStatus{Count: 1, Status: scnarioConf.Order[0], NotUnderstand: s.session[request.Token].NotUnderstand + 1} } response.Data.BargeIn = "" response.Data.RecodingFile = "" response.Data.SttMaxTime = 10 } else { if request.Method == "HANGUP" { s.ByeCheck(request.Method, request.Token) } else { switch request.Method { case "INIT": token := createToken(request.CallId) response.ResultCode = 200 response.Token = token response.Action = scnarioConf.Order[1] response.AnounceMents = scnarioConf.Value[0] response.Data.BargeIn = scnarioConf.BargeIn[0] response.Data.RecodingFile = "Y" response.Data.SttMaxTime = scnarioConf.SttMaxTime[0] s.m.Lock() s.session[token] = ResStatus{Count: 1, Status: scnarioConf.Order[0], NotUnderstand: 0} s.m.Unlock() case "STT": response.ResultCode = 200 response.Token = request.Token response.Action = scnarioConf.Order[s.session[request.Token].Count+1] response.AnounceMents = scnarioConf.Value[s.session[request.Token].Count] response.Data.BargeIn = scnarioConf.BargeIn[s.session[request.Token].Count] response.Data.RecodingFile = "Y" response.Data.SttMaxTime = scnarioConf.SttMaxTime[s.session[request.Token].Count] s.m.Lock() s.session[request.Token] = ResStatus{Count: s.session[request.Token].Count + 1, Status: scnarioConf.Order[s.session[request.Token].Count+1]} s.m.Unlock() case "DTMF": response.ResultCode = 200 response.Token = request.Token response.Action = scnarioConf.Order[s.session[request.Token].Count+1] response.AnounceMents = scnarioConf.Value[s.session[request.Token].Count] response.Data.BargeIn = "N" response.Data.RecodingFile = "Y" response.Data.SttMaxTime = 3 s.m.Lock() s.session[request.Token] = ResStatus{Count: s.session[request.Token].Count + 1, Status: scnarioConf.Order[s.session[request.Token].Count+1]} s.m.Unlock() case "REFER": response.ResultCode = 200 response.Token = request.Token response.Action = "END" response.AnounceMents = "네. 매장으로 연결해드릴게요." response.Data.BargeIn = "" response.Data.RecodingFile = "" response.Data.SttMaxTime = 0 response.Data.TelNo = request.TelNo s.m.Lock() s.session[request.Token] = ResStatus{Count: s.session[request.Token].Count + 1, Status: scnarioConf.Order[s.session[request.Token].Count+1]} s.m.Unlock() case "BYE": // 전화 종료 요청 response.ResultCode = 200 response.Token = request.Token response.Action = "END" response.AnounceMents = "접수가 완료되었습니다." response.Data.BargeIn = "Y" response.Data.RecodingFile = "Y" response.Data.SttMaxTime = 0 // case "HANGUP": // response.ResultCode = 200 // response.Token = request.Token // response.Action = "" // response.AnounceMents = "" // response.Data.BargeIn = "" // response.Data.RecodingFile = "" // response.Data.SttMaxTime = 0 // if ByeCheck(request.Method, request.Token) { // } } } } // response marshal resMarshal, jerr := json.Marshal(response) if jerr != nil { fmt.Println("Json Marshal error ", jerr) } l.Printf(icslog.LOG_LEVEL_INFO, -1, "Send Message\n %s", string(resMarshal)) if conf.TimeSleep.Value { time.Sleep(time.Second * time.Duration(time.Duration(conf.TimeSleep.TimeSleep))) } // send response fmt.Fprintln(w, string(resMarshal)) } func createToken(callId string) string { token := "" // length := []int{8, 4, 4, 4, 12} // codeAlphabet := "abcdefghijklmnopqrstuvwxyz" // codeAlphabet += "0123456789" // for i, v := range length { // fmt.Println(i, v) // for j := 0; j < v; j++ { // token += string(codeAlphabet[cryptoRandSecure(int64(len(codeAlphabet)))]) // } // if v != 12 { // token += "-" // } // } times := fmt.Sprintf("%d", time.Now().UnixNano()/1000000) tokens := strings.Split(callId, "@")[0] + times token = tokens[0:8] + "-" + tokens[8:12] + "-" + tokens[12:16] + "-" + tokens[16:20] + "-" + tokens[20:32] return token } func cryptoRandSecure(max int64) int64 { nBig, err := rand.Int(rand.Reader, big.NewInt(max)) if err != nil { fmt.Println(err) } return nBig.Int64() } func (s *ScenarioSession) ByeCheck(method string, token string) { l := icslog.GetIcsLog() // fmt.Printf(">>>>>> SESSION BYE INFO - token [%s]\n", token) l.Printf(icslog.LOG_LEVEL_INFO, -1, ">>>>>> SESSION BYE INFO - token [%s] ", token) s.m.Lock() delete(s.session, token) s.m.Unlock() }