/// <summary>
        /// Tries to send the given message activity to the given party using this bot on the same
        /// channel as the party who the message is sent to.
        /// </summary>
        /// <param name="partyToMessage">The party to send the message to.</param>
        /// <param name="messageActivity">The message activity to send (message content).</param>
        /// <returns>The ResourceResponse instance or null in case of an error.</returns>
        public async Task <ResourceResponse> SendMessageToPartyByBotAsync(Party partyToMessage, IMessageActivity messageActivity)
        {
            Party botParty = null;

            if (partyToMessage != null)
            {
                // We need the channel account of the bot in the SAME CHANNEL as the RECIPIENT.
                // The identity of the bot in the channel of the sender is most likely a different one and
                // thus unusable since it will not be recognized on the recipient's channel.
                botParty = RoutingDataManager.FindBotPartyByChannelAndConversation(
                    partyToMessage.ChannelId, partyToMessage.ConversationAccount);
            }

            if (botParty != null)
            {
                messageActivity.From = botParty.ChannelAccount;

                MessagingUtils.ConnectorClientAndMessageBundle bundle =
                    MessagingUtils.CreateConnectorClientAndMessageActivity(
                        partyToMessage.ServiceUrl, messageActivity);

                return(await bundle.connectorClient.Conversations.SendToConversationAsync(
                           (Activity)bundle.messageActivity));
            }

            return(null);
        }
        /// <summary>
        /// Tries to initiates the engagement by creating a request on behalf of the sender in the
        /// given activity. This method does nothing, if a request for the same user already exists.
        /// </summary>
        /// <param name="activity">The activity.</param>
        /// <returns>The result of the operation.</returns>
        public async Task <MessageRouterResult> InitiateEngagementAsync(Activity activity)
        {
            MessageRouterResult messageRouterResult =
                RoutingDataManager.AddPendingRequest(MessagingUtils.CreateSenderParty(activity));

            messageRouterResult.Activity = activity;
            await HandleAndLogMessageRouterResultAsync(messageRouterResult);

            return(messageRouterResult);
        }
        /// <summary>
        /// From IMessageRouterResultHandler.
        /// </summary>
        /// <param name="messageRouterResult">The result to handle.</param>
        /// <returns></returns>
        public virtual async Task HandleResultAsync(MessageRouterResult messageRouterResult)
        {
            if (messageRouterResult == null)
            {
                throw new ArgumentNullException($"The given result ({nameof(messageRouterResult)}) is null");
            }

            string message = "";
            MessageRouterManager messageRouterManager;

            switch (messageRouterResult.Type)
            {
            case MessageRouterResultType.NoActionTaken:
            case MessageRouterResultType.OK:
                // No need to do anything
                break;

            case MessageRouterResultType.EngagementInitiated:
            case MessageRouterResultType.EngagementAlreadyInitiated:
            case MessageRouterResultType.EngagementRejected:
            case MessageRouterResultType.EngagementAdded:
            case MessageRouterResultType.EngagementRemoved:
                await HandleEngagementChangedResultAsync(messageRouterResult);

                break;

            case MessageRouterResultType.NoAggregationChannel:
                if (messageRouterResult.Activity != null)
                {
                    messageRouterManager = MessageRouterManager.Instance;

                    string botName = messageRouterManager.RoutingDataManager.ResolveBotNameInConversation(
                        MessagingUtils.CreateSenderParty(messageRouterResult.Activity));

                    message  = $"{(string.IsNullOrEmpty(messageRouterResult.ErrorMessage) ? "" : $"{messageRouterResult.ErrorMessage}: ")}The message router manager is not initialized; type \"";
                    message += string.IsNullOrEmpty(botName) ? $"{Commands.CommandKeyword} " : $"@{botName} ";
                    message += $"{Commands.CommandAddAggregationChannel}\" to setup the aggregation channel";

                    await MessagingUtils.ReplyToActivityAsync(messageRouterResult.Activity, message);
                }
        /// <summary>
        /// From IMessageRouterResultHandler.
        /// </summary>
        /// <param name="messageRouterResult">The result to handle.</param>
        /// <returns></returns>
        public virtual async Task HandleResultAsync(MessageRouterResult messageRouterResult)
        {
            if (messageRouterResult == null)
            {
                throw new ArgumentNullException($"The given result ({nameof(messageRouterResult)}) is null");
            }

            if (messageRouterResult.Type == MessageRouterResultType.NoActionTaken ||
                messageRouterResult.Type == MessageRouterResultType.OK)
            {
                // No need to do anything
            }
            if (messageRouterResult.Type == MessageRouterResultType.EngagementInitiated ||
                messageRouterResult.Type == MessageRouterResultType.EngagementAlreadyInitiated ||
                messageRouterResult.Type == MessageRouterResultType.EngagementRejected ||
                messageRouterResult.Type == MessageRouterResultType.EngagementAdded ||
                messageRouterResult.Type == MessageRouterResultType.EngagementRemoved)
            {
                await HandleEngagementChangedResultAsync(messageRouterResult);
            }
            else if (messageRouterResult.Type == MessageRouterResultType.NoAggregationChannel)
            {
                if (messageRouterResult.Activity != null)
                {
                    MessageRouterManager messageRouterManager = MessageRouterManager.Instance;

                    string botName = messageRouterManager.RoutingDataManager.ResolveBotNameInConversation(
                        MessagingUtils.CreateSenderParty(messageRouterResult.Activity));

                    string message = $"{(string.IsNullOrEmpty(messageRouterResult.ErrorMessage)? "" : $"{messageRouterResult.ErrorMessage}: ")}The message router manager is not initialized; type \"";
                    message += string.IsNullOrEmpty(botName) ? $"{Commands.CommandKeyword} " : $"@{botName} ";
                    message += $"{Commands.CommandAddAggregationChannel}\" to setup the aggregation channel";

                    await MessagingUtils.ReplyToActivityAsync(messageRouterResult.Activity, message);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("The activity of the result is null");
                }
            }
        /// <summary>
        /// Checks the given activity for back channel messages and handles them, if detected.
        /// Currently the only back channel message supported is for adding engagements
        /// (establishing 1:1 conversations).
        /// </summary>
        /// <param name="activity">The activity to check for back channel messages.</param>
        /// <returns>True, if a back channel message was detected and handled. False otherwise.</returns>
        public async Task <bool> HandleBackChannelMessageAsync(Activity activity)
        {
            MessageRouterResult messageRouterResult = new MessageRouterResult();

            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.StartsWith(BackChannelId))
            {
                if (activity.ChannelData == null)
                {
                    messageRouterResult.Type         = MessageRouterResultType.Error;
                    messageRouterResult.ErrorMessage = "No channel data";
                }
                else
                {
                    // Handle accepted request and start 1:1 conversation
                    string partyId = ((JObject)activity.ChannelData)[BackChannelId][PartyIdPropertyId].ToString();
                    Party  conversationClientParty = Party.FromIdString(partyId);

                    Party conversationOwnerParty = MessagingUtils.CreateSenderParty(activity);

                    messageRouterResult = RoutingDataManager.AddEngagementAndClearPendingRequest(
                        conversationOwnerParty, conversationClientParty);
                    messageRouterResult.Activity = activity;
                }
            }
            else
            {
                // No back channel message detected
                messageRouterResult.Type = MessageRouterResultType.NoActionTaken;
            }

            await HandleAndLogMessageRouterResultAsync(messageRouterResult);

            return(messageRouterResult.Type == MessageRouterResultType.EngagementAdded);
        }
        /// <summary>
        /// Tries to send the given message to the given party using this bot on the same channel
        /// as the party who the message is sent to.
        /// </summary>
        /// <param name="partyToMessage">The party to send the message to.</param>
        /// <param name="messageText">The message content.</param>
        /// <returns>The ResourceResponse instance or null in case of an error.</returns>
        public async Task <ResourceResponse> SendMessageToPartyByBotAsync(Party partyToMessage, string messageText)
        {
            Party botParty = null;

            if (partyToMessage != null)
            {
                botParty = RoutingDataManager.FindBotPartyByChannelAndConversation(
                    partyToMessage.ChannelId, partyToMessage.ConversationAccount);
            }

            if (botParty != null)
            {
                MessagingUtils.ConnectorClientAndMessageBundle bundle =
                    MessagingUtils.CreateConnectorClientAndMessageActivity(
                        partyToMessage, messageText, botParty?.ChannelAccount);

                return(await bundle.connectorClient.Conversations.SendToConversationAsync(
                           (Activity)bundle.messageActivity));
            }

            return(null);
        }
        /// <summary>
        /// All messages where the bot was mentioned ("@<bot name>) are checked for possible commands.
        /// See IBotCommandHandler.cs for more information.
        /// </summary>
        public async virtual Task <bool> HandleCommandAsync(Activity activity)
        {
            bool     wasHandled    = false;
            Activity replyActivity = null;

            if (WasBotAddressedDirectly(activity) ||
                (!string.IsNullOrEmpty(activity.Text) && activity.Text.StartsWith($"{Commands.CommandKeyword} ")))
            {
                string message = MessagingUtils.StripMentionsFromMessage(activity);

                if (message.StartsWith($"{Commands.CommandKeyword} "))
                {
                    message = message.Remove(0, Commands.CommandKeyword.Length + 1);
                }

                string messageInLowerCase = message?.ToLower();

                if (messageInLowerCase.StartsWith(Commands.CommandAddAggregationChannel))
                {
                    // Check if the Aggregation Party already exists
                    Party aggregationParty = new Party(
                        activity.ServiceUrl,
                        activity.ChannelId,
                        /* not a specific user, but a channel/conv */ null,
                        activity.Conversation);

                    // Establish the sender's channel/conversation as an aggreated one
                    if (_routingDataManager.AddAggregationParty(aggregationParty))
                    {
                        replyActivity = activity.CreateReply(
                            "This channel/conversation is now where the requests are aggregated");
                    }
                    else
                    {
                        // Aggregation already exists
                        replyActivity = activity.CreateReply(
                            "This channel/conversation is already receiving requests");
                    }

                    wasHandled = true;
                }
                else if (messageInLowerCase.StartsWith(Commands.CommandAcceptRequest) ||
                         messageInLowerCase.StartsWith(Commands.CommandRejectRequest))
                {
                    // Accept/reject conversation request
                    bool  doAccept    = messageInLowerCase.StartsWith(Commands.CommandAcceptRequest);
                    Party senderParty = MessagingUtils.CreateSenderParty(activity);

                    if (_routingDataManager.IsAssociatedWithAggregation(senderParty))
                    {
                        // The party is associated with the aggregation and has the right to accept/reject
                        Party senderInConversation =
                            _routingDataManager.FindEngagedPartyByChannel(senderParty.ChannelId, senderParty.ChannelAccount);

                        if (senderInConversation == null || !_routingDataManager.IsEngaged(senderInConversation, EngagementProfile.Owner))
                        {
                            if (_routingDataManager.GetPendingRequests().Count > 0)
                            {
                                // The name of the user to accept should be the second word
                                string[] splitMessage = message.Split(' ');

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

                                    try
                                    {
                                        partyToAcceptOrReject = _routingDataManager.GetPendingRequests().Single(
                                            party => (party.ChannelAccount != null &&
                                                      !string.IsNullOrEmpty(party.ChannelAccount.Id) &&
                                                      party.ChannelAccount.Id.Equals(splitMessage[1])));
                                    }
                                    catch (InvalidOperationException e)
                                    {
                                        errorMessage = e.Message;
                                    }

                                    if (partyToAcceptOrReject != null)
                                    {
                                        MessageRouterManager messageRouterManager = MessageRouterManager.Instance;

                                        if (doAccept)
                                        {
                                            await messageRouterManager.AddEngagementAsync(senderParty, partyToAcceptOrReject);
                                        }
                                        else
                                        {
                                            await messageRouterManager.RejectPendingRequestAsync(partyToAcceptOrReject, senderParty);
                                        }
                                    }
                                    else
                                    {
                                        replyActivity = activity.CreateReply(
                                            $"Could not find a pending request for user {splitMessage[1]}; {errorMessage}");
                                    }
                                }
                                else
                                {
                                    replyActivity = activity.CreateReply("User name missing");
                                }
                            }
                            else
                            {
                                replyActivity = activity.CreateReply("No pending requests");
                            }
                        }
                        else
                        {
                            Party otherParty = _routingDataManager.GetEngagedCounterpart(senderInConversation);

                            if (otherParty != null)
                            {
                                replyActivity = activity.CreateReply($"You are already engaged in a conversation with {otherParty.ChannelAccount.Name}");
                            }
                            else
                            {
                                replyActivity = activity.CreateReply("An error occured");
                            }
                        }

                        wasHandled = true;
                    }
                }
                else if (messageInLowerCase.StartsWith(Commands.CommandEndEngagement))
                {
                    // End the 1:1 conversation
                    Party senderParty = MessagingUtils.CreateSenderParty(activity);

                    if (await MessageRouterManager.Instance.EndEngagementAsync(senderParty) == false)
                    {
                        replyActivity = activity.CreateReply("Failed to end the engagement");
                    }

                    wasHandled = true;
                }

                /*
                 * NOTE: Either remove these commands or make them unaccessible should you use this
                 * code in production!
                 */
                #region Commands for debugging
                else if (messageInLowerCase.StartsWith(Commands.CommandDeleteAllRoutingData))
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
                    await connector.Conversations.ReplyToActivityAsync(activity.CreateReply("Deleting all data..."));

                    _routingDataManager.DeleteAll();

                    wasHandled = true;
                }
                else if (messageInLowerCase.StartsWith(Commands.CommandListAllParties))
                {
                    string replyMessage = string.Empty;
                    string parties      = string.Empty;

                    foreach (Party userParty in _routingDataManager.GetUserParties())
                    {
                        parties += userParty.ToString() + "\n";
                    }

                    if (string.IsNullOrEmpty(parties))
                    {
                        replyMessage = "No user parties;\n";
                    }
                    else
                    {
                        replyMessage = "Users:\n" + parties;
                    }

                    parties = string.Empty;

                    foreach (Party botParty in _routingDataManager.GetBotParties())
                    {
                        parties += botParty.ToString() + "\n";
                    }

                    if (string.IsNullOrEmpty(parties))
                    {
                        replyMessage += "No bot parties";
                    }
                    else
                    {
                        replyMessage += "Bot:\n" + parties;
                    }

                    replyActivity = activity.CreateReply(replyMessage);
                    wasHandled    = true;
                }
                else if (messageInLowerCase.StartsWith(Commands.CommandListPendingRequests))
                {
                    string parties = string.Empty;

                    foreach (Party party in _routingDataManager.GetPendingRequests())
                    {
                        parties += party.ToString() + "\n";
                    }

                    if (parties.Length == 0)
                    {
                        parties = "No pending requests";
                    }

                    replyActivity = activity.CreateReply(parties);
                    wasHandled    = true;
                }
                else if (messageInLowerCase.StartsWith(Commands.CommandListEngagements))
                {
                    string parties = _routingDataManager.EngagementsAsString();

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

                    wasHandled = true;
                }
#if DEBUG
                else if (messageInLowerCase.StartsWith(Commands.CommandListLastMessageRouterResults))
                {
                    LocalRoutingDataManager routingDataManager =
                        (MessageRouterManager.Instance.RoutingDataManager as LocalRoutingDataManager);

                    if (routingDataManager != null)
                    {
                        string resultsAsString = routingDataManager.GetLastMessageRouterResults();
                        replyActivity = activity.CreateReply($"{(string.IsNullOrEmpty(resultsAsString) ? "No results" : resultsAsString)}");
                        wasHandled    = true;
                    }
                }
#endif
                #endregion Commands for debugging

                else
                {
                    replyActivity = activity.CreateReply($"Command \"{messageInLowerCase}\" not recognized");
                }
            }

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

            return(wasHandled);
        }
        /// <summary>
        /// Handles the incoming message activities. For instance, if it is a message from party
        /// engaged in a chat, the message will be forwarded to the counterpart in whatever
        /// channel that party is on.
        /// </summary>
        /// <param name="activity">The activity to handle.</param>
        /// <param name="addClientNameToMessage">If true, will add the client's name to the beginning of the message.</param>
        /// <param name="addOwnerNameToMessage">If true, will add the owner's (agent) name to the beginning of the message.</param>
        /// <returns>The result of the operation.</returns>
        public async Task <MessageRouterResult> HandleMessageAsync(
            Activity activity, bool addClientNameToMessage = true, bool addOwnerNameToMessage = false)
        {
            MessageRouterResult result = new MessageRouterResult()
            {
                Type     = MessageRouterResultType.NoActionTaken,
                Activity = activity
            };

            Party senderParty = MessagingUtils.CreateSenderParty(activity);

            if (RoutingDataManager.IsEngaged(senderParty, EngagementProfile.Owner))
            {
                // Sender is an owner of an ongoing conversation - forward the message
                result.ConversationOwnerParty = senderParty;
                Party partyToForwardMessageTo = RoutingDataManager.GetEngagedCounterpart(senderParty);

                if (partyToForwardMessageTo != null)
                {
                    result.ConversationClientParty = partyToForwardMessageTo;
                    string message = addOwnerNameToMessage
                        ? $"{senderParty.ChannelAccount.Name}: {activity.Text}" : activity.Text;
                    ResourceResponse resourceResponse =
                        await SendMessageToPartyByBotAsync(partyToForwardMessageTo, activity.Text);

                    if (MessagingUtils.WasSuccessful(resourceResponse))
                    {
                        result.Type = MessageRouterResultType.OK;
                    }
                    else
                    {
                        result.Type         = MessageRouterResultType.FailedToForwardMessage;
                        result.ErrorMessage = $"Failed to forward the message to user {partyToForwardMessageTo}";
                    }
                }
                else
                {
                    result.Type         = MessageRouterResultType.FailedToForwardMessage;
                    result.ErrorMessage = "Failed to find the party to forward the message to";
                }
            }
            else if (RoutingDataManager.IsEngaged(senderParty, EngagementProfile.Client))
            {
                // Sender is a participant of an ongoing conversation - forward the message
                result.ConversationClientParty = senderParty;
                Party partyToForwardMessageTo = RoutingDataManager.GetEngagedCounterpart(senderParty);

                if (partyToForwardMessageTo != null)
                {
                    result.ConversationOwnerParty = partyToForwardMessageTo;
                    string message = addClientNameToMessage
                        ? $"{senderParty.ChannelAccount.Name}: {activity.Text}" : activity.Text;
                    await SendMessageToPartyByBotAsync(partyToForwardMessageTo, message);

                    result.Type = MessageRouterResultType.OK;
                }
                else
                {
                    result.Type         = MessageRouterResultType.FailedToForwardMessage;
                    result.ErrorMessage = "Failed to find the party to forward the message to";
                }
            }

            await HandleAndLogMessageRouterResultAsync(result);

            return(result);
        }
 /// <summary>
 /// Checks the given activity for new parties and adds them to the collection, if not
 /// already there.
 /// </summary>
 /// <param name="activity">The activity.</param>
 public void MakeSurePartiesAreTracked(IActivity activity)
 {
     MakeSurePartiesAreTracked(
         MessagingUtils.CreateSenderParty(activity),
         MessagingUtils.CreateRecipientParty(activity));
 }
Пример #10
0
        /// <summary>
        /// All messages where the bot was mentioned ("@<bot name>) are checked for possible commands.
        /// See IBotCommandHandler.cs for more information.
        /// </summary>
        public async virtual Task <bool> HandleCommandAsync(Activity activity)
        {
            bool     wasHandled    = false;
            Activity replyActivity = null;

            // Sole use of mentions here is unreliable for Skype & Webchat
            // Therefore just parse the text for any bot id reference
            if (WasBotAddressedDirectly(activity) ||
                activity.Text.Contains(activity.Recipient.Name) ||
                (!string.IsNullOrEmpty(activity.Text) && activity.Text.StartsWith($"{Commands.CommandKeyword} ")))
            {
                //activity.RemoveRecipientMention(); not working here
                //string message = MessagingUtils.StripMentionsFromMessage(activity);
                string message = activity.Text.Replace($"@{activity.Recipient.Name}", "").Trim();
                if (message.StartsWith($"{Commands.CommandKeyword} "))
                {
                    message = message.Remove(0, Commands.CommandKeyword.Length + 1);
                }

                switch (message?.ToLower())
                {
                case string s when(s.StartsWith(Commands.CommandEndEngagement)):
                {
                    // End the 1:1 conversation
                    Party senderParty = MessagingUtils.CreateSenderParty(activity);

                    if (await MessageRouterManager.Instance.EndEngagementAsync(senderParty) == false)
                    {
                        replyActivity = activity.CreateReply("Failed to end the engagement");
                    }
                    wasHandled = true;
                    break;
                }

                case string s when(s.StartsWith(Commands.CommandAddAggregationChannel)):
                {
                    // Check if the Aggregation Party already exists
                    /* not a specific user, but a channel/conv */
                    Party aggregationParty = new Party(activity.ServiceUrl, activity.ChannelId, null, activity.Conversation);

                    // Establish the sender's channel/conversation as an aggreated one if not already exists
                    if (_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 s when(s.StartsWith(Commands.CommandAcceptRequest) || s.StartsWith(Commands.CommandRejectRequest)):
                {
                    // Accept/reject conversation request
                    bool  doAccept    = s.StartsWith(Commands.CommandAcceptRequest);
                    Party senderParty = MessagingUtils.CreateSenderParty(activity);

                    if (_routingDataManager.IsAssociatedWithAggregation(senderParty))
                    {
                        // The party is associated with the aggregation and has the right to accept/reject
                        Party senderInConversation =
                            _routingDataManager.FindEngagedPartyByChannel(senderParty.ChannelId, senderParty.ChannelAccount);
                        replyActivity = await GetEngagementActivity(activity, replyActivity, message, doAccept, senderParty, senderInConversation);

                        wasHandled = true;
                    }
                    break;
                }

                case string s when(s.StartsWith(Commands.CommandListOptions)):
                {
                    replyActivity = await GetAvaialableAgentOptionsCard(activity);

                    wasHandled = true;
                    break;
                }

                    #region Agent debugging commands

                // TODO: Remove from production
                case string s when(s.StartsWith(Commands.CommandDeleteAllRoutingData)):
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
                    await connector.Conversations.ReplyToActivityAsync(activity.CreateReply("Deleting all data..."));

                    _routingDataManager.DeleteAll();
                    wasHandled = true;
                    break;
                }

                case string s when(s.StartsWith(Commands.CommandListAllParties)):
                {
                    string replyMessage = string.Empty;
                    string parties      = string.Empty;

                    foreach (Party userParty in _routingDataManager.GetUserParties())
                    {
                        parties += userParty.ToString() + "\n";
                    }

                    if (string.IsNullOrEmpty(parties))
                    {
                        replyMessage = "No user parties;\n";
                    }
                    else
                    {
                        replyMessage = "Users:\n" + parties;
                    }

                    parties = string.Empty;

                    foreach (Party botParty in _routingDataManager.GetBotParties())
                    {
                        parties += botParty.ToString() + "\n";
                    }

                    if (string.IsNullOrEmpty(parties))
                    {
                        replyMessage += "No bot parties";
                    }
                    else
                    {
                        replyMessage += "Bot:\n" + parties;
                    }

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

                case string s when(s.StartsWith(Commands.CommandListPendingRequests)):
                {
                    var attachments = new List <Attachment>();

                    foreach (Party party in _routingDataManager.GetPendingRequests())
                    {
                        attachments.Add(MessagingUtils.GetAgentRequestHeroCard(party.ChannelAccount.Name, party.ChannelId, party.ChannelAccount.Id, party));
                    }

                    replyActivity = activity.CreateReply("No pending requests");
                    if (attachments.Count > 0)
                    {
                        replyActivity.Text             = $"{attachments.Count} Pending requests found:";
                        replyActivity.AttachmentLayout = AttachmentLayoutTypes.Carousel;
                        replyActivity.Attachments      = attachments;
                    }
                    wasHandled = true;
                    break;
                }

                case string s when(s.StartsWith(Commands.CommandListEngagements)):
                {
                    string parties = _routingDataManager.EngagementsAsString();

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

                    wasHandled = true;
                    break;
                }

#if DEBUG
                case string s when(s.StartsWith(Commands.CommandListLastMessageRouterResults)):
                {
                    LocalRoutingDataManager routingDataManager =
                        (MessageRouterManager.Instance.RoutingDataManager as LocalRoutingDataManager);

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

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

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

            return(wasHandled);
        }