380 lines
12 KiB
Go
380 lines
12 KiB
Go
|
package controller
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/gin-gonic/gin"
|
||
|
"github.com/patrickmn/go-cache"
|
||
|
"github.com/tidwall/gjson"
|
||
|
"io/ioutil"
|
||
|
"kefu/common"
|
||
|
"kefu/lib"
|
||
|
"kefu/lib/wechat_kf_sdk"
|
||
|
"kefu/models"
|
||
|
"kefu/service"
|
||
|
"kefu/tools"
|
||
|
"kefu/types"
|
||
|
"kefu/ws"
|
||
|
"log"
|
||
|
"os"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// 企业微信相关
|
||
|
var retryCache = cache.New(60*time.Minute, 10*time.Minute)
|
||
|
|
||
|
func isRetry(signature string) bool {
|
||
|
var base = "retry:signature:%s"
|
||
|
key := fmt.Sprintf(base, signature)
|
||
|
_, found := retryCache.Get(key)
|
||
|
if found {
|
||
|
return true
|
||
|
}
|
||
|
retryCache.Set(key, "1", 1*time.Minute)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// 企业微信客服消息回调
|
||
|
func PostEntWechatCallback(c *gin.Context) {
|
||
|
entId := c.Param("entId")
|
||
|
kefuName := c.Param("kefuName")
|
||
|
verifyMsgSign := c.Query("msg_signature")
|
||
|
verifyTimestamp := c.Query("timestamp")
|
||
|
verifyNonce := c.Query("nonce")
|
||
|
bodyBytes, _ := ioutil.ReadAll(c.Request.Body)
|
||
|
body := string(bodyBytes)
|
||
|
log.Println("企业微信客服回调:", body, verifyMsgSign)
|
||
|
if isRetry(verifyMsgSign) {
|
||
|
log.Println("企业微信客服重试机制:", verifyMsgSign)
|
||
|
return
|
||
|
}
|
||
|
//获取配置
|
||
|
configs := models.GetEntConfigsMap(entId, "QdrantAIStatus", "chatGPTUrl", "chatGPTSecret", "kefuWeworkCorpid", "kefuWeworkSecret", "kefuWeworkToken", "kefuWeworkEncodingAESKey", "kefuWeworkNextCursor", "QiyeWechatKefuPreRobot")
|
||
|
kefuWework := wechat_kf_sdk.NewKefuWework(configs["kefuWeworkCorpid"], configs["kefuWeworkSecret"], configs["kefuWeworkToken"], configs["kefuWeworkEncodingAESKey"])
|
||
|
receiveMessage, err := kefuWework.DecryptMsg(verifyMsgSign, verifyTimestamp, verifyNonce, body)
|
||
|
if err != nil {
|
||
|
log.Println("企业微信客服解析数据失败:", err)
|
||
|
return
|
||
|
}
|
||
|
syncMsgs, err := kefuWework.SyncMsg(map[string]interface{}{
|
||
|
"token": receiveMessage.Token,
|
||
|
"cursor": configs["kefuWeworkNextCursor"],
|
||
|
})
|
||
|
if err != nil {
|
||
|
log.Println("企业微信客服获取数据失败:", err)
|
||
|
return
|
||
|
}
|
||
|
models.UpdateInsertEntConfig(entId, "企业微信客服NextCursor", "kefuWeworkNextCursor", syncMsgs.NextCursor)
|
||
|
size := len(syncMsgs.MsgList)
|
||
|
if size < 1 {
|
||
|
log.Println("企业微信客服获取数据为空", syncMsgs)
|
||
|
return
|
||
|
}
|
||
|
current := syncMsgs.MsgList[size-1]
|
||
|
log.Println("企业微信客服最新数据", current)
|
||
|
userId := current.ExternalUserid
|
||
|
kfId := current.OpenKfid
|
||
|
content := ""
|
||
|
//文本消息
|
||
|
fildDir := fmt.Sprintf("%s%d%s/", common.Upload, time.Now().Year(), time.Now().Month().String())
|
||
|
if current.Msgtype == "text" {
|
||
|
content = current.Text.Content
|
||
|
|
||
|
} else if current.Msgtype == "image" {
|
||
|
//图片消息
|
||
|
mediaId := current.Image.MediaId
|
||
|
fileName := mediaId + ".jpg"
|
||
|
isExist, _ := tools.IsFileExist(fildDir)
|
||
|
if !isExist {
|
||
|
os.Mkdir(fildDir, os.ModePerm)
|
||
|
}
|
||
|
filepath := fmt.Sprintf("%s%s", fildDir, fileName)
|
||
|
_, err := kefuWework.DownloadTempFileByMediaID(mediaId, filepath)
|
||
|
if err != nil {
|
||
|
log.Println("企业微信客服下载临时素材失败:", err)
|
||
|
return
|
||
|
}
|
||
|
content = "img[/" + filepath + "]"
|
||
|
} else if current.Msgtype == "voice" {
|
||
|
//语音消息
|
||
|
mediaId := current.Voice.MediaId
|
||
|
fileName := mediaId + ".wav"
|
||
|
isExist, _ := tools.IsFileExist(fildDir)
|
||
|
if !isExist {
|
||
|
os.Mkdir(fildDir, os.ModePerm)
|
||
|
}
|
||
|
filepath := fmt.Sprintf("%s%s", fildDir, fileName)
|
||
|
_, err := kefuWework.DownloadTempFileByMediaID(mediaId, filepath)
|
||
|
if err != nil {
|
||
|
log.Println("企业微信客服下载临时素材失败:", err)
|
||
|
return
|
||
|
}
|
||
|
if configs["chatGPTUrl"] != "" && configs["chatGPTSecret"] != "" {
|
||
|
gpt := lib.NewChatGptTool(configs["chatGPTUrl"], configs["chatGPTSecret"])
|
||
|
response, err := gpt.GetWhisper(filepath)
|
||
|
if err == nil {
|
||
|
log.Println(response)
|
||
|
content = gjson.Get(response, "text").String()
|
||
|
} else {
|
||
|
log.Println(err)
|
||
|
content = "audio[/" + filepath + "]"
|
||
|
}
|
||
|
} else {
|
||
|
content = "audio[/" + filepath + "]"
|
||
|
}
|
||
|
|
||
|
} else if current.Msgtype == "event" {
|
||
|
//事件消息
|
||
|
//进入会话
|
||
|
if current.Event.EventType == "enter_session" {
|
||
|
welcomeCode := current.Event.WelcomeCode
|
||
|
//发送欢迎语
|
||
|
welcomes := models.FindWelcomesByKeyword(kefuName, "wechat")
|
||
|
if len(welcomes) > 0 {
|
||
|
welcomeContent := ""
|
||
|
for _, welcome := range welcomes {
|
||
|
welcome.Content, _ = tools.ReplaceStringByRegex(welcome.Content, `<[^a>]+>|target="[^"]+"`, "")
|
||
|
welcomeContent += welcome.Content + "\r\n"
|
||
|
}
|
||
|
go kefuWework.SendWelcomeMsg(welcomeContent, welcomeCode)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
if content == "" {
|
||
|
return
|
||
|
}
|
||
|
//查找访客插入访客表
|
||
|
visitorId := fmt.Sprintf("wxkf|%s|%s", entId, userId)
|
||
|
vistorInfo := models.FindVisitorByVistorId(visitorId)
|
||
|
if vistorInfo.ID == 0 {
|
||
|
//获取客户昵称头像
|
||
|
reqData := map[string]interface{}{
|
||
|
"external_userid_list": []string{
|
||
|
userId,
|
||
|
},
|
||
|
}
|
||
|
ret, _ := kefuWework.BatchGet(reqData)
|
||
|
visitorName := gjson.Get(ret, "customer_list.0.nickname").String()
|
||
|
avator := gjson.Get(ret, "customer_list.0.avatar").String()
|
||
|
extra := gjson.Get(ret, "customer_list.0.enter_session_context.scene_param").String()
|
||
|
refer := gjson.Get(ret, "customer_list.0.enter_session_context.scene").String()
|
||
|
if visitorName == "" {
|
||
|
visitorName = "微信客服用户"
|
||
|
}
|
||
|
if avator == "" {
|
||
|
avator = "/static/images/we-chat-wx.png"
|
||
|
}
|
||
|
vistorInfo = models.Visitor{
|
||
|
Model: models.Model{
|
||
|
CreatedAt: time.Now(),
|
||
|
UpdatedAt: time.Now(),
|
||
|
},
|
||
|
Name: visitorName,
|
||
|
Avator: avator,
|
||
|
ToId: kfId,
|
||
|
VisitorId: visitorId,
|
||
|
Status: 1,
|
||
|
Refer: refer,
|
||
|
EntId: entId,
|
||
|
VisitNum: 0,
|
||
|
City: "微信客服",
|
||
|
Extra: extra,
|
||
|
ClientIp: c.ClientIP(),
|
||
|
}
|
||
|
vistorInfo.AddVisitor()
|
||
|
} else {
|
||
|
go models.UpdateVisitorKefuName(visitorId, kfId)
|
||
|
}
|
||
|
|
||
|
kefuInfo := models.FindUser(kfId)
|
||
|
if kefuInfo.ID == 0 {
|
||
|
kefuInfo = models.FindUserByUid(entId)
|
||
|
}
|
||
|
go ws.VisitorOnline(kefuInfo.Name, vistorInfo)
|
||
|
go ws.VisitorSendToKefu(kefuInfo.Name, vistorInfo, content)
|
||
|
go SendWechatVisitorMessageTemplate(kefuInfo.Name, vistorInfo.Name, vistorInfo.VisitorId, content, vistorInfo.EntId)
|
||
|
go service.SendWorkWechatMesage(vistorInfo.EntId, vistorInfo, kefuInfo, content, c)
|
||
|
//判断当前访客是否机器人回复
|
||
|
if !models.CheckVisitorRobotReply(vistorInfo.State) {
|
||
|
return
|
||
|
}
|
||
|
result := ""
|
||
|
//调用自动回复
|
||
|
result = GetLearnReplyContent(entId, content)
|
||
|
if result == "" && configs["QdrantAIStatus"] == "true" {
|
||
|
if configs["QiyeWechatKefuPreRobot"] != "" {
|
||
|
go kefuWework.SendTextMsg(kfId, userId, configs["QiyeWechatKefuPreRobot"])
|
||
|
}
|
||
|
//调用GPT3.5
|
||
|
result = ws.Gpt3Knowledge(entId, visitorId, kefuInfo, content)
|
||
|
}
|
||
|
if result != "" {
|
||
|
result, _ = tools.ReplaceStringByRegex(result, `<[^a>]+>|target="[^"]+"`, "")
|
||
|
|
||
|
var err error
|
||
|
if imgUrl := ParseImgMessage2(result); imgUrl != "" {
|
||
|
err = kefuWework.SendImagesMsg(kfId, userId, common.StaticDirPath+imgUrl)
|
||
|
} else if voiceUrl := ParseVoiceMessage(result); voiceUrl != "" {
|
||
|
err = kefuWework.SendVoiceMsg(kfId, userId, common.UploadDirPath+voiceUrl)
|
||
|
} else {
|
||
|
log.Println("企业微信客服发送消息:", kfId, userId, result)
|
||
|
err = kefuWework.SendTextMsg(kfId, userId, result)
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Println("企业微信客服发送消息失败:", err)
|
||
|
} else {
|
||
|
models.CreateMessage(kefuInfo.Name, visitorId, result, "kefu", entId, "read")
|
||
|
go ws.KefuMessage(visitorId, result, kefuInfo)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 微信客服验签
|
||
|
func GetWechatKefuCheckSign(c *gin.Context) {
|
||
|
entId := c.Param("entId")
|
||
|
signature := c.Query("msg_signature")
|
||
|
timestamp := c.Query("timestamp")
|
||
|
nonce := c.Query("nonce")
|
||
|
echostr := c.Query("echostr")
|
||
|
//获取配置
|
||
|
configs := models.GetEntConfigsMap(entId, "kefuWeworkCorpid", "kefuWeworkSecret", "kefuWeworkToken", "kefuWeworkEncodingAESKey")
|
||
|
kefuWework := wechat_kf_sdk.NewKefuWework(configs["kefuWeworkCorpid"], configs["kefuWeworkSecret"], configs["kefuWeworkToken"], configs["kefuWeworkEncodingAESKey"])
|
||
|
res, err := kefuWework.CheckSign(signature, timestamp, nonce, echostr)
|
||
|
log.Println("企业微信客服验签:", res, err)
|
||
|
c.Writer.Write([]byte(res))
|
||
|
}
|
||
|
|
||
|
// 微信客服列表
|
||
|
func GetKefuWeworkList(c *gin.Context) {
|
||
|
entId, _ := c.Get("ent_id")
|
||
|
wechat := service.GetKefuWework(entId.(string))
|
||
|
str, _ := wechat.GetAccountList(0, 1000)
|
||
|
c.JSON(200, gin.H{
|
||
|
"code": types.ApiCode.SUCCESS,
|
||
|
"msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
|
||
|
"result": str,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 微信客服链接
|
||
|
func GetKefuWeworkLink(c *gin.Context) {
|
||
|
entId, _ := c.Get("ent_id")
|
||
|
openKfId := c.Query("open_kfid")
|
||
|
//获取配置
|
||
|
configs := models.GetEntConfigsMap(entId.(string), "kefuWeworkCorpid", "kefuWeworkSecret", "kefuWeworkToken", "kefuWeworkEncodingAESKey")
|
||
|
kefuWework := wechat_kf_sdk.NewKefuWework(configs["kefuWeworkCorpid"], configs["kefuWeworkSecret"], configs["kefuWeworkToken"], configs["kefuWeworkEncodingAESKey"])
|
||
|
str, _ := kefuWework.GetAccountLink(openKfId)
|
||
|
c.JSON(200, gin.H{
|
||
|
"code": types.ApiCode.SUCCESS,
|
||
|
"msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
|
||
|
"result": str,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 企业微信机器人
|
||
|
func PostEntWechatRobot(c *gin.Context) {
|
||
|
entId := c.Param("entId")
|
||
|
robotCode := c.Param("robotCode")
|
||
|
kefuName := c.Param("kefuName")
|
||
|
var jsonData map[string]interface{}
|
||
|
if err := c.ShouldBindJSON(&jsonData); err != nil {
|
||
|
// 发生错误,返回错误响应
|
||
|
c.JSON(200, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
}
|
||
|
log.Println("entwechat robot", jsonData)
|
||
|
spoken := jsonData["spoken"].(string)
|
||
|
receivedName := jsonData["receivedName"].(string)
|
||
|
groupName := jsonData["groupName"].(string)
|
||
|
groupRemark := jsonData["groupRemark"].(string)
|
||
|
if groupRemark != "" {
|
||
|
groupName = groupRemark
|
||
|
}
|
||
|
roomType := jsonData["roomType"]
|
||
|
if spoken == "" {
|
||
|
c.JSON(200, gin.H{
|
||
|
"code": 0,
|
||
|
"message": "success",
|
||
|
"data": gin.H{
|
||
|
"type": 5000,
|
||
|
"info": gin.H{
|
||
|
"text": "",
|
||
|
},
|
||
|
},
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
kefuInfo := models.FindUserByUid(entId)
|
||
|
if kefuInfo.ID == 0 || kefuInfo.Name != kefuName {
|
||
|
c.JSON(200, gin.H{
|
||
|
"code": types.ApiCode.ACCOUNT_NO_EXIST,
|
||
|
"msg": types.ApiCode.GetMessage(types.ApiCode.ACCOUNT_NO_EXIST),
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
//查找访客插入访客表
|
||
|
visitorId := fmt.Sprintf("entwechat|%s|%s", entId, tools.Md5(fmt.Sprintf("%s|%s|%v", groupName, receivedName, roomType)))
|
||
|
vistorInfo := models.FindVisitorByVistorId(visitorId)
|
||
|
if vistorInfo.ID == 0 {
|
||
|
visitorName := receivedName
|
||
|
avator := "/static/images/we-chat-wx.png"
|
||
|
if visitorName == "" {
|
||
|
visitorName = "企业微信用户"
|
||
|
}
|
||
|
vistorInfo = models.Visitor{
|
||
|
Model: models.Model{
|
||
|
CreatedAt: time.Now(),
|
||
|
UpdatedAt: time.Now(),
|
||
|
},
|
||
|
Name: visitorName,
|
||
|
Avator: avator,
|
||
|
ToId: kefuInfo.Name,
|
||
|
VisitorId: visitorId,
|
||
|
Status: 1,
|
||
|
Refer: "来自企业微信",
|
||
|
EntId: entId,
|
||
|
VisitNum: 0,
|
||
|
City: "企业微信",
|
||
|
ClientIp: c.ClientIP(),
|
||
|
}
|
||
|
vistorInfo.AddVisitor()
|
||
|
} else {
|
||
|
go models.UpdateVisitorStatus(visitorId, 3)
|
||
|
}
|
||
|
go ws.VisitorOnline(kefuInfo.Name, vistorInfo)
|
||
|
go ws.VisitorSendToKefu(kefuInfo.Name, vistorInfo, spoken)
|
||
|
|
||
|
//调用GPT3.5
|
||
|
result := ws.Gpt3Knowledge(entId, visitorId, kefuInfo, spoken)
|
||
|
models.CreateMessage(kefuInfo.Name, visitorId, result, "kefu", entId, "read")
|
||
|
go ws.KefuMessage(visitorId, result, kefuInfo)
|
||
|
payload := fmt.Sprintf(`{
|
||
|
"socketType":2,
|
||
|
"list":[
|
||
|
{
|
||
|
"type":203,
|
||
|
"titleList":[
|
||
|
"%s"
|
||
|
],
|
||
|
"atList":[
|
||
|
"%s"
|
||
|
],
|
||
|
"receivedContent":"%s"
|
||
|
}
|
||
|
]
|
||
|
}`, groupName, receivedName, result)
|
||
|
url := "https://worktool.asrtts.cn/wework/sendRawMessage?robotId=" + robotCode
|
||
|
tools.PostJson(url, []byte(payload))
|
||
|
c.JSON(200, gin.H{
|
||
|
"code": 0,
|
||
|
"message": "success",
|
||
|
"data": gin.H{
|
||
|
"type": 5000,
|
||
|
"info": gin.H{
|
||
|
"text": "",
|
||
|
},
|
||
|
},
|
||
|
})
|
||
|
}
|