/// <summary> /// This method will post a new message to a private group based on the provided group id /// and message. /// </summary> /// <param name="context">The http context of the current request</param> /// <param name="groupId">The id of the group</param> /// <param name="message">The message to be posted</param> /// <returns>A task that encapsulates an either monad</returns> public async Task <Either <Unit, Error> > PostAsync(HttpContext context, long groupId, Message message) { try { var url = $"/api/groups/{groupId}/messages"; var method = HttpMethod.Post; var jsonMessage = JsonConvert.SerializeObject(message); var content = new StringContent(jsonMessage, Encoding.UTF8, "application/json"); return(await _apiInteropService.SendAsync(context, method, url, content)); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <Unit, Error>(SystemErrors.Exception())); } }
/// <summary> /// Adds a new direct messaging entry, if it does not already exist, and also assings the callers /// connection id to a signalr group based on the resulting direct messaging id. /// </summary> /// <param name="firstParticipantId">The user id of the first participant</param> /// <param name="secondParticipantId">The user id of the second participant</param> /// <returns>A Task instance</returns> public async Task PostNewDirectMessaging(long firstParticipantId, long secondParticipantId) { // Trying to find an existing direct messaging entry based on the users provided. var httpContext = Context.GetHttpContext(); var getMonad = await _directMessagingService.GetAsync(httpContext, firstParticipantId, secondParticipantId); if (getMonad is Success <DirectMessaging, Error> success) { var signalGroup = DirectMessagingName(success.Value.Id); var payload = new Payload <DirectMessaging>(signalGroup, success.Value); await Groups.AddToGroupAsync(Context.ConnectionId, signalGroup); await Clients.Caller.NewDirectMessaging(payload); } // Creates a new direct messaging entry between two users. var directMessaging = new DirectMessaging { FirstParticipantUserId = firstParticipantId, SecondParticipantUserId = secondParticipantId }; var monad = await _directMessagingService.PostAsync(httpContext, directMessaging); switch (monad) { case Success <DirectMessaging, Error> postSuccess: var signalGroup = DirectMessagingName(postSuccess.Value.Id); var payload = new Payload <DirectMessaging>(signalGroup, postSuccess.Value); await Groups.AddToGroupAsync(Context.ConnectionId, signalGroup); await Clients.Caller.NewDirectMessaging(payload); break; case Failure <DirectMessaging, Error> failure: await Clients.Caller.NewDirectMessaging(failure.Value); break; default: await Clients.Caller.NewDirectMessaging(SystemErrors.Exception()); break; } }
/// <summary> /// This method will delete a direct messaging entry. /// </summary> /// <param name="userId">The id of the requesting user</param> /// <param name="directMessagingId">The id of the group</param> /// <returns>An either monad</returns> public Either <DirectMessaging, Error> Delete(long userId, long directMessagingId) { try { return(Get(userId, directMessagingId).Bind(directMessaging => { _burstChatContext .DirectMessaging .Remove(directMessaging); return new Success <DirectMessaging, Error>(directMessaging); })); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <DirectMessaging, Error>(SystemErrors.Exception())); } }
/// <summary> /// Will check if the provided alpha invitation code exists in the database and is valid. /// </summary> /// <param name="alphaInvitationCode">The alpha invitation code instance</param> /// <returns>An either monad</returns> private Either <Unit, Error> AlphaInvitationCodeExists(Guid alphaInvitationCode) { try { var codeExists = _burstChatContext .AlphaInvitations .Any(a => a.Code == alphaInvitationCode && a.DateExpired >= DateTime.Now); return(codeExists ? new Success <Unit, Error>(new Unit()) : new Failure <Unit, Error>(AlphaInvitationErrors.AlphaInvitationCodeIsNotValid())); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <Unit, Error>(SystemErrors.Exception())); } }
/// <summary> /// Updates an invitation's state and informs the appropriate users. /// </summary> /// <param name="invitation">The invitation to be updated</param> /// <returns>A Task instance</returns> public async Task UpdateInvitation(long id, bool accepted) { var httpContext = Context.GetHttpContext(); var data = new UpdateInvitation { InvitationId = id, Accepted = accepted }; var monad = await _userService.UpdateInvitationAsync(httpContext, data); var signalGroup = string.Empty; var invite = new Invitation(); switch (monad) { case Success <Invitation, Error> success when success.Value.Accepted: invite = success.Value; signalGroup = ServerSignalName(invite.ServerId); await Clients.Group(signalGroup).UpdatedInvitation(invite); await Clients.Caller.UpdatedInvitation(invite); break; case Success <Invitation, Error> success when !success.Value.Accepted: invite = success.Value; signalGroup = ServerSignalName(invite.ServerId); await Clients.Caller.UpdatedInvitation(invite); break; case Failure <Invitation, Error> failure: await Clients.Caller.UpdatedInvitation(failure.Value); break; default: await Clients.Caller.UpdatedInvitation(SystemErrors.Exception()); break; } }
/// <summary> /// This method will fetch all information about direct messaging entry. /// </summary> /// <param name="userId">The id of the requesting user</param> /// <param name="directMessagingId">The id of the direct messages</param> /// <returns>An either monad</returns> public Either <DirectMessaging, Error> Get(long userId, long directMessagingId) { try { var directMessaging = _burstChatContext .DirectMessaging .FirstOrDefault(dm => dm.Id == directMessagingId && (dm.FirstParticipantUserId == userId || dm.SecondParticipantUserId == userId)); return(directMessaging is not null ? new Success <DirectMessaging, Error>(directMessaging) : new Failure <DirectMessaging, Error>(DirectMessagingErrors.DirectMessagesNotFound())); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <DirectMessaging, Error>(SystemErrors.Exception())); } }
/// <summary> /// This method will invoke a call to the BurstChat API for the creation of a new channel. /// </summary> /// <param name="context">The http context of the current request</param> /// <param name="serverId">The id of the channel</param> /// <param name="channel">The instance of the new channel</param> /// <returns>A task that encapsulates an either monad</returns> public async Task <Either <Channel, Error> > PostAsync(HttpContext context, int serverId, Channel channel) { try { var method = HttpMethod.Post; var url = "api/channels"; var query = HttpUtility.ParseQueryString(string.Empty); query["serverId"] = serverId.ToString(); url += $"/?{query}"; var jsonMessage = JsonSerializer.Serialize(channel); var content = new StringContent(jsonMessage, Encoding.UTF8, "application/json"); return(await _apiInteropService.SendAsync <Channel>(context, method, url, content)); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <Channel, Error>(SystemErrors.Exception())); } }
/// <summary> /// Fetches all available information about a direct messaging entry based on the provided /// participants. /// </summary> /// <param name="context">The http context of the current request</param> /// <param name="firstParticipantId">The user id of the first participant</param> /// <param name="secondParticipantId">The user id of the seconad participant</param> /// <returns>An either monad</returns> public async Task <Either <DirectMessaging, Error> > GetAsync(HttpContext context, long firstParticipantId, long secondParticipantId) { try { var method = HttpMethod.Get; var url = "/api/direct"; var content = new FormUrlEncodedContent(new[] { new KeyValuePair <string?, string?>("firstParticipantId", firstParticipantId.ToString()), new KeyValuePair <string?, string?>("secondParticipantId", secondParticipantId.ToString()) }); return(await _apiInteropService.SendAsync <DirectMessaging>(context, method, url, content)); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <DirectMessaging, Error>(SystemErrors.Exception())); } }
/// <summary> /// This method will be edit a message of a direct messaging entry. /// </summary> /// <param name="userId">The id of the requesting user</param> /// <param name="directMessagingId">The id of the direct messaging entry</param> /// <param name="message">The message instance that will be used for the edit</param> /// <returns>An either monad<returns> public Either <Message, Error> UpdateMessage(long userId, long directMessagingId, Message message) { try { if (message is null) { return(new Failure <Message, Error>(DirectMessagingErrors.DirectMessageNotFound())); } return(Get(userId, directMessagingId).Bind <Message>(_ => { var entries = _burstChatContext .DirectMessaging .Include(dm => dm.Messages) .ThenInclude(dm => dm.User) .Include(dm => dm.Messages) .ThenInclude(dm => dm.Links) .Where(dm => dm.Id == directMessagingId) .Select(dm => dm.Messages.FirstOrDefault(m => m.Id == message.Id)) .ToList(); if (entries.Count != 1) { return new Failure <Message, Error>(DirectMessagingErrors.DirectMessageNotFound()); } var entry = entries.First() !; entry.Links = message.GetLinksFromContent(); entry.Content = message.RemoveLinksFromContent(); entry.Edited = true; _burstChatContext.SaveChanges(); return new Success <Message, Error>(entry); })); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <Message, Error>(SystemErrors.Exception())); } }
/// <summary> /// Executes a request to an Asterisk server in order to create a new aor configuration /// for the provided endpoint. /// </summary> /// <param name="endpoint">The endpoint name</param> /// <returns>A task of an either monad</returns> private async Task <Either <Unit, Error> > PostAorAsync(string endpoint) { try { var parameters = new { id = endpoint, maxContacts = 5, removeExisting = true, supportPath = true }; await _connection.ExecuteAsync(InsertAor, parameters); return(new Success <Unit, Error>(Unit.New())); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <Unit, Error>(SystemErrors.Exception())); } }
/// <summary> /// This method will fetch all available messages of a direct messaging entry. /// If a message id is provided then 300 messages sent prior will be returned. /// </summary> /// <param name="userId">The id of the requesting user</param> /// <param name="directMessagingId">The id of the direct messaging entry</param> /// <param name="searchTerm">A search term that needs to be present in all returned messages</param> /// <param name="lastMessageId">The message id from which all prior messages will be fetched</param> /// <returns>An either monad</returns> public Either <IEnumerable <Message>, Error> GetMessages(long userId, long directMessagingId, string?searchTerm = null, long?lastMessageId = null) { try { return(Get(userId, directMessagingId).Bind(_ => { var messages = _burstChatContext .DirectMessaging .Include(dm => dm.Messages) .ThenInclude(m => m.User) .Include(dm => dm.Messages) .ThenInclude(m => m.Links) .Where(dm => dm.Id == directMessagingId) .Select(dm => dm.Messages .Where(m => m.Id < (lastMessageId ?? long.MaxValue) && (searchTerm == null || m.Content.Contains(searchTerm))) .OrderByDescending(m => m.Id) .Take(100)) .ToList() .Aggregate(new List <Message>(), (current, next) => { current.AddRange(next); return current; }) .OrderBy(m => m.Id) .ToList(); return new Success <IEnumerable <Message>, Error>(messages); })); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <IEnumerable <Message>, Error>(SystemErrors.Exception())); } }
/// <summary> /// Sends an Http request to a remote Asterisk server in order to create a new /// auth configuration for the provided pjsip enpoint. /// </summary> /// <param name="endpoint">The endpoint name</param> /// <param name="password">The endpoint password</param> /// <returns>A task of an either monad</returns> private async Task <Either <Unit, Error> > PostAuthAsync(string endpoint, Guid password) { try { var parameters = new { id = $"auth{endpoint}", authType = "userpass", username = endpoint, password = password.ToString() }; await _connection.ExecuteAsync(InsertAuth, parameters); return(new Success <Unit, Error>(Unit.New())); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <Unit, Error>(SystemErrors.Exception())); } }
/// <summary> /// Returns all invitations sent to a user. /// </summary> /// <returns>A Task instance</returns> public async Task GetInvitations() { var httpContext = Context.GetHttpContext(); var monad = await _userService.GetAllInvitationsAsync(httpContext); switch (monad) { case Success <IEnumerable <Invitation>, Error> success: await Clients.Caller.Invitations(success.Value); break; case Failure <IEnumerable <Invitation>, Error> failure: await Clients.Caller.Invitations(failure.Value); break; default: await Clients.Caller.Invitations(SystemErrors.Exception()); break; } }
/// <summary> /// Sends an email to the provided recipient that contains an one time password /// </summary> /// <param name="recipient">The recipient email address</param> /// <param name="oneTimePassword">The one time password to be sent</param> /// <returns>A task of an either monad</returns> public async Task <Either <Unit, Error> > SendOneTimePasswordAsync(string recipient, string oneTimePassword) { try { var sender = _smtpOptions.Sender; var subject = "BurstChat reset password"; var body = $"BurstChat account one time password: {oneTimePassword}"; await _smtpClient.SendMailAsync(sender, recipient, subject, body); return(new Success <Unit, Error>(new Unit())); } catch (Exception e) { if (_webHostEnvironment.IsDevelopment()) { _logger.LogInformation($"[email: {recipient}] one time pass {oneTimePassword}"); return(new Success <Unit, Error>(new Unit())); } _logger.LogError(e.Message); return(new Failure <Unit, Error>(SystemErrors.Exception())); } }
/// <summary> /// Creates a new channel for a server. /// </summary> /// <param name="serverId">The id of the server that the channel belongs</param> /// <param name="channel">The instance of the channel to be created</param> /// <returns>A task instance</returns> public async Task PostChannel(int serverId, Channel channel) { var httpContext = Context.GetHttpContext(); var monad = await _channelsService.PostAsync(httpContext, serverId, channel); switch (monad) { case Success <Channel, Error> success: var signalGroup = ServerSignalName(serverId); await Clients.Group(signalGroup).ChannelCreated(new dynamic[] { serverId, success.Value }); break; case Failure <Channel, Error> failure: await Clients.Caller.ChannelCreated(failure.Value); break; default: await Clients.Caller.ChannelCreated(SystemErrors.Exception()); break; } }
/// <summary> /// This method will fetch all users that the requesting user has direct messaged. /// </summary> /// <param name="userId">The id of the requesting user</param> /// <returns>An either monad</returns> public Either <IEnumerable <User?>, Error> GetUsers(long userId) { try { var users = _burstChatContext .DirectMessaging .Include(dm => dm.FirstParticipantUser) .Include(dm => dm.SecondParticipantUser) .Where(dm => dm.FirstParticipantUserId == userId || dm.SecondParticipantUserId == userId) .ToList() .Select(dm => dm.FirstParticipantUserId != userId ? dm.FirstParticipantUser : dm.SecondParticipantUser) .ToList(); return(new Success <IEnumerable <User?>, Error>(users)); } catch (Exception e) { _logger.LogError(e.Message); return(new Failure <IEnumerable <User?>, Error>(SystemErrors.Exception())); } }
/// <summary> /// Removes a channel from a server. /// </summary> /// <param name="serverId">The id of the server that the channel belongs</param> /// <param name="channelId">The id of the channel to be deleted</param> /// <returns>A task instance</returns> public async Task DeleteChannel(int serverId, int channelId) { var httpContext = Context.GetHttpContext(); var monad = await _channelsService.DeleteAsync(httpContext, channelId); switch (monad) { case Success <Channel, Error> _: var signalGroup = ServerSignalName(serverId); await Clients.Group(signalGroup).ChannelDeleted(channelId); break; case Failure <Channel, Error> failure: await Clients.Caller.ChannelDeleted(failure.Value); break; default: await Clients.Caller.ChannelDeleted(SystemErrors.Exception()); break; } }
/// <summary> /// Adds a new server and informs the caller of the results. /// </summary> /// <param name="server">The server instance to be added</param> /// <returns>A task instance</returns> public async Task AddServer(Server server) { var httpContext = Context.GetHttpContext(); var sub = Context; var monad = await _serverService.PostAsync(httpContext, server); switch (monad) { case Success <Server, Error> success: await Clients.Caller.AddedServer(success.Value); break; case Failure <Server, Error> failure: await Clients.Caller.AddedServer(failure.Value); break; default: await Clients.Caller.AddedServer(SystemErrors.Exception()); break; } }
/// <summary> /// Informs all associated connection of a server, that its information were updated. /// </summary> /// <param name="serverId">The id of the target server</param> /// <returns>A task instance</returns> public async Task UpdateServerInfo(int serverId) { var httpContext = Context.GetHttpContext(); var monad = await _serverService.GetAsync(httpContext, serverId); switch (monad) { case Success <Server, Error> success: var signalGroup = ServerSignalName(serverId); await Clients.Group(signalGroup).UpdatedServer(success.Value); break; case Failure <Server, Error> failure: await Clients.Caller.UpdatedServer(failure.Value); break; default: await Clients.Caller.UpdatedServer(SystemErrors.Exception()); break; } }