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": "", }, }, }) }