kefu/controller/common.go

587 lines
18 KiB
Go
Raw Permalink Normal View History

2024-12-10 02:50:12 +00:00
package controller
import (
"encoding/json"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"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"
"github.com/silenceper/wechat/v2/util"
workConfig "github.com/silenceper/wechat/v2/work/config"
"github.com/tidwall/gjson"
"kefu/common"
"kefu/lib"
"kefu/lib/wechat_kf_sdk"
"kefu/models"
"kefu/tools"
"kefu/types"
"kefu/ws"
"log"
"regexp"
"strconv"
"strings"
"time"
)
var IP_SERVER_URL = "https://gofly.sopans.com/uc/v2/ipAuth"
var memory = tools.NewMemory()
// 处理错误
func HandleError(c *gin.Context, code uint, msg string, err error) {
c.JSON(200, gin.H{
"code": code,
"msg": msg,
"result": err.Error(),
})
}
/*
*
处理分页页码
*/
func HandlePagePageSize(c *gin.Context) (uint, uint) {
var page uint
pagesize := common.VisitorPageSize
myPage, _ := strconv.Atoi(c.Query("page"))
if myPage != 0 {
page = uint(myPage)
}
myPagesize, _ := strconv.Atoi(c.Query("pagesize"))
if myPagesize != 0 {
pagesize = uint(myPagesize)
}
return page, pagesize
}
// 发送访客微信消息
func SendWechatVisitorMessage(visitorId, content, entId string) bool {
visitorIdArr := strings.Split(visitorId, "|")
if len(visitorIdArr) < 3 || visitorIdArr[0] != "wx" {
return false
}
content = tools.TrimHtml(content)
return SendWechatMesage(visitorIdArr[2], content, entId)
}
// 客服发送给小程序访客消息
func SendMiniVisitorMessage(visitorId, content, entId string) bool {
content = tools.TrimHtml(content)
visitorIdArr := strings.Split(visitorId, "|")
if len(visitorIdArr) < 3 || visitorIdArr[0] != "mini" {
return false
}
toUser := visitorIdArr[2]
configs := models.GetEntConfigsMap(entId, "WechatMiniToken", "WechatMiniAppId", "WechatMiniAppSecret")
cfg := &offConfigMini.Config{
AppID: configs["WechatMiniAppId"],
AppSecret: configs["WechatMiniAppSecret"],
//EncodingAESKey: "xxxx",
Cache: memory,
}
wc := wechat.NewWechat()
mini := wc.GetMiniProgram(cfg).GetCustomerMessage()
if imgUrl := ParseImgMessage(content); imgUrl != "" {
accessToken, _ := mini.GetAccessToken()
uri := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s", accessToken, "image")
response, err := util.PostFile("media", common.UploadDirPath+imgUrl, uri)
mediaId := gjson.Get(string(response), "media_id").String()
if err != nil || mediaId == "" {
log.Println("发送小程序微信客服图片消息-上传临时素材错误:", toUser, imgUrl, err)
return false
}
msg := miniMsg.NewCustomerImgMessage(toUser, mediaId)
mini.Send(msg)
return true
}
msg := miniMsg.NewCustomerTextMessage(toUser, content)
mini.Send(msg)
return true
}
// 客服发送给微信客服用户的消息
func SendWeworkKfVisitorMessage(visitor models.Visitor, content string) bool {
visitorIdArr := strings.Split(visitor.VisitorId, "|")
if len(visitorIdArr) < 3 || visitorIdArr[0] != "wxkf" {
return false
}
content = tools.TrimHtml(content)
configs := models.GetEntConfigsMap(visitor.EntId, "QdrantAIStatus", "kefuWeworkCorpid", "kefuWeworkSecret", "kefuWeworkToken", "kefuWeworkEncodingAESKey")
kefuWework := wechat_kf_sdk.NewKefuWework(configs["kefuWeworkCorpid"], configs["kefuWeworkSecret"], configs["kefuWeworkToken"], configs["kefuWeworkEncodingAESKey"])
var err error
if imgUrl := ParseImgMessage(content); imgUrl != "" {
err = kefuWework.SendImagesMsg(visitor.ToId, visitorIdArr[2], common.UploadDirPath+imgUrl)
} else if voiceUrl := ParseVoiceMessage(content); voiceUrl != "" {
err = kefuWework.SendVoiceMsg(visitor.ToId, visitorIdArr[2], common.UploadDirPath+voiceUrl)
} else {
log.Println("企业微信客服发送消息:", visitor.ToId, visitorIdArr[2], content)
err = kefuWework.SendTextMsg(visitor.ToId, visitorIdArr[2], content)
}
if err != nil {
log.Println("企业微信客服发送消息失败:", err)
}
return true
}
// 发送客服微信消息
func SendWechatKefuNotice(kefuName, content, entId string) bool {
oauth := models.FindOauthById(kefuName)
if oauth.OauthId == "" {
return false
}
return SendWechatMesage(oauth.OauthId, content, entId)
}
// 发送新访客提醒模板消息
//func SendWechatVisitorTemplate(kefuName, visitorName, content, entId string) bool {
// oauths := models.FindOauthsById(kefuName)
// if len(oauths) == 0 {
// return false
// }
// wechatConfig, _ := lib.NewWechatLib(entId)
// if wechatConfig.WechatMessageTemplateId == "" {
// systemBussinesId := models.FindConfig("SystemBussinesId")
// if systemBussinesId == "" {
// return false
// }
// wechatConfig, _ = lib.NewWechatLib(systemBussinesId)
// if wechatConfig.WechatMessageTemplateId == "" {
// return false
// }
// }
// msgData := make(map[string]*message.TemplateDataItem)
// msgData["keyword1"] = &message.TemplateDataItem{
// Value: visitorName,
// Color: "",
// }
// msgData["keyword2"] = &message.TemplateDataItem{
// Value: time.Now().Format("2006-01-02 15:04:05"),
// Color: "",
// }
// msgData["keyword3"] = &message.TemplateDataItem{
// Value: content,
// Color: "",
// }
// for _, oauth := range oauths {
// msg := &message.TemplateMessage{
// ToUser: oauth.OauthId,
// Data: msgData,
// TemplateID: wechatConfig.WechatVisitorTemplateId,
// URL: wechatConfig.WechatHost + "/wechatKefuTransfer?ent_id=" + entId + "&kefu_name=" + kefuName,
// }
// SendWechatTemplate(wechatConfig, msg)
// }
// return true
//}
// 发送访客新消息提醒模板消息
func SendWechatVisitorMessageTemplate(kefuName, visitorName, visitorId, content, entId string) bool {
oauths := models.FindOauthsById(kefuName)
if len(oauths) == 0 {
return false
}
openids := make([]string, 0)
for _, oauth := range oauths {
openids = append(openids, oauth.OauthId)
}
systemBussinesId := models.FindConfig("SystemBussinesId")
configs := models.GetEntConfigsMap(entId, "WechatAppId", "WechatHost", "WechatAppSecret", "WechatAppToken", "WechatMessageTemplateId", "WechatMessageTemplateColumn")
for _, v := range configs {
if v == "" {
configs = models.GetEntConfigsMap(systemBussinesId, "WechatAppId", "WechatHost", "WechatAppSecret", "WechatAppToken", "WechatMessageTemplateId", "WechatMessageTemplateColumn")
break
}
}
for _, v := range configs {
if v == "" {
return false
}
}
templateColums := strings.Split(configs["WechatMessageTemplateColumn"], "|")
if len(templateColums) < 3 {
return false
}
offical := lib.NewWechatOffical(configs["WechatAppId"], configs["WechatAppSecret"], configs["WechatAppToken"], memory)
messages := []map[string]string{
{"key": templateColums[0], "value": visitorName},
{"key": templateColums[1], "value": time.Now().Format("2006-01-02 15:04:05")},
{"key": templateColums[2], "value": content},
}
offical.SendTemplateMessage(
openids,
configs["WechatMessageTemplateId"],
configs["WechatHost"]+"/wechatKefuTransfer?ent_id="+entId+"&kefu_name="+kefuName,
messages,
)
//wechatConfig, _ := lib.NewWechatLib(entId)
//if wechatConfig.WechatMessageTemplateId == "" {
// systemBussinesId := models.FindConfig("SystemBussinesId")
// if systemBussinesId == "" {
// return false
// }
// wechatConfig, _ = lib.NewWechatLib(systemBussinesId)
// if wechatConfig.WechatMessageTemplateId == "" {
// return false
// }
//}
//msgData := make(map[string]*message.TemplateDataItem)
//msgData["keyword1"] = &message.TemplateDataItem{
// Value: visitorName,
// Color: "",
//}
//msgData["keyword2"] = &message.TemplateDataItem{
// Value: time.Now().Format("2006-01-02 15:04:05"),
// Color: "",
//}
//msgData["keyword3"] = &message.TemplateDataItem{
// Value: content,
// Color: "",
//}
//msgData["remark"] = &message.TemplateDataItem{
// Value: models.FindConfig("WechatTemplateRemark"),
// Color: "",
//}
//for _, oauth := range oauths {
// msg := &message.TemplateMessage{
// ToUser: oauth.OauthId,
// Data: msgData,
// TemplateID: wechatConfig.WechatMessageTemplateId,
// URL: wechatConfig.WechatHost + "/wechatKefuTransfer?ent_id=" + entId + "&kefu_name=" + kefuName,
// }
// SendWechatTemplate(wechatConfig, msg)
//}
msg := ws.TypeMessage{
Type: "wechat_notice",
Data: 1,
}
go ws.VisitorCustomMessage(visitorId, msg)
return true
}
// 发送客服回复模板消息
//func SendWechatKefuTemplate(visitorId, kefuName, kefuNickname, content, entId string) bool {
// visitorIdArr := strings.Split(visitorId, "|")
// if len(visitorIdArr) < 3 || visitorIdArr[0] != "wx" {
// return false
// }
// wechatConfig, _ := lib.NewWechatLib(entId)
// if wechatConfig.WechatKefuTemplateId == "" {
// return false
// }
// msgData := make(map[string]*message.TemplateDataItem)
// msgData["keyword1"] = &message.TemplateDataItem{
// Value: kefuNickname,
// Color: "",
// }
// msgData["keyword2"] = &message.TemplateDataItem{
// Value: time.Now().Format("2006-01-02 15:04:05"),
// Color: "",
// }
// msgData["keyword3"] = &message.TemplateDataItem{
// Value: tools.TrimHtml(content),
// Color: "",
// }
// msgData["remark"] = &message.TemplateDataItem{
// Value: models.FindConfig("WechatTemplateRemark"),
// Color: "",
// }
// msg := &message.TemplateMessage{
// ToUser: visitorIdArr[2],
// Data: msgData,
// TemplateID: wechatConfig.WechatKefuTemplateId,
// URL: wechatConfig.WechatHost + "/wechatIndex" +
// "?ent_id=" + entId +
// "&kefu_id=" + kefuName +
// "&visitor_id=" + visitorId,
// }
// res, _ := SendWechatTemplate(wechatConfig, msg)
// return res
//}
// 发送微信模板消息
func SendWechatTemplate(wechatConfig *lib.Wechat, msg *message.TemplateMessage) (bool, error) {
if wechatConfig == nil {
return false, errors.New("该企业未配置appid等公众号资料")
}
if msg.TemplateID == "" || msg.ToUser == "" {
return false, errors.New("openid或templateId不存在")
}
wc := wechat.NewWechat()
cfg := &offConfig.Config{
AppID: wechatConfig.AppId,
AppSecret: wechatConfig.AppSecret,
Token: wechatConfig.Token,
//EncodingAESKey: "xxxx",
Cache: memory,
}
officialAccount := wc.GetOfficialAccount(cfg)
template := officialAccount.GetTemplate()
_, err := template.Send(msg)
if err != nil {
log.Println("发送模板消息失败:", err.Error())
return false, err
}
m, _ := json.Marshal(msg)
log.Println("发送模板消息成功!:" + string(m))
return true, nil
}
// 发送微信客服消息
func SendWechatMesage(openId, content, entId string) bool {
wechatConfig, _ := lib.NewWechatLib(entId)
if wechatConfig == nil || wechatConfig.WechatKefu == "" || wechatConfig.WechatKefu == "off" {
return false
}
wc := wechat.NewWechat()
cfg := &offConfig.Config{
AppID: wechatConfig.AppId,
AppSecret: wechatConfig.AppSecret,
Token: wechatConfig.Token,
//EncodingAESKey: "xxxx",
Cache: memory,
}
officialAccount := wc.GetOfficialAccount(cfg)
messager := officialAccount.GetCustomerMessageManager()
if imgUrl := ParseImgMessage(content); imgUrl != "" {
media, err := officialAccount.GetMaterial().MediaUpload("image", common.UploadDirPath+imgUrl)
if err != nil {
log.Println("发送公众号微信客服图片消息-上传临时素材错误:", openId, imgUrl, err)
return true
}
err = messager.Send(message.NewCustomerImgMessage(openId, media.MediaID))
log.Println("发送公众号微信客服图片消息:", openId, content, err)
return true
}
if voiceUrl := ParseVoiceMessage(content); voiceUrl != "" {
media, err := officialAccount.GetMaterial().MediaUpload("voice", common.UploadDirPath+voiceUrl)
if err != nil {
log.Println("发送公众号微信客服图片消息-上传临时素材错误:", openId, voiceUrl, err)
return true
}
err = messager.Send(message.NewCustomerVoiceMessage(openId, media.MediaID))
log.Println("发送公众号微信客服语音消息:", openId, content, err)
return true
}
err := messager.Send(message.NewCustomerTextMessage(openId, content))
log.Println("发送公众号微信客服消息:", openId, content, err)
return true
}
// 验证访客黑名单
func CheckVisitorBlack(visitorId string) bool {
black := models.FindVisitorBlack("visitor_id = ?", visitorId)
if black.Id != 0 {
return false
}
return true
}
// 过滤敏感词
func ReplaceBlackWords(content string) (string, bool) {
blackWords := models.FindConfig("BlackWords")
strings.ReplaceAll(blackWords, "\r\n", "\n")
blackList := strings.Split(blackWords, "\n")
exist := false
for _, word := range blackList {
word = strings.Trim(word, " ")
if word == "" {
continue
}
if strings.Contains(content, word) {
exist = true
content = strings.ReplaceAll(content, word, "*")
}
}
return content, exist
}
// 发送企业微信应用通知
func SendWorkWechatMesage(entId string, visitor models.Visitor, kefu models.User, content string, c *gin.Context) bool {
currentHost := tools.GetHost(c.Request)
token := GenUserToken(kefu)
pcUrl := fmt.Sprintf("%s/sso?token=%s&redirect=%s/main", currentHost, token, currentHost)
//企业微信群机器人webhook
webhookUrl := models.FindEntConfig(entId, "WorkWechatWebHookUrl").ConfValue
if webhookUrl != "" {
data := map[string]interface{}{
"msgtype": "markdown",
"markdown": map[string]interface{}{
"content": fmt.Sprintf(`### %s
> 访客名<font color="warning">%s</font>
> 客服名<font color="warning">%s</font>
> 访问次数%d
> 时间%s
> 回复请点击[H5后台](%s/h5/)[PC后台](%s)
`, content, visitor.Name, kefu.Nickname, visitor.VisitNum, tools.GetNowTime(), currentHost, pcUrl),
},
}
str, _ := tools.JsonEncode(data)
url := webhookUrl
res, err := tools.PostJson(url, []byte(str))
log.Println(url, str, err, res)
}
wechatConfig, _ := lib.NewWechatLib(entId)
if wechatConfig.WorkWechatCorpid == "" ||
wechatConfig.WorkWechatAppAgentId == "" ||
wechatConfig.WorkWechatAppSecret == "" {
return false
}
wc := wechat.NewWechat()
cfg := &workConfig.Config{
CorpID: wechatConfig.WorkWechatCorpid,
CorpSecret: wechatConfig.WorkWechatAppSecret,
Cache: memory,
}
work := wc.GetWork(cfg)
accessToken, _ := work.GetContext().GetAccessToken()
data := map[string]interface{}{
"touser": "@all",
"toparty": "",
"totag": "",
"msgtype": "markdown",
"agentid": wechatConfig.WorkWechatAppAgentId,
"markdown": map[string]interface{}{
"content": fmt.Sprintf(`### %s
> 访客名<font color="warning">%s</font>
> 客服名<font color="warning">%s</font>
> 访客编号@%d
> 访问次数%d
> 时间%s
> 回复请点击[H5后台](%s/h5/)[PC后台](%s)
`, content, visitor.Name, kefu.Nickname, visitor.ID, visitor.VisitNum, tools.GetNowTime(), currentHost, pcUrl),
},
"safe": 0,
"enable_id_trans": 0,
"enable_duplicate_check": 0,
"duplicate_check_interval": 1800,
}
str, _ := tools.JsonEncode(data)
url := "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken
res, err := tools.PostJson(url, []byte(str))
log.Println(url, str, err, res)
return true
}
// 获取企业配置信息
func GetEntConfigsMap(entId string, keys ...string) map[string]string {
configs := models.FindEntConfigByEntid(entId)
result := make(map[string]string)
for _, key := range keys {
result[key] = ""
}
for _, config := range configs {
mapKey := config.ConfKey
if _, ok := result[mapKey]; ok {
result[mapKey] = config.ConfValue
}
}
return result
}
func ParseImgMessage(str string) string {
var url string
url, _ = tools.GetOneStringByRegex(str, "img\\[.*?/static/upload/(.*?)\\]")
return url
}
func ParseVoiceMessage(str string) string {
var url string
url, _ = tools.GetOneStringByRegex(str, "audio\\[.*?/static/upload/(.*?)\\]")
return url
}
func ParseImgMessage2(str string) string {
var url string
url, _ = tools.GetOneStringByRegex(str, "img\\[.*?/static/(.*?)\\]")
return url
}
func ParseVoiceMessage2(str string) string {
var url string
url, _ = tools.GetOneStringByRegex(str, "audio\\[(.*?)\\]")
return url
}
func CheckSystemAuthCode(c *gin.Context) bool {
authCode := models.FindConfig("SystemAuthCode")
privateKey := common.RsaPrivateKey
if authCode == "" || privateKey == "" {
return false
}
rsa := tools.NewRsa("", privateKey)
decContent, _ := tools.Base64Decode2(authCode)
jsonByte, err := rsa.Decrypt(decContent)
if err != nil {
jsonByte = []byte("")
}
allowHost := gjson.Get(string(jsonByte), "host").String()
if allowHost == "" {
return false
}
if !strings.Contains(allowHost, c.Request.Host) {
return false
}
return true
}
// 发送通知邮件
func SendSystemNoticeEmail(email, title, content string) (bool, error) {
//验证邮箱
matched, _ := regexp.MatchString("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*", email)
if !matched {
return false, errors.New(types.ApiCode.GetMessage(types.ApiCode.INVALID))
}
smtp := models.FindConfig("NoticeEmailSmtp")
sender := models.FindConfig("NoticeEmailAddress")
password := models.FindConfig("NoticeEmailPassword")
if smtp == "" || sender == "" || password == "" {
return false, errors.New("系统没有配置发送邮箱")
}
htmlContent := strings.Replace(common.NoticeTemplate, "[:content]", content, -1)
err := tools.SendSmtp(smtp, sender, password, []string{email}, title, htmlContent)
log.Println("发送邮件:" + smtp + "," + sender + "," + password + "," + email + "," + content)
if err != nil {
log.Println("发送邮件验证码失败:", err)
return false, err
}
return true, nil
}
// 发送企业微信WEBHOOK通知
func SendWorkWechatWebHook(entId string, visitor models.Visitor, kefu models.User, content string, c *gin.Context) bool {
currentHost := tools.GetHost(c.Request)
token := GenUserToken(kefu)
pcUrl := fmt.Sprintf("%s/sso?token=%s&redirect=%s/main", currentHost, token, currentHost)
//企业微信群机器人webhook
webhookUrl := models.FindEntConfig(entId, "WorkWechatWebHookUrl").ConfValue
if webhookUrl != "" {
data := map[string]interface{}{
"msgtype": "markdown",
"markdown": map[string]interface{}{
"content": fmt.Sprintf(`### %s
> 访客名<font color="warning">%s</font>
> 客服名<font color="warning">%s</font>
> 访问次数%d
> 时间%s
> 回复请点击[H5后台](%s/h5/)[PC后台](%s)
`, content, visitor.Name, kefu.Nickname, visitor.VisitNum, tools.GetNowTime(), currentHost, pcUrl),
},
}
str, _ := tools.JsonEncode(data)
url := webhookUrl
res, err := tools.PostJson(url, []byte(str))
log.Println(url, str, err, res)
}
return true
}