Compare commits
	
		
			No commits in common. "17e3b6513f5d80c12b8f50ff7892f94c2823d3a8" and "ac5fe7a2669a029f0a7a1c2b818acd3cf2178449" have entirely different histories.
		
	
	
		
			17e3b6513f
			...
			ac5fe7a266
		
	
		
| @ -194,8 +194,8 @@ func (o *ChatRoom) Register(c *Client) (sessionId string) { | |||||||
| // message: 消息内容
 | // message: 消息内容
 | ||||||
| func (o *ChatRoom) SendSessionMessage(sender *accountFiee.ChatUserData, sessionId string, msgType WsType, message any) (userIdInSession []int64, err error) { | func (o *ChatRoom) SendSessionMessage(sender *accountFiee.ChatUserData, sessionId string, msgType WsType, message any) (userIdInSession []int64, err error) { | ||||||
| 	fmt.Println("ChatRoom.SendSessionMessage ------------------1") | 	fmt.Println("ChatRoom.SendSessionMessage ------------------1") | ||||||
| 	o.clientsRwLocker.Lock() | 	//o.clientsRwLocker.Lock()
 | ||||||
| 	defer o.clientsRwLocker.Unlock() | 	//defer o.clientsRwLocker.Unlock()
 | ||||||
| 	var msg = WsSessionInfo{ | 	var msg = WsSessionInfo{ | ||||||
| 		Type:    msgType, | 		Type:    msgType, | ||||||
| 		Content: message, | 		Content: message, | ||||||
| @ -209,26 +209,18 @@ func (o *ChatRoom) SendSessionMessage(sender *accountFiee.ChatUserData, sessionI | |||||||
| 	fmt.Println("ChatRoom.SendSessionMessage ------------------3") | 	fmt.Println("ChatRoom.SendSessionMessage ------------------3") | ||||||
| 	usableClients := []*Client{} | 	usableClients := []*Client{} | ||||||
| 	fmt.Printf("sessionId:[%s],客户端数量%d\n", sessionId, len(o.Session[sessionId])) | 	fmt.Printf("sessionId:[%s],客户端数量%d\n", sessionId, len(o.Session[sessionId])) | ||||||
| 	pushed := false |  | ||||||
| 	for i, client := range o.Session[sessionId] { | 	for i, client := range o.Session[sessionId] { | ||||||
| 		if client != nil { | 		if client != nil { | ||||||
| 			_, exist := o.clients[client.UserId][client.ClientId] | 			_, exist := o.clients[client.UserId][client.ClientId] | ||||||
| 			if exist { | 			if exist { | ||||||
| 				usableClients = append(usableClients, o.Session[sessionId][i]) | 				usableClients = append(usableClients, o.Session[sessionId][i]) | ||||||
| 				if !pushed { | 				go o.pushEvent(EventChatMessage, EventProgressBefore, sender, o.Session[sessionId][i], message) | ||||||
| 					go o.pushEvent(EventChatMessage, EventProgressBefore, sender, o.Session[sessionId][i], message) |  | ||||||
| 					pushed = true |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		fmt.Printf("client:%+v\n", client) | 		fmt.Printf("client:%+v\n", client) | ||||||
| 		pushed = false |  | ||||||
| 		if client != nil && (client.UserId != sender.ID || sender.Role == 3) { | 		if client != nil && (client.UserId != sender.ID || sender.Role == 3) { | ||||||
| 			client.Send <- msgBytes | 			client.Send <- msgBytes | ||||||
| 			if !pushed { | 			go o.pushEvent(EventChatMessage, EventProgressAfter, sender, o.Session[sessionId][i], message) | ||||||
| 				go o.pushEvent(EventChatMessage, EventProgressAfter, sender, o.Session[sessionId][i], message) |  | ||||||
| 				pushed = true |  | ||||||
| 			} |  | ||||||
| 			userIdInSession = append(userIdInSession, client.UserId) | 			userIdInSession = append(userIdInSession, client.UserId) | ||||||
| 		} | 		} | ||||||
| 		//client.Send <- msgBytes
 | 		//client.Send <- msgBytes
 | ||||||
|  | |||||||
| @ -40,42 +40,39 @@ func AuthorizationVerify(sourceData []byte) (userInfo *accountFiee.ChatUserData, | |||||||
| 	var ctx = context.Background() | 	var ctx = context.Background() | ||||||
| 	var accountInfo accountFiee.ChatUserData | 	var accountInfo accountFiee.ChatUserData | ||||||
| 	//fiee token校验
 | 	//fiee token校验
 | ||||||
| 	switch msg.Content.Domain { | 	var fieeJwtInfo *jwt.Claims | ||||||
| 	case "app": | 	fieeJwtInfo, err = jwt.ParseToken(msg.Content.Auth, m.JWTSecret) | ||||||
| 		var fieeJwtInfo *jwt.Claims | 	if err != nil { | ||||||
| 		fieeJwtInfo, err = jwt.ParseToken(msg.Content.Auth, m.JWTSecret) | 		check = false | ||||||
|  | 		fmt.Printf("fiee token parse err:%v\n", err) | ||||||
|  | 	} else { | ||||||
|  | 		fmt.Printf("fieeJwtInfo :%#v\n", fieeJwtInfo) | ||||||
|  | 		accountInfo.Origin = config.AppConfig.System.Domain | ||||||
|  | 		//accountInfo.OriginId = int64(fieeJwtInfo.ID)
 | ||||||
|  | 		accountInfo.Account = fieeJwtInfo.Account | ||||||
|  | 		accountInfo.NickName = fieeJwtInfo.NickName | ||||||
|  | 		infoReq := &accountFiee.UserByTelRequest{ | ||||||
|  | 			Tel:    fieeJwtInfo.Phone, | ||||||
|  | 			Domain: config.AppConfig.System.Domain, | ||||||
|  | 		} | ||||||
|  | 		var accInfo *accountFiee.UserInfoResponse | ||||||
|  | 		accInfo, err = service.AccountFieeProvider.UserByTel(ctx, infoReq) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			check = false | 			check = false | ||||||
| 			fmt.Printf("fiee token parse err:%v\n", err) | 			fmt.Printf("err:%#v\n", err) | ||||||
| 		} else { | 		} else if accInfo != nil { | ||||||
| 			fmt.Printf("fieeJwtInfo :%#v\n", fieeJwtInfo) | 			fmt.Printf("fiee accInfo :%#v\n", accInfo) | ||||||
| 			accountInfo.Origin = config.AppConfig.System.Domain | 			accountInfo.OriginId = int64(accInfo.Id) | ||||||
| 			//accountInfo.OriginId = int64(fieeJwtInfo.ID)
 | 			accountInfo.Account = accInfo.TelNum | ||||||
| 			accountInfo.Account = fieeJwtInfo.Account | 			accountInfo.Avatar = accInfo.GroupPhoto | ||||||
| 			accountInfo.NickName = fieeJwtInfo.NickName | 			if accInfo.Name != "" { | ||||||
| 			infoReq := &accountFiee.UserByTelRequest{ | 				accountInfo.NickName = accInfo.Name | ||||||
| 				Tel:    fieeJwtInfo.Phone, |  | ||||||
| 				Domain: config.AppConfig.System.Domain, |  | ||||||
| 			} |  | ||||||
| 			var accInfo *accountFiee.UserInfoResponse |  | ||||||
| 			accInfo, err = service.AccountFieeProvider.UserByTel(ctx, infoReq) |  | ||||||
| 			if err != nil { |  | ||||||
| 				check = false |  | ||||||
| 				fmt.Printf("err:%#v\n", err) |  | ||||||
| 			} else if accInfo != nil { |  | ||||||
| 				fmt.Printf("fiee accInfo :%#v\n", accInfo) |  | ||||||
| 				accountInfo.OriginId = int64(accInfo.Id) |  | ||||||
| 				accountInfo.Account = accInfo.TelNum |  | ||||||
| 				accountInfo.Avatar = accInfo.GroupPhoto |  | ||||||
| 				if accInfo.Name != "" { |  | ||||||
| 					accountInfo.NickName = accInfo.Name |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	case "fontree": | 	} | ||||||
|  | 	if !check { | ||||||
| 		msg.Content.Auth, err = secret.GetJwtFromStr(msg.Content.Auth) | 		msg.Content.Auth, err = secret.GetJwtFromStr(msg.Content.Auth) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fmt.Println("token解析失败:", err.Error()) |  | ||||||
| 			check = false | 			check = false | ||||||
| 		} else { | 		} else { | ||||||
| 			var fontreeJwtInfo *account.DecryptJwtResponse | 			var fontreeJwtInfo *account.DecryptJwtResponse | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerList(c *gin.Context) { | |||||||
| 	var protoReq = accountFiee.GetChatAutoReplyRulerListRequest{Query: &accountFiee.ChatAutoReplyRulerData{}} | 	var protoReq = accountFiee.GetChatAutoReplyRulerListRequest{Query: &accountFiee.ChatAutoReplyRulerData{}} | ||||||
| 	utils.RequestDataConvert(&req, &protoReq) | 	utils.RequestDataConvert(&req, &protoReq) | ||||||
| 	if req.RuleType != "" { | 	if req.RuleType != "" { | ||||||
| 		protoReq.Where = fmt.Sprintf("ruler LIKE '%%%s\":{\"enable\":true%%'", req.RuleType) | 		protoReq.Where = fmt.Sprintf("ruler LIKE '%%\"%s\":{\"enable\":true}%%'", req.RuleType) | ||||||
| 	} | 	} | ||||||
| 	resp, err := service.AccountFieeProvider.GetChatAutoReplyRulerList(c, &protoReq) | 	resp, err := service.AccountFieeProvider.GetChatAutoReplyRulerList(c, &protoReq) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
| @ -99,7 +99,7 @@ func (cr ChatCache) GetChatRecord(sessionId string) (data []*accountFiee.ChatRec | |||||||
| 		//log.Print("获取聊天记录失败", zap.Error(err))
 | 		//log.Print("获取聊天记录失败", zap.Error(err))
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	//fmt.Printf("cache data: %+v", string(messages))
 | 	fmt.Printf("cache data: %+v", string(messages)) | ||||||
| 	if len(messages) > 0 { | 	if len(messages) > 0 { | ||||||
| 		_ = json.Unmarshal(messages, &data) | 		_ = json.Unmarshal(messages, &data) | ||||||
| 	} | 	} | ||||||
| @ -144,36 +144,29 @@ func (cr ChatCache) IncreaseNewMessageTotal(ownerId int64, sessionId string) (er | |||||||
| 
 | 
 | ||||||
| // 重置新消息数量
 | // 重置新消息数量
 | ||||||
| func (cr ChatCache) ResetNewMessageTotal(ownerId int64, sessionId string, total ...int64) error { | func (cr ChatCache) ResetNewMessageTotal(ownerId int64, sessionId string, total ...int64) error { | ||||||
| 	fmt.Printf("ResetNewMessageTotal: %d ,sessionId:%s ,total:%v\n", ownerId, sessionId, total) |  | ||||||
| 	chatCacheLocker.Lock() | 	chatCacheLocker.Lock() | ||||||
| 	defer chatCacheLocker.Unlock() | 	defer chatCacheLocker.Unlock() | ||||||
| 	var tl int64 | 	var tl int64 | ||||||
| 	if len(total) > 0 { | 	if len(total) > 0 { | ||||||
| 		tl = total[0] | 		tl = total[0] | ||||||
| 	} | 	} | ||||||
| 	fmt.Println("ResetNewMessageTotal tl:", tl) |  | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	data := cr.GetNewMessageStat(ctx, ownerId) | 	data := cr.GetNewMessageStat(ctx, ownerId) | ||||||
| 	fmt.Printf("ResetNewMessageTotal data:%+v\n", data) |  | ||||||
| 	found := false | 	found := false | ||||||
| 	for i, v := range data { | 	for i, v := range data { | ||||||
| 		if v.SessionId == sessionId { | 		if v.SessionId == sessionId { | ||||||
| 			found = true | 			found = true | ||||||
| 			data[i].Total = tl | 			data[i].Total = tl | ||||||
| 			fmt.Println("ResetNewMessageTotal found!") |  | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !found { | 	if !found { | ||||||
| 		fmt.Println("ResetNewMessageTotal not found!") |  | ||||||
| 		data = append(data, dto.UserMsgStatic{ | 		data = append(data, dto.UserMsgStatic{ | ||||||
| 			SessionId: sessionId, | 			SessionId: sessionId, | ||||||
| 			Total:     tl, | 			Total:     tl, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 	err := cr.coverOwnerNewMessageStat(ctx, ownerId, data) | 	return cr.coverOwnerNewMessageStat(ctx, ownerId, data) | ||||||
| 	fmt.Println("ResetNewMessageTotal result:", err) |  | ||||||
| 	return err |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (cr ChatCache) RecountNewMessageTotal(ownerId int64) { | func (cr ChatCache) RecountNewMessageTotal(ownerId int64) { | ||||||
| @ -201,8 +194,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) { | |||||||
| 		if len(messages) > 0 { | 		if len(messages) > 0 { | ||||||
| 			_ = json.Unmarshal(messages, &data) | 			_ = json.Unmarshal(messages, &data) | ||||||
| 		} | 		} | ||||||
| 		lastIndex := strings.Count(key, ":") | 		var sessionId = strings.Split(key, ":")[1] | ||||||
| 		var sessionId = strings.Split(key, ":")[lastIndex] |  | ||||||
| 		countMap[sessionId] = 0 | 		countMap[sessionId] = 0 | ||||||
| 		for _, v := range data { | 		for _, v := range data { | ||||||
| 			if v.WaiterRead == 2 { //统计未读消息数量
 | 			if v.WaiterRead == 2 { //统计未读消息数量
 | ||||||
|  | |||||||
| @ -31,10 +31,8 @@ type MessageMedia struct { | |||||||
| // 客户端发送消息请求,使用api发送消息
 | // 客户端发送消息请求,使用api发送消息
 | ||||||
| type NewMessageRequest struct { | type NewMessageRequest struct { | ||||||
| 	Waiter    bool   `json:"waiter"` //是否是客服发送,客服没有userId
 | 	Waiter    bool   `json:"waiter"` //是否是客服发送,客服没有userId
 | ||||||
| 	Robot     bool   `json:"-"`      //是否机器人发送
 |  | ||||||
| 	SessionId string `json:"sessionId"` | 	SessionId string `json:"sessionId"` | ||||||
| 	Message | 	Message | ||||||
| 	AtUserId int64 `json:"atUserId"` //指定发送给sessionId中的某一个用户
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 服务端接收到消息后,使用websocket发送给userId关联的客户端,通知客户端有新消息,然后调用接口获取消息
 | // 服务端接收到消息后,使用websocket发送给userId关联的客户端,通知客户端有新消息,然后调用接口获取消息
 | ||||||
|  | |||||||
| @ -230,9 +230,9 @@ func (cr ChatHandler) MessageList(c *gin.Context) { | |||||||
| 		service.Error(c, err) | 		service.Error(c, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	//domain := c.GetHeader("domain")
 | 	domain := c.GetHeader("domain") | ||||||
| 	//fmt.Println("MessageList domain:", domain)
 | 	fmt.Println("MessageList domain:", domain) | ||||||
| 	if (request.Direction == 0 && !request.Recent) || (request.Direction > 0 && request.Recent) { | 	if (request.Direction == 0 && request.Recent == false) || (request.Direction > 0 && request.Recent == true) { | ||||||
| 		service.Error(c, errors.New("组合条件校验失败")) | 		service.Error(c, errors.New("组合条件校验失败")) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -249,7 +249,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) { | |||||||
| 		service.Success(c, resp) | 		service.Success(c, resp) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	accessUser, code := jwt.ParseToChatUser(c) | 	chatUser, code := jwt.ParseToChatUser(c) | ||||||
| 	if code != 0 { | 	if code != 0 { | ||||||
| 		service.ErrWithCode(c, code) | 		service.ErrWithCode(c, code) | ||||||
| 		return | 		return | ||||||
| @ -261,16 +261,16 @@ func (cr ChatHandler) MessageList(c *gin.Context) { | |||||||
| 	//		return
 | 	//		return
 | ||||||
| 	//	}
 | 	//	}
 | ||||||
| 	//}
 | 	//}
 | ||||||
| 	messages := cr.cache.GetChatRecord(request.SessionId) | 	//messages := cr.cache.GetChatRecord(request.SessionId)
 | ||||||
| 	//messages := []*accountFiee.ChatRecordData{}
 | 	messages := []*accountFiee.ChatRecordData{} | ||||||
| 	var returnDataIdList = make([]int64, 0) | 	var returnDataIdList = make([]int64, 0) | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		//获取最新数据时,重置新消息数量统计
 | 		//获取最新数据时,重置新消息数量统计
 | ||||||
| 		if request.Direction == 2 || request.Recent { | 		if request.Direction == 2 || request.Recent { | ||||||
| 			cr.cache.ResetNewMessageTotal(accessUser.ID, request.SessionId) | 			cr.cache.ResetNewMessageTotal(chatUser.ID, request.SessionId) | ||||||
| 		} | 		} | ||||||
| 		//设置消息已被客服阅读,当客服重新通过通过websocket连接时,这些消息将不被纳入新消息数量统计
 | 		//设置消息已被客服阅读,当客服重新通过通过websocket连接时,这些消息将不被纳入新消息数量统计
 | ||||||
| 		if len(returnDataIdList) > 0 && accessUser.Role == 2 { | 		if len(returnDataIdList) > 0 && domain == "fontree" { | ||||||
| 			for _, hasReadId := range returnDataIdList { | 			for _, hasReadId := range returnDataIdList { | ||||||
| 				for i, message := range messages { | 				for i, message := range messages { | ||||||
| 					if message.ID == hasReadId { | 					if message.ID == hasReadId { | ||||||
| @ -368,8 +368,6 @@ func (cr ChatHandler) MessageList(c *gin.Context) { | |||||||
| 			resp[i].Message.Media = []dto.MessageMedia{} | 			resp[i].Message.Media = []dto.MessageMedia{} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if accessUser.Role == 1 { |  | ||||||
| 	} |  | ||||||
| 	service.Success(c, resp) | 	service.Success(c, resp) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -503,21 +501,10 @@ func (cr ChatHandler) UserMessageStat(c *gin.Context) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if result[i].Name == "" { | 		if result[i].Name == "" { | ||||||
| 			result[i].Name = beautifulZeroNameWithPhone(result[i].Name, result[i].UserId) | 			result[i].Name = beautifulZeroName(result[i].Name, result[i].UserId) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	reverse(result) | 	reverse(result) | ||||||
| 	if chatUser.Role == 1 { |  | ||||||
| 		userSessionId := fmt.Sprintf("%d", chatUser.ID) |  | ||||||
| 		newResp := []dto.UserMsgStatic{} |  | ||||||
| 		for _, v := range result { |  | ||||||
| 			if v.SessionId == userSessionId { |  | ||||||
| 				newResp = append(newResp, v) |  | ||||||
| 				service.Success(c, newResp) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	service.Success(c, result) | 	service.Success(c, result) | ||||||
| } | } | ||||||
| func reverse(slice []dto.UserMsgStatic) { | func reverse(slice []dto.UserMsgStatic) { | ||||||
| @ -576,10 +563,6 @@ func (cr ChatHandler) UserDetail(c *gin.Context) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	//fmt.Printf("chatUser:%#v\n", chatUser)
 |  | ||||||
| 	//if chatUser.Origin == "fiee" {
 |  | ||||||
| 	//	chatUser.Origin = "app"
 |  | ||||||
| 	//}
 |  | ||||||
| 	resp, err := service.AccountFieeProvider.Info(c, &accountFiee.InfoRequest{ID: uint64(chatUser.OriginId), Domain: chatUser.Origin}) | 	resp, err := service.AccountFieeProvider.Info(c, &accountFiee.InfoRequest{ID: uint64(chatUser.OriginId), Domain: chatUser.Origin}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		service.Error(c, err) | 		service.Error(c, err) | ||||||
| @ -605,31 +588,3 @@ func (cr ChatHandler) UserDetail(c *gin.Context) { | |||||||
| func beautifulZeroName(name string, userId int64) string { | func beautifulZeroName(name string, userId int64) string { | ||||||
| 	return utils.IfGec(name == "", fmt.Sprintf("未实名用户:%d", userId), name) | 	return utils.IfGec(name == "", fmt.Sprintf("未实名用户:%d", userId), name) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var userIdMapPhone = make(map[int64]string) |  | ||||||
| 
 |  | ||||||
| func beautifulZeroNameWithPhone(name string, userId int64) string { |  | ||||||
| 	var ctx = context.Background() |  | ||||||
| 	if name == "" { |  | ||||||
| 		telNum, ok := userIdMapPhone[userId] |  | ||||||
| 		if ok { |  | ||||||
| 			return telNum |  | ||||||
| 		} |  | ||||||
| 		chatUserRes, err := service.AccountFieeProvider.GetChatUserDetail(ctx, &accountFiee.GetChatUserByIdRequest{Id: userId}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return fmt.Sprintf("未实名用户:%d", userId) |  | ||||||
| 		} else { |  | ||||||
| 			if userRes, errs := service.AccountFieeProvider.Info(ctx, &accountFiee.InfoRequest{ |  | ||||||
| 				Domain: chatUserRes.Origin, |  | ||||||
| 				ID:     uint64(chatUserRes.OriginId), |  | ||||||
| 				Scene:  "", |  | ||||||
| 			}); errs != nil { |  | ||||||
| 				return fmt.Sprintf("未实名用户:%d", userId) |  | ||||||
| 			} else { |  | ||||||
| 				userIdMapPhone[userId] = userRes.TelNum |  | ||||||
| 				return userRes.TelNum |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return name |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -24,8 +24,8 @@ import ( | |||||||
| //}
 | //}
 | ||||||
| 
 | 
 | ||||||
| type AutoReply struct { | type AutoReply struct { | ||||||
| 	Response string                `json:"response"` | 	Response string           `json:"response"` | ||||||
| 	Rules    map[string]IRobotTask `json:"rules"` | 	Rules    map[string]IRule `json:"rules"` | ||||||
| } | } | ||||||
| type AutoReplyRule struct { | type AutoReplyRule struct { | ||||||
| 	Enable       bool     `json:"enable"` | 	Enable       bool     `json:"enable"` | ||||||
|  | |||||||
| @ -26,6 +26,3 @@ web端和后端交互式时,增删改查的规则配置是存放在rules对象 | |||||||
| - keywords :关键字回复 | - keywords :关键字回复 | ||||||
| - joinSession:用户打开聊天窗口后 | - joinSession:用户打开聊天窗口后 | ||||||
| - noReplyAfter:客服指定时间没有回复后 | - noReplyAfter:客服指定时间没有回复后 | ||||||
| 
 |  | ||||||
| ## 注意 |  | ||||||
| - 目前不支持用户多端登录,会导致用户收到重复消息 |  | ||||||
| @ -7,20 +7,26 @@ | |||||||
| package robot | package robot | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"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/asChat/chatCache" | ||||||
|  | 	"fonchain-fiee/pkg/service/asChat/dto" | ||||||
|  | 	"fonchain-fiee/pkg/service/asChat/logic" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // 回复规则
 | // 回复规则
 | ||||||
| type Reply struct { | type Reply struct { | ||||||
| 	Title    string |  | ||||||
| 	Response string | 	Response string | ||||||
| 	Rules    []IRobotTask | 	Rules    []IRule | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *Reply) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, rule IRobotTask) { | func (r *Reply) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) { | ||||||
| 	for _, rule = range r.Rules { | 	for _, rule := range r.Rules { | ||||||
| 		hit = rule.Hit(event, robotInfo) | 		hit, task = rule.Hit(event, robotInfo) | ||||||
| 		if hit { | 		if hit { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -28,219 +34,226 @@ func (r *Reply) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserDat | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //
 | // 规则接口
 | ||||||
| //// 规则接口
 | type IRule interface { | ||||||
| //type IRule interface {
 | 	Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) | ||||||
| //	Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask)
 | } | ||||||
| //}
 | 
 | ||||||
| //
 | // KeywordsRuleChecker 关键字回复
 | ||||||
| //// KeywordsRuleChecker 关键字回复
 | type ReplyWhenHitKeywords struct { | ||||||
| //type ReplyWhenHitKeywords struct {
 | 	Keywords []string `json:"keywords"` | ||||||
| //	Keywords []string `json:"keywords"`
 | } | ||||||
| //}
 | 
 | ||||||
| //
 | func NewReplyWhenHitKeywords(keywords []string) IRule { | ||||||
| //func NewReplyWhenHitKeywords(keywords []string) IRule {
 | 	return &ReplyWhenHitKeywords{Keywords: keywords} | ||||||
| //	return &ReplyWhenHitKeywords{Keywords: keywords}
 | } | ||||||
| //}
 | func (k ReplyWhenHitKeywords) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) { | ||||||
| //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 { | ||||||
| //	if event.EventType != ws.EventChatMessage || event.Msg == "" || event.Client == nil || event.ChatUser == nil {
 | 		return | ||||||
| //		return
 | 	} | ||||||
| //	}
 | 	//客服的消息不需要处理
 | ||||||
| //	if event.ChatUser.Role != 1 {
 | 	if event.ChatUser.Role == 2 { | ||||||
| //		return
 | 		return | ||||||
| //	}
 | 	} | ||||||
| //	for _, v := range k.Keywords {
 | 	for _, v := range k.Keywords { | ||||||
| //		if strings.Contains(event.Msg, v) {
 | 		if strings.Contains(event.Msg, v) { | ||||||
| //			hit = true
 | 			hit = true | ||||||
| //			break
 | 			break | ||||||
| //		}
 | 		} | ||||||
| //	}
 | 	} | ||||||
| //	atUserId := event.Client.UserId
 | 	task = RobotTask{ | ||||||
| //	task = RobotTask{
 | 		ChatUser: event.ChatUser, | ||||||
| //		ChatUser: event.ChatUser,
 | 		Run: func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error { | ||||||
| //		Run: func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error {
 | 			return logic.NewMessage(context.Background(), cache, Sender, dto.NewMessageRequest{ | ||||||
| //			return logic.NewMessage(context.Background(), cache, Sender, dto.NewMessageRequest{
 | 				Waiter:    true, | ||||||
| //				Waiter:    true,
 | 				SessionId: event.Client.SessionId, | ||||||
| //				Robot:     true,
 | 				Message: dto.Message{ | ||||||
| //				AtUserId:  atUserId,
 | 					MsgType:    1, | ||||||
| //				SessionId: event.Client.SessionId,
 | 					Text:       msg, | ||||||
| //				Message: dto.Message{
 | 					LocalStamp: time.Now().Unix(), | ||||||
| //					MsgType:    1,
 | 				}, | ||||||
| //					Text:       msg,
 | 			}) | ||||||
| //					LocalStamp: time.Now().Unix(),
 | 		}, | ||||||
| //				},
 | 	} | ||||||
| //			})
 | 	//logicFunc = func(content string, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData) error {
 | ||||||
| //		},
 | 	//	//var notice = dto.MessageListType{}
 | ||||||
| //	}
 | 	//	//newRecord := &accountFiee.ChatRecordData{
 | ||||||
| //	return
 | 	//	//	SessionId: wsClient.SessionId,
 | ||||||
| //}
 | 	//	//	UserId:    wsClient.UserId,
 | ||||||
| //
 | 	//	//	Name:      chatUser.NickName,
 | ||||||
| //// 用户打开聊天会话直接发送
 | 	//	//	Avatar:    robotInfo.Avatar,
 | ||||||
| //type ReplyWhenUserJoinSession struct {
 | 	//	//	MsgType:   1,
 | ||||||
| //}
 | 	//	//	Content:   content,
 | ||||||
| //
 | 	//	//}
 | ||||||
| //func NewReplyWhenUserJoinSession() IRule {
 | 	//	//notice.BuildMessage(newRecord)
 | ||||||
| //	return &ReplyWhenUserJoinSession{}
 | 	//	//_, err := consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
 | ||||||
| //}
 | 	//	//return err
 | ||||||
| //
 | 	//	err := logic.NewMessage(context.Background(), cache, chatUser, dto.NewMessageRequest{
 | ||||||
| //func (k ReplyWhenUserJoinSession) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) {
 | 	//		Waiter:    true,
 | ||||||
| //	if event.EventType != ws.EventUserJoin {
 | 	//		SessionId: wsClient.SessionId,
 | ||||||
| //		return
 | 	//		Message: dto.Message{
 | ||||||
| //	}
 | 	//			MsgType:    1,
 | ||||||
| //	if event.Client == nil {
 | 	//			Text:       msg,
 | ||||||
| //		return
 | 	//			LocalStamp: time.Now().Unix(),
 | ||||||
| //	}
 | 	//		},
 | ||||||
| //	clientSessionId := event.Client.SessionId
 | 	//	})
 | ||||||
| //	atUserId := event.Client.UserId
 | 	//	return err
 | ||||||
| //	ctx := context.Background()
 | 	//}
 | ||||||
| //	queryRes, err := service.AccountFieeProvider.GetChatRecordList(ctx, &accountFiee.GetChatRecordListRequest{
 | 	return | ||||||
| //		Query: &accountFiee.ChatRecordData{
 | } | ||||||
| //			SessionId: event.Client.SessionId,
 | 
 | ||||||
| //		},
 | // 用户打开聊天会话直接发送
 | ||||||
| //		Page:     1,
 | type ReplyWhenUserJoinSession struct { | ||||||
| //		PageSize: 1,
 | } | ||||||
| //		Order:    "created_at desc",
 | 
 | ||||||
| //	})
 | func NewReplyWhenUserJoinSession() IRule { | ||||||
| //	if err != nil {
 | 	return &ReplyWhenUserJoinSession{} | ||||||
| //		return
 | } | ||||||
| //	}
 | 
 | ||||||
| //	//如果最近一次的消息也是机器人发送的,就不再发送了
 | func (k ReplyWhenUserJoinSession) Hit(event ws.ListenEventData, robotInfo *accountFiee.ChatUserData) (hit bool, task RobotTask) { | ||||||
| //	for i, v := range queryRes.List {
 | 	if event.EventType != ws.EventUserJoin { | ||||||
| //		if i == 0 {
 | 		return | ||||||
| //			if v.UserId == robotInfo.ID {
 | 	} | ||||||
| //				return
 | 	if event.Client == nil { | ||||||
| //			} else {
 | 		return | ||||||
| //				break
 | 	} | ||||||
| //			}
 | 	ctx := context.Background() | ||||||
| //		}
 | 	queryRes, err := service.AccountFieeProvider.GetChatRecordList(ctx, &accountFiee.GetChatRecordListRequest{ | ||||||
| //	}
 | 		Query: &accountFiee.ChatRecordData{ | ||||||
| //	hit = true
 | 			SessionId: event.Client.SessionId, | ||||||
| //	if event.ChatUser == nil {
 | 		}, | ||||||
| //		event.ChatUser, err = service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: event.Client.UserId})
 | 		Page:     1, | ||||||
| //		if err != nil {
 | 		PageSize: 1, | ||||||
| //			return
 | 		Order:    "created_at desc", | ||||||
| //		}
 | 	}) | ||||||
| //	}
 | 	if err != nil { | ||||||
| //	task = RobotTask{
 | 		return | ||||||
| //		ChatUser: event.ChatUser,
 | 	} | ||||||
| //		Run: func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error {
 | 	//如果最近一次的消息也是机器人发送的,就不再发送了
 | ||||||
| //			return logic.NewMessage(ctx, cache, Sender, dto.NewMessageRequest{
 | 	for i, v := range queryRes.List { | ||||||
| //				Waiter:    true,
 | 		if i == 0 { | ||||||
| //				Robot:     true,
 | 			if v.UserId == robotInfo.ID { | ||||||
| //				AtUserId:  atUserId,
 | 				return | ||||||
| //				SessionId: clientSessionId,
 | 			} else { | ||||||
| //				Message: dto.Message{
 | 				break | ||||||
| //					MsgType:    1,
 | 			} | ||||||
| //					Text:       msg,
 | 		} | ||||||
| //					LocalStamp: time.Now().Unix(),
 | 	} | ||||||
| //				},
 | 	hit = true | ||||||
| //			})
 | 	if event.ChatUser == nil { | ||||||
| //		},
 | 		event.ChatUser, err = service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: event.Client.UserId}) | ||||||
| //	}
 | 		if err != nil { | ||||||
| //	//logicFunc = func(msg string, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData) error {
 | 			return | ||||||
| //	//	//var notice = dto.MessageListType{}
 | 		} | ||||||
| //	//	//newRecord := &accountFiee.ChatRecordData{
 | 	} | ||||||
| //	//	//	SessionId: wsClient.SessionId,
 | 	task = RobotTask{ | ||||||
| //	//	//	UserId:    wsClient.UserId,
 | 		ChatUser: event.ChatUser, | ||||||
| //	//	//	Name:      wsClient.SessionId,
 | 		Run: func(msg string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error { | ||||||
| //	//	//	Avatar:    robotInfo.Avatar,
 | 			return logic.NewMessage(ctx, cache, Sender, dto.NewMessageRequest{ | ||||||
| //	//	//	MsgType:   1,
 | 				Waiter:    true, | ||||||
| //	//	//	Content:   msg,
 | 				SessionId: event.Client.SessionId, | ||||||
| //	//	//}
 | 				Message: dto.Message{ | ||||||
| //	//	//notice.BuildMessage(newRecord)
 | 					MsgType:    1, | ||||||
| //	//	//_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
 | 					Text:       msg, | ||||||
| //	//	err = logic.NewMessage(ctx, cache, chatUser, dto.NewMessageRequest{
 | 					LocalStamp: time.Now().Unix(), | ||||||
| //	//		Waiter:    true,
 | 				}, | ||||||
| //	//		SessionId: wsClient.SessionId,
 | 			}) | ||||||
| //	//		Message: dto.Message{
 | 		}, | ||||||
| //	//			MsgType:    1,
 | 	} | ||||||
| //	//			Text:       msg,
 | 	//logicFunc = func(msg string, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData) error {
 | ||||||
| //	//			LocalStamp: time.Now().Unix(),
 | 	//	//var notice = dto.MessageListType{}
 | ||||||
| //	//		},
 | 	//	//newRecord := &accountFiee.ChatRecordData{
 | ||||||
| //	//	})
 | 	//	//	SessionId: wsClient.SessionId,
 | ||||||
| //	//	return err
 | 	//	//	UserId:    wsClient.UserId,
 | ||||||
| //	//}
 | 	//	//	Name:      wsClient.SessionId,
 | ||||||
| //	return
 | 	//	//	Avatar:    robotInfo.Avatar,
 | ||||||
| //}
 | 	//	//	MsgType:   1,
 | ||||||
| //
 | 	//	//	Content:   msg,
 | ||||||
| //// 客服指定时间不回复则自动回复
 | 	//	//}
 | ||||||
| //
 | 	//	//notice.BuildMessage(newRecord)
 | ||||||
| //type ReplyWhenWaiterNoAction struct {
 | 	//	//_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
 | ||||||
| //	DelaySecond time.Duration
 | 	//	err = logic.NewMessage(ctx, cache, chatUser, dto.NewMessageRequest{
 | ||||||
| //}
 | 	//		Waiter:    true,
 | ||||||
| //
 | 	//		SessionId: wsClient.SessionId,
 | ||||||
| ////func NewReplyWhenWaiterNoAction(delaySecond time.Duration) *ReplyWhenWaiterNoAction {
 | 	//		Message: dto.Message{
 | ||||||
| ////	return &ReplyWhenWaiterNoAction{
 | 	//			MsgType:    1,
 | ||||||
| ////		DelaySecond: delaySecond,
 | 	//			Text:       msg,
 | ||||||
| ////	}
 | 	//			LocalStamp: time.Now().Unix(),
 | ||||||
| ////}
 | 	//		},
 | ||||||
| //
 | 	//	})
 | ||||||
| //func (k *ReplyWhenWaiterNoAction) Hit(event ws.ListenEventData, sender *accountFiee.ChatUserData) (hit bool, task RobotTask) {
 | 	//	return err
 | ||||||
| //	if event.Client == nil || event.EventType != ws.EventChatMessage {
 | 	//}
 | ||||||
| //		return
 | 	return | ||||||
| //	}
 | } | ||||||
| //	//客服和机器人的的消息不需要处理
 | 
 | ||||||
| //	if event.ChatUser.Role != 1 {
 | // 客服指定时间不回复则自动回复
 | ||||||
| //		return
 | 
 | ||||||
| //	}
 | type ReplyWhenWaiterNoAction struct { | ||||||
| //	hit = true // 立即保存SessionId的值
 | 	DelaySecond time.Duration | ||||||
| //
 | } | ||||||
| //	clientSessionId := event.Client.SessionId
 | 
 | ||||||
| //	atUserId := event.Client.UserId
 | func NewReplyWhenWaiterNoAction(delaySecond time.Duration) *ReplyWhenWaiterNoAction { | ||||||
| //	fmt.Printf("闭包前: clientSessionId=%s\n", clientSessionId)
 | 	return &ReplyWhenWaiterNoAction{ | ||||||
| //	task = RobotTask{
 | 		DelaySecond: delaySecond, | ||||||
| //		RunTime: time.Now().Add(k.DelaySecond * time.Second),
 | 	} | ||||||
| //		Run: func(content string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error {
 | } | ||||||
| //			// 记录闭包执行时的Client状态
 | 
 | ||||||
| //			fmt.Printf("闭包执行: clientSessionId=%s\n", clientSessionId)
 | func (k *ReplyWhenWaiterNoAction) Hit(event ws.ListenEventData, sender *accountFiee.ChatUserData) (hit bool, task RobotTask) { | ||||||
| //
 | 	if event.Client == nil || event.EventType != ws.EventChatMessage { | ||||||
| //			//如果客服已经回复则不发送消息
 | 		return | ||||||
| //			chatRecordListRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{
 | 	} | ||||||
| //				Query: &accountFiee.ChatRecordData{
 | 	//客服的消息不需要处理
 | ||||||
| //					SessionId: event.Client.SessionId,
 | 	if event.ChatUser.Role == 2 { | ||||||
| //				},
 | 		return | ||||||
| //				Page:     1,
 | 	} | ||||||
| //				PageSize: 1,
 | 	hit = true | ||||||
| //				Order:    "created_at desc",
 | 	task = RobotTask{ | ||||||
| //			})
 | 		RunTime: time.Now().Add(k.DelaySecond * time.Second), | ||||||
| //			if err != nil || chatRecordListRes.Total == 0 {
 | 		Run: func(content string, cache *chatCache.ChatCache, Sender *accountFiee.ChatUserData) error { | ||||||
| //				return err
 | 			//如果客服已经回复则不发送消息
 | ||||||
| //			}
 | 			chatRecordListRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{ | ||||||
| //			checkUserId := chatRecordListRes.List[0].UserId
 | 				Query: &accountFiee.ChatRecordData{ | ||||||
| //			checkChatUser, err := service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: checkUserId})
 | 					SessionId: event.Client.SessionId, | ||||||
| //			if err != nil || checkChatUser.Role != 1 {
 | 				}, | ||||||
| //				return err
 | 				Page:     1, | ||||||
| //			}
 | 				PageSize: 1, | ||||||
| //
 | 				Order:    "created_at desc", | ||||||
| //			//var notice = dto.MessageListType{}
 | 			}) | ||||||
| //			//newRecord := &accountFiee.ChatRecordData{
 | 			if err != nil || chatRecordListRes.Total == 0 { | ||||||
| //			//	SessionId: wsClient.SessionId,
 | 				return err | ||||||
| //			//	UserId:    wsClient.UserId,
 | 			} | ||||||
| //			//	Name:      chatUser.NickName,
 | 			checkUserId := chatRecordListRes.List[0].UserId | ||||||
| //			//	Avatar:    robotInfo.Avatar,
 | 			checkChatUser, err := service.AccountFieeProvider.GetChatUserDetail(context.Background(), &accountFiee.GetChatUserByIdRequest{Id: checkUserId}) | ||||||
| //			//	MsgType:   1,
 | 			if err != nil || checkChatUser.Role != 1 { | ||||||
| //			//	Content:   content,
 | 				return err | ||||||
| //			//}
 | 			} | ||||||
| //			//notice.BuildMessage(newRecord)
 | 
 | ||||||
| //			//_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
 | 			//var notice = dto.MessageListType{}
 | ||||||
| //			//return err
 | 			//newRecord := &accountFiee.ChatRecordData{
 | ||||||
| //			fmt.Println("延时回复 sessionID:", clientSessionId)
 | 			//	SessionId: wsClient.SessionId,
 | ||||||
| //			err = logic.NewMessage(context.Background(), cache, sender, dto.NewMessageRequest{
 | 			//	UserId:    wsClient.UserId,
 | ||||||
| //				Waiter:    true,
 | 			//	Name:      chatUser.NickName,
 | ||||||
| //				Robot:     true,
 | 			//	Avatar:    robotInfo.Avatar,
 | ||||||
| //				AtUserId:  atUserId,
 | 			//	MsgType:   1,
 | ||||||
| //				SessionId: clientSessionId,
 | 			//	Content:   content,
 | ||||||
| //				Message: dto.Message{
 | 			//}
 | ||||||
| //					MsgType:    1,
 | 			//notice.BuildMessage(newRecord)
 | ||||||
| //					Text:       content,
 | 			//_, err = consts.ChatRoom.SendSessionMessage(robotInfo, wsClient.SessionId, ws.NewChatMsgType, notice)
 | ||||||
| //					LocalStamp: time.Now().Unix(),
 | 			//return err
 | ||||||
| //				},
 | 			err = logic.NewMessage(context.Background(), cache, sender, dto.NewMessageRequest{ | ||||||
| //			})
 | 				Waiter:    true, | ||||||
| //			return err
 | 				SessionId: event.Client.SessionId, | ||||||
| //		},
 | 				Message: dto.Message{ | ||||||
| //		Response: "",
 | 					MsgType:    1, | ||||||
| //		ChatUser: event.ChatUser,
 | 					Text:       content, | ||||||
| //	}
 | 					LocalStamp: time.Now().Unix(), | ||||||
| //	return
 | 				}, | ||||||
| //
 | 			}) | ||||||
| //}
 | 			return err | ||||||
|  | 		}, | ||||||
|  | 		Response: "", | ||||||
|  | 		ChatUser: event.ChatUser, | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | |||||||
| @ -57,7 +57,7 @@ func NewRobot(cache *chatCache.ChatCache) *Robot { | |||||||
| 		cache: cache, | 		cache: cache, | ||||||
| 	} | 	} | ||||||
| 	err = r.ReloadRules(ctx) | 	err = r.ReloadRules(ctx) | ||||||
| 	fmt.Println("机器人规则加载完成,结果:", err) | 	fmt.Println("机器人规则加载失败") | ||||||
| 	consts.ChatRoom.RegisterEventListener(r.EventListener) | 	consts.ChatRoom.RegisterEventListener(r.EventListener) | ||||||
| 	go r.Run() | 	go r.Run() | ||||||
| 	return r | 	return r | ||||||
| @ -66,7 +66,7 @@ func NewRobot(cache *chatCache.ChatCache) *Robot { | |||||||
| type Robot struct { | type Robot struct { | ||||||
| 	Info      *accountFiee.ChatUserData //机器人信息
 | 	Info      *accountFiee.ChatUserData //机器人信息
 | ||||||
| 	Rules     []Reply                   //回复规则
 | 	Rules     []Reply                   //回复规则
 | ||||||
| 	DelayTask []IRobotTask              //演示任务
 | 	DelayTask []RobotTask               //演示任务
 | ||||||
| 	ticker    *time.Ticker              //定时器
 | 	ticker    *time.Ticker              //定时器
 | ||||||
| 	stopChan  chan struct{}             //停止管道
 | 	stopChan  chan struct{}             //停止管道
 | ||||||
| 	isRunning bool                      //运行状态
 | 	isRunning bool                      //运行状态
 | ||||||
| @ -140,12 +140,12 @@ func (r *Robot) Run() { | |||||||
| 				//return // 没有任务时退出
 | 				//return // 没有任务时退出
 | ||||||
| 			} | 			} | ||||||
| 			now := time.Now() | 			now := time.Now() | ||||||
| 			var remainingTasks []IRobotTask | 			var remainingTasks []RobotTask | ||||||
| 			for _, task := range r.DelayTask { | 			for _, task := range r.DelayTask { | ||||||
| 				if now.After(task.RunTime()) { | 				if now.After(task.RunTime) { | ||||||
| 					// 执行任务
 | 					// 执行任务
 | ||||||
| 					go func() { | 					go func() { | ||||||
| 						err := task.Run(r.cache) | 						err := task.Run(task.Response, r.cache, task.ChatUser) | ||||||
| 						if err != nil { | 						if err != nil { | ||||||
| 							log.Printf("聊天机器人[%d]延时回复消息失败:%v", r.Info.ID, err) | 							log.Printf("聊天机器人[%d]延时回复消息失败:%v", r.Info.ID, err) | ||||||
| 						} else { | 						} else { | ||||||
| @ -163,26 +163,23 @@ func (r *Robot) Run() { | |||||||
| 			return | 			return | ||||||
| 		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) | ||||||
| 			r.mu.Lock() |  | ||||||
| 			for _, ruleResponse := range r.Rules { | 			for _, ruleResponse := range r.Rules { | ||||||
| 				hit, task := ruleResponse.Hit(event, r.Info) | 				hit, task := ruleResponse.Hit(event, r.Info) | ||||||
| 				if hit { | 				if hit { | ||||||
| 					fmt.Println("命中规则:", ruleResponse.Title) | 					if task.RunTime.IsZero() { | ||||||
| 					if task.RunTime().IsZero() { | 						err := task.Run(ruleResponse.Response, r.cache, r.Info) | ||||||
| 						task.SetResponse(ruleResponse.Response) |  | ||||||
| 						err := task.Run(r.cache) |  | ||||||
| 						if err != nil { | 						if err != nil { | ||||||
| 							log.Printf("robot 执行任务失败:%v\n", err) | 							log.Printf("robot 执行任务失败:%v\n", err) | ||||||
| 						} | 						} | ||||||
| 					} else { | 					} else { | ||||||
| 						ruleResponse := ruleResponse | 						ruleResponse := ruleResponse | ||||||
| 						task.SetResponse(ruleResponse.Response) | 						task.Response = ruleResponse.Response | ||||||
| 						r.RegisterDelayTask(task) | 						r.RegisterDelayTask(task) | ||||||
|  | 
 | ||||||
| 					} | 					} | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			r.mu.Unlock() |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -195,7 +192,9 @@ func (r *Robot) Stop() { | |||||||
| 	} | 	} | ||||||
| 	r.mu.Unlock() | 	r.mu.Unlock() | ||||||
| } | } | ||||||
| func (r *Robot) RegisterDelayTask(task IRobotTask) { | func (r *Robot) RegisterDelayTask(task RobotTask) { | ||||||
|  | 	r.mu.Lock() | ||||||
|  | 	defer r.mu.Unlock() | ||||||
| 	if task.Run == nil { | 	if task.Run == nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ | |||||||
| package robot | package robot | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/dto" | 	"fonchain-fiee/pkg/service/asChat/dto" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
| @ -15,7 +14,6 @@ import ( | |||||||
| // 自动回复规则结构转换
 | // 自动回复规则结构转换
 | ||||||
| func ParseReplyRule(data *dto.ChatAutoReplyData) (r Reply) { | func ParseReplyRule(data *dto.ChatAutoReplyData) (r Reply) { | ||||||
| 	r.Response = data.Response | 	r.Response = data.Response | ||||||
| 	r.Title = data.Title |  | ||||||
| 	for ruleName, v := range data.Rules { | 	for ruleName, v := range data.Rules { | ||||||
| 		if !v.Enable { | 		if !v.Enable { | ||||||
| 			continue | 			continue | ||||||
| @ -28,7 +26,6 @@ func ParseReplyRule(data *dto.ChatAutoReplyData) (r Reply) { | |||||||
| 			} else { | 			} else { | ||||||
| 				keywords = strings.Split(v.Content, ",") | 				keywords = strings.Split(v.Content, ",") | ||||||
| 			} | 			} | ||||||
| 			fmt.Println("ParseReplyRule 解析keywords:", keywords) |  | ||||||
| 			r.Rules = append(r.Rules, NewReplyWhenHitKeywords(keywords)) | 			r.Rules = append(r.Rules, NewReplyWhenHitKeywords(keywords)) | ||||||
| 		case "joinSession": //加入聊天后回复
 | 		case "joinSession": //加入聊天后回复
 | ||||||
| 			r.Rules = append(r.Rules, NewReplyWhenUserJoinSession()) | 			r.Rules = append(r.Rules, NewReplyWhenUserJoinSession()) | ||||||
|  | |||||||
| @ -1,98 +0,0 @@ | |||||||
| // Package robot -----------------------------
 |  | ||||||
| // @file      : ruler_ReplyWhenWaiterNoAction.go
 |  | ||||||
| // @author    : JJXu
 |  | ||||||
| // @contact   : wavingbear@163.com
 |  | ||||||
| // @time      : 2025/6/13 18:02
 |  | ||||||
| // -------------------------------------------
 |  | ||||||
| package robot |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"fonchain-fiee/api/accountFiee" |  | ||||||
| 	"fonchain-fiee/pkg/common/ws" |  | ||||||
| 	"fonchain-fiee/pkg/service" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/chatCache" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/dto" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/logic" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type IRobotTask interface { |  | ||||||
| 	Hit(event ws.ListenEventData, sender *accountFiee.ChatUserData) (hit bool) |  | ||||||
| 	Run(cache *chatCache.ChatCache) error |  | ||||||
| 	RunTime() time.Time |  | ||||||
| 	SetResponse(response string) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 客服指定时间不回复则自动回复
 |  | ||||||
| func NewReplyWhenWaiterNoAction(delaySecond time.Duration) IRobotTask { |  | ||||||
| 	return &RobotTaskReplyWhenWaiterNoAction{ |  | ||||||
| 		delaySecond: delaySecond, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type RobotTaskReplyWhenWaiterNoAction struct { |  | ||||||
| 	runTime     time.Time |  | ||||||
| 	Response    string |  | ||||||
| 	Receiver    *accountFiee.ChatUserData |  | ||||||
| 	Sender      *accountFiee.ChatUserData |  | ||||||
| 	Msg         string |  | ||||||
| 	Resp        string |  | ||||||
| 	delaySecond time.Duration |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *RobotTaskReplyWhenWaiterNoAction) Hit(event ws.ListenEventData, sender *accountFiee.ChatUserData) (hit bool) { |  | ||||||
| 	if event.Client == nil || event.EventType != ws.EventChatMessage { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	//客服和机器人的的消息不需要处理
 |  | ||||||
| 	if event.ChatUser.Role != 1 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	hit = true // 立即保存SessionId的值
 |  | ||||||
| 	r.Sender = sender |  | ||||||
| 	r.Receiver = event.ChatUser |  | ||||||
| 	r.runTime = time.Now().Add(r.delaySecond * time.Second) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *RobotTaskReplyWhenWaiterNoAction) Run(cache *chatCache.ChatCache) error { |  | ||||||
| 	clientSessionId := fmt.Sprintf("%d", r.Receiver.ID) |  | ||||||
| 	fmt.Println("延时回复 sessionID:", clientSessionId) |  | ||||||
| 	//如果客服已经回复则不发送消息
 |  | ||||||
| 	chatRecordListRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{ |  | ||||||
| 		Query: &accountFiee.ChatRecordData{ |  | ||||||
| 			SessionId: clientSessionId, |  | ||||||
| 		}, |  | ||||||
| 		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 |  | ||||||
| 	} |  | ||||||
| 	err = logic.NewMessage(context.Background(), cache, r.Sender, dto.NewMessageRequest{ |  | ||||||
| 		Waiter:    true, |  | ||||||
| 		Robot:     true, |  | ||||||
| 		AtUserId:  r.Receiver.ID, |  | ||||||
| 		SessionId: clientSessionId, |  | ||||||
| 		Message: dto.Message{ |  | ||||||
| 			MsgType:    1, |  | ||||||
| 			Text:       r.Resp, |  | ||||||
| 			LocalStamp: time.Now().Unix(), |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
| func (r *RobotTaskReplyWhenWaiterNoAction) RunTime() time.Time { |  | ||||||
| 	return r.runTime |  | ||||||
| } |  | ||||||
| func (r *RobotTaskReplyWhenWaiterNoAction) SetResponse(response string) { |  | ||||||
| 	r.Resp = response |  | ||||||
| } |  | ||||||
| @ -1,69 +0,0 @@ | |||||||
| package robot |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"fonchain-fiee/api/accountFiee" |  | ||||||
| 	"fonchain-fiee/pkg/common/ws" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/chatCache" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/dto" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/logic" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type RobotTaskWithKeyworkds struct { |  | ||||||
| 	runTime  time.Time |  | ||||||
| 	Response string |  | ||||||
| 	Receiver *accountFiee.ChatUserData |  | ||||||
| 	Sender   *accountFiee.ChatUserData |  | ||||||
| 	Msg      string |  | ||||||
| 	Resp     string |  | ||||||
| 	keywords []string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NewReplyWhenHitKeywords(keywords []string) IRobotTask { |  | ||||||
| 	return &RobotTaskWithKeyworkds{keywords: keywords} |  | ||||||
| } |  | ||||||
| func (r *RobotTaskWithKeyworkds) Hit(event ws.ListenEventData, sender *accountFiee.ChatUserData) (hit bool) { |  | ||||||
| 	if event.EventType != ws.EventChatMessage || event.Msg == "" || event.Client == nil || event.ChatUser == nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if event.ChatUser.Role != 1 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	r.Sender = sender |  | ||||||
| 	r.Receiver = event.ChatUser |  | ||||||
| 	for _, v := range r.keywords { |  | ||||||
| 		if strings.Contains(event.Msg, v) { |  | ||||||
| 			fmt.Printf("关键词比对:%s ----- %s : true", event.Msg, v) |  | ||||||
| 			hit = true |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		fmt.Printf("关键词比对:%s ----- %s: false", event.Msg, v) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *RobotTaskWithKeyworkds) Run(cache *chatCache.ChatCache) (err error) { |  | ||||||
| 	err = logic.NewMessage(context.Background(), cache, r.Sender, dto.NewMessageRequest{ |  | ||||||
| 		Waiter:    true, |  | ||||||
| 		Robot:     true, |  | ||||||
| 		AtUserId:  r.Receiver.ID, |  | ||||||
| 		SessionId: fmt.Sprintf("%d", r.Receiver.ID), |  | ||||||
| 		Message: dto.Message{ |  | ||||||
| 			MsgType:    1, |  | ||||||
| 			Text:       r.Resp, |  | ||||||
| 			LocalStamp: time.Now().Unix(), |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *RobotTaskWithKeyworkds) RunTime() time.Time { |  | ||||||
| 	return time.Time{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *RobotTaskWithKeyworkds) SetResponse(response string) { |  | ||||||
| 	r.Resp = response |  | ||||||
| } |  | ||||||
| @ -1,85 +0,0 @@ | |||||||
| package robot |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fonchain-fiee/api/accountFiee" |  | ||||||
| 	"fonchain-fiee/pkg/common/ws" |  | ||||||
| 	"fonchain-fiee/pkg/service" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/chatCache" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/dto" |  | ||||||
| 	"fonchain-fiee/pkg/service/asChat/logic" |  | ||||||
| 	"strconv" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func NewReplyWhenUserJoinSession() IRobotTask { |  | ||||||
| 	return &ReplyWhenUserJoinSession{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type ReplyWhenUserJoinSession struct { |  | ||||||
| 	Response  string |  | ||||||
| 	Sender    *accountFiee.ChatUserData |  | ||||||
| 	Msg       string |  | ||||||
| 	Resp      string |  | ||||||
| 	sessionId string |  | ||||||
| 	atUserId  int |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *ReplyWhenUserJoinSession) Hit(event ws.ListenEventData, sender *accountFiee.ChatUserData) (hit bool) { |  | ||||||
| 	if event.EventType != ws.EventUserJoin { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if event.Client == nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ctx := context.Background() |  | ||||||
| 	queryRes, err := service.AccountFieeProvider.GetChatRecordList(ctx, &accountFiee.GetChatRecordListRequest{ |  | ||||||
| 		Query: &accountFiee.ChatRecordData{ |  | ||||||
| 			SessionId: event.Client.SessionId, |  | ||||||
| 		}, |  | ||||||
| 		Page:     1, |  | ||||||
| 		PageSize: 1, |  | ||||||
| 		Order:    "created_at desc", |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	//如果最近一次的消息也是机器人发送的,就不再发送了
 |  | ||||||
| 	for i, v := range queryRes.List { |  | ||||||
| 		if i == 0 { |  | ||||||
| 			if v.UserId == sender.ID { |  | ||||||
| 				return |  | ||||||
| 			} else { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	hit = true |  | ||||||
| 	r.Sender = sender |  | ||||||
| 	r.sessionId = event.Client.SessionId |  | ||||||
| 	r.atUserId, _ = strconv.Atoi(event.Client.SessionId) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *ReplyWhenUserJoinSession) Run(cache *chatCache.ChatCache) (err error) { |  | ||||||
| 	err = logic.NewMessage(context.Background(), cache, r.Sender, dto.NewMessageRequest{ |  | ||||||
| 		Waiter:    true, |  | ||||||
| 		Robot:     true, |  | ||||||
| 		AtUserId:  int64(r.atUserId), |  | ||||||
| 		SessionId: r.sessionId, |  | ||||||
| 		Message: dto.Message{ |  | ||||||
| 			MsgType:    1, |  | ||||||
| 			Text:       r.Resp, |  | ||||||
| 			LocalStamp: time.Now().Unix(), |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *ReplyWhenUserJoinSession) RunTime() time.Time { |  | ||||||
| 	return time.Time{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *ReplyWhenUserJoinSession) SetResponse(response string) { |  | ||||||
| 	r.Resp = response |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user