package chat import ( "encoding/json" "os" "sort" "sync" "time" "bet24.com/log" "bet24.com/servers/common" "bet24.com/servers/coreservice/keyword" "bet24.com/servers/coreservice/serviceconfig" inventory "bet24.com/servers/micros/item_inventory/proto" item "bet24.com/servers/micros/item_inventory/proto" cash "bet24.com/servers/micros/money/proto" notification "bet24.com/servers/micros/notification/proto" platformconfig "bet24.com/servers/micros/platformconfig/proto" task "bet24.com/servers/micros/task/proto" user "bet24.com/servers/micros/userservices/proto" ) const ( Channel_World = iota // 世界频道, 目前只有一个世界频道,后续有需求再加 Channel_Speaker // 大喇叭频道 channel_Max ) const ( MessageType_Normal = iota // 普通消息 MessageType_Gift // 1礼物消息 MessageType_Room // 2房间消息 ) type Chat_MSg struct { ChannelID int //聊天频道, 目前只有世界频道 SendUserID int //发送者ID Vip int //VIP等级 NickName string //昵称 FaceUrl string //头像URL FaceId int //头像ID Sex int //性别 RecvUserID int //接收者ID,私聊备用 ChatMsg string //内容 SendTime string Decorations []user.UserDecoration MsgType int VipExpire int } type chatmgr struct { chatMsgList map[int][]Chat_MSg //key: ChannelID lock *sync.RWMutex robotChats []robot_chat } type robot_chat struct { UserId int // 用户id Msg string // 消息 Seconds int // 间隔时间(秒) BeginTime string // 开始时间 EndTime string // 截止时间 SendTime string // 发送时间 } const config_key = "robotchat_config" const refresh_config_sec = 600 func newChatMgr() *chatmgr { cm := &chatmgr{} cm.chatMsgList = make(map[int][]Chat_MSg) for i := Channel_World; i < channel_Max; i++ { var l []Chat_MSg cm.chatMsgList[i] = l } cm.lock = &sync.RWMutex{} cm.loadRobotChats() go cm.sendRefresh() log.Debug("chat manager running") return cm } func (m *chatmgr) loadRobotChats() { defer func() { time.AfterFunc(refresh_config_sec*time.Second, m.loadRobotChats) }() configString := platformconfig.GetConfig(config_key) if configString == "" { data, err := os.ReadFile("fishconf/robotchats.json") if err != nil { log.Release("gold2chipwheelmgr.loadData read robotchats failed") return } configString = string(data) platformconfig.SetConfig(config_key, configString) } else { log.Debug("chat.chatmgr loading config from redis") } m.lock.Lock() defer m.lock.Unlock() err := json.Unmarshal([]byte(configString), &m.robotChats) if err != nil { log.Release("chat.chatmgr.loadData Unmarshal robotchats failed err:%v", err) return } } // 获取机器人信息 func (cm *chatmgr) getRobotChatInfo(userId int) robot_chat { cm.lock.RLock() defer cm.lock.RUnlock() for _, v := range cm.robotChats { if v.UserId == userId { return v } } return robot_chat{} } // 获取机器人列表 func (cm *chatmgr) getRobotChatList() []robot_chat { cm.lock.RLock() defer cm.lock.RUnlock() sort.SliceStable(cm.robotChats, func(i, j int) bool { return cm.robotChats[i].EndTime > cm.robotChats[j].EndTime }) return cm.robotChats } // 添加机器人信息 func (cm *chatmgr) addRobotChat(userId int, msg string, seconds int, beginTime, endTime string) bool { if info := cm.getRobotChatInfo(userId); info.UserId > 0 { return cm.updateRobotChat(userId, msg, seconds, beginTime, endTime) } cm.lock.Lock() defer cm.lock.Unlock() cm.robotChats = append(cm.robotChats, robot_chat{ UserId: userId, Msg: msg, Seconds: seconds, BeginTime: beginTime, EndTime: endTime, SendTime: time.Now().Format(common.Layout), }) go cm.setRobotChatConfig() return true } // 修改机器人信息 func (cm *chatmgr) updateRobotChat(userId int, msg string, seconds int, beginTime, endTime string) bool { cm.lock.Lock() defer cm.lock.Unlock() for i := 0; i < len(cm.robotChats); i++ { if cm.robotChats[i].UserId != userId { continue } cm.robotChats[i].Msg = msg cm.robotChats[i].Seconds = seconds cm.robotChats[i].BeginTime = beginTime cm.robotChats[i].EndTime = endTime go cm.setRobotChatConfig() return true } return false } // 删除机器人 func (cm *chatmgr) delRobotChat(userId int) bool { for i := 0; i < len(cm.robotChats); i++ { if cm.robotChats[i].UserId != userId { continue } cm.robotChats = append(cm.robotChats[:i], cm.robotChats[i+1:]...) go cm.setRobotChatConfig() return true } return false } // 写入redis func (cm *chatmgr) setRobotChatConfig() { cm.lock.RLock() d, _ := json.Marshal(cm.robotChats) cm.lock.RUnlock() platformconfig.SetConfig(config_key, string(d)) } // 按条件发送机器人聊天信息 func (cm *chatmgr) sendRobotChatMsg() { now := common.GetNowTime() cm.lock.RLock() defer cm.lock.RUnlock() for i := 0; i < len(cm.robotChats); i++ { v := &cm.robotChats[i] begin := common.ParseTime(v.BeginTime) // log.Debug("chatmgr.sendRobotChatMsg v=%+v now=%v begin=%v", v, now, begin) if now.Before(begin) { continue } end := common.ParseTime(v.EndTime) // log.Debug("chatmgr.sendRobotChatMsg v=%+v now=%v end=%v", v, now, end) if now.After(end) { continue } send := common.ParseTime(v.SendTime) // log.Debug("chatmgr.sendRobotChatMsg v=%+v now=%v send=%v", v, now, send) if now.Sub(send).Seconds() < float64(v.Seconds) { continue } // log.Debug("chatmgr.sendRobotChatMsg now=%v v=%+v 已发送", now, v) v.SendTime = now.Format(common.Layout) u := user.GetUserInfo(v.UserId) if u == nil { log.Release("chatmgr.sendRefresh userId=%d ==> %+v", v.UserId, u) continue } go cm.sendChatMsg(Channel_Speaker, u.UserId, u.FaceId, u.Sex, u.Vip, -1, u.NickName, u.FaceUrl, v.Msg, "127.0.0.1", true, MessageType_Normal, u.VipExpire) } } func (cm *chatmgr) sendRefresh() { ticker := time.NewTicker(1 * time.Second) go func(t *time.Ticker) { for { select { case <-t.C: cm.sendRobotChatMsg() } } }(ticker) } func (cm *chatmgr) getChatMsg(userId, channelID int) string { cm.lock.RLock() defer cm.lock.RUnlock() for k, l := range cm.chatMsgList { if k == channelID { d, _ := json.Marshal(l) // go notification.AddNotification(userId, notification.Notification_Chat, string(d)) return string(d) } } return "" } func (cm *chatmgr) dump(param string) { log.Release("chatmgr.dump ----------") defer log.Release("chatmgr.dump end ++++++++++++++++") log.Release(" %s", cm.getChatMsg(0, 0)) } func (cm *chatmgr) sendChatMsg(channelID, userId, faceId, sex, vipLevel, recvID int, nickName, faceUrl, chatMsg, ipAddress string, isRobot bool, messageType, vipExpire int) bool { if channelID >= channel_Max || channelID < 0 { return false } // 大喇叭,需要扣道具 if channelID == Channel_Speaker && !isRobot { ok, errMsg := inventory.Consume(userId, item.Item_Speaker, 0, 1, 0) if !ok { log.Release("chatMgr.sendChatMsg sending speaker comsume item failed %s", errMsg) return false } } else { //非vip用户要扣金币 cost := serviceconfig.SpecialCfg.ChatCost if isRobot { cost = 0 } if cost > 0 && userId > 0 && vipLevel < 1 { _, m := cash.GetMoney(userId) if m < cost { return false } cash.ReduceMoney(userId, serviceconfig.SpecialCfg.ChatCost, common.LOGTYPE_CHAT, "聊天", "聊天", ipAddress) } } if channelID == Channel_World && !isRobot { task.DoTaskAction(userId, task.TaskAction_worldchat, 1, task.TaskScope{}) } var msg Chat_MSg msg.ChannelID = channelID msg.SendUserID = userId msg.Vip = vipLevel msg.NickName = nickName msg.FaceUrl = faceUrl msg.FaceId = faceId msg.Decorations = user.GetUserDecoration(userId) msg.RecvUserID = recvID msg.ChatMsg = chatMsg msg.Sex = sex msg.MsgType = messageType msg.SendTime = time.Now().Format("2006-01-02 15:04:05") msg.VipExpire = vipExpire msg.ChatMsg = keyword.ParseKeyword(msg.ChatMsg) cm.lock.Lock() l := cm.chatMsgList[channelID] l = append(l, msg) //只保留50条 if len(l) > 50 { l = append(l[:0], l[1:]...) } cm.chatMsgList[channelID] = l cm.lock.Unlock() //广播聊天消息 d, _ := json.Marshal(msg) //世界频道,广播给所有人 //if channelID == Channel_World { log.Debug("add notification of world chat") go notification.AddNotification(-1, notification.Notification_Chat, string(d)) //} return true }