You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
9.5 KiB
Go

package icsstt
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"batchmodule/icsconf"
encry "batchmodule/icsencry"
"batchmodule/icserror"
"batchmodule/icshttp"
"batchmodule/icslog"
)
type Transcript struct {
Text string `json:"text"`
StartTime int `json:"startTime"`
EndTime int `json:"endTime"`
EndPoint bool `json:"endPoint"`
}
type ResponseData struct {
Uid string `json:"uid"`
Ext string `json:"ext"`
SpeakerTag string `json:"speakerTag"`
Transcripts []Transcript `json:"transcripts"`
}
type TAMessage struct {
Cmd string `json:"cmd"` // STT 이벤트 상태 구분값 S:시작, E:종료, T:Text, P:Partial
ConnID string `json:"connid"` //Cti 생성콜 key
Tel string `json:"tel"` //고객 전화번호
Ext string `json:"ext"` //상담사 내선번호
CallStartTime string `json:"callStartTime"` //콜 시작시간 unixtime
IgGbn string `json:"igGbn"` //통화유형 0:알수없음, 1:인바운드, 2:아웃바운드
DateTime string `json:"datetime"` //STT 시작시간
Stt string `json:"stt"` //STT 결과
Dir string `json:"dir"` //STT 화자구분 R:고객, T:상담사
}
var (
conf icsconf.AppInfo
l *log.Logger
taUrl string
)
func init() {
conf = icsconf.Getconfig()
l = icslog.InitializeLogger()
taUrl = conf.Urls.TAUrl
}
func ProcessPostRequestForDataList() {
var reqDataForVoiceFile RequestData
if len(parsedRes.List) > 0 {
for _, item := range parsedRes.List {
reqDataForVoiceFile = RequestData{
StartTime: item.StartTime,
Ext: item.Ext,
ConnID: item.ConnID,
}
// Requesting For wav file and download it at local
postReqForWavFileAndDownload(reqDataForVoiceFile)
}
} else {
l.Println("No items in the datalist.")
}
}
func postReqForWavFileAndDownload(parsedRes RequestData) {
jsonData, _ := json.Marshal(parsedRes)
encryptedRequestBody := encry.Encrypting(jsonData)
url := "https://192.168.0.69:8080/sttRest.do"
method := "POST"
requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody)
response, err := icshttp.HttpRequest(url, method, requestBody)
if err != nil {
l.Println("error occured while requesting http post for datalist:", err)
return
}
st := parsedRes.StartTime
wavFilePath := conf.Directories.WAVDirectory
fileName := fmt.Sprintf(`%s/%s/%s.wav`, wavFilePath, st, parsedRes.ConnId)
file, err := os.Create(fileName)
if err != nil {
l.Println("error occured while creating file:", err)
return
}
defer file.Close()
_, err = io.Copy(file, response.Body)
if err != nil {
l.Println("error occured while writing to file:", err)
return
}
// 잘 쪼개졌다면 각 left, right을 stt 돌리기
pcmResult, folderName := devideWavTo2Pcm(fileName)
if pcmResult {
sttCallRes, err := ControlSTT(parsedRes, folderName)
if err != nil {
l.Println("runSTT() failed with the error: ", err)
}
// stt 콜이 성공이라면 pcm 파일 지우기
if sttCallRes {
pcmDir := filepath.Join(conf.Directories.PCMDirectory, st)
deletePcmFolder(pcmDir)
}
}
}
func deletePcmFolder(foldername string) (bool, *icserror.IcsError) {
fmt.Println("foldername in deletePcmFolder func: ", foldername)
if _, err := os.Stat(foldername); os.IsNotExist(err) {
l.Println("pcm folder does not exist with the dir: ", foldername) //error here
l.Println("error: ", err)
return false, icserror.ICSERRFileOpen
}
err := os.RemoveAll(foldername)
if err != nil {
l.Panicln("error occured while deleting folder: ", foldername)
return false, icserror.ICSERRWriteFile
}
l.Printf(`folder: %s has been successfully deleted`, foldername)
return true, nil
}
func devideWavTo2Pcm(fileName string) (result bool, folderName string) {
// fileName: /home/prac/svc/icsbc/voice/wav/20250913223412.wav
fileNameWithoutWav := fileName[:len(fileName)-4]
lastSlashIdx := strings.LastIndex(fileNameWithoutWav, "/")
starttime := fileNameWithoutWav[lastSlashIdx+1:] //starttime: 20250913223412
pcmDir := conf.Directories.PCMDirectory
starttimeDir := fmt.Sprintf("%s/%s", pcmDir, starttime)
os.MkdirAll(starttimeDir, os.ModePerm)
leftOutputFile := fmt.Sprintf(`%s/%s-left.pcm`, starttimeDir, starttime)
rightOutputFile := fmt.Sprintf(`%s/%s-right.pcm`, starttimeDir, starttime)
fmt.Println("inFile reading wav path: ", fileName)
inFile, err := os.Open(fileName)
if err != nil {
l.Println("failed to open input file: %v", err)
return false, ""
}
defer inFile.Close()
leftFile, err := os.Create(leftOutputFile)
if err != nil {
l.Println("failed to create left channel file: %v", err)
return false, ""
}
defer leftFile.Close()
rightFile, err := os.Create(rightOutputFile)
if err != nil {
l.Println("failed to create right channel file: %v", err)
return false, ""
}
defer rightFile.Close()
// Skip WAV header (44 bytes)
wavHeader := make([]byte, 44)
if _, err := inFile.Read(wavHeader); err != nil {
l.Println("failed to read WAV header: %v", err)
return false, ""
}
// Check if it's a valid WAV file
if string(wavHeader[:4]) != "RIFF" || string(wavHeader[8:12]) != "WAVE" {
l.Println("invalid WAV file format")
return false, ""
}
// Check if WAV file is stereo
numChannels := binary.LittleEndian.Uint16(wavHeader[22:24])
if numChannels != 2 {
l.Println("unsupported channel count: this function only supports stereo (2 channels)")
return false, ""
}
buf := make([]byte, 4) // 2 bytes per sample * 2 channels
for {
n, err := inFile.Read(buf)
if err == io.EOF {
break
}
if err != nil {
l.Println("failed to read from input file: %v", err)
return false, ""
}
if n < 4 {
l.Printf("unexpected sample size read: %d\n", n)
// Break or handle the case based on your needs
for i := n; i < 4; i++ {
buf[i] = 0
}
break
}
leftSample := buf[:2]
rightSample := buf[2:]
if err := binary.Write(leftFile, binary.LittleEndian, leftSample); err != nil {
l.Println("failed to write to left channel file: %v", err)
return false, ""
}
if err := binary.Write(rightFile, binary.LittleEndian, rightSample); err != nil {
l.Println("failed to write to right channel file: %v", err)
return false, ""
}
}
l.Println("WAV file split successfully")
return true, starttime
}
func STTController(reqDataForVoiceFile icshttp.FailedDataListReqBody, folderName string) (bool, *icserror.IcsError) {
pcmDirectory := conf.Directories.PCMDirectory
pcmDirectory = filepath.Join(pcmDirectory, folderName)
// walking the file that has each left and right pcm files
filepath.Walk(pcmDirectory, func(path string, info os.FileInfo, err error) error {
if err != nil {
l.Printf("[ERR] sttcontroller>ControlSTT()> filepath.Walk() failed. pcmDirectory: %s", pcmDirectory)
return err
}
if !info.IsDir() && filepath.Ext(path) == "left.pcm" {
stterr := connectSelvasServerRunSTT(reqDataForVoiceFile, path, "left")
if stterr != nil {
l.Println("[ERR] sttcontroller>ControlSTT()> connectSelvasServerRunSTT(). error: ", stterr)
return stterr
}
}
if !info.IsDir() && filepath.Ext(path) == "right.pcm" {
stterr := connectSelvasServerRunSTT(reqDataForVoiceFile, path, "right")
if stterr != nil {
l.Println("[ERR] sttcontroller>ControlSTT()> connectSelvasServerRunSTT(). error: ", stterr)
return stterr
}
}
return nil
})
return true, nil
}
func connectSelvasServerRunSTT(reqDataForVoiceFile icshttp.FailedDataListReqBody, filePath string, leftright string) *icserror.IcsError {
voicedata, err := os.ReadFile(filePath)
if err != nil {
return icserror.ICSERRCONFOpenFile
}
ip := conf.STT.SrcIP
port, _ := strconv.Atoi(conf.STT.Port)
connId := reqDataForVoiceFile.ConnId
ext := reqDataForVoiceFile.Ext
recordFilePath := conf.Directories.RecDirectory
dir := "R"
if strings.Contains(leftright, "right") {
dir = "T"
}
// connecting with STT server
s, sttErr := NewSTTSshort(ip, port, connId, recordFilePath, isRx, true)
if sttErr != nil {
l.Printf("sttcontroller>connectSelvasServerRunSTT> Failed to initialize Selvas STT connId(%s) - error: %s", connId, sttErr.GetMessage())
return icserror.ICSERRSTTConnectFail
}
l.Printf("sttcontroller>connectSelvasServerRunSTT> Initialized Selvas STT connId(%s) - %s:%d", connId, ip, port)
// STT 호출
sttRes, sttErr := s.SendSTT(voicedata, true, nil)
if sttErr != nil {
l.Println("[ERR] sttcontroller>connectSelvasServerRunSTT> sendSTT() failed. error: ", sttErr)
s.Close()
return icserror.ICSERRSTTSendFail
}
l.Println("sttcontroller>connectSelvasServerRunSTT> sttRes: ", sttRes)
finalSTTRes, err := s.SendSTTProcGetResult()
if err != nil {
l.Printf("[ERR] sttcontroller>connectSelvasServerRunSTT> SendSTTProcGetResult() Failed. error: %v", err)
}
// STT server and chanel close
err = s.Close()
if err != nil {
l.Println("[ERR] sttcontroller>connectSelvasServerRunSTT> s.Close() failed. err: %v", err)
}
// STT 호출 성공시 결과를 websocket, ta로 송신
if finalSTTRes != "" {
l.Printf("[DEBUG] sttcontroller>connectSelvasServerRunSTT> taUrl: %s", taUrl)
_, err := icshttp.PostProcessTA(taUrl, "E", connId, "", ext, "empIdSample", "", "", "0", finalSTTRes, dir, "1")
if err != nil {
l.Printf("[ERR] sttcontroller>connectSelvasServerRunSTT> PostProcessTA() got error : %v", err)
}
err = icshttp.SendSTTResToNockChiServer(reqDataForVoiceFile, finalSTTRes, dir)
if err != nil {
l.Printf("[ERR] sttcontroller>connectSelvasServerRunSTT> SendSTTResToNockChiServer() got error : %v", err)
}
}
return nil
}