* LCIMConversation.cs: chore: 未读消息等逻辑
* LCIMClient.cs: * Program.cs: * LCIMFileMessage.cs: * LCIMAudioMessage.cs: * LCIMImageMessage.cs: * LCIMTypedMessage.cs: * LCIMVideoMessage.cs: * LCIMConversationQuery.cs: * LCIMMessageQueryOptions.cs: * LCIMServiceConversation.cs: * LCWebSocketConnection.cs:
parent
5dcdc56f56
commit
cd453ce134
|
@ -38,6 +38,14 @@ namespace LeanCloud.Realtime {
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Unread {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCIMMessage LastMessage {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
public DateTime CreatedAt {
|
public DateTime CreatedAt {
|
||||||
get; internal set;
|
get; internal set;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +94,28 @@ namespace LeanCloud.Realtime {
|
||||||
return response.ConvMessage.Count;
|
return response.ConvMessage.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将该会话标记为已读
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<LCIMConversation> Read() {
|
||||||
|
if (LastMessage == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
ReadCommand read = new ReadCommand();
|
||||||
|
ReadTuple tuple = new ReadTuple {
|
||||||
|
Cid = Id,
|
||||||
|
Mid = LastMessage.Id,
|
||||||
|
Timestamp = LastMessage.SentTimestamp
|
||||||
|
};
|
||||||
|
read.Convs.Add(tuple);
|
||||||
|
GenericCommand request = client.NewCommand(CommandType.Read, OpType.Open);
|
||||||
|
request.ReadMessage = read;
|
||||||
|
await client.connection.SendRequest(request);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<LCIMConversation> Save() {
|
public async Task<LCIMConversation> Save() {
|
||||||
ConvCommand conv = new ConvCommand {
|
ConvCommand conv = new ConvCommand {
|
||||||
Cid = Id,
|
Cid = Id,
|
||||||
|
@ -479,6 +509,38 @@ namespace LeanCloud.Realtime {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<LCIMMessage>> QueryMessage(LCIMMessageQueryEndpoint start = null,
|
||||||
|
LCIMMessageQueryEndpoint end = null,
|
||||||
|
LCIMMessageQueryDirection direction = LCIMMessageQueryDirection.NewToOld,
|
||||||
|
int limit = 20,
|
||||||
|
int messageType = 0) {
|
||||||
|
LogsCommand logs = new LogsCommand {
|
||||||
|
Cid = Id
|
||||||
|
};
|
||||||
|
if (start != null) {
|
||||||
|
logs.T = start.SentTimestamp;
|
||||||
|
logs.Mid = start.MessageId;
|
||||||
|
logs.TIncluded = start.IsClosed;
|
||||||
|
}
|
||||||
|
if (end != null) {
|
||||||
|
logs.Tt = end.SentTimestamp;
|
||||||
|
logs.Tmid = end.MessageId;
|
||||||
|
logs.TtIncluded = end.IsClosed;
|
||||||
|
}
|
||||||
|
logs.Direction = direction == LCIMMessageQueryDirection.NewToOld ?
|
||||||
|
LogsCommand.Types.QueryDirection.Old : LogsCommand.Types.QueryDirection.New;
|
||||||
|
logs.Limit = limit;
|
||||||
|
if (messageType != 0) {
|
||||||
|
logs.Lctype = messageType;
|
||||||
|
}
|
||||||
|
GenericCommand request = client.NewCommand(CommandType.Logs, OpType.Open);
|
||||||
|
request.LogsMessage = logs;
|
||||||
|
GenericCommand response = await client.connection.SendRequest(request);
|
||||||
|
// TODO 反序列化聊天记录
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private LCIMPartiallySuccessResult NewPartiallySuccessResult(IEnumerable<string> succesfulIds, IEnumerable<ErrorCommand> errors) {
|
private LCIMPartiallySuccessResult NewPartiallySuccessResult(IEnumerable<string> succesfulIds, IEnumerable<ErrorCommand> errors) {
|
||||||
LCIMPartiallySuccessResult result = new LCIMPartiallySuccessResult {
|
LCIMPartiallySuccessResult result = new LCIMPartiallySuccessResult {
|
||||||
SuccessfulClientIdList = succesfulIds.ToList()
|
SuccessfulClientIdList = succesfulIds.ToList()
|
||||||
|
|
|
@ -256,7 +256,10 @@ namespace LeanCloud.Realtime {
|
||||||
foreach (object c in convs) {
|
foreach (object c in convs) {
|
||||||
Dictionary<string, object> cd = c as Dictionary<string, object>;
|
Dictionary<string, object> cd = c as Dictionary<string, object>;
|
||||||
string convId = cd["objectId"] as string;
|
string convId = cd["objectId"] as string;
|
||||||
LCIMConversation conversation = client.GetOrCreateConversation(convId);
|
if (!client.conversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
|
||||||
|
conversation = new LCIMConversation(client);
|
||||||
|
client.conversationDict[convId] = conversation;
|
||||||
|
}
|
||||||
conversation.MergeFrom(cd);
|
conversation.MergeFrom(cd);
|
||||||
convList.Add(conversation);
|
convList.Add(conversation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMMessageQueryEndpoint {
|
||||||
|
public string MessageId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SentTimestamp {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsClosed {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCIMMessageQueryEndpoint() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LCIMMessageQueryDirection {
|
||||||
|
NewToOld,
|
||||||
|
OldToNew
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LeanCloud.Realtime {
|
namespace LeanCloud.Realtime {
|
||||||
public class LCIMServiceConversation : LCIMConversation {
|
public class LCIMServiceConversation : LCIMConversation {
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
|
|
||||||
private string id;
|
private string id;
|
||||||
|
|
||||||
internal Action<GenericCommand> OnNotification {
|
internal Func<GenericCommand, Task> OnNotification {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,14 +77,14 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
}
|
}
|
||||||
// 拼合 WebSocket Frame
|
// 拼合 WebSocket Frame
|
||||||
byte[] oldData = data;
|
byte[] oldData = data;
|
||||||
data = new byte[data.Length + result.Count];
|
data = new byte[oldData.Length + result.Count];
|
||||||
Array.Copy(oldData, data, oldData.Length);
|
Array.Copy(oldData, data, oldData.Length);
|
||||||
Array.Copy(buffer, 0, data, oldData.Length, result.Count);
|
Array.Copy(buffer, 0, data, oldData.Length, result.Count);
|
||||||
} while (!result.EndOfMessage);
|
} while (!result.EndOfMessage);
|
||||||
try {
|
try {
|
||||||
GenericCommand command = GenericCommand.Parser.ParseFrom(data);
|
GenericCommand command = GenericCommand.Parser.ParseFrom(data);
|
||||||
LCLogger.Debug($"{id} <= {command.Cmd}/{command.Op}: {command.ToString()}");
|
LCLogger.Debug($"{id} <= {command.Cmd}/{command.Op}: {command.ToString()}");
|
||||||
HandleCommand(command);
|
_ = HandleCommand(command);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 解析消息错误
|
// 解析消息错误
|
||||||
LCLogger.Error(e.Message);
|
LCLogger.Error(e.Message);
|
||||||
|
@ -96,25 +96,29 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleCommand(GenericCommand command) {
|
private async Task HandleCommand(GenericCommand command) {
|
||||||
if (command.HasI) {
|
try {
|
||||||
// 应答
|
if (command.HasI) {
|
||||||
if (responses.TryGetValue(command.I, out TaskCompletionSource<GenericCommand> tcs)) {
|
// 应答
|
||||||
if (command.HasErrorMessage) {
|
if (responses.TryGetValue(command.I, out TaskCompletionSource<GenericCommand> tcs)) {
|
||||||
// 错误
|
if (command.HasErrorMessage) {
|
||||||
ErrorCommand error = command.ErrorMessage;
|
// 错误
|
||||||
int code = error.Code;
|
ErrorCommand error = command.ErrorMessage;
|
||||||
string detail = error.Detail;
|
int code = error.Code;
|
||||||
// TODO 包装成异常抛出
|
string detail = error.Detail;
|
||||||
LCException exception = new LCException(code, detail);
|
// TODO 包装成异常抛出
|
||||||
tcs.SetException(exception);
|
LCException exception = new LCException(code, detail);
|
||||||
} else {
|
tcs.SetException(exception);
|
||||||
tcs.SetResult(command);
|
} else {
|
||||||
|
tcs.SetResult(command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 通知
|
||||||
|
await OnNotification?.Invoke(command);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (Exception e) {
|
||||||
// 通知
|
LCLogger.Error(e.Message);
|
||||||
OnNotification?.Invoke(command);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,14 @@ using System.Linq;
|
||||||
using LeanCloud.Realtime.Internal.WebSocket;
|
using LeanCloud.Realtime.Internal.WebSocket;
|
||||||
using LeanCloud.Realtime.Protocol;
|
using LeanCloud.Realtime.Protocol;
|
||||||
using LeanCloud.Storage.Internal.Codec;
|
using LeanCloud.Storage.Internal.Codec;
|
||||||
|
using LeanCloud.Storage.Internal;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LeanCloud.Realtime {
|
namespace LeanCloud.Realtime {
|
||||||
public class LCIMClient {
|
public class LCIMClient {
|
||||||
internal LCWebSocketConnection connection;
|
internal LCWebSocketConnection connection;
|
||||||
|
|
||||||
private Dictionary<string, LCIMConversation> conversationDict;
|
internal Dictionary<string, LCIMConversation> conversationDict;
|
||||||
|
|
||||||
public string ClientId {
|
public string ClientId {
|
||||||
get; private set;
|
get; private set;
|
||||||
|
@ -29,6 +30,11 @@ namespace LeanCloud.Realtime {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前客户端在某个对话中被禁言
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, string> OnMuted;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 客户端连接断开
|
/// 客户端连接断开
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -50,6 +56,25 @@ namespace LeanCloud.Realtime {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户端连接断开
|
||||||
|
/// </summary>
|
||||||
|
public Action OnDisconnect {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户在其他客户端登录,当前客户端被服务端强行下线
|
||||||
|
/// </summary>
|
||||||
|
public Action<string> OnConflict {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 该对话信息被更新
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, Dictionary<string, object>, string> OnConversationInfoUpdated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前用户被添加至某个对话
|
/// 当前用户被添加至某个对话
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -71,14 +96,74 @@ namespace LeanCloud.Realtime {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有成员被从某个对话中移除
|
||||||
|
/// </summary>
|
||||||
public Action<LCIMConversation, List<string>, string> OnMembersLeft {
|
public Action<LCIMConversation, List<string>, string> OnMembersLeft {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有成员被加入某个对话的黑名单
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, List<string>, string> OnMembersBlocked {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有成员被移出某个对话的黑名单
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, List<string>, string> OnMembersUnblocked {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有成员在某个对话中被禁言
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, List<string>, string> OnMembersMuted {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有成员被移出某个对话的黑名单
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, List<string>, string> OnMembersUnmuted {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有成员的对话信息被更新
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, string, Dictionary<string, object>, string> OnMemberInfoUpdated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前用户收到消息
|
||||||
|
/// </summary>
|
||||||
public Action<LCIMConversation, LCIMMessage> OnMessageReceived {
|
public Action<LCIMConversation, LCIMMessage> OnMessageReceived {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息被撤回
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, LCIMMessage> OnMessageRecall {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息被修改
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, LCIMMessage> OnMessageUpdate {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 未读消息数目更新
|
||||||
|
/// </summary>
|
||||||
|
public Action<List<LCIMConversation>> OnUnreadMessagesCountUpdated {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
internal ILCIMSignatureFactory SignatureFactory {
|
internal ILCIMSignatureFactory SignatureFactory {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +202,8 @@ namespace LeanCloud.Realtime {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Close() {
|
public async Task Close() {
|
||||||
|
GenericCommand request = NewCommand(CommandType.Session, OpType.Close);
|
||||||
|
await connection.SendRequest(request);
|
||||||
await connection.Close();
|
await connection.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,8 +296,8 @@ namespace LeanCloud.Realtime {
|
||||||
if (string.IsNullOrEmpty(id)) {
|
if (string.IsNullOrEmpty(id)) {
|
||||||
throw new ArgumentNullException(nameof(id));
|
throw new ArgumentNullException(nameof(id));
|
||||||
}
|
}
|
||||||
LCIMConversationQuery query = GetQuery();
|
LCIMConversationQuery query = GetQuery()
|
||||||
query.WhereEqualTo("objectId", id)
|
.WhereEqualTo("objectId", id)
|
||||||
.Limit(1);
|
.Limit(1);
|
||||||
List<LCIMConversation> results = await query.Find();
|
List<LCIMConversation> results = await query.Find();
|
||||||
if (results == null || results.Count < 1) {
|
if (results == null || results.Count < 1) {
|
||||||
|
@ -244,19 +331,44 @@ namespace LeanCloud.Realtime {
|
||||||
return new LCIMConversationQuery(this);
|
return new LCIMConversationQuery(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNotification(GenericCommand notification) {
|
private async Task OnNotification(GenericCommand notification) {
|
||||||
switch (notification.Cmd) {
|
switch (notification.Cmd) {
|
||||||
|
case CommandType.Session:
|
||||||
|
await OnSessionNotification(notification);
|
||||||
|
break;
|
||||||
case CommandType.Conv:
|
case CommandType.Conv:
|
||||||
OnConversationNotification(notification);
|
OnConversationNotification(notification);
|
||||||
break;
|
break;
|
||||||
case CommandType.Direct:
|
case CommandType.Direct:
|
||||||
OnDirectNotification(notification.DirectMessage);
|
await OnDirectNotification(notification.DirectMessage);
|
||||||
|
break;
|
||||||
|
case CommandType.Unread:
|
||||||
|
await OnUnreadNotification(notification.UnreadMessage);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnSessionNotification(GenericCommand notification) {
|
||||||
|
switch (notification.Op) {
|
||||||
|
case OpType.Closed:
|
||||||
|
await OnSessionClosed(notification.SessionMessage);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSessionClosed(SessionCommand session) {
|
||||||
|
int code = session.Code;
|
||||||
|
string reason = session.Reason;
|
||||||
|
string detail = session.Detail;
|
||||||
|
await connection.Close();
|
||||||
|
// TODO 关闭连接后回调给开发者
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void OnConversationNotification(GenericCommand notification) {
|
private void OnConversationNotification(GenericCommand notification) {
|
||||||
ConvCommand conv = notification.ConvMessage;
|
ConvCommand conv = notification.ConvMessage;
|
||||||
switch (notification.Op) {
|
switch (notification.Op) {
|
||||||
|
@ -273,21 +385,24 @@ namespace LeanCloud.Realtime {
|
||||||
OnConversationMemberLeft(conv);
|
OnConversationMemberLeft(conv);
|
||||||
break;
|
break;
|
||||||
case OpType.Updated:
|
case OpType.Updated:
|
||||||
OnPropertiesUpdated(conv);
|
OnConversationPropertiesUpdated(conv);
|
||||||
|
break;
|
||||||
|
case OpType.MemberInfoChanged:
|
||||||
|
OnConversationMemberInfoChanged(conv);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConversationJoined(ConvCommand conv) {
|
private async void OnConversationJoined(ConvCommand conv) {
|
||||||
LCIMConversation conversation = GetOrCreateConversation(conv.Cid);
|
LCIMConversation conversation = await GetOrQueryConversation(conv.Cid);
|
||||||
conversation.MergeFrom(conv);
|
conversation.MergeFrom(conv);
|
||||||
OnInvited?.Invoke(conversation, conv.InitBy);
|
OnInvited?.Invoke(conversation, conv.InitBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConversationMembersJoined(ConvCommand conv) {
|
private async void OnConversationMembersJoined(ConvCommand conv) {
|
||||||
LCIMConversation conversation = GetOrCreateConversation(conv.Cid);
|
LCIMConversation conversation = await GetOrQueryConversation(conv.Cid);
|
||||||
conversation.MergeFrom(conv);
|
conversation.MergeFrom(conv);
|
||||||
OnMembersJoined?.Invoke(conversation, conv.M.ToList(), conv.InitBy);
|
OnMembersJoined?.Invoke(conversation, conv.M.ToList(), conv.InitBy);
|
||||||
}
|
}
|
||||||
|
@ -305,14 +420,19 @@ namespace LeanCloud.Realtime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPropertiesUpdated(ConvCommand conv) {
|
private void OnConversationPropertiesUpdated(ConvCommand conv) {
|
||||||
if (conversationDict.TryGetValue(conv.Cid, out LCIMConversation conversation)) {
|
if (conversationDict.TryGetValue(conv.Cid, out LCIMConversation conversation)) {
|
||||||
// TODO
|
// TODO 修改对话属性,并回调给开发者
|
||||||
|
|
||||||
|
OnConversationInfoUpdated?.Invoke(conversation, null, conv.InitBy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDirectNotification(DirectCommand direct) {
|
private void OnConversationMemberInfoChanged(ConvCommand conv) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnDirectNotification(DirectCommand direct) {
|
||||||
LCIMMessage message = null;
|
LCIMMessage message = null;
|
||||||
if (direct.HasBinaryMsg) {
|
if (direct.HasBinaryMsg) {
|
||||||
// 二进制消息
|
// 二进制消息
|
||||||
|
@ -321,7 +441,8 @@ namespace LeanCloud.Realtime {
|
||||||
} else {
|
} else {
|
||||||
// 文本消息
|
// 文本消息
|
||||||
string messageData = direct.Msg;
|
string messageData = direct.Msg;
|
||||||
Dictionary<string, object> msg = JsonConvert.DeserializeObject<Dictionary<string, object>>(messageData);
|
Dictionary<string, object> msg = JsonConvert.DeserializeObject<Dictionary<string, object>>(messageData,
|
||||||
|
new LCJsonConverter());
|
||||||
int msgType = (int)(long)msg["_lctype"];
|
int msgType = (int)(long)msg["_lctype"];
|
||||||
switch (msgType) {
|
switch (msgType) {
|
||||||
case -1:
|
case -1:
|
||||||
|
@ -347,16 +468,30 @@ namespace LeanCloud.Realtime {
|
||||||
}
|
}
|
||||||
message.Decode(direct);
|
message.Decode(direct);
|
||||||
}
|
}
|
||||||
// TODO 获取对话
|
// 获取对话
|
||||||
|
LCIMConversation conversation = await GetOrQueryConversation(direct.Cid);
|
||||||
OnMessageReceived?.Invoke(null, message);
|
OnMessageReceived?.Invoke(conversation, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LCIMConversation GetOrCreateConversation(string convId) {
|
private async Task OnUnreadNotification(UnreadCommand unread) {
|
||||||
if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
|
List<LCIMConversation> conversationList = new List<LCIMConversation>();
|
||||||
conversation = new LCIMConversation(this);
|
foreach (UnreadTuple conv in unread.Convs) {
|
||||||
conversationDict.Add(convId, conversation);
|
// 查询对话
|
||||||
|
LCIMConversation conversation = await GetOrQueryConversation(conv.Cid);
|
||||||
|
conversation.Unread = conv.Unread;
|
||||||
|
// TODO 反序列化对话
|
||||||
|
// 最后一条消息
|
||||||
|
JsonConvert.DeserializeObject<Dictionary<string, object>>(conv.Data);
|
||||||
|
conversationList.Add(conversation);
|
||||||
}
|
}
|
||||||
|
OnUnreadMessagesCountUpdated?.Invoke(conversationList);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<LCIMConversation> GetOrQueryConversation(string convId) {
|
||||||
|
if (conversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
|
conversation = await GetConversation(convId);
|
||||||
return conversation;
|
return conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,9 @@ namespace LeanCloud.Realtime {
|
||||||
Dictionary<string, object> data = base.Encode();
|
Dictionary<string, object> data = base.Encode();
|
||||||
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
|
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
|
||||||
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
||||||
metaData["duration"] = File.MetaData["duration"];
|
if (File.MetaData.TryGetValue("duration", out object duration)) {
|
||||||
|
metaData["duration"] = duration;
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,13 @@ namespace LeanCloud.Realtime {
|
||||||
{ "url", File.Url },
|
{ "url", File.Url },
|
||||||
{ "metaData", new Dictionary<string, object> {
|
{ "metaData", new Dictionary<string, object> {
|
||||||
{ "name", File.Name },
|
{ "name", File.Name },
|
||||||
{ "format", File.MimeType },
|
{ "format", File.MimeType }
|
||||||
{ "size", File.MetaData["size"] }
|
|
||||||
} }
|
} }
|
||||||
};
|
};
|
||||||
|
if (File.MetaData.TryGetValue("size", out object size)) {
|
||||||
|
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
||||||
|
metaData["size"] = size;
|
||||||
|
}
|
||||||
Dictionary<string, object> data = base.Encode();
|
Dictionary<string, object> data = base.Encode();
|
||||||
data["_lcfile"] = fileData;
|
data["_lcfile"] = fileData;
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -32,8 +32,12 @@ namespace LeanCloud.Realtime {
|
||||||
Dictionary<string, object> data = base.Encode();
|
Dictionary<string, object> data = base.Encode();
|
||||||
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
|
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
|
||||||
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
||||||
metaData["width"] = File.MetaData["width"];
|
if (File.MetaData.TryGetValue("width", out object width)) {
|
||||||
metaData["height"] = File.MetaData["height"];
|
metaData["width"] = width;
|
||||||
|
}
|
||||||
|
if (File.MetaData.TryGetValue("height", out object height)) {
|
||||||
|
metaData["height"] = height;
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,5 +52,34 @@ namespace LeanCloud.Realtime {
|
||||||
customProperties = LCDecoder.Decode(attrObj) as Dictionary<string, object>;
|
customProperties = LCDecoder.Decode(attrObj) as Dictionary<string, object>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static LCIMTypedMessage Deserialize(Dictionary<string, object> messageData) {
|
||||||
|
LCIMTypedMessage message = null;
|
||||||
|
int msgType = (int)(long)messageData["_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);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,15 @@ namespace LeanCloud.Realtime {
|
||||||
Dictionary<string, object> data = base.Encode();
|
Dictionary<string, object> data = base.Encode();
|
||||||
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
|
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
|
||||||
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
|
||||||
metaData["width"] = File.MetaData["width"];
|
if (File.MetaData.TryGetValue("width", out object width)) {
|
||||||
metaData["height"] = File.MetaData["height"];
|
metaData["width"] = width;
|
||||||
metaData["duration"] = File.MetaData["duration"];
|
}
|
||||||
|
if (File.MetaData.TryGetValue("height", out object height)) {
|
||||||
|
metaData["height"] = height;
|
||||||
|
}
|
||||||
|
if (File.MetaData.TryGetValue("duration", out object duration)) {
|
||||||
|
metaData["duration"] = duration;
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,13 @@ namespace RealtimeConsole {
|
||||||
LCLogger.LogDelegate += (level, info) => {
|
LCLogger.LogDelegate += (level, info) => {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LCLogLevel.Debug:
|
case LCLogLevel.Debug:
|
||||||
Console.WriteLine($"[DEBUG] {info}");
|
Console.WriteLine($"[DEBUG]\n{info}");
|
||||||
break;
|
break;
|
||||||
case LCLogLevel.Warn:
|
case LCLogLevel.Warn:
|
||||||
Console.WriteLine($"[WARNING] {info}");
|
Console.WriteLine($"[WARNING]\n{info}");
|
||||||
break;
|
break;
|
||||||
case LCLogLevel.Error:
|
case LCLogLevel.Error:
|
||||||
Console.WriteLine($"[ERROR] {info}");
|
Console.WriteLine($"[ERROR]\n{info}");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine(info);
|
Console.WriteLine(info);
|
||||||
|
@ -30,22 +30,131 @@ 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");
|
||||||
|
|
||||||
//_ = Start();
|
//Conversation().Wait();
|
||||||
|
|
||||||
//_ = ChatRoom();
|
//_ = ChatRoom();
|
||||||
|
|
||||||
_ = TemporaryConversation();
|
//_ = TemporaryConversation();
|
||||||
|
|
||||||
|
//_ = Signature();
|
||||||
|
|
||||||
|
//_ = Block();
|
||||||
|
|
||||||
|
//_ = Mute();
|
||||||
|
|
||||||
|
//QueryConversation().Wait();
|
||||||
|
|
||||||
|
//_ = OpenAndClose();
|
||||||
|
|
||||||
|
//SendMessage().Wait();
|
||||||
|
|
||||||
|
_ = Unread();
|
||||||
|
|
||||||
Console.ReadKey(true);
|
Console.ReadKey(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async Task Run(int s) {
|
||||||
|
for (int i = 0; i < s; i++) {
|
||||||
|
Console.WriteLine($"run {i}");
|
||||||
|
await Task.Delay(1000);
|
||||||
|
Console.WriteLine($"run {i} done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task Unread() {
|
||||||
|
LCIMClient u2 = new LCIMClient("u2");
|
||||||
|
await u2.Open();
|
||||||
|
u2.OnUnreadMessagesCountUpdated = conversationList => {
|
||||||
|
foreach (LCIMConversation conv in conversationList) {
|
||||||
|
Console.WriteLine($"unread: {conv.Unread}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task SendMessage() {
|
||||||
|
try {
|
||||||
|
LCIMClient u1 = new LCIMClient("u1");
|
||||||
|
await u1.Open();
|
||||||
|
LCIMConversation conversation = await u1.CreateConversation(new string[] { "u2" });
|
||||||
|
|
||||||
|
LCIMTextMessage textMessage = new LCIMTextMessage("hello, text message");
|
||||||
|
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);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task OpenAndClose() {
|
||||||
|
LCIMClient o1 = new LCIMClient("o1");
|
||||||
|
await o1.Open();
|
||||||
|
await o1.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task QueryConversation() {
|
||||||
|
LCIMClient m2 = new LCIMClient("m2");
|
||||||
|
await m2.Open();
|
||||||
|
|
||||||
|
LCIMConversation conv = (await m2.GetQuery()
|
||||||
|
.WhereEqualTo("objectId", "5e7863bf90aef5aa849be75a")
|
||||||
|
.Find())[0];
|
||||||
|
LCIMTextMessage textMessage = new LCIMTextMessage("hello, world");
|
||||||
|
await conv.Send(textMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task Mute() {
|
||||||
|
LCIMClient m1 = new LCIMClient("m0");
|
||||||
|
await m1.Open();
|
||||||
|
|
||||||
|
LCIMClient m2 = new LCIMClient("m2");
|
||||||
|
await m2.Open();
|
||||||
|
|
||||||
|
LCIMConversation conversation = await m1.CreateConversation(new string[] { "m2", "m3" });
|
||||||
|
await conversation.MuteMembers(new string[] { "m2" });
|
||||||
|
|
||||||
|
LCIMConversation conv = (await m2.GetQuery()
|
||||||
|
.WhereEqualTo("objectId", conversation.Id)
|
||||||
|
.Find())[0];
|
||||||
|
LCIMTextMessage textMessage = new LCIMTextMessage("hello, world");
|
||||||
|
await conv.Send(textMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task Block() {
|
||||||
|
LocalSignatureFactory signatureFactory = new LocalSignatureFactory();
|
||||||
|
LCIMClient c1 = new LCIMClient("c0");
|
||||||
|
await c1.Open();
|
||||||
|
LCIMConversation conversation = await c1.CreateConversation(new string[] { "c2", "c3", "c4", "c5" });
|
||||||
|
LCIMTextMessage textMessage = new LCIMTextMessage("hello");
|
||||||
|
await conversation.Send(textMessage);
|
||||||
|
await conversation.BlockMembers(new string[] { "c5" });
|
||||||
|
|
||||||
|
LCIMClient c5 = new LCIMClient("c5");
|
||||||
|
await c5.Open();
|
||||||
|
await conversation.Add(new string[] { "c5" });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task Signature() {
|
||||||
|
LocalSignatureFactory signatureFactory = new LocalSignatureFactory();
|
||||||
|
LCIMClient hello = new LCIMClient("hello111", signatureFactory);
|
||||||
|
await hello.Open();
|
||||||
|
}
|
||||||
|
|
||||||
static async Task ChatRoom() {
|
static async Task ChatRoom() {
|
||||||
LCIMClient hello = new LCIMClient("hello");
|
LocalSignatureFactory signatureFactory = new LocalSignatureFactory();
|
||||||
|
LCIMClient hello = new LCIMClient("hello", signatureFactory);
|
||||||
await hello.Open();
|
await hello.Open();
|
||||||
|
|
||||||
string name = Guid.NewGuid().ToString();
|
string name = Guid.NewGuid().ToString();
|
||||||
LCIMChatRoom chatRoom = await hello.CreateChatRoom(name);
|
LCIMChatRoom chatRoom = await hello.CreateChatRoom(name);
|
||||||
Console.WriteLine(chatRoom.Name);
|
Console.WriteLine(chatRoom.Name);
|
||||||
|
|
||||||
|
await chatRoom.Add(new string[] { "world" });
|
||||||
|
|
||||||
|
await chatRoom.Remove(new string[] { "world" });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async Task TemporaryConversation() {
|
static async Task TemporaryConversation() {
|
||||||
|
@ -61,7 +170,7 @@ namespace RealtimeConsole {
|
||||||
Console.WriteLine(temporaryConversation.Id);
|
Console.WriteLine(temporaryConversation.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async Task Start() {
|
static async Task Conversation() {
|
||||||
LCIMClient hello = new LCIMClient("hello");
|
LCIMClient hello = new LCIMClient("hello");
|
||||||
|
|
||||||
await hello.Open();
|
await hello.Open();
|
||||||
|
|
Loading…
Reference in New Issue