using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using System.Collections.ObjectModel; using LeanCloud.Common; using LeanCloud.Storage; using LeanCloud.Realtime.Internal.Protocol; using LeanCloud.Realtime.Internal.Controller; using LeanCloud.Realtime.Internal.Connection; namespace LeanCloud.Realtime { /// /// 通信客户端 /// public class LCIMClient { internal Dictionary ConversationDict; public string Id { get; private set; } public string Tag { get; private set; } public string DeviceId { get; private set; } internal string SessionToken { get; private set; } #region 事件 #region 连接状态事件 /// /// 客户端连接断开 /// public Action OnPaused { get; set; } /// /// 客户端连接恢复正常 /// public Action OnResume { get; set; } /// /// 当前客户端被服务端强行下线 /// public Action OnClose { get; set; } #endregion #region 对话事件 /// /// 当前用户被加入某个对话的黑名单 /// public Action OnBlocked { get; set; } /// /// 当用户被解除黑名单 /// public Action OnUnblocked { get; set; } /// /// 当前用户在某个对话中被禁言 /// public Action OnMuted; /// /// 当前用户在某个对话中被解除禁言 /// public Action OnUnmuted; /// /// 该对话信息被更新 /// public Action, string> OnConversationInfoUpdated; /// /// 当前用户被添加至某个对话 /// public Action OnInvited { get; set; } /// /// 当前用户被从某个对话中移除 /// public Action OnKicked { get; set; } /// /// 有用户被添加至某个对话 /// public Action, string> OnMembersJoined { get; set; } /// /// 有成员被从某个对话中移除 /// public Action, string> OnMembersLeft { get; set; } /// /// 有成员被加入某个对话的黑名单 /// public Action, string> OnMembersBlocked { get; set; } /// /// 有成员被移出某个对话的黑名单 /// public Action, string> OnMembersUnblocked { get; set; } /// /// 有成员在某个对话中被禁言 /// public Action, string> OnMembersMuted { get; set; } /// /// 有成员被移出某个对话的黑名单 /// public Action, string> OnMembersUnmuted { get; set; } /// /// 有成员的对话信息被更新 /// public Action OnMemberInfoUpdated; #endregion #region 消息事件 /// /// 当前用户收到消息 /// public Action OnMessage { get; set; } /// /// 消息被撤回 /// public Action OnMessageRecalled { get; set; } /// /// 消息被修改 /// public Action OnMessageUpdated { get; set; } /// /// 消息已送达 /// public Action OnMessageDelivered { get; set; } /// /// 消息已读 /// public Action OnMessageRead { get; set; } /// /// 未读消息数目更新 /// public Action> OnUnreadMessagesCountUpdated { get; set; } /// /// 最近分发消息更新 /// public Action OnLastDeliveredAtUpdated { get; set; } /// /// 最近已读消息更新 /// public Action OnLastReadAtUpdated { get; set; } #endregion #endregion internal ILCIMSignatureFactory SignatureFactory { get; private set; } internal LCConnection Connection { get; set; } internal LCIMSessionController SessionController { get; private set; } internal LCIMMessageController MessageController { get; private set; } internal LCIMGoAwayController GoAwayController { get; private set; } internal LCIMConversationController ConversationController { get; private set; } #region 接口 public LCIMClient(string clientId, string tag = null, string deviceId = null, ILCIMSignatureFactory signatureFactory = null) { if (string.IsNullOrEmpty(clientId)) { throw new ArgumentNullException(nameof(clientId)); } SetUpClient(clientId, tag, deviceId, signatureFactory); } public LCIMClient(LCUser user, string tag = null, string deviceId = null, ILCIMSignatureFactory signatureFactory = null) { if (user == null) { throw new ArgumentNullException(nameof(user)); } if (string.IsNullOrEmpty(user.ObjectId) || string.IsNullOrEmpty(user.SessionToken)) { throw new ArgumentException("User must be authenticacted."); } SetUpClient(user.ObjectId, tag, deviceId, signatureFactory); SessionToken = user.SessionToken; } private void SetUpClient(string clientId, string tag, string deviceId, ILCIMSignatureFactory signatureFactory) { Id = clientId; Tag = tag; DeviceId = deviceId; SignatureFactory = signatureFactory; ConversationDict = new Dictionary(); // 模块 SessionController = new LCIMSessionController(this); ConversationController = new LCIMConversationController(this); MessageController = new LCIMMessageController(this); GoAwayController = new LCIMGoAwayController(this); Connection = new LCConnection(Id) { OnNotification = OnConnectionNotification, OnDisconnect = OnConnectionDisconnect, OnReconnected = OnConnectionReconnect }; } /// /// 登录 /// /// 是否强制登录 /// public async Task Open(bool force = true) { await Connection.Connect(); try { // 打开 Session await SessionController.Open(force); } catch (Exception e) { LCLogger.Error(e); // 如果 session 阶段异常,则关闭连接 await Connection.Close(); throw e; } } /// /// 关闭 /// /// public async Task Close() { // 关闭 session await SessionController.Close(); await Connection.Close(); } /// /// 创建普通对话 /// /// /// /// /// /// public async Task CreateConversation( IEnumerable members, string name = null, bool unique = true, Dictionary properties = null) { return await ConversationController.CreateConv(members: members, name: name, unique: unique, properties: properties); } /// /// 创建聊天室 /// /// /// /// public async Task CreateChatRoom( string name, Dictionary properties = null) { LCIMChatRoom chatRoom = await ConversationController.CreateConv(name: name, transient: true, properties: properties) as LCIMChatRoom; return chatRoom; } /// /// 创建临时对话 /// /// /// /// /// public async Task CreateTemporaryConversation( IEnumerable members, int ttl = 86400, Dictionary properties = null) { LCIMTemporaryConversation tempConversation = await ConversationController.CreateConv(members: members, temporary: true, temporaryTtl: ttl, properties: properties) as LCIMTemporaryConversation; return tempConversation; } /// /// 根据 id 获取对话 /// /// /// public async Task GetConversation(string id) { if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException(nameof(id)); } if (LCIMConversation.IsTemporayConversation(id)) { List temporaryConversationList = await ConversationController.GetTemporaryConversations(new string[] { id }); if (temporaryConversationList == null || temporaryConversationList.Count < 1) { return null; } return temporaryConversationList[0]; } LCIMConversationQuery query = GetQuery() .WhereEqualTo("objectId", id) .Limit(1); ReadOnlyCollection results = await ConversationController.Find(query); if (results == null || results.Count < 1) { return null; } return results[0]; } /// /// 获取某些特定的对话 /// /// /// public async Task> GetConversationList(IEnumerable ids) { if (ids == null || ids.Count() == 0) { throw new ArgumentNullException(nameof(ids)); } // 区分临时对话 IEnumerable tempConvIds = ids.Where(item => { return LCIMConversation.IsTemporayConversation(item); }); IEnumerable convIds = ids.Where(item => { return !tempConvIds.Contains(item); }); List conversationList = new List(); if (tempConvIds.Count() > 0) { List temporaryConversations = await ConversationController.GetTemporaryConversations(tempConvIds); conversationList.AddRange(temporaryConversations); } if (convIds.Count() > 0) { LCIMConversationQuery query = GetQuery() .WhereContainedIn("objectId", convIds) .Limit(convIds.Count()); ReadOnlyCollection conversations = await ConversationController.Find(query); conversationList.AddRange(conversations); } return conversationList.AsReadOnly(); } /// /// 获取对话查询对象 /// /// public LCIMConversationQuery GetQuery() { return new LCIMConversationQuery(this); } #endregion private void OnConnectionNotification(GenericCommand notification) { switch (notification.Cmd) { case CommandType.Session: _ = SessionController.OnNotification(notification); break; case CommandType.Conv: case CommandType.Unread: _ = ConversationController.OnNotification(notification); break; case CommandType.Direct: case CommandType.Patch: case CommandType.Rcp: _ = MessageController.OnNotification(notification); break; case CommandType.Goaway: _ = GoAwayController.OnNotification(notification); break; default: break; } } private void OnConnectionDisconnect() { OnPaused?.Invoke(); } private void OnConnectionReconnect() { _ = HandleReconnected(); } private async Task HandleReconnected() { try { // 打开 Session await SessionController.Reopen(); // 回调用户 OnResume?.Invoke(); } catch (Exception e) { LCLogger.Error(e); await Connection.Close(); // TODO 告知 OnClose?.Invoke(0, string.Empty); } } internal async Task GetOrQueryConversation(string convId) { if (ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) { return conversation; } conversation = await GetConversation(convId); return conversation; } } }