1033 lines
30 KiB
Go
1033 lines
30 KiB
Go
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("绑定客服账户成功!<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×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[] <nil> map[] 100.108.79.233:59663 /?msg_signature=825075c093249d5a60967fe4a613cae93146636b×tamp=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, "×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(`
|
||
<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)
|
||
}
|
||
|
||
}
|