* LCIMClient.cs:

* LCIMPageResult.cs:
* LCIMChatRoom.cs:
* LCIMConversation.cs:
* LCIMConversationQuery.cs:
* LCIMGoAwayController.cs:
* LCIMUnreadController.cs:
* LCWebSocketConnection.cs:
* LCIMMessageController.cs:
* LCIMSessionController.cs:
* LCIMConversationController.cs:

* Program.cs: chore: 完善接口
oneRain 2020-03-27 15:52:34 +08:00
parent bfd14f5b59
commit a1376a346d
12 changed files with 475 additions and 104 deletions

View File

@ -5,7 +5,8 @@ using LeanCloud.Realtime.Protocol;
namespace LeanCloud.Realtime {
public class LCIMChatRoom : LCIMConversation {
public LCIMChatRoom(LCIMClient client) : base(client) {
public LCIMChatRoom(LCIMClient client) :
base(client) {
}
public async Task<int> GetOnlineMembersCount() {

View File

@ -2,8 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json;
using Google.Protobuf;
using System.Collections.ObjectModel;
using LeanCloud.Realtime.Protocol;
using LeanCloud.Storage;
@ -29,12 +28,16 @@ namespace LeanCloud.Realtime {
get; set;
}
public List<string> MemberIdList {
get; internal set;
public ReadOnlyCollection<string> MemberIdList {
get {
return new ReadOnlyCollection<string>(ids.ToList());
}
}
public List<string> MutedMemberIdList {
get; internal set;
public ReadOnlyCollection<string> MutedMemberIds {
get {
return new ReadOnlyCollection<string>(mutedIds.ToList());
}
}
public int Unread {
@ -76,6 +79,10 @@ namespace LeanCloud.Realtime {
private Dictionary<string, object> customProperties;
internal HashSet<string> ids;
internal HashSet<string> mutedIds;
internal LCIMConversation(LCIMClient client) {
Client = client;
customProperties = new Dictionary<string, object>();
@ -286,7 +293,7 @@ namespace LeanCloud.Realtime {
/// 获取对话中成员的角色(只返回管理员)
/// </summary>
/// <returns></returns>
public async Task<List<LCIMConversationMemberInfo>> GetAllMemberInfo() {
public async Task<ReadOnlyCollection<LCIMConversationMemberInfo>> GetAllMemberInfo() {
return await Client.ConversationController.GetAllMemberInfo(Id);
}
@ -299,7 +306,7 @@ namespace LeanCloud.Realtime {
if (string.IsNullOrEmpty(memberId)) {
throw new ArgumentNullException(nameof(memberId));
}
List<LCIMConversationMemberInfo> members = await GetAllMemberInfo();
ReadOnlyCollection<LCIMConversationMemberInfo> members = await GetAllMemberInfo();
foreach (LCIMConversationMemberInfo member in members) {
if (member.MemberId == memberId) {
return member;
@ -308,11 +315,25 @@ namespace LeanCloud.Realtime {
return null;
}
public async Task<LCIMPageResult> QueryMutedMembers(int limit = 10, string next = null) {
/// <summary>
/// 查询禁言用户
/// </summary>
/// <param name="limit"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task<LCIMPageResult> QueryMutedMembers(int limit = 10,
string next = null) {
return await Client.ConversationController.QueryMutedMembers(Id, limit, next);
}
public async Task<LCIMPageResult> QueryBlockedMembers(int limit = 10, string next = null) {
/// <summary>
/// 查询黑名单用户
/// </summary>
/// <param name="limit"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task<LCIMPageResult> QueryBlockedMembers(int limit = 10,
string next = null) {
return await Client.ConversationController.QueryBlockedMembers(Id, limit, next);
}
@ -342,7 +363,7 @@ namespace LeanCloud.Realtime {
UpdatedAt = DateTime.Parse(conv.Udate);
}
if (conv.M.Count > 0) {
MemberIdList = conv.M.ToList();
ids = new HashSet<string>(conv.M.ToList());
}
}
@ -363,10 +384,10 @@ namespace LeanCloud.Realtime {
CreatorId = co as string;
}
if (conv.TryGetValue("m", out object mo)) {
MemberIdList = mo as List<string>;
ids = new HashSet<string>(mo as List<string>);
}
if (conv.TryGetValue("mu", out object muo)) {
MutedMemberIdList = muo as List<string>;
mutedIds = new HashSet<string>(muo as List<string>);
}
}

View File

@ -1,6 +1,7 @@
using System.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using LeanCloud.Storage.Internal.Query;
namespace LeanCloud.Realtime {
@ -22,7 +23,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public LCIMConversationQuery WhereEqualTo(string key, object value) {
public LCIMConversationQuery WhereEqualTo(string key,
object value) {
Condition.WhereEqualTo(key, value);
return this;
}
@ -33,7 +35,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public LCIMConversationQuery WhereNotEqualTo(string key, object value) {
public LCIMConversationQuery WhereNotEqualTo(string key,
object value) {
Condition.WhereNotEqualTo(key, value);
return this;
}
@ -44,7 +47,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public LCIMConversationQuery WhereContainedIn(string key, IEnumerable values) {
public LCIMConversationQuery WhereContainedIn(string key,
IEnumerable values) {
Condition.WhereContainedIn(key, values);
return this;
}
@ -55,7 +59,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public LCIMConversationQuery WhereContainsAll(string key, IEnumerable values) {
public LCIMConversationQuery WhereContainsAll(string key,
IEnumerable values) {
Condition.WhereContainsAll(key, values);
return this;
}
@ -86,7 +91,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="size"></param>
/// <returns></returns>
public LCIMConversationQuery WhereSizeEqualTo(string key, int size) {
public LCIMConversationQuery WhereSizeEqualTo(string key,
int size) {
Condition.WhereSizeEqualTo(key, size);
return this;
}
@ -97,7 +103,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public LCIMConversationQuery WhereGreaterThan(string key, object value) {
public LCIMConversationQuery WhereGreaterThan(string key,
object value) {
Condition.WhereGreaterThan(key, value);
return this;
}
@ -108,7 +115,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public LCIMConversationQuery WhereGreaterThanOrEqualTo(string key, object value) {
public LCIMConversationQuery WhereGreaterThanOrEqualTo(string key,
object value) {
Condition.WhereGreaterThanOrEqualTo(key, value);
return this;
}
@ -119,7 +127,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public LCIMConversationQuery WhereLessThan(string key, object value) {
public LCIMConversationQuery WhereLessThan(string key,
object value) {
Condition.WhereLessThan(key, value);
return this;
}
@ -130,7 +139,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public LCIMConversationQuery WhereLessThanOrEqualTo(string key, object value) {
public LCIMConversationQuery WhereLessThanOrEqualTo(string key,
object value) {
Condition.WhereLessThanOrEqualTo(key, value);
return this;
}
@ -141,7 +151,8 @@ namespace LeanCloud.Realtime {
/// <param name="key"></param>
/// <param name="prefix"></param>
/// <returns></returns>
public LCIMConversationQuery WhereStartsWith(string key, string prefix) {
public LCIMConversationQuery WhereStartsWith(string key,
string prefix) {
Condition.WhereStartsWith(key, prefix);
return this;
}
@ -236,7 +247,7 @@ namespace LeanCloud.Realtime {
/// 查找
/// </summary>
/// <returns></returns>
public async Task<List<LCIMConversation>> Find() {
public async Task<ReadOnlyCollection<LCIMConversation>> Find() {
return await client.ConversationController.Find(this);
}
}

View File

@ -1,13 +1,13 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Newtonsoft.Json;
using LeanCloud.Realtime.Protocol;
using LeanCloud.Storage.Internal;
using LeanCloud.Storage.Internal.Codec;
using LeanCloud.Common;
using Google.Protobuf;
namespace LeanCloud.Realtime.Internal.Controller {
internal class LCIMConversationController : LCIMController {
@ -15,6 +15,19 @@ namespace LeanCloud.Realtime.Internal.Controller {
}
#region 内部接口
/// <summary>
/// 创建对话
/// </summary>
/// <param name="members"></param>
/// <param name="name"></param>
/// <param name="transient"></param>
/// <param name="unique"></param>
/// <param name="temporary"></param>
/// <param name="temporaryTtl"></param>
/// <param name="properties"></param>
/// <returns></returns>
internal async Task<LCIMConversation> CreateConv(
IEnumerable<string> members = null,
string name = null,
@ -66,12 +79,17 @@ namespace LeanCloud.Realtime.Internal.Controller {
}
// 合并请求数据
conversation.Name = name;
conversation.MemberIdList = members?.ToList();
conversation.ids = members != null ? new HashSet<string>(members) : null;
// 合并服务端推送的数据
conversation.MergeFrom(response.ConvMessage);
return conversation;
}
/// <summary>
/// 查询成员数量
/// </summary>
/// <param name="convId"></param>
/// <returns></returns>
internal async Task<int> GetMembersCount(string convId) {
ConvCommand conv = new ConvCommand {
Cid = convId,
@ -82,6 +100,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return response.ConvMessage.Count;
}
/// <summary>
/// 标记对话的消息已读
/// </summary>
/// <param name="convId"></param>
/// <param name="message"></param>
/// <returns></returns>
internal async Task Read(string convId,
LCIMMessage message) {
ReadCommand read = new ReadCommand();
@ -96,6 +120,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
await Client.Connection.SendRequest(request);
}
/// <summary>
/// 更新对话属性
/// </summary>
/// <param name="convId"></param>
/// <param name="attributes"></param>
/// <returns></returns>
internal async Task<Dictionary<string, object>> UpdateInfo(string convId,
Dictionary<string, object> attributes) {
ConvCommand conv = new ConvCommand {
@ -116,6 +146,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return null;
}
/// <summary>
/// 增加成员
/// </summary>
/// <param name="convId"></param>
/// <param name="clientIds"></param>
/// <returns></returns>
internal async Task<LCIMPartiallySuccessResult> AddMembers(string convId,
IEnumerable<string> clientIds) {
ConvCommand conv = new ConvCommand {
@ -140,6 +176,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return NewPartiallySuccessResult(allowedIds, errors);
}
/// <summary>
/// 移除成员
/// </summary>
/// <param name="convId"></param>
/// <param name="removeIds"></param>
/// <returns></returns>
internal async Task<LCIMPartiallySuccessResult> RemoveMembers(string convId,
IEnumerable<string> removeIds) {
ConvCommand conv = new ConvCommand {
@ -164,6 +206,11 @@ namespace LeanCloud.Realtime.Internal.Controller {
return NewPartiallySuccessResult(allowedIds, errors);
}
/// <summary>
/// 静音
/// </summary>
/// <param name="convId"></param>
/// <returns></returns>
internal async Task Mute(string convId) {
ConvCommand conv = new ConvCommand {
Cid = convId
@ -173,6 +220,11 @@ namespace LeanCloud.Realtime.Internal.Controller {
await Client.Connection.SendRequest(request);
}
/// <summary>
/// 解除静音
/// </summary>
/// <param name="convId"></param>
/// <returns></returns>
internal async Task Unmute(string convId) {
ConvCommand conv = new ConvCommand {
Cid = convId
@ -182,6 +234,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
await Client.Connection.SendRequest(request);
}
/// <summary>
/// 禁言用户
/// </summary>
/// <param name="convId"></param>
/// <param name="clientIds"></param>
/// <returns></returns>
internal async Task<LCIMPartiallySuccessResult> MuteMembers(string convId,
IEnumerable<string> clientIds) {
if (clientIds == null || clientIds.Count() == 0) {
@ -197,6 +255,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids);
}
/// <summary>
/// 解除用户禁言
/// </summary>
/// <param name="convId"></param>
/// <param name="clientIds"></param>
/// <returns></returns>
internal async Task<LCIMPartiallySuccessResult> UnmuteMembers(string convId,
IEnumerable<string> clientIds) {
ConvCommand conv = new ConvCommand {
@ -209,6 +273,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return NewPartiallySuccessResult(response.ConvMessage.AllowedPids, response.ConvMessage.FailedPids);
}
/// <summary>
/// 拉黑成员
/// </summary>
/// <param name="convId"></param>
/// <param name="clientIds"></param>
/// <returns></returns>
internal async Task<LCIMPartiallySuccessResult> BlockMembers(string convId,
IEnumerable<string> clientIds) {
BlacklistCommand blacklist = new BlacklistCommand {
@ -230,6 +300,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids);
}
/// <summary>
/// 移除成员黑名单
/// </summary>
/// <param name="convId"></param>
/// <param name="clientIds"></param>
/// <returns></returns>
internal async Task<LCIMPartiallySuccessResult> UnblockMembers(string convId,
IEnumerable<string> clientIds) {
BlacklistCommand blacklist = new BlacklistCommand {
@ -251,6 +327,13 @@ namespace LeanCloud.Realtime.Internal.Controller {
return NewPartiallySuccessResult(response.BlacklistMessage.AllowedPids, response.BlacklistMessage.FailedPids);
}
/// <summary>
/// 修改成员角色
/// </summary>
/// <param name="convId"></param>
/// <param name="memberId"></param>
/// <param name="role"></param>
/// <returns></returns>
internal async Task UpdateMemberRole(string convId,
string memberId,
string role) {
@ -267,7 +350,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
GenericCommand response = await Client.Connection.SendRequest(request);
}
internal async Task<List<LCIMConversationMemberInfo>> GetAllMemberInfo(string convId) {
/// <summary>
/// 获取所有成员角色
/// </summary>
/// <param name="convId"></param>
/// <returns></returns>
internal async Task<ReadOnlyCollection<LCIMConversationMemberInfo>> GetAllMemberInfo(string convId) {
string path = "classes/_ConversationMemberInfo";
string token = await Client.SessionController.GetToken();
Dictionary<string, object> headers = new Dictionary<string, object> {
@ -280,18 +368,23 @@ namespace LeanCloud.Realtime.Internal.Controller {
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
return results.Select(item => {
Dictionary<string, object> memberInfo = item as Dictionary<string, object>;
return new LCIMConversationMemberInfo {
ConversationId = memberInfo["cid"] as string,
MemberId = memberInfo["clientId"] as string,
Role = memberInfo["role"] as string
};
memberList.Add(member);
}
return memberList;
}).ToList().AsReadOnly();
}
/// <summary>
/// 查询禁言成员
/// </summary>
/// <param name="convId"></param>
/// <param name="limit"></param>
/// <param name="next"></param>
/// <returns></returns>
internal async Task<LCIMPageResult> QueryMutedMembers(string convId,
int limit = 10,
string next = null) {
@ -304,11 +397,18 @@ namespace LeanCloud.Realtime.Internal.Controller {
request.ConvMessage = conv;
GenericCommand response = await Client.Connection.SendRequest(request);
return new LCIMPageResult {
Results = response.ConvMessage.M.ToList(),
Results = new ReadOnlyCollection<string>(response.ConvMessage.M),
Next = response.ConvMessage.Next
};
}
/// <summary>
/// 查询黑名单用户
/// </summary>
/// <param name="convId"></param>
/// <param name="limit"></param>
/// <param name="next"></param>
/// <returns></returns>
internal async Task<LCIMPageResult> QueryBlockedMembers(string convId,
int limit = 10,
string next = null) {
@ -321,12 +421,17 @@ namespace LeanCloud.Realtime.Internal.Controller {
request.BlacklistMessage = black;
GenericCommand response = await Client.Connection.SendRequest(request);
return new LCIMPageResult {
Results = response.BlacklistMessage.BlockedPids.ToList(),
Results = new ReadOnlyCollection<string>(response.BlacklistMessage.BlockedPids),
Next = response.BlacklistMessage.Next
};
}
internal async Task<List<LCIMConversation>> Find(LCIMConversationQuery query) {
/// <summary>
/// 查找
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
internal async Task<ReadOnlyCollection<LCIMConversation>> Find(LCIMConversationQuery query) {
GenericCommand command = new GenericCommand {
Cmd = CommandType.Conv,
Op = OpType.Query,
@ -348,7 +453,7 @@ namespace LeanCloud.Realtime.Internal.Controller {
GenericCommand response = await Connection.SendRequest(command);
JsonObjectMessage results = response.ConvMessage.Results;
List<object> convs = JsonConvert.DeserializeObject<List<object>>(results.Data, new LCJsonConverter());
List<LCIMConversation> convList = convs.Select(item => {
return convs.Select(item => {
Dictionary<string, object> conv = item as Dictionary<string, object>;
string convId = conv["objectId"] as string;
if (!Client.ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
@ -359,11 +464,13 @@ namespace LeanCloud.Realtime.Internal.Controller {
}
conversation.MergeFrom(conv);
return conversation;
}).ToList();
return convList;
}).ToList().AsReadOnly();
}
internal async Task<List<LCIMTemporaryConversation>> GetTemporaryConversations(IEnumerable<string> convIds) {
if (convIds == null || convIds.Count() == 0) {
return null;
}
ConvCommand convMessage = new ConvCommand();
convMessage.TempConvIds.AddRange(convIds);
GenericCommand request = Client.NewCommand(CommandType.Conv, OpType.Query);
@ -393,68 +500,194 @@ namespace LeanCloud.Realtime.Internal.Controller {
return result;
}
#endregion
#region 消息处理
internal override async Task OnNotification(GenericCommand notification) {
ConvCommand conv = notification.ConvMessage;
ConvCommand convMessage = notification.ConvMessage;
switch (notification.Op) {
case OpType.Joined:
await OnConversationJoined(conv);
await OnJoined(convMessage);
break;
case OpType.MembersJoined:
await OnConversationMembersJoined(conv);
await OnMembersJoined(convMessage);
break;
case OpType.Left:
await OnConversationLeft(conv);
await OnLeft(convMessage);
break;
case OpType.MembersLeft:
await OnConversationMemberLeft(conv);
await OnMemberLeft(convMessage);
break;
case OpType.Blocked:
await OnBlocked(convMessage);
break;
case OpType.MembersBlocked:
await OnMembersBlocked(convMessage);
break;
case OpType.MembersUnblocked:
await OnMembersUnblocked(convMessage);
break;
case OpType.Shutuped:
await OnMuted(convMessage);
break;
case OpType.MembersShutuped:
await OnMembersMuted(convMessage);
break;
case OpType.MembersUnshutuped:
await OnMembersUnmuted(convMessage);
break;
case OpType.Updated:
await OnConversationPropertiesUpdated(conv);
await OnPropertiesUpdated(convMessage);
break;
case OpType.MemberInfoChanged:
await OnConversationMemberInfoChanged(conv);
await OnMemberInfoChanged(convMessage);
break;
default:
break;
}
}
private async Task OnConversationJoined(ConvCommand conv) {
LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid);
conversation.MergeFrom(conv);
Client.OnInvited?.Invoke(conversation, conv.InitBy);
/// <summary>
/// 当前用户加入会话
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnJoined(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
conversation.MergeFrom(convMessage);
Client.OnInvited?.Invoke(conversation, convMessage.InitBy);
}
private async Task OnConversationMembersJoined(ConvCommand conv) {
LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid);
conversation.MergeFrom(conv);
Client.OnMembersJoined?.Invoke(conversation, conv.M.ToList(), conv.InitBy);
/// <summary>
/// 有用户加入会话
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMembersJoined(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
ReadOnlyCollection<string> joinedIds = new ReadOnlyCollection<string>(convMessage.M);
conversation.ids.Union(joinedIds);
Client.OnMembersJoined?.Invoke(conversation, joinedIds, convMessage.InitBy);
}
private async Task OnConversationLeft(ConvCommand conv) {
LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid);
Client.OnKicked?.Invoke(conversation, conv.InitBy);
/// <summary>
/// 当前用户离开会话
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnLeft(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
Client.OnKicked?.Invoke(conversation, convMessage.InitBy);
// TODO 从内存中清除对话
}
private async Task OnConversationMemberLeft(ConvCommand conv) {
LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid);
List<string> leftIdList = conv.M.ToList();
Client.OnMembersLeft?.Invoke(conversation, leftIdList, conv.InitBy);
/// <summary>
/// 有成员离开会话
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMemberLeft(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
ReadOnlyCollection<string> leftIdList = new ReadOnlyCollection<string>(convMessage.M);
conversation.ids.RemoveWhere(item => leftIdList.Contains(item));
Client.OnMembersLeft?.Invoke(conversation, leftIdList, convMessage.InitBy);
}
private async Task OnConversationPropertiesUpdated(ConvCommand conv) {
/// <summary>
/// 当前用户被禁言
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMuted(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
Client.OnMuted?.Invoke(conversation, convMessage.InitBy);
}
/// <summary>
/// 有成员被禁言
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMembersMuted(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
ReadOnlyCollection<string> mutedMemberIds = new ReadOnlyCollection<string>(convMessage.M);
conversation.mutedIds.Union(mutedMemberIds);
Client.OnMembersMuted?.Invoke(conversation, mutedMemberIds, convMessage.InitBy);
}
/// <summary>
/// 有成员被解除禁言
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMembersUnmuted(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
ReadOnlyCollection<string> unmutedMemberIds = new ReadOnlyCollection<string>(convMessage.M);
conversation.mutedIds.RemoveWhere(id => unmutedMemberIds.Contains(id));
Client.OnMembersUnmuted?.Invoke(conversation, unmutedMemberIds, convMessage.InitBy);
}
/// <summary>
/// 当前用户被拉黑
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnBlocked(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
Client.OnBlocked?.Invoke(conversation, convMessage.InitBy);
}
/// <summary>
/// 有用户被拉黑
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMembersBlocked(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
ReadOnlyCollection<string> blockedMemberIds = convMessage.M.ToList().AsReadOnly();
Client.OnMembersBlocked?.Invoke(conversation, blockedMemberIds, convMessage.InitBy);
}
/// <summary>
/// 有用户被移除黑名单
/// </summary>
/// <param name="convMessage"></param>
/// <returns></returns>
private async Task OnMembersUnblocked(ConvCommand convMessage) {
LCIMConversation conversation = await Client.GetOrQueryConversation(convMessage.Cid);
ReadOnlyCollection<string> unblockedMemberIds = convMessage.M.ToList().AsReadOnly();
Client.OnMembersUnblocked?.Invoke(conversation, unblockedMemberIds, convMessage.InitBy);
}
/// <summary>
/// 对话属性被修改
/// </summary>
/// <param name="conv"></param>
/// <returns></returns>
private async Task OnPropertiesUpdated(ConvCommand conv) {
LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid);
Dictionary<string, object> updatedAttr = JsonConvert.DeserializeObject<Dictionary<string, object>>(conv.AttrModified.Data,
new LCJsonConverter());
// 更新内存数据
conversation.MergeInfo(updatedAttr);
Client.OnConversationInfoUpdated?.Invoke(conversation, updatedAttr, conv.InitBy);
Client.OnConversationInfoUpdated?.Invoke(conversation,
new ReadOnlyDictionary<string, object>(updatedAttr),
conv.InitBy);
}
private async Task OnConversationMemberInfoChanged(ConvCommand conv) {
/// <summary>
/// 用户角色被修改
/// </summary>
/// <param name="conv"></param>
/// <returns></returns>
private async Task OnMemberInfoChanged(ConvCommand conv) {
LCIMConversation conversation = await Client.GetOrQueryConversation(conv.Cid);
ConvMemberInfo memberInfo = conv.Info;
Client.OnMemberInfoUpdated?.Invoke(conversation, memberInfo.Pid, memberInfo.Role, conv.InitBy);
}
#endregion
}
}

View File

@ -7,10 +7,14 @@ namespace LeanCloud.Realtime.Internal.Controller {
}
#region 消息处理
internal override async Task OnNotification(GenericCommand notification) {
// 清空缓存,断开连接,等待重新连接
Connection.Router.Reset();
await Connection.Close();
}
#endregion
}
}

View File

@ -12,6 +12,14 @@ namespace LeanCloud.Realtime.Internal.Controller {
}
#region 内部接口
/// <summary>
/// 发送消息
/// </summary>
/// <param name="convId"></param>
/// <param name="message"></param>
/// <returns></returns>
internal async Task<LCIMMessage> Send(string convId,
LCIMMessage message) {
DirectCommand direct = new DirectCommand {
@ -35,6 +43,12 @@ namespace LeanCloud.Realtime.Internal.Controller {
return message;
}
/// <summary>
/// 撤回消息
/// </summary>
/// <param name="convId"></param>
/// <param name="message"></param>
/// <returns></returns>
internal async Task RecallMessage(string convId,
LCIMMessage message) {
PatchCommand patch = new PatchCommand();
@ -49,6 +63,13 @@ namespace LeanCloud.Realtime.Internal.Controller {
await Client.Connection.SendRequest(request);
}
/// <summary>
/// 修改消息
/// </summary>
/// <param name="convId"></param>
/// <param name="oldMessage"></param>
/// <param name="newMessage"></param>
/// <returns></returns>
internal async Task UpdateMessage(string convId,
LCIMMessage oldMessage,
LCIMMessage newMessage) {
@ -76,6 +97,16 @@ namespace LeanCloud.Realtime.Internal.Controller {
GenericCommand response = await Client.Connection.SendRequest(request);
}
/// <summary>
/// 查询消息
/// </summary>
/// <param name="convId"></param>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="direction"></param>
/// <param name="limit"></param>
/// <param name="messageType"></param>
/// <returns></returns>
internal async Task<List<LCIMMessage>> QueryMessages(string convId,
LCIMMessageQueryEndpoint start = null,
LCIMMessageQueryEndpoint end = null,
@ -109,6 +140,10 @@ namespace LeanCloud.Realtime.Internal.Controller {
return null;
}
#endregion
#region 消息处理
internal override async Task OnNotification(GenericCommand notification) {
DirectCommand direct = notification.DirectMessage;
LCIMMessage message = null;
@ -150,5 +185,7 @@ namespace LeanCloud.Realtime.Internal.Controller {
LCIMConversation conversation = await Client.GetOrQueryConversation(direct.Cid);
Client.OnMessage?.Invoke(conversation, message);
}
#endregion
}
}

View File

@ -7,10 +7,17 @@ namespace LeanCloud.Realtime.Internal.Controller {
private string token;
private DateTimeOffset expiredAt;
internal LCIMSessionController(LCIMClient client) : base(client) {
internal LCIMSessionController(LCIMClient client)
: base(client) {
}
#region 内部接口
/// <summary>
/// 打开会话
/// </summary>
/// <returns></returns>
internal async Task Open() {
SessionCommand session = NewSessionCommand();
GenericCommand request = Client.NewCommand(CommandType.Session, OpType.Open);
@ -19,11 +26,19 @@ namespace LeanCloud.Realtime.Internal.Controller {
UpdateSession(response.SessionMessage);
}
/// <summary>
/// 关闭会话
/// </summary>
/// <returns></returns>
internal async Task Close() {
GenericCommand request = Client.NewCommand(CommandType.Session, OpType.Close);
await Client.Connection.SendRequest(request);
}
/// <summary>
/// 获取可用 token
/// </summary>
/// <returns></returns>
internal async Task<string> GetToken() {
if (IsExpired) {
await Refresh();
@ -31,6 +46,8 @@ namespace LeanCloud.Realtime.Internal.Controller {
return token;
}
#endregion
private async Task Refresh() {
SessionCommand session = NewSessionCommand();
GenericCommand request = Client.NewCommand(CommandType.Session, OpType.Refresh);
@ -56,6 +73,14 @@ namespace LeanCloud.Realtime.Internal.Controller {
expiredAt = DateTimeOffset.Now + TimeSpan.FromSeconds(ttl);
}
private bool IsExpired {
get {
return DateTimeOffset.Now > expiredAt;
}
}
#region 消息处理
internal override async Task OnNotification(GenericCommand notification) {
switch (notification.Op) {
case OpType.Closed:
@ -66,12 +91,11 @@ namespace LeanCloud.Realtime.Internal.Controller {
}
}
private bool IsExpired {
get {
return DateTimeOffset.Now > expiredAt;
}
}
/// <summary>
/// 被关闭
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
private async Task OnClosed(SessionCommand session) {
int code = session.Code;
string reason = session.Reason;
@ -79,5 +103,7 @@ namespace LeanCloud.Realtime.Internal.Controller {
await Connection.Close();
Client.OnClose?.Invoke(code, reason, detail);
}
#endregion
}
}

View File

@ -1,6 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Newtonsoft.Json;
using LeanCloud.Realtime.Protocol;
using LeanCloud.Storage.Internal;
@ -8,17 +9,20 @@ using LeanCloud.Storage.Internal;
namespace LeanCloud.Realtime.Internal.Controller {
internal class LCIMUnreadController : LCIMController {
internal LCIMUnreadController(LCIMClient client) : base(client) {
}
#region 消息处理
internal override async Task OnNotification(GenericCommand notification) {
UnreadCommand unread = notification.UnreadMessage;
IEnumerable<string> convIds = unread.Convs
.Select(conv => conv.Cid);
Dictionary<string, LCIMConversation> conversations = (await Client.GetConversationList(convIds))
Dictionary<string, LCIMConversation> conversationDict = (await Client.GetConversationList(convIds))
.ToDictionary(item => item.Id);
List<LCIMConversation> conversationList = unread.Convs.Select(conv => {
LCIMConversation conversation = conversations[conv.Cid];
ReadOnlyCollection<LCIMConversation> conversations = unread.Convs.Select(conv => {
LCIMConversation conversation = conversationDict[conv.Cid];
conversation.Unread = conv.Unread;
// 解析最后一条消息
Dictionary<string, object> msgData = JsonConvert.DeserializeObject<Dictionary<string, object>>(conv.Data,
@ -53,8 +57,10 @@ namespace LeanCloud.Realtime.Internal.Controller {
message.SentTimestamp = conv.Timestamp;
conversation.LastMessage = message;
return conversation;
}).ToList();
Client.OnUnreadMessagesCountUpdated?.Invoke(conversationList);
}).ToList().AsReadOnly();
Client.OnUnreadMessagesCountUpdated?.Invoke(conversations);
}
#endregion
}
}

View File

@ -116,7 +116,7 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
ErrorCommand error = command.ErrorMessage;
int code = error.Code;
string detail = error.Detail;
// TODO 包装成异常抛出
// 包装成异常抛出
LCException exception = new LCException(code, detail);
tcs.SetException(exception);
} else {

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.ObjectModel;
using LeanCloud.Realtime.Internal.WebSocket;
using LeanCloud.Realtime.Protocol;
using LeanCloud.Realtime.Internal.Controller;
@ -78,7 +79,7 @@ namespace LeanCloud.Realtime {
/// <summary>
/// 该对话信息被更新
/// </summary>
public Action<LCIMConversation, Dictionary<string, object>, string> OnConversationInfoUpdated;
public Action<LCIMConversation, ReadOnlyDictionary<string, object>, string> OnConversationInfoUpdated;
/// <summary>
/// 当前用户被添加至某个对话
@ -97,42 +98,42 @@ namespace LeanCloud.Realtime {
/// <summary>
/// 有用户被添加至某个对话
/// </summary>
public Action<LCIMConversation, List<string>, string> OnMembersJoined {
public Action<LCIMConversation, ReadOnlyCollection<string>, string> OnMembersJoined {
get; set;
}
/// <summary>
/// 有成员被从某个对话中移除
/// </summary>
public Action<LCIMConversation, List<string>, string> OnMembersLeft {
public Action<LCIMConversation, ReadOnlyCollection<string>, string> OnMembersLeft {
get; set;
}
/// <summary>
/// 有成员被加入某个对话的黑名单
/// </summary>
public Action<LCIMConversation, List<string>, string> OnMembersBlocked {
public Action<LCIMConversation, ReadOnlyCollection<string>, string> OnMembersBlocked {
get; set;
}
/// <summary>
/// 有成员被移出某个对话的黑名单
/// </summary>
public Action<LCIMConversation, List<string>, string> OnMembersUnblocked {
public Action<LCIMConversation, ReadOnlyCollection<string>, string> OnMembersUnblocked {
get; set;
}
/// <summary>
/// 有成员在某个对话中被禁言
/// </summary>
public Action<LCIMConversation, List<string>, string> OnMembersMuted {
public Action<LCIMConversation, ReadOnlyCollection<string>, string> OnMembersMuted {
get; set;
}
/// <summary>
/// 有成员被移出某个对话的黑名单
/// </summary>
public Action<LCIMConversation, List<string>, string> OnMembersUnmuted {
public Action<LCIMConversation, ReadOnlyCollection<string>, string> OnMembersUnmuted {
get; set;
}
@ -165,7 +166,7 @@ namespace LeanCloud.Realtime {
/// <summary>
/// 未读消息数目更新
/// </summary>
public Action<List<LCIMConversation>> OnUnreadMessagesCountUpdated {
public Action<ReadOnlyCollection<LCIMConversation>> OnUnreadMessagesCountUpdated {
get; set;
}
@ -318,7 +319,7 @@ namespace LeanCloud.Realtime {
LCIMConversationQuery query = GetQuery()
.WhereEqualTo("objectId", id)
.Limit(1);
List<LCIMConversation> results = await ConversationController.Find(query);
ReadOnlyCollection<LCIMConversation> results = await ConversationController.Find(query);
if (results == null || results.Count < 1) {
return null;
}
@ -330,7 +331,7 @@ namespace LeanCloud.Realtime {
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public async Task<List<LCIMConversation>> GetConversationList(IEnumerable<string> ids) {
public async Task<ReadOnlyCollection<LCIMConversation>> GetConversationList(IEnumerable<string> ids) {
if (ids == null || ids.Count() == 0) {
throw new ArgumentNullException(nameof(ids));
}
@ -341,13 +342,19 @@ namespace LeanCloud.Realtime {
IEnumerable<string> convIds = ids.Where(item => {
return !tempConvIds.Contains(item);
});
List<LCIMTemporaryConversation> temporaryConversations = await ConversationController.GetTemporaryConversations(tempConvIds);
LCIMConversationQuery query = GetQuery()
.WhereContainedIn("objectId", convIds)
.Limit(999);
List<LCIMConversation> conversations = await ConversationController.Find(query);
conversations.AddRange(temporaryConversations);
return conversations;
List<LCIMConversation> conversationList = new List<LCIMConversation>();
if (tempConvIds.Count() > 0) {
List<LCIMTemporaryConversation> temporaryConversations = await ConversationController.GetTemporaryConversations(tempConvIds);
conversationList.AddRange(temporaryConversations);
}
if (convIds.Count() > 0) {
LCIMConversationQuery query = GetQuery()
.WhereContainedIn("objectId", convIds)
.Limit(convIds.Count());
ReadOnlyCollection<LCIMConversation> conversations = await ConversationController.Find(query);
conversationList.AddRange(conversations);
}
return conversationList.AsReadOnly();
}
/// <summary>

View File

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

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud;
@ -31,7 +32,7 @@ namespace RealtimeConsole {
SingleThreadSynchronizationContext.Run(async () => {
Console.WriteLine($"start at {Thread.CurrentThread.ManagedThreadId}");
await Run("cc2");
await Run("cc4");
//await ChatRoom();
//await TemporaryConversation();
//await CreateConversation();
@ -55,12 +56,36 @@ namespace RealtimeConsole {
//Unread().Wait();
//DemoAsync().Wait();
//SingleThreadSynchronizationContext.Run(async () => {
// await DemoAsync();
//});
Console.ReadKey(true);
}
static async Task DemoAsync() {
Dictionary<int, int> d = new Dictionary<int, int>();
for (int i = 0; i < 10000; i++) {
int id = Thread.CurrentThread.ManagedThreadId;
int count;
d[id] = d.TryGetValue(id, out count) ? count + 1 : 1;
await Task.Yield();
}
foreach (KeyValuePair<int, int> kv in d) {
Console.WriteLine(kv);
}
}
static async Task Run(string id) {
LCIMClient client = new LCIMClient(id);
await client.Open();
client.OnUnreadMessagesCountUpdated = (conversations) => {
foreach (LCIMConversation conv in conversations) {
Console.WriteLine($"unread: {conv.Id}");
}
};
client.OnMessage = (conversation, message) => {
Console.WriteLine($"recv: {conversation.Id}, {message.Id} at {Thread.CurrentThread.ManagedThreadId}");
};
@ -77,12 +102,12 @@ namespace RealtimeConsole {
static async Task QueryMyConversation() {
LCIMClient cc1 = new LCIMClient("cc1");
await cc1.Open();
List<LCIMConversation> conversationList = await cc1.GetQuery()
ReadOnlyCollection<LCIMConversation> conversationList = await cc1.GetQuery()
.WhereEqualTo("objectId", "5e7c283790aef5aa846b5683")
.Find();
conversationList.ForEach(conv => {
foreach (LCIMConversation conv in conversationList) {
Console.WriteLine($"convId: {conv.Id}");
});
}
}
static async Task Unread() {