package controller import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/tidwall/gjson" "kefu/lib" "kefu/models" "kefu/tools" "kefu/types" "kefu/ws" "log" "strings" "time" ) func PostDouyinWebhooks(c *gin.Context) { var jsonData map[string]interface{} // 使用 ShouldBindJSON 将请求中的 JSON 数据绑定到 jsonData 变量 if err := c.ShouldBindJSON(&jsonData); err != nil { // 发生错误,返回错误响应 c.JSON(400, gin.H{"error": err.Error()}) return } jsonStrByte, _ := json.Marshal(jsonData) jsonStr := string(jsonStrByte) log.Println("douyin:", jsonStr) jsonEnvent := gjson.Get(jsonStr, "event").String() toUserId := gjson.Get(jsonStr, "to_user_id").String() fromUserId := gjson.Get(jsonStr, "from_user_id").String() clientKey := gjson.Get(jsonStr, "client_key").String() content := gjson.Get(jsonStr, "content").String() //验证 if jsonEnvent == "verify_webhook" { insertDouyinWebhook(jsonEnvent, fromUserId, toUserId, clientKey, content, "", "") challenge := jsonData["content"].(map[string]interface{})["challenge"] c.JSON(200, gin.H{"challenge": challenge}) return } userDouyin := models.FindUserDouyinRow("open_id = ?", toUserId) if userDouyin.EntId == "" || userDouyin.KefuName == "" { return } insertDouyinWebhook(jsonEnvent, fromUserId, toUserId, clientKey, content, userDouyin.EntId, userDouyin.KefuName) //视频评论事件item_comment_reply if jsonEnvent == "item_comment_reply" && fromUserId != toUserId { go douyinReplyHandle(toUserId, content, userDouyin) } //接受私信事件im_receive_msg if jsonEnvent == "im_receive_msg" { go douyinMessageReplyHandle(toUserId, fromUserId, content, userDouyin, jsonEnvent) } //发出私信事件 im_send_msg if jsonEnvent == "im_send_msg" { } //进入私信事件 im_enter_direct_msg if jsonEnvent == "im_enter_direct_msg" { go douyinMessageReplyHandle(toUserId, fromUserId, content, userDouyin, jsonEnvent) } } func insertDouyinWebhook(event, formUserId, toUserId, clientKey, content, entId, kefuName string) { newWebhook := models.DouyinWebhook{ KefuName: kefuName, Event: event, FromUserID: formUserId, ToUserID: toUserId, ClientKey: clientKey, Content: content, EntID: entId, } go newWebhook.AddDouyinWebhook() } func douyinReplyHandle(toUserId, responseJson string, userDouyin *models.User_douyin) { content := gjson.Get(responseJson, "content").String() avatar := gjson.Get(responseJson, "avatar").String() comment_user_id := gjson.Get(responseJson, "comment_user_id").String() comment_id := gjson.Get(responseJson, "comment_id").String() nickName := gjson.Get(responseJson, "nick_name").String() //视频ID reply_to_item_id := gjson.Get(responseJson, "reply_to_item_id").String() kefuInfo := models.FindUserByUid(userDouyin.EntId) content = strings.TrimSpace(content) //查找访客插入访客表 visitorId := fmt.Sprintf("douyin_comment|%s|%s", userDouyin.EntId, comment_user_id) vistorInfo := models.FindVisitorByVistorId(visitorId) if vistorInfo.ID == 0 { visitorName := nickName vistorInfo = models.Visitor{ Model: models.Model{ CreatedAt: time.Now(), UpdatedAt: time.Now(), }, Name: visitorName, Avator: avatar, ToId: kefuInfo.Name, VisitorId: visitorId, Status: 1, Refer: "来自抖音评论", EntId: userDouyin.EntId, VisitNum: 0, City: "抖音用户", } vistorInfo.AddVisitor() } else { go models.UpdateVisitorStatus(visitorId, 3) } go ws.VisitorOnline(kefuInfo.Name, vistorInfo) go ws.VisitorSendToKefu(kefuInfo.Name, vistorInfo, content) result := "" dingConfig := models.GetEntConfigsMap(userDouyin.EntId, "QdrantAIStatus") //调用自动回复 result = GetLearnReplyContent(userDouyin.EntId, content) if result == "" && dingConfig["QdrantAIStatus"] == "true" { //调用GPT3.5 result = ws.Gpt3Knowledge(userDouyin.EntId, visitorId, kefuInfo, content) } result = tools.TrimHtml(result) if result == "" { return } //回复评论 clientId := models.FindConfig("DouyinClientKey") clientKey := models.FindConfig("DouyinClientSecret") douyin, _ := lib.NewDouyin(clientId, clientKey) accessToken, _ := douyin.RefeshAccessToken(userDouyin.AccessToken) res, err := douyin.ReplyVideoComment(accessToken, toUserId, result, reply_to_item_id, comment_id) log.Println("调用抖音回复评论接口:", res, err) models.CreateMessage(kefuInfo.Name, visitorId, result, "kefu", userDouyin.EntId, "read") go ws.KefuMessage(visitorId, result, kefuInfo) } // 处理抖音私信 func douyinMessageReplyHandle(fromUserId, toUserId, responseJson string, userDouyin *models.User_douyin, eventType string) { message_type := gjson.Get(responseJson, "message_type").String() content := "其他类型消息" if message_type == "text" { content = gjson.Get(responseJson, "text").String() } if eventType == "im_enter_direct_msg" { content = "用户进入私信会话页" } avatar := gjson.Get(responseJson, "user_infos.0.avatar").String() nickName := gjson.Get(responseJson, "user_infos.0.nick_name").String() openId := gjson.Get(responseJson, "user_infos.0.open_id").String() kefuInfo := models.FindUserByUid(userDouyin.EntId) content = strings.TrimSpace(content) //查找访客插入访客表 visitorId := fmt.Sprintf("douyin_message|%s|%s", userDouyin.EntId, openId) vistorInfo := models.FindVisitorByVistorId(visitorId) if vistorInfo.ID == 0 { visitorName := nickName vistorInfo = models.Visitor{ Model: models.Model{ CreatedAt: time.Now(), UpdatedAt: time.Now(), }, Name: visitorName, Avator: avatar, ToId: kefuInfo.Name, VisitorId: visitorId, Status: 1, Refer: "来自抖音私信", EntId: userDouyin.EntId, VisitNum: 0, City: "抖音用户", } vistorInfo.AddVisitor() } else { go models.UpdateVisitorStatus(visitorId, 3) } go ws.VisitorOnline(kefuInfo.Name, vistorInfo) go ws.VisitorSendToKefu(kefuInfo.Name, vistorInfo, content) result := "" dingConfig := models.GetEntConfigsMap(userDouyin.EntId, "QdrantAIStatus") //调用自动回复 result = GetLearnReplyContent(userDouyin.EntId, content) if result == "" && dingConfig["QdrantAIStatus"] == "true" { //调用GPT3.5 result = ws.Gpt3Knowledge(userDouyin.EntId, visitorId, kefuInfo, content) } result = tools.TrimHtml(result) //调用访客消息回调 visitorMessageCallUrlResult := SendVisitorMessageCallUrl(vistorInfo.EntId, vistorInfo.VisitorId, vistorInfo.ToId, vistorInfo.Name, vistorInfo.Avator, content) result = gjson.Get(visitorMessageCallUrlResult, "result").String() if result == "" { return } //回复 clientId := models.FindConfig("DouyinClientKey") clientKey := models.FindConfig("DouyinClientSecret") douyin, _ := lib.NewDouyin(clientId, clientKey) accessToken, _ := douyin.RefeshAccessToken(userDouyin.AccessToken) conversation_short_id := gjson.Get(responseJson, "conversation_short_id").String() server_message_id := gjson.Get(responseJson, "server_message_id").String() res, err := douyin.ReplyMessage(accessToken, eventType, fromUserId, toUserId, conversation_short_id, server_message_id, result) log.Println("调用抖音私信接口:", res, err) models.CreateMessage(kefuInfo.Name, visitorId, result, "kefu", userDouyin.EntId, "read") go ws.KefuMessage(visitorId, result, kefuInfo) } // 客服发送抖音视频评论 func SendDouyinCommentMessage(visitor models.Visitor, content string) bool { visitorIdArr := strings.Split(visitor.VisitorId, "|") if len(visitorIdArr) < 3 || visitorIdArr[0] != "douyin_comment" { return false } toUserId := visitorIdArr[2] userDouyin := models.FindUserDouyinRow("kefu_name = ?", visitor.ToId) douyinWebhook := models.FindDouyinWebhook("kefu_name = ? and from_user_id = ?", visitor.ToId, toUserId) if userDouyin.EntId == "" || userDouyin.KefuName == "" || douyinWebhook.Content == "" { return false } //回复评论 clientId := models.FindConfig("DouyinClientKey") clientKey := models.FindConfig("DouyinClientSecret") douyin, _ := lib.NewDouyin(clientId, clientKey) accessToken, _ := douyin.RefeshAccessToken(userDouyin.AccessToken) //评论ID comment_id := gjson.Get(douyinWebhook.Content, "comment_id").String() //视频ID reply_to_item_id := gjson.Get(douyinWebhook.Content, "reply_to_item_id").String() res, err := douyin.ReplyVideoComment(accessToken, userDouyin.OpenId, content, reply_to_item_id, comment_id) log.Println("调用抖音回复评论接口:", res, err) return true } // 客服发送抖音私信 func SendDouyinPrivateMessage(visitor models.Visitor, content string) bool { visitorIdArr := strings.Split(visitor.VisitorId, "|") if len(visitorIdArr) < 3 || visitorIdArr[0] != "douyin_message" { return false } toUserId := visitorIdArr[2] userDouyin := models.FindUserDouyinRow("kefu_name = ?", visitor.ToId) douyinWebhook := models.FindDouyinWebhook("kefu_name = ? and from_user_id = ?", visitor.ToId, toUserId) if userDouyin.EntId == "" || userDouyin.KefuName == "" || douyinWebhook.Content == "" { return false } clientId := models.FindConfig("DouyinClientKey") clientKey := models.FindConfig("DouyinClientSecret") douyin, _ := lib.NewDouyin(clientId, clientKey) accessToken, _ := douyin.RefeshAccessToken(userDouyin.AccessToken) conversation_short_id := gjson.Get(douyinWebhook.Content, "conversation_short_id").String() server_message_id := gjson.Get(douyinWebhook.Content, "server_message_id").String() res, err := douyin.ReplyMessage(accessToken, "im_receive_msg", userDouyin.OpenId, toUserId, conversation_short_id, server_message_id, content) log.Println("调用抖音私信接口:", res, err) return true } // 抖音绑定 func GetDouyinList(c *gin.Context) { entId, _ := c.Get("ent_id") kefuName, _ := c.Get("kefu_name") list := models.FindUserDouyinRows("ent_id = ? and kefu_name = ?", entId, kefuName) c.JSON(200, gin.H{ "code": types.ApiCode.SUCCESS, "msg": "ok", "result": list, }) } // 删除抖音绑定 func GetDeleteDouyinList(c *gin.Context) { entId, _ := c.Get("ent_id") kefuName, _ := c.Get("kefu_name") id := c.Query("id") models.DeleteUserDouyinRows("ent_id = ? and kefu_name = ? and id = ?", entId, kefuName, id) c.JSON(200, gin.H{ "code": types.ApiCode.SUCCESS, "msg": "ok", }) } // 抖音绑定 func PostDouyinBind(c *gin.Context) { entId, _ := c.Get("ent_id") kefuName, _ := c.Get("kefu_name") code := c.PostForm("code") clientId := models.FindConfig("DouyinClientKey") clientKey := models.FindConfig("DouyinClientSecret") douyin, _ := lib.NewDouyin(clientId, clientKey) tokenResult, err := douyin.GetAccessToken(code) if err != nil { log.Println("douyin:", err) c.JSON(200, gin.H{ "code": types.ApiCode.FAILED, "msg": err.Error(), }) return } //获取access_token失败 status := gjson.Get(tokenResult, "message").String() if status != "success" { log.Println("douyin:", tokenResult) c.JSON(200, gin.H{ "code": types.ApiCode.FAILED, "msg": tokenResult, }) return } accessToken := gjson.Get(tokenResult, "data.access_token").String() refreshToken := gjson.Get(tokenResult, "data.refresh_token").String() expiresIn := gjson.Get(tokenResult, "data.expires_in").Int() refreshExpiresIn := gjson.Get(tokenResult, "data.refresh_expires_in").Int() expiresInInt := tools.Now() + expiresIn refreshExpiresInInt := tools.Now() + refreshExpiresIn openId := gjson.Get(tokenResult, "data.open_id").String() //获取昵称头像 douyinInfo, _ := douyin.GetUserInfo(accessToken, openId) nickname := gjson.Get(douyinInfo, "data.nickname").String() avatar := gjson.Get(douyinInfo, "data.avatar").String() unionId := gjson.Get(douyinInfo, "data.union_id").String() douyinModel := &models.User_douyin{ Nickname: nickname, Avatar: avatar, EntId: entId.(string), KefuName: kefuName.(string), OpenId: openId, AccessToken: accessToken, RefreshToken: refreshToken, ExpiresIn: types.Time{tools.IntToTime(expiresInInt - 3600)}, RefreshExpiresIn: types.Time{tools.IntToTime(refreshExpiresInInt - 3600)}, ClientTokenExpires: tools.IntToTimeStr(expiresInInt, "2006-01-02 15:04:05"), UnionId: unionId, CreatedAt: types.Time{}, } douyinModel.UpdateOrInsert() c.JSON(200, gin.H{ "code": types.ApiCode.SUCCESS, "msg": "ok", "result": gin.H{ "token_result": tokenResult, "info_result": douyinInfo, }, }) return }