using System; using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json; using LeanCloud.Realtime.Protocol; using LeanCloud.Storage.Internal; using LeanCloud.Storage.Internal.Codec; using LeanCloud.Common; using Google.Protobuf; namespace LeanCloud.Realtime.Internal.Controller { internal class LCIMConversationController : LCIMController { internal LCIMConversationController(LCIMClient client) : base(client) { } internal async Task CreateConv( IEnumerable members = null, string name = null, bool transient = false, bool unique = true, bool temporary = false, int temporaryTtl = 86400, Dictionary properties = null) { GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Start); ConvCommand conv = new ConvCommand { Transient = transient, Unique = unique, }; if (members != null) { conv.M.AddRange(members); } if (!string.IsNullOrEmpty(name)) { conv.N = name; } if (temporary) { conv.TempConv = temporary; conv.TempConvTTL = temporaryTtl; } if (properties != null) { conv.Attr = new JsonObjectMessage { Data = JsonConvert.SerializeObject(LCEncoder.Encode(properties)) }; } if (Client.SignatureFactory != null) { LCIMSignature signature = Client.SignatureFactory.CreateStartConversationSignature(Client.Id, members); conv.S = signature.Signature; conv.T = signature.Timestamp; conv.N = signature.Nonce; } request.ConvMessage = conv; GenericCommand response = await Connection.SendRequest(request); string convId = response.ConvMessage.Cid; if (!Client.ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) { if (transient) { conversation = new LCIMChatRoom(Client); } else if (temporary) { conversation = new LCIMTemporaryConversation(Client); } else if (properties != null && properties.ContainsKey("system")) { conversation = new LCIMServiceConversation(Client); } else { conversation = new LCIMConversation(Client); } Client.ConversationDict[convId] = conversation; } // 合并请求数据 conversation.Name = name; conversation.MemberIdList = members?.ToList(); // 合并服务端推送的数据 conversation.MergeFrom(response.ConvMessage); return conversation; } internal async Task GetMembersCount(string convId) { ConvCommand conv = new ConvCommand { Cid = convId, }; GenericCommand command = Client.NewCommand(CommandType.Conv, OpType.Count); command.ConvMessage = conv; GenericCommand response = await Connection.SendRequest(command); return response.ConvMessage.Count; } internal async Task Read(string convId, LCIMMessage message) { ReadCommand read = new ReadCommand(); ReadTuple tuple = new ReadTuple { Cid = convId, Mid = message.Id, Timestamp = message.SentTimestamp }; read.Convs.Add(tuple); GenericCommand request = Client.NewCommand(CommandType.Read, OpType.Open); request.ReadMessage = read; await Client.Connection.SendRequest(request); } internal async Task> UpdateInfo(string convId, Dictionary attributes) { ConvCommand conv = new ConvCommand { Cid = convId, }; conv.Attr = new JsonObjectMessage { Data = JsonConvert.SerializeObject(attributes) }; GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Update); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); JsonObjectMessage attr = response.ConvMessage.AttrModified; // 更新自定义属性 if (attr != null) { Dictionary updatedAttr = JsonConvert.DeserializeObject>(attr.Data); return updatedAttr; } return null; } internal async Task AddMembers(string convId, IEnumerable clientIds) { ConvCommand conv = new ConvCommand { Cid = convId, }; conv.M.AddRange(clientIds); // 签名参数 if (Client.SignatureFactory != null) { LCIMSignature signature = Client.SignatureFactory.CreateConversationSignature(convId, Client.Id, clientIds, LCIMSignatureAction.Invite); conv.S = signature.Signature; conv.T = signature.Timestamp; conv.N = signature.Nonce; } GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Add); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); List allowedIds = response.ConvMessage.AllowedPids.ToList(); List errors = response.ConvMessage.FailedPids.ToList(); return NewPartiallySuccessResult(allowedIds, errors); } internal async Task RemoveMembers(string convId, IEnumerable removeIds) { ConvCommand conv = new ConvCommand { Cid = convId, }; conv.M.AddRange(removeIds); // 签名参数 if (Client.SignatureFactory != null) { LCIMSignature signature = Client.SignatureFactory.CreateConversationSignature(convId, Client.Id, removeIds, LCIMSignatureAction.Kick); conv.S = signature.Signature; conv.T = signature.Timestamp; conv.N = signature.Nonce; } GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Remove); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); List allowedIds = response.ConvMessage.AllowedPids.ToList(); List errors = response.ConvMessage.FailedPids.ToList(); return NewPartiallySuccessResult(allowedIds, errors); } internal async Task Mute(string convId) { ConvCommand conv = new ConvCommand { Cid = convId }; GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Mute); request.ConvMessage = conv; await Client.Connection.SendRequest(request); } internal async Task Unmute(string convId) { ConvCommand conv = new ConvCommand { Cid = convId }; GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Unmute); request.ConvMessage = conv; await Client.Connection.SendRequest(request); } internal async Task MuteMembers(string convId, IEnumerable clientIds) { if (clientIds == null || clientIds.Count() == 0) { throw new ArgumentNullException(nameof(clientIds)); } ConvCommand conv = new ConvCommand { Cid = convId }; conv.M.AddRange(clientIds); GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.AddShutup); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids); } internal async Task UnmuteMembers(string convId, IEnumerable clientIds) { ConvCommand conv = new ConvCommand { Cid = convId }; conv.M.AddRange(clientIds); GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Remove); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids); } internal async Task BlockMembers(string convId, IEnumerable clientIds) { BlacklistCommand blacklist = new BlacklistCommand { SrcCid = convId, }; blacklist.ToPids.AddRange(clientIds); if (Client.SignatureFactory != null) { LCIMSignature signature = Client.SignatureFactory.CreateBlacklistSignature(convId, Client.Id, clientIds, LCIMSignatureAction.ConversationBlockClients); blacklist.S = signature.Signature; blacklist.T = signature.Timestamp; blacklist.N = signature.Nonce; } GenericCommand request = Client.NewCommand(CommandType.Blacklist, OpType.Block); request.BlacklistMessage = blacklist; GenericCommand response = await Client.Connection.SendRequest(request); return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids); } internal async Task UnblockMembers(string convId, IEnumerable clientIds) { BlacklistCommand blacklist = new BlacklistCommand { SrcCid = convId, }; blacklist.ToPids.AddRange(clientIds); if (Client.SignatureFactory != null) { LCIMSignature signature = Client.SignatureFactory.CreateBlacklistSignature(convId, Client.Id, clientIds, LCIMSignatureAction.ConversationUnblockClients); blacklist.S = signature.Signature; blacklist.T = signature.Timestamp; blacklist.N = signature.Nonce; } GenericCommand request = Client.NewCommand(CommandType.Blacklist, OpType.Unblock); request.BlacklistMessage = blacklist; GenericCommand response = await Client.Connection.SendRequest(request); return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids); } internal async Task UpdateMemberRole(string convId, string memberId, string role) { ConvCommand conv = new ConvCommand { Cid = convId, TargetClientId = memberId, Info = new ConvMemberInfo { Pid = memberId, Role = role } }; GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.MemberInfoUpdate); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); } internal async Task> GetAllMemberInfo(string convId) { string path = "classes/_ConversationMemberInfo"; string token = await Client.SessionController.GetToken(); Dictionary headers = new Dictionary { { "X-LC-IM-Session-Token", token } }; Dictionary queryParams = new Dictionary { { "client_id", Client.Id }, { "cid", convId } }; Dictionary response = await LCApplication.HttpClient.Get>(path, headers: headers, queryParams: queryParams); List results = response["results"] as List; List memberList = new List(); foreach (Dictionary item in results) { LCIMConversationMemberInfo member = new LCIMConversationMemberInfo { ConversationId = item["cid"] as string, MemberId = item["clientId"] as string, Role = item["role"] as string }; memberList.Add(member); } return memberList; } internal async Task QueryMutedMembers(string convId, int limit = 10, string next = null) { ConvCommand conv = new ConvCommand { Cid = convId, Limit = limit, Next = next }; GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.QueryShutup); request.ConvMessage = conv; GenericCommand response = await Client.Connection.SendRequest(request); return new LCIMPageResult { Results = response.ConvMessage.M.ToList(), Next = response.ConvMessage.Next }; } internal async Task QueryBlockedMembers(string convId, int limit = 10, string next = null) { BlacklistCommand black = new BlacklistCommand { SrcCid = convId, Limit = limit, Next = next }; GenericCommand request = Client.NewCommand(CommandType.Blacklist, OpType.Query); request.BlacklistMessage = black; GenericCommand response = await Client.Connection.SendRequest(request); return new LCIMPageResult { Results = response.BlacklistMessage.BlockedPids.ToList(), Next = response.BlacklistMessage.Next }; } internal async Task> Find(LCIMConversationQuery query) { GenericCommand command = new GenericCommand { Cmd = CommandType.Conv, Op = OpType.Query, AppId = LCApplication.AppId, PeerId = Client.Id, }; ConvCommand convMessage = new ConvCommand(); string where = query.Condition.BuildWhere(); if (!string.IsNullOrEmpty(where)) { try { convMessage.Where = new JsonObjectMessage { Data = where }; } catch (Exception e) { LCLogger.Error(e.Message); } } command.ConvMessage = convMessage; GenericCommand response = await Connection.SendRequest(command); JsonObjectMessage results = response.ConvMessage.Results; List convs = JsonConvert.DeserializeObject>(results.Data, new LCJsonConverter()); List convList = convs.Select(item => { Dictionary conv = item as Dictionary; string convId = conv["objectId"] as string; if (!Client.ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) { // TODO 解析是哪种类型的对话 conversation = new LCIMConversation(Client); Client.ConversationDict[convId] = conversation; } conversation.MergeFrom(conv); return conversation; }).ToList(); return convList; } internal async Task> GetTemporaryConversations(IEnumerable convIds) { ConvCommand convMessage = new ConvCommand(); convMessage.TempConvIds.AddRange(convIds); GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Query); request.ConvMessage = convMessage; GenericCommand response = await Connection.SendRequest(request); JsonObjectMessage results = response.ConvMessage.Results; List convs = JsonConvert.DeserializeObject>(results.Data, new LCJsonConverter()); List convList = convs.Select(item => { LCIMTemporaryConversation temporaryConversation = new LCIMTemporaryConversation(Client); temporaryConversation.MergeFrom(item as Dictionary); return temporaryConversation; }).ToList(); return convList; } private LCIMPartiallySuccessResult NewPartiallySuccessResult(IEnumerable succesfulIds, IEnumerable errors) { LCIMPartiallySuccessResult result = new LCIMPartiallySuccessResult { SuccessfulClientIdList = succesfulIds.ToList() }; if (errors != null) { result.FailureList = new List(); foreach (ErrorCommand error in errors) { result.FailureList.Add(new LCIMOperationFailure(error)); } } return result; } internal override async Task OnNotification(GenericCommand notification) { ConvCommand conv = notification.ConvMessage; switch (notification.Op) { case OpType.Joined: await OnConversationJoined(conv); break; case OpType.MembersJoined: await OnConversationMembersJoined(conv); break; case OpType.Left: await OnConversationLeft(conv); break; case OpType.MembersLeft: await OnConversationMemberLeft(conv); break; case OpType.Updated: await OnConversationPropertiesUpdated(conv); break; case OpType.MemberInfoChanged: await OnConversationMemberInfoChanged(conv); break; default: break; } } private async Task OnConversationJoined(ConvCommand conv) { LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid); conversation.MergeFrom(conv); Client.OnInvited?.Invoke(conversation, conv.InitBy); } private async Task OnConversationMembersJoined(ConvCommand conv) { LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid); conversation.MergeFrom(conv); Client.OnMembersJoined?.Invoke(conversation, conv.M.ToList(), conv.InitBy); } private async Task OnConversationLeft(ConvCommand conv) { LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid); Client.OnKicked?.Invoke(conversation, conv.InitBy); } private async Task OnConversationMemberLeft(ConvCommand conv) { LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid); List leftIdList = conv.M.ToList(); Client.OnMembersLeft?.Invoke(conversation, leftIdList, conv.InitBy); } private async Task OnConversationPropertiesUpdated(ConvCommand conv) { LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid); Dictionary updatedAttr = JsonConvert.DeserializeObject>(conv.AttrModified.Data, new LCJsonConverter()); // 更新内存数据 conversation.MergeInfo(updatedAttr); Client.OnConversationInfoUpdated?.Invoke(conversation, updatedAttr, conv.InitBy); } private async Task OnConversationMemberInfoChanged(ConvCommand conv) { LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid); ConvMemberInfo memberInfo = conv.Info; Client.OnMemberInfoUpdated?.Invoke(conversation, memberInfo.Pid, memberInfo.Role, conv.InitBy); } } }