* 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:
parent
29a84b8afb
commit
6781d4e94f
|
@ -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<string> allowedIds = response.ConvMessage.AllowedPids.ToList();
|
||||
List<ErrorCommand> 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<string> allowedIds = response.ConvMessage.AllowedPids.ToList();
|
||||
List<ErrorCommand> 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
|
||||
|
|
|
@ -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<LCIMConversation> convList = null;
|
||||
// TODO 反序列化
|
||||
|
|
|
@ -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++;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<int, TaskCompletionSource<GenericCommand>>();
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ using LeanCloud.Realtime.Internal;
|
|||
|
||||
namespace LeanCloud {
|
||||
public static class LCApplicationRealtimeExt {
|
||||
static LCConnection connection;
|
||||
|
||||
public static async Task<LCIMClient> CreateIMClient(this LCApplication application, string clientId) {
|
||||
if (string.IsNullOrEmpty(clientId)) {
|
||||
|
|
|
@ -9,7 +9,7 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace LeanCloud.Realtime {
|
||||
public class LCIMClient {
|
||||
internal LCWebSocketClient client;
|
||||
internal LCWebSocketConnection connection;
|
||||
|
||||
private Dictionary<string, LCIMConversation> conversationDict;
|
||||
|
||||
|
@ -75,6 +75,10 @@ namespace LeanCloud.Realtime {
|
|||
get; set;
|
||||
}
|
||||
|
||||
public Action<LCIMConversation, LCIMMessage> OnMessageReceived {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public LCIMClient(string clientId) {
|
||||
ClientId = clientId;
|
||||
conversationDict = new Dictionary<string, LCIMConversation>();
|
||||
|
@ -85,14 +89,14 @@ namespace LeanCloud.Realtime {
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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 {
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task Close() {
|
||||
await client.Close();
|
||||
await connection.Close();
|
||||
}
|
||||
|
||||
public async Task<LCIMChatRoom> 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<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) {
|
||||
if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
|
||||
conversation = new LCIMConversation(this);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string, object> 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<string, object> data = new Dictionary<string, object> {
|
||||
Dictionary<string, object> fileData = new Dictionary<string, object> {
|
||||
{ "objId", File.ObjectId },
|
||||
{ "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> {
|
||||
{ "_lcfile", data }
|
||||
});
|
||||
Dictionary<string, object> data = base.Encode();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Realtime {
|
||||
|
@ -21,7 +21,22 @@ namespace LeanCloud.Realtime {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,18 +8,29 @@ namespace LeanCloud.Realtime {
|
|||
get; set;
|
||||
}
|
||||
|
||||
internal LCIMLocationMessage() {
|
||||
}
|
||||
|
||||
public LCIMLocationMessage(LCGeoPoint locaction) : base(null) {
|
||||
Location = locaction;
|
||||
}
|
||||
|
||||
internal override string Serialize() {
|
||||
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||
internal override Dictionary<string, object> Encode() {
|
||||
Dictionary<string, object> data = base.Encode();
|
||||
Dictionary<string, object> locationData = new Dictionary<string, object> {
|
||||
{ "longitude", Location.Longitude },
|
||||
{ "latitude", Location.Latitude }
|
||||
};
|
||||
return JsonConvert.SerializeObject(new Dictionary<string, object> {
|
||||
{ "_lcloc", data }
|
||||
});
|
||||
}
|
||||
data["_lcloc"] = locationData;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string, object> Encode() {
|
||||
Dictionary<string, object> data = base.Encode();
|
||||
if (!string.IsNullOrEmpty(Text)) {
|
||||
data["_lctext"] = Text;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
internal override string GetText() {
|
||||
return Text;
|
||||
protected override void DecodeMessageData(Dictionary<string, object> msgData) {
|
||||
base.DecodeMessageData(msgData);
|
||||
if (msgData.TryGetValue("_lctext", out object value)) {
|
||||
Text = value as string;
|
||||
}
|
||||
}
|
||||
|
||||
internal override int MessageType => TextMessageType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string, object> Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "_lctype", MessageType }
|
||||
};
|
||||
}
|
||||
|
||||
internal override byte[] GetBytes() {
|
||||
return null;
|
||||
internal override void Decode(DirectCommand direct) {
|
||||
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"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
LCHttpUtils.PrintResponse(response, resultString);
|
||||
|
||||
if (response.IsSuccessStatusCode) {
|
||||
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
|
||||
T ret = JsonConvert.DeserializeObject<T>(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<T>(resultString, new LeanCloudJsonConverter());
|
||||
T ret = JsonConvert.DeserializeObject<T>(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<T>(resultString, new LeanCloudJsonConverter());
|
||||
T ret = JsonConvert.DeserializeObject<T>(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<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LCJsonConverter());
|
||||
return;
|
||||
}
|
||||
throw HandleErrorResponse(response.StatusCode, resultString);
|
||||
|
@ -161,7 +161,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
string message = responseContent;
|
||||
try {
|
||||
// 尝试获取 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"];
|
||||
message = error["error"].ToString();
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<string> memberIdList = new List<string> { "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);
|
||||
|
|
Loading…
Reference in New Issue