mirror of
				https://github.com/openimsdk/open-im-server.git
				synced 2025-10-26 21:22:16 +08:00 
			
		
		
		
	Merge branch 'openimsdk:main' into main
This commit is contained in:
		
						commit
						13cb82488c
					
				
							
								
								
									
										25
									
								
								.github/workflows/go-build-test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/go-build-test.yml
									
									
									
									
										vendored
									
									
								
							| @ -12,6 +12,10 @@ jobs: | |||||||
|   go-build: |   go-build: | ||||||
|     name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} |     name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} | ||||||
|     runs-on: ${{ matrix.os }} |     runs-on: ${{ matrix.os }} | ||||||
|  | 
 | ||||||
|  |     env: | ||||||
|  |       SHARE_CONFIG_PATH: config/share.yml | ||||||
|  | 
 | ||||||
|     permissions: |     permissions: | ||||||
|       contents: write |       contents: write | ||||||
|       pull-requests: write |       pull-requests: write | ||||||
| @ -40,6 +44,10 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           compose-file: "./docker-compose.yml" |           compose-file: "./docker-compose.yml" | ||||||
| 
 | 
 | ||||||
|  |       - name: Modify Server Configuration | ||||||
|  |         run: | | ||||||
|  |           yq e '.secret = 123456' -i ${{ env.SHARE_CONFIG_PATH }} | ||||||
|  | 
 | ||||||
|       # - name: Get Internal IP Address |       # - name: Get Internal IP Address | ||||||
|       #   id: get-ip |       #   id: get-ip | ||||||
|       #   run: | |       #   run: | | ||||||
| @ -71,6 +79,11 @@ jobs: | |||||||
|           go mod download |           go mod download | ||||||
|           go install github.com/magefile/mage@latest |           go install github.com/magefile/mage@latest | ||||||
| 
 | 
 | ||||||
|  |       - name: Modify Chat Configuration | ||||||
|  |         run: | | ||||||
|  |           cd ${{ github.workspace }}/chat-repo | ||||||
|  |           yq e '.openIM.secret = 123456' -i ${{ env.SHARE_CONFIG_PATH }} | ||||||
|  | 
 | ||||||
|       - name: Build and test Chat Services |       - name: Build and test Chat Services | ||||||
|         run: | |         run: | | ||||||
|           cd ${{ github.workspace }}/chat-repo |           cd ${{ github.workspace }}/chat-repo | ||||||
| @ -132,7 +145,7 @@ jobs: | |||||||
| 
 | 
 | ||||||
|           # Test get admin token |           # Test get admin token | ||||||
|           get_admin_token_response=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -d '{ |           get_admin_token_response=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -d '{ | ||||||
|             "secret": "openIM123", |             "secret": "123456", | ||||||
|             "platformID": 2, |             "platformID": 2, | ||||||
|             "userID": "imAdmin" |             "userID": "imAdmin" | ||||||
|           }' http://127.0.0.1:10002/auth/get_admin_token) |           }' http://127.0.0.1:10002/auth/get_admin_token) | ||||||
| @ -169,7 +182,8 @@ jobs: | |||||||
|       contents: write |       contents: write | ||||||
|     env: |     env: | ||||||
|       SDK_DIR: openim-sdk-core |       SDK_DIR: openim-sdk-core | ||||||
|       CONFIG_PATH: config/notification.yml |       NOTIFICATION_CONFIG_PATH: config/notification.yml | ||||||
|  |       SHARE_CONFIG_PATH: config/share.yml | ||||||
| 
 | 
 | ||||||
|     strategy: |     strategy: | ||||||
|       matrix: |       matrix: | ||||||
| @ -184,7 +198,7 @@ jobs: | |||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v4 | ||||||
|         with: |         with: | ||||||
|           repository: "openimsdk/openim-sdk-core" |           repository: "openimsdk/openim-sdk-core" | ||||||
|           ref: "release-v3.8" |           ref: "main" | ||||||
|           path: ${{ env.SDK_DIR }} |           path: ${{ env.SDK_DIR }} | ||||||
| 
 | 
 | ||||||
|       - name: Set up Go ${{ matrix.go_version }} |       - name: Set up Go ${{ matrix.go_version }} | ||||||
| @ -199,8 +213,9 @@ jobs: | |||||||
| 
 | 
 | ||||||
|       - name: Modify Server Configuration |       - name: Modify Server Configuration | ||||||
|         run: | |         run: | | ||||||
|           yq e '.groupCreated.isSendMsg = true' -i ${{ env.CONFIG_PATH }} |           yq e '.groupCreated.isSendMsg = true' -i ${{ env.NOTIFICATION_CONFIG_PATH }} | ||||||
|           yq e '.friendApplicationApproved.isSendMsg = true' -i ${{ env.CONFIG_PATH }} |           yq e '.friendApplicationApproved.isSendMsg = true' -i ${{ env.NOTIFICATION_CONFIG_PATH }} | ||||||
|  |           yq e '.secret = 123456' -i ${{ env.SHARE_CONFIG_PATH }} | ||||||
| 
 | 
 | ||||||
|       - name: Start Server Services |       - name: Start Server Services | ||||||
|         run: | |         run: | | ||||||
|  | |||||||
| @ -181,3 +181,19 @@ afterImportFriends: | |||||||
| afterRemoveBlack: | afterRemoveBlack: | ||||||
|   enable: false |   enable: false | ||||||
|   timeout: 5 |   timeout: 5 | ||||||
|  | beforeCreateSingleChatConversations: | ||||||
|  |   enable: false | ||||||
|  |   timeout: 5 | ||||||
|  |   failedContinue: false | ||||||
|  | afterCreateSingleChatConversations: | ||||||
|  |   enable: false | ||||||
|  |   timeout: 5 | ||||||
|  |   failedContinue: false | ||||||
|  | beforeCreateGroupChatConversations: | ||||||
|  |   enable: false | ||||||
|  |   timeout: 5 | ||||||
|  |   failedContinue: false | ||||||
|  | afterCreateGroupChatConversations: | ||||||
|  |   enable: false | ||||||
|  |   timeout: 5 | ||||||
|  |   failedContinue: false | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -12,7 +12,7 @@ require ( | |||||||
| 	github.com/gorilla/websocket v1.5.1 | 	github.com/gorilla/websocket v1.5.1 | ||||||
| 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 | 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 | ||||||
| 	github.com/mitchellh/mapstructure v1.5.0 | 	github.com/mitchellh/mapstructure v1.5.0 | ||||||
| 	github.com/openimsdk/protocol v0.0.72-alpha.79 | 	github.com/openimsdk/protocol v0.0.72-alpha.81 | ||||||
| 	github.com/openimsdk/tools v0.0.50-alpha.74 | 	github.com/openimsdk/tools v0.0.50-alpha.74 | ||||||
| 	github.com/pkg/errors v0.9.1 // indirect | 	github.com/pkg/errors v0.9.1 // indirect | ||||||
| 	github.com/prometheus/client_golang v1.18.0 | 	github.com/prometheus/client_golang v1.18.0 | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -347,8 +347,8 @@ github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= | |||||||
| github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= | github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= | ||||||
| github.com/openimsdk/gomake v0.0.15-alpha.2 h1:5Q8yl8ezy2yx+q8/ucU/t4kJnDfCzNOrkXcDACCqtyM= | github.com/openimsdk/gomake v0.0.15-alpha.2 h1:5Q8yl8ezy2yx+q8/ucU/t4kJnDfCzNOrkXcDACCqtyM= | ||||||
| github.com/openimsdk/gomake v0.0.15-alpha.2/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= | github.com/openimsdk/gomake v0.0.15-alpha.2/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= | ||||||
| github.com/openimsdk/protocol v0.0.72-alpha.79 h1:e46no8WVAsmTzyy405klrdoUiG7u+1ohDsXvQuFng4s= | github.com/openimsdk/protocol v0.0.72-alpha.81 h1:6tDuZ3Anfi1uhX/V5mWxITqJnGQPnvgeaxeqJlEHIVE= | ||||||
| github.com/openimsdk/protocol v0.0.72-alpha.79/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw= | github.com/openimsdk/protocol v0.0.72-alpha.81/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw= | ||||||
| github.com/openimsdk/tools v0.0.50-alpha.74 h1:yh10SiMiivMEjicEQg+QAsH4pvaO+4noMPdlw+ew0Kc= | github.com/openimsdk/tools v0.0.50-alpha.74 h1:yh10SiMiivMEjicEQg+QAsH4pvaO+4noMPdlw+ew0Kc= | ||||||
| github.com/openimsdk/tools v0.0.50-alpha.74/go.mod h1:n2poR3asX1e1XZce4O+MOWAp+X02QJRFvhcLCXZdzRo= | github.com/openimsdk/tools v0.0.50-alpha.74/go.mod h1:n2poR3asX1e1XZce4O+MOWAp+X02QJRFvhcLCXZdzRo= | ||||||
| github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ package api | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/protocol/conversation" | 	"github.com/openimsdk/protocol/conversation" | ||||||
| 	"github.com/openimsdk/tools/a2r" | 	"github.com/openimsdk/tools/a2r" | ||||||
| ) | ) | ||||||
| @ -71,3 +72,7 @@ func (o *ConversationApi) GetNotNotifyConversationIDs(c *gin.Context) { | |||||||
| func (o *ConversationApi) GetPinnedConversationIDs(c *gin.Context) { | func (o *ConversationApi) GetPinnedConversationIDs(c *gin.Context) { | ||||||
| 	a2r.Call(c, conversation.ConversationClient.GetPinnedConversationIDs, o.Client) | 	a2r.Call(c, conversation.ConversationClient.GetPinnedConversationIDs, o.Client) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (o *ConversationApi) UpdateConversationsByUser(c *gin.Context) { | ||||||
|  | 	a2r.Call(c, conversation.ConversationClient.UpdateConversationsByUser, o.Client) | ||||||
|  | } | ||||||
|  | |||||||
| @ -2,10 +2,14 @@ package jssdk | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||||
|  | 	"github.com/openimsdk/protocol/constant" | ||||||
|  | 	"github.com/openimsdk/tools/log" | ||||||
|  | 
 | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/protocol/conversation" | 	"github.com/openimsdk/protocol/conversation" | ||||||
| 	"github.com/openimsdk/protocol/jssdk" | 	"github.com/openimsdk/protocol/jssdk" | ||||||
| 	"github.com/openimsdk/protocol/msg" | 	"github.com/openimsdk/protocol/msg" | ||||||
| @ -109,10 +113,7 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive | |||||||
| 	if len(conversationIDs) == 0 { | 	if len(conversationIDs) == 0 { | ||||||
| 		return &jssdk.GetActiveConversationsResp{}, nil | 		return &jssdk.GetActiveConversationsResp{}, nil | ||||||
| 	} | 	} | ||||||
| 	readSeq, err := x.msgClient.GetHasReadSeqs(ctx, conversationIDs, req.OwnerUserID) | 
 | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	activeConversation, err := x.msgClient.GetActiveConversation(ctx, conversationIDs) | 	activeConversation, err := x.msgClient.GetActiveConversation(ctx, conversationIDs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -120,6 +121,10 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive | |||||||
| 	if len(activeConversation) == 0 { | 	if len(activeConversation) == 0 { | ||||||
| 		return &jssdk.GetActiveConversationsResp{}, nil | 		return &jssdk.GetActiveConversationsResp{}, nil | ||||||
| 	} | 	} | ||||||
|  | 	readSeq, err := x.msgClient.GetHasReadSeqs(ctx, conversationIDs, req.OwnerUserID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
| 	sortConversations := sortActiveConversations{ | 	sortConversations := sortActiveConversations{ | ||||||
| 		Conversation: activeConversation, | 		Conversation: activeConversation, | ||||||
| 	} | 	} | ||||||
| @ -147,6 +152,7 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | 	x.checkMessagesAndGetLastMessage(ctx, req.OwnerUserID, msgs) | ||||||
| 	conversationMap := datautil.SliceToMap(conversations, func(c *conversation.Conversation) string { | 	conversationMap := datautil.SliceToMap(conversations, func(c *conversation.Conversation) string { | ||||||
| 		return c.ConversationID | 		return c.ConversationID | ||||||
| 	}) | 	}) | ||||||
| @ -156,17 +162,16 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive | |||||||
| 		if !ok { | 		if !ok { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		var lastMsg *sdkws.MsgData |  | ||||||
| 		if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { | 		if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { | ||||||
| 			lastMsg = msgList.Msgs[0] |  | ||||||
| 		} |  | ||||||
| 			resp = append(resp, &jssdk.ConversationMsg{ | 			resp = append(resp, &jssdk.ConversationMsg{ | ||||||
| 				Conversation: conv, | 				Conversation: conv, | ||||||
| 			LastMsg:      lastMsg, | 				LastMsg:      msgList.Msgs[0], | ||||||
| 				MaxSeq:       c.MaxSeq, | 				MaxSeq:       c.MaxSeq, | ||||||
| 				ReadSeq:      readSeq[c.ConversationID], | 				ReadSeq:      readSeq[c.ConversationID], | ||||||
| 			}) | 			}) | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
| 	if err := x.fillConversations(ctx, resp); err != nil { | 	if err := x.fillConversations(ctx, resp); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -219,19 +224,19 @@ func (x *JSSdk) getConversations(ctx context.Context, req *jssdk.GetConversation | |||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	x.checkMessagesAndGetLastMessage(ctx, req.OwnerUserID, msgs) | ||||||
| 	resp := make([]*jssdk.ConversationMsg, 0, len(conversations)) | 	resp := make([]*jssdk.ConversationMsg, 0, len(conversations)) | ||||||
| 	for _, c := range conversations { | 	for _, c := range conversations { | ||||||
| 		var lastMsg *sdkws.MsgData |  | ||||||
| 		if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { | 		if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { | ||||||
| 			lastMsg = msgList.Msgs[0] |  | ||||||
| 		} |  | ||||||
| 			resp = append(resp, &jssdk.ConversationMsg{ | 			resp = append(resp, &jssdk.ConversationMsg{ | ||||||
| 				Conversation: c, | 				Conversation: c, | ||||||
| 			LastMsg:      lastMsg, | 				LastMsg:      msgList.Msgs[0], | ||||||
| 				MaxSeq:       maxSeqs[c.ConversationID], | 				MaxSeq:       maxSeqs[c.ConversationID], | ||||||
| 				ReadSeq:      readSeqs[c.ConversationID], | 				ReadSeq:      readSeqs[c.ConversationID], | ||||||
| 			}) | 			}) | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
| 	if err := x.fillConversations(ctx, resp); err != nil { | 	if err := x.fillConversations(ctx, resp); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -247,3 +252,36 @@ func (x *JSSdk) getConversations(ctx context.Context, req *jssdk.GetConversation | |||||||
| 		UnreadCount:   unreadCount, | 		UnreadCount:   unreadCount, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // This function checks whether the latest MaxSeq message is valid. | ||||||
|  | // If not, it needs to fetch a valid message again. | ||||||
|  | func (x *JSSdk) checkMessagesAndGetLastMessage(ctx context.Context, userID string, messages map[string]*sdkws.PullMsgs) { | ||||||
|  | 	var conversationIDs []string | ||||||
|  | 
 | ||||||
|  | 	for conversationID, message := range messages { | ||||||
|  | 		allInValid := true | ||||||
|  | 		for _, data := range message.Msgs { | ||||||
|  | 			if data.Status < constant.MsgStatusHasDeleted { | ||||||
|  | 				allInValid = false | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if allInValid { | ||||||
|  | 			conversationIDs = append(conversationIDs, conversationID) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if len(conversationIDs) > 0 { | ||||||
|  | 		resp, err := x.msgClient.GetLastMessage(ctx, &msg.GetLastMessageReq{ | ||||||
|  | 			UserID:          userID, | ||||||
|  | 			ConversationIDs: conversationIDs, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.ZError(ctx, "fetchLatestValidMessages", err, "conversationIDs", conversationIDs) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		for conversationID, message := range resp.Msgs { | ||||||
|  | 			messages[conversationID] = &sdkws.PullMsgs{Msgs: []*sdkws.MsgData{message}} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | |||||||
| @ -551,11 +551,3 @@ func (m *MessageApi) SearchMsg(c *gin.Context) { | |||||||
| func (m *MessageApi) GetServerTime(c *gin.Context) { | func (m *MessageApi) GetServerTime(c *gin.Context) { | ||||||
| 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) | 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func (m *MessageApi) GetStreamMsg(c *gin.Context) { |  | ||||||
| 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *MessageApi) AppendStreamMsg(c *gin.Context) { |  | ||||||
| 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import ( | |||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
| 	"github.com/gin-gonic/gin/binding" | 	"github.com/gin-gonic/gin/binding" | ||||||
| 	"github.com/go-playground/validator/v10" | 	"github.com/go-playground/validator/v10" | ||||||
|  | 	clientv3 "go.etcd.io/etcd/client/v3" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/open-im-server/v3/internal/api/jssdk" | 	"github.com/openimsdk/open-im-server/v3/internal/api/jssdk" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||||
| @ -27,7 +29,6 @@ import ( | |||||||
| 	"github.com/openimsdk/tools/discovery/etcd" | 	"github.com/openimsdk/tools/discovery/etcd" | ||||||
| 	"github.com/openimsdk/tools/log" | 	"github.com/openimsdk/tools/log" | ||||||
| 	"github.com/openimsdk/tools/mw" | 	"github.com/openimsdk/tools/mw" | ||||||
| 	clientv3 "go.etcd.io/etcd/client/v3" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @ -246,8 +247,6 @@ func newGinRouter(ctx context.Context, client discovery.Conn, cfg *Config) (*gin | |||||||
| 		msgGroup.POST("/batch_send_msg", m.BatchSendMsg) | 		msgGroup.POST("/batch_send_msg", m.BatchSendMsg) | ||||||
| 		msgGroup.POST("/check_msg_is_send_success", m.CheckMsgIsSendSuccess) | 		msgGroup.POST("/check_msg_is_send_success", m.CheckMsgIsSendSuccess) | ||||||
| 		msgGroup.POST("/get_server_time", m.GetServerTime) | 		msgGroup.POST("/get_server_time", m.GetServerTime) | ||||||
| 		msgGroup.POST("/get_stream_msg", m.GetStreamMsg) |  | ||||||
| 		msgGroup.POST("/append_stream_msg", m.AppendStreamMsg) |  | ||||||
| 	} | 	} | ||||||
| 	// Conversation | 	// Conversation | ||||||
| 	{ | 	{ | ||||||
| @ -264,6 +263,7 @@ func newGinRouter(ctx context.Context, client discovery.Conn, cfg *Config) (*gin | |||||||
| 		conversationGroup.POST("/get_owner_conversation", c.GetOwnerConversation) | 		conversationGroup.POST("/get_owner_conversation", c.GetOwnerConversation) | ||||||
| 		conversationGroup.POST("/get_not_notify_conversation_ids", c.GetNotNotifyConversationIDs) | 		conversationGroup.POST("/get_not_notify_conversation_ids", c.GetNotNotifyConversationIDs) | ||||||
| 		conversationGroup.POST("/get_pinned_conversation_ids", c.GetPinnedConversationIDs) | 		conversationGroup.POST("/get_pinned_conversation_ids", c.GetPinnedConversationIDs) | ||||||
|  | 		conversationGroup.POST("/update_conversations_by_user", c.UpdateConversationsByUser) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	{ | 	{ | ||||||
|  | |||||||
							
								
								
									
										117
									
								
								internal/rpc/conversation/callback.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								internal/rpc/conversation/callback.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | |||||||
|  | package conversation | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 
 | ||||||
|  | 	"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" | ||||||
|  | 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||||
|  | 	dbModel "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||||
|  | 	"github.com/openimsdk/open-im-server/v3/pkg/common/webhook" | ||||||
|  | 	"github.com/openimsdk/tools/utils/datautil" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (c *conversationServer) webhookBeforeCreateSingleChatConversations(ctx context.Context, before *config.BeforeConfig, req *dbModel.Conversation) error { | ||||||
|  | 	return webhook.WithCondition(ctx, before, func(ctx context.Context) error { | ||||||
|  | 		cbReq := &callbackstruct.CallbackBeforeCreateSingleChatConversationsReq{ | ||||||
|  | 			CallbackCommand:  callbackstruct.CallbackBeforeCreateSingleChatConversationsCommand, | ||||||
|  | 			OwnerUserID:      req.OwnerUserID, | ||||||
|  | 			ConversationID:   req.ConversationID, | ||||||
|  | 			ConversationType: req.ConversationType, | ||||||
|  | 			UserID:           req.UserID, | ||||||
|  | 			RecvMsgOpt:       req.RecvMsgOpt, | ||||||
|  | 			IsPinned:         req.IsPinned, | ||||||
|  | 			IsPrivateChat:    req.IsPrivateChat, | ||||||
|  | 			BurnDuration:     req.BurnDuration, | ||||||
|  | 			GroupAtType:      req.GroupAtType, | ||||||
|  | 			AttachedInfo:     req.AttachedInfo, | ||||||
|  | 			Ex:               req.Ex, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		resp := &callbackstruct.CallbackBeforeCreateSingleChatConversationsResp{} | ||||||
|  | 
 | ||||||
|  | 		if err := c.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		datautil.NotNilReplace(&req.RecvMsgOpt, resp.RecvMsgOpt) | ||||||
|  | 		datautil.NotNilReplace(&req.IsPinned, resp.IsPinned) | ||||||
|  | 		datautil.NotNilReplace(&req.IsPrivateChat, resp.IsPrivateChat) | ||||||
|  | 		datautil.NotNilReplace(&req.BurnDuration, resp.BurnDuration) | ||||||
|  | 		datautil.NotNilReplace(&req.GroupAtType, resp.GroupAtType) | ||||||
|  | 		datautil.NotNilReplace(&req.AttachedInfo, resp.AttachedInfo) | ||||||
|  | 		datautil.NotNilReplace(&req.Ex, resp.Ex) | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *conversationServer) webhookAfterCreateSingleChatConversations(ctx context.Context, after *config.AfterConfig, req *dbModel.Conversation) error { | ||||||
|  | 	cbReq := &callbackstruct.CallbackAfterCreateSingleChatConversationsReq{ | ||||||
|  | 		CallbackCommand:  callbackstruct.CallbackAfterCreateSingleChatConversationsCommand, | ||||||
|  | 		OwnerUserID:      req.OwnerUserID, | ||||||
|  | 		ConversationID:   req.ConversationID, | ||||||
|  | 		ConversationType: req.ConversationType, | ||||||
|  | 		UserID:           req.UserID, | ||||||
|  | 		RecvMsgOpt:       req.RecvMsgOpt, | ||||||
|  | 		IsPinned:         req.IsPinned, | ||||||
|  | 		IsPrivateChat:    req.IsPrivateChat, | ||||||
|  | 		BurnDuration:     req.BurnDuration, | ||||||
|  | 		GroupAtType:      req.GroupAtType, | ||||||
|  | 		AttachedInfo:     req.AttachedInfo, | ||||||
|  | 		Ex:               req.Ex, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateSingleChatConversationsResp{}, after) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *conversationServer) webhookBeforeCreateGroupChatConversations(ctx context.Context, before *config.BeforeConfig, req *dbModel.Conversation) error { | ||||||
|  | 	return webhook.WithCondition(ctx, before, func(ctx context.Context) error { | ||||||
|  | 		cbReq := &callbackstruct.CallbackBeforeCreateGroupChatConversationsReq{ | ||||||
|  | 			CallbackCommand:  callbackstruct.CallbackBeforeCreateGroupChatConversationsCommand, | ||||||
|  | 			ConversationID:   req.ConversationID, | ||||||
|  | 			ConversationType: req.ConversationType, | ||||||
|  | 			GroupID:          req.GroupID, | ||||||
|  | 			RecvMsgOpt:       req.RecvMsgOpt, | ||||||
|  | 			IsPinned:         req.IsPinned, | ||||||
|  | 			IsPrivateChat:    req.IsPrivateChat, | ||||||
|  | 			BurnDuration:     req.BurnDuration, | ||||||
|  | 			GroupAtType:      req.GroupAtType, | ||||||
|  | 			AttachedInfo:     req.AttachedInfo, | ||||||
|  | 			Ex:               req.Ex, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		resp := &callbackstruct.CallbackBeforeCreateGroupChatConversationsResp{} | ||||||
|  | 
 | ||||||
|  | 		if err := c.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		datautil.NotNilReplace(&req.RecvMsgOpt, resp.RecvMsgOpt) | ||||||
|  | 		datautil.NotNilReplace(&req.IsPinned, resp.IsPinned) | ||||||
|  | 		datautil.NotNilReplace(&req.IsPrivateChat, resp.IsPrivateChat) | ||||||
|  | 		datautil.NotNilReplace(&req.BurnDuration, resp.BurnDuration) | ||||||
|  | 		datautil.NotNilReplace(&req.GroupAtType, resp.GroupAtType) | ||||||
|  | 		datautil.NotNilReplace(&req.AttachedInfo, resp.AttachedInfo) | ||||||
|  | 		datautil.NotNilReplace(&req.Ex, resp.Ex) | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *conversationServer) webhookAfterCreateGroupChatConversations(ctx context.Context, after *config.AfterConfig, req *dbModel.Conversation) error { | ||||||
|  | 	cbReq := &callbackstruct.CallbackAfterCreateGroupChatConversationsReq{ | ||||||
|  | 		CallbackCommand:  callbackstruct.CallbackAfterCreateGroupChatConversationsCommand, | ||||||
|  | 		ConversationID:   req.ConversationID, | ||||||
|  | 		ConversationType: req.ConversationType, | ||||||
|  | 		GroupID:          req.GroupID, | ||||||
|  | 		RecvMsgOpt:       req.RecvMsgOpt, | ||||||
|  | 		IsPinned:         req.IsPinned, | ||||||
|  | 		IsPrivateChat:    req.IsPrivateChat, | ||||||
|  | 		BurnDuration:     req.BurnDuration, | ||||||
|  | 		GroupAtType:      req.GroupAtType, | ||||||
|  | 		AttachedInfo:     req.AttachedInfo, | ||||||
|  | 		Ex:               req.Ex, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateGroupChatConversationsResp{}, after) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @ -22,6 +22,8 @@ import ( | |||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/dbbuild" | 	"github.com/openimsdk/open-im-server/v3/pkg/dbbuild" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||||
| 
 | 
 | ||||||
|  | 	"google.golang.org/grpc" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/convert" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/convert" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" | ||||||
| @ -30,6 +32,7 @@ import ( | |||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||||
| 	dbModel "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | 	dbModel "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||||
|  | 	"github.com/openimsdk/open-im-server/v3/pkg/common/webhook" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/localcache" | 	"github.com/openimsdk/open-im-server/v3/pkg/localcache" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" | 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" | ||||||
| 	"github.com/openimsdk/protocol/constant" | 	"github.com/openimsdk/protocol/constant" | ||||||
| @ -39,7 +42,6 @@ import ( | |||||||
| 	"github.com/openimsdk/tools/errs" | 	"github.com/openimsdk/tools/errs" | ||||||
| 	"github.com/openimsdk/tools/log" | 	"github.com/openimsdk/tools/log" | ||||||
| 	"github.com/openimsdk/tools/utils/datautil" | 	"github.com/openimsdk/tools/utils/datautil" | ||||||
| 	"google.golang.org/grpc" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type conversationServer struct { | type conversationServer struct { | ||||||
| @ -49,6 +51,7 @@ type conversationServer struct { | |||||||
| 	conversationNotificationSender *ConversationNotificationSender | 	conversationNotificationSender *ConversationNotificationSender | ||||||
| 	config                         *Config | 	config                         *Config | ||||||
| 
 | 
 | ||||||
|  | 	webhookClient *webhook.Client | ||||||
| 	userClient    *rpcli.UserClient | 	userClient    *rpcli.UserClient | ||||||
| 	msgClient     *rpcli.MsgClient | 	msgClient     *rpcli.MsgClient | ||||||
| 	groupClient   *rpcli.GroupClient | 	groupClient   *rpcli.GroupClient | ||||||
| @ -60,6 +63,7 @@ type Config struct { | |||||||
| 	MongodbConfig      config.Mongo | 	MongodbConfig      config.Mongo | ||||||
| 	NotificationConfig config.Notification | 	NotificationConfig config.Notification | ||||||
| 	Share              config.Share | 	Share              config.Share | ||||||
|  | 	WebhooksConfig     config.Webhooks | ||||||
| 	LocalCacheConfig   config.LocalCache | 	LocalCacheConfig   config.LocalCache | ||||||
| 	Discovery          config.Discovery | 	Discovery          config.Discovery | ||||||
| } | } | ||||||
| @ -90,16 +94,25 @@ func Start(ctx context.Context, config *Config, client discovery.Conn, server gr | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	msgClient := rpcli.NewMsgClient(msgConn) | 	msgClient := rpcli.NewMsgClient(msgConn) | ||||||
| 	localcache.InitLocalCache(&config.LocalCacheConfig) | 
 | ||||||
| 	pbconversation.RegisterConversationServer(server, &conversationServer{ | 	cs := conversationServer{ | ||||||
| 		conversationNotificationSender: NewConversationNotificationSender(&config.NotificationConfig, msgClient), | 		config:        config, | ||||||
| 		conversationDatabase: controller.NewConversationDatabase(conversationDB, | 		webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL), | ||||||
| 			redis.NewConversationRedis(rdb, &config.LocalCacheConfig, conversationDB), mgocli.GetTx()), |  | ||||||
| 		userClient:    rpcli.NewUserClient(userConn), | 		userClient:    rpcli.NewUserClient(userConn), | ||||||
| 		groupClient:   rpcli.NewGroupClient(groupConn), | 		groupClient:   rpcli.NewGroupClient(groupConn), | ||||||
| 		msgClient:     msgClient, | 		msgClient:     msgClient, | ||||||
| 	}) | 	} | ||||||
|  | 
 | ||||||
|  | 	cs.conversationNotificationSender = NewConversationNotificationSender(&config.NotificationConfig, msgClient) | ||||||
|  | 	cs.conversationDatabase = controller.NewConversationDatabase( | ||||||
|  | 		conversationDB, | ||||||
|  | 		redis.NewConversationRedis(rdb, &config.LocalCacheConfig, conversationDB), | ||||||
|  | 		mgocli.GetTx()) | ||||||
|  | 
 | ||||||
|  | 	localcache.InitLocalCache(&config.LocalCacheConfig) | ||||||
|  | 	pbconversation.RegisterConversationServer(server, &cs) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -317,6 +330,19 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbconver | |||||||
| 	return &pbconversation.SetConversationsResp{}, nil | 	return &pbconversation.SetConversationsResp{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (c *conversationServer) UpdateConversationsByUser(ctx context.Context, req *pbconversation.UpdateConversationsByUserReq) (*pbconversation.UpdateConversationsByUserResp, error) { | ||||||
|  | 	m := make(map[string]any) | ||||||
|  | 	if req.Ex != nil { | ||||||
|  | 		m["ex"] = req.Ex.Value | ||||||
|  | 	} | ||||||
|  | 	if len(m) > 0 { | ||||||
|  | 		if err := c.conversationDatabase.UpdateUserConversations(ctx, req.UserID, m); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return &pbconversation.UpdateConversationsByUserResp{}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Get user IDs with "Do Not Disturb" enabled in super large groups. | // Get user IDs with "Do Not Disturb" enabled in super large groups. | ||||||
| func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbconversation.GetRecvMsgNotNotifyUserIDsReq) (*pbconversation.GetRecvMsgNotNotifyUserIDsResp, error) { | func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbconversation.GetRecvMsgNotNotifyUserIDsReq) (*pbconversation.GetRecvMsgNotNotifyUserIDsResp, error) { | ||||||
| 	return nil, errs.New("deprecated") | 	return nil, errs.New("deprecated") | ||||||
| @ -326,49 +352,76 @@ func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req | |||||||
| func (c *conversationServer) CreateSingleChatConversations(ctx context.Context, | func (c *conversationServer) CreateSingleChatConversations(ctx context.Context, | ||||||
| 	req *pbconversation.CreateSingleChatConversationsReq, | 	req *pbconversation.CreateSingleChatConversationsReq, | ||||||
| ) (*pbconversation.CreateSingleChatConversationsResp, error) { | ) (*pbconversation.CreateSingleChatConversationsResp, error) { | ||||||
|  | 	var conversation dbModel.Conversation | ||||||
| 	switch req.ConversationType { | 	switch req.ConversationType { | ||||||
| 	case constant.SingleChatType: | 	case constant.SingleChatType: | ||||||
| 		var conversation dbModel.Conversation | 		// sendUser create | ||||||
| 		conversation.ConversationID = req.ConversationID | 		conversation.ConversationID = req.ConversationID | ||||||
| 		conversation.ConversationType = req.ConversationType | 		conversation.ConversationType = req.ConversationType | ||||||
| 		conversation.OwnerUserID = req.SendID | 		conversation.OwnerUserID = req.SendID | ||||||
| 		conversation.UserID = req.RecvID | 		conversation.UserID = req.RecvID | ||||||
|  | 		if err := c.webhookBeforeCreateSingleChatConversations(ctx, &c.config.WebhooksConfig.BeforeCreateSingleChatConversations, &conversation); err != nil && err != servererrs.ErrCallbackContinue { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		err := c.conversationDatabase.CreateConversation(ctx, []*dbModel.Conversation{&conversation}) | 		err := c.conversationDatabase.CreateConversation(ctx, []*dbModel.Conversation{&conversation}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.ZWarn(ctx, "create conversation failed", err, "conversation", conversation) | 			log.ZWarn(ctx, "create conversation failed", err, "conversation", conversation) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		c.webhookAfterCreateSingleChatConversations(ctx, &c.config.WebhooksConfig.AfterCreateSingleChatConversations, &conversation) | ||||||
|  | 
 | ||||||
|  | 		// recvUser create | ||||||
| 		conversation2 := conversation | 		conversation2 := conversation | ||||||
| 		conversation2.OwnerUserID = req.RecvID | 		conversation2.OwnerUserID = req.RecvID | ||||||
| 		conversation2.UserID = req.SendID | 		conversation2.UserID = req.SendID | ||||||
|  | 		if err := c.webhookBeforeCreateSingleChatConversations(ctx, &c.config.WebhooksConfig.BeforeCreateSingleChatConversations, &conversation); err != nil && err != servererrs.ErrCallbackContinue { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		err = c.conversationDatabase.CreateConversation(ctx, []*dbModel.Conversation{&conversation2}) | 		err = c.conversationDatabase.CreateConversation(ctx, []*dbModel.Conversation{&conversation2}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.ZWarn(ctx, "create conversation failed", err, "conversation2", conversation) | 			log.ZWarn(ctx, "create conversation failed", err, "conversation2", conversation) | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		c.webhookAfterCreateSingleChatConversations(ctx, &c.config.WebhooksConfig.AfterCreateSingleChatConversations, &conversation2) | ||||||
| 	case constant.NotificationChatType: | 	case constant.NotificationChatType: | ||||||
| 		var conversation dbModel.Conversation |  | ||||||
| 		conversation.ConversationID = req.ConversationID | 		conversation.ConversationID = req.ConversationID | ||||||
| 		conversation.ConversationType = req.ConversationType | 		conversation.ConversationType = req.ConversationType | ||||||
| 		conversation.OwnerUserID = req.RecvID | 		conversation.OwnerUserID = req.RecvID | ||||||
| 		conversation.UserID = req.SendID | 		conversation.UserID = req.SendID | ||||||
|  | 		if err := c.webhookBeforeCreateSingleChatConversations(ctx, &c.config.WebhooksConfig.BeforeCreateSingleChatConversations, &conversation); err != nil && err != servererrs.ErrCallbackContinue { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		err := c.conversationDatabase.CreateConversation(ctx, []*dbModel.Conversation{&conversation}) | 		err := c.conversationDatabase.CreateConversation(ctx, []*dbModel.Conversation{&conversation}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.ZWarn(ctx, "create conversation failed", err, "conversation2", conversation) | 			log.ZWarn(ctx, "create conversation failed", err, "conversation2", conversation) | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		c.webhookAfterCreateSingleChatConversations(ctx, &c.config.WebhooksConfig.AfterCreateSingleChatConversations, &conversation) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &pbconversation.CreateSingleChatConversationsResp{}, nil | 	return &pbconversation.CreateSingleChatConversationsResp{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, req *pbconversation.CreateGroupChatConversationsReq) (*pbconversation.CreateGroupChatConversationsResp, error) { | func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, req *pbconversation.CreateGroupChatConversationsReq) (*pbconversation.CreateGroupChatConversationsResp, error) { | ||||||
| 	err := c.conversationDatabase.CreateGroupChatConversation(ctx, req.GroupID, req.UserIDs) | 	var conversation dbModel.Conversation | ||||||
|  | 
 | ||||||
|  | 	conversation.ConversationID = msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupID) | ||||||
|  | 	conversation.GroupID = req.GroupID | ||||||
|  | 	conversation.ConversationType = constant.ReadGroupChatType | ||||||
|  | 
 | ||||||
|  | 	if err := c.webhookBeforeCreateGroupChatConversations(ctx, &c.config.WebhooksConfig.BeforeCreateGroupChatConversations, &conversation); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := c.conversationDatabase.CreateGroupChatConversation(ctx, req.GroupID, req.UserIDs, &conversation) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupID) | 
 | ||||||
| 	if err := c.msgClient.SetUserConversationMaxSeq(ctx, conversationID, req.UserIDs, 0); err != nil { | 	c.webhookAfterCreateGroupChatConversations(ctx, &c.config.WebhooksConfig.AfterCreateGroupChatConversations, &conversation) | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return &pbconversation.CreateGroupChatConversationsResp{}, nil | 	return &pbconversation.CreateGroupChatConversationsResp{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -48,7 +48,3 @@ func (m *MsgNotificationSender) MarkAsReadNotification(ctx context.Context, conv | |||||||
| 	} | 	} | ||||||
| 	m.NotificationWithSessionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips) | 	m.NotificationWithSessionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func (m *MsgNotificationSender) StreamMsgNotification(ctx context.Context, sendID string, recvID string, sessionType int32, tips *sdkws.StreamMsgTips) { |  | ||||||
| 	m.NotificationWithSessionType(ctx, sendID, recvID, constant.StreamMsgNotification, sessionType, tips) |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -17,6 +17,8 @@ package msg | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 
 | 
 | ||||||
|  | 	"google.golang.org/protobuf/proto" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" | 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil" | 	"github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil" | ||||||
| @ -29,7 +31,6 @@ import ( | |||||||
| 	"github.com/openimsdk/tools/log" | 	"github.com/openimsdk/tools/log" | ||||||
| 	"github.com/openimsdk/tools/mcontext" | 	"github.com/openimsdk/tools/mcontext" | ||||||
| 	"github.com/openimsdk/tools/utils/datautil" | 	"github.com/openimsdk/tools/utils/datautil" | ||||||
| 	"google.golang.org/protobuf/proto" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (*pbmsg.SendMsgResp, error) { | func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (*pbmsg.SendMsgResp, error) { | ||||||
| @ -49,11 +50,6 @@ func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (*pbmsg. | |||||||
| 
 | 
 | ||||||
| func (m *msgServer) sendMsg(ctx context.Context, req *pbmsg.SendMsgReq, before **sdkws.MsgData) (*pbmsg.SendMsgResp, error) { | func (m *msgServer) sendMsg(ctx context.Context, req *pbmsg.SendMsgReq, before **sdkws.MsgData) (*pbmsg.SendMsgResp, error) { | ||||||
| 	m.encapsulateMsgData(req.MsgData) | 	m.encapsulateMsgData(req.MsgData) | ||||||
| 	if req.MsgData.ContentType == constant.Stream { |  | ||||||
| 		if err := m.handlerStreamMsg(ctx, req.MsgData); err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	switch req.MsgData.SessionType { | 	switch req.MsgData.SessionType { | ||||||
| 	case constant.SingleChatType: | 	case constant.SingleChatType: | ||||||
| 		return m.sendMsgSingleChat(ctx, req, before) | 		return m.sendMsgSingleChat(ctx, req, before) | ||||||
|  | |||||||
| @ -61,7 +61,6 @@ type msgServer struct { | |||||||
| 	msg.UnimplementedMsgServer | 	msg.UnimplementedMsgServer | ||||||
| 	RegisterCenter         discovery.Conn                   // Service discovery registry for service registration. | 	RegisterCenter         discovery.Conn                   // Service discovery registry for service registration. | ||||||
| 	MsgDatabase            controller.CommonMsgDatabase     // Interface for message database operations. | 	MsgDatabase            controller.CommonMsgDatabase     // Interface for message database operations. | ||||||
| 	StreamMsgDatabase      controller.StreamMsgDatabase |  | ||||||
| 	UserLocalCache         *rpccache.UserLocalCache         // Local cache for user data. | 	UserLocalCache         *rpccache.UserLocalCache         // Local cache for user data. | ||||||
| 	FriendLocalCache       *rpccache.FriendLocalCache       // Local cache for friend data. | 	FriendLocalCache       *rpccache.FriendLocalCache       // Local cache for friend data. | ||||||
| 	GroupLocalCache        *rpccache.GroupLocalCache        // Local cache for group data. | 	GroupLocalCache        *rpccache.GroupLocalCache        // Local cache for group data. | ||||||
| @ -117,10 +116,6 @@ func Start(ctx context.Context, config *Config, client discovery.Conn, server gr | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	streamMsg, err := mgo.NewStreamMsgMongo(mgocli.GetDB()) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	seqUserCache := redis.NewSeqUserCacheRedis(rdb, seqUser) | 	seqUserCache := redis.NewSeqUserCacheRedis(rdb, seqUser) | ||||||
| 	userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) | 	userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -142,7 +137,6 @@ func Start(ctx context.Context, config *Config, client discovery.Conn, server gr | |||||||
| 	msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel, seqUserCache, seqConversationCache, redisProducer) | 	msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel, seqUserCache, seqConversationCache, redisProducer) | ||||||
| 	s := &msgServer{ | 	s := &msgServer{ | ||||||
| 		MsgDatabase:            msgDatabase, | 		MsgDatabase:            msgDatabase, | ||||||
| 		StreamMsgDatabase:      controller.NewStreamMsgDatabase(streamMsg), |  | ||||||
| 		RegisterCenter:         client, | 		RegisterCenter:         client, | ||||||
| 		UserLocalCache:         rpccache.NewUserLocalCache(rpcli.NewUserClient(userConn), &config.LocalCacheConfig, rdb), | 		UserLocalCache:         rpccache.NewUserLocalCache(rpcli.NewUserClient(userConn), &config.LocalCacheConfig, rdb), | ||||||
| 		GroupLocalCache:        rpccache.NewGroupLocalCache(rpcli.NewGroupClient(groupConn), &config.LocalCacheConfig, rdb), | 		GroupLocalCache:        rpccache.NewGroupLocalCache(rpcli.NewGroupClient(groupConn), &config.LocalCacheConfig, rdb), | ||||||
|  | |||||||
| @ -1,115 +0,0 @@ | |||||||
| package msg |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" |  | ||||||
| 	"github.com/openimsdk/protocol/constant" |  | ||||||
| 	"github.com/openimsdk/protocol/msg" |  | ||||||
| 	"github.com/openimsdk/protocol/sdkws" |  | ||||||
| 	"github.com/openimsdk/tools/errs" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const StreamDeadlineTime = time.Second * 60 * 10 |  | ||||||
| 
 |  | ||||||
| func (m *msgServer) handlerStreamMsg(ctx context.Context, msgData *sdkws.MsgData) error { |  | ||||||
| 	now := time.Now() |  | ||||||
| 	val := &model.StreamMsg{ |  | ||||||
| 		ClientMsgID:    msgData.ClientMsgID, |  | ||||||
| 		ConversationID: msgprocessor.GetConversationIDByMsg(msgData), |  | ||||||
| 		UserID:         msgData.SendID, |  | ||||||
| 		CreateTime:     now, |  | ||||||
| 		DeadlineTime:   now.Add(StreamDeadlineTime), |  | ||||||
| 	} |  | ||||||
| 	return m.StreamMsgDatabase.CreateStreamMsg(ctx, val) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *msgServer) getStreamMsg(ctx context.Context, clientMsgID string) (*model.StreamMsg, error) { |  | ||||||
| 	res, err := m.StreamMsgDatabase.GetStreamMsg(ctx, clientMsgID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	now := time.Now() |  | ||||||
| 	if !res.End && res.DeadlineTime.Before(now) { |  | ||||||
| 		res.End = true |  | ||||||
| 		res.DeadlineTime = now |  | ||||||
| 		_ = m.StreamMsgDatabase.AppendStreamMsg(ctx, res.ClientMsgID, 0, nil, true, now) |  | ||||||
| 	} |  | ||||||
| 	return res, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *msgServer) AppendStreamMsg(ctx context.Context, req *msg.AppendStreamMsgReq) (*msg.AppendStreamMsgResp, error) { |  | ||||||
| 	res, err := m.getStreamMsg(ctx, req.ClientMsgID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	if res.End { |  | ||||||
| 		return nil, errs.ErrNoPermission.WrapMsg("stream msg is end") |  | ||||||
| 	} |  | ||||||
| 	if len(res.Packets) < int(req.StartIndex) { |  | ||||||
| 		return nil, errs.ErrNoPermission.WrapMsg("start index is invalid") |  | ||||||
| 	} |  | ||||||
| 	if val := len(res.Packets) - int(req.StartIndex); val > 0 { |  | ||||||
| 		exist := res.Packets[int(req.StartIndex):] |  | ||||||
| 		for i, s := range exist { |  | ||||||
| 			if len(req.Packets) == 0 { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			if s != req.Packets[i] { |  | ||||||
| 				return nil, errs.ErrNoPermission.WrapMsg(fmt.Sprintf("packet %d has been written and is inconsistent", i)) |  | ||||||
| 			} |  | ||||||
| 			req.StartIndex++ |  | ||||||
| 			req.Packets = req.Packets[1:] |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(req.Packets) == 0 && res.End == req.End { |  | ||||||
| 		return &msg.AppendStreamMsgResp{}, nil |  | ||||||
| 	} |  | ||||||
| 	deadlineTime := time.Now().Add(StreamDeadlineTime) |  | ||||||
| 	if err := m.StreamMsgDatabase.AppendStreamMsg(ctx, req.ClientMsgID, int(req.StartIndex), req.Packets, req.End, deadlineTime); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	conversation, err := m.conversationClient.GetConversation(ctx, res.ConversationID, res.UserID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	tips := &sdkws.StreamMsgTips{ |  | ||||||
| 		ConversationID: res.ConversationID, |  | ||||||
| 		ClientMsgID:    res.ClientMsgID, |  | ||||||
| 		StartIndex:     req.StartIndex, |  | ||||||
| 		Packets:        req.Packets, |  | ||||||
| 		End:            req.End, |  | ||||||
| 	} |  | ||||||
| 	var ( |  | ||||||
| 		recvID      string |  | ||||||
| 		sessionType int32 |  | ||||||
| 	) |  | ||||||
| 	if conversation.GroupID == "" { |  | ||||||
| 		sessionType = constant.SingleChatType |  | ||||||
| 		recvID = conversation.UserID |  | ||||||
| 	} else { |  | ||||||
| 		sessionType = constant.ReadGroupChatType |  | ||||||
| 		recvID = conversation.GroupID |  | ||||||
| 	} |  | ||||||
| 	m.msgNotificationSender.StreamMsgNotification(ctx, res.UserID, recvID, sessionType, tips) |  | ||||||
| 	return &msg.AppendStreamMsgResp{}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *msgServer) GetStreamMsg(ctx context.Context, req *msg.GetStreamMsgReq) (*msg.GetStreamMsgResp, error) { |  | ||||||
| 	res, err := m.getStreamMsg(ctx, req.ClientMsgID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return &msg.GetStreamMsgResp{ |  | ||||||
| 		ClientMsgID:    res.ClientMsgID, |  | ||||||
| 		ConversationID: res.ConversationID, |  | ||||||
| 		UserID:         res.UserID, |  | ||||||
| 		Packets:        res.Packets, |  | ||||||
| 		End:            res.End, |  | ||||||
| 		CreateTime:     res.CreateTime.UnixMilli(), |  | ||||||
| 		DeadlineTime:   res.DeadlineTime.UnixMilli(), |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
| @ -49,6 +49,10 @@ import ( | |||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	defaultSecret = "openIM123" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| type userServer struct { | type userServer struct { | ||||||
| 	pbuser.UnimplementedUserServer | 	pbuser.UnimplementedUserServer | ||||||
| 	online                   cache.OnlineCache | 	online                   cache.OnlineCache | ||||||
| @ -88,7 +92,7 @@ func Start(ctx context.Context, config *Config, client discovery.Conn, server gr | |||||||
| 	users := make([]*tablerelation.User, 0) | 	users := make([]*tablerelation.User, 0) | ||||||
| 
 | 
 | ||||||
| 	for _, v := range config.Share.IMAdminUserID { | 	for _, v := range config.Share.IMAdminUserID { | ||||||
| 		users = append(users, &tablerelation.User{UserID: v, Nickname: v, AppMangerLevel: constant.AppNotificationAdmin}) | 		users = append(users, &tablerelation.User{UserID: v, Nickname: v, AppMangerLevel: constant.AppAdmin}) | ||||||
| 	} | 	} | ||||||
| 	userDB, err := mgo.NewUserMongo(mgocli.GetDB()) | 	userDB, err := mgo.NewUserMongo(mgocli.GetDB()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -273,6 +277,10 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR | |||||||
| 	if len(req.Users) == 0 { | 	if len(req.Users) == 0 { | ||||||
| 		return nil, errs.ErrArgs.WrapMsg("users is empty") | 		return nil, errs.ErrArgs.WrapMsg("users is empty") | ||||||
| 	} | 	} | ||||||
|  | 	// check if secret is changed | ||||||
|  | 	if s.config.Share.Secret == defaultSecret { | ||||||
|  | 		return nil, servererrs.ErrSecretNotChanged.Wrap() | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil { | 	if err = authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -605,7 +613,7 @@ func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.Get | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, servererrs.ErrUserIDNotFound.Wrap() | 		return nil, servererrs.ErrUserIDNotFound.Wrap() | ||||||
| 	} | 	} | ||||||
| 	if user.AppMangerLevel == constant.AppAdmin || user.AppMangerLevel >= constant.AppNotificationAdmin { | 	if user.AppMangerLevel >= constant.AppAdmin { | ||||||
| 		return &pbuser.GetNotificationAccountResp{Account: &pbuser.NotificationAccountInfo{ | 		return &pbuser.GetNotificationAccountResp{Account: &pbuser.NotificationAccountInfo{ | ||||||
| 			UserID:         user.UserID, | 			UserID:         user.UserID, | ||||||
| 			FaceURL:        user.FaceURL, | 			FaceURL:        user.FaceURL, | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ func TestName(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	srv := &cronServer{ | 	srv := &cronServer{ | ||||||
| 		ctx: ctx, | 		ctx: ctx, | ||||||
| 		config: &CronTaskConfig{ | 		config: &Config{ | ||||||
| 			CronTask: config.CronTask{ | 			CronTask: config.CronTask{ | ||||||
| 				RetainChatRecords: 1, | 				RetainChatRecords: 1, | ||||||
| 				FileExpireTime:    1, | 				FileExpireTime:    1, | ||||||
|  | |||||||
| @ -62,4 +62,8 @@ const ( | |||||||
| 	CallbackBeforeMembersJoinGroupCommand              = "callbackBeforeMembersJoinGroupCommand" | 	CallbackBeforeMembersJoinGroupCommand              = "callbackBeforeMembersJoinGroupCommand" | ||||||
| 	CallbackBeforeSetGroupMemberInfoCommand            = "callbackBeforeSetGroupMemberInfoCommand" | 	CallbackBeforeSetGroupMemberInfoCommand            = "callbackBeforeSetGroupMemberInfoCommand" | ||||||
| 	CallbackAfterSetGroupMemberInfoCommand             = "callbackAfterSetGroupMemberInfoCommand" | 	CallbackAfterSetGroupMemberInfoCommand             = "callbackAfterSetGroupMemberInfoCommand" | ||||||
|  | 	CallbackBeforeCreateSingleChatConversationsCommand = "callbackBeforeCreateSingleChatConversationsCommand" | ||||||
|  | 	CallbackAfterCreateSingleChatConversationsCommand  = "callbackAfterCreateSingleChatConversationsCommand" | ||||||
|  | 	CallbackBeforeCreateGroupChatConversationsCommand  = "callbackBeforeCreateGroupChatConversationsCommand" | ||||||
|  | 	CallbackAfterCreateGroupChatConversationsCommand   = "callbackAfterCreateGroupChatConversationsCommand" | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										91
									
								
								pkg/callbackstruct/conversation.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								pkg/callbackstruct/conversation.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | package callbackstruct | ||||||
|  | 
 | ||||||
|  | type CallbackBeforeCreateSingleChatConversationsReq struct { | ||||||
|  | 	CallbackCommand  `json:"callbackCommand"` | ||||||
|  | 	OwnerUserID      string `json:"owner_user_id"` | ||||||
|  | 	ConversationID   string `json:"conversation_id"` | ||||||
|  | 	ConversationType int32  `json:"conversation_type"` | ||||||
|  | 	UserID           string `json:"user_id"` | ||||||
|  | 	RecvMsgOpt       int32  `json:"recv_msg_opt"` | ||||||
|  | 	IsPinned         bool   `json:"is_pinned"` | ||||||
|  | 	IsPrivateChat    bool   `json:"is_private_chat"` | ||||||
|  | 	BurnDuration     int32  `json:"burn_duration"` | ||||||
|  | 	GroupAtType      int32  `json:"group_at_type"` | ||||||
|  | 	AttachedInfo     string `json:"attached_info"` | ||||||
|  | 	Ex               string `json:"ex"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackBeforeCreateSingleChatConversationsResp struct { | ||||||
|  | 	CommonCallbackResp | ||||||
|  | 	RecvMsgOpt    *int32  `json:"recv_msg_opt"` | ||||||
|  | 	IsPinned      *bool   `json:"is_pinned"` | ||||||
|  | 	IsPrivateChat *bool   `json:"is_private_chat"` | ||||||
|  | 	BurnDuration  *int32  `json:"burn_duration"` | ||||||
|  | 	GroupAtType   *int32  `json:"group_at_type"` | ||||||
|  | 	AttachedInfo  *string `json:"attached_info"` | ||||||
|  | 	Ex            *string `json:"ex"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackAfterCreateSingleChatConversationsReq struct { | ||||||
|  | 	CallbackCommand  `json:"callbackCommand"` | ||||||
|  | 	OwnerUserID      string `json:"owner_user_id"` | ||||||
|  | 	ConversationID   string `json:"conversation_id"` | ||||||
|  | 	ConversationType int32  `json:"conversation_type"` | ||||||
|  | 	UserID           string `json:"user_id"` | ||||||
|  | 	RecvMsgOpt       int32  `json:"recv_msg_opt"` | ||||||
|  | 	IsPinned         bool   `json:"is_pinned"` | ||||||
|  | 	IsPrivateChat    bool   `json:"is_private_chat"` | ||||||
|  | 	BurnDuration     int32  `json:"burn_duration"` | ||||||
|  | 	GroupAtType      int32  `json:"group_at_type"` | ||||||
|  | 	AttachedInfo     string `json:"attached_info"` | ||||||
|  | 	Ex               string `json:"ex"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackAfterCreateSingleChatConversationsResp struct { | ||||||
|  | 	CommonCallbackResp | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackBeforeCreateGroupChatConversationsReq struct { | ||||||
|  | 	CallbackCommand  `json:"callbackCommand"` | ||||||
|  | 	OwnerUserID      string `json:"owner_user_id"` | ||||||
|  | 	ConversationID   string `json:"conversation_id"` | ||||||
|  | 	ConversationType int32  `json:"conversation_type"` | ||||||
|  | 	GroupID          string `json:"group_id"` | ||||||
|  | 	RecvMsgOpt       int32  `json:"recv_msg_opt"` | ||||||
|  | 	IsPinned         bool   `json:"is_pinned"` | ||||||
|  | 	IsPrivateChat    bool   `json:"is_private_chat"` | ||||||
|  | 	BurnDuration     int32  `json:"burn_duration"` | ||||||
|  | 	GroupAtType      int32  `json:"group_at_type"` | ||||||
|  | 	AttachedInfo     string `json:"attached_info"` | ||||||
|  | 	Ex               string `json:"ex"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackBeforeCreateGroupChatConversationsResp struct { | ||||||
|  | 	CommonCallbackResp | ||||||
|  | 	RecvMsgOpt    *int32  `json:"recv_msg_opt"` | ||||||
|  | 	IsPinned      *bool   `json:"is_pinned"` | ||||||
|  | 	IsPrivateChat *bool   `json:"is_private_chat"` | ||||||
|  | 	BurnDuration  *int32  `json:"burn_duration"` | ||||||
|  | 	GroupAtType   *int32  `json:"group_at_type"` | ||||||
|  | 	AttachedInfo  *string `json:"attached_info"` | ||||||
|  | 	Ex            *string `json:"ex"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackAfterCreateGroupChatConversationsReq struct { | ||||||
|  | 	CallbackCommand  `json:"callbackCommand"` | ||||||
|  | 	OwnerUserID      string `json:"owner_user_id"` | ||||||
|  | 	ConversationID   string `json:"conversation_id"` | ||||||
|  | 	ConversationType int32  `json:"conversation_type"` | ||||||
|  | 	GroupID          string `json:"group_id"` | ||||||
|  | 	RecvMsgOpt       int32  `json:"recv_msg_opt"` | ||||||
|  | 	IsPinned         bool   `json:"is_pinned"` | ||||||
|  | 	IsPrivateChat    bool   `json:"is_private_chat"` | ||||||
|  | 	BurnDuration     int32  `json:"burn_duration"` | ||||||
|  | 	GroupAtType      int32  `json:"group_at_type"` | ||||||
|  | 	AttachedInfo     string `json:"attached_info"` | ||||||
|  | 	Ex               string `json:"ex"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type CallbackAfterCreateGroupChatConversationsResp struct { | ||||||
|  | 	CommonCallbackResp | ||||||
|  | } | ||||||
| @ -41,6 +41,7 @@ func NewConversationRpcCmd() *ConversationRpcCmd { | |||||||
| 		config.MongodbConfigFileName:            &conversationConfig.MongodbConfig, | 		config.MongodbConfigFileName:            &conversationConfig.MongodbConfig, | ||||||
| 		config.ShareFileName:                    &conversationConfig.Share, | 		config.ShareFileName:                    &conversationConfig.Share, | ||||||
| 		config.NotificationFileName:             &conversationConfig.NotificationConfig, | 		config.NotificationFileName:             &conversationConfig.NotificationConfig, | ||||||
|  | 		config.WebhooksConfigFileName:           &conversationConfig.WebhooksConfig, | ||||||
| 		config.LocalCacheConfigFileName:         &conversationConfig.LocalCacheConfig, | 		config.LocalCacheConfigFileName:         &conversationConfig.LocalCacheConfig, | ||||||
| 		config.DiscoveryConfigFilename:          &conversationConfig.Discovery, | 		config.DiscoveryConfigFilename:          &conversationConfig.Discovery, | ||||||
| 	} | 	} | ||||||
| @ -67,6 +68,7 @@ func (a *ConversationRpcCmd) runE() error { | |||||||
| 			a.conversationConfig.NotificationConfig.GetConfigFileName(), | 			a.conversationConfig.NotificationConfig.GetConfigFileName(), | ||||||
| 			a.conversationConfig.Share.GetConfigFileName(), | 			a.conversationConfig.Share.GetConfigFileName(), | ||||||
| 			a.conversationConfig.LocalCacheConfig.GetConfigFileName(), | 			a.conversationConfig.LocalCacheConfig.GetConfigFileName(), | ||||||
|  | 			a.conversationConfig.WebhooksConfig.GetConfigFileName(), | ||||||
| 			a.conversationConfig.Discovery.GetConfigFileName(), | 			a.conversationConfig.Discovery.GetConfigFileName(), | ||||||
| 		}, nil, | 		}, nil, | ||||||
| 		conversation.Start) | 		conversation.Start) | ||||||
|  | |||||||
| @ -435,6 +435,10 @@ type Webhooks struct { | |||||||
| 	BeforeImportFriends                 BeforeConfig `yaml:"beforeImportFriends"` | 	BeforeImportFriends                 BeforeConfig `yaml:"beforeImportFriends"` | ||||||
| 	AfterImportFriends                  AfterConfig  `yaml:"afterImportFriends"` | 	AfterImportFriends                  AfterConfig  `yaml:"afterImportFriends"` | ||||||
| 	AfterRemoveBlack                    AfterConfig  `yaml:"afterRemoveBlack"` | 	AfterRemoveBlack                    AfterConfig  `yaml:"afterRemoveBlack"` | ||||||
|  | 	BeforeCreateSingleChatConversations BeforeConfig `yaml:"beforeCreateSingleChatConversations"` | ||||||
|  | 	AfterCreateSingleChatConversations  AfterConfig  `yaml:"afterCreateSingleChatConversations"` | ||||||
|  | 	BeforeCreateGroupChatConversations  BeforeConfig `yaml:"beforeCreateGroupChatConversations"` | ||||||
|  | 	AfterCreateGroupChatConversations   AfterConfig  `yaml:"afterCreateGroupChatConversations"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ZooKeeper struct { | type ZooKeeper struct { | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ import ( | |||||||
| func TestLoadLogConfig(t *testing.T) { | func TestLoadLogConfig(t *testing.T) { | ||||||
| 	var log Log | 	var log Log | ||||||
| 	os.Setenv("IMENV_LOG_REMAINLOGLEVEL", "5") | 	os.Setenv("IMENV_LOG_REMAINLOGLEVEL", "5") | ||||||
| 	err := Load("../../../config/", "log.yml", "IMENV_LOG", "source", &log) | 	err := Load("../../../config/", "log.yml", "IMENV_LOG", &log) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	t.Log(log.RemainLogLevel) | 	t.Log(log.RemainLogLevel) | ||||||
| 	// assert.Equal(t, "../../../../logs/", log.StorageLocation) | 	// assert.Equal(t, "../../../../logs/", log.StorageLocation) | ||||||
| @ -22,7 +22,7 @@ func TestLoadMongoConfig(t *testing.T) { | |||||||
| 	os.Setenv("IMENV_MONGODB_PASSWORD", "openIM1231231") | 	os.Setenv("IMENV_MONGODB_PASSWORD", "openIM1231231") | ||||||
| 	// os.Setenv("IMENV_MONGODB_URI", "openIM123") | 	// os.Setenv("IMENV_MONGODB_URI", "openIM123") | ||||||
| 	// os.Setenv("IMENV_MONGODB_USERNAME", "openIM123") | 	// os.Setenv("IMENV_MONGODB_USERNAME", "openIM123") | ||||||
| 	err := Load("../../../config/", "mongodb.yml", "IMENV_MONGODB", "source", &mongo) | 	err := Load("../../../config/", "mongodb.yml", "IMENV_MONGODB", &mongo) | ||||||
| 	// err := LoadApiConfig("../../../config/mongodb.yml", "IMENV_MONGODB", &mongo) | 	// err := LoadApiConfig("../../../config/mongodb.yml", "IMENV_MONGODB", &mongo) | ||||||
| 
 | 
 | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| @ -38,14 +38,14 @@ func TestLoadMongoConfig(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestLoadMinioConfig(t *testing.T) { | func TestLoadMinioConfig(t *testing.T) { | ||||||
| 	var storageConfig Minio | 	var storageConfig Minio | ||||||
| 	err := Load("../../../config/minio.yml", "IMENV_MINIO", "", "source", &storageConfig) | 	err := Load("../../../config/minio.yml", "IMENV_MINIO", "", &storageConfig) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	assert.Equal(t, "openim", storageConfig.Bucket) | 	assert.Equal(t, "openim", storageConfig.Bucket) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestLoadWebhooksConfig(t *testing.T) { | func TestLoadWebhooksConfig(t *testing.T) { | ||||||
| 	var webhooks Webhooks | 	var webhooks Webhooks | ||||||
| 	err := Load("../../../config/webhooks.yml", "IMENV_WEBHOOKS", "", "source", &webhooks) | 	err := Load("../../../config/webhooks.yml", "IMENV_WEBHOOKS", "", &webhooks) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	assert.Equal(t, 5, webhooks.BeforeAddBlack.Timeout) | 	assert.Equal(t, 5, webhooks.BeforeAddBlack.Timeout) | ||||||
| 
 | 
 | ||||||
| @ -53,7 +53,7 @@ func TestLoadWebhooksConfig(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestLoadOpenIMRpcUserConfig(t *testing.T) { | func TestLoadOpenIMRpcUserConfig(t *testing.T) { | ||||||
| 	var user User | 	var user User | ||||||
| 	err := Load("../../../config/openim-rpc-user.yml", "IMENV_OPENIM_RPC_USER", "", "source", &user) | 	err := Load("../../../config/openim-rpc-user.yml", "IMENV_OPENIM_RPC_USER", "", &user) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	//export IMENV_OPENIM_RPC_USER_RPC_LISTENIP="0.0.0.0" | 	//export IMENV_OPENIM_RPC_USER_RPC_LISTENIP="0.0.0.0" | ||||||
| 	assert.Equal(t, "0.0.0.0", user.RPC.ListenIP) | 	assert.Equal(t, "0.0.0.0", user.RPC.ListenIP) | ||||||
| @ -63,14 +63,14 @@ func TestLoadOpenIMRpcUserConfig(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestLoadNotificationConfig(t *testing.T) { | func TestLoadNotificationConfig(t *testing.T) { | ||||||
| 	var noti Notification | 	var noti Notification | ||||||
| 	err := Load("../../../config/notification.yml", "IMENV_NOTIFICATION", "", "source", ¬i) | 	err := Load("../../../config/notification.yml", "IMENV_NOTIFICATION", "", ¬i) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	assert.Equal(t, "Your friend's profile has been changed", noti.FriendRemarkSet.OfflinePush.Title) | 	assert.Equal(t, "Your friend's profile has been changed", noti.FriendRemarkSet.OfflinePush.Title) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestLoadOpenIMThirdConfig(t *testing.T) { | func TestLoadOpenIMThirdConfig(t *testing.T) { | ||||||
| 	var third Third | 	var third Third | ||||||
| 	err := Load("../../../config/openim-rpc-third.yml", "IMENV_OPENIM_RPC_THIRD", "", "source", &third) | 	err := Load("../../../config/openim-rpc-third.yml", "IMENV_OPENIM_RPC_THIRD", "", &third) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	assert.Equal(t, "enabled", third.Object.Enable) | 	assert.Equal(t, "enabled", third.Object.Enable) | ||||||
| 	assert.Equal(t, "https://oss-cn-chengdu.aliyuncs.com", third.Object.Oss.Endpoint) | 	assert.Equal(t, "https://oss-cn-chengdu.aliyuncs.com", third.Object.Oss.Endpoint) | ||||||
| @ -86,7 +86,7 @@ func TestLoadOpenIMThirdConfig(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestTransferConfig(t *testing.T) { | func TestTransferConfig(t *testing.T) { | ||||||
| 	var tran MsgTransfer | 	var tran MsgTransfer | ||||||
| 	err := Load("../../../config/openim-msgtransfer.yml", "IMENV_OPENIM-MSGTRANSFER", "", "source", &tran) | 	err := Load("../../../config/openim-msgtransfer.yml", "IMENV_OPENIM-MSGTRANSFER", "", &tran) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| 	assert.Equal(t, true, tran.Prometheus.Enable) | 	assert.Equal(t, true, tran.Prometheus.Enable) | ||||||
| 	assert.Equal(t, true, tran.Prometheus.AutoSetPorts) | 	assert.Equal(t, true, tran.Prometheus.AutoSetPorts) | ||||||
|  | |||||||
| @ -38,6 +38,7 @@ const ( | |||||||
| // General error codes. | // General error codes. | ||||||
| const ( | const ( | ||||||
| 	NoError = 0 // No error | 	NoError = 0 // No error | ||||||
|  | 
 | ||||||
| 	DatabaseError = 90002 // Database error (redis/mysql, etc.) | 	DatabaseError = 90002 // Database error (redis/mysql, etc.) | ||||||
| 	NetworkError  = 90004 // Network error | 	NetworkError  = 90004 // Network error | ||||||
| 	DataError     = 90007 // Data error | 	DataError     = 90007 // Data error | ||||||
| @ -50,6 +51,7 @@ const ( | |||||||
| 	NoPermissionError     = 1002 // Insufficient permission | 	NoPermissionError     = 1002 // Insufficient permission | ||||||
| 	DuplicateKeyError     = 1003 | 	DuplicateKeyError     = 1003 | ||||||
| 	RecordNotFoundError   = 1004 // Record does not exist | 	RecordNotFoundError   = 1004 // Record does not exist | ||||||
|  | 	SecretNotChangedError = 1050 // secret not changed | ||||||
| 
 | 
 | ||||||
| 	// Account error codes. | 	// Account error codes. | ||||||
| 	UserIDNotFoundError    = 1101 // UserID does not exist or is not registered | 	UserIDNotFoundError    = 1101 // UserID does not exist or is not registered | ||||||
|  | |||||||
| @ -17,6 +17,8 @@ package servererrs | |||||||
| import "github.com/openimsdk/tools/errs" | import "github.com/openimsdk/tools/errs" | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | 	ErrSecretNotChanged = errs.NewCodeError(SecretNotChangedError, "secret not changed, please change secret in config/share.yml for security reasons") | ||||||
|  | 
 | ||||||
| 	ErrDatabase         = errs.NewCodeError(DatabaseError, "DatabaseError") | 	ErrDatabase         = errs.NewCodeError(DatabaseError, "DatabaseError") | ||||||
| 	ErrNetwork          = errs.NewCodeError(NetworkError, "NetworkError") | 	ErrNetwork          = errs.NewCodeError(NetworkError, "NetworkError") | ||||||
| 	ErrCallback         = errs.NewCodeError(CallbackError, "CallbackError") | 	ErrCallback         = errs.NewCodeError(CallbackError, "CallbackError") | ||||||
|  | |||||||
| @ -80,8 +80,6 @@ func (a *authDatabase) BatchSetTokenMapByUidPid(ctx context.Context, tokens []st | |||||||
| 
 | 
 | ||||||
| // Create Token. | // Create Token. | ||||||
| func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformID int) (string, error) { | func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformID int) (string, error) { | ||||||
| 	isAdmin := authverify.IsManagerUserID(userID, a.adminUserIDs) |  | ||||||
| 	if !isAdmin { |  | ||||||
| 	tokens, err := a.cache.GetAllTokensWithoutError(ctx, userID) | 	tokens, err := a.cache.GetAllTokensWithoutError(ctx, userID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| @ -106,7 +104,6 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI | |||||||
| 			log.ZDebug(ctx, "kicked token in create token", "token", k) | 			log.ZDebug(ctx, "kicked token in create token", "token", k) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	claims := tokenverify.BuildClaims(userID, platformID, a.accessExpire) | 	claims := tokenverify.BuildClaims(userID, platformID, a.accessExpire) | ||||||
| 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | ||||||
| @ -115,11 +112,9 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI | |||||||
| 		return "", errs.WrapMsg(err, "token.SignedString") | 		return "", errs.WrapMsg(err, "token.SignedString") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !isAdmin { |  | ||||||
| 	if err = a.cache.SetTokenFlagEx(ctx, userID, platformID, tokenString, constant.NormalToken); err != nil { | 	if err = a.cache.SetTokenFlagEx(ctx, userID, platformID, tokenString, constant.NormalToken); err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return tokenString, nil | 	return tokenString, nil | ||||||
| } | } | ||||||
| @ -224,9 +219,6 @@ func (a *authDatabase) checkToken(ctx context.Context, tokens map[int]map[string | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//var adminTokenMaxNum = a.multiLogin.MaxNumOneEnd | 	//var adminTokenMaxNum = a.multiLogin.MaxNumOneEnd | ||||||
| 	//if a.multiLogin.Policy == constant.Customize { |  | ||||||
| 	//	adminTokenMaxNum = a.multiLogin.CustomizeLoginNum[constant.AdminPlatformID] |  | ||||||
| 	//} |  | ||||||
| 	//l := len(adminToken) | 	//l := len(adminToken) | ||||||
| 	//if platformID == constant.AdminPlatformID { | 	//if platformID == constant.AdminPlatformID { | ||||||
| 	//	l++ | 	//	l++ | ||||||
| @ -234,5 +226,8 @@ func (a *authDatabase) checkToken(ctx context.Context, tokens map[int]map[string | |||||||
| 	//if l > adminTokenMaxNum { | 	//if l > adminTokenMaxNum { | ||||||
| 	//	kickToken = append(kickToken, adminToken[:l-adminTokenMaxNum]...) | 	//	kickToken = append(kickToken, adminToken[:l-adminTokenMaxNum]...) | ||||||
| 	//} | 	//} | ||||||
|  | 	if platformID == constant.AdminPlatformID { | ||||||
|  | 		kickToken = append(kickToken, adminToken...) | ||||||
|  | 	} | ||||||
| 	return deleteToken, kickToken, nil | 	return deleteToken, kickToken, nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,7 +22,6 @@ import ( | |||||||
| 	relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | 	relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||||
| 
 | 
 | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" |  | ||||||
| 	"github.com/openimsdk/protocol/constant" | 	"github.com/openimsdk/protocol/constant" | ||||||
| 	"github.com/openimsdk/tools/db/pagination" | 	"github.com/openimsdk/tools/db/pagination" | ||||||
| 	"github.com/openimsdk/tools/db/tx" | 	"github.com/openimsdk/tools/db/tx" | ||||||
| @ -47,8 +46,11 @@ type ConversationDatabase interface { | |||||||
| 	// SetUsersConversationFieldTx updates a specific field for multiple users' conversations, creating new conversations if they do not exist, or updates them otherwise. This operation is | 	// SetUsersConversationFieldTx updates a specific field for multiple users' conversations, creating new conversations if they do not exist, or updates them otherwise. This operation is | ||||||
| 	// transactional. | 	// transactional. | ||||||
| 	SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.Conversation, fieldMap map[string]any) error | 	SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.Conversation, fieldMap map[string]any) error | ||||||
|  | 	// UpdateUserConversations updates all conversations related to a specified user. | ||||||
|  | 	// This function does NOT update the user's own conversations but rather the conversations where this user is involved (e.g., other users' conversations referencing this user). | ||||||
|  | 	UpdateUserConversations(ctx context.Context, userID string, args map[string]any) error | ||||||
| 	// CreateGroupChatConversation creates a group chat conversation for the specified group ID and user IDs. | 	// CreateGroupChatConversation creates a group chat conversation for the specified group ID and user IDs. | ||||||
| 	CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error | 	CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string, conversations *relationtb.Conversation) error | ||||||
| 	// GetConversationIDs retrieves conversation IDs for a given user. | 	// GetConversationIDs retrieves conversation IDs for a given user. | ||||||
| 	GetConversationIDs(ctx context.Context, userID string) ([]string, error) | 	GetConversationIDs(ctx context.Context, userID string) ([]string, error) | ||||||
| 	// GetUserConversationIDsHash gets the hash of conversation IDs for a given user. | 	// GetUserConversationIDsHash gets the hash of conversation IDs for a given user. | ||||||
| @ -146,6 +148,18 @@ func (c *conversationDatabase) SetUsersConversationFieldTx(ctx context.Context, | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (c *conversationDatabase) UpdateUserConversations(ctx context.Context, userID string, args map[string]any) error { | ||||||
|  | 	conversations, err := c.conversationDB.UpdateUserConversations(ctx, userID, args) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	cache := c.cache.CloneConversationCache() | ||||||
|  | 	for _, conversation := range conversations { | ||||||
|  | 		cache = cache.DelUsersConversation(conversation.ConversationID, conversation.OwnerUserID).DelConversationVersionUserIDs(conversation.OwnerUserID) | ||||||
|  | 	} | ||||||
|  | 	return cache.ChainExecDel(ctx) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *conversationDatabase) UpdateUsersConversationField(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error { | func (c *conversationDatabase) UpdateUsersConversationField(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error { | ||||||
| 	_, err := c.conversationDB.UpdateByMap(ctx, userIDs, conversationID, args) | 	_, err := c.conversationDB.UpdateByMap(ctx, userIDs, conversationID, args) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -298,10 +312,10 @@ func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUs | |||||||
| //	return c.cache.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) | //	return c.cache.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) | ||||||
| //} | //} | ||||||
| 
 | 
 | ||||||
| func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error { | func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string, conversation *relationtb.Conversation) error { | ||||||
| 	return c.tx.Transaction(ctx, func(ctx context.Context) error { | 	return c.tx.Transaction(ctx, func(ctx context.Context) error { | ||||||
| 		cache := c.cache.CloneConversationCache() | 		cache := c.cache.CloneConversationCache() | ||||||
| 		conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID) | 		conversationID := conversation.ConversationID | ||||||
| 		existConversationUserIDs, err := c.conversationDB.FindUserID(ctx, userIDs, []string{conversationID}) | 		existConversationUserIDs, err := c.conversationDB.FindUserID(ctx, userIDs, []string{conversationID}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| @ -309,7 +323,15 @@ func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, | |||||||
| 		notExistUserIDs := stringutil.DifferenceString(userIDs, existConversationUserIDs) | 		notExistUserIDs := stringutil.DifferenceString(userIDs, existConversationUserIDs) | ||||||
| 		var conversations []*relationtb.Conversation | 		var conversations []*relationtb.Conversation | ||||||
| 		for _, v := range notExistUserIDs { | 		for _, v := range notExistUserIDs { | ||||||
| 			conversation := relationtb.Conversation{ConversationType: constant.ReadGroupChatType, GroupID: groupID, OwnerUserID: v, ConversationID: conversationID} | 			conversation := relationtb.Conversation{ | ||||||
|  | 				ConversationType: conversation.ConversationType, GroupID: groupID, OwnerUserID: v, ConversationID: conversationID, | ||||||
|  | 				// the parameters have default value | ||||||
|  | 				RecvMsgOpt: conversation.RecvMsgOpt, IsPinned: conversation.IsPinned, IsPrivateChat: conversation.IsPrivateChat, | ||||||
|  | 				BurnDuration: conversation.BurnDuration, GroupAtType: conversation.GroupAtType, AttachedInfo: conversation.AttachedInfo, | ||||||
|  | 				Ex: conversation.Ex, MaxSeq: conversation.MaxSeq, MinSeq: conversation.MinSeq, CreateTime: conversation.CreateTime, | ||||||
|  | 				MsgDestructTime: conversation.MsgDestructTime, IsMsgDestruct: conversation.IsMsgDestruct, LatestMsgDestructTime: conversation.LatestMsgDestructTime, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			conversations = append(conversations, &conversation) | 			conversations = append(conversations, &conversation) | ||||||
| 			cache = cache.DelConversations(v, conversationID).DelConversationNotReceiveMessageUserIDs(conversationID) | 			cache = cache.DelConversations(v, conversationID).DelConversationNotReceiveMessageUserIDs(conversationID) | ||||||
| 		} | 		} | ||||||
| @ -320,7 +342,7 @@ func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		_, err = c.conversationDB.UpdateByMap(ctx, existConversationUserIDs, conversationID, map[string]any{"max_seq": 0}) | 		_, err = c.conversationDB.UpdateByMap(ctx, existConversationUserIDs, conversationID, map[string]any{"max_seq": conversation.MaxSeq}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -1,34 +0,0 @@ | |||||||
| package controller |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type StreamMsgDatabase interface { |  | ||||||
| 	CreateStreamMsg(ctx context.Context, model *model.StreamMsg) error |  | ||||||
| 	AppendStreamMsg(ctx context.Context, clientMsgID string, startIndex int, packets []string, end bool, deadlineTime time.Time) error |  | ||||||
| 	GetStreamMsg(ctx context.Context, clientMsgID string) (*model.StreamMsg, error) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NewStreamMsgDatabase(db database.StreamMsg) StreamMsgDatabase { |  | ||||||
| 	return &streamMsgDatabase{db: db} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type streamMsgDatabase struct { |  | ||||||
| 	db database.StreamMsg |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *streamMsgDatabase) CreateStreamMsg(ctx context.Context, model *model.StreamMsg) error { |  | ||||||
| 	return m.db.CreateStreamMsg(ctx, model) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *streamMsgDatabase) AppendStreamMsg(ctx context.Context, clientMsgID string, startIndex int, packets []string, end bool, deadlineTime time.Time) error { |  | ||||||
| 	return m.db.AppendStreamMsg(ctx, clientMsgID, startIndex, packets, end, deadlineTime) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *streamMsgDatabase) GetStreamMsg(ctx context.Context, clientMsgID string) (*model.StreamMsg, error) { |  | ||||||
| 	return m.db.GetStreamMsg(ctx, clientMsgID) |  | ||||||
| } |  | ||||||
| @ -21,12 +21,11 @@ import ( | |||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||||
| 	"github.com/openimsdk/protocol/constant" | 	"github.com/openimsdk/protocol/constant" | ||||||
|  | 	"github.com/openimsdk/protocol/user" | ||||||
| 	"github.com/openimsdk/tools/db/pagination" | 	"github.com/openimsdk/tools/db/pagination" | ||||||
| 	"github.com/openimsdk/tools/db/tx" | 	"github.com/openimsdk/tools/db/tx" | ||||||
| 	"github.com/openimsdk/tools/utils/datautil" |  | ||||||
| 
 |  | ||||||
| 	"github.com/openimsdk/protocol/user" |  | ||||||
| 	"github.com/openimsdk/tools/errs" | 	"github.com/openimsdk/tools/errs" | ||||||
|  | 	"github.com/openimsdk/tools/utils/datautil" | ||||||
| 
 | 
 | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache" | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ import ( | |||||||
| type Conversation interface { | type Conversation interface { | ||||||
| 	Create(ctx context.Context, conversations []*model.Conversation) (err error) | 	Create(ctx context.Context, conversations []*model.Conversation) (err error) | ||||||
| 	UpdateByMap(ctx context.Context, userIDs []string, conversationID string, args map[string]any) (rows int64, err error) | 	UpdateByMap(ctx context.Context, userIDs []string, conversationID string, args map[string]any) (rows int64, err error) | ||||||
|  | 	UpdateUserConversations(ctx context.Context, userID string, args map[string]any) ([]*model.Conversation, error) | ||||||
| 	Update(ctx context.Context, conversation *model.Conversation) (err error) | 	Update(ctx context.Context, conversation *model.Conversation) (err error) | ||||||
| 	Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*model.Conversation, err error) | 	Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*model.Conversation, err error) | ||||||
| 	FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) ([]string, error) | 	FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) ([]string, error) | ||||||
|  | |||||||
| @ -21,23 +21,32 @@ import ( | |||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||||
| 
 | 
 | ||||||
|  | 	"go.mongodb.org/mongo-driver/bson" | ||||||
|  | 	"go.mongodb.org/mongo-driver/mongo" | ||||||
|  | 	"go.mongodb.org/mongo-driver/mongo/options" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/protocol/constant" | 	"github.com/openimsdk/protocol/constant" | ||||||
| 	"github.com/openimsdk/tools/db/mongoutil" | 	"github.com/openimsdk/tools/db/mongoutil" | ||||||
| 	"github.com/openimsdk/tools/db/pagination" | 	"github.com/openimsdk/tools/db/pagination" | ||||||
| 	"github.com/openimsdk/tools/errs" | 	"github.com/openimsdk/tools/errs" | ||||||
| 	"go.mongodb.org/mongo-driver/bson" |  | ||||||
| 	"go.mongodb.org/mongo-driver/mongo" |  | ||||||
| 	"go.mongodb.org/mongo-driver/mongo/options" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func NewConversationMongo(db *mongo.Database) (*ConversationMgo, error) { | func NewConversationMongo(db *mongo.Database) (*ConversationMgo, error) { | ||||||
| 	coll := db.Collection(database.ConversationName) | 	coll := db.Collection(database.ConversationName) | ||||||
| 	_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{ | 	_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{ | ||||||
|  | 		{ | ||||||
| 			Keys: bson.D{ | 			Keys: bson.D{ | ||||||
| 				{Key: "owner_user_id", Value: 1}, | 				{Key: "owner_user_id", Value: 1}, | ||||||
| 				{Key: "conversation_id", Value: 1}, | 				{Key: "conversation_id", Value: 1}, | ||||||
| 			}, | 			}, | ||||||
| 			Options: options.Index().SetUnique(true), | 			Options: options.Index().SetUnique(true), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Keys: bson.D{ | ||||||
|  | 				{Key: "user_id", Value: 1}, | ||||||
|  | 			}, | ||||||
|  | 			Options: options.Index(), | ||||||
|  | 		}, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, errs.Wrap(err) | 		return nil, errs.Wrap(err) | ||||||
| @ -101,6 +110,38 @@ func (c *ConversationMgo) UpdateByMap(ctx context.Context, userIDs []string, con | |||||||
| 	return rows, nil | 	return rows, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (c *ConversationMgo) UpdateUserConversations(ctx context.Context, userID string, args map[string]any) ([]*model.Conversation, error) { | ||||||
|  | 	if len(args) == 0 { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 	filter := bson.M{ | ||||||
|  | 		"user_id": userID, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	conversations, err := mongoutil.Find[*model.Conversation](ctx, c.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1, "conversation_id": 1})) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	err = mongoutil.IncrVersion(func() error { | ||||||
|  | 		_, err := mongoutil.UpdateMany(ctx, c.coll, filter, bson.M{"$set": args}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}, func() error { | ||||||
|  | 		for _, conversation := range conversations { | ||||||
|  | 			if err := c.version.IncrVersion(ctx, conversation.OwnerUserID, []string{conversation.ConversationID}, model.VersionStateUpdate); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return conversations, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *ConversationMgo) Update(ctx context.Context, conversation *model.Conversation) (err error) { | func (c *ConversationMgo) Update(ctx context.Context, conversation *model.Conversation) (err error) { | ||||||
| 	return mongoutil.IncrVersion(func() error { | 	return mongoutil.IncrVersion(func() error { | ||||||
| 		return mongoutil.UpdateOne(ctx, c.coll, bson.M{"owner_user_id": conversation.OwnerUserID, "conversation_id": conversation.ConversationID}, bson.M{"$set": conversation}, true) | 		return mongoutil.UpdateOne(ctx, c.coll, bson.M{"owner_user_id": conversation.OwnerUserID, "conversation_id": conversation.ConversationID}, bson.M{"$set": conversation}, true) | ||||||
|  | |||||||
| @ -1,60 +0,0 @@ | |||||||
| package mgo |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" |  | ||||||
| 	"github.com/openimsdk/tools/db/mongoutil" |  | ||||||
| 	"github.com/openimsdk/tools/errs" |  | ||||||
| 	"go.mongodb.org/mongo-driver/bson" |  | ||||||
| 	"go.mongodb.org/mongo-driver/mongo" |  | ||||||
| 	"go.mongodb.org/mongo-driver/mongo/options" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func NewStreamMsgMongo(db *mongo.Database) (*StreamMsgMongo, error) { |  | ||||||
| 	coll := db.Collection(database.StreamMsgName) |  | ||||||
| 	_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{ |  | ||||||
| 		Keys: bson.D{ |  | ||||||
| 			{Key: "client_msg_id", Value: 1}, |  | ||||||
| 		}, |  | ||||||
| 		Options: options.Index().SetUnique(true), |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, errs.Wrap(err) |  | ||||||
| 	} |  | ||||||
| 	return &StreamMsgMongo{coll: coll}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type StreamMsgMongo struct { |  | ||||||
| 	coll *mongo.Collection |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *StreamMsgMongo) CreateStreamMsg(ctx context.Context, val *model.StreamMsg) error { |  | ||||||
| 	if val.Packets == nil { |  | ||||||
| 		val.Packets = []string{} |  | ||||||
| 	} |  | ||||||
| 	return mongoutil.InsertMany(ctx, m.coll, []*model.StreamMsg{val}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *StreamMsgMongo) AppendStreamMsg(ctx context.Context, clientMsgID string, startIndex int, packets []string, end bool, deadlineTime time.Time) error { |  | ||||||
| 	update := bson.M{ |  | ||||||
| 		"$set": bson.M{ |  | ||||||
| 			"end":           end, |  | ||||||
| 			"deadline_time": deadlineTime, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	if len(packets) > 0 { |  | ||||||
| 		update["$push"] = bson.M{ |  | ||||||
| 			"packets": bson.M{ |  | ||||||
| 				"$each":     packets, |  | ||||||
| 				"$position": startIndex, |  | ||||||
| 			}, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return mongoutil.UpdateOne(ctx, m.coll, bson.M{"client_msg_id": clientMsgID, "end": false}, update, true) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *StreamMsgMongo) GetStreamMsg(ctx context.Context, clientMsgID string) (*model.StreamMsg, error) { |  | ||||||
| 	return mongoutil.FindOne[*model.StreamMsg](ctx, m.coll, bson.M{"client_msg_id": clientMsgID}) |  | ||||||
| } |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| package database |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type StreamMsg interface { |  | ||||||
| 	CreateStreamMsg(ctx context.Context, model *model.StreamMsg) error |  | ||||||
| 	AppendStreamMsg(ctx context.Context, clientMsgID string, startIndex int, packets []string, end bool, deadlineTime time.Time) error |  | ||||||
| 	GetStreamMsg(ctx context.Context, clientMsgID string) (*model.StreamMsg, error) |  | ||||||
| } |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| package model |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	StreamMsgStatusWait = 0 |  | ||||||
| 	StreamMsgStatusDone = 1 |  | ||||||
| 	StreamMsgStatusFail = 2 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type StreamMsg struct { |  | ||||||
| 	ClientMsgID    string    `bson:"client_msg_id"` |  | ||||||
| 	ConversationID string    `bson:"conversation_id"` |  | ||||||
| 	UserID         string    `bson:"user_id"` |  | ||||||
| 	Packets        []string  `bson:"packets"` |  | ||||||
| 	End            bool      `bson:"end"` |  | ||||||
| 	CreateTime     time.Time `bson:"create_time"` |  | ||||||
| 	DeadlineTime   time.Time `bson:"deadline_time"` |  | ||||||
| } |  | ||||||
| @ -2,9 +2,11 @@ package rpcli | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 
 | ||||||
|  | 	"google.golang.org/grpc" | ||||||
|  | 
 | ||||||
| 	"github.com/openimsdk/protocol/msg" | 	"github.com/openimsdk/protocol/msg" | ||||||
| 	"github.com/openimsdk/protocol/sdkws" | 	"github.com/openimsdk/protocol/sdkws" | ||||||
| 	"google.golang.org/grpc" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func NewMsgClient(cc grpc.ClientConnInterface) *MsgClient { | func NewMsgClient(cc grpc.ClientConnInterface) *MsgClient { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user