You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openim-sdk-cpp/go/chao-sdk-core/internal/conversation_msg/entering.go

217 lines
6.7 KiB

package conversation_msg
import (
"context"
"encoding/json"
"github.com/jinzhu/copier"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
"github.com/patrickmn/go-cache"
"time"
)
const (
_ int = iota
stateCodeSuccess
stateCodeEnd
)
const (
inputStatesSendTime = time.Second * 10 // input status sending interval time
inputStatesTimeout = inputStatesSendTime + inputStatesSendTime/2 // input status timeout
inputStatesMsgTimeout = inputStatesSendTime / 2 // message sending timeout
)
func newTyping(c *Conversation) *typing {
e := &typing{
conv: c,
send: cache.New(inputStatesSendTime, inputStatesTimeout),
state: cache.New(inputStatesTimeout, inputStatesTimeout),
}
e.platformIDs = make([]int32, 0, len(constant.PlatformID2Name))
e.platformIDSet = make(map[int32]struct{})
for id := range constant.PlatformID2Name {
e.platformIDSet[int32(id)] = struct{}{}
e.platformIDs = append(e.platformIDs, int32(id))
}
datautil.Sort(e.platformIDs, true)
e.state.OnEvicted(func(key string, val interface{}) {
var data inputStatesKey
if err := json.Unmarshal([]byte(key), &data); err != nil {
return
}
e.changes(data.ConversationID, data.UserID)
})
return e
}
type typing struct {
send *cache.Cache
state *cache.Cache
conv *Conversation
platformIDs []int32
platformIDSet map[int32]struct{}
}
func (e *typing) ChangeInputStates(ctx context.Context, conversationID string, focus bool) error {
if conversationID == "" {
return errs.ErrArgs.WrapMsg("conversationID can't be empty")
}
conversation, err := e.conv.db.GetConversation(ctx, conversationID)
if err != nil {
return err
}
key := conversation.ConversationID
if focus {
if val, ok := e.send.Get(key); ok {
if val.(int) == stateCodeSuccess {
log.ZDebug(ctx, "typing stateCodeSuccess", "conversationID", conversationID, "focus", focus)
return nil
}
}
e.send.SetDefault(key, stateCodeSuccess)
} else {
if val, ok := e.send.Get(key); ok {
if val.(int) == stateCodeEnd {
log.ZDebug(ctx, "typing stateCodeEnd", "conversationID", conversationID, "focus", focus)
return nil
}
e.send.SetDefault(key, stateCodeEnd)
} else {
log.ZDebug(ctx, "typing send not found", "conversationID", conversationID, "focus", focus)
return nil
}
}
ctx, cancel := context.WithTimeout(ctx, inputStatesMsgTimeout)
defer cancel()
if err := e.sendMsg(ctx, conversation, focus); err != nil {
e.send.Delete(key)
return err
}
return nil
}
func (e *typing) sendMsg(ctx context.Context, conversation *model_struct.LocalConversation, focus bool) error {
s := sdk_struct.MsgStruct{}
err := e.conv.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Typing)
if err != nil {
return err
}
s.RecvID = conversation.UserID
s.GroupID = conversation.GroupID
s.SessionType = conversation.ConversationType
var typingElem sdk_struct.TypingElem
if focus {
typingElem.MsgTips = "yes"
} else {
typingElem.MsgTips = "no"
}
s.Content = utils.StructToJsonString(typingElem)
options := make(map[string]bool, 6)
utils.SetSwitchFromOptions(options, constant.IsHistory, false)
utils.SetSwitchFromOptions(options, constant.IsPersistent, false)
utils.SetSwitchFromOptions(options, constant.IsSenderSync, false)
utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, false)
utils.SetSwitchFromOptions(options, constant.IsSenderConversationUpdate, false)
utils.SetSwitchFromOptions(options, constant.IsUnreadCount, false)
utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
var wsMsgData sdkws.MsgData
copier.Copy(&wsMsgData, s)
wsMsgData.Content = []byte(s.Content)
wsMsgData.CreateTime = s.CreateTime
wsMsgData.Options = options
var sendMsgResp sdkws.UserSendMsgResp
err = e.conv.LongConnMgr.SendReqWaitResp(ctx, &wsMsgData, constant.SendMsg, &sendMsgResp)
if err != nil {
log.ZError(ctx, "typing msg to server failed", err, "message", s)
return err
}
return nil
}
type inputStatesKey struct {
ConversationID string `json:"cid,omitempty"`
UserID string `json:"uid,omitempty"`
PlatformID int32 `json:"pid,omitempty"`
}
func (e *typing) getStateKey(conversationID string, userID string, platformID int32) string {
data, err := json.Marshal(inputStatesKey{ConversationID: conversationID, UserID: userID, PlatformID: platformID})
if err != nil {
panic(err)
}
return string(data)
}
func (e *typing) onNewMsg(ctx context.Context, msg *sdkws.MsgData) {
var enteringElem sdk_struct.TypingElem
if err := json.Unmarshal(msg.Content, &enteringElem); err != nil {
log.ZError(ctx, "typing onNewMsg Unmarshal failed", err, "message", msg)
return
}
if msg.SendID == e.conv.loginUserID {
return
}
if _, ok := e.platformIDSet[msg.SenderPlatformID]; !ok {
return
}
now := time.Now().UnixMilli()
expirationTimestamp := msg.SendTime + int64(inputStatesSendTime/time.Millisecond)
if msg.SendTime > now || expirationTimestamp <= now {
return
}
var sourceID string
if msg.GroupID == "" {
sourceID = msg.SendID
} else {
sourceID = msg.GroupID
}
conversationID := e.conv.getConversationIDBySessionType(sourceID, int(msg.SessionType))
key := e.getStateKey(conversationID, msg.SendID, msg.SenderPlatformID)
if enteringElem.MsgTips == "yes" {
d := time.Duration(expirationTimestamp-now) * time.Millisecond
if v, t, ok := e.state.GetWithExpiration(key); ok {
if t.UnixMilli() >= expirationTimestamp {
return
}
e.state.Set(key, v, d)
} else {
e.state.Set(key, struct{}{}, d)
e.changes(conversationID, msg.SendID)
}
} else {
if _, ok := e.state.Get(key); ok {
e.state.Delete(key)
}
}
}
type InputStatesChangedData struct {
ConversationID string `json:"conversationID"`
UserID string `json:"userID"`
PlatformIDs []int32 `json:"platformIDs"`
}
func (e *typing) changes(conversationID string, userID string) {
data := InputStatesChangedData{ConversationID: conversationID, UserID: userID, PlatformIDs: e.GetInputStates(conversationID, userID)}
e.conv.ConversationListener().OnConversationUserInputStatusChanged(utils.StructToJsonString(data))
}
func (e *typing) GetInputStates(conversationID string, userID string) []int32 {
platformIDs := make([]int32, 0, 1)
for _, platformID := range e.platformIDs {
key := e.getStateKey(conversationID, userID, platformID)
if _, ok := e.state.Get(key); ok {
platformIDs = append(platformIDs, platformID)
}
}
return platformIDs
}