* LCIMConversation.cs: chore: 完善消息的编解码

* 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:
oneRain 2020-03-18 16:21:29 +08:00
parent 29a84b8afb
commit 6781d4e94f
19 changed files with 263 additions and 221 deletions

View File

@ -77,7 +77,7 @@ namespace LeanCloud.Realtime {
}; };
GenericCommand command = client.NewCommand(CommandType.Conv, OpType.Count); GenericCommand command = client.NewCommand(CommandType.Conv, OpType.Count);
command.ConvMessage = conv; command.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(command); GenericCommand response = await client.connection.SendRequest(command);
return response.ConvMessage.Count; return response.ConvMessage.Count;
} }
@ -92,7 +92,7 @@ namespace LeanCloud.Realtime {
}; };
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Update); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Update);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
JsonObjectMessage attr = response.ConvMessage.AttrModified; JsonObjectMessage attr = response.ConvMessage.AttrModified;
// 更新自定义属性 // 更新自定义属性
if (attr != null) { if (attr != null) {
@ -122,7 +122,7 @@ namespace LeanCloud.Realtime {
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Add); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Add);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
List<string> allowedIds = response.ConvMessage.AllowedPids.ToList(); List<string> allowedIds = response.ConvMessage.AllowedPids.ToList();
List<ErrorCommand> failedIds = response.ConvMessage.FailedPids.ToList(); List<ErrorCommand> failedIds = response.ConvMessage.FailedPids.ToList();
// TODO 转化为返回 // TODO 转化为返回
@ -147,7 +147,7 @@ namespace LeanCloud.Realtime {
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
List<string> allowedIds = response.ConvMessage.AllowedPids.ToList(); List<string> allowedIds = response.ConvMessage.AllowedPids.ToList();
List<ErrorCommand> failedIds = response.ConvMessage.FailedPids.ToList(); List<ErrorCommand> failedIds = response.ConvMessage.FailedPids.ToList();
// TODO 转化为返回 // TODO 转化为返回
@ -180,11 +180,17 @@ namespace LeanCloud.Realtime {
DirectCommand direct = new DirectCommand { DirectCommand direct = new DirectCommand {
FromPeerId = client.ClientId, FromPeerId = client.ClientId,
Cid = Id, 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(); GenericCommand command = client.NewDirectCommand();
command.DirectMessage = direct; command.DirectMessage = direct;
GenericCommand response = await client.client.SendRequest(command); GenericCommand response = await client.connection.SendRequest(command);
// 消息发送应答 // 消息发送应答
AckCommand ack = response.AckMessage; AckCommand ack = response.AckMessage;
message.Id = ack.Uid; message.Id = ack.Uid;
@ -202,7 +208,7 @@ namespace LeanCloud.Realtime {
}; };
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Mute); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Mute);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
IsMute = true; IsMute = true;
return this; return this;
} }
@ -217,7 +223,7 @@ namespace LeanCloud.Realtime {
}; };
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Unmute); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Unmute);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
IsMute = false; IsMute = false;
return this; return this;
} }
@ -237,7 +243,7 @@ namespace LeanCloud.Realtime {
conv.M.AddRange(clientIds); conv.M.AddRange(clientIds);
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.AddShutup); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.AddShutup);
request.ConvMessage = conv; 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); return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids);
} }
@ -256,7 +262,7 @@ namespace LeanCloud.Realtime {
conv.M.AddRange(clientIds); conv.M.AddRange(clientIds);
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove);
request.ConvMessage = conv; 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); return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids);
} }
@ -275,7 +281,7 @@ namespace LeanCloud.Realtime {
blacklist.ToPids.AddRange(clientIds); blacklist.ToPids.AddRange(clientIds);
GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Block); GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Block);
request.BlacklistMessage = blacklist; 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); return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids);
} }
@ -289,7 +295,7 @@ namespace LeanCloud.Realtime {
blacklist.ToPids.AddRange(clientIds); blacklist.ToPids.AddRange(clientIds);
GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Unblock); GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Unblock);
request.BlacklistMessage = blacklist; 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); return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids);
} }
@ -311,7 +317,7 @@ namespace LeanCloud.Realtime {
patch.Patches.Add(item); patch.Patches.Add(item);
GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify); GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify);
request.PatchMessage = patch; request.PatchMessage = patch;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
return null; return null;
} }
@ -335,11 +341,10 @@ namespace LeanCloud.Realtime {
Timestamp = oldMessage.DeliveredTimestamp, Timestamp = oldMessage.DeliveredTimestamp,
Recall = false, Recall = false,
}; };
if (newMessage.GetText() != null) { if (newMessage is LCIMTypedMessage typedMessage) {
item.Data = newMessage.GetText(); item.Data = JsonConvert.SerializeObject(typedMessage.Encode());
} } else if (newMessage is LCIMBinaryMessage binaryMessage) {
if (newMessage.GetBytes() != null) { item.BinaryMsg = ByteString.CopyFrom(binaryMessage.Data);
item.BinaryMsg = ByteString.CopyFrom(newMessage.GetBytes());
} }
if (newMessage.MentionList != null) { if (newMessage.MentionList != null) {
item.MentionPids.AddRange(newMessage.MentionList); item.MentionPids.AddRange(newMessage.MentionList);
@ -350,7 +355,7 @@ namespace LeanCloud.Realtime {
patch.Patches.Add(item); patch.Patches.Add(item);
GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify); GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify);
request.PatchMessage = patch; request.PatchMessage = patch;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
return null; return null;
} }
@ -371,7 +376,7 @@ namespace LeanCloud.Realtime {
}; };
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.MemberInfoUpdate); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.MemberInfoUpdate);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
// TODO 同步 members // TODO 同步 members
return this; return this;
@ -422,7 +427,7 @@ namespace LeanCloud.Realtime {
}; };
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.QueryShutup); GenericCommand request = client.NewCommand(CommandType.Conv, OpType.QueryShutup);
request.ConvMessage = conv; request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request); GenericCommand response = await client.connection.SendRequest(request);
return new LCIMPageResult { return new LCIMPageResult {
Results = response.ConvMessage.M.ToList(), Results = response.ConvMessage.M.ToList(),
Next = response.ConvMessage.Next Next = response.ConvMessage.Next

View File

@ -245,7 +245,7 @@ namespace LeanCloud.Realtime {
conv.Where = JsonObjectMessage.Parser.ParseJson(where); conv.Where = JsonObjectMessage.Parser.ParseJson(where);
} }
command.ConvMessage = conv; command.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(command); GenericCommand response = await client.connection.SendRequest(command);
JsonObjectMessage results = response.ConvMessage.Results; JsonObjectMessage results = response.ConvMessage.Results;
List<LCIMConversation> convList = null; List<LCIMConversation> convList = null;
// TODO 反序列化 // TODO 反序列化

View File

@ -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<int, TaskCompletionSource<GenericCommand>> responses;
internal LCConnection() {
responses = new Dictionary<int, TaskCompletionSource<GenericCommand>>();
}
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<byte> bytes = new ArraySegment<byte>(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<byte>(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<GenericCommand> 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++;
};
}
}
}
}

View File

@ -9,7 +9,7 @@ using LeanCloud.Common;
using Google.Protobuf; using Google.Protobuf;
namespace LeanCloud.Realtime.Internal.WebSocket { namespace LeanCloud.Realtime.Internal.WebSocket {
internal class LCWebSocketClient { internal class LCWebSocketConnection {
private const int KEEP_ALIVE_INTERVAL = 10; private const int KEEP_ALIVE_INTERVAL = 10;
private const int RECV_BUFFER_SIZE = 1024; private const int RECV_BUFFER_SIZE = 1024;
@ -25,7 +25,7 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
get; set; get; set;
} }
internal LCWebSocketClient() { internal LCWebSocketConnection() {
responses = new Dictionary<int, TaskCompletionSource<GenericCommand>>(); responses = new Dictionary<int, TaskCompletionSource<GenericCommand>>();
} }

View File

@ -5,7 +5,6 @@ using LeanCloud.Realtime.Internal;
namespace LeanCloud { namespace LeanCloud {
public static class LCApplicationRealtimeExt { public static class LCApplicationRealtimeExt {
static LCConnection connection;
public static async Task<LCIMClient> CreateIMClient(this LCApplication application, string clientId) { public static async Task<LCIMClient> CreateIMClient(this LCApplication application, string clientId) {
if (string.IsNullOrEmpty(clientId)) { if (string.IsNullOrEmpty(clientId)) {

View File

@ -9,7 +9,7 @@ using Newtonsoft.Json;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMClient { public class LCIMClient {
internal LCWebSocketClient client; internal LCWebSocketConnection connection;
private Dictionary<string, LCIMConversation> conversationDict; private Dictionary<string, LCIMConversation> conversationDict;
@ -75,6 +75,10 @@ namespace LeanCloud.Realtime {
get; set; get; set;
} }
public Action<LCIMConversation, LCIMMessage> OnMessageReceived {
get; set;
}
public LCIMClient(string clientId) { public LCIMClient(string clientId) {
ClientId = clientId; ClientId = clientId;
conversationDict = new Dictionary<string, LCIMConversation>(); conversationDict = new Dictionary<string, LCIMConversation>();
@ -85,14 +89,14 @@ namespace LeanCloud.Realtime {
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task Open() { public async Task Open() {
client = new LCWebSocketClient { connection = new LCWebSocketConnection {
OnNotification = OnNotification OnNotification = OnNotification
}; };
await client.Connect(); await connection.Connect();
// Open Session // Open Session
GenericCommand request = NewCommand(CommandType.Session, OpType.Open); GenericCommand request = NewCommand(CommandType.Session, OpType.Open);
request.SessionMessage = new SessionCommand(); request.SessionMessage = new SessionCommand();
GenericCommand response = await client.SendRequest(request); GenericCommand response = await connection.SendRequest(request);
SessionToken = response.SessionMessage.St; SessionToken = response.SessionMessage.St;
} }
@ -101,7 +105,7 @@ namespace LeanCloud.Realtime {
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task Close() { public async Task Close() {
await client.Close(); await connection.Close();
} }
public async Task<LCIMChatRoom> CreateChatRoom( public async Task<LCIMChatRoom> CreateChatRoom(
@ -154,7 +158,7 @@ namespace LeanCloud.Realtime {
}; };
} }
command.ConvMessage = conv; command.ConvMessage = conv;
GenericCommand response = await client.SendRequest(command); GenericCommand response = await connection.SendRequest(command);
LCIMConversation conversation = GetOrCreateConversation(response.ConvMessage.Cid); LCIMConversation conversation = GetOrCreateConversation(response.ConvMessage.Cid);
conversation.MergeFrom(response.ConvMessage); conversation.MergeFrom(response.ConvMessage);
conversationDict[conversation.Id] = conversation; conversationDict[conversation.Id] = conversation;
@ -210,6 +214,9 @@ namespace LeanCloud.Realtime {
case CommandType.Conv: case CommandType.Conv:
OnConversationNotification(notification); OnConversationNotification(notification);
break; break;
case CommandType.Direct:
OnDirectNotification(notification.DirectMessage);
break;
default: default:
break; 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<string, object> msg = JsonConvert.DeserializeObject<Dictionary<string, object>>(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) { private LCIMConversation GetOrCreateConversation(string convId) {
if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) { if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
conversation = new LCIMConversation(this); conversation = new LCIMConversation(this);

View File

@ -1,17 +1,32 @@
using LeanCloud.Storage; using System.Collections.Generic;
using LeanCloud.Storage;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMAudioMessage : LCIMFileMessage { public class LCIMAudioMessage : LCIMFileMessage {
public double Duration { public double Duration {
get { get {
if (double.TryParse("duration", out double duration)) { if (double.TryParse(File.MetaData["duration"] as string, out double duration)) {
return duration; return duration;
} }
return 0; return 0;
} }
} }
public LCIMAudioMessage(LCFile file) : base(file) { internal LCIMAudioMessage() {
} }
public LCIMAudioMessage(LCFile file) : base(file) {
}
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
metaData["duration"] = File.MetaData["duration"];
return data;
}
internal override int MessageType => AudioMessageType;
} }
} }

View File

@ -1,25 +1,14 @@
using System; using System;
using System.Collections.Generic;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMBinaryMessage : LCIMMessage { public class LCIMBinaryMessage : LCIMMessage {
public byte[] Data { public byte[] Data {
get; set; get; internal set;
} }
public LCIMBinaryMessage(byte[] data) { public LCIMBinaryMessage(byte[] data) {
Data = data; Data = data;
} }
internal override string Serialize() {
throw new NotImplementedException();
}
internal override string GetText() {
return null;
}
internal override byte[] GetBytes() {
return Data;
}
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
using LeanCloud.Storage; using LeanCloud.Storage;
namespace LeanCloud.Realtime { 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; File = file;
} }
internal override string Serialize() { internal override Dictionary<string, object> Encode() {
if (File == null) { if (File == null) {
throw new Exception("File MUST NOT be null before sent."); throw new Exception("File MUST NOT be null before sent.");
} }
File.MetaData["name"] = File.Name; Dictionary<string, object> fileData = new Dictionary<string, object> {
File.MetaData["format"] = File.MimeType;
Dictionary<string, object> data = new Dictionary<string, object> {
{ "objId", File.ObjectId }, { "objId", File.ObjectId },
{ "url", File.Url }, { "url", File.Url },
{ "metaData", File.MetaData } { "metaData", new Dictionary<string, object> {
{ "name", File.Name },
{ "format", File.MimeType },
{ "size", File.MetaData["size"] }
} }
}; };
return JsonConvert.SerializeObject(new Dictionary<string, object> { Dictionary<string, object> data = base.Encode();
{ "_lcfile", data } data["_lcfile"] = fileData;
}); return data;
} }
protected override void DecodeMessageData(Dictionary<string, object> msgData) {
base.DecodeMessageData(msgData);
Dictionary<string, object> fileData = msgData["_lcfile"] as Dictionary<string, object>;
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<string, object>;
}
}
internal override int MessageType => FileMessageType;
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System.Collections.Generic;
using LeanCloud.Storage; using LeanCloud.Storage;
namespace LeanCloud.Realtime { 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<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
metaData["width"] = File.MetaData["width"];
metaData["height"] = File.MetaData["height"];
return data;
}
internal override int MessageType => ImageMessageType;
} }
} }

View File

@ -8,18 +8,29 @@ namespace LeanCloud.Realtime {
get; set; get; set;
} }
internal LCIMLocationMessage() {
}
public LCIMLocationMessage(LCGeoPoint locaction) : base(null) { public LCIMLocationMessage(LCGeoPoint locaction) : base(null) {
Location = locaction; Location = locaction;
} }
internal override string Serialize() { internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = new Dictionary<string, object> { Dictionary<string, object> data = base.Encode();
Dictionary<string, object> locationData = new Dictionary<string, object> {
{ "longitude", Location.Longitude }, { "longitude", Location.Longitude },
{ "latitude", Location.Latitude } { "latitude", Location.Latitude }
}; };
return JsonConvert.SerializeObject(new Dictionary<string, object> { data["_lcloc"] = locationData;
{ "_lcloc", data } return data;
});
} }
protected override void DecodeMessageData(Dictionary<string, object> msgData) {
base.DecodeMessageData(msgData);
Dictionary<string, object> locationData = msgData["_lcloc"] as Dictionary<string, object>;
Location = new LCGeoPoint((double)locationData["latitude"], (double)locationData["longitude"]);
}
internal override int MessageType => LocationMessageType;
} }
} }

View File

@ -1,8 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using LeanCloud.Realtime.Protocol;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public abstract class LCIMMessage { 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 { public string ConversationId {
get; set; get; set;
} }
@ -71,9 +79,11 @@ namespace LeanCloud.Realtime {
} }
internal abstract string Serialize(); internal virtual void Decode(DirectCommand direct) {
ConversationId = direct.Cid;
internal abstract string GetText(); Id = direct.Id;
internal abstract byte[] GetBytes(); FromClientId = direct.FromPeerId;
DeliveredTimestamp = direct.Timestamp;
}
} }
} }

View File

@ -1,24 +1,33 @@
using System; using System.Collections.Generic;
using Newtonsoft.Json;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMTextMessage : LCIMTypedMessage { public class LCIMTextMessage : LCIMTypedMessage {
const int TextMessageType = -1;
public string Text { public string Text {
get; set; get; set;
} }
public LCIMTextMessage(string text) : base(TextMessageType) { internal LCIMTextMessage() {
}
public LCIMTextMessage(string text) : base() {
Text = text; Text = text;
} }
internal override string Serialize() { internal override Dictionary<string, object> Encode() {
return Text; Dictionary<string, object> data = base.Encode();
if (!string.IsNullOrEmpty(Text)) {
data["_lctext"] = Text;
}
return data;
} }
internal override string GetText() { protected override void DecodeMessageData(Dictionary<string, object> msgData) {
return Text; base.DecodeMessageData(msgData);
if (msgData.TryGetValue("_lctext", out object value)) {
Text = value as string;
} }
} }
internal override int MessageType => TextMessageType;
}
} }

View File

@ -1,23 +1,32 @@
using System; using System.Collections.Generic;
using Newtonsoft.Json;
using LeanCloud.Realtime.Protocol;
using LeanCloud.Storage.Internal;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMTypedMessage : LCIMMessage { public abstract class LCIMTypedMessage : LCIMMessage {
protected int type; protected LCIMTypedMessage() {
protected LCIMTypedMessage(int type) {
this.type = type;
} }
internal override string Serialize() { internal virtual int MessageType {
throw new NotImplementedException(); get; private set;
} }
internal override string GetText() { internal virtual Dictionary<string, object> Encode() {
return null; return new Dictionary<string, object> {
{ "_lctype", MessageType }
};
} }
internal override byte[] GetBytes() { internal override void Decode(DirectCommand direct) {
return null; base.Decode(direct);
Dictionary<string, object> msgData = JsonConvert.DeserializeObject<Dictionary<string, object>>(direct.Msg, new LCJsonConverter());
DecodeMessageData(msgData);
}
protected virtual void DecodeMessageData(Dictionary<string, object> msgData) {
MessageType = (int)msgData["_lctype"];
} }
} }
} }

View File

@ -1,4 +1,5 @@
using LeanCloud.Storage; using System.Collections.Generic;
using LeanCloud.Storage;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMVideoMessage : LCIMFileMessage { public class LCIMVideoMessage : LCIMFileMessage {
@ -11,8 +12,23 @@ namespace LeanCloud.Realtime {
} }
} }
internal LCIMVideoMessage() {
}
public LCIMVideoMessage(LCFile file) : base(file) { public LCIMVideoMessage(LCFile file) : base(file) {
} }
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
metaData["width"] = File.MetaData["width"];
metaData["height"] = File.MetaData["height"];
metaData["duration"] = File.MetaData["duration"];
return data;
}
internal override int MessageType => VideoMessageType;
} }
} }

View File

@ -61,7 +61,7 @@ namespace LeanCloud.Storage.Internal.Http {
LCHttpUtils.PrintResponse(response, resultString); LCHttpUtils.PrintResponse(response, resultString);
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter()); T ret = JsonConvert.DeserializeObject<T>(resultString, new LCJsonConverter());
return ret; return ret;
} }
throw HandleErrorResponse(response.StatusCode, resultString); throw HandleErrorResponse(response.StatusCode, resultString);
@ -94,7 +94,7 @@ namespace LeanCloud.Storage.Internal.Http {
LCHttpUtils.PrintResponse(response, resultString); LCHttpUtils.PrintResponse(response, resultString);
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter()); T ret = JsonConvert.DeserializeObject<T>(resultString, new LCJsonConverter());
return ret; return ret;
} }
throw HandleErrorResponse(response.StatusCode, resultString); throw HandleErrorResponse(response.StatusCode, resultString);
@ -127,7 +127,7 @@ namespace LeanCloud.Storage.Internal.Http {
LCHttpUtils.PrintResponse(response, resultString); LCHttpUtils.PrintResponse(response, resultString);
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter()); T ret = JsonConvert.DeserializeObject<T>(resultString, new LCJsonConverter());
return ret; return ret;
} }
throw HandleErrorResponse(response.StatusCode, resultString); throw HandleErrorResponse(response.StatusCode, resultString);
@ -150,7 +150,7 @@ namespace LeanCloud.Storage.Internal.Http {
LCHttpUtils.PrintResponse(response, resultString); LCHttpUtils.PrintResponse(response, resultString);
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter()); Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LCJsonConverter());
return; return;
} }
throw HandleErrorResponse(response.StatusCode, resultString); throw HandleErrorResponse(response.StatusCode, resultString);
@ -161,7 +161,7 @@ namespace LeanCloud.Storage.Internal.Http {
string message = responseContent; string message = responseContent;
try { try {
// 尝试获取 LeanCloud 返回错误信息 // 尝试获取 LeanCloud 返回错误信息
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent, new LeanCloudJsonConverter()); Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent, new LCJsonConverter());
code = (int)error["code"]; code = (int)error["code"];
message = error["error"].ToString(); message = error["error"].ToString();
} catch (Exception e) { } catch (Exception e) {

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal { namespace LeanCloud.Storage.Internal {
public class LeanCloudJsonConverter : JsonConverter { public class LCJsonConverter : JsonConverter {
public override bool CanConvert(Type objectType) { public override bool CanConvert(Type objectType) {
return objectType == typeof(object); return objectType == typeof(object);
} }

View File

@ -86,7 +86,7 @@ namespace LeanCloud.Storage {
if (string.IsNullOrEmpty(objectId)) { if (string.IsNullOrEmpty(objectId)) {
throw new ArgumentNullException(nameof(objectId)); throw new ArgumentNullException(nameof(objectId));
} }
LCObject obj = new LCObject(className); LCObject obj = Create(className);
obj.data.ObjectId = objectId; obj.data.ObjectId = objectId;
obj.isNew = false; obj.isNew = false;
return obj; return obj;

View File

@ -36,26 +36,28 @@ namespace RealtimeConsole {
}; };
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com"); LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
LCIMClient client = new LCIMClient("hello123"); LCIMClient hello = new LCIMClient("hello");
try { await hello.Open();
await client.Open();
Console.WriteLine($"End {Thread.CurrentThread.ManagedThreadId}");
} catch (Exception e) {
Console.WriteLine(e.Message);
}
client.OnInvited = (conv, initBy) => { hello.OnInvited = (conv, initBy) => {
Console.WriteLine($"on invited: {initBy}"); Console.WriteLine($"on invited: {initBy}");
}; };
client.OnMembersJoined = (conv, memberList, initBy) => { hello.OnMembersJoined = (conv, memberList, initBy) => {
Console.WriteLine($"on members joined: {initBy}"); Console.WriteLine($"on members joined: {initBy}");
}; };
List<string> memberIdList = new List<string> { "world", "code" }; List<string> memberIdList = new List<string> { "world", "code" };
string name = Guid.NewGuid().ToString(); 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"); //LCIMTextMessage textMessage = new LCIMTextMessage("hello, world");
//await conversation.Send(textMessage); //await conversation.Send(textMessage);
@ -77,10 +79,13 @@ namespace RealtimeConsole {
//LCIMTextMessage textMessage = new LCIMTextMessage("hello, world"); //LCIMTextMessage textMessage = new LCIMTextMessage("hello, world");
//await conversation.Send(textMessage); //await conversation.Send(textMessage);
LCFile file = new LCFile("avatar", "../../../Storage.Test/assets/hello.png"); //LCFile file = new LCFile("avatar", "../../../Storage.Test/assets/hello.png");
await file.Save(); //file.MetaData["width"] = 225;
LCIMImageMessage imageMessage = new LCIMImageMessage(file); //file.MetaData["height"] = 225;
await conversation.Send(imageMessage); //file.MetaData["size"] = 1186;
//await file.Save();
//LCIMImageMessage imageMessage = new LCIMImageMessage(file);
//await conversation.Send(imageMessage);
LCGeoPoint location = new LCGeoPoint(11, 12); LCGeoPoint location = new LCGeoPoint(11, 12);
LCIMLocationMessage locationMessage = new LCIMLocationMessage(location); LCIMLocationMessage locationMessage = new LCIMLocationMessage(location);