From 6781d4e94fb0bc0939014dff4e511f260dbc100e Mon Sep 17 00:00:00 2001 From: oneRain Date: Wed, 18 Mar 2020 16:21:29 +0800 Subject: [PATCH] =?UTF-8?q?*=20LCIMConversation.cs:=20chore:=20=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E6=B6=88=E6=81=AF=E7=9A=84=E7=BC=96=E8=A7=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * LCObject.cs: * LCIMClient.cs: * Program.cs: * LCIMMessage.cs: * LCConnection.cs: * LCIMTextMessage.cs: * LCIMFileMessage.cs: * LCIMVideoMessage.cs: * LCIMTypedMessage.cs: * LCIMImageMessage.cs: * LCIMAudioMessage.cs: * LCApplicationRealtimeExt.cs: * LCIMBinaryMessage.cs: * LCHttpClient.cs: * LCIMLocationMessage.cs: * LCJsonConverter.cs: * LCIMConversationQuery.cs: * LCWebSocketConnection.cs: --- Realtime/Conversation/LCIMConversation.cs | 47 ++++---- .../Conversation/LCIMConversationQuery.cs | 2 +- Realtime/Internal/LCConnection.cs | 111 ------------------ ...cketClient.cs => LCWebSocketConnection.cs} | 4 +- Realtime/LCApplicationRealtimeExt.cs | 1 - Realtime/LCIMClient.cs | 59 +++++++++- Realtime/Message/LCIMAudioMessage.cs | 21 +++- Realtime/Message/LCIMBinaryMessage.cs | 15 +-- Realtime/Message/LCIMFileMessage.cs | 43 +++++-- Realtime/Message/LCIMImageMessage.cs | 19 ++- Realtime/Message/LCIMLocationMessage.cs | 21 +++- Realtime/Message/LCIMMessage.cs | 18 ++- Realtime/Message/LCIMTextMessage.cs | 27 +++-- Realtime/Message/LCIMTypedMessage.cs | 31 +++-- Realtime/Message/LCIMVideoMessage.cs | 18 ++- Storage/Internal/Http/LCHttpClient.cs | 10 +- ...oudJsonConverter.cs => LCJsonConverter.cs} | 2 +- Storage/LCObject.cs | 2 +- Test/RealtimeConsole/Program.cs | 33 +++--- 19 files changed, 263 insertions(+), 221 deletions(-) delete mode 100644 Realtime/Internal/LCConnection.cs rename Realtime/Internal/WebSocket/{LCWebSocketClient.cs => LCWebSocketConnection.cs} (98%) rename Storage/Internal/Http/{LeanCloudJsonConverter.cs => LCJsonConverter.cs} (95%) diff --git a/Realtime/Conversation/LCIMConversation.cs b/Realtime/Conversation/LCIMConversation.cs index fb92742..3eba049 100644 --- a/Realtime/Conversation/LCIMConversation.cs +++ b/Realtime/Conversation/LCIMConversation.cs @@ -77,7 +77,7 @@ namespace LeanCloud.Realtime { }; GenericCommand command = client.NewCommand(CommandType.Conv, OpType.Count); command.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(command); + GenericCommand response = await client.connection.SendRequest(command); return response.ConvMessage.Count; } @@ -92,7 +92,7 @@ namespace LeanCloud.Realtime { }; GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Update); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); JsonObjectMessage attr = response.ConvMessage.AttrModified; // 更新自定义属性 if (attr != null) { @@ -122,7 +122,7 @@ namespace LeanCloud.Realtime { GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Add); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); List allowedIds = response.ConvMessage.AllowedPids.ToList(); List failedIds = response.ConvMessage.FailedPids.ToList(); // TODO 转化为返回 @@ -147,7 +147,7 @@ namespace LeanCloud.Realtime { GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); List allowedIds = response.ConvMessage.AllowedPids.ToList(); List failedIds = response.ConvMessage.FailedPids.ToList(); // TODO 转化为返回 @@ -180,11 +180,17 @@ namespace LeanCloud.Realtime { DirectCommand direct = new DirectCommand { FromPeerId = client.ClientId, Cid = Id, - Msg = message.Serialize(), }; + if (message is LCIMTypedMessage typedMessage) { + direct.Msg = JsonConvert.SerializeObject(typedMessage.Encode()); + } else if (message is LCIMBinaryMessage binaryMessage) { + direct.BinaryMsg = ByteString.CopyFrom(binaryMessage.Data); + } else { + throw new ArgumentException("Message MUST BE LCIMTypedMessage or LCIMBinaryMessage."); + } GenericCommand command = client.NewDirectCommand(); command.DirectMessage = direct; - GenericCommand response = await client.client.SendRequest(command); + GenericCommand response = await client.connection.SendRequest(command); // 消息发送应答 AckCommand ack = response.AckMessage; message.Id = ack.Uid; @@ -202,7 +208,7 @@ namespace LeanCloud.Realtime { }; GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Mute); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); IsMute = true; return this; } @@ -217,7 +223,7 @@ namespace LeanCloud.Realtime { }; GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Unmute); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); IsMute = false; return this; } @@ -237,7 +243,7 @@ namespace LeanCloud.Realtime { conv.M.AddRange(clientIds); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.AddShutup); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids); } @@ -256,7 +262,7 @@ namespace LeanCloud.Realtime { conv.M.AddRange(clientIds); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids); } @@ -275,7 +281,7 @@ namespace LeanCloud.Realtime { blacklist.ToPids.AddRange(clientIds); GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Block); request.BlacklistMessage = blacklist; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids); } @@ -289,7 +295,7 @@ namespace LeanCloud.Realtime { blacklist.ToPids.AddRange(clientIds); GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Unblock); request.BlacklistMessage = blacklist; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids); } @@ -311,7 +317,7 @@ namespace LeanCloud.Realtime { patch.Patches.Add(item); GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify); request.PatchMessage = patch; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return null; } @@ -335,11 +341,10 @@ namespace LeanCloud.Realtime { Timestamp = oldMessage.DeliveredTimestamp, Recall = false, }; - if (newMessage.GetText() != null) { - item.Data = newMessage.GetText(); - } - if (newMessage.GetBytes() != null) { - item.BinaryMsg = ByteString.CopyFrom(newMessage.GetBytes()); + if (newMessage is LCIMTypedMessage typedMessage) { + item.Data = JsonConvert.SerializeObject(typedMessage.Encode()); + } else if (newMessage is LCIMBinaryMessage binaryMessage) { + item.BinaryMsg = ByteString.CopyFrom(binaryMessage.Data); } if (newMessage.MentionList != null) { item.MentionPids.AddRange(newMessage.MentionList); @@ -350,7 +355,7 @@ namespace LeanCloud.Realtime { patch.Patches.Add(item); GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify); request.PatchMessage = patch; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return null; } @@ -371,7 +376,7 @@ namespace LeanCloud.Realtime { }; GenericCommand request = client.NewCommand(CommandType.Conv, OpType.MemberInfoUpdate); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); // TODO 同步 members return this; @@ -422,7 +427,7 @@ namespace LeanCloud.Realtime { }; GenericCommand request = client.NewCommand(CommandType.Conv, OpType.QueryShutup); request.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(request); + GenericCommand response = await client.connection.SendRequest(request); return new LCIMPageResult { Results = response.ConvMessage.M.ToList(), Next = response.ConvMessage.Next diff --git a/Realtime/Conversation/LCIMConversationQuery.cs b/Realtime/Conversation/LCIMConversationQuery.cs index cff39a0..95e6535 100644 --- a/Realtime/Conversation/LCIMConversationQuery.cs +++ b/Realtime/Conversation/LCIMConversationQuery.cs @@ -245,7 +245,7 @@ namespace LeanCloud.Realtime { conv.Where = JsonObjectMessage.Parser.ParseJson(where); } command.ConvMessage = conv; - GenericCommand response = await client.client.SendRequest(command); + GenericCommand response = await client.connection.SendRequest(command); JsonObjectMessage results = response.ConvMessage.Results; List convList = null; // TODO 反序列化 diff --git a/Realtime/Internal/LCConnection.cs b/Realtime/Internal/LCConnection.cs deleted file mode 100644 index a66bbbf..0000000 --- a/Realtime/Internal/LCConnection.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Net.WebSockets; -using Google.Protobuf; -using LeanCloud.Realtime.Protocol; -using LeanCloud.Storage; - -namespace LeanCloud.Realtime.Internal { - internal class LCConnection { - private const int KEEP_ALIVE_INTERVAL = 10; - private const int RECV_BUFFER_SIZE = 1024; - - private ClientWebSocket ws; - - private volatile int requestI = 1; - - private readonly object requestILock = new object(); - - private readonly Dictionary> responses; - - internal LCConnection() { - responses = new Dictionary>(); - } - - internal async Task Connect() { - ws = new ClientWebSocket(); - ws.Options.AddSubProtocol("lc.protobuf2.3"); - ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(KEEP_ALIVE_INTERVAL); - await ws.ConnectAsync(new Uri(""), default); - } - - internal async Task SendRequest(GenericCommand request) { - request.I = RequestI; - ArraySegment bytes = new ArraySegment(request.ToByteArray()); - try { - await ws.SendAsync(bytes, WebSocketMessageType.Binary, true, default); - } catch (Exception e) { - // TODO 发送消息异常 - - } - } - - internal async Task Close() { - await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "1", default); - } - - private async Task StartReceive() { - byte[] buffer = new byte[RECV_BUFFER_SIZE]; - try { - while (ws.State == WebSocketState.Open) { - byte[] data = new byte[0]; - WebSocketReceiveResult result; - do { - result = await ws.ReceiveAsync(new ArraySegment(buffer), default); - if (result.MessageType == WebSocketMessageType.Close) { - // TODO 区分主动断开和被动断开 - - return; - } - // 拼合 WebSocket Frame - byte[] oldData = data; - data = new byte[data.Length + result.Count]; - Array.Copy(oldData, data, oldData.Length); - Array.Copy(buffer, 0, data, oldData.Length, result.Count); - } while (!result.EndOfMessage); - try { - GenericCommand command = GenericCommand.Parser.ParseFrom(data); - HandleCommand(command); - } catch (Exception e) { - // 解析消息错误 - - } - } - } catch (Exception e) { - // TODO 连接断开 - - } - } - - private void HandleCommand(GenericCommand command) { - if (command.HasI) { - // 应答 - if (responses.TryGetValue(command.I, out TaskCompletionSource tcs)) { - if (command.HasErrorMessage) { - // 错误 - ErrorCommand error = command.ErrorMessage; - int code = error.Code; - string detail = error.Detail; - // TODO 包装成异常抛出 - LCException exception = new LCException(code, detail); - tcs.SetException(exception); - } else { - tcs.SetResult(command); - } - } - } else { - // 通知 - - } - } - - private int RequestI { - get { - lock (requestILock) { - return requestI++; - }; - } - } - } -} diff --git a/Realtime/Internal/WebSocket/LCWebSocketClient.cs b/Realtime/Internal/WebSocket/LCWebSocketConnection.cs similarity index 98% rename from Realtime/Internal/WebSocket/LCWebSocketClient.cs rename to Realtime/Internal/WebSocket/LCWebSocketConnection.cs index cc056fa..a78632c 100644 --- a/Realtime/Internal/WebSocket/LCWebSocketClient.cs +++ b/Realtime/Internal/WebSocket/LCWebSocketConnection.cs @@ -9,7 +9,7 @@ using LeanCloud.Common; using Google.Protobuf; namespace LeanCloud.Realtime.Internal.WebSocket { - internal class LCWebSocketClient { + internal class LCWebSocketConnection { private const int KEEP_ALIVE_INTERVAL = 10; private const int RECV_BUFFER_SIZE = 1024; @@ -25,7 +25,7 @@ namespace LeanCloud.Realtime.Internal.WebSocket { get; set; } - internal LCWebSocketClient() { + internal LCWebSocketConnection() { responses = new Dictionary>(); } diff --git a/Realtime/LCApplicationRealtimeExt.cs b/Realtime/LCApplicationRealtimeExt.cs index ea87775..73f48ad 100644 --- a/Realtime/LCApplicationRealtimeExt.cs +++ b/Realtime/LCApplicationRealtimeExt.cs @@ -5,7 +5,6 @@ using LeanCloud.Realtime.Internal; namespace LeanCloud { public static class LCApplicationRealtimeExt { - static LCConnection connection; public static async Task CreateIMClient(this LCApplication application, string clientId) { if (string.IsNullOrEmpty(clientId)) { diff --git a/Realtime/LCIMClient.cs b/Realtime/LCIMClient.cs index 5941e2e..c8a6f0e 100644 --- a/Realtime/LCIMClient.cs +++ b/Realtime/LCIMClient.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json; namespace LeanCloud.Realtime { public class LCIMClient { - internal LCWebSocketClient client; + internal LCWebSocketConnection connection; private Dictionary conversationDict; @@ -75,6 +75,10 @@ namespace LeanCloud.Realtime { get; set; } + public Action OnMessageReceived { + get; set; + } + public LCIMClient(string clientId) { ClientId = clientId; conversationDict = new Dictionary(); @@ -85,14 +89,14 @@ namespace LeanCloud.Realtime { /// /// public async Task Open() { - client = new LCWebSocketClient { + connection = new LCWebSocketConnection { OnNotification = OnNotification }; - await client.Connect(); + await connection.Connect(); // Open Session GenericCommand request = NewCommand(CommandType.Session, OpType.Open); request.SessionMessage = new SessionCommand(); - GenericCommand response = await client.SendRequest(request); + GenericCommand response = await connection.SendRequest(request); SessionToken = response.SessionMessage.St; } @@ -101,7 +105,7 @@ namespace LeanCloud.Realtime { /// /// public async Task Close() { - await client.Close(); + await connection.Close(); } public async Task CreateChatRoom( @@ -154,7 +158,7 @@ namespace LeanCloud.Realtime { }; } command.ConvMessage = conv; - GenericCommand response = await client.SendRequest(command); + GenericCommand response = await connection.SendRequest(command); LCIMConversation conversation = GetOrCreateConversation(response.ConvMessage.Cid); conversation.MergeFrom(response.ConvMessage); conversationDict[conversation.Id] = conversation; @@ -210,6 +214,9 @@ namespace LeanCloud.Realtime { case CommandType.Conv: OnConversationNotification(notification); break; + case CommandType.Direct: + OnDirectNotification(notification.DirectMessage); + break; default: break; } @@ -270,6 +277,46 @@ namespace LeanCloud.Realtime { } } + private void OnDirectNotification(DirectCommand direct) { + LCIMMessage message = null; + if (direct.HasBinaryMsg) { + // 二进制消息 + byte[] bytes = direct.BinaryMsg.ToByteArray(); + message = new LCIMBinaryMessage(bytes); + } else { + // 文本消息 + string messageData = direct.Msg; + Dictionary msg = JsonConvert.DeserializeObject>(messageData); + int msgType = (int)(long)msg["_lctype"]; + switch (msgType) { + case -1: + message = new LCIMTextMessage(); + break; + case -2: + message = new LCIMImageMessage(); + break; + case -3: + message = new LCIMAudioMessage(); + break; + case -4: + message = new LCIMVideoMessage(); + break; + case -5: + message = new LCIMLocationMessage(); + break; + case -6: + message = new LCIMFileMessage(); + break; + default: + break; + } + message.Decode(direct); + } + // TODO 获取对话 + + OnMessageReceived?.Invoke(null, message); + } + private LCIMConversation GetOrCreateConversation(string convId) { if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) { conversation = new LCIMConversation(this); diff --git a/Realtime/Message/LCIMAudioMessage.cs b/Realtime/Message/LCIMAudioMessage.cs index f170be9..3dcece3 100644 --- a/Realtime/Message/LCIMAudioMessage.cs +++ b/Realtime/Message/LCIMAudioMessage.cs @@ -1,17 +1,32 @@ -using LeanCloud.Storage; +using System.Collections.Generic; +using LeanCloud.Storage; namespace LeanCloud.Realtime { public class LCIMAudioMessage : LCIMFileMessage { public double Duration { get { - if (double.TryParse("duration", out double duration)) { + if (double.TryParse(File.MetaData["duration"] as string, out double duration)) { return duration; } return 0; } } - public LCIMAudioMessage(LCFile file) : base(file) { + internal LCIMAudioMessage() { } + + public LCIMAudioMessage(LCFile file) : base(file) { + + } + + internal override Dictionary Encode() { + Dictionary data = base.Encode(); + Dictionary fileData = data["_lcfile"] as Dictionary; + Dictionary metaData = fileData["metaData"] as Dictionary; + metaData["duration"] = File.MetaData["duration"]; + return data; + } + + internal override int MessageType => AudioMessageType; } } diff --git a/Realtime/Message/LCIMBinaryMessage.cs b/Realtime/Message/LCIMBinaryMessage.cs index 2707661..c720e07 100644 --- a/Realtime/Message/LCIMBinaryMessage.cs +++ b/Realtime/Message/LCIMBinaryMessage.cs @@ -1,25 +1,14 @@ using System; +using System.Collections.Generic; namespace LeanCloud.Realtime { public class LCIMBinaryMessage : LCIMMessage { public byte[] Data { - get; set; + get; internal set; } public LCIMBinaryMessage(byte[] data) { Data = data; } - - internal override string Serialize() { - throw new NotImplementedException(); - } - - internal override string GetText() { - return null; - } - - internal override byte[] GetBytes() { - return Data; - } } } diff --git a/Realtime/Message/LCIMFileMessage.cs b/Realtime/Message/LCIMFileMessage.cs index ef22a5d..1861400 100644 --- a/Realtime/Message/LCIMFileMessage.cs +++ b/Realtime/Message/LCIMFileMessage.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; using LeanCloud.Storage; namespace LeanCloud.Realtime { @@ -30,24 +29,48 @@ namespace LeanCloud.Realtime { } } - public LCIMFileMessage(LCFile file) : base(null) { + internal LCIMFileMessage() : base() { + + } + + public LCIMFileMessage(LCFile file) : base() { File = file; } - internal override string Serialize() { + internal override Dictionary Encode() { if (File == null) { throw new Exception("File MUST NOT be null before sent."); } - File.MetaData["name"] = File.Name; - File.MetaData["format"] = File.MimeType; - Dictionary data = new Dictionary { + Dictionary fileData = new Dictionary { { "objId", File.ObjectId }, { "url", File.Url }, - { "metaData", File.MetaData } + { "metaData", new Dictionary { + { "name", File.Name }, + { "format", File.MimeType }, + { "size", File.MetaData["size"] } + } } }; - return JsonConvert.SerializeObject(new Dictionary { - { "_lcfile", data } - }); + Dictionary data = base.Encode(); + data["_lcfile"] = fileData; + return data; } + + protected override void DecodeMessageData(Dictionary msgData) { + base.DecodeMessageData(msgData); + Dictionary fileData = msgData["_lcfile"] as Dictionary; + string objectId = fileData["objId"] as string; + File = LCObject.CreateWithoutData(LCFile.CLASS_NAME, objectId) as LCFile; + if (fileData.TryGetValue("name", out object name)) { + File.Name = name as string; + } + if (fileData.TryGetValue("url", out object url)) { + File.Url = url as string; + } + if (fileData.TryGetValue("metaData", out object metaData)) { + File.MetaData = metaData as Dictionary; + } + } + + internal override int MessageType => FileMessageType; } } diff --git a/Realtime/Message/LCIMImageMessage.cs b/Realtime/Message/LCIMImageMessage.cs index db7e4b0..5e13028 100644 --- a/Realtime/Message/LCIMImageMessage.cs +++ b/Realtime/Message/LCIMImageMessage.cs @@ -1,4 +1,4 @@ -using System; +using System.Collections.Generic; using LeanCloud.Storage; namespace LeanCloud.Realtime { @@ -21,7 +21,22 @@ namespace LeanCloud.Realtime { } } - public LCIMImageMessage(LCFile file) : base(file) { + internal LCIMImageMessage() : base() { } + + public LCIMImageMessage(LCFile file) : base(file) { + + } + + internal override Dictionary Encode() { + Dictionary data = base.Encode(); + Dictionary fileData = data["_lcfile"] as Dictionary; + Dictionary metaData = fileData["metaData"] as Dictionary; + metaData["width"] = File.MetaData["width"]; + metaData["height"] = File.MetaData["height"]; + return data; + } + + internal override int MessageType => ImageMessageType; } } diff --git a/Realtime/Message/LCIMLocationMessage.cs b/Realtime/Message/LCIMLocationMessage.cs index b7106a4..848e820 100644 --- a/Realtime/Message/LCIMLocationMessage.cs +++ b/Realtime/Message/LCIMLocationMessage.cs @@ -8,18 +8,29 @@ namespace LeanCloud.Realtime { get; set; } + internal LCIMLocationMessage() { + } + public LCIMLocationMessage(LCGeoPoint locaction) : base(null) { Location = locaction; } - internal override string Serialize() { - Dictionary data = new Dictionary { + internal override Dictionary Encode() { + Dictionary data = base.Encode(); + Dictionary locationData = new Dictionary { { "longitude", Location.Longitude }, { "latitude", Location.Latitude } }; - return JsonConvert.SerializeObject(new Dictionary { - { "_lcloc", data } - }); + data["_lcloc"] = locationData; + return data; } + + protected override void DecodeMessageData(Dictionary msgData) { + base.DecodeMessageData(msgData); + Dictionary locationData = msgData["_lcloc"] as Dictionary; + Location = new LCGeoPoint((double)locationData["latitude"], (double)locationData["longitude"]); + } + + internal override int MessageType => LocationMessageType; } } diff --git a/Realtime/Message/LCIMMessage.cs b/Realtime/Message/LCIMMessage.cs index 82531e8..07f8cdb 100644 --- a/Realtime/Message/LCIMMessage.cs +++ b/Realtime/Message/LCIMMessage.cs @@ -1,8 +1,16 @@ using System; using System.Collections.Generic; +using LeanCloud.Realtime.Protocol; namespace LeanCloud.Realtime { public abstract class LCIMMessage { + internal const int TextMessageType = -1; + internal const int ImageMessageType = -2; + internal const int AudioMessageType = -3; + internal const int VideoMessageType = -4; + internal const int LocationMessageType = -5; + internal const int FileMessageType = -6; + public string ConversationId { get; set; } @@ -71,9 +79,11 @@ namespace LeanCloud.Realtime { } - internal abstract string Serialize(); - - internal abstract string GetText(); - internal abstract byte[] GetBytes(); + internal virtual void Decode(DirectCommand direct) { + ConversationId = direct.Cid; + Id = direct.Id; + FromClientId = direct.FromPeerId; + DeliveredTimestamp = direct.Timestamp; + } } } diff --git a/Realtime/Message/LCIMTextMessage.cs b/Realtime/Message/LCIMTextMessage.cs index ba4b852..bf831b3 100644 --- a/Realtime/Message/LCIMTextMessage.cs +++ b/Realtime/Message/LCIMTextMessage.cs @@ -1,24 +1,33 @@ -using System; -using Newtonsoft.Json; +using System.Collections.Generic; namespace LeanCloud.Realtime { public class LCIMTextMessage : LCIMTypedMessage { - const int TextMessageType = -1; - public string Text { get; set; } - public LCIMTextMessage(string text) : base(TextMessageType) { + internal LCIMTextMessage() { + } + + public LCIMTextMessage(string text) : base() { Text = text; } - internal override string Serialize() { - return Text; + internal override Dictionary Encode() { + Dictionary data = base.Encode(); + if (!string.IsNullOrEmpty(Text)) { + data["_lctext"] = Text; + } + return data; } - internal override string GetText() { - return Text; + protected override void DecodeMessageData(Dictionary msgData) { + base.DecodeMessageData(msgData); + if (msgData.TryGetValue("_lctext", out object value)) { + Text = value as string; + } } + + internal override int MessageType => TextMessageType; } } diff --git a/Realtime/Message/LCIMTypedMessage.cs b/Realtime/Message/LCIMTypedMessage.cs index 047cd8e..1f595e0 100644 --- a/Realtime/Message/LCIMTypedMessage.cs +++ b/Realtime/Message/LCIMTypedMessage.cs @@ -1,23 +1,32 @@ -using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using LeanCloud.Realtime.Protocol; +using LeanCloud.Storage.Internal; namespace LeanCloud.Realtime { - public class LCIMTypedMessage : LCIMMessage { - protected int type; + public abstract class LCIMTypedMessage : LCIMMessage { + protected LCIMTypedMessage() { - protected LCIMTypedMessage(int type) { - this.type = type; } - internal override string Serialize() { - throw new NotImplementedException(); + internal virtual int MessageType { + get; private set; } - internal override string GetText() { - return null; + internal virtual Dictionary Encode() { + return new Dictionary { + { "_lctype", MessageType } + }; } - internal override byte[] GetBytes() { - return null; + internal override void Decode(DirectCommand direct) { + base.Decode(direct); + Dictionary msgData = JsonConvert.DeserializeObject>(direct.Msg, new LCJsonConverter()); + DecodeMessageData(msgData); + } + + protected virtual void DecodeMessageData(Dictionary msgData) { + MessageType = (int)msgData["_lctype"]; } } } diff --git a/Realtime/Message/LCIMVideoMessage.cs b/Realtime/Message/LCIMVideoMessage.cs index 6e04688..95c17a2 100644 --- a/Realtime/Message/LCIMVideoMessage.cs +++ b/Realtime/Message/LCIMVideoMessage.cs @@ -1,4 +1,5 @@ -using LeanCloud.Storage; +using System.Collections.Generic; +using LeanCloud.Storage; namespace LeanCloud.Realtime { public class LCIMVideoMessage : LCIMFileMessage { @@ -11,8 +12,23 @@ namespace LeanCloud.Realtime { } } + internal LCIMVideoMessage() { + } + public LCIMVideoMessage(LCFile file) : base(file) { } + + internal override Dictionary Encode() { + Dictionary data = base.Encode(); + Dictionary fileData = data["_lcfile"] as Dictionary; + Dictionary metaData = fileData["metaData"] as Dictionary; + metaData["width"] = File.MetaData["width"]; + metaData["height"] = File.MetaData["height"]; + metaData["duration"] = File.MetaData["duration"]; + return data; + } + + internal override int MessageType => VideoMessageType; } } diff --git a/Storage/Internal/Http/LCHttpClient.cs b/Storage/Internal/Http/LCHttpClient.cs index 83fb444..9351f0d 100644 --- a/Storage/Internal/Http/LCHttpClient.cs +++ b/Storage/Internal/Http/LCHttpClient.cs @@ -61,7 +61,7 @@ namespace LeanCloud.Storage.Internal.Http { LCHttpUtils.PrintResponse(response, resultString); if (response.IsSuccessStatusCode) { - T ret = JsonConvert.DeserializeObject(resultString, new LeanCloudJsonConverter()); + T ret = JsonConvert.DeserializeObject(resultString, new LCJsonConverter()); return ret; } throw HandleErrorResponse(response.StatusCode, resultString); @@ -94,7 +94,7 @@ namespace LeanCloud.Storage.Internal.Http { LCHttpUtils.PrintResponse(response, resultString); if (response.IsSuccessStatusCode) { - T ret = JsonConvert.DeserializeObject(resultString, new LeanCloudJsonConverter()); + T ret = JsonConvert.DeserializeObject(resultString, new LCJsonConverter()); return ret; } throw HandleErrorResponse(response.StatusCode, resultString); @@ -127,7 +127,7 @@ namespace LeanCloud.Storage.Internal.Http { LCHttpUtils.PrintResponse(response, resultString); if (response.IsSuccessStatusCode) { - T ret = JsonConvert.DeserializeObject(resultString, new LeanCloudJsonConverter()); + T ret = JsonConvert.DeserializeObject(resultString, new LCJsonConverter()); return ret; } throw HandleErrorResponse(response.StatusCode, resultString); @@ -150,7 +150,7 @@ namespace LeanCloud.Storage.Internal.Http { LCHttpUtils.PrintResponse(response, resultString); if (response.IsSuccessStatusCode) { - Dictionary ret = JsonConvert.DeserializeObject>(resultString, new LeanCloudJsonConverter()); + Dictionary ret = JsonConvert.DeserializeObject>(resultString, new LCJsonConverter()); return; } throw HandleErrorResponse(response.StatusCode, resultString); @@ -161,7 +161,7 @@ namespace LeanCloud.Storage.Internal.Http { string message = responseContent; try { // 尝试获取 LeanCloud 返回错误信息 - Dictionary error = JsonConvert.DeserializeObject>(responseContent, new LeanCloudJsonConverter()); + Dictionary error = JsonConvert.DeserializeObject>(responseContent, new LCJsonConverter()); code = (int)error["code"]; message = error["error"].ToString(); } catch (Exception e) { diff --git a/Storage/Internal/Http/LeanCloudJsonConverter.cs b/Storage/Internal/Http/LCJsonConverter.cs similarity index 95% rename from Storage/Internal/Http/LeanCloudJsonConverter.cs rename to Storage/Internal/Http/LCJsonConverter.cs index ed5fd05..d79bd9d 100644 --- a/Storage/Internal/Http/LeanCloudJsonConverter.cs +++ b/Storage/Internal/Http/LCJsonConverter.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Newtonsoft.Json; namespace LeanCloud.Storage.Internal { - public class LeanCloudJsonConverter : JsonConverter { + public class LCJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(object); } diff --git a/Storage/LCObject.cs b/Storage/LCObject.cs index 2b0e3e9..f3cf750 100644 --- a/Storage/LCObject.cs +++ b/Storage/LCObject.cs @@ -86,7 +86,7 @@ namespace LeanCloud.Storage { if (string.IsNullOrEmpty(objectId)) { throw new ArgumentNullException(nameof(objectId)); } - LCObject obj = new LCObject(className); + LCObject obj = Create(className); obj.data.ObjectId = objectId; obj.isNew = false; return obj; diff --git a/Test/RealtimeConsole/Program.cs b/Test/RealtimeConsole/Program.cs index 0224114..6ec85c1 100644 --- a/Test/RealtimeConsole/Program.cs +++ b/Test/RealtimeConsole/Program.cs @@ -36,26 +36,28 @@ namespace RealtimeConsole { }; LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com"); - LCIMClient client = new LCIMClient("hello123"); + LCIMClient hello = new LCIMClient("hello"); - try { - await client.Open(); - Console.WriteLine($"End {Thread.CurrentThread.ManagedThreadId}"); - } catch (Exception e) { - Console.WriteLine(e.Message); - } + await hello.Open(); - client.OnInvited = (conv, initBy) => { + hello.OnInvited = (conv, initBy) => { Console.WriteLine($"on invited: {initBy}"); }; - client.OnMembersJoined = (conv, memberList, initBy) => { + hello.OnMembersJoined = (conv, memberList, initBy) => { Console.WriteLine($"on members joined: {initBy}"); }; List memberIdList = new List { "world", "code" }; string name = Guid.NewGuid().ToString(); - LCIMConversation conversation = await client.CreateConversation(memberIdList, name: name, unique: true); + LCIMConversation conversation = await hello.CreateConversation(memberIdList, name: name, unique: true); + + LCIMClient world = new LCIMClient("world"); + await world.Open(); + + world.OnMessageReceived = (conv, message) => { + Console.WriteLine(message); + }; //LCIMTextMessage textMessage = new LCIMTextMessage("hello, world"); //await conversation.Send(textMessage); @@ -77,10 +79,13 @@ namespace RealtimeConsole { //LCIMTextMessage textMessage = new LCIMTextMessage("hello, world"); //await conversation.Send(textMessage); - LCFile file = new LCFile("avatar", "../../../Storage.Test/assets/hello.png"); - await file.Save(); - LCIMImageMessage imageMessage = new LCIMImageMessage(file); - await conversation.Send(imageMessage); + //LCFile file = new LCFile("avatar", "../../../Storage.Test/assets/hello.png"); + //file.MetaData["width"] = 225; + //file.MetaData["height"] = 225; + //file.MetaData["size"] = 1186; + //await file.Save(); + //LCIMImageMessage imageMessage = new LCIMImageMessage(file); + //await conversation.Send(imageMessage); LCGeoPoint location = new LCGeoPoint(11, 12); LCIMLocationMessage locationMessage = new LCIMLocationMessage(location);