package controller import ( "encoding/json" "encoding/xml" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/silenceper/wechat/v2/officialaccount/basic" "github.com/tidwall/gjson" "io/ioutil" "kefu/common" "kefu/lib" "kefu/lib/wxbizjsonmsgcrypt" "kefu/models" "kefu/tools" "kefu/types" "kefu/ws" "log" "net/http" "net/url" "os" "strings" "time" wechat "github.com/silenceper/wechat/v2" offConfigMini "github.com/silenceper/wechat/v2/miniprogram/config" miniMsg "github.com/silenceper/wechat/v2/miniprogram/message" offConfig "github.com/silenceper/wechat/v2/officialaccount/config" "github.com/silenceper/wechat/v2/officialaccount/message" workConfig "github.com/silenceper/wechat/v2/work/config" toolsWechat "kefu/tools/wechat" ) func GetCheckWeixinSign(c *gin.Context) { token := models.FindConfig("WeixinToken") signature := c.Query("signature") timestamp := c.Query("timestamp") nonce := c.Query("nonce") echostr := c.Query("echostr") wechat := &lib.Wechat{ AppId: "", AppSecret: "", Token: token, } res, err := wechat.CheckWechatSign(signature, timestamp, nonce, echostr) if err == nil { c.Writer.Write([]byte(res)) } else { log.Println("微信API验证失败") } } // 处理微信消息 func PostWechatServer(c *gin.Context) { kefuName := c.Param("kefuName") entId := c.Param("entId") serveWechat(c.Writer, c.Request, entId, kefuName) } func serveWechat(rw http.ResponseWriter, req *http.Request, entId, kefuName string) { //这里本地内存保存access_token,也可选择redis,memcache或者自定cache wechatConfig, _ := lib.NewWechatLib(entId) if wechatConfig == nil { return } cfg := &offConfig.Config{ AppID: wechatConfig.AppId, AppSecret: wechatConfig.AppSecret, Token: wechatConfig.Token, //EncodingAESKey: "xxxx", Cache: memory, } wc := wechat.NewWechat() officialAccount := wc.GetOfficialAccount(cfg) // 传入request和responseWriter server := officialAccount.GetServer(req, rw) //设置接收消息的处理方法 server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply { //TODO visitorId := fmt.Sprintf("wx|%s|%s", entId, string(msg.CommonToken.FromUserName)) avator := "/static/images/we-chat-wx.png" visitorName := "微信公众号用户" vistorInfo := models.FindVisitorByVistorId(visitorId) kefuInfo := models.FindUser(kefuName) isExist := true if vistorInfo.ID == 0 { isExist = false vistorInfo = *models.CreateVisitor(visitorName, avator, "", kefuName, visitorId, "微信对话框", "", "", entId, "") } else { avator = vistorInfo.Avator visitorName = vistorInfo.Name } log.Println("微信公众号回调消息体:", msg) if msg.MsgType == "text" { //访客上线 go ws.VisitorOnline(kefuName, vistorInfo) //访客发给客服 ws.VisitorSendToKefu(kefuInfo.Name, vistorInfo, msg.Content) if isExist { go models.UpdateVisitorStatus(visitorId, 3) } if !models.CheckVisitorRobotReply(vistorInfo.State) { return nil } //自动回复消息 go func() { replyContent, isGPT := ws.VisitorAutoReply(vistorInfo, kefuInfo, msg.Content) //reply := models.FindArticleRow("ent_id = ? and title like ?", vistorInfo.EntId, "%"+msg.Content+"%") replyContent, _ = tools.ReplaceStringByRegex(replyContent, `<[^a>]+>|target="[^"]+"`, "") if replyContent != "" { SendWechatVisitorMessage(vistorInfo.VisitorId, replyContent, entId) if isGPT { models.CreateMessage(kefuInfo.Name, vistorInfo.VisitorId, replyContent, "kefu", vistorInfo.EntId, "read") ws.KefuMessage(vistorInfo.VisitorId, replyContent, kefuInfo) } } }() //go models.ReadMessageByVisitorId(vistorInfo.VisitorId, "kefu") //go models.CreateMessage(kefuInfo.Name, vistorInfo.VisitorId, reply.Content, "kefu", vistorInfo.EntId, "read") //text := message.NewText(replyContent) return nil } else if msg.MsgType == "event" { msg.Content = string(msg.Event) if string(msg.Event) == "subscribe" { subResult := HandleScanSubscribe(msg, wechatConfig, visitorId) if subResult == "kefu" { url := wechatConfig.WechatHost + "/wechatKefuTransfer?ent_id=" + entId + "&kefu_name=" + kefuName text := message.NewText(fmt.Sprintf("绑定客服账户成功!前往客服助手H5", url)) return &message.Reply{MsgType: message.MsgTypeText, MsgData: text} } } //取关事件 if string(msg.Event) == "unsubscribe" { go models.UpdateVisitorStatus(visitorId, 2) go models.DelOauth(string(msg.CommonToken.FromUserName)) } if string(msg.Event) == "SCAN" { subResult := HandleScanSubscribe(msg, wechatConfig, visitorId) if subResult == "kefu" { url := wechatConfig.WechatHost + "/wechatKefuTransfer?ent_id=" + entId + "&kefu_name=" + kefuName text := message.NewText(fmt.Sprintf("绑定客服账户成功!前往客服助手H5", url)) return &message.Reply{MsgType: message.MsgTypeText, MsgData: text} } } //模板事件 if msg.Content == "TEMPLATESENDJOBFINISH" || msg.Content == "VIEW" { return nil } } else if msg.MsgType == "image" { if isExist { go models.UpdateVisitorVisitorId(visitorId, visitorId) } fildDir := fmt.Sprintf("%s%d%s/", common.Upload, time.Now().Year(), time.Now().Month().String()) mediaId := msg.MediaID fileName := mediaId + ".jpg" isExist, _ := tools.IsFileExist(fildDir) if !isExist { os.Mkdir(fildDir, os.ModePerm) } filepath := fmt.Sprintf("%s%s", fildDir, fileName) tools.HttpDown(msg.PicURL, filepath) msg.Content = fmt.Sprintf("img[/%s]", filepath) //访客上线 go ws.VisitorOnline(kefuName, vistorInfo) //访客发给客服 ws.VisitorSendToKefu(kefuInfo.Name, vistorInfo, msg.Content) if isExist { go models.UpdateVisitorStatus(visitorId, 3) } return nil } else { text := message.NewText("仅支持图片和文本") return &message.Reply{MsgType: message.MsgTypeText, MsgData: text} } //欢迎 replyContent := "" welcomes := models.FindWelcomesByKeyword(kefuName, "wechat") if len(welcomes) > 0 { for _, welcome := range welcomes { welcome.Content, _ = tools.ReplaceStringByRegex(welcome.Content, `<[^a>]+>|target="[^"]+"`, "") replyContent += welcome.Content + "\r\n" } } //发送热门问题 questions := models.FindTopArticles("ent_id = ? and is_top = 1 ", entId) if len(questions) > 0 { for _, item := range questions { replyContent += fmt.Sprintf("\r\n%s", item.Title, item.Id, item.Title) } } text := message.NewText(replyContent) return &message.Reply{MsgType: message.MsgTypeText, MsgData: text} }) //处理消息接收以及回复 err := server.Serve() if err != nil { log.Println(err) return } if server.ResponseMsg != nil { //发送回复的消息 server.Send() } } //处理关注或扫码事件 /* 返回解释: kefu:客服扫码带场景 visitor:访客扫码带场景 customer:营销会员扫码 empty:扫码不带场景 */ func HandleScanSubscribe(msg *message.MixMessage, wechatConfig *lib.Wechat, visitorId string) string { openId := string(msg.CommonToken.FromUserName) //带场景扫码 if msg.EventKey != "" { qrSenceTxt := strings.Replace(msg.EventKey, "qrscene_", "", -1) //客服扫码带场景 if strings.HasPrefix(qrSenceTxt, "kf_") { qrSenceTxt := strings.Replace(qrSenceTxt, "kf_", "", -1) go models.CreateOauth(qrSenceTxt, string(msg.CommonToken.FromUserName)) return "kefu" } //客服扫码登录场景 if strings.HasPrefix(qrSenceTxt, "wechatLogin_") { qrSenceTxt := strings.Replace(qrSenceTxt, "wechatLogin_", "", -1) oauth := models.FindOauthsByOpenId(openId) if oauth.UserId == "" { oauth.UserId = openId models.CreateOauth(openId, openId) } user := models.FindUser(openId) if user.ID == 0 { dd, _ := time.ParseDuration("180h") dd1 := time.Now().Add(dd) kefuInfo := models.User{ Name: openId, Password: tools.Md5("123456"), Nickname: "在线客服", Avator: "/static/images/4.jpg", Pid: 1, RecNum: 0, AgentNum: 0, Status: 0, OnlineStatus: 1, ExpiredAt: types.Time{dd1}, } uid, _ := kefuInfo.AddUser() models.CreateUserRole(uid, 2) } wechatLogin := &models.Wechat_login{ KefuName: oauth.UserId, OpenId: openId, TempKefuId: qrSenceTxt, Status: "SUCCESS", } go wechatLogin.SaveWechatLogin("temp_kefu_id = ?", qrSenceTxt) return "kefu" } //访客扫码带场景 if strings.HasPrefix(qrSenceTxt, "vs_") { qrSenceTxt := strings.Replace(qrSenceTxt, "vs_", "", -1) go models.UpdateVisitorVisitorId(qrSenceTxt, visitorId) go models.UpdateMessageVisitorId(qrSenceTxt, visitorId) go ws.VisitorChangeId(qrSenceTxt, visitorId) return "visitor" } //营销会员 if strings.HasPrefix(qrSenceTxt, "cu_") { qrSenceTxts := strings.Split(qrSenceTxt, "_") entId := qrSenceTxts[1] kefuName := qrSenceTxts[2] companyName := qrSenceTxts[3] customer := models.FindCustomerWhere("ent_id = ? and acount_openid = ?", entId, openId) msgData := make(map[string]*message.TemplateDataItem) var msg *message.TemplateMessage customerName := "待授权" msgData["first"] = &message.TemplateDataItem{ Value: "感谢您关注,欢迎点击注册成为会员", Color: "", } //提示注册成为会员 if customer.ID == 0 { go models.CreateCustomer(openId, entId, kefuName) msgData["remark"] = &message.TemplateDataItem{ Value: "点击此处授权成为会员", Color: "", } } else { //展示会员积分 msgData["remark"] = &message.TemplateDataItem{ Value: fmt.Sprintf("您的积分为:%d", customer.Score), Color: "", } if customer.Name != "" { customerName = customer.Name } if customer.Tel != "" { msgData["first"] = &message.TemplateDataItem{ Value: "感谢您关注,您的会员信息", Color: "", } customerName = customer.Name + customer.Tel } else { msgData["remark"] = &message.TemplateDataItem{ Value: "点击此处授权完善手机号", Color: "", } } } msgData["keyword1"] = &message.TemplateDataItem{ Value: companyName, Color: "", } msgData["keyword2"] = &message.TemplateDataItem{ Value: customerName, Color: "", } msgData["keyword3"] = &message.TemplateDataItem{ Value: time.Now().Format("2006-01-02 15:04:05"), Color: "", } msg = &message.TemplateMessage{ ToUser: openId, Data: msgData, TemplateID: wechatConfig.WechatCustomerTemplateId, URL: "", MiniProgram: struct { AppID string `json:"appid"` PagePath string `json:"pagepath"` }{ AppID: wechatConfig.WechatMiniAppId, PagePath: fmt.Sprintf("/pages/index/index?kefu_name=%s&ent_id=%s&acount_openid=%s", kefuName, entId, openId), }, } _, err := SendWechatTemplate(wechatConfig, msg) if err != nil { log.Println("发送营销会员模板消息失败:", err.Error(), openId, wechatConfig.AppId, wechatConfig.WechatCustomerTemplateId) } return "customer" } } else { //扫码不带场景 go models.CreateOauth(visitorId, openId) } return "empty" } func sendWeixinToKefu(kefuName, visitorName, avator, entId, visitorId, content string) { msgId := models.CreateMessage(kefuName, visitorId, content, "visitor", entId, "read") msg := ws.TypeMessage{ Type: "message", Data: ws.ClientMessage{ MsgId: msgId, Avator: avator, Id: visitorId, VisitorId: visitorId, Name: visitorName, ToId: kefuName, Content: content, Time: time.Now().Format("2006-01-02 15:04:05"), IsKefu: "no", }, } str, _ := json.Marshal(msg) ws.OneKefuMessage(kefuName, str) go SendAppGetuiPush(kefuName, "[信息]"+visitorName, content) } // 查询绑定的oauth func GetWechatOauth(c *gin.Context) { visitorId := c.Query("visitor_id") visitorIdArr := strings.Split(visitorId, "|") var oauth models.Oauth if len(visitorIdArr) >= 3 && visitorIdArr[0] == "wx" { args := []interface{}{ visitorIdArr[2], } oauth = models.FindOauthsQuery("oauth_id = ? ", args) } else { oauth = models.FindOauthById(visitorId) } if oauth.OauthId == "" { c.JSON(200, gin.H{ "code": 400, "msg": "未绑定公众号", }) return } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": oauth.OauthId, }) } // 展示带参二维码 func GetShowQrCode(c *gin.Context) { entId := c.Query("entId") sceneName := c.Query("sceneName") configs := models.GetEntConfigsMap(entId, "WechatAppId", "WechatAppSecret", "WechatAppToken") AppID := configs["WechatAppId"] AppSecret := configs["WechatAppSecret"] Token := configs["WechatAppToken"] if AppID == "" || AppSecret == "" || Token == "" { systemBussinesId := models.FindConfig("SystemBussinesId") if systemBussinesId == "" { c.JSON(200, gin.H{ "code": 400, "msg": "AppID,AppSecret,Token不能为空", }) return } entId = systemBussinesId configs := models.GetEntConfigsMap(entId, "WechatAppId", "WechatAppSecret", "WechatAppToken") AppID = configs["WechatAppId"] AppSecret = configs["WechatAppSecret"] Token = configs["WechatAppToken"] } offical := lib.NewWechatOffical(AppID, AppSecret, Token, memory) url, err := offical.TmpQrCode(sceneName) if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "url": url, }, }) } // 获取永久带参二维码 func GetLimitQrcode(c *gin.Context) { entId := c.Query("entId") sceneName := c.Query("sceneName") url, err := MakeQrcode(entId, sceneName, true) if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "url": url, }, }) } // 获取系统永久带参二维码 func PostUserMarketingQrcode(c *gin.Context) { kefuName := c.PostForm("kefu_name") kefuInfo := models.FindUser(kefuName) if kefuInfo.ID == 0 { c.JSON(200, gin.H{ "code": 400, "msg": "账号不存在", }) return } entId := models.FindConfig("SystemBussinesId") sceneName := fmt.Sprintf("%s_%d_%s_%s", "cu", kefuInfo.ID, kefuName, kefuInfo.Nickname) url, err := MakeQrcode(entId, sceneName, true) if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "url": url, "logo": kefuInfo.Avator, }, }) } // 生成带参二维码 func MakeQrcode(entId, sceneName string, isLimit bool) (string, error) { configs := models.GetEntConfigsMap(entId, "WechatAppId", "WechatAppSecret", "WechatAppToken") AppID := configs["WechatAppId"] AppSecret := configs["WechatAppSecret"] Token := configs["WechatAppToken"] if AppID == "" || AppSecret == "" || Token == "" { return "", errors.New("AppID,AppSecret,Token不能为空") } cfg := &offConfig.Config{ AppID: AppID, AppSecret: AppSecret, Token: Token, //EncodingAESKey: "xxxx", Cache: memory, } wc := wechat.NewWechat() officialAccount := wc.GetOfficialAccount(cfg) basicObj := officialAccount.GetBasic() var tq *basic.Request if isLimit { tq = basic.NewLimitQrRequest(sceneName) } else { tq = basic.NewTmpQrRequest(time.Duration(24*3600), sceneName) } ticket, err := basicObj.GetQRTicket(tq) if err != nil { return "", err } url := basic.ShowQRCode(ticket) return url, nil } // 带参二维码直接输出图片 func GetVisitorQrCode(c *gin.Context) { entId := c.Query("ent_id") if models.FindConfig("SystemBussinesId") != "" { entId = models.FindConfig("SystemBussinesId") } sceneName := c.Query("visitor_id") configs := models.GetEntConfigsMap(entId, "WechatAppId", "WechatAppSecret", "WechatAppToken") AppID := configs["WechatAppId"] AppSecret := configs["WechatAppSecret"] Token := configs["WechatAppToken"] if AppID == "" || AppSecret == "" || Token == "" { c.JSON(200, gin.H{ "code": 400, "msg": "AppID,AppSecret,Token不能为空", }) return } cfg := &offConfig.Config{ AppID: AppID, AppSecret: AppSecret, Token: Token, //EncodingAESKey: "xxxx", Cache: memory, } wc := wechat.NewWechat() officialAccount := wc.GetOfficialAccount(cfg) basicObj := officialAccount.GetBasic() tq := basic.NewTmpQrRequest(time.Duration(24*3600), "vs_"+sceneName) ticket, err := basicObj.GetQRTicket(tq) if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } url := basic.ShowQRCode(ticket) content, _ := tools.HTTPGet(url) c.Writer.Header().Set("Content-Type", "image/png") c.Writer.Write(content) } // 获取小程序用户信息 func GetMiniUserTel(c *gin.Context) { code := c.Query("code") entId := c.Query("entId") systemId := models.FindConfig("SystemBussinesId") if systemId == "" { systemId = entId } configs := models.FindEntConfigs(systemId) AppID := "" AppSecret := "" for _, config := range configs { if config.ConfKey == "WechatMiniAppId" { AppID = config.ConfValue } if config.ConfKey == "WechatMiniAppSecret" { AppSecret = config.ConfValue } } if AppID == "" || AppSecret == "" { c.JSON(200, gin.H{ "code": 400, "msg": "AppID,AppSecret不能为空", }) return } cfg := &offConfigMini.Config{ AppID: AppID, AppSecret: AppSecret, //EncodingAESKey: "xxxx", Cache: memory, } wc := wechat.NewWechat() mini := wc.GetMiniProgram(cfg) accessToken, err := mini.GetAuth().GetAccessToken() if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } data := fmt.Sprintf("{\"code\":\"%s\"}", code) result, _ := tools.Post("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token="+accessToken, "application/json;charset=utf-8", []byte(data)) log.Println(result) c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": result, }) } // 获取小程序手机号 func GetMiniUserInfo(c *gin.Context) { code := c.Query("code") entId := c.Query("entId") systemId := models.FindConfig("SystemBussinesId") if systemId == "" { systemId = entId } configs := models.FindEntConfigs(systemId) AppID := "" AppSecret := "" for _, config := range configs { if config.ConfKey == "WechatMiniAppId" { AppID = config.ConfValue } if config.ConfKey == "WechatMiniAppSecret" { AppSecret = config.ConfValue } } if AppID == "" || AppSecret == "" { c.JSON(200, gin.H{ "code": 400, "msg": "AppID,AppSecret不能为空", }) return } cfg := &offConfigMini.Config{ AppID: AppID, AppSecret: AppSecret, //EncodingAESKey: "xxxx", Cache: memory, } wc := wechat.NewWechat() mini := wc.GetMiniProgram(cfg) res, err := mini.GetAuth().Code2Session(code) if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } openId := res.OpenID customer := models.FindCustomerWhere("ent_id = ? and openid = ?", entId, openId) kefuInfo := models.FindUserByUid(entId) c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "open_id": openId, "avatar": customer.Avatar, "nickname": customer.Name, "tel": customer.Tel, "score": customer.Score, "kefu_nickname": kefuInfo.Nickname, "company_pic": kefuInfo.CompanyPic, }, }) } // 发送应用消息 func SendWorkAppMsg(c *gin.Context) { //SendWorkWechatMesage("2", "xxx", "kefu2", "你好") c.JSON(200, gin.H{ "code": 200, "msg": "ok", }) } const token = "taoshihan" const receiverId = "wwa4266261c4ea2c08" const encodingAeskey = "aoazdxwpDKjOKM6T04YINN9HpAowEOVl6VDGMUJKn54" // 接收应用消息 func ReceiveWorkAppMsg(c *gin.Context) { httpstr := c.Request.URL.RawQuery echo := strings.Index(httpstr, "echostr") entId := c.Param("entId") wechatConfig, _ := lib.NewWechatLib(entId) if wechatConfig.WorkWechatCorpid == "" || wechatConfig.WorkWechatAppToken == "" || wechatConfig.WorkWechatAppEncodingAESKey == "" { c.JSON(200, gin.H{ "code": 400, "msg": "配置错误", }) } if echo != -1 { VerifyURL(c.Writer, c.Request, wechatConfig.WorkWechatAppToken, wechatConfig.WorkWechatAppEncodingAESKey, wechatConfig.WorkWechatCorpid) } else { MsgHandler(c.Writer, c.Request, wechatConfig.WorkWechatAppToken, wechatConfig.WorkWechatAppEncodingAESKey, wechatConfig.WorkWechatCorpid, entId) } //c.JSON(200, gin.H{ // "code": 200, // "msg": "ok", //}) } func VerifyURL(w http.ResponseWriter, r *http.Request, token, encodingAeskey, receiverId string) { //httpstr := `&{GET /?msg_signature=825075c093249d5a60967fe4a613cae93146636b×tamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D HTTP/1.1 1 1 map[Cache-Control:[no-cache] Accept:[*/*] Pragma:[no-cache] User-Agent:[Mozilla/4.0]] 0x86c180 0 [] false 100.108.211.112:8893 map[] map[] map[] 100.108.79.233:59663 /?msg_signature=825075c093249d5a60967fe4a613cae93146636b×tamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D }` fmt.Println(r, r.Body) httpstr := r.URL.RawQuery start := strings.Index(httpstr, "msg_signature=") start += len("msg_signature=") var msg_signature string next := getString(httpstr, "×tamp=", start, &msg_signature) var timestamp string next = getString(httpstr, "&nonce=", next, ×tamp) var nonce string next = getString(httpstr, "&echostr=", next, &nonce) echostr := httpstr[next:len(httpstr)] echostr, _ = url.QueryUnescape(echostr) fmt.Println(msg_signature, timestamp, nonce, echostr, next) wxcpt := wxbizjsonmsgcrypt.NewWXBizMsgCrypt(token, encodingAeskey, receiverId, wxbizjsonmsgcrypt.JsonType) echoStr, cryptErr := wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr) if nil != cryptErr { fmt.Println("verifyUrl fail", cryptErr) } fmt.Println("verifyUrl success echoStr", string(echoStr)) fmt.Fprintf(w, string(echoStr)) } func getString(str, endstr string, start int, msg *string) int { end := strings.Index(str, endstr) *msg = str[start:end] return end + len(endstr) } type MsgContent struct { ToUsername string `json:"ToUserName"` FromUsername string `json:"FromUserName"` CreateTime uint32 `json:"CreateTime"` MsgType string `json:"MsgType"` Content string `json:"Content"` Msgid uint64 `json:"MsgId"` Agentid uint32 `json:"AgentId"` } func MsgHandler(w http.ResponseWriter, r *http.Request, token, encodingAeskey, receiverId, entId string) { httpstr := r.URL.RawQuery start := strings.Index(httpstr, "msg_signature=") start += len("msg_signature=") var msg_signature string next := getString(httpstr, "×tamp=", start, &msg_signature) var timestamp string next = getString(httpstr, "&nonce=", next, ×tamp) nonce := httpstr[next:len(httpstr)] fmt.Println(msg_signature, timestamp, nonce) body, err := ioutil.ReadAll(r.Body) fmt.Println(string(body), err) wxcpt := lib.NewWXBizMsgCrypt(token, encodingAeskey, receiverId, lib.XmlType) msg, err_ := wxcpt.DecryptMsg(msg_signature, timestamp, nonce, body) fmt.Println(string(msg), err_) var msgContent MsgContent err = xml.Unmarshal(msg, &msgContent) if nil != err { fmt.Println("Unmarshal fail") } else { fmt.Println("struct", msgContent) } fmt.Println(msgContent, err) if msgContent.Content == "" { return } result := "" kefuInfo := models.FindUserByUid(entId) config := models.GetEntConfigsMap(entId, "QdrantAIStatus") if config["QdrantAIStatus"] == "true" { //调用GPT3.5 result = ws.Gpt3Knowledge(entId, "", kefuInfo, msgContent.Content) } else { //调用自动回复 result = GetLearnReplyContent(entId, msgContent.Content) } timeStr := tools.Int2Str(tools.Now()) respXml := fmt.Sprintf(` %s `, msgContent.FromUsername, msgContent.ToUsername, timeStr, result) resp, _ := wxcpt.EncryptMsg(respXml, timeStr, "1372623149") w.Write(resp) //id, _ := tools.GetOneStringByRegex(msgContent.Content, "@(\\d+)") //if id == "" { // return //} //content := strings.Trim(strings.ReplaceAll(msgContent.Content, "@"+id, ""), " ") //if content == "" { // return //} //vistorInfo := models.FindVisitorByCondition("id = ? and ent_id = ?", id, entId) //if vistorInfo.ID != 0 { // kefuInfo := models.FindUserByUid(entId) // isRead := "unread" // models.CreateMessage(kefuInfo.Name, vistorInfo.VisitorId, content, "kefu", entId, isRead) // go ws.VisitorMessage(vistorInfo.VisitorId, content, kefuInfo) //} } // 获取企业微信客服列表 func GetWorkAccountList(c *gin.Context) { wc := wechat.NewWechat() cfg := &workConfig.Config{ CorpID: "wwa4266261c4ea2c08", CorpSecret: "jdYozJmgcmsfUNfmGgHPgE--7iGyEIc2UZ8IkBJW-AA", Cache: memory, } work := wc.GetWork(cfg) kfClient, err := work.GetKF() if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } accountList, err := kfClient.AccountList() if err != nil { c.JSON(200, gin.H{ "code": 400, "msg": err.Error(), }) return } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": accountList.AccountList, }) } func GetMiniMessage(c *gin.Context) { entId := c.Param("entId") configs := models.GetEntConfigsMap(entId, "WechatMiniToken", "WechatMiniAppId", "WechatMiniAppSecret") token := configs["WechatMiniToken"] signature := c.Query("signature") timestamp := c.Query("timestamp") nonce := c.Query("nonce") echostr := c.Query("echostr") rawData, _ := c.GetRawData() jsonMsg := string(rawData) log.Println("微信小程序:", jsonMsg) fromUsername := gjson.Get(jsonMsg, "FromUserName").String() toUsername := gjson.Get(jsonMsg, "ToUserName").String() event := gjson.Get(jsonMsg, "Event").String() msgType := gjson.Get(jsonMsg, "MsgType").String() content := gjson.Get(jsonMsg, "Content").String() //首次提交接口的时候需要验证 if msgType == "" { res, err := toolsWechat.CheckWechatSign(token, signature, timestamp, nonce, echostr) if err == nil { c.Writer.Write([]byte(res)) } else { log.Println("微信API验证失败", res) } return } cfg := &offConfigMini.Config{ AppID: configs["WechatMiniAppId"], AppSecret: configs["WechatMiniAppSecret"], //EncodingAESKey: "xxxx", Cache: memory, } wc := wechat.NewWechat() mini := wc.GetMiniProgram(cfg).GetCustomerMessage() visitorId := fmt.Sprintf("mini|%s|%s", entId, fromUsername) avator := "/static/images/we-chat-wx.png" visitorName := "微信小程序用户" vistorInfo := models.FindVisitorByVistorId(visitorId) kefuInfo := models.FindUserByUid(entId) go ws.VisitorOnline(kefuInfo.Name, vistorInfo) if msgType == "text" { go models.CreateMessage(kefuInfo.Name, vistorInfo.VisitorId, content, "visitor", vistorInfo.EntId, "unread") ws.VisitorToKefuMessage(vistorInfo, kefuInfo.Name, content) go SendWechatVisitorMessageTemplate(kefuInfo.Name, vistorInfo.Name, vistorInfo.VisitorId, content, vistorInfo.EntId) if vistorInfo.ID != 0 { go models.UpdateVisitorVisitorId(visitorId, visitorId) } c.JSON(200, gin.H{ "MsgType": "transfer_customer_service", "FromUserName": toUsername, "ToUserName": fromUsername, "CreateTime": timestamp, }) //自动回复 reply := models.FindArticleRow("ent_id = ? and title like ?", vistorInfo.EntId, "%"+content+"%") reply.Content, _ = tools.ReplaceStringByRegex(reply.Content, `<[^a>]+>|target="[^"]+"`, "") if reply.Content != "" { go ws.KefuMessage(vistorInfo.VisitorId, reply.Content, kefuInfo) go models.ReadMessageByVisitorId(vistorInfo.VisitorId, "kefu") go models.CreateMessage(kefuInfo.Name, vistorInfo.VisitorId, reply.Content, "kefu", vistorInfo.EntId, "read") msg := miniMsg.NewCustomerTextMessage(fromUsername, reply.Content) go mini.Send(msg) return } } if msgType == "image" { mediaId := gjson.Get(jsonMsg, "MediaId").String() accessToken, _ := mini.GetAccessToken() imgUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s", accessToken, mediaId) content = fmt.Sprintf("", imgUrl, imgUrl, imgUrl) models.CreateMessage(kefuInfo.Name, vistorInfo.VisitorId, content, "visitor", vistorInfo.EntId, "unread") go ws.VisitorToKefuMessage(vistorInfo, kefuInfo.Name, content) go SendWechatVisitorMessageTemplate(kefuInfo.Name, vistorInfo.Name, vistorInfo.VisitorId, content, vistorInfo.EntId) if vistorInfo.ID != 0 { go models.UpdateVisitorVisitorId(visitorId, visitorId) } c.JSON(200, gin.H{ "MsgType": "transfer_customer_service", "FromUserName": toUsername, "ToUserName": fromUsername, "CreateTime": timestamp, }) } if event == "user_enter_tempsession" { //发送欢迎 welcomes := models.FindWelcomesByKeyword(kefuInfo.Name, "wechat") if len(welcomes) > 0 { for _, welcome := range welcomes { welcome.Content, _ = tools.ReplaceStringByRegex(welcome.Content, `<[^a>]+>|target="[^"]+"`, "") msg := miniMsg.NewCustomerTextMessage(fromUsername, welcome.Content) mini.Send(msg) } } //发送热门问题 questions := models.FindTopArticles("ent_id = ? and is_top = 1 ", entId) if len(questions) > 0 { num := time.Now().Unix() questionStr := "热门问题导航" for _, item := range questions { questionStr += fmt.Sprintf("\r\n%s", item.Title, item.Id, item.Title) num++ } msg := miniMsg.NewCustomerTextMessage(fromUsername, questionStr) mini.Send(msg) } if vistorInfo.ID == 0 { vistorInfo = *models.CreateVisitor(visitorName, avator, c.ClientIP(), kefuInfo.Name, visitorId, "微信小程序客服界面", "未知", c.ClientIP(), entId, "") } else { vistorInfo.VisitNum = vistorInfo.VisitNum + 1 models.UpdateVisitor(entId, vistorInfo.Name, avator, visitorId, kefuInfo.Name, vistorInfo.Status, c.ClientIP(), c.ClientIP(), "微信小程序客服界面", "", vistorInfo.City, vistorInfo.VisitNum, ) } noticeContent := fmt.Sprintf("%s访问%d次", vistorInfo.Name, vistorInfo.VisitNum) go SendVisitorLoginNotice(kefuInfo.Name, vistorInfo.Name, vistorInfo.Avator, noticeContent, vistorInfo.VisitorId) } }