* LCIMConversation.cs: chore: 支持修改成员角色;支持修改消息

* LCIMClient.cs:
* LCApplication.cs:
* LCIMPageResult.cs:
* LCIMMessage.cs:
* Program.cs:
* LCIMOperationFailure.cs:
* LCIMTextMessage.cs:
* LCIMTypedMessage.cs:
* LCIMBinaryMessage.cs:
* LCHttpClient.cs:
* LCIMPartiallySuccessResult.cs:
* LCIMConversationMemberInfo.cs:
oneRain 2020-03-17 16:17:19 +08:00
parent 05e642237e
commit 6b4d28b000
13 changed files with 303 additions and 47 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Google.Protobuf;
using LeanCloud.Realtime.Protocol; using LeanCloud.Realtime.Protocol;
using LeanCloud.Storage.Internal.Codec; using LeanCloud.Storage.Internal.Codec;
@ -191,23 +192,6 @@ namespace LeanCloud.Realtime {
return message; return message;
} }
public async Task<LCIMRecalledMessage> Recall(LCIMMessage message) {
if (message == null) {
throw new ArgumentNullException(nameof(message));
}
PatchCommand patch = new PatchCommand();
PatchItem item = new PatchItem {
Cid = Id,
Mid = message.Id,
Recall = true
};
patch.Patches.Add(item);
GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify);
request.PatchMessage = patch;
GenericCommand response = await client.client.SendRequest(request);
return null;
}
/// <summary> /// <summary>
/// 静音 /// 静音
/// </summary> /// </summary>
@ -243,7 +227,7 @@ namespace LeanCloud.Realtime {
/// </summary> /// </summary>
/// <param name="clientIds"></param> /// <param name="clientIds"></param>
/// <returns></returns> /// <returns></returns>
public async Task MuteMembers(IEnumerable<string> clientIds) { public async Task<LCIMPartiallySuccessResult> MuteMembers(IEnumerable<string> clientIds) {
if (clientIds == null || clientIds.Count() == 0) { if (clientIds == null || clientIds.Count() == 0) {
throw new ArgumentNullException(nameof(clientIds)); throw new ArgumentNullException(nameof(clientIds));
} }
@ -254,7 +238,7 @@ namespace LeanCloud.Realtime {
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.client.SendRequest(request);
return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids);
} }
/// <summary> /// <summary>
@ -262,7 +246,7 @@ namespace LeanCloud.Realtime {
/// </summary> /// </summary>
/// <param name="clientIdList"></param> /// <param name="clientIdList"></param>
/// <returns></returns> /// <returns></returns>
public async Task UnmuteMembers(IEnumerable<string> clientIds) { public async Task<LCIMPartiallySuccessResult> UnmuteMembers(IEnumerable<string> clientIds) {
if (clientIds == null || clientIds.Count() == 0) { if (clientIds == null || clientIds.Count() == 0) {
throw new ArgumentNullException(nameof(clientIds)); throw new ArgumentNullException(nameof(clientIds));
} }
@ -273,6 +257,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.client.SendRequest(request);
return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids);
} }
/// <summary> /// <summary>
@ -280,28 +265,115 @@ namespace LeanCloud.Realtime {
/// </summary> /// </summary>
/// <param name="clientIds"></param> /// <param name="clientIds"></param>
/// <returns></returns> /// <returns></returns>
public async Task BlockMembers(IEnumerable<string> clientIds) { public async Task<LCIMPartiallySuccessResult> BlockMembers(IEnumerable<string> clientIds) {
if (clientIds == null || clientIds.Count() == 0) { if (clientIds == null || clientIds.Count() == 0) {
throw new ArgumentNullException(nameof(clientIds)); throw new ArgumentNullException(nameof(clientIds));
} }
BlacklistCommand blacklist = new BlacklistCommand { BlacklistCommand blacklist = new BlacklistCommand {
SrcCid = Id, SrcCid = Id,
}; };
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;
await client.client.SendRequest(request); GenericCommand response = await client.client.SendRequest(request);
return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids);
} }
public async Task UnblockMembers(IEnumerable<string> clientIds) { public async Task<LCIMPartiallySuccessResult> UnblockMembers(IEnumerable<string> clientIds) {
if (clientIds == null || clientIds.Count() == 0) {
throw new ArgumentNullException(nameof(clientIds));
}
BlacklistCommand blacklist = new BlacklistCommand {
SrcCid = Id,
};
blacklist.ToPids.AddRange(clientIds);
GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Unblock);
request.BlacklistMessage = blacklist;
GenericCommand response = await client.client.SendRequest(request);
return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids);
} }
/// <summary>
/// 撤回消息
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task<LCIMRecalledMessage> Recall(LCIMMessage message) {
if (message == null) {
throw new ArgumentNullException(nameof(message));
}
PatchCommand patch = new PatchCommand();
PatchItem item = new PatchItem {
Cid = Id,
Mid = message.Id,
Recall = true
};
patch.Patches.Add(item);
GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify);
request.PatchMessage = patch;
GenericCommand response = await client.client.SendRequest(request);
return null;
}
/// <summary>
/// 修改消息
/// </summary>
/// <param name="oldMessage"></param>
/// <param name="newMessage"></param>
/// <returns></returns>
public async Task<LCIMMessage> Update(LCIMMessage oldMessage, LCIMMessage newMessage) { public async Task<LCIMMessage> Update(LCIMMessage oldMessage, LCIMMessage newMessage) {
if (oldMessage == null) {
throw new ArgumentNullException(nameof(oldMessage));
}
if (newMessage == null) {
throw new ArgumentNullException(nameof(newMessage));
}
PatchCommand patch = new PatchCommand();
PatchItem item = new PatchItem {
Cid = Id,
Mid = oldMessage.Id,
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.MentionList != null) {
item.MentionPids.AddRange(newMessage.MentionList);
}
if (newMessage.MentionAll) {
item.MentionAll = newMessage.MentionAll;
}
patch.Patches.Add(item);
GenericCommand request = client.NewCommand(CommandType.Patch, OpType.Modify);
request.PatchMessage = patch;
GenericCommand response = await client.client.SendRequest(request);
return null; return null;
} }
public async Task<LCIMConversation> UpdateMemberRole(string memberId, string role) { public async Task<LCIMConversation> UpdateMemberRole(string memberId, string role) {
if (string.IsNullOrEmpty(memberId)) {
throw new ArgumentNullException(nameof(memberId));
}
if (role != LCIMConversationMemberInfo.Manager && role != LCIMConversationMemberInfo.Member) {
throw new ArgumentException("role MUST be Manager Or Memebr");
}
ConvCommand conv = new ConvCommand {
Cid = Id,
TargetClientId = memberId,
Info = new ConvMemberInfo {
Pid = memberId,
Role = role
}
};
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.MemberInfoUpdate);
request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request);
// TODO 同步 members
return this; return this;
} }
@ -309,12 +381,65 @@ namespace LeanCloud.Realtime {
if (string.IsNullOrEmpty(memberId)) { if (string.IsNullOrEmpty(memberId)) {
throw new ArgumentNullException(nameof(memberId)); throw new ArgumentNullException(nameof(memberId));
} }
List<LCIMConversationMemberInfo> members = await GetAllMemberInfo();
foreach (LCIMConversationMemberInfo member in members) {
if (member.MemberId == memberId) {
return member;
}
}
return null; return null;
} }
public async Task<List<LCIMConversationMemberInfo>> GetAllMemberInfo() { public async Task<List<LCIMConversationMemberInfo>> GetAllMemberInfo() {
return null; string path = "classes/_ConversationMemberInfo";
Dictionary<string, object> headers = new Dictionary<string, object> {
{ "X-LC-IM-Session-Token", client.SessionToken }
};
Dictionary<string, object> queryParams = new Dictionary<string, object> {
{ "client_id", client.ClientId },
{ "cid", Id }
};
Dictionary<string, object> response = await LCApplication.HttpClient.Get<Dictionary<string, object>>(path,
headers: headers, queryParams: queryParams);
List<object> results = response["results"] as List<object>;
List<LCIMConversationMemberInfo> memberList = new List<LCIMConversationMemberInfo>();
foreach (Dictionary<string, object> item in results) {
LCIMConversationMemberInfo member = new LCIMConversationMemberInfo {
ConversationId = item["cid"] as string,
MemberId = item["clientId"] as string,
Role = item["role"] as string
};
memberList.Add(member);
}
return memberList;
}
public async Task<LCIMPageResult> QueryMutedMembers(int limit = 50, string next = null) {
ConvCommand conv = new ConvCommand {
Cid = Id,
Limit = limit,
Next = next
};
GenericCommand request = client.NewCommand(CommandType.Conv, OpType.QueryShutup);
request.ConvMessage = conv;
GenericCommand response = await client.client.SendRequest(request);
return new LCIMPageResult {
Results = response.ConvMessage.M.ToList(),
Next = response.ConvMessage.Next
};
}
private LCIMPartiallySuccessResult NewPartiallySuccessResult(IEnumerable<string> succesfulIds, IEnumerable<ErrorCommand> errors) {
LCIMPartiallySuccessResult result = new LCIMPartiallySuccessResult {
SuccessfulClientIdList = succesfulIds.ToList()
};
if (errors != null) {
result.FailureList = new List<LCIMOperationFailure>();
foreach (ErrorCommand error in errors) {
result.FailureList.Add(new LCIMOperationFailure(error));
}
}
return result;
} }
internal void MergeFrom(ConvCommand conv) { internal void MergeFrom(ConvCommand conv) {

View File

@ -1,7 +1,20 @@
using System; namespace LeanCloud.Realtime {
namespace LeanCloud.Realtime {
public class LCIMConversationMemberInfo { public class LCIMConversationMemberInfo {
/// <summary>
/// 群主
/// </summary>
public const string Owner = "Owner";
/// <summary>
/// 管理员
/// </summary>
public const string Manager = "Manager";
/// <summary>
/// 成员
/// </summary>
public const string Member = "Member";
public string ConversationId { public string ConversationId {
get; set; get; set;
} }
@ -10,12 +23,20 @@ namespace LeanCloud.Realtime {
get; set; get; set;
} }
public bool IsOwner {
get; set;
}
public string Role { public string Role {
get; set; get; set;
} }
public bool IsOwner {
get {
return Role == Owner;
}
}
public bool IsManager {
get {
return Role == Manager;
}
}
} }
} }

View File

@ -17,6 +17,11 @@ namespace LeanCloud.Realtime {
get; private set; get; private set;
} }
// TODO 判断过期
internal string SessionToken {
get; private set;
}
/// <summary> /// <summary>
/// 当前用户被加入某个对话的黑名单 /// 当前用户被加入某个对话的黑名单
/// </summary> /// </summary>
@ -85,9 +90,10 @@ namespace LeanCloud.Realtime {
}; };
await client.Connect(); await client.Connect();
// Open Session // Open Session
GenericCommand command = NewCommand(CommandType.Session, OpType.Open); GenericCommand request = NewCommand(CommandType.Session, OpType.Open);
command.SessionMessage = new SessionCommand(); request.SessionMessage = new SessionCommand();
await client.SendRequest(command); GenericCommand response = await client.SendRequest(request);
SessionToken = response.SessionMessage.St;
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,25 @@
using System.Linq;
using System.Collections.Generic;
using LeanCloud.Realtime.Protocol;
namespace LeanCloud.Realtime {
public class LCIMOperationFailure {
public int Code {
get; set;
}
public string Reason {
get; set;
}
public List<string> MemberList {
get; set;
}
public LCIMOperationFailure(ErrorCommand error) {
Code = error.Code;
Reason = error.Reason;
MemberList = error.Pids.ToList();
}
}
}

View File

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace LeanCloud.Realtime {
public class LCIMPageResult {
public List<string> Results {
get; internal set;
}
public string Next {
get; internal set;
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using LeanCloud.Storage;
namespace LeanCloud.Realtime {
public class LCIMPartiallySuccessResult {
public List<string> SuccessfulClientIdList {
get; internal set;
}
public List<LCIMOperationFailure> FailureList {
get; internal set;
}
public LCIMPartiallySuccessResult() {
}
}
}

View File

@ -2,14 +2,24 @@
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMBinaryMessage : LCIMMessage { public class LCIMBinaryMessage : LCIMMessage {
private byte[] data; public byte[] Data {
get; set;
}
public LCIMBinaryMessage(byte[] data) { public LCIMBinaryMessage(byte[] data) {
this.data = data; Data = data;
} }
internal override string Serialize() { internal override string Serialize() {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override string GetText() {
return null;
}
internal override byte[] GetBytes() {
return Data;
}
} }
} }

View File

@ -63,10 +63,17 @@ namespace LeanCloud.Realtime {
get; set; get; set;
} }
public bool MentionAll {
get; set;
}
public LCIMMessage() { public LCIMMessage() {
} }
internal abstract string Serialize(); internal abstract string Serialize();
internal abstract string GetText();
internal abstract byte[] GetBytes();
} }
} }

View File

@ -5,14 +5,20 @@ namespace LeanCloud.Realtime {
public class LCIMTextMessage : LCIMTypedMessage { public class LCIMTextMessage : LCIMTypedMessage {
const int TextMessageType = -1; const int TextMessageType = -1;
private string text; public string Text {
get; set;
}
public LCIMTextMessage(string text) : base(TextMessageType) { public LCIMTextMessage(string text) : base(TextMessageType) {
this.text = text; Text = text;
} }
internal override string Serialize() { internal override string Serialize() {
return text; return Text;
}
internal override string GetText() {
return Text;
} }
} }
} }

View File

@ -11,5 +11,13 @@ namespace LeanCloud.Realtime {
internal override string Serialize() { internal override string Serialize() {
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal override string GetText() {
return null;
}
internal override byte[] GetBytes() {
return null;
}
} }
} }

View File

@ -11,7 +11,7 @@ using Newtonsoft.Json;
using LeanCloud.Common; using LeanCloud.Common;
namespace LeanCloud.Storage.Internal.Http { namespace LeanCloud.Storage.Internal.Http {
internal class LCHttpClient { public class LCHttpClient {
private readonly string appId; private readonly string appId;
readonly string appKey; readonly string appKey;
@ -26,7 +26,7 @@ namespace LeanCloud.Storage.Internal.Http {
readonly MD5 md5; readonly MD5 md5;
internal LCHttpClient(string appId, string appKey, string server, string sdkVersion, string apiVersion) { public LCHttpClient(string appId, string appKey, string server, string sdkVersion, string apiVersion) {
this.appId = appId; this.appId = appId;
this.appKey = appKey; this.appKey = appKey;
this.server = server; this.server = server;
@ -42,7 +42,7 @@ namespace LeanCloud.Storage.Internal.Http {
md5 = MD5.Create(); md5 = MD5.Create();
} }
internal async Task<T> Get<T>(string path, public async Task<T> Get<T>(string path,
Dictionary<string, object> headers = null, Dictionary<string, object> headers = null,
Dictionary<string, object> queryParams = null) { Dictionary<string, object> queryParams = null) {
string url = await BuildUrl(path, queryParams); string url = await BuildUrl(path, queryParams);

View File

@ -30,7 +30,7 @@ namespace LeanCloud {
get; private set; get; private set;
} }
internal static LCHttpClient HttpClient { public static LCHttpClient HttpClient {
get; private set; get; private set;
} }

View File

@ -54,10 +54,27 @@ namespace RealtimeConsole {
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: false); LCIMConversation conversation = await client.CreateConversation(memberIdList, name: name, unique: true);
LCIMTextMessage textMessage = new LCIMTextMessage("hello, world"); LCIMTextMessage textMessage = new LCIMTextMessage("hello, world");
await conversation.Send(textMessage); await conversation.Send(textMessage);
await Task.Delay(3000);
LCIMTextMessage newMessage = new LCIMTextMessage("hello, code");
await conversation.Update(textMessage, newMessage);
//// 设置成员的角色
//await conversation.UpdateMemberRole("world", LCIMConversationMemberInfo.Manager);
//List<LCIMConversationMemberInfo> members = await conversation.GetAllMemberInfo();
//foreach (LCIMConversationMemberInfo member in members) {
// Console.WriteLine(member.MemberId);
//}
//LCIMTextMessage textMessage = new LCIMTextMessage("hello, world");
//await conversation.Send(textMessage);
} }
} }
} }