Compare commits

..

No commits in common. "5706c238a443227c604f23ec7130cede396436b9" and "cef7e50112ea95b11cc1250f00392702e06d58e7" have entirely different histories.

20 changed files with 2321 additions and 2922 deletions

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,8 @@
syntax = "proto3";
package accountFiee;
import "github.com/mwitkow/go-proto-validators/validator.proto";
import "github.com/mwitkow/go-proto-validators@v0.3.2/validator.proto";
option go_package = "./;accountFiee";
//protoc -I . -I C:\Users\lenovo\go\src --go_out=. --go-triple_out=. ./accountFiee.proto
@ -988,8 +989,8 @@ message ChatAutoReplyRulerData{
int64 deletedAt = 4; //
string title = 5; //
string ruler = 6; //
int32 status = 7; //: 1= 2=
string response =8; //
int32 rulerStatus = 7; //: 1= 2=
}
message CreateChatAutoReplyRulerResp{

View File

@ -1,8 +1,8 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions:
// - protoc-gen-go-triple v1.0.8
// - protoc v4.22.0--rc2
// source: accountFiee.proto
// - protoc v4.24.0--rc1
// source: api/accountFiee/accountFiee.proto
package accountFiee
@ -35,7 +35,7 @@ type AccountFieeClient interface {
OnlineLog(ctx context.Context, in *LoginInfosByUserIdRequest, opts ...grpc_go.CallOption) (*LoginLogsResponse, common.ErrorWithAttachment)
OnlineLogById(ctx context.Context, in *OnlineLogByIdRequest, opts ...grpc_go.CallOption) (*LoginLog, common.ErrorWithAttachment)
CheckPwd(ctx context.Context, in *CheckPwdRequest, opts ...grpc_go.CallOption) (*UpdateResponse, common.ErrorWithAttachment)
// rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {}
// rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {}
SendMsg(ctx context.Context, in *SendMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment)
SendCustomMsg(ctx context.Context, in *SendCustomMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment)
SendExCustomMsg(ctx context.Context, in *SendCustomMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment)
@ -70,7 +70,7 @@ type AccountFieeClient interface {
VerifySliderStatus(ctx context.Context, in *VerifySliderStatusRequest, opts ...grpc_go.CallOption) (*VerifySliderStatusResponse, common.ErrorWithAttachment)
// submit info
SaveSubmitInfo(ctx context.Context, in *SubmitInfoRequest, opts ...grpc_go.CallOption) (*CommonResponse, common.ErrorWithAttachment)
// -----------------------------客服聊天系统--------------------------------
//-----------------------------客服聊天系统--------------------------------
CreateChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CreateChatUserResp, common.ErrorWithAttachment)
UpdateChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CommonMsg, common.ErrorWithAttachment)
SaveChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CommonMsg, common.ErrorWithAttachment)
@ -591,7 +591,7 @@ type AccountFieeServer interface {
OnlineLog(context.Context, *LoginInfosByUserIdRequest) (*LoginLogsResponse, error)
OnlineLogById(context.Context, *OnlineLogByIdRequest) (*LoginLog, error)
CheckPwd(context.Context, *CheckPwdRequest) (*UpdateResponse, error)
// rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {}
// rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {}
SendMsg(context.Context, *SendMsgRequest) (*SendMsgStatusResponse, error)
SendCustomMsg(context.Context, *SendCustomMsgRequest) (*SendMsgStatusResponse, error)
SendExCustomMsg(context.Context, *SendCustomMsgRequest) (*SendMsgStatusResponse, error)
@ -626,7 +626,7 @@ type AccountFieeServer interface {
VerifySliderStatus(context.Context, *VerifySliderStatusRequest) (*VerifySliderStatusResponse, error)
// submit info
SaveSubmitInfo(context.Context, *SubmitInfoRequest) (*CommonResponse, error)
// -----------------------------客服聊天系统--------------------------------
//-----------------------------客服聊天系统--------------------------------
CreateChatUser(context.Context, *ChatUserData) (*CreateChatUserResp, error)
UpdateChatUser(context.Context, *ChatUserData) (*CommonMsg, error)
SaveChatUser(context.Context, *ChatUserData) (*CommonMsg, error)
@ -3074,5 +3074,5 @@ var AccountFiee_ServiceDesc = grpc_go.ServiceDesc{
},
},
Streams: []grpc_go.StreamDesc{},
Metadata: "accountFiee.proto",
Metadata: "api/accountFiee/accountFiee.proto",
}

View File

@ -14,7 +14,6 @@ import (
"fonchain-fiee/pkg/e"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils/secret"
"github.com/gin-gonic/gin"
)
@ -25,9 +24,6 @@ func ParseToChatUser(c *gin.Context) (chatUserInfo *accountFiee.ChatUserData, co
if exist {
domain = domainAny.(string)
}
if domain == "" {
domain = config.AppConfig.System.Domain
}
var err error
token := c.GetHeader(e.Authorization)
if token == "" {

View File

@ -13,6 +13,17 @@ import (
"strings"
)
type WsType int
const (
RegisterType WsType = iota
ErrorType
TestType
ChatType
NewChatMsgType //新消息通知
AuthorizationType //token校验通知
)
// 消息结构
type WSMessage struct {
Type string `json:"type"`

View File

@ -40,8 +40,6 @@ func NewChatRoom() *ChatRoom {
register: make(clientChan),
UnRegister: make(clientChan),
broadcast: make(broadcastChan),
eventBus: []*EventListener{},
EventRwLocker: &sync.RWMutex{},
}
go room.Run()
return &room
@ -51,14 +49,12 @@ type broadcastMessage struct {
UserIds []int64
message []byte
}
type ChatRoomEvent struct {
ListenEvent []ListenEvent
message []byte
SenderId int64
ReceiverIds []int64
}
type (
// []byte类型管道 用于客户端接收消息数据
messageChan chan []byte
//
wsConnChan chan *websocket.Conn
// Client类型数据管道
clientChan chan *Client
@ -68,7 +64,6 @@ type (
type ChatRoom struct {
clientsRwLocker *sync.RWMutex
EventRwLocker *sync.RWMutex
//clients 客户端信息存储
//// 支持多客户端连接 map[userId]map[clientId]*Client
clients map[int64]map[string]*Client
@ -82,11 +77,7 @@ type ChatRoom struct {
//unRegister 注销管道 接收需要注销的客户端
UnRegister clientChan
// 消息广播管道
broadcast broadcastChan
// 事件广播管道,向其它程序推送消息
eventBus []*EventListener
}
func (o *ChatRoom) Run() {
@ -95,9 +86,8 @@ func (o *ChatRoom) Run() {
select {
// 注册事件
case newClient := <-o.register:
o.pushEvent(EventUserJoin, EventProgressBefore, newClient)
defer o.pushEvent(EventUserJoin, EventProgressAfter, newClient)
////删除临时map中的客户户端
//delete(o.tempClient, client.clientId)
o.clientsRwLocker.Lock()
//添加到客户端集合中
if o.clients[newClient.UserId] == nil {
@ -108,6 +98,15 @@ func (o *ChatRoom) Run() {
if o.Session == nil {
o.Session = make(map[string][]*Client)
}
//if _, ok := o.Session[newClient.SessionId]; ok {
// for i, client := range o.Session[newClient.SessionId] {
// if client.ClientId == newClient.ClientId {
// //将之前的客户端注销
// o.UnRegister <- client
// }
// o.Session[newClient.SessionId][i] = newClient
// }
//}
if newClient.Waiter {
//客服人员没有默认会话窗口,而是自动加入所有用户的会话
for sessionId, _ := range o.Session {
@ -119,7 +118,7 @@ func (o *ChatRoom) Run() {
}
}
} else {
//普通用户添加会话的逻辑
//画家添加会话的逻辑
_, ok := o.Session[newClient.SessionId]
if !ok {
o.Session[newClient.SessionId] = make([]*Client, 0)
@ -142,8 +141,6 @@ func (o *ChatRoom) Run() {
o.clientsRwLocker.Unlock()
//注销事件
case client := <-o.UnRegister:
o.pushEvent(EventUserLeave, EventProgressBefore, client)
defer o.pushEvent(EventUserLeave, EventProgressAfter, client)
//panic 恢复
defer func() {
if r := recover(); r != "" {
@ -192,10 +189,6 @@ func (o *ChatRoom) Register(c *Client) (sessionId string) {
func (o *ChatRoom) SendSessionMessage(sendUserId int64, sessionId string, msgType WsType, message any) (userIdInSession []int64, err error) {
o.clientsRwLocker.Lock()
defer o.clientsRwLocker.Unlock()
o.pushEvent(EventChatMessage, EventProgressBefore, sendUserId, sessionId, msgType, message)
defer o.pushEvent(EventChatMessage, EventProgressAfter, sendUserId, sessionId, msgType, message)
var msg = WsSessionInfo{
Type: msgType,
Content: message,
@ -241,7 +234,7 @@ func (o *ChatRoom) GetUserIdInSession(sessionId string, withoutUserId ...int64)
for _, userId := range withoutUserId {
if client.UserId == userId {
jump = true
break
continue
}
}
}
@ -275,14 +268,14 @@ func (o *ChatRoom) GetUserIdInSession(sessionId string, withoutUserId ...int64)
return
}
// func (o *ChatRoom) RegisterClient(c *Client) {
// func (o ChatRoom) RegisterClient(c *Client) {
// o.register <- c
// }
//
// func (o *ChatRoom) DeleteClient(c *Client) {
// func (o ChatRoom) DeleteClient(c *Client) {
// o.unRegister <- c
// }
func (o *ChatRoom) Broadcast(message []byte, userIds ...int64) {
func (o ChatRoom) Broadcast(message []byte, userIds ...int64) {
// 如果userIds为空则群发,否则找到这个用户的ws对象
if userIds == nil {
for _, userClients := range o.clients {
@ -320,49 +313,3 @@ func (o *ChatRoom) Broadcast(message []byte, userIds ...int64) {
}
}
}
// RegisterEventListener 注册聊天室事件监听者
func (o *ChatRoom) RegisterEventListener(listenerChan *EventListener) {
o.EventRwLocker.Lock()
defer o.EventRwLocker.Unlock()
o.eventBus = append(o.eventBus, listenerChan)
}
// 注销监听者
func (o *ChatRoom) UnRegisterEventListener(listenerChan *EventListener) {
o.EventRwLocker.Lock()
defer o.EventRwLocker.Unlock()
var registerListenerList []*EventListener
for i, listener := range o.eventBus {
if listener.Name == listenerChan.Name {
continue
}
registerListenerList = append(registerListenerList, o.eventBus[i])
}
o.eventBus = registerListenerList
}
// pushEvent 推送聊天室事件
func (o *ChatRoom) pushEvent(eventType EventType, progress EventProgress, data ...any) {
o.EventRwLocker.Lock()
defer o.EventRwLocker.Unlock()
for _, listener := range o.eventBus {
hit := false
for _, need := range listener.ListenEvents {
if need.EventType == eventType && need.ProgressType == progress {
hit = true
break
}
}
if hit == false {
continue
}
listener.Chan <- ListenEventData{
ListenEvent: ListenEvent{
EventType: eventType,
ProgressType: progress,
},
Data: data,
}
}
}

View File

@ -1,52 +0,0 @@
// Package ws -----------------------------
// @file : consts.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/14 09:44
// -------------------------------------------
package ws
// websocket 消息类型
type WsType int
const (
RegisterType WsType = iota //用户注册消息
ErrorType //错误消息
TestType //测试消息
ChatType //聊天消息
NewChatMsgType //新消息通知
AuthorizationType //token校验通知
)
// 事件总线中的事件类型
type EventType string
const (
EventConnection EventType = "connection" //websocket连接事件
EventUserJoin EventType = "user_join" //用户/客服加入聊天事件
EventUserLeave EventType = "user_leave" //用户离开事件
EventChatMessage EventType = "chat_message" //聊天消息传递事件
)
// before
type EventProgress string
const (
EventProgressBefore EventProgress = "before"
EventProgressAfter EventProgress = "after"
)
type ListenEvent struct {
EventType EventType `json:"type"`
ProgressType EventProgress `json:"progress"`
}
type ListenEventData struct {
ListenEvent
Data interface{}
}
type ListenEventChan chan ListenEventData
type EventListener struct {
Name string
ListenEvents []ListenEvent //需要监听的事件列表
Chan ListenEventChan
}

View File

@ -4,7 +4,7 @@
// @contact : wavingbear@163.com
// @time : 2024/9/11 下午5:18
// -------------------------------------------
package chatCache
package asChat
import (
"context"
@ -12,7 +12,6 @@ import (
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/cache"
"fonchain-fiee/pkg/service/asChat/dto"
"github.com/go-redis/redis"
"github.com/goccy/go-json"
"go.uber.org/zap"
@ -29,7 +28,7 @@ const CacheNewMsgStatKey = "fiee:newMsgStat"
var chatCacheLocker sync.RWMutex
type ChatCache struct {
NewMessageStatExpireAfter time.Duration //消息统计的数据过期时间
newMessageStatExpireAfter time.Duration //消息统计的数据过期时间
}
// ------------------------------存储用户的会话ID--------------------------------
@ -42,7 +41,7 @@ func (cr ChatCache) SaveUserSession(userId int64, sessionId string) {
////var c = context.Background()
err := cache.RedisClient.Set(cr.GetUserSessionCacheKey(userId), sessionId, 0).Err()
if err != nil {
log.Print("保存用户会话失败", zap.Error(err))
log.Fatal("保存用户会话失败", zap.Error(err))
}
}
func (cr ChatCache) GetUserSession(userId int64) (sessionId string) {
@ -56,7 +55,7 @@ func (cr ChatCache) GetUserSession(userId int64) (sessionId string) {
if err.Error() == "redis: nil" {
err = nil
} else {
log.Print("获取用户会话失败", zap.Error(err))
log.Fatal("获取用户会话失败", zap.Error(err))
}
}
fmt.Println("GetUserSession-3, sessionId:", sessionId)
@ -96,7 +95,7 @@ func (cr ChatCache) GetChatRecord(sessionId string) (data []*accountFiee.ChatRec
if err.Error() == "redis: nil" {
err = nil
}
//log.Print("获取聊天记录失败", zap.Error(err))
//log.Fatal("获取聊天记录失败", zap.Error(err))
return
}
fmt.Printf("cache data: %+v", string(messages))
@ -134,10 +133,10 @@ func (cr ChatCache) IncreaseNewMessageTotal(ownerId int64, sessionId string) (er
copy(data[1:], data[0:foundIndex])
data[0] = elementToMove
} else if foundIndex == -1 {
data = append([]dto.UserMsgStatic{{SessionId: sessionId, Total: 1}}, data...)
data = append([]UserMsgStatic{{SessionId: sessionId, Total: 1}}, data...)
}
} else {
data = []dto.UserMsgStatic{{SessionId: sessionId, Total: 1}}
data = []UserMsgStatic{{SessionId: sessionId, Total: 1}}
}
return cr.coverOwnerNewMessageStat(ctx, ownerId, data)
}
@ -161,7 +160,7 @@ func (cr ChatCache) ResetNewMessageTotal(ownerId int64, sessionId string, total
}
}
if !found {
data = append(data, dto.UserMsgStatic{
data = append(data, UserMsgStatic{
SessionId: sessionId,
Total: tl,
})
@ -175,7 +174,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
var err error
keys, err = cache.RedisClient.Keys(CacheChatRecordKey + "*").Result()
if err != nil {
log.Print("获取聊天记录所有缓存KEY失败", zap.Error(err))
log.Fatal("获取聊天记录所有缓存KEY失败", zap.Error(err))
return
}
var countMap = make(map[string]int)
@ -187,7 +186,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
if err.Error() == "redis: nil" {
err = nil
}
log.Print("获取聊天记录失败", zap.Error(err))
log.Fatal("获取聊天记录失败", zap.Error(err))
data = make([]*accountFiee.ChatRecordData, 0)
continue
}
@ -205,7 +204,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
for sessionId, count := range countMap {
err = cr.ResetNewMessageTotal(ownerId, sessionId, int64(count))
if err != nil {
log.Print("重置新消息数量统计",
log.Fatal("重置新消息数量统计",
zap.String("function", "RecountNewMessageTotal"),
zap.Int64("ownerId", ownerId),
zap.String("sessionId", sessionId),
@ -218,13 +217,13 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
}
// erp获取最新的消息统计
func (cr ChatCache) GetNewMessageStat(ctx context.Context, ownerId int64) (result []dto.UserMsgStatic) {
func (cr ChatCache) GetNewMessageStat(ctx context.Context, ownerId int64) (result []UserMsgStatic) {
//chatCacheLocker.RLock()
//defer chatCacheLocker.RUnlock()
result = make([]dto.UserMsgStatic, 0)
result = make([]UserMsgStatic, 0)
vals, err := cache.RedisClient.Get(cr.GetNewMsgStatCacheKey(ownerId)).Bytes()
if err != nil && errors.Is(err, redis.Nil) {
log.Print("从缓存获取新消息统计失败", zap.Error(err), zap.Int64("ownerId", ownerId))
log.Fatal("从缓存获取新消息统计失败", zap.Error(err), zap.Int64("ownerId", ownerId))
return
}
if vals != nil {
@ -234,9 +233,9 @@ func (cr ChatCache) GetNewMessageStat(ctx context.Context, ownerId int64) (resul
}
// 覆盖指定erp用户的新消息统计
func (cr ChatCache) coverOwnerNewMessageStat(ctx context.Context, ownerId int64, data []dto.UserMsgStatic) (err error) {
func (cr ChatCache) coverOwnerNewMessageStat(ctx context.Context, ownerId int64, data []UserMsgStatic) (err error) {
value, _ := json.Marshal(data)
//err = cache.RedisClient.Set(ctx, cr.GetNewMsgStatCacheKey(ownerId), value, cr.NewMessageStatExpireAfter).Err()
//err = cache.RedisClient.Set(ctx, cr.GetNewMsgStatCacheKey(ownerId), value, cr.newMessageStatExpireAfter).Err()
err = cache.RedisClient.Set(cr.GetNewMsgStatCacheKey(ownerId), value, 0).Err()
return
}

View File

@ -1,13 +1,11 @@
package asChat
import (
"encoding/json"
"fmt"
"fonchain-fiee/api/account"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/dto"
"fonchain-fiee/pkg/utils"
"fonchain-fiee/pkg/utils/secret"
"fonchain-fiee/pkg/utils/stime"
@ -23,18 +21,12 @@ type ChatAutoReplyRulerHandler struct {
// 创建自动回复规则
func (a *ChatAutoReplyRulerHandler) CreateChatAutoReplyRuler(c *gin.Context) {
var req dto.ChatAutoReplyData
var req accountFiee.ChatAutoReplyRulerData
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return
}
rulerBytes, _ := json.Marshal(req.Rules)
protoReq := accountFiee.ChatAutoReplyRulerData{
Title: req.Title,
Ruler: string(rulerBytes),
Response: req.Response,
}
_, err := service.AccountFieeProvider.CreateChatAutoReplyRuler(c, &protoReq)
_, err := service.AccountFieeProvider.CreateChatAutoReplyRuler(c, &req)
if err != nil {
service.Error(c, err)
return
@ -59,13 +51,12 @@ func (a *ChatAutoReplyRulerHandler) DeleteChatAutoReplyRuler(c *gin.Context) {
// 更新自动回复规则
func (a *ChatAutoReplyRulerHandler) UpdateChatAutoReplyRuler(c *gin.Context) {
var req dto.ChatAutoReplyData
var req accountFiee.ChatAutoReplyRulerData
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return
}
protoReq := req.ToProtoData()
_, err := service.AccountFieeProvider.UpdateChatAutoReplyRuler(c, protoReq)
_, err := service.AccountFieeProvider.UpdateChatAutoReplyRuler(c, &req)
if err != nil {
service.Error(c, err)
return
@ -85,14 +76,12 @@ func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerDetail(c *gin.Context)
service.Error(c, err)
return
}
tmp := dto.ChatAutoReplyData{}
tmp.Parse(resp)
service.Success(c, tmp)
service.Success(c, resp)
}
// 批量查询自动回复规则
func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerList(c *gin.Context) {
var req dto.GetChatAutoReplyRulerListRequest
var req GetChatAutoReplyRulerListRequest
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return
@ -104,21 +93,10 @@ func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerList(c *gin.Context) {
service.Error(c, err)
return
}
var data []dto.ChatAutoReplyData
for _, v := range resp.List {
tmp := dto.ChatAutoReplyData{}
tmp.Parse(v)
data = append(data, tmp)
}
service.Success(c, map[string]interface{}{
"data": data,
"page": resp.Page,
"pagesize": resp.PageSize,
"total": resp.Total,
})
service.Success(c, resp.List)
}
func (a *ChatAutoReplyRulerHandler) ErpLoginDemo(c *gin.Context) {
var req dto.ErpLoginDemoReq
var req ErpLoginDemoReq
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return
@ -163,7 +141,7 @@ func (a *ChatAutoReplyRulerHandler) ErpLoginDemo(c *gin.Context) {
service.Success(c, loginRes)
}
func (a *ChatAutoReplyRulerHandler) FieeLoginDemo(c *gin.Context) {
var req dto.ErpLoginDemoReq
var req ErpLoginDemoReq
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return

View File

@ -4,7 +4,7 @@
// @contact : wavingbear@163.com
// @time : 2022/10/21 18:17:17
// -------------------------------------------
package consts
package asChat
import (
"encoding/json"

View File

@ -1,7 +0,0 @@
// Package consts -----------------------------
// @file : consts.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 17:40
// -------------------------------------------
package consts

View File

@ -4,12 +4,11 @@
// @contact : wavingbear@163.com
// @time : 2024/9/10 下午6:28
// -------------------------------------------
package dto
package asChat
import (
"encoding/json"
"fonchain-fiee/api/accountFiee"
"log"
"time"
)
@ -141,43 +140,3 @@ type ErpLoginDemoReq struct {
Nickname string `json:"nickname"`
RealName string `json:"realName"`
}
type ChatAutoReplyData struct {
ID int64 `json:"id"`
Title string `json:"title"`
Rules map[string]*AutoReplyRule `json:"rules"`
Response string `json:"response"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
Status int32 `json:"status"`
}
func (r *ChatAutoReplyData) ToProtoData() (data *accountFiee.ChatAutoReplyRulerData) {
jsonBytes, _ := json.Marshal(r.Rules)
data = &accountFiee.ChatAutoReplyRulerData{
ID: r.ID,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
Title: r.Title,
Ruler: string(jsonBytes),
Status: r.Status,
Response: r.Response,
}
return
}
func (r *ChatAutoReplyData) Parse(data *accountFiee.ChatAutoReplyRulerData) {
err := json.Unmarshal([]byte(data.Ruler), &r.Rules)
log.Printf("ChatAutoReplyData parse err:%v\n", err)
r.ID = data.ID
r.CreatedAt = data.CreatedAt
r.UpdatedAt = data.UpdatedAt
r.Title = data.Title
r.Status = data.Status
r.Response = data.Response
}
type AutoReplyRule struct {
Enable bool `json:"enable"`
Content string `json:"content,omitempty"`
SecondDuration time.Duration `json:"secondDuration,omitempty"`
}

View File

@ -18,11 +18,6 @@ import (
"fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/e"
"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/logic"
"fonchain-fiee/pkg/service/asChat/robot"
"fonchain-fiee/pkg/service/upload"
"fonchain-fiee/pkg/utils"
"fonchain-fiee/pkg/utils/stime"
@ -42,20 +37,18 @@ import (
)
var ChatHandlerIns = ChatHandler{
cache: chatCache.ChatCache{NewMessageStatExpireAfter: 10 * time.Minute},
robot: robot.NewRobot(),
cache: ChatCache{newMessageStatExpireAfter: 10 * time.Minute},
}
type ChatHandler struct {
cache chatCache.ChatCache
robot *robot.Robot
cache ChatCache
}
func (cr ChatHandler) Connection(c *gin.Context) {
conn, err := ws.UpGrader.Upgrade(c.Writer, c.Request, nil)
conn.SetReadDeadline(time.Now().Add(time.Second * 10))
if err != nil {
log.Print("无法升级为websocket连接", zap.Error(err))
log.Fatal("无法升级为websocket连接", zap.Error(err))
c.String(500, "无法转为websocket连接")
return
}
@ -87,16 +80,16 @@ func (cr ChatHandler) Connection(c *gin.Context) {
fmt.Println("44444444444444,ws.NewClient")
//注册ws客户端并发送clientId给ws客户端
var cli = ws.NewClient(userInfo.ID, "", conn, consts.ChatRoom)
var cli = ws.NewClient(userInfo.ID, "", conn, ChatRoom)
cli.Waiter = userInfo.Role == 2
fmt.Println("55555555555555,GetUserSession")
//查询是否有历史的sessionId
cli.SessionId = cr.cache.GetUserSession(userInfo.ID)
consts.ChatRoom.Register(cli)
ChatRoom.Register(cli)
cr.cache.SaveUserSession(userInfo.ID, cli.SessionId)
fmt.Println("66666666666666666666666666")
go cli.WriteWait()
cli.Send <- consts.WsMessageRegisterCallback(cli.ClientId, cli.SessionId)
cli.Send <- WsMessageRegisterCallback(cli.ClientId, cli.SessionId)
fmt.Println("777777777777777777777777")
// 处理websocket连接的逻辑
ctx, _ := context.WithCancel(context.Background())
@ -109,7 +102,7 @@ func (cr ChatHandler) Connection(c *gin.Context) {
}
func (cr ChatHandler) NewMessage(c *gin.Context) {
var request dto.NewMessageRequest
var request NewMessageRequest
if err := c.ShouldBindJSON(&request); err != nil {
service.Error(c, err)
return
@ -129,97 +122,90 @@ func (cr ChatHandler) NewMessage(c *gin.Context) {
service.ErrWithCode(c, code)
return
}
err := logic.NewMessage(c, &cr.cache, chatUser, request)
fmt.Println("NewMessage 22222222222222222222222222222222222")
//存储入库
if chatUser.NickName != "" {
chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID)
}
fmt.Println("NewMessage 3333333333333333333333333333333333")
var data = accountFiee.ChatRecordData{
SessionId: request.SessionId,
UserId: chatUser.ID,
Name: chatUser.NickName,
Avatar: "",
MsgType: request.MsgType,
Content: request.Message.Text,
LocalStamp: request.LocalStamp,
Medias: nil,
}
if len(request.Message.Media) > 0 {
for _, media := range request.Message.Media {
data.Medias = append(data.Medias, &accountFiee.ChatMediaData{
ID: media.MediaId,
})
}
}
fmt.Println("NewMessage 4444444444444444444444444444444444")
resp, err := service.AccountFieeProvider.CreateChatRecord(c, &data)
if err != nil {
service.Error(c, err)
service.Error(c, errors.New("创建失败"))
return
}
//fmt.Println("NewMessage 22222222222222222222222222222222222")
////存储入库
//if chatUser.NickName != "" {
// chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID)
//}
//fmt.Println("NewMessage 3333333333333333333333333333333333")
//var data = accountFiee.ChatRecordData{
// SessionId: request.SessionId,
// UserId: chatUser.ID,
// Name: chatUser.NickName,
// Avatar: "",
// MsgType: request.MsgType,
// Content: request.Message.Text,
// LocalStamp: request.LocalStamp,
// Medias: nil,
//}
//if len(request.Message.Media) > 0 {
// for _, media := range request.Message.Media {
// data.Medias = append(data.Medias, &accountFiee.ChatMediaData{
// ID: media.MediaId,
// })
fmt.Printf("CreateChatRecord resp:%+v\n", resp)
//录入缓存
err = cr.cache.AddChatRecord(request.SessionId, resp.Data)
if err != nil {
service.Error(c, errors.New("创建失败"))
return
}
fmt.Println("NewMessage 5 消息数量+1")
//新消息数量统计+1
noticeUserId := ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
fmt.Println("NewMessage 5.1 消息数量配置结束")
fmt.Printf("noticeUserId %+v\n", noticeUserId)
for _, userId := range noticeUserId {
fmt.Println("userId")
cr.cache.IncreaseNewMessageTotal(userId, request.SessionId)
}
fmt.Println("NewMessage 6")
//发送websocket消息提醒通知
var notice = MessageListType{}
notice.BuildMessage(resp.Data)
fmt.Printf("ws消息提醒:%+v\n", notice)
_, err = ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
if err != nil {
log.Fatal("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
}
fmt.Println("NewMessage 7 -end")
//发送app推送(无横幅推送)
//go func() {
// omitMessage := ""
// switch request.MsgType {
// case accountFiee.MsgType_TextMsgType:
// runMsg := []rune(request.Text)
// if len(runMsg) > 15 {
// omitMessage = string(runMsg[:15]) + "..."
// } else {
// omitMessage = request.Text
// }
// case accountFiee.MsgType_ImageMsgType:
// omitMessage = "[图片]"
// case accountFiee.MsgType_AudioMsgType:
// omitMessage = "[音频]"
// case accountFiee.MsgType_VideoMsgType:
// omitMessage = "[视频]"
// default:
// omitMessage = "新消息请查收"
// }
//}
//fmt.Println("NewMessage 4444444444444444444444444444444444")
//resp, err := service.AccountFieeProvider.CreateChatRecord(c, &data)
//if err != nil {
// service.Error(c, errors.New("创建失败"))
// return
//}
//fmt.Printf("CreateChatRecord resp:%+v\n", resp)
////录入缓存
//err = cr.cache.AddChatRecord(request.SessionId, resp.Data)
//if err != nil {
// service.Error(c, errors.New("创建失败"))
// return
//}
//fmt.Println("NewMessage 5 消息数量+1")
////新消息数量统计+1
//noticeUserId := consts.ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
//fmt.Println("NewMessage 5.1 消息数量配置结束")
//fmt.Printf("noticeUserId %+v\n", noticeUserId)
//for _, userId := range noticeUserId {
// fmt.Println("userId")
// cr.cache.IncreaseNewMessageTotal(userId, request.SessionId)
//}
//fmt.Println("NewMessage 6")
////发送websocket消息提醒通知
//var notice = dto.MessageListType{}
//notice.BuildMessage(resp.Data)
//fmt.Printf("ws消息提醒:%+v\n", notice)
//_, err = consts.ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
//if err != nil {
// log.Print("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
//}
//cr.robot.Listen(&data)
//fmt.Println("NewMessage 7 -end")
////发送app推送(无横幅推送)
////go func() {
//// omitMessage := ""
//// switch request.MsgType {
//// case accountFiee.MsgType_TextMsgType:
//// runMsg := []rune(request.Text)
//// if len(runMsg) > 15 {
//// omitMessage = string(runMsg[:15]) + "..."
//// } else {
//// omitMessage = request.Text
//// }
//// case accountFiee.MsgType_ImageMsgType:
//// omitMessage = "[图片]"
//// case accountFiee.MsgType_AudioMsgType:
//// omitMessage = "[音频]"
//// case accountFiee.MsgType_VideoMsgType:
//// omitMessage = "[视频]"
//// default:
//// omitMessage = "新消息请查收"
//// }
//// for _, userId := range noticeUserId {
//// _ = asPusher.NewArtistinfoUniPush().NewChatMessageNotice(userId, omitMessage)
//// }
////}()
// for _, userId := range noticeUserId {
// _ = asPusher.NewArtistinfoUniPush().NewChatMessageNotice(userId, omitMessage)
// }
//}()
service.Success(c)
}
func (cr ChatHandler) MessageList(c *gin.Context) {
var request dto.MessageListRequest
var request MessageListRequest
if err := c.ShouldBindJSON(&request); err != nil {
service.Error(c, err)
return
@ -237,7 +223,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
service.Error(c, errors.New("pageSize校验错误"))
return
}
var resp = make([]*dto.MessageListType, 0)
var resp = make([]*MessageListType, 0)
if request.CurrentId == 0 && request.Direction == 1 {
service.Success(c, resp)
return
@ -273,12 +259,12 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
}
err := cr.cache.CoverChatRecord(request.SessionId, messages)
if err != nil {
log.Print("设置消息已读失败", zap.Error(err))
log.Fatal("设置消息已读失败", zap.Error(err))
}
for _, v := range messages {
_, err = service.AccountFieeProvider.SaveChatRecord(context.Background(), v)
if err != nil {
log.Print("设置消息已读失败", zap.Error(err))
log.Fatal("设置消息已读失败", zap.Error(err))
}
}
}
@ -298,7 +284,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
messages = recordResp.List
err = cr.cache.CoverChatRecord(request.SessionId, messages)
if err != nil {
log.Print("覆盖聊天记录失败", zap.Error(err))
log.Fatal("覆盖聊天记录失败", zap.Error(err))
}
}
if request.Recent {
@ -314,7 +300,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
}
}
returnDataIdList = append(returnDataIdList, message.ID)
var msg = &dto.MessageListType{}
var msg = &MessageListType{}
msg.BuildMessage(message)
resp = append(resp, msg)
}
@ -346,7 +332,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
}
total++
returnDataIdList = append(returnDataIdList, message.ID)
var msg = &dto.MessageListType{}
var msg = &MessageListType{}
msg.BuildMessage(message)
resp = append(resp, msg)
}
@ -358,7 +344,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
//优化空列表
for i, v := range resp {
if v.Message.Media == nil {
resp[i].Message.Media = []dto.MessageMedia{}
resp[i].Message.Media = []MessageMedia{}
}
}
service.Success(c, resp)
@ -375,7 +361,7 @@ func (cr ChatHandler) Upload(c *gin.Context) {
//获取文件对象
file, err := c.FormFile("file")
if err != nil {
log.Print("ERROR: upload file failed. ", zap.Error(err))
log.Fatal("ERROR: upload file failed. ", zap.Error(err))
return
}
duration := c.PostForm("duration")
@ -414,7 +400,7 @@ func (cr ChatHandler) Upload(c *gin.Context) {
//检查文件是否存在
checkResp, err := service.AccountFieeProvider.GetChatMediaList(c, &accountFiee.GetChatMediaListRequest{Query: &accountFiee.ChatMediaData{Md5: md5String}, Page: 1, PageSize: 1})
if err != nil {
log.Print("md5查询附件失败", zap.Error(err))
log.Fatal("md5查询附件失败", zap.Error(err))
}
if checkResp.Total > 0 {
service.Success(c, checkResp.List[0])
@ -497,13 +483,13 @@ func (cr ChatHandler) UserMessageStat(c *gin.Context) {
reverse(result)
service.Success(c, result)
}
func reverse(slice []dto.UserMsgStatic) {
func reverse(slice []UserMsgStatic) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
}
func (cr ChatHandler) VoiceToText(c *gin.Context) {
var req dto.VoiceToTextRequest
var req VoiceToTextRequest
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return

View File

@ -1,13 +0,0 @@
// Package autoReply -----------------------------
// @file : intreface.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 16:15
// -------------------------------------------
package asChat
type IReplyRuler interface {
Name() string //规则名称
Check()
RunScript() string //运行脚本
}

View File

@ -1,86 +0,0 @@
// Package service -----------------------------
// @file : chat.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 19:04
// -------------------------------------------
package logic
import (
"context"
"errors"
"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/consts"
"fonchain-fiee/pkg/service/asChat/dto"
"go.uber.org/zap"
"log"
)
func NewMessage(ctx context.Context, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData, request dto.NewMessageRequest) (err error) {
if request.SessionId == "" {
return errors.New("sessionId不能为空")
}
if request.MsgType == 0 {
return errors.New("msgType不能为空")
}
fmt.Println("NewMessage 1111111111111111111111111111111")
fmt.Println("NewMessage 22222222222222222222222222222222222")
//存储入库
if chatUser.NickName != "" {
chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID)
}
fmt.Println("NewMessage 3333333333333333333333333333333333")
var data = accountFiee.ChatRecordData{
SessionId: request.SessionId,
UserId: chatUser.ID,
Name: chatUser.NickName,
Avatar: "",
MsgType: request.MsgType,
Content: request.Message.Text,
LocalStamp: request.LocalStamp,
Medias: nil,
}
if len(request.Message.Media) > 0 {
for _, media := range request.Message.Media {
data.Medias = append(data.Medias, &accountFiee.ChatMediaData{
ID: media.MediaId,
})
}
}
fmt.Println("NewMessage 4444444444444444444444444444444444")
resp, err := service.AccountFieeProvider.CreateChatRecord(ctx, &data)
if err != nil {
return errors.New("消息发送失败")
}
fmt.Printf("CreateChatRecord resp:%+v\n", resp)
//录入缓存
err = cache.AddChatRecord(request.SessionId, resp.Data)
if err != nil {
log.Printf("cache.AddChatRecord 失败:%v", err)
return errors.New("消息发送失败")
}
fmt.Println("NewMessage 5 消息数量+1")
//新消息数量统计+1
noticeUserId := consts.ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
fmt.Println("NewMessage 5.1 消息数量配置结束")
fmt.Printf("noticeUserId %+v\n", noticeUserId)
for _, userId := range noticeUserId {
fmt.Println("userId")
cache.IncreaseNewMessageTotal(userId, request.SessionId)
}
fmt.Println("NewMessage 6")
//发送websocket消息提醒通知
var notice = dto.MessageListType{}
notice.BuildMessage(resp.Data)
fmt.Printf("ws消息提醒:%+v\n", notice)
_, err = consts.ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
if err != nil {
log.Print("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
}
fmt.Println("NewMessage 7 -end")
return nil
}

View File

@ -1,39 +0,0 @@
// Package autoReply -----------------------------
// @file : KeywordsReplyRuler.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 16:21
// -------------------------------------------
package robot
import (
"time"
)
// 使用go开发一个自动回复功能
// 一个自动回复消息有多种触发条件:
// 1. 关键词触发
// 2. 进入聊天系统后直接发送
// 3. 若干秒不回复自动发送
//func (k KeywordsRuleChecker) Do(sessionId string, response string, chatRoom *ws.ChatRoom) (err error) {
// var notice = dto.MessageListType{}
// notice.BuildMessage(response)
// _, err = chatRoom.SendSessionMessage(1, sessionId, ws.NewChatMsgType, notice)
// return nil
//}
type AutoReply struct {
Response string `json:"response"`
Rules map[string]IRule `json:"rules"`
}
type AutoReplyRule struct {
Enable bool `json:"enable"`
Keywords []string `json:"keywords"`
ReplyTimeout int `json:"replyTimeout"` // 回复超时时间
}
type AutoReplyManager struct {
replies []AutoReply
lastMessage time.Time
}

View File

@ -1,84 +0,0 @@
// Package robot -----------------------------
// @file : replyRuler.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 17:39
// -------------------------------------------
package robot
import (
"context"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/consts"
"fonchain-fiee/pkg/service/asChat/dto"
"strings"
"time"
)
type Reply struct {
Response string
Rules []IRule
}
type IRule interface {
Hit(msg *accountFiee.ChatRecordData) (hit bool, runTime time.Time, logic func(robotId int64, response string) error)
}
// KeywordsRuleChecker 关键字回复
type KeywordsRuleChecker struct {
Keywords []string `json:"keywords"`
}
func (k KeywordsRuleChecker) Hit(record *accountFiee.ChatRecordData) (hit bool, runTime time.Time, logic func(robotId int64, response string) error) {
for _, v := range k.Keywords {
if strings.Contains(record.Content, v) {
hit = true
break
}
}
logic = func(robotId int64, response string) error {
var notice = dto.MessageListType{}
notice.BuildMessage(record)
_, err := consts.ChatRoom.SendSessionMessage(robotId, record.SessionId, ws.NewChatMsgType, notice)
return err
}
return
}
// 用户打开聊天会话直接发送
type ReplyWhenUserJoinSession struct {
}
func (k ReplyWhenUserJoinSession) Hit(record *accountFiee.ChatRecordData, robotId int64) (hit bool, runTime time.Time, logic func(robotId int64, response string) error) {
queryRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{
Query: &accountFiee.ChatRecordData{
SessionId: record.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 == robotId {
return
} else {
break
}
}
}
logic = func(robotId int64, response string) error {
var notice = dto.MessageListType{}
notice.BuildMessage(record)
_, err = consts.ChatRoom.SendSessionMessage(robotId, record.SessionId, ws.NewChatMsgType, notice)
return err
}
return
}

View File

@ -1,167 +0,0 @@
// Package robot -----------------------------
// @file : robot.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 17:41
// -------------------------------------------
package robot
import (
"context"
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/consts"
"log"
"sync"
"time"
)
func NewRobot() *Robot {
ctx := context.Background()
robotQuery, err := service.AccountFieeProvider.GetChatUserList(ctx, &accountFiee.GetChatUserListRequest{
Query: &accountFiee.ChatUserData{Role: 3},
Page: 1, PageSize: 1,
})
if err != nil {
panic("聊天机器人初始化失败,err:" + err.Error())
}
var robotInfo *accountFiee.ChatUserData
if robotQuery.Total > 0 {
robotInfo = robotQuery.List[0]
} else {
robotInfo = &accountFiee.ChatUserData{
NickName: "阿泰",
Role: 3,
Origin: "fontree",
}
createChatUserResp, errs := service.AccountFieeProvider.CreateChatUser(ctx, robotInfo)
if errs != nil {
panic("聊天机器人创建失败,err:" + errs.Error())
}
robotInfo = createChatUserResp.Data
}
r := &Robot{
Info: robotInfo,
EventListener: &ws.EventListener{
Name: "robot1",
ListenEvents: []ws.ListenEvent{ //只监听消息推送事件
{ws.EventChatMessage, ws.EventProgressAfter},
},
Chan: make(ws.ListenEventChan),
},
}
consts.ChatRoom.RegisterEventListener(r.EventListener)
go r.Run()
return r
}
type Robot struct {
Info *accountFiee.ChatUserData //机器人信息
Rules []Reply //回复规则
DelayTask []RobotTask //演示任务
ticker *time.Ticker //定时器
stopChan chan struct{} //停止管道
isRunning bool //运行状态
mu sync.Mutex
*ws.EventListener
}
func (r *Robot) Listen(record *accountFiee.ChatRecordData) {
for _, replyRules := range r.Rules {
for _, rule := range replyRules.Rules {
hit, runTime, function := rule.Hit(record)
if hit && function != nil {
if runTime.IsZero() {
go func() {
err := function(r.Info.ID, replyRules.Response)
if err != nil {
log.Printf("聊天机器人[%d]回复消息失败:%v", r.Info.ID, err)
}
}()
} else {
r.mu.Lock()
r.DelayTask = append(r.DelayTask, RobotTask{
RunTime: runTime,
Run: function,
Response: replyRules.Response,
})
r.mu.Unlock()
// 添加任务后启动定时任务(如果未运行)
if !r.isRunning {
go r.Run()
}
}
break
}
}
}
}
func (r *Robot) Run() {
r.mu.Lock()
if r.isRunning {
r.mu.Unlock()
return
}
r.isRunning = true
r.ticker = time.NewTicker(time.Second)
r.stopChan = make(chan struct{})
r.mu.Unlock()
defer func() {
r.mu.Lock()
r.isRunning = false
if r.ticker != nil {
r.ticker.Stop()
r.ticker = nil
}
r.stopChan = nil
r.mu.Unlock()
}()
for {
select {
case <-r.ticker.C:
r.mu.Lock()
if len(r.DelayTask) == 0 {
r.mu.Unlock()
break
//return // 没有任务时退出
}
now := time.Now()
var remainingTasks []RobotTask
for _, task := range r.DelayTask {
if now.After(task.RunTime) {
// 执行任务
go func() {
err := task.Run(r.Info.ID, task.Response)
if err != nil {
log.Printf("聊天机器人[%d]回复消息失败:%v", r.Info.ID, err)
}
}()
} else {
// 保留未到期的任务
remainingTasks = append(remainingTasks, task)
}
}
r.DelayTask = remainingTasks
r.mu.Unlock()
case <-r.stopChan:
return
case event := <-r.EventListener.Chan:
fmt.Sprintf("listen event:%#v\n", event)
}
}
}
// Stop 主动停止机器人的定时任务
func (r *Robot) Stop() {
r.mu.Lock()
if r.stopChan != nil {
close(r.stopChan)
}
r.mu.Unlock()
}

View File

@ -1,7 +0,0 @@
// Package autoReply -----------------------------
// @file : rulerList.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 16:16
// -------------------------------------------
package robot

View File

@ -1,15 +0,0 @@
// Package robot -----------------------------
// @file : task.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 18:02
// -------------------------------------------
package robot
import "time"
type RobotTask struct {
RunTime time.Time
Run func(robotId int64, response string) error
Response string
}