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.
1199 lines
46 KiB
1199 lines
46 KiB
// Copyright © 2023 OpenIM SDK. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package conversation_msg
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/openimsdk/openim-sdk-core/v3/internal/file"
|
|
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/content_type"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
|
|
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/openimsdk/tools/log"
|
|
|
|
pbConversation "github.com/openimsdk/protocol/conversation"
|
|
"github.com/openimsdk/protocol/sdkws"
|
|
"github.com/openimsdk/protocol/wrapperspb"
|
|
|
|
"github.com/jinzhu/copier"
|
|
)
|
|
|
|
func (c *Conversation) GetAllConversationList(ctx context.Context) ([]*model_struct.LocalConversation, error) {
|
|
return c.db.GetAllConversationListDB(ctx)
|
|
}
|
|
|
|
func (c *Conversation) GetConversationListSplit(ctx context.Context, offset, count int) ([]*model_struct.LocalConversation, error) {
|
|
return c.db.GetConversationListSplitDB(ctx, offset, count)
|
|
}
|
|
|
|
func (c *Conversation) HideConversation(ctx context.Context, conversationID string) error {
|
|
err := c.db.ResetConversation(ctx, conversationID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Conversation) GetAtAllTag(_ context.Context) string {
|
|
return constant.AtAllString
|
|
}
|
|
|
|
// deprecated
|
|
func (c *Conversation) GetConversationRecvMessageOpt(ctx context.Context, conversationIDs []string) (resp []*server_api_params.GetConversationRecvMessageOptResp, err error) {
|
|
conversations, err := c.db.GetMultipleConversationDB(ctx, conversationIDs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, conversation := range conversations {
|
|
resp = append(resp, &server_api_params.GetConversationRecvMessageOptResp{
|
|
ConversationID: conversation.ConversationID,
|
|
Result: &conversation.RecvMsgOpt,
|
|
})
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// Method to set global message receiving options
|
|
func (c *Conversation) GetOneConversation(ctx context.Context, sessionType int32, sourceID string) (*model_struct.LocalConversation, error) {
|
|
conversationID := c.getConversationIDBySessionType(sourceID, int(sessionType))
|
|
lc, err := c.db.GetConversation(ctx, conversationID)
|
|
if err == nil {
|
|
return lc, nil
|
|
} else {
|
|
var newConversation model_struct.LocalConversation
|
|
newConversation.ConversationID = conversationID
|
|
newConversation.ConversationType = sessionType
|
|
switch sessionType {
|
|
case constant.SingleChatType:
|
|
newConversation.UserID = sourceID
|
|
faceUrl, name, err := c.getUserNameAndFaceURL(ctx, sourceID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newConversation.ShowName = name
|
|
newConversation.FaceURL = faceUrl
|
|
case constant.GroupChatType, constant.SuperGroupChatType:
|
|
newConversation.GroupID = sourceID
|
|
g, err := c.full.GetGroupInfoFromLocal2Svr(ctx, sourceID, sessionType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newConversation.ShowName = g.GroupName
|
|
newConversation.FaceURL = g.FaceURL
|
|
}
|
|
time.Sleep(time.Millisecond * 500)
|
|
lc, errTemp := c.db.GetConversation(ctx, conversationID)
|
|
if errTemp == nil {
|
|
return lc, nil
|
|
}
|
|
err := c.db.InsertConversation(ctx, &newConversation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &newConversation, nil
|
|
}
|
|
}
|
|
|
|
func (c *Conversation) GetMultipleConversation(ctx context.Context, conversationIDList []string) ([]*model_struct.LocalConversation, error) {
|
|
conversations, err := c.db.GetMultipleConversationDB(ctx, conversationIDList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return conversations, nil
|
|
|
|
}
|
|
|
|
func (c *Conversation) HideAllConversations(ctx context.Context) error {
|
|
err := c.db.ResetAllConversation(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Conversation) SetConversationDraft(ctx context.Context, conversationID, draftText string) error {
|
|
if draftText != "" {
|
|
err := c.db.SetConversationDraftDB(ctx, conversationID, draftText)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
err := c.db.RemoveConversationDraft(ctx, conversationID, draftText)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{Action: constant.ConChange, Args: []string{conversationID}}, c.GetCh())
|
|
return nil
|
|
}
|
|
|
|
func (c *Conversation) setConversationAndSync(ctx context.Context, conversationID string, req *pbConversation.ConversationReq) error {
|
|
lc, err := c.db.GetConversation(ctx, conversationID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
apiReq := &pbConversation.SetConversationsReq{Conversation: req}
|
|
err = c.setConversation(ctx, apiReq, lc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.SyncConversations(ctx, []string{conversationID})
|
|
return nil
|
|
}
|
|
|
|
func (c *Conversation) ResetConversationGroupAtType(ctx context.Context, conversationID string) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{GroupAtType: &wrapperspb.Int32Value{Value: 0}})
|
|
}
|
|
|
|
func (c *Conversation) PinConversation(ctx context.Context, conversationID string, isPinned bool) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{IsPinned: &wrapperspb.BoolValue{Value: isPinned}})
|
|
}
|
|
|
|
func (c *Conversation) SetOneConversationPrivateChat(ctx context.Context, conversationID string, isPrivate bool) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{IsPrivateChat: &wrapperspb.BoolValue{Value: isPrivate}})
|
|
}
|
|
|
|
func (c *Conversation) SetConversationMsgDestructTime(ctx context.Context, conversationID string, msgDestructTime int64) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{MsgDestructTime: &wrapperspb.Int64Value{Value: msgDestructTime}})
|
|
}
|
|
|
|
func (c *Conversation) SetConversationIsMsgDestruct(ctx context.Context, conversationID string, isMsgDestruct bool) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{IsMsgDestruct: &wrapperspb.BoolValue{Value: isMsgDestruct}})
|
|
}
|
|
|
|
func (c *Conversation) SetOneConversationBurnDuration(ctx context.Context, conversationID string, burnDuration int32) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{BurnDuration: &wrapperspb.Int32Value{Value: burnDuration}})
|
|
}
|
|
|
|
func (c *Conversation) SetOneConversationRecvMessageOpt(ctx context.Context, conversationID string, opt int) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{RecvMsgOpt: &wrapperspb.Int32Value{Value: int32(opt)}})
|
|
}
|
|
func (c *Conversation) SetOneConversationEx(ctx context.Context, conversationID string, ex string) error {
|
|
return c.setConversationAndSync(ctx, conversationID, &pbConversation.ConversationReq{Ex: &wrapperspb.StringValue{
|
|
Value: ex,
|
|
}})
|
|
}
|
|
func (c *Conversation) GetTotalUnreadMsgCount(ctx context.Context) (totalUnreadCount int32, err error) {
|
|
return c.db.GetTotalUnreadMsgCountDB(ctx)
|
|
}
|
|
|
|
func (c *Conversation) SetConversationListener(listener func() open_im_sdk_callback.OnConversationListener) {
|
|
c.ConversationListener = listener
|
|
}
|
|
|
|
func (c *Conversation) msgStructToLocalChatLog(src *sdk_struct.MsgStruct) *model_struct.LocalChatLog {
|
|
var lc model_struct.LocalChatLog
|
|
copier.Copy(&lc, src)
|
|
switch src.ContentType {
|
|
case constant.Text:
|
|
lc.Content = utils.StructToJsonString(src.TextElem)
|
|
case constant.Picture:
|
|
lc.Content = utils.StructToJsonString(src.PictureElem)
|
|
case constant.Sound:
|
|
lc.Content = utils.StructToJsonString(src.SoundElem)
|
|
case constant.Video:
|
|
lc.Content = utils.StructToJsonString(src.VideoElem)
|
|
case constant.File:
|
|
lc.Content = utils.StructToJsonString(src.FileElem)
|
|
case constant.AtText:
|
|
lc.Content = utils.StructToJsonString(src.AtTextElem)
|
|
case constant.Merger:
|
|
lc.Content = utils.StructToJsonString(src.MergeElem)
|
|
case constant.Card:
|
|
lc.Content = utils.StructToJsonString(src.CardElem)
|
|
case constant.Location:
|
|
lc.Content = utils.StructToJsonString(src.LocationElem)
|
|
case constant.Custom:
|
|
lc.Content = utils.StructToJsonString(src.CustomElem)
|
|
case constant.Quote:
|
|
lc.Content = utils.StructToJsonString(src.QuoteElem)
|
|
case constant.Face:
|
|
lc.Content = utils.StructToJsonString(src.FaceElem)
|
|
case constant.AdvancedText:
|
|
lc.Content = utils.StructToJsonString(src.AdvancedTextElem)
|
|
default:
|
|
lc.Content = utils.StructToJsonString(src.NotificationElem)
|
|
}
|
|
if src.SessionType == constant.GroupChatType || src.SessionType == constant.SuperGroupChatType {
|
|
lc.RecvID = src.GroupID
|
|
}
|
|
lc.AttachedInfo = utils.StructToJsonString(src.AttachedInfoElem)
|
|
return &lc
|
|
}
|
|
func (c *Conversation) msgDataToLocalChatLog(src *sdkws.MsgData) *model_struct.LocalChatLog {
|
|
var lc model_struct.LocalChatLog
|
|
copier.Copy(&lc, src)
|
|
lc.Content = string(src.Content)
|
|
if src.SessionType == constant.GroupChatType || src.SessionType == constant.SuperGroupChatType {
|
|
lc.RecvID = src.GroupID
|
|
|
|
}
|
|
return &lc
|
|
|
|
}
|
|
func (c *Conversation) msgDataToLocalErrChatLog(src *model_struct.LocalChatLog) *model_struct.LocalErrChatLog {
|
|
var lc model_struct.LocalErrChatLog
|
|
copier.Copy(&lc, src)
|
|
return &lc
|
|
|
|
}
|
|
|
|
func localChatLogToMsgStruct(dst *sdk_struct.NewMsgList, src []*model_struct.LocalChatLog) {
|
|
copier.Copy(dst, &src)
|
|
|
|
}
|
|
|
|
func (c *Conversation) updateMsgStatusAndTriggerConversation(ctx context.Context, clientMsgID, serverMsgID string, sendTime int64, status int32, s *sdk_struct.MsgStruct,
|
|
lc *model_struct.LocalConversation, isOnlineOnly bool) {
|
|
log.ZDebug(ctx, "this is test send message ", "sendTime", sendTime, "status", status, "clientMsgID", clientMsgID, "serverMsgID", serverMsgID)
|
|
if isOnlineOnly {
|
|
return
|
|
}
|
|
s.SendTime = sendTime
|
|
s.Status = status
|
|
s.ServerMsgID = serverMsgID
|
|
err := c.db.UpdateMessageTimeAndStatus(ctx, lc.ConversationID, clientMsgID, serverMsgID, sendTime, status)
|
|
if err != nil {
|
|
log.ZWarn(ctx, "send message update message status error", err,
|
|
"sendTime", sendTime, "status", status, "clientMsgID", clientMsgID, "serverMsgID", serverMsgID)
|
|
}
|
|
err = c.db.DeleteSendingMessage(ctx, lc.ConversationID, clientMsgID)
|
|
if err != nil {
|
|
log.ZWarn(ctx, "send message delete sending message error", err)
|
|
}
|
|
lc.LatestMsg = utils.StructToJsonString(s)
|
|
lc.LatestMsgSendTime = sendTime
|
|
// log.Info("", "2 send message come here", *lc)
|
|
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: lc.ConversationID, Action: constant.AddConOrUpLatMsg, Args: *lc}, c.GetCh())
|
|
}
|
|
|
|
func (c *Conversation) fileName(ftype string, id string) string {
|
|
return fmt.Sprintf("msg_%s_%s", ftype, id)
|
|
}
|
|
|
|
func (c *Conversation) checkID(ctx context.Context, s *sdk_struct.MsgStruct,
|
|
recvID, groupID string, options map[string]bool) (*model_struct.LocalConversation, error) {
|
|
if recvID == "" && groupID == "" {
|
|
return nil, sdkerrs.ErrArgs
|
|
}
|
|
s.SendID = c.loginUserID
|
|
s.SenderPlatformID = c.platformID
|
|
lc := &model_struct.LocalConversation{LatestMsgSendTime: s.CreateTime}
|
|
//根据单聊群聊类型组装消息和会话
|
|
if recvID == "" {
|
|
g, err := c.full.GetGroupInfoByGroupID(ctx, groupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lc.ShowName = g.GroupName
|
|
lc.FaceURL = g.FaceURL
|
|
switch g.GroupType {
|
|
case constant.NormalGroup:
|
|
s.SessionType = constant.GroupChatType
|
|
lc.ConversationType = constant.GroupChatType
|
|
lc.ConversationID = c.getConversationIDBySessionType(groupID, constant.GroupChatType)
|
|
case constant.SuperGroup, constant.WorkingGroup:
|
|
s.SessionType = constant.SuperGroupChatType
|
|
lc.ConversationID = c.getConversationIDBySessionType(groupID, constant.SuperGroupChatType)
|
|
lc.ConversationType = constant.SuperGroupChatType
|
|
}
|
|
s.GroupID = groupID
|
|
lc.GroupID = groupID
|
|
gm, err := c.db.GetGroupMemberInfoByGroupIDUserID(ctx, groupID, c.loginUserID)
|
|
if err == nil && gm != nil {
|
|
if gm.Nickname != "" {
|
|
s.SenderNickname = gm.Nickname
|
|
}
|
|
}
|
|
var attachedInfo sdk_struct.AttachedInfoElem
|
|
attachedInfo.GroupHasReadInfo.GroupMemberCount = g.MemberCount
|
|
s.AttachedInfoElem = &attachedInfo
|
|
} else {
|
|
s.SessionType = constant.SingleChatType
|
|
s.RecvID = recvID
|
|
lc.ConversationID = utils.GetConversationIDByMsg(s)
|
|
lc.UserID = recvID
|
|
lc.ConversationType = constant.SingleChatType
|
|
oldLc, err := c.db.GetConversation(ctx, lc.ConversationID)
|
|
if err == nil && oldLc.IsPrivateChat {
|
|
options[constant.IsNotPrivate] = false
|
|
var attachedInfo sdk_struct.AttachedInfoElem
|
|
attachedInfo.IsPrivateChat = true
|
|
attachedInfo.BurnDuration = oldLc.BurnDuration
|
|
s.AttachedInfoElem = &attachedInfo
|
|
}
|
|
if err != nil {
|
|
t := time.Now()
|
|
faceUrl, name, err := c.getUserNameAndFaceURL(ctx, recvID)
|
|
log.ZDebug(ctx, "GetUserNameAndFaceURL", "cost time", time.Since(t))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lc.FaceURL = faceUrl
|
|
lc.ShowName = name
|
|
}
|
|
|
|
}
|
|
return lc, nil
|
|
}
|
|
func (c *Conversation) getConversationIDBySessionType(sourceID string, sessionType int) string {
|
|
switch sessionType {
|
|
case constant.SingleChatType:
|
|
l := []string{c.loginUserID, sourceID}
|
|
sort.Strings(l)
|
|
return "si_" + strings.Join(l, "_") // single chat
|
|
case constant.GroupChatType:
|
|
return "g_" + sourceID // group chat
|
|
case constant.SuperGroupChatType:
|
|
return "sg_" + sourceID // super group chat
|
|
case constant.NotificationChatType:
|
|
return "sn_" + sourceID + "_" + c.loginUserID // server notification chat
|
|
}
|
|
return ""
|
|
}
|
|
func (c *Conversation) GetConversationIDBySessionType(_ context.Context, sourceID string, sessionType int) string {
|
|
return c.getConversationIDBySessionType(sourceID, sessionType)
|
|
}
|
|
func (c *Conversation) SendMessage(ctx context.Context, s *sdk_struct.MsgStruct, recvID, groupID string, p *sdkws.OfflinePushInfo, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
|
|
filepathExt := func(name ...string) string {
|
|
for _, path := range name {
|
|
if ext := filepath.Ext(path); ext != "" {
|
|
return ext
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
options := make(map[string]bool, 2)
|
|
lc, err := c.checkID(ctx, s, recvID, groupID, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
callback, _ := ctx.Value("callback").(open_im_sdk_callback.SendMsgCallBack)
|
|
log.ZDebug(ctx, "before insert message is", "message", *s)
|
|
if !isOnlineOnly {
|
|
oldMessage, err := c.db.GetMessage(ctx, lc.ConversationID, s.ClientMsgID)
|
|
if err != nil {
|
|
localMessage := c.msgStructToLocalChatLog(s)
|
|
err := c.db.InsertMessage(ctx, lc.ConversationID, localMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = c.db.InsertSendingMessage(ctx, &model_struct.LocalSendingMessages{
|
|
ConversationID: lc.ConversationID,
|
|
ClientMsgID: localMessage.ClientMsgID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if oldMessage.Status != constant.MsgStatusSendFailed {
|
|
return nil, sdkerrs.ErrMsgRepeated
|
|
} else {
|
|
s.Status = constant.MsgStatusSending
|
|
err = c.db.InsertSendingMessage(ctx, &model_struct.LocalSendingMessages{
|
|
ConversationID: lc.ConversationID,
|
|
ClientMsgID: s.ClientMsgID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
lc.LatestMsg = utils.StructToJsonString(s)
|
|
log.ZDebug(ctx, "send message come here", "conversion", *lc)
|
|
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: lc.ConversationID, Action: constant.AddConOrUpLatMsg, Args: *lc}, c.GetCh())
|
|
}
|
|
|
|
var delFile []string
|
|
//media file handle
|
|
switch s.ContentType {
|
|
case constant.Picture:
|
|
if s.Status == constant.MsgStatusSendSuccess {
|
|
s.Content = utils.StructToJsonString(s.PictureElem)
|
|
break
|
|
}
|
|
var sourcePath string
|
|
if utils.FileExist(s.PictureElem.SourcePath) {
|
|
sourcePath = s.PictureElem.SourcePath
|
|
delFile = append(delFile, utils.FileTmpPath(s.PictureElem.SourcePath, c.DataDir))
|
|
} else {
|
|
sourcePath = utils.FileTmpPath(s.PictureElem.SourcePath, c.DataDir)
|
|
delFile = append(delFile, sourcePath)
|
|
}
|
|
// log.Info("", "file", sourcePath, delFile)
|
|
log.ZDebug(ctx, "send picture", "path", sourcePath)
|
|
|
|
res, err := c.file.UploadFile(ctx, &file.UploadFileReq{
|
|
ContentType: s.PictureElem.SourcePicture.Type,
|
|
Filepath: sourcePath,
|
|
Uuid: s.PictureElem.SourcePicture.UUID,
|
|
Name: c.fileName("picture", s.ClientMsgID) + filepathExt(s.PictureElem.SourcePicture.UUID, sourcePath),
|
|
Cause: "msg-picture",
|
|
}, NewUploadFileCallback(ctx, callback.OnProgress, s, lc.ConversationID, c.db))
|
|
if err != nil {
|
|
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime, constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
|
|
return nil, err
|
|
}
|
|
s.PictureElem.SourcePicture.Url = res.URL
|
|
s.PictureElem.BigPicture = s.PictureElem.SourcePicture
|
|
u, err := url.Parse(res.URL)
|
|
if err == nil {
|
|
snapshot := u.Query()
|
|
snapshot.Set("type", "image")
|
|
snapshot.Set("width", "640")
|
|
snapshot.Set("height", "640")
|
|
u.RawQuery = snapshot.Encode()
|
|
s.PictureElem.SnapshotPicture = &sdk_struct.PictureBaseInfo{
|
|
Width: 640,
|
|
Height: 640,
|
|
Url: u.String(),
|
|
}
|
|
} else {
|
|
log.ZError(ctx, "parse url failed", err, "url", res.URL, "err", err)
|
|
s.PictureElem.SnapshotPicture = s.PictureElem.SourcePicture
|
|
}
|
|
s.Content = utils.StructToJsonString(s.PictureElem)
|
|
case constant.Sound:
|
|
if s.Status == constant.MsgStatusSendSuccess {
|
|
s.Content = utils.StructToJsonString(s.SoundElem)
|
|
break
|
|
}
|
|
var sourcePath string
|
|
if utils.FileExist(s.SoundElem.SoundPath) {
|
|
sourcePath = s.SoundElem.SoundPath
|
|
delFile = append(delFile, utils.FileTmpPath(s.SoundElem.SoundPath, c.DataDir))
|
|
} else {
|
|
sourcePath = utils.FileTmpPath(s.SoundElem.SoundPath, c.DataDir)
|
|
delFile = append(delFile, sourcePath)
|
|
}
|
|
// log.Info("", "file", sourcePath, delFile)
|
|
|
|
res, err := c.file.UploadFile(ctx, &file.UploadFileReq{
|
|
ContentType: s.SoundElem.SoundType,
|
|
Filepath: sourcePath,
|
|
Uuid: s.SoundElem.UUID,
|
|
Name: c.fileName("voice", s.ClientMsgID) + filepathExt(s.SoundElem.UUID, sourcePath),
|
|
Cause: "msg-voice",
|
|
}, NewUploadFileCallback(ctx, callback.OnProgress, s, lc.ConversationID, c.db))
|
|
if err != nil {
|
|
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime, constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
|
|
return nil, err
|
|
}
|
|
s.SoundElem.SourceURL = res.URL
|
|
s.Content = utils.StructToJsonString(s.SoundElem)
|
|
case constant.Video:
|
|
if s.Status == constant.MsgStatusSendSuccess {
|
|
s.Content = utils.StructToJsonString(s.VideoElem)
|
|
break
|
|
}
|
|
var videoPath string
|
|
var snapPath string
|
|
if utils.FileExist(s.VideoElem.VideoPath) {
|
|
videoPath = s.VideoElem.VideoPath
|
|
snapPath = s.VideoElem.SnapshotPath
|
|
delFile = append(delFile, utils.FileTmpPath(s.VideoElem.VideoPath, c.DataDir))
|
|
delFile = append(delFile, utils.FileTmpPath(s.VideoElem.SnapshotPath, c.DataDir))
|
|
} else {
|
|
videoPath = utils.FileTmpPath(s.VideoElem.VideoPath, c.DataDir)
|
|
snapPath = utils.FileTmpPath(s.VideoElem.SnapshotPath, c.DataDir)
|
|
delFile = append(delFile, videoPath)
|
|
delFile = append(delFile, snapPath)
|
|
}
|
|
log.ZDebug(ctx, "file", "videoPath", videoPath, "snapPath", snapPath, "delFile", delFile)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
var putErrs error
|
|
go func() {
|
|
defer wg.Done()
|
|
snapRes, err := c.file.UploadFile(ctx, &file.UploadFileReq{
|
|
ContentType: s.VideoElem.SnapshotType,
|
|
Filepath: snapPath,
|
|
Uuid: s.VideoElem.SnapshotUUID,
|
|
Name: c.fileName("videoSnapshot", s.ClientMsgID) + filepathExt(s.VideoElem.SnapshotUUID, snapPath),
|
|
Cause: "msg-video-snapshot",
|
|
}, nil)
|
|
if err != nil {
|
|
log.ZWarn(ctx, "upload video snapshot failed", err)
|
|
return
|
|
}
|
|
s.VideoElem.SnapshotURL = snapRes.URL
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
res, err := c.file.UploadFile(ctx, &file.UploadFileReq{
|
|
ContentType: content_type.GetType(s.VideoElem.VideoType, filepath.Ext(s.VideoElem.VideoPath)),
|
|
Filepath: videoPath,
|
|
Uuid: s.VideoElem.VideoUUID,
|
|
Name: c.fileName("video", s.ClientMsgID) + filepathExt(s.VideoElem.VideoUUID, videoPath),
|
|
Cause: "msg-video",
|
|
}, NewUploadFileCallback(ctx, callback.OnProgress, s, lc.ConversationID, c.db))
|
|
if err != nil {
|
|
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime, constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
|
|
putErrs = err
|
|
}
|
|
if res != nil {
|
|
s.VideoElem.VideoURL = res.URL
|
|
}
|
|
}()
|
|
wg.Wait()
|
|
if err := putErrs; err != nil {
|
|
return nil, err
|
|
}
|
|
s.Content = utils.StructToJsonString(s.VideoElem)
|
|
case constant.File:
|
|
if s.Status == constant.MsgStatusSendSuccess {
|
|
s.Content = utils.StructToJsonString(s.FileElem)
|
|
break
|
|
}
|
|
name := s.FileElem.FileName
|
|
if name == "" {
|
|
name = s.FileElem.FilePath
|
|
}
|
|
if name == "" {
|
|
name = fmt.Sprintf("msg_file_%s.unknown", s.ClientMsgID)
|
|
}
|
|
res, err := c.file.UploadFile(ctx, &file.UploadFileReq{
|
|
ContentType: content_type.GetType(s.FileElem.FileType, filepath.Ext(s.FileElem.FilePath), filepath.Ext(s.FileElem.FileName)),
|
|
Filepath: s.FileElem.FilePath,
|
|
Uuid: s.FileElem.UUID,
|
|
Name: c.fileName("file", s.ClientMsgID) + "/" + filepath.Base(name),
|
|
Cause: "msg-file",
|
|
}, NewUploadFileCallback(ctx, callback.OnProgress, s, lc.ConversationID, c.db))
|
|
if err != nil {
|
|
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime, constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
|
|
return nil, err
|
|
}
|
|
s.FileElem.SourceURL = res.URL
|
|
s.Content = utils.StructToJsonString(s.FileElem)
|
|
case constant.Text:
|
|
s.Content = utils.StructToJsonString(s.TextElem)
|
|
case constant.AtText:
|
|
s.Content = utils.StructToJsonString(s.AtTextElem)
|
|
case constant.Location:
|
|
s.Content = utils.StructToJsonString(s.LocationElem)
|
|
case constant.Custom:
|
|
s.Content = utils.StructToJsonString(s.CustomElem)
|
|
case constant.Merger:
|
|
s.Content = utils.StructToJsonString(s.MergeElem)
|
|
case constant.Quote:
|
|
s.Content = utils.StructToJsonString(s.QuoteElem)
|
|
case constant.Card:
|
|
s.Content = utils.StructToJsonString(s.CardElem)
|
|
case constant.Face:
|
|
s.Content = utils.StructToJsonString(s.FaceElem)
|
|
case constant.AdvancedText:
|
|
s.Content = utils.StructToJsonString(s.AdvancedTextElem)
|
|
default:
|
|
return nil, sdkerrs.ErrMsgContentTypeNotSupport
|
|
}
|
|
if utils.IsContainInt(int(s.ContentType), []int{constant.Picture, constant.Sound, constant.Video, constant.File}) {
|
|
if !isOnlineOnly {
|
|
localMessage := c.msgStructToLocalChatLog(s)
|
|
log.ZDebug(ctx, "update message is ", "localMessage", localMessage)
|
|
err = c.db.UpdateMessage(ctx, lc.ConversationID, localMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
return c.sendMessageToServer(ctx, s, lc, callback, delFile, p, options, isOnlineOnly)
|
|
|
|
}
|
|
func (c *Conversation) SendMessageNotOss(ctx context.Context, s *sdk_struct.MsgStruct, recvID, groupID string,
|
|
p *sdkws.OfflinePushInfo, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
|
|
options := make(map[string]bool, 2)
|
|
lc, err := c.checkID(ctx, s, recvID, groupID, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
callback, _ := ctx.Value("callback").(open_im_sdk_callback.SendMsgCallBack)
|
|
if !isOnlineOnly {
|
|
oldMessage, err := c.db.GetMessage(ctx, lc.ConversationID, s.ClientMsgID)
|
|
if err != nil {
|
|
localMessage := c.msgStructToLocalChatLog(s)
|
|
err := c.db.InsertMessage(ctx, lc.ConversationID, localMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = c.db.InsertSendingMessage(ctx, &model_struct.LocalSendingMessages{
|
|
ConversationID: lc.ConversationID,
|
|
ClientMsgID: localMessage.ClientMsgID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if oldMessage.Status != constant.MsgStatusSendFailed {
|
|
return nil, sdkerrs.ErrMsgRepeated
|
|
} else {
|
|
s.Status = constant.MsgStatusSending
|
|
err = c.db.InsertSendingMessage(ctx, &model_struct.LocalSendingMessages{
|
|
ConversationID: lc.ConversationID,
|
|
ClientMsgID: s.ClientMsgID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
lc.LatestMsg = utils.StructToJsonString(s)
|
|
var delFile []string
|
|
switch s.ContentType {
|
|
case constant.Picture:
|
|
s.Content = utils.StructToJsonString(s.PictureElem)
|
|
case constant.Sound:
|
|
s.Content = utils.StructToJsonString(s.SoundElem)
|
|
case constant.Video:
|
|
s.Content = utils.StructToJsonString(s.VideoElem)
|
|
case constant.File:
|
|
s.Content = utils.StructToJsonString(s.FileElem)
|
|
case constant.Text:
|
|
s.Content = utils.StructToJsonString(s.TextElem)
|
|
case constant.AtText:
|
|
s.Content = utils.StructToJsonString(s.AtTextElem)
|
|
case constant.Location:
|
|
s.Content = utils.StructToJsonString(s.LocationElem)
|
|
case constant.Custom:
|
|
s.Content = utils.StructToJsonString(s.CustomElem)
|
|
case constant.Merger:
|
|
s.Content = utils.StructToJsonString(s.MergeElem)
|
|
case constant.Quote:
|
|
s.Content = utils.StructToJsonString(s.QuoteElem)
|
|
case constant.Card:
|
|
s.Content = utils.StructToJsonString(s.CardElem)
|
|
case constant.Face:
|
|
s.Content = utils.StructToJsonString(s.FaceElem)
|
|
case constant.AdvancedText:
|
|
s.Content = utils.StructToJsonString(s.AdvancedTextElem)
|
|
default:
|
|
return nil, sdkerrs.ErrMsgContentTypeNotSupport
|
|
}
|
|
if utils.IsContainInt(int(s.ContentType), []int{constant.Picture, constant.Sound, constant.Video, constant.File}) {
|
|
if isOnlineOnly {
|
|
localMessage := c.msgStructToLocalChatLog(s)
|
|
err = c.db.UpdateMessage(ctx, lc.ConversationID, localMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
return c.sendMessageToServer(ctx, s, lc, callback, delFile, p, options, isOnlineOnly)
|
|
}
|
|
|
|
func (c *Conversation) sendMessageToServer(ctx context.Context, s *sdk_struct.MsgStruct, lc *model_struct.LocalConversation, callback open_im_sdk_callback.SendMsgCallBack,
|
|
delFile []string, offlinePushInfo *sdkws.OfflinePushInfo, options map[string]bool, isOnlineOnly bool) (*sdk_struct.MsgStruct, error) {
|
|
if isOnlineOnly {
|
|
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)
|
|
}
|
|
//Protocol conversion
|
|
var wsMsgData sdkws.MsgData
|
|
copier.Copy(&wsMsgData, s)
|
|
wsMsgData.AttachedInfo = utils.StructToJsonString(s.AttachedInfoElem)
|
|
wsMsgData.Content = []byte(s.Content)
|
|
wsMsgData.CreateTime = s.CreateTime
|
|
wsMsgData.SendTime = 0
|
|
wsMsgData.Options = options
|
|
if wsMsgData.ContentType == constant.AtText {
|
|
wsMsgData.AtUserIDList = s.AtTextElem.AtUserList
|
|
}
|
|
wsMsgData.OfflinePushInfo = offlinePushInfo
|
|
s.Content = ""
|
|
var sendMsgResp sdkws.UserSendMsgResp
|
|
|
|
err := c.LongConnMgr.SendReqWaitResp(ctx, &wsMsgData, constant.SendMsg, &sendMsgResp)
|
|
if err != nil {
|
|
//if send message network timeout need to double-check message has received by db.
|
|
if sdkerrs.ErrNetworkTimeOut.Is(err) && !isOnlineOnly {
|
|
oldMessage, _ := c.db.GetMessage(ctx, lc.ConversationID, s.ClientMsgID)
|
|
if oldMessage.Status == constant.MsgStatusSendSuccess {
|
|
sendMsgResp.SendTime = oldMessage.SendTime
|
|
sendMsgResp.ClientMsgID = oldMessage.ClientMsgID
|
|
sendMsgResp.ServerMsgID = oldMessage.ServerMsgID
|
|
} else {
|
|
log.ZError(ctx, "send msg to server failed", err, "message", s)
|
|
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime,
|
|
constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
|
|
return s, err
|
|
}
|
|
} else {
|
|
log.ZError(ctx, "send msg to server failed", err, "message", s)
|
|
c.updateMsgStatusAndTriggerConversation(ctx, s.ClientMsgID, "", s.CreateTime,
|
|
constant.MsgStatusSendFailed, s, lc, isOnlineOnly)
|
|
return s, err
|
|
}
|
|
}
|
|
s.SendTime = sendMsgResp.SendTime
|
|
s.Status = constant.MsgStatusSendSuccess
|
|
s.ServerMsgID = sendMsgResp.ServerMsgID
|
|
go func() {
|
|
//remove media cache file
|
|
for _, v := range delFile {
|
|
err := os.Remove(v)
|
|
if err != nil {
|
|
// log.Error("", "remove failed,", err.Error(), v)
|
|
}
|
|
// log.Debug("", "remove file: ", v)
|
|
}
|
|
c.updateMsgStatusAndTriggerConversation(ctx, sendMsgResp.ClientMsgID, sendMsgResp.ServerMsgID, sendMsgResp.SendTime, constant.MsgStatusSendSuccess, s, lc, isOnlineOnly)
|
|
}()
|
|
return s, nil
|
|
|
|
}
|
|
|
|
func (c *Conversation) FindMessageList(ctx context.Context, req []*sdk_params_callback.ConversationArgs) (*sdk_params_callback.FindMessageListCallback, error) {
|
|
var r sdk_params_callback.FindMessageListCallback
|
|
type tempConversationAndMessageList struct {
|
|
conversation *model_struct.LocalConversation
|
|
msgIDList []string
|
|
}
|
|
var s []*tempConversationAndMessageList
|
|
for _, conversationsArgs := range req {
|
|
localConversation, err := c.db.GetConversation(ctx, conversationsArgs.ConversationID)
|
|
if err != nil {
|
|
log.ZError(ctx, "GetConversation err", err, "conversationsArgs", conversationsArgs)
|
|
} else {
|
|
t := new(tempConversationAndMessageList)
|
|
t.conversation = localConversation
|
|
t.msgIDList = conversationsArgs.ClientMsgIDList
|
|
s = append(s, t)
|
|
}
|
|
}
|
|
for _, v := range s {
|
|
messages, err := c.db.GetMessagesByClientMsgIDs(ctx, v.conversation.ConversationID, v.msgIDList)
|
|
if err == nil {
|
|
var tempMessageList []*sdk_struct.MsgStruct
|
|
for _, message := range messages {
|
|
temp := sdk_struct.MsgStruct{}
|
|
temp.ClientMsgID = message.ClientMsgID
|
|
temp.ServerMsgID = message.ServerMsgID
|
|
temp.CreateTime = message.CreateTime
|
|
temp.SendTime = message.SendTime
|
|
temp.SessionType = message.SessionType
|
|
temp.SendID = message.SendID
|
|
temp.RecvID = message.RecvID
|
|
temp.MsgFrom = message.MsgFrom
|
|
temp.ContentType = message.ContentType
|
|
temp.SenderPlatformID = message.SenderPlatformID
|
|
temp.SenderNickname = message.SenderNickname
|
|
temp.SenderFaceURL = message.SenderFaceURL
|
|
temp.Content = message.Content
|
|
temp.Seq = message.Seq
|
|
temp.IsRead = message.IsRead
|
|
temp.Status = message.Status
|
|
temp.AttachedInfo = message.AttachedInfo
|
|
temp.Ex = message.Ex
|
|
temp.LocalEx = message.LocalEx
|
|
err := c.msgHandleByContentType(&temp)
|
|
if err != nil {
|
|
log.ZError(ctx, "msgHandleByContentType err", err, "message", temp)
|
|
continue
|
|
}
|
|
switch message.SessionType {
|
|
case constant.GroupChatType:
|
|
fallthrough
|
|
case constant.SuperGroupChatType:
|
|
temp.GroupID = temp.RecvID
|
|
temp.RecvID = c.loginUserID
|
|
}
|
|
tempMessageList = append(tempMessageList, &temp)
|
|
}
|
|
findResultItem := sdk_params_callback.SearchByConversationResult{}
|
|
findResultItem.ConversationID = v.conversation.ConversationID
|
|
findResultItem.FaceURL = v.conversation.FaceURL
|
|
findResultItem.ShowName = v.conversation.ShowName
|
|
findResultItem.ConversationType = v.conversation.ConversationType
|
|
findResultItem.MessageList = tempMessageList
|
|
findResultItem.MessageCount = len(findResultItem.MessageList)
|
|
r.FindResultItems = append(r.FindResultItems, &findResultItem)
|
|
r.TotalCount += findResultItem.MessageCount
|
|
} else {
|
|
log.ZError(ctx, "GetMessagesByClientMsgIDs err", err, "conversationID", v.conversation.ConversationID, "msgIDList", v.msgIDList)
|
|
}
|
|
}
|
|
return &r, nil
|
|
|
|
}
|
|
|
|
func (c *Conversation) GetAdvancedHistoryMessageList(ctx context.Context, req sdk_params_callback.GetAdvancedHistoryMessageListParams) (*sdk_params_callback.GetAdvancedHistoryMessageListCallback, error) {
|
|
result, err := c.getAdvancedHistoryMessageList(ctx, req, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(result.MessageList) == 0 {
|
|
s := make([]*sdk_struct.MsgStruct, 0)
|
|
result.MessageList = s
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (c *Conversation) GetAdvancedHistoryMessageListReverse(ctx context.Context, req sdk_params_callback.GetAdvancedHistoryMessageListParams) (*sdk_params_callback.GetAdvancedHistoryMessageListCallback, error) {
|
|
result, err := c.getAdvancedHistoryMessageList(ctx, req, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(result.MessageList) == 0 {
|
|
s := make([]*sdk_struct.MsgStruct, 0)
|
|
result.MessageList = s
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (c *Conversation) RevokeMessage(ctx context.Context, conversationID, clientMsgID string) error {
|
|
return c.revokeOneMessage(ctx, conversationID, clientMsgID)
|
|
}
|
|
|
|
func (c *Conversation) TypingStatusUpdate(ctx context.Context, recvID, msgTip string) error {
|
|
return c.typingStatusUpdate(ctx, recvID, msgTip)
|
|
}
|
|
|
|
// funcation (c *Conversation) MarkMessageAsReadByConID(ctx context.Context, conversationID string, msgIDList []string) error {
|
|
// if len(msgIDList) == 0 {
|
|
// _ = c.setOneConversationUnread(ctx, conversationID, 0)
|
|
// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.UnreadCountSetZero}, c.GetCh())
|
|
// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}, c.GetCh())
|
|
// return nil
|
|
// }
|
|
// return nil
|
|
|
|
// }
|
|
|
|
// deprecated
|
|
// funcation (c *Conversation) MarkGroupMessageHasRead(ctx context.Context, groupID string) {
|
|
// conversationID := c.getConversationIDBySessionType(groupID, constant.GroupChatType)
|
|
// _ = c.setOneConversationUnread(ctx, conversationID, 0)
|
|
// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.UnreadCountSetZero}, c.GetCh())
|
|
// _ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}, c.GetCh())
|
|
// }
|
|
|
|
// read draw
|
|
func (c *Conversation) MarkConversationMessageAsRead(ctx context.Context, conversationID string) error {
|
|
return c.markConversationMessageAsRead(ctx, conversationID)
|
|
}
|
|
|
|
func (c *Conversation) MarkMessagesAsReadByMsgID(ctx context.Context, conversationID string, clientMsgIDs []string) error {
|
|
return c.markMessagesAsReadByMsgID(ctx, conversationID, clientMsgIDs)
|
|
}
|
|
|
|
// delete
|
|
func (c *Conversation) DeleteMessageFromLocalStorage(ctx context.Context, conversationID string, clientMsgID string) error {
|
|
return c.deleteMessageFromLocal(ctx, conversationID, clientMsgID)
|
|
}
|
|
|
|
func (c *Conversation) DeleteMessage(ctx context.Context, conversationID string, clientMsgID string) error {
|
|
return c.deleteMessage(ctx, conversationID, clientMsgID)
|
|
}
|
|
|
|
func (c *Conversation) DeleteAllMsgFromLocalAndSvr(ctx context.Context) error {
|
|
return c.deleteAllMsgFromLocalAndSvr(ctx)
|
|
}
|
|
|
|
func (c *Conversation) DeleteAllMessageFromLocalStorage(ctx context.Context) error {
|
|
return c.deleteAllMsgFromLocal(ctx, true)
|
|
}
|
|
|
|
func (c *Conversation) ClearConversationAndDeleteAllMsg(ctx context.Context, conversationID string) error {
|
|
return c.clearConversationFromLocalAndSvr(ctx, conversationID, c.db.ClearConversation)
|
|
}
|
|
|
|
func (c *Conversation) DeleteConversationAndDeleteAllMsg(ctx context.Context, conversationID string) error {
|
|
return c.clearConversationFromLocalAndSvr(ctx, conversationID, c.db.ResetConversation)
|
|
}
|
|
|
|
// insert
|
|
func (c *Conversation) InsertSingleMessageToLocalStorage(ctx context.Context, s *sdk_struct.MsgStruct, recvID, sendID string) (*sdk_struct.MsgStruct, error) {
|
|
if recvID == "" || sendID == "" {
|
|
return nil, sdkerrs.ErrArgs
|
|
}
|
|
var conversation model_struct.LocalConversation
|
|
if sendID != c.loginUserID {
|
|
faceUrl, name, err := c.getUserNameAndFaceURL(ctx, sendID)
|
|
if err != nil {
|
|
//log.Error(operationID, "GetUserNameAndFaceURL err", err.Error(), sendID)
|
|
}
|
|
s.SenderFaceURL = faceUrl
|
|
s.SenderNickname = name
|
|
conversation.FaceURL = faceUrl
|
|
conversation.ShowName = name
|
|
conversation.UserID = sendID
|
|
conversation.ConversationID = c.getConversationIDBySessionType(sendID, constant.SingleChatType)
|
|
|
|
} else {
|
|
conversation.UserID = recvID
|
|
conversation.ConversationID = c.getConversationIDBySessionType(recvID, constant.SingleChatType)
|
|
_, err := c.db.GetConversation(ctx, conversation.ConversationID)
|
|
if err != nil {
|
|
faceUrl, name, err := c.getUserNameAndFaceURL(ctx, recvID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conversation.FaceURL = faceUrl
|
|
conversation.ShowName = name
|
|
}
|
|
}
|
|
|
|
s.SendID = sendID
|
|
s.RecvID = recvID
|
|
s.ClientMsgID = utils.GetMsgID(s.SendID)
|
|
s.SendTime = utils.GetCurrentTimestampByMill()
|
|
s.SessionType = constant.SingleChatType
|
|
s.Status = constant.MsgStatusSendSuccess
|
|
localMessage := c.msgStructToLocalChatLog(s)
|
|
conversation.LatestMsg = utils.StructToJsonString(s)
|
|
conversation.ConversationType = constant.SingleChatType
|
|
conversation.LatestMsgSendTime = s.SendTime
|
|
err := c.insertMessageToLocalStorage(ctx, conversation.ConversationID, localMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversation.ConversationID, Action: constant.AddConOrUpLatMsg, Args: conversation}, c.GetCh())
|
|
return s, nil
|
|
|
|
}
|
|
|
|
func (c *Conversation) InsertGroupMessageToLocalStorage(ctx context.Context, s *sdk_struct.MsgStruct, groupID, sendID string) (*sdk_struct.MsgStruct, error) {
|
|
if groupID == "" || sendID == "" {
|
|
return nil, sdkerrs.ErrArgs
|
|
}
|
|
var conversation model_struct.LocalConversation
|
|
var err error
|
|
_, conversation.ConversationType, err = c.getConversationTypeByGroupID(ctx, groupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conversation.ConversationID = c.getConversationIDBySessionType(groupID, int(conversation.ConversationType))
|
|
if sendID != c.loginUserID {
|
|
faceUrl, name, err := c.getUserNameAndFaceURL(ctx, sendID)
|
|
if err != nil {
|
|
// log.Error("", "getUserNameAndFaceUrlByUid err", err.Error(), sendID)
|
|
}
|
|
s.SenderFaceURL = faceUrl
|
|
s.SenderNickname = name
|
|
}
|
|
s.SendID = sendID
|
|
s.RecvID = groupID
|
|
s.GroupID = groupID
|
|
s.ClientMsgID = utils.GetMsgID(s.SendID)
|
|
s.SendTime = utils.GetCurrentTimestampByMill()
|
|
s.SessionType = conversation.ConversationType
|
|
s.Status = constant.MsgStatusSendSuccess
|
|
localMessage := c.msgStructToLocalChatLog(s)
|
|
conversation.LatestMsg = utils.StructToJsonString(s)
|
|
conversation.LatestMsgSendTime = s.SendTime
|
|
conversation.FaceURL = s.SenderFaceURL
|
|
conversation.ShowName = s.SenderNickname
|
|
err = c.insertMessageToLocalStorage(ctx, conversation.ConversationID, localMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{ConID: conversation.ConversationID, Action: constant.AddConOrUpLatMsg, Args: conversation}, c.GetCh())
|
|
return s, nil
|
|
|
|
}
|
|
|
|
func (c *Conversation) SearchLocalMessages(ctx context.Context, searchParam *sdk_params_callback.SearchLocalMessagesParams) (*sdk_params_callback.SearchLocalMessagesCallback, error) {
|
|
searchParam.KeywordList = utils.TrimStringList(searchParam.KeywordList)
|
|
return c.searchLocalMessages(ctx, searchParam)
|
|
|
|
}
|
|
func (c *Conversation) SetMessageLocalEx(ctx context.Context, conversationID string, clientMsgID string, localEx string) error {
|
|
err := c.db.UpdateColumnsMessage(ctx, conversationID, clientMsgID, map[string]interface{}{"local_ex": localEx})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conversation, err := c.db.GetConversation(ctx, conversationID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var latestMsg sdk_struct.MsgStruct
|
|
utils.JsonStringToStruct(conversation.LatestMsg, &latestMsg)
|
|
if latestMsg.ClientMsgID == clientMsgID {
|
|
log.ZDebug(ctx, "latestMsg local ex changed", "seq", latestMsg.Seq, "clientMsgID", latestMsg.ClientMsgID)
|
|
latestMsg.LocalEx = localEx
|
|
latestMsgStr := utils.StructToJsonString(latestMsg)
|
|
if err = c.db.UpdateColumnsConversation(ctx, conversationID, map[string]interface{}{"latest_msg": latestMsgStr, "latest_msg_send_time": latestMsg.SendTime}); err != nil {
|
|
return err
|
|
}
|
|
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{conversationID}}})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Conversation) initBasicInfo(ctx context.Context, message *sdk_struct.MsgStruct, msgFrom, contentType int32) error {
|
|
message.CreateTime = utils.GetCurrentTimestampByMill()
|
|
message.SendTime = message.CreateTime
|
|
message.IsRead = false
|
|
message.Status = constant.MsgStatusSending
|
|
message.SendID = c.loginUserID
|
|
userInfo, err := c.db.GetLoginUser(ctx, c.loginUserID)
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
message.SenderFaceURL = userInfo.FaceURL
|
|
message.SenderNickname = userInfo.Nickname
|
|
}
|
|
ClientMsgID := utils.GetMsgID(message.SendID)
|
|
message.ClientMsgID = ClientMsgID
|
|
message.MsgFrom = msgFrom
|
|
message.ContentType = contentType
|
|
message.SenderPlatformID = c.platformID
|
|
message.IsExternalExtensions = c.IsExternalExtensions
|
|
return nil
|
|
}
|
|
|
|
//// 删除本地和服务器
|
|
//// 删除本地的话不用改服务器的数据
|
|
//// 删除服务器的话,需要把本地的消息状态改成删除
|
|
//funcation (c *Conversation) DeleteConversationFromLocalAndSvr(ctx context.Context, conversationID string) error {
|
|
// // Use conversationID to remove conversations and messages from the server first
|
|
// err := c.clearConversationFromSvr(ctx, conversationID)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// return c.deleteConversation(ctx, conversationID)
|
|
//}
|
|
|
|
func (c *Conversation) getConversationTypeByGroupID(ctx context.Context, groupID string) (conversationID string, conversationType int32, err error) {
|
|
g, err := c.full.GetGroupInfoByGroupID(ctx, groupID)
|
|
if err != nil {
|
|
return "", 0, utils.Wrap(err, "get group info error")
|
|
}
|
|
switch g.GroupType {
|
|
case constant.NormalGroup:
|
|
return c.getConversationIDBySessionType(groupID, constant.GroupChatType), constant.GroupChatType, nil
|
|
case constant.SuperGroup, constant.WorkingGroup:
|
|
return c.getConversationIDBySessionType(groupID, constant.SuperGroupChatType), constant.SuperGroupChatType, nil
|
|
default:
|
|
return "", 0, sdkerrs.ErrGroupType
|
|
}
|
|
}
|
|
|
|
func (c *Conversation) SetMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req []*server_api_params.KeyValue) ([]*server_api_params.ExtensionResult, error) {
|
|
return c.setMessageReactionExtensions(ctx, s, req)
|
|
|
|
}
|
|
|
|
func (c *Conversation) AddMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, reactionExtensionList []*server_api_params.KeyValue) ([]*server_api_params.ExtensionResult, error) {
|
|
return c.addMessageReactionExtensions(ctx, s, reactionExtensionList)
|
|
|
|
}
|
|
|
|
func (c *Conversation) DeleteMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, reactionExtensionKeyList []string) ([]*server_api_params.ExtensionResult, error) {
|
|
return c.deleteMessageReactionExtensions(ctx, s, reactionExtensionKeyList)
|
|
}
|
|
|
|
func (c *Conversation) GetMessageListReactionExtensions(ctx context.Context, conversationID string, messageList []*sdk_struct.MsgStruct) ([]*server_api_params.SingleMessageExtensionResult, error) {
|
|
return c.getMessageListReactionExtensions(ctx, conversationID, messageList)
|
|
|
|
}
|
|
func (c *Conversation) SearchConversation(ctx context.Context, searchParam string) ([]*server_api_params.Conversation, error) {
|
|
// Check if search parameter is empty
|
|
if searchParam == "" {
|
|
return nil, sdkerrs.ErrArgs.WrapMsg("search parameter cannot be empty")
|
|
}
|
|
|
|
// Perform the search in your database or data source
|
|
// This is a placeholder for the actual database call
|
|
conversations, err := c.db.SearchConversations(ctx, searchParam)
|
|
if err != nil {
|
|
// Handle any errors that occurred during the search
|
|
return nil, err
|
|
}
|
|
apiConversations := make([]*server_api_params.Conversation, len(conversations))
|
|
for i, localConv := range conversations {
|
|
// Create new server_api_params.Conversation and map fields from localConv
|
|
apiConv := &server_api_params.Conversation{
|
|
ConversationID: localConv.ConversationID,
|
|
ConversationType: localConv.ConversationType,
|
|
UserID: localConv.UserID,
|
|
GroupID: localConv.GroupID,
|
|
RecvMsgOpt: localConv.RecvMsgOpt,
|
|
UnreadCount: localConv.UnreadCount,
|
|
DraftTextTime: localConv.DraftTextTime,
|
|
IsPinned: localConv.IsPinned,
|
|
IsPrivateChat: localConv.IsPrivateChat,
|
|
BurnDuration: localConv.BurnDuration,
|
|
GroupAtType: localConv.GroupAtType,
|
|
IsNotInGroup: localConv.IsNotInGroup,
|
|
UpdateUnreadCountTime: localConv.UpdateUnreadCountTime,
|
|
AttachedInfo: localConv.AttachedInfo,
|
|
Ex: localConv.Ex,
|
|
}
|
|
apiConversations[i] = apiConv
|
|
}
|
|
// Return the list of conversations
|
|
return apiConversations, nil
|
|
}
|
|
|
|
/**
|
|
**Get some reaction extensions in reactionExtensionKeyList of message list
|
|
*/
|
|
//funcation (c *Conversation) GetMessageListSomeReactionExtensions(ctx context.Context, messageList, reactionExtensionKeyList, operationID string) {
|
|
// var messagelist []*sdk_struct.MsgStruct
|
|
// common.JsonUnmarshalAndArgsValidate(messageList, &messagelist, callback, operationID)
|
|
// var list []string
|
|
// common.JsonUnmarshalAndArgsValidate(reactionExtensionKeyList, &list, callback, operationID)
|
|
// result := c.getMessageListSomeReactionExtensions(callback, messagelist, list, operationID)
|
|
// callback.OnSuccess(utils.StructToJsonString(result))
|
|
// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
|
|
//}
|
|
//funcation (c *Conversation) SetTypeKeyInfo(ctx context.Context, message, typeKey, ex string, isCanRepeat bool, operationID string) {
|
|
// s := sdk_struct.MsgStruct{}
|
|
// common.JsonUnmarshalAndArgsValidate(message, &s, callback, operationID)
|
|
// result := c.setTypeKeyInfo(callback, &s, typeKey, ex, isCanRepeat, operationID)
|
|
// callback.OnSuccess(utils.StructToJsonString(result))
|
|
// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
|
|
//}
|
|
//funcation (c *Conversation) GetTypeKeyListInfo(ctx context.Context, message, typeKeyList, operationID string) {
|
|
// s := sdk_struct.MsgStruct{}
|
|
// common.JsonUnmarshalAndArgsValidate(message, &s, callback, operationID)
|
|
// var list []string
|
|
// common.JsonUnmarshalAndArgsValidate(typeKeyList, &list, callback, operationID)
|
|
// result := c.getTypeKeyListInfo(callback, &s, list, operationID)
|
|
// callback.OnSuccess(utils.StructToJsonString(result))
|
|
// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
|
|
//}
|
|
//funcation (c *Conversation) GetAllTypeKeyInfo(ctx context.Context, message, operationID string) {
|
|
// s := sdk_struct.MsgStruct{}
|
|
// common.JsonUnmarshalAndArgsValidate(message, &s, callback, operationID)
|
|
// result := c.getAllTypeKeyInfo(callback, &s, operationID)
|
|
// callback.OnSuccess(utils.StructToJsonString(result))
|
|
// log.NewInfo(operationID, utils.GetSelfFuncName(), "callback: ", utils.StructToJsonString(result))
|
|
//}
|
|
|