Beispiel #1
0
        public string RefreshAgent(int id)
        {
            string response = ResponseNone;
            MessageRouterManager messageRouterManager = WebApiConfig.MessageRouterManager;
            IRoutingDataManager  routingDataManager   = messageRouterManager.RoutingDataManager;
            var     Settings = new Settings.BotSettings();
            Manager manager  = new Manager(Settings[BotSettings.KeyRoutingDataStorageConnectionString]);

            try
            {
                Dictionary <Party, Party> connectedParties        = routingDataManager.GetConnectedParties();
                Dictionary <Party, Party> waitingConnectedParties = manager.GetWaitingConnectedParties();

                foreach (var connectedPartie in connectedParties)
                {
                    clientsParties.Add(connectedPartie.Value);
                }

                foreach (var waitingConnectedPartie in waitingConnectedParties)
                {
                    clientsParties.Add(waitingConnectedPartie.Value);
                }

                response = JsonConvert.SerializeObject(clientsParties);
            }
            catch (InvalidOperationException e)
            {
                Debug.WriteLine($"{e.Message}");
            }

            Debug.WriteLine("refresh");

            return(response);
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="routingDataManager">The routing data manager instance.</param>
        /// <param name="backchannelId">The ID for back channel messages. If null, the default value is used.</param>
        /// <param name="partyKey">The key identifying the serialized party data. If null, the default value is used.</param>
        public BackChannelMessageHandler(IRoutingDataManager routingDataManager, string backChannelId = null, string partyKey = null)
        {
            _routingDataManager = routingDataManager
                                  ?? throw new ArgumentNullException("Routing data manager instance must be given");

            BackChannelId = string.IsNullOrEmpty(backChannelId) ? DefaultBackChannelId : backChannelId;
            PartyKey      = string.IsNullOrEmpty(partyKey) ? DefaultPartyKey : partyKey;
        }
Beispiel #3
0
        /// <summary>
        /// Creates and sets up the instances required for message routing.
        /// </summary>
        public static void InitializeMessageRouting()
        {
            Settings = new BotSettings();
            string connectionString = Settings[BotSettings.KeyRoutingDataStorageConnectionString];
            IRoutingDataManager routingDataManager = null;

            if (string.IsNullOrEmpty(connectionString))
            {
                System.Diagnostics.Debug.WriteLine($"WARNING!!! No connection string found - using {nameof(LocalRoutingDataManager)}");
                routingDataManager = new LocalRoutingDataManager();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine($"Found a connection string - using {nameof(AzureTableStorageRoutingDataManager)}");
                routingDataManager = new AzureTableStorageRoutingDataManager(connectionString);
            }

            MessageRouterManager       = new MessageRouterManager(routingDataManager);
            MessageRouterResultHandler = new MessageRouterResultHandler(MessageRouterManager);
            CommandMessageHandler      = new CommandMessageHandler(MessageRouterManager, MessageRouterResultHandler);
            BackChannelMessageHandler  = new BackChannelMessageHandler(MessageRouterManager.RoutingDataManager);
        }
Beispiel #4
0
        public string GetAgentById(int id)
        {
            string response = ResponseNone;
            MessageRouterManager messageRouterManager = WebApiConfig.MessageRouterManager;
            IRoutingDataManager  routingDataManager   = messageRouterManager.RoutingDataManager;

            if (routingDataManager.GetAggregationParties().Count == 0 &&
                routingDataManager.GetPendingRequests().Count > 0)
            {
                try
                {
                    Party conversationClientParty = messageRouterManager.RoutingDataManager.GetPendingRequests().First();
                    messageRouterManager.RoutingDataManager.RemovePendingRequest(conversationClientParty);
                    response = conversationClientParty.ToJsonString();
                }
                catch (InvalidOperationException e)
                {
                    System.Diagnostics.Debug.WriteLine($"Failed to handle a pending request: {e.Message}");
                }
            }

            return(response);
        }
        /// <summary>
        /// Checks the given activity for a possible command.
        ///
        /// All messages that start with a specific command keyword or contain a mention of the bot
        /// ("@<bot name>") are checked for possible commands.
        /// </summary>
        /// <param name="activity">An Activity instance containing a possible command.</param>
        /// <returns>True, if a command was detected and handled. False otherwise.</returns>
        public async virtual Task <bool> HandleCommandAsync(Activity activity)
        {
            bool     wasHandled    = false;
            Activity replyActivity = null;
            Command  command       = ExtractCommand(activity);

            if (command != null)
            {
                Party senderParty = MessagingUtils.CreateSenderParty(activity);

                switch (command.BaseCommand.ToLower())
                {
                case string baseCommand when(baseCommand.Equals(Commands.CommandListOptions)):
                    // Present all command options in a card
                    replyActivity = CommandCardFactory.AddCardToActivity(
                        activity.CreateReply(), CommandCardFactory.CreateCommandOptionsCard(activity.Recipient?.Name));

                    wasHandled = true;
                    break;

                case string baseCommand when(baseCommand.Equals(Commands.CommandAddAggregationChannel)):
                    // Establish the sender's channel/conversation as an aggreated one if not already exists
                    Party aggregationPartyToAdd =
                        new Party(activity.ServiceUrl, activity.ChannelId, null, activity.Conversation);

                    if (_messageRouterManager.RoutingDataManager.AddAggregationParty(aggregationPartyToAdd))
                    {
                        replyActivity = activity.CreateReply(ConversationText.AggregationChannelSet);
                    }
                    else
                    {
                        replyActivity = activity.CreateReply(ConversationText.AggregationChannelAlreadySet);
                    }

                    wasHandled = true;
                    break;

                case string baseCommand when(baseCommand.Equals(Commands.CommandRemoveAggregationChannel)):
                    // Remove the sender's channel/conversation from the list of aggregation channels
                    if (_messageRouterManager.RoutingDataManager.IsAssociatedWithAggregation(senderParty))
                    {
                        Party aggregationPartyToRemove =
                            new Party(activity.ServiceUrl, activity.ChannelId, null, activity.Conversation);

                        if (_messageRouterManager.RoutingDataManager.RemoveAggregationParty(aggregationPartyToRemove))
                        {
                            replyActivity = activity.CreateReply(ConversationText.AggregationChannelRemoved);
                        }
                        else
                        {
                            replyActivity = activity.CreateReply(ConversationText.FailedToRemoveAggregationChannel);
                        }

                        wasHandled = true;
                    }

                    break;

                case string baseCommand when(baseCommand.Equals(Commands.CommandAcceptRequest) || baseCommand.Equals(Commands.CommandRejectRequest)):
                    // Accept/reject conversation request
                    bool doAccept = baseCommand.Equals(Commands.CommandAcceptRequest);

                    if (_messageRouterManager.RoutingDataManager.IsAssociatedWithAggregation(senderParty))
                    {
                        // The party is associated with the aggregation and has the right to accept/reject
                        if (command.Parameters.Count == 0)
                        {
                            replyActivity = activity.CreateReply();

                            IList <Party> pendingRequests =
                                _messageRouterManager.RoutingDataManager.GetPendingRequests();

                            if (pendingRequests.Count == 0)
                            {
                                replyActivity.Text = ConversationText.NoPendingRequests;
                            }
                            else
                            {
                                replyActivity = CommandCardFactory.AddCardToActivity(
                                    replyActivity, CommandCardFactory.CreateAcceptOrRejectCardForMultipleRequests(
                                        pendingRequests, doAccept, activity.Recipient?.Name));
                            }
                        }
                        else if (!doAccept &&
                                 command.Parameters[0].Equals(Commands.CommandParameterAll))
                        {
                            if (!await new MessageRoutingUtils().RejectAllPendingRequestsAsync(
                                    _messageRouterManager, _messageRouterResultHandler))
                            {
                                replyActivity      = activity.CreateReply();
                                replyActivity.Text = ConversationText.FailedToRejectPendingRequests;
                            }
                        }
                        else
                        {
                            string errorMessage = await new MessageRoutingUtils().AcceptOrRejectRequestAsync(
                                _messageRouterManager, _messageRouterResultHandler, senderParty, doAccept, command.Parameters[0]);

                            if (!string.IsNullOrEmpty(errorMessage))
                            {
                                replyActivity      = activity.CreateReply();
                                replyActivity.Text = errorMessage;
                            }
                        }
                    }
#if DEBUG
                    // We shouldn't respond to command attempts by regular users, but I guess
                    // it's okay when debugging
                    else
                    {
                        replyActivity = activity.CreateReply(ConversationText.ConnectionRequestResponseNotAllowed);
                    }
#endif

                    wasHandled = true;
                    break;

                case string baseCommand when(baseCommand.Equals(Commands.CommandDisconnect)):
                    // End the 1:1 conversation
                    IList <MessageRouterResult> messageRouterResults = _messageRouterManager.Disconnect(senderParty);

                    foreach (MessageRouterResult messageRouterResult in messageRouterResults)
                    {
                        await _messageRouterResultHandler.HandleResultAsync(messageRouterResult);
                    }

                    wasHandled = true;
                    break;


                    #region Implementation of debugging commands
#if DEBUG
                case string baseCommand when(baseCommand.Equals(Commands.CommandDeleteAllRoutingData)):
                    // DELETE ALL ROUTING DATA
                    replyActivity = activity.CreateReply(ConversationText.DeletingAllData);

                    _messageRouterManager.RoutingDataManager.DeleteAll();
                    wasHandled = true;
                    break;

                case string baseCommand when(baseCommand.Equals(Commands.CommandList)):
                    bool listAll = command.Parameters.Contains(Commands.CommandParameterAll);

                    replyActivity = activity.CreateReply();
                    string replyMessageText = string.Empty;

                    if (listAll || command.Parameters.Contains(Commands.CommandParameterParties))
                    {
                        // List user and bot parties
                        IRoutingDataManager routingDataManager = _messageRouterManager.RoutingDataManager;
                        string partiesAsString = PartyListToString(routingDataManager.GetUserParties());

                        replyMessageText += string.IsNullOrEmpty(partiesAsString)
                                ? $"{ConversationText.NoUsersStored}{StringAndCharConstants.LineBreak}"
                                : $"{ConversationText.Users}:{StringAndCharConstants.LineBreak}{partiesAsString}{StringAndCharConstants.LineBreak}";

                        partiesAsString = PartyListToString(routingDataManager.GetBotParties());

                        replyMessageText += string.IsNullOrEmpty(partiesAsString)
                                ? $"{ConversationText.NoBotsStored}{StringAndCharConstants.LineBreak}"
                                : $"{ConversationText.Bots}:{StringAndCharConstants.LineBreak}{partiesAsString}{StringAndCharConstants.LineBreak}";

                        wasHandled = true;
                    }

                    if (listAll || command.Parameters.Contains(Commands.CommandParameterRequests))
                    {
                        // List all pending requests
                        IList <Attachment> attachments =
                            CommandCardFactory.CreateMultipleRequestCards(
                                _messageRouterManager.RoutingDataManager.GetPendingRequests(), activity.Recipient?.Name);

                        if (attachments.Count > 0)
                        {
                            replyMessageText += string.Format(ConversationText.PendingRequestsFoundWithCount, attachments.Count);
                            replyMessageText += StringAndCharConstants.LineBreak;
                            replyActivity.AttachmentLayout = AttachmentLayoutTypes.Carousel;
                            replyActivity.Attachments      = attachments;
                        }
                        else
                        {
                            replyMessageText += $"{ConversationText.NoPendingRequests}{StringAndCharConstants.LineBreak}";
                        }

                        wasHandled = true;
                    }

                    if (listAll || command.Parameters.Contains(Commands.CommandParameterConnections))
                    {
                        // List all connections (conversations)
                        string connectionsAsString = _messageRouterManager.RoutingDataManager.ConnectionsToString();

                        replyMessageText += string.IsNullOrEmpty(connectionsAsString)
                                ? $"{ConversationText.NoConversations}{StringAndCharConstants.LineBreak}"
                                : $"{connectionsAsString}{StringAndCharConstants.LineBreak}";

                        wasHandled = true;
                    }

                    if (listAll || command.Parameters.Contains(Commands.CommandParameterResults))
                    {
                        // List all logged message router results
                        string resultsAsString = _messageRouterManager.RoutingDataManager.GetLastMessageRouterResults();

                        replyMessageText += string.IsNullOrEmpty(resultsAsString)
                                ? $"{ConversationText.NoResults}{StringAndCharConstants.LineBreak}"
                                : $"{resultsAsString}{StringAndCharConstants.LineBreak}";

                        wasHandled = true;
                    }

                    if (!wasHandled)
                    {
                        replyMessageText = ConversationText.InvalidOrMissingCommandParameter;
                    }

                    replyActivity.Text = replyMessageText;
                    break;
#endif
                    #endregion

                default:
                    replyActivity = activity.CreateReply(string.Format(ConversationText.CommandNotRecognized, command.BaseCommand));
                    break;
                }

                if (replyActivity != null)
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
                    await connector.Conversations.ReplyToActivityAsync(replyActivity);
                }
            }

            return(wasHandled);
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="routingDataManager">The routing data manager.</param>
 public MessageRouterManager(IRoutingDataManager routingDataManager)
 {
     RoutingDataManager = routingDataManager;
     BackChannelId      = DefaultBackChannelId;
     PartyPropertyId    = DefaultPartyPropertyId;
 }
Beispiel #7
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="routingDataManager">The routing data manager.</param>
 public MessageRouterManager(IRoutingDataManager routingDataManager)
 {
     RoutingDataManager = routingDataManager;
 }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="routingDataManager">The routing data manager.</param>
 public DefaultBotCommandHandler(IRoutingDataManager routingDataManager)
 {
     _routingDataManager = routingDataManager;
 }
Beispiel #9
0
        /// <summary>
        /// Notifies both the conversation owner (agent) and the conversation client (customer)
        /// about the connection status change.
        /// </summary>
        /// <param name="messageRouterResult">The result to handle.</param>
        protected virtual async Task HandleConnectionChangedResultAsync(MessageRouterResult messageRouterResult)
        {
            IRoutingDataManager routingDataManager = _messageRouterManager.RoutingDataManager;

            Party conversationOwnerParty  = messageRouterResult.ConversationOwnerParty;
            Party conversationClientParty = messageRouterResult.ConversationClientParty;

            string conversationOwnerName =
                string.IsNullOrEmpty(conversationOwnerParty?.ChannelAccount.Name)
                    ? StringAndCharConstants.NoUserNamePlaceholder
                    : conversationOwnerParty?.ChannelAccount.Name;

            string conversationClientName =
                string.IsNullOrEmpty(conversationClientParty?.ChannelAccount.Name)
                    ? StringAndCharConstants.NoUserNamePlaceholder
                    : conversationClientParty?.ChannelAccount.Name;

            string messageToConversationOwner  = string.Empty;
            string messageToConversationClient = string.Empty;

            if (messageRouterResult.Type == MessageRouterResultType.ConnectionRequested)
            {
                bool conversationClientPartyMissing =
                    (conversationClientParty == null || conversationClientParty.ChannelAccount == null);

                foreach (Party aggregationParty in _messageRouterManager.RoutingDataManager.GetAggregationParties())
                {
                    Party botParty = routingDataManager.FindBotPartyByChannelAndConversation(
                        aggregationParty.ChannelId, aggregationParty.ConversationAccount);

                    if (botParty != null)
                    {
                        if (conversationClientPartyMissing)
                        {
                            await _messageRouterManager.SendMessageToPartyByBotAsync(
                                aggregationParty, ConversationText.RequestorDetailsMissing);
                        }
                        else
                        {
                            IMessageActivity messageActivity = Activity.CreateMessageActivity();
                            messageActivity.Conversation = aggregationParty.ConversationAccount;
                            messageActivity.Recipient    = aggregationParty.ChannelAccount;
                            messageActivity.Attachments  = new List <Attachment>
                            {
                                CommandCardFactory.CreateRequestCard(
                                    conversationClientParty, botParty.ChannelAccount?.Name).ToAttachment()
                            };

                            await _messageRouterManager.SendMessageToPartyByBotAsync(
                                aggregationParty, messageActivity);
                        }
                    }
                }

                if (!conversationClientPartyMissing)
                {
                    messageToConversationClient = ConversationText.NotifyClientWaitForRequestHandling;
                }
            }
            else if (messageRouterResult.Type == MessageRouterResultType.ConnectionAlreadyRequested)
            {
                messageToConversationClient = ConversationText.NotifyClientDuplicateRequest;
            }
            else if (messageRouterResult.Type == MessageRouterResultType.ConnectionRejected)
            {
                messageToConversationOwner  = string.Format(ConversationText.NotifyOwnerRequestRejected, conversationClientName);
                messageToConversationClient = ConversationText.NotifyClientRequestRejected;
            }
            else if (messageRouterResult.Type == MessageRouterResultType.Connected)
            {
                messageToConversationOwner  = string.Format(ConversationText.NotifyOwnerConnected, conversationClientName);
                messageToConversationClient = string.Format(ConversationText.NotifyClientConnected, conversationOwnerName);
            }
            else if (messageRouterResult.Type == MessageRouterResultType.Disconnected)
            {
                messageToConversationOwner  = string.Format(ConversationText.NotifyOwnerDisconnected, conversationClientName);
                messageToConversationClient = string.Format(ConversationText.NotifyClientDisconnected, conversationOwnerName);
            }

            if (conversationOwnerParty != null &&
                !string.IsNullOrEmpty(messageToConversationOwner))
            {
                await _messageRouterManager.SendMessageToPartyByBotAsync(
                    conversationOwnerParty, messageToConversationOwner);
            }

            if (conversationClientParty != null &&
                !string.IsNullOrEmpty(messageToConversationClient))
            {
                await _messageRouterManager.SendMessageToPartyByBotAsync(
                    conversationClientParty, messageToConversationClient);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Tries to accept/reject a pending request.
        /// </summary>
        /// <param name="messageRouterManager">The message router manager.</param>
        /// <param name="messageRouterResultHandler">The message router result handler.</param>
        /// <param name="senderParty">The sender party (accepter/rejecter).</param>
        /// <param name="doAccept">If true, will try to accept the request. If false, will reject.</param>
        /// <param name="channelAccountIdOfPartyToAcceptOrReject">The channel account ID of the party whose request to accep/reject.</param>
        /// <returns>Null, if an accept/reject operation was executed successfully.
        /// A user friendly error message otherwise.</returns>
        public async Task <string> AcceptOrRejectRequestAsync(
            MessageRouterManager messageRouterManager, MessageRouterResultHandler messageRouterResultHandler,
            Party senderParty, bool doAccept, string channelAccountIdOfPartyToAcceptOrReject)
        {
            string errorMessage = null;

            IRoutingDataManager routingDataManager = messageRouterManager.RoutingDataManager;
            Party partyToAcceptOrReject            = null;

            if (routingDataManager.GetPendingRequests().Count > 0)
            {
                try
                {
                    partyToAcceptOrReject = routingDataManager.GetPendingRequests().Single(
                        party => (party.ChannelAccount != null &&
                                  !string.IsNullOrEmpty(party.ChannelAccount.Id) &&
                                  party.ChannelAccount.Id.Equals(channelAccountIdOfPartyToAcceptOrReject)));
                }
                catch (InvalidOperationException e)
                {
                    errorMessage = string.Format(
                        ConversationText.FailedToFindPendingRequestForUserWithErrorMessage,
                        channelAccountIdOfPartyToAcceptOrReject,
                        e.Message);
                }
            }

            if (partyToAcceptOrReject != null)
            {
                Party connectedSenderParty =
                    routingDataManager.FindConnectedPartyByChannel(
                        senderParty.ChannelId, senderParty.ChannelAccount);

                bool senderIsConnected =
                    (connectedSenderParty != null &&
                     routingDataManager.IsConnected(connectedSenderParty, ConnectionProfile.Owner));

                MessageRouterResult messageRouterResult = null;

                if (doAccept)
                {
                    if (senderIsConnected)
                    {
                        // The sender (accepter/rejecter) is ALREADY connected with another party
                        Party otherParty = routingDataManager.GetConnectedCounterpart(connectedSenderParty);

                        if (otherParty != null)
                        {
                            errorMessage = string.Format(
                                ConversationText.AlreadyConnectedWithUser, otherParty.ChannelAccount?.Name);
                        }
                        else
                        {
                            errorMessage = ConversationText.ErrorOccured;
                        }
                    }
                    else
                    {
                        bool createNewDirectConversation =
                            !(NoDirectConversationsWithChannels.Contains(senderParty.ChannelId.ToLower()));

                        // Try to accept
                        messageRouterResult = await messageRouterManager.ConnectAsync(
                            senderParty,
                            partyToAcceptOrReject,
                            createNewDirectConversation);
                    }
                }
                else
                {
                    // Note: Rejecting is OK even if the sender is alreay connected
                    messageRouterResult = messageRouterManager.RejectPendingRequest(partyToAcceptOrReject, senderParty);
                }

                if (messageRouterResult != null)
                {
                    await messageRouterResultHandler.HandleResultAsync(messageRouterResult);
                }
            }
            else
            {
                errorMessage = ConversationText.FailedToFindPendingRequest;
            }

            return(errorMessage);
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 private MessageRouterManager()
 {
     // TODO: Get this instance from a database instead of keeping a local copy!
     RoutingDataManager = new LocalRoutingDataManager();
 }
Beispiel #12
0
        /// <summary>
        /// Checks the given activity for back channel messages and handles them, if detected.
        /// Currently the only back channel message supported is for creating connections
        /// (establishing 1:1 conversations).
        /// </summary>
        /// <param name="activity">The activity to check for back channel messages.</param>
        /// <returns>
        /// The result:
        ///     * MessageRouterResultType.Connected: A connection (1:1 conversation) was created
        ///     * MessageRouterResultType.NoActionTaken: No back channel message detected
        ///     * MessageRouterResultType.Error: See the error message for details
        /// </returns>
        public virtual MessageRouterResult HandleBackChannelMessage(Activity activity)
        {
            MessageRouterResult messageRouterResultNoAction = new MessageRouterResult {
                Type = MessageRouterResultType.NoActionTaken
            };
            MessageRouterResult messageRouterResult = new MessageRouterResult();
            var     Settings = new BotSettings();
            Manager manager  = new Manager(Settings[BotSettings.KeyRoutingDataStorageConnectionString]);

            if (activity == null || string.IsNullOrEmpty(activity.Text))
            {
                messageRouterResult.Type         = MessageRouterResultType.Error;
                messageRouterResult.ErrorMessage = $"The given activity ({nameof(activity)}) is either null or the message is missing";
            }
            else if (activity.Text.Equals(BackChannelId))
            {
                if (activity.ChannelData == null)
                {
                    messageRouterResult.Type         = MessageRouterResultType.Error;
                    messageRouterResult.ErrorMessage = "No channel data";
                }
                else
                {
                    // Handle accepted request and start 1:1 conversation
                    Party conversationClientParty = null;

                    try
                    {
                        conversationClientParty = ParsePartyFromChannelData(activity.ChannelData);
                        Debug.WriteLine($"Client : {JsonConvert.SerializeObject(conversationClientParty)}");
                    }
                    catch (Exception e)
                    {
                        messageRouterResult.Type         = MessageRouterResultType.Error;
                        messageRouterResult.ErrorMessage =
                            $"Failed to parse the party information from the back channel message: {e.Message}";
                    }

                    if (conversationClientParty != null)
                    {
                        Party conversationOwnerParty = MessagingUtils.CreateSenderParty(activity);
                        Debug.WriteLine($"Owner : {JsonConvert.SerializeObject(conversationOwnerParty)}");

                        MessageRouterManager messageRouterManager = WebApiConfig.MessageRouterManager;
                        IRoutingDataManager  routingDataManager   = messageRouterManager.RoutingDataManager;

                        bool isConnected = false;

                        Dictionary <Party, Party> connectedParties = routingDataManager.GetConnectedParties();

                        foreach (var connectedPartie in connectedParties)
                        {
                            if (connectedPartie.Value.ConversationAccount.Id == conversationClientParty.ConversationAccount.Id)
                            {
                                isConnected = true;
                                break;
                            }
                        }

                        if (isConnected)
                        {
                            bool deleteConnexion = manager.ExecuteRemoveConnexionByConversationClientId(conversationClientParty.ConversationAccount.Id);
                            messageRouterResult = deleteConnexion ? manager.Connect(conversationOwnerParty, conversationClientParty) : messageRouterResultNoAction;
                        }
                        else
                        {
                            Dictionary <Party, Party> waitingConnectedParties = manager.GetWaitingConnectedParties();
                            bool isWaitingConnected = false;

                            foreach (var waitingConnectedPartie in waitingConnectedParties)
                            {
                                if (waitingConnectedPartie.Value.ConversationAccount.Id == conversationClientParty.ConversationAccount.Id)
                                {
                                    isWaitingConnected = true;
                                    break;
                                }
                            }

                            if (isWaitingConnected)
                            {
                                bool deleteWaitingConnection = manager.ExecuteRemoveWaitingConnexionByConversationClientId(conversationClientParty.ConversationAccount.Id);
                                messageRouterResult = deleteWaitingConnection ? manager.WaitingConnectAndClearPendingRequest(conversationOwnerParty, conversationClientParty) : messageRouterResultNoAction;

                                messageRouterResult.Activity = activity;
                            }
                            else
                            {
                                messageRouterResult = manager.WaitingConnectAndClearPendingRequest(conversationOwnerParty, conversationClientParty);

                                //messageRouterResult = _routingDataManager.ConnectAndClearPendingRequest(
                                //    conversationOwnerParty, conversationClientParty);

                                messageRouterResult.Activity = activity;
                            }
                        }
                    }
                }
            }
            else
            {
                // No back channel message detected
                //messageRouterResult.Type = MessageRouterResultType.NoActionTaken;
                ConnectionEntity waitingConnection = manager.RetrieveWaitingConnectionByConversationIdOwner(activity.Conversation.Id);

                if (waitingConnection != null)
                {
                    Party conversationOwnerParty  = JsonConvert.DeserializeObject <PartyEntity>(waitingConnection.Owner).ToParty();
                    Party conversationClientParty = JsonConvert.DeserializeObject <PartyEntity>(waitingConnection.Client).ToParty();

                    if (activity.Text == $"@{conversationOwnerParty.ChannelAccount.Name} accept {conversationClientParty.ChannelAccount.Id}" || activity.Text == $"@{conversationOwnerParty.ChannelAccount.Name} reject {conversationClientParty.ChannelAccount.Id}")
                    {
                        if (activity.Text.Contains("accept"))
                        {
                            messageRouterResult = manager.Connect(conversationOwnerParty, conversationClientParty);
                        }
                        else
                        {
                            messageRouterResult.Type = MessageRouterResultType.ConnectionRejected;
                            messageRouterResult.ConversationClientParty = conversationClientParty;
                            messageRouterResult.ConversationOwnerParty  = conversationOwnerParty;
                        }

                        manager.RemoveWaitingConnection(conversationOwnerParty, conversationClientParty);
                    }
                    else
                    {
                        messageRouterResult.Type = MessageRouterResultType.ConnectionAlreadyRequested;

                        if (activity.Conversation.Id == conversationOwnerParty.ConversationAccount.Id)
                        {
                            messageRouterResult.ConversationOwnerParty  = conversationOwnerParty;
                            messageRouterResult.ConversationClientParty = conversationClientParty;
                        }
                        else
                        {
                            messageRouterResult.ConversationClientParty = conversationClientParty;
                        }
                    }
                }
                else
                {
                    // No back channel message detected
                    messageRouterResult.Type = MessageRouterResultType.NoActionTaken;
                }
            }

            return(messageRouterResult);
        }
Beispiel #13
0
        /// <summary>
        /// Tries to accept/reject a pending request.
        /// </summary>
        /// <param name="senderParty">The sender party (accepter/rejecter).</param>
        /// <param name="commandMessage">The command message. Required for resolving the party to accept/reject.</param>
        /// <param name="doAccept">If true, will try to accept the request. If false, will reject.</param>
        /// <returns>Null, if successful. A user friendly error message otherwise.</returns>
        private async Task <string> AcceptOrRejectRequestAsync(Party senderParty, string commandMessage, bool doAccept)
        {
            string errorMessage = null;
            IRoutingDataManager routingDataManager = _messageRouterManager.RoutingDataManager;
            Party connectedSenderParty             = routingDataManager.FindConnectedPartyByChannel(senderParty.ChannelId, senderParty.ChannelAccount);

            if (connectedSenderParty == null || !routingDataManager.IsConnected(senderParty, ConnectionProfile.Owner))
            {
                // The sender (accepter/rejecter) is NOT connected with another party
                if (routingDataManager.GetPendingRequests().Count > 0)
                {
                    // The name of the user to accept should be the second word
                    string[] splitMessage = commandMessage.Split(' ');

                    if (splitMessage.Count() > 1 && !string.IsNullOrEmpty(splitMessage[1]))
                    {
                        Party partyToAcceptOrReject = null;

                        try
                        {
                            partyToAcceptOrReject = routingDataManager.GetPendingRequests().Single(
                                party => (party.ChannelAccount != null &&
                                          !string.IsNullOrEmpty(party.ChannelAccount.Id) &&
                                          party.ChannelAccount.Id.Equals(splitMessage[1])));
                        }
                        catch (InvalidOperationException e)
                        {
                            errorMessage = $"Failed to find a pending request for user \"{splitMessage[1]}\": {e.Message}";
                        }

                        if (partyToAcceptOrReject != null)
                        {
                            MessageRouterResult messageRouterResult = null;

                            if (doAccept)
                            {
                                messageRouterResult = await _messageRouterManager.ConnectAsync(
                                    senderParty, partyToAcceptOrReject, !partyToAcceptOrReject.ChannelId.Contains("skype"));
                            }
                            else
                            {
                                messageRouterResult = _messageRouterManager.RejectPendingRequest(partyToAcceptOrReject, senderParty);
                            }

                            await _messageRouterResultHandler.HandleResultAsync(messageRouterResult);
                        }
                    }
                    else
                    {
                        errorMessage = "User name missing";
                    }
                }
                else
                {
                    errorMessage = "No pending requests";
                }
            }
            else
            {
                // The sender (accepter/rejecter) is ALREADY connected with another party
                Party otherParty = routingDataManager.GetConnectedCounterpart(connectedSenderParty);

                if (otherParty != null)
                {
                    errorMessage = $"You are already connected with user \"{otherParty.ChannelAccount.Name}\"";
                }
                else
                {
                    errorMessage = "An error occured";
                }
            }

            return(errorMessage);
        }
Beispiel #14
0
        /// <summary>
        /// Checks the given activity for a possible command.
        ///
        /// All messages that start with a specific command keyword or contain a mention of the bot
        /// ("@<bot name>") are checked for possible commands.
        /// </summary>
        /// <param name="activity">An Activity instance containing a possible command.</param>
        /// <returns>True, if a command was detected and handled. False otherwise.</returns>
        public async virtual Task <bool> HandleCommandAsync(Activity activity)
        {
            bool     wasHandled    = false;
            Activity replyActivity = null;

            if ((!string.IsNullOrEmpty(activity.Text) && activity.Text.StartsWith($"{Commands.CommandKeyword} ")) ||
                WasBotAddressedDirectly(activity, false))
            {
                string commandMessage = ExtractCleanCommandMessage(activity);
                Party  senderParty    = MessagingUtils.CreateSenderParty(activity);

                switch (commandMessage.ToLower())
                {
                case string command when(command.StartsWith(Commands.CommandListOptions)):
                    // Present all command options in a card
                    replyActivity = CreateCommandOptionsCard(activity);

                    wasHandled = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandAddAggregationChannel)):
                    // Establish the sender's channel/conversation as an aggreated one if not already exists
                    Party aggregationParty = new Party(activity.ServiceUrl, activity.ChannelId, null, activity.Conversation);

                    if (_messageRouterManager.RoutingDataManager.AddAggregationParty(aggregationParty))
                    {
                        replyActivity = activity.CreateReply("This channel/conversation is now where the requests are aggregated");
                    }
                    else
                    {
                        replyActivity = activity.CreateReply("This channel/conversation is already receiving requests");
                    }

                    wasHandled = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandAcceptRequest) || command.StartsWith(Commands.CommandRejectRequest)):
                    // Accept/reject conversation request
                    bool doAccept = command.StartsWith(Commands.CommandAcceptRequest);

                    if (_messageRouterManager.RoutingDataManager.IsAssociatedWithAggregation(senderParty))
                    {
                        // The party is associated with the aggregation and has the right to accept/reject
                        string errorMessage = await AcceptOrRejectRequestAsync(senderParty, commandMessage, doAccept);

                        if (!string.IsNullOrEmpty(errorMessage))
                        {
                            replyActivity = activity.CreateReply(errorMessage);
                        }
                    }
                    else
                    {
                        replyActivity = activity.CreateReply("Sorry, you are not allowed to accept/reject requests");
                    }

                    wasHandled = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandDisconnect)):
                    // End the 1:1 conversation
                    IList <MessageRouterResult> messageRouterResults = _messageRouterManager.Disconnect(senderParty);

                    foreach (MessageRouterResult messageRouterResult in messageRouterResults)
                    {
                        await _messageRouterResultHandler.HandleResultAsync(messageRouterResult);
                    }

                    wasHandled = true;
                    break;


                    #region Implementation of debugging commands
#if DEBUG
                case string command when(command.StartsWith(Commands.CommandDeleteAllRoutingData)):
                    // DELETE ALL ROUTING DATA
                    await _messageRouterManager.BroadcastMessageToAggregationChannelsAsync(
                        $"Deleting all data as requested by \"{senderParty.ChannelAccount?.Name}\"...");

                    replyActivity = activity.CreateReply("Deleting all data...");
                    _messageRouterManager.RoutingDataManager.DeleteAll();
                    wasHandled = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandListAllParties)):
                    // List user and bot parties
                    IRoutingDataManager routingDataManager = _messageRouterManager.RoutingDataManager;

                    string replyMessage    = string.Empty;
                    string partiesAsString = PartyListToString(routingDataManager.GetUserParties());

                    if (string.IsNullOrEmpty(partiesAsString))
                    {
                        replyMessage = $"No user parties{LineBreak}";
                    }
                    else
                    {
                        replyMessage = $"Users:{LineBreak}{partiesAsString}";
                    }

                    partiesAsString = PartyListToString(routingDataManager.GetBotParties());

                    if (string.IsNullOrEmpty(partiesAsString))
                    {
                        replyMessage += "No bot parties";
                    }
                    else
                    {
                        replyMessage += $"Bot:{LineBreak}{partiesAsString}";
                    }

                    replyActivity = activity.CreateReply(replyMessage);
                    wasHandled    = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandListPendingRequests)):
                    // List all pending requests
                    var attachments = new List <Attachment>();

                    foreach (Party party in _messageRouterManager.RoutingDataManager.GetPendingRequests())
                    {
                        attachments.Add(CreateRequestCard(party, activity.Recipient.Name));
                    }

                    if (attachments.Count > 0)
                    {
                        replyActivity = activity.CreateReply($"{attachments.Count} pending request(s) found:");
                        replyActivity.AttachmentLayout = AttachmentLayoutTypes.Carousel;
                        replyActivity.Attachments      = attachments;
                    }
                    else
                    {
                        replyActivity = activity.CreateReply("No pending requests");
                    }

                    wasHandled = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandListConnections)):
                    // List all connections (conversations)
                    string parties = _messageRouterManager.RoutingDataManager.ConnectionsToString();

                    if (string.IsNullOrEmpty(parties))
                    {
                        replyActivity = activity.CreateReply("No conversations");
                    }
                    else
                    {
                        replyActivity = activity.CreateReply(parties);
                    }

                    wasHandled = true;
                    break;

                case string command when(command.StartsWith(Commands.CommandListLastMessageRouterResults)):
                    // List all logged message router results
                    string resultsAsString = _messageRouterManager.RoutingDataManager.GetLastMessageRouterResults();

                    replyActivity = activity.CreateReply($"{(string.IsNullOrEmpty(resultsAsString) ? "No results" : resultsAsString)}");
                    wasHandled    = true;
                    break;
#endif
                    #endregion

                default:
                    replyActivity = activity.CreateReply($"Command \"{commandMessage}\" not recognized");
                    break;
                }

                if (replyActivity != null)
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
                    await connector.Conversations.ReplyToActivityAsync(replyActivity);
                }
            }

            return(wasHandled);
        }
        /// <summary>
        /// Notifies both the conversation owner (agent) and the conversation client (customer)
        /// about the connection status change.
        /// </summary>
        /// <param name="messageRouterResult">The result to handle.</param>
        protected virtual async Task HandleConnectionChangedResultAsync(MessageRouterResult messageRouterResult)
        {
            IRoutingDataManager routingDataManager = _messageRouterManager.RoutingDataManager;

            Party conversationOwnerParty  = messageRouterResult.ConversationOwnerParty;
            Party conversationClientParty = messageRouterResult.ConversationClientParty;

            string conversationOwnerName  = conversationOwnerParty?.ChannelAccount.Name;
            string conversationClientName = conversationClientParty?.ChannelAccount.Name;

            string messageToConversationOwner  = string.Empty;
            string messageToConversationClient = string.Empty;

            if (messageRouterResult.Type == MessageRouterResultType.ConnectionRequested)
            {
                if (conversationClientParty == null || conversationClientParty.ChannelAccount == null)
                {
                    await _messageRouterManager.BroadcastMessageToAggregationChannelsAsync(
                        ConversationText.ConnectionRequestMadeButRequestorIsNull);

                    throw new NullReferenceException(ConversationText.ConnectionRequestMadeButRequestorIsNull);
                }

                foreach (Party aggregationParty in _messageRouterManager.RoutingDataManager.GetAggregationParties())
                {
                    Party botParty = routingDataManager
                                     .FindBotPartyByChannelAndConversation(aggregationParty.ChannelId, aggregationParty.ConversationAccount);

                    if (botParty != null)
                    {
                        IMessageActivity messageActivity = Activity.CreateMessageActivity();
                        messageActivity.Conversation = aggregationParty.ConversationAccount;
                        messageActivity.Recipient    = aggregationParty.ChannelAccount;
                        messageActivity.Attachments  = new List <Attachment>
                        {
                            CommandCardFactory.CreateRequestCard(
                                conversationClientParty, botParty.ChannelAccount?.Name).ToAttachment()
                        };

                        try
                        {
                            await _messageRouterManager.SendMessageToPartyByBotAsync(aggregationParty, messageActivity);
                        }
                        catch (UnauthorizedAccessException e)
                        {
                            System.Diagnostics.Debug.WriteLine($"Failed to broadcast message: {e.Message}");
                        }
                    }
                    else
                    {
                        try
                        {
                            await _messageRouterManager.BroadcastMessageToAggregationChannelsAsync(
                                string.Format(
                                    ConversationText.FailedToFindBotOnAggregationChannel,
                                    aggregationParty.ConversationAccount.Name));
                        }
                        catch (UnauthorizedAccessException e)
                        {
                            System.Diagnostics.Debug.WriteLine($"Failed to send message: {e.Message}");
                        }
                    }
                }

                messageToConversationClient = ConversationText.NotifyClientWaitForRequestHandling;
            }
            else if (messageRouterResult.Type == MessageRouterResultType.ConnectionAlreadyRequested)
            {
                messageToConversationClient = ConversationText.NotifyClientDuplicateRequest;
            }
            else if (messageRouterResult.Type == MessageRouterResultType.ConnectionRejected)
            {
                messageToConversationOwner  = string.Format(ConversationText.NotifyOwnerRequestRejected, conversationClientName);
                messageToConversationClient = ConversationText.NotifyClientRequestRejected;
            }
            else if (messageRouterResult.Type == MessageRouterResultType.Connected)
            {
                messageToConversationOwner  = string.Format(ConversationText.NotifyOwnerConnected, conversationClientName);
                messageToConversationClient = string.Format(ConversationText.NotifyClientConnected, conversationOwnerName);
            }
            else if (messageRouterResult.Type == MessageRouterResultType.Disconnected)
            {
                messageToConversationOwner  = string.Format(ConversationText.NotifyOwnerDisconnected, conversationClientName);
                messageToConversationClient = string.Format(ConversationText.NotifyClientDisconnected, conversationOwnerName);
            }

            if (!string.IsNullOrEmpty(messageToConversationOwner) && conversationOwnerParty != null)
            {
                try
                {
                    await _messageRouterManager.SendMessageToPartyByBotAsync(
                        conversationOwnerParty, messageToConversationOwner);
                }
                catch (UnauthorizedAccessException e)
                {
                    System.Diagnostics.Debug.WriteLine($"Failed to send message: {e.Message}");
                }
            }

            if (!string.IsNullOrEmpty(messageToConversationClient) && conversationClientParty != null)
            {
                try
                {
                    await _messageRouterManager.SendMessageToPartyByBotAsync(
                        conversationClientParty, messageToConversationClient);
                }
                catch (UnauthorizedAccessException e)
                {
                    System.Diagnostics.Debug.WriteLine($"Failed to send message: {e.Message}");
                }
            }
        }