kefu/controller/weixin.go

1033 lines
30 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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也可选择redismemcache或者自定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("绑定客服账户成功!<a href='%s'>前往客服助手H5</a>", 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("绑定客服账户成功!<a href='%s'>前往客服助手H5</a>", 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<a href='weixin://bizmsgmenu?msgmenucontent=%s&msgmenuid=%d'>%s</a>", 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&timestamp=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[] <nil> map[] 100.108.79.233:59663 /?msg_signature=825075c093249d5a60967fe4a613cae93146636b&timestamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D <nil>}`
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, "&timestamp=", start, &msg_signature)
var timestamp string
next = getString(httpstr, "&nonce=", next, &timestamp)
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, "&timestamp=", start, &msg_signature)
var timestamp string
next = getString(httpstr, "&nonce=", next, &timestamp)
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(`
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>
`, 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("<a href='%s' target='_blank'><img class='chatImagePic' src='%s' data-src='%s'/></a>", 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<a href='weixin://bizmsgmenu?msgmenucontent=%s&msgmenuid=%d'>%s</a>", 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)
}
}