|
|
|
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)
|
|
|
|
|
|
|
|
response := new(Response)
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
///////////// EXCEPT ///////////////
|
|
|
|
////////////////////////////////////
|
|
|
|
if conf.ExceptTest.TestInfo.Value {
|
|
|
|
// eType := conf.ExceptTest.TestInfo.Type
|
|
|
|
|
|
|
|
if request.Method == "INIT" {
|
|
|
|
response.ResultCode = conf.ExceptTest.TestInfo.ResultCode
|
|
|
|
response.Token = conf.ExceptTest.TestInfo.Token
|
|
|
|
response.Action = conf.ExceptTest.TestInfo.Action
|
|
|
|
response.AnounceMents = conf.ExceptTest.TestInfo.Announcement
|
|
|
|
response.Data.BargeIn = conf.ExceptTest.TestInfo.Bargein
|
|
|
|
response.Data.RecodingFile = conf.ExceptTest.TestInfo.Recodingfile
|
|
|
|
response.Data.SttMaxTime = conf.ExceptTest.TestInfo.Sttmaxtime
|
|
|
|
s.session[conf.ExceptTest.TestInfo.Token] = ResStatus{Count: 1, Status: scnarioConf.Order[0], NotUnderstand: 0}
|
|
|
|
} else if request.TalkText == "ERROR_TTS" || request.TalkText == "ERROR_STT" {
|
|
|
|
response.ResultCode = 200
|
|
|
|
response.Token = request.Token
|
|
|
|
response.AnounceMents = "서비스에 문제가 발생하여 매장으로 연결해드릴게요."
|
|
|
|
response.Action = "TRANSFER"
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// switch eType {
|
|
|
|
// case "sizeup":
|
|
|
|
// response.ResultCode = 200
|
|
|
|
// response.Token = request.Token + "000000000000000000000000000000"
|
|
|
|
// response.Action = ""
|
|
|
|
// response.AnounceMents = ""
|
|
|
|
// response.Data.BargeIn = ""
|
|
|
|
// response.Data.RecodingFile = ""
|
|
|
|
// response.Data.SttMaxTime = 10
|
|
|
|
// case "sizedown":
|
|
|
|
// case "space":
|
|
|
|
// case "null":
|
|
|
|
// }
|
|
|
|
} else {
|
|
|
|
////////////////////////////////////
|
|
|
|
//////// SCENARIO MAPPING///////////
|
|
|
|
////////////////////////////////////
|
|
|
|
|
|
|
|
// 현재 들어온 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 scnarioConf.TimeValue[s.session[request.Token].Count] {
|
|
|
|
l.Printf(icslog.LOG_LEVEL_INFO, -1, "Time Sleep %d \r\n", scnarioConf.TimeSleep[s.session[request.Token].Count])
|
|
|
|
time.Sleep(time.Second * time.Duration(time.Duration(scnarioConf.TimeSleep[s.session[request.Token].Count])))
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
}
|