using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using System.Collections.ObjectModel; using LeanCloud.Storage; using LeanCloud.Realtime.Internal.Protocol; using LeanCloud.Realtime.Internal.Controller; namespace LeanCloud.Realtime { public class LCIMClient { /// /// Conversation cache /// internal Dictionary ConversationDict; /// /// Client Id /// public string Id { get; private set; } /// /// Client tag /// public string Tag { get; private set; } public string DeviceId { get; private set; } internal string SessionToken { get; private set; } #region 连接状态事件 /// /// Occurs when the connection is lost. /// public Action OnPaused { get; set; } /// /// Occurs when the connection is recovered. /// public Action OnResume { get; set; } /// /// Occurs when the connection is closed and there will be no auto reconnection. /// Possible causes include there is a single device login conflict or the client has been kicked off by the server. /// public Action OnClose { get; set; } #endregion #region 对话事件 /// /// Occurs when the current user is added into the blacklist of a conversation. /// public Action OnBlocked { get; set; } /// /// Occurs when the current user is removed from the blacklist of a conversation. /// public Action OnUnblocked { get; set; } /// /// Occurs when the current user is muted in a conversation. /// public Action OnMuted; /// /// Occurs when the current user is unmuted in a conversation. /// public Action OnUnmuted; /// /// Occurs when the properties of a conversation are updated. /// public Action, string> OnConversationInfoUpdated; /// /// Occurs when the current user is invited to a conversation. /// public Action OnInvited { get; set; } /// /// Occurs when the current user is kicked from a conversation. /// public Action OnKicked { get; set; } /// /// Occurs when a user joined a conversation. /// public Action, string> OnMembersJoined { get; set; } /// /// Occurs when a user left a conversation. /// public Action, string> OnMembersLeft { get; set; } /// /// Occurs when a user is added to the blacklist of a conversation. /// public Action, string> OnMembersBlocked { get; set; } /// /// Occurs when a user is removed from the blacklist of a conversation. /// public Action, string> OnMembersUnblocked { get; set; } /// /// Occurs when a user is muted in a conversation. /// public Action, string> OnMembersMuted { get; set; } /// /// Occurs when a user is unmuted in a conversation. /// public Action, string> OnMembersUnmuted { get; set; } /// /// Occurs when the properties of someone are updated. /// public Action OnMemberInfoUpdated; #endregion #region 消息事件 /// /// Occurs when a new message is delivered to a conversation the current user is already in. /// public Action OnMessage { get; set; } /// /// Occurs when a message is recalled. /// public Action OnMessageRecalled { get; set; } /// /// Occurs when a message is updated. /// public Action OnMessageUpdated { get; set; } /// /// Occurs when a message is delivered. /// public Action OnMessageDelivered { get; set; } /// /// Occurs when a message is read. /// public Action OnMessageRead { get; set; } /// /// Occurs when the number of unreadMessagesCount is updatded. /// public Action> OnUnreadMessagesCountUpdated { get; set; } /// /// Occurs when the last delivered message is updated. /// public Action OnLastDeliveredAtUpdated { get; set; } /// /// Occurs when the last read message is updated. /// public Action OnLastReadAtUpdated { get; set; } #endregion internal ILCIMSignatureFactory SignatureFactory { get; private set; } internal LCIMSessionController SessionController { get; private set; } internal LCIMMessageController MessageController { 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); } /// /// Signing in /// /// If this is ture (default value), and single device sign-on is enabled, users already logged in on another device with the same tag will be logged out. /// public async Task Open(bool force = true) { try { // 打开 Session await SessionController.Open(force); } catch (Exception e) { LCLogger.Error(e); // 如果 session 阶段异常,则关闭连接 throw e; } } /// /// Closes the session /// /// public async Task Close() { // 关闭 session await SessionController.Close(); } /// /// Creates a conversation /// /// The list of clientIds of participants in this conversation (except the creator) /// The name of this conversation /// Whether this conversation is unique; /// if it is true and an existing conversation contains the same composition of members, /// the existing conversation will be reused, otherwise a new conversation will be created. /// Custom attributes of this conversation /// 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); } /// /// Creates a chatroom /// /// The name of this chatroom /// Custom attributes of this chatroom /// public async Task CreateChatRoom( string name, Dictionary properties = null) { LCIMChatRoom chatRoom = await ConversationController.CreateConv(name: name, transient: true, properties: properties) as LCIMChatRoom; return chatRoom; } /// /// Creates a temporary conversation /// /// The list of clientIds of participants in this temporary conversation (except the creator) /// TTL of this temporary conversation /// Custom attributes of this temporary conversation /// 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; } /// /// Queries a conversation based on its id. /// /// objectId /// 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]; } /// /// Queries conversations based on their ids. /// /// objectId list /// 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(); } /// /// Constructs a conversation query. /// /// public LCIMConversationQuery GetQuery() { return new LCIMConversationQuery(this); } #endregion internal void HandleNotification(GenericCommand notification) { switch (notification.Cmd) { case CommandType.Session: SessionController.HandleNotification(notification); break; case CommandType.Conv: case CommandType.Unread: ConversationController.HandleNotification(notification); break; case CommandType.Direct: case CommandType.Patch: case CommandType.Rcp: MessageController.HandleNotification(notification); break; default: break; } } internal void HandleDisconnected() { OnPaused?.Invoke(); } internal async void HandleReconnected() { try { // 打开 Session await SessionController.Reopen(); // 回调用户 OnResume?.Invoke(); } catch (LCException e) { LCLogger.Error(e); // 重连成功,但 session/open 失败 OnClose?.Invoke(e.Code, e.Message); } } internal async Task GetOrQueryConversation(string convId) { if (ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) { return conversation; } conversation = await GetConversation(convId); return conversation; } } }