Compare commits

..

No commits in common. "47da1435aea33add7d46cf321a6ab0591c26fa99" and "7c75f0722aa1163304b2e2f121efc4b0bd5a01b3" have entirely different histories.

6 changed files with 97 additions and 178 deletions

View File

@ -216,7 +216,7 @@ func (o *ChatRoom) SendSessionMessage(chatUser *accountFiee.ChatUserData, sessio
} }
} }
fmt.Printf("client:%+v\n", client) fmt.Printf("client:%+v\n", client)
if client != nil && (client.UserId != chatUser.ID || chatUser.Role == 3) { if client != nil && client.UserId != chatUser.ID {
client.Send <- msgBytes client.Send <- msgBytes
go o.pushEvent(EventChatMessage, EventProgressAfter, chatUser, o.Session[sessionId][i], message) go o.pushEvent(EventChatMessage, EventProgressAfter, chatUser, o.Session[sessionId][i], message)
userIdInSession = append(userIdInSession, client.UserId) userIdInSession = append(userIdInSession, client.UserId)

View File

@ -81,7 +81,7 @@ func (m *MessageListType) BuildMessage(data *accountFiee.ChatRecordData) {
}) })
} }
} }
case accountFiee.MsgType_TextMsgType: case accountFiee.MsgType_TextMsgType, accountFiee.MsgType_CardType:
m.Message = Message{ m.Message = Message{
MsgType: data.MsgType, MsgType: data.MsgType,
Text: data.Content, Text: data.Content,

View File

@ -42,14 +42,9 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
var ChatHandlerIns = NewChatHandler() var ChatHandlerIns = ChatHandler{
cache: chatCache.ChatCache{NewMessageStatExpireAfter: 10 * time.Minute},
func NewChatHandler() ChatHandler { robot: robot.NewRobot(),
c := ChatHandler{
cache: chatCache.ChatCache{NewMessageStatExpireAfter: 10 * time.Minute},
}
c.robot = robot.NewRobot(&c.cache)
return c
} }
type ChatHandler struct { type ChatHandler struct {

View File

@ -8,12 +8,12 @@ package robot
import ( import (
"context" "context"
"fmt"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws" "fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/chatCache" "fonchain-fiee/pkg/service/asChat/consts"
"fonchain-fiee/pkg/service/asChat/dto" "fonchain-fiee/pkg/service/asChat/dto"
"fonchain-fiee/pkg/service/asChat/logic"
"strings" "strings"
"time" "time"
) )
@ -24,9 +24,9 @@ type Reply struct {
Rules []IRule Rules []IRule
} }
func (r *Reply) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) { func (r *Reply) Hit(eventType ws.EventType, chatUser *accountFiee.ChatUserData, wsClient *ws.Client, msg string, robotInfo *accountFiee.ChatUserData) (hit bool, runTime time.Time, logic func(msg string) error) {
for _, rule := range r.Rules { for _, rule := range r.Rules {
hit, task = rule.Hit(event, robotInfo) hit, runTime, logic = rule.Hit(eventType, chatUser, wsClient, msg, robotInfo)
if hit { if hit {
return return
} }
@ -36,7 +36,11 @@ func (r *Reply) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserDat
// 规则接口 // 规则接口
type IRule interface { type IRule interface {
Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) Hit(eventType ws.EventType, chatUser *accountFiee.ChatUserData, wsClient *ws.Client, msg string, robotInfo *accountFiee.ChatUserData) (hit bool, runTime time.Time, logic func(msg string) error)
}
func NewReplyWhenHitKeywords(keywords []string) IRule {
return &ReplyWhenHitKeywords{Keywords: keywords}
} }
// KeywordsRuleChecker 关键字回复 // KeywordsRuleChecker 关键字回复
@ -44,80 +48,53 @@ type ReplyWhenHitKeywords struct {
Keywords []string `json:"keywords"` Keywords []string `json:"keywords"`
} }
func NewReplyWhenHitKeywords(keywords []string) IRule { func (k ReplyWhenHitKeywords) Hit(eventType ws.EventType, chatUser *accountFiee.ChatUserData, wsClient *ws.Client, msg string, robotInfo *accountFiee.ChatUserData) (hit bool, runTime time.Time, logic func(msg string) error) {
return &ReplyWhenHitKeywords{Keywords: keywords} if eventType != ws.EventChatMessage || msg == "" || wsClient == nil || chatUser == nil {
}
func (k ReplyWhenHitKeywords) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) {
if event.EventType != ws.EventChatMessage || event.Msg == "" || event.Client == nil || event.ChatUser == nil {
return return
} }
for _, v := range k.Keywords { for _, v := range k.Keywords {
if strings.Contains(event.Msg, v) { if strings.Contains(msg, v) {
hit = true hit = true
break break
} }
} }
task = RobotTask{ logic = func(content string) error {
ChatUser: event.ChatUser, var notice = dto.MessageListType{}
Run: func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error { newRecord := &accountFiee.ChatRecordData{
return logic.NewMessage(context.Background(), cache, Sender, dto.NewMessageRequest{ SessionId: wsClient.SessionId,
Waiter: true, UserId: wsClient.UserId,
SessionId: event.Client.SessionId, Name: chatUser.NickName,
Message: dto.Message{ Avatar: robotInfo.Avatar,
MsgType: 1, MsgType: 1,
Text: msg, Content: content,
LocalStamp: time.Now().Unix(), }
}, notice.BuildMessage(newRecord)
}) _, err := consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
}, return err
} }
//logicFunc = func(content string, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData) error {
// //var notice = dto.MessageListType{}
// //newRecord := &accountFiee.ChatRecordData{
// // SessionId: wsClient.SessionId,
// // UserId: wsClient.UserId,
// // Name: chatUser.NickName,
// // Avatar: robotInfo.Avatar,
// // MsgType: 1,
// // Content: content,
// //}
// //notice.BuildMessage(newRecord)
// //_, err := consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
// //return err
// err := logic.NewMessage(context.Background(), cache, chatUser, dto.NewMessageRequest{
// Waiter: true,
// SessionId: wsClient.SessionId,
// Message: dto.Message{
// MsgType: 1,
// Text: msg,
// LocalStamp: time.Now().Unix(),
// },
// })
// return err
//}
return return
} }
// 用户打开聊天会话直接发送 // 用户打开聊天会话直接发送
type ReplyWhenUserJoinSession struct {
}
func NewReplyWhenUserJoinSession() IRule { func NewReplyWhenUserJoinSession() IRule {
return &ReplyWhenUserJoinSession{} return &ReplyWhenUserJoinSession{}
} }
func (k ReplyWhenUserJoinSession) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) { type ReplyWhenUserJoinSession struct {
if event.EventType != ws.EventUserJoin { }
func (k ReplyWhenUserJoinSession) Hit(eventType ws.EventType, chatUser *accountFiee.ChatUserData, wsClient *ws.Client, msg string, robotInfo *accountFiee.ChatUserData) (hit bool, runTime time.Time, logic func(msg string) error) {
if eventType != ws.EventUserJoin {
return return
} }
if event.Client == nil { if wsClient == nil {
return return
} }
ctx := context.Background() ctx := context.Background()
queryRes, err := service.AccountFieeProvider.GetChatRecordList(ctx, &accountFiee.GetChatRecordListRequest{ queryRes, err := service.AccountFieeProvider.GetChatRecordList(ctx, &accountFiee.GetChatRecordListRequest{
Query: &accountFiee.ChatRecordData{ Query: &accountFiee.ChatRecordData{
SessionId: event.Client.SessionId, SessionId: wsClient.SessionId,
}, },
Page: 1, Page: 1,
PageSize: 1, PageSize: 1,
@ -137,115 +114,67 @@ func (k ReplyWhenUserJoinSession) Hit(event ws.ListenEventData, robotInfo *accou
} }
} }
hit = true hit = true
if event.ChatUser == nil { logic = func(msg string) error {
event.ChatUser, err = service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: event.Client.UserId}) var notice = dto.MessageListType{}
if err != nil { newRecord := &accountFiee.ChatRecordData{
return SessionId: wsClient.SessionId,
UserId: wsClient.UserId,
Name: wsClient.SessionId,
Avatar: robotInfo.Avatar,
MsgType: 1,
Content: msg,
} }
notice.BuildMessage(newRecord)
_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
return err
} }
task = RobotTask{
ChatUser: event.ChatUser,
Run: func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error {
return logic.NewMessage(ctx, cache, Sender, dto.NewMessageRequest{
Waiter: true,
SessionId: event.Client.SessionId,
Message: dto.Message{
MsgType: 1,
Text: msg,
LocalStamp: time.Now().Unix(),
},
})
},
}
//logicFunc = func(msg string, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData) error {
// //var notice = dto.MessageListType{}
// //newRecord := &accountFiee.ChatRecordData{
// // SessionId: wsClient.SessionId,
// // UserId: wsClient.UserId,
// // Name: wsClient.SessionId,
// // Avatar: robotInfo.Avatar,
// // MsgType: 1,
// // Content: msg,
// //}
// //notice.BuildMessage(newRecord)
// //_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
// err = logic.NewMessage(ctx, cache, chatUser, dto.NewMessageRequest{
// Waiter: true,
// SessionId: wsClient.SessionId,
// Message: dto.Message{
// MsgType: 1,
// Text: msg,
// LocalStamp: time.Now().Unix(),
// },
// })
// return err
//}
return return
} }
// 客服指定时间不回复则自动回复 // 客服
type ReplyWhenWaiterNoAction struct {
DelaySecond time.Duration
}
func NewReplyWhenWaiterNoAction(delaySecond time.Duration) *ReplyWhenWaiterNoAction { func NewReplyWhenWaiterNoAction(delaySecond time.Duration) *ReplyWhenWaiterNoAction {
return &ReplyWhenWaiterNoAction{ return &ReplyWhenWaiterNoAction{
DelaySecond: delaySecond, DelaySecond: delaySecond,
} }
} }
func (k *ReplyWhenWaiterNoAction) Hit(event ws.ListenEventData, chatUser *accountFiee.ChatUserData) (hit bool, task RobotTask) { type ReplyWhenWaiterNoAction struct {
if event.Client == nil || event.EventType != ws.EventChatMessage { DelaySecond time.Duration
return }
}
task = RobotTask{
RunTime: time.Now().Add(k.DelaySecond * time.Second),
Run: func(content string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error {
//如果客服已经回复则不发送消息
chatRecordListRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{
Query: &accountFiee.ChatRecordData{
SessionId: event.Client.SessionId,
},
Page: 1,
PageSize: 1,
Order: "created_at desc",
})
if err != nil || chatRecordListRes.Total == 0 {
return err
}
checkUserId := chatRecordListRes.List[0].UserId
checkChatUser, err := service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: checkUserId})
if err != nil || checkChatUser.Role != 1 {
return err
}
//var notice = dto.MessageListType{} func (k *ReplyWhenWaiterNoAction) Hit(eventType ws.EventType, chatUser *accountFiee.ChatUserData, wsClient *ws.Client, msg string, robotInfo *accountFiee.ChatUserData) (hit bool, runTime time.Time, logic func(msg string) error) {
//newRecord := &accountFiee.ChatRecordData{ runTime = time.Now().Add(k.DelaySecond * time.Second)
// SessionId: wsClient.SessionId, logic = func(content string) error {
// UserId: wsClient.UserId, //如果客服已经回复则不发送消息
// Name: chatUser.NickName, chatRecordListRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{
// Avatar: robotInfo.Avatar, Query: &accountFiee.ChatRecordData{
// MsgType: 1, SessionId: fmt.Sprintf("%d", chatUser.ID),
// Content: content, },
//} Page: 1,
//notice.BuildMessage(newRecord) PageSize: 1,
//_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice) Order: "created_at desc",
//return err })
if err != nil || chatRecordListRes.Total == 0 {
err = logic.NewMessage(context.Background(), cache, chatUser, dto.NewMessageRequest{
Waiter: true,
SessionId: event.Client.SessionId,
Message: dto.Message{
MsgType: 1,
Text: event.Msg,
LocalStamp: time.Now().Unix(),
},
})
return err return err
}, }
Response: "", checkUserId := chatRecordListRes.List[0].UserId
ChatUser: event.ChatUser, checkChatUser, err := service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: checkUserId})
if err != nil || checkChatUser.Role != 1 {
return err
}
var notice = dto.MessageListType{}
newRecord := &accountFiee.ChatRecordData{
SessionId: wsClient.SessionId,
UserId: wsClient.UserId,
Name: chatUser.NickName,
Avatar: robotInfo.Avatar,
MsgType: 1,
Content: content,
}
notice.BuildMessage(newRecord)
_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
return err
} }
return return

View File

@ -12,7 +12,6 @@ import (
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws" "fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/chatCache"
"fonchain-fiee/pkg/service/asChat/consts" "fonchain-fiee/pkg/service/asChat/consts"
"fonchain-fiee/pkg/service/asChat/dto" "fonchain-fiee/pkg/service/asChat/dto"
"log" "log"
@ -20,7 +19,7 @@ import (
"time" "time"
) )
func NewRobot(cache *chatCache.ChatCache) *Robot { func NewRobot() *Robot {
ctx := context.Background() ctx := context.Background()
robotQuery, err := service.AccountFieeProvider.GetChatUserList(ctx, &accountFiee.GetChatUserListRequest{ robotQuery, err := service.AccountFieeProvider.GetChatUserList(ctx, &accountFiee.GetChatUserListRequest{
Query: &accountFiee.ChatUserData{Role: 3}, Query: &accountFiee.ChatUserData{Role: 3},
@ -54,7 +53,6 @@ func NewRobot(cache *chatCache.ChatCache) *Robot {
}, },
Chan: make(ws.ListenEventChan), Chan: make(ws.ListenEventChan),
}, },
cache: cache,
} }
ruleListRes, err := service.AccountFieeProvider.GetChatAutoReplyRulerList(ctx, &accountFiee.GetChatAutoReplyRulerListRequest{ ruleListRes, err := service.AccountFieeProvider.GetChatAutoReplyRulerList(ctx, &accountFiee.GetChatAutoReplyRulerListRequest{
Query: &accountFiee.ChatAutoReplyRulerData{Status: 1}, Query: &accountFiee.ChatAutoReplyRulerData{Status: 1},
@ -89,7 +87,6 @@ type Robot struct {
isRunning bool //运行状态 isRunning bool //运行状态
mu sync.Mutex mu sync.Mutex
*ws.EventListener *ws.EventListener
cache *chatCache.ChatCache
} }
//func (r *Robot) Listen(record *accountFiee.ChatRecordData) { //func (r *Robot) Listen(record *accountFiee.ChatRecordData) {
@ -162,7 +159,7 @@ func (r *Robot) Run() {
if now.After(task.RunTime) { if now.After(task.RunTime) {
// 执行任务 // 执行任务
go func() { go func() {
err := task.Run(task.Response, r.cache, task.ChatUser) err := task.Run(task.Response)
if err != nil { if err != nil {
log.Printf("聊天机器人[%d]回复消息失败:%v", r.Info.ID, err) log.Printf("聊天机器人[%d]回复消息失败:%v", r.Info.ID, err)
} }
@ -179,16 +176,19 @@ func (r *Robot) Run() {
case event := <-r.EventListener.Chan: case event := <-r.EventListener.Chan:
fmt.Printf("robot listen event:%#v\n", event) fmt.Printf("robot listen event:%#v\n", event)
for _, ruleResponse := range r.Rules { for _, ruleResponse := range r.Rules {
hit, task := ruleResponse.Hit(event, r.Info) hit, runtime, logic := ruleResponse.Hit(event.EventType, event.ChatUser, event.Client, event.Msg, r.Info)
if hit { if hit {
if task.RunTime.IsZero() { if runtime.IsZero() {
err := task.Run(ruleResponse.Response, r.cache, r.Info) err := logic(ruleResponse.Response)
if err != nil { if err != nil {
log.Printf("robot 执行任务失败:%v\n", err) log.Printf("robot 执行任务失败:%v\n", err)
} }
} else { } else {
task.Response = ruleResponse.Response r.RegisterDelayTask(RobotTask{
r.RegisterDelayTask(task) RunTime: runtime,
Run: logic,
Response: ruleResponse.Response,
})
} }
break break

View File

@ -6,15 +6,10 @@
// ------------------------------------------- // -------------------------------------------
package robot package robot
import ( import "time"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/service/asChat/chatCache"
"time"
)
type RobotTask struct { type RobotTask struct {
RunTime time.Time RunTime time.Time
Run func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error Run func(msg string) error
Response string Response string
ChatUser *accountFiee.ChatUserData
} }