/// <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> /// 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); }
/// <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> /// 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); }