/// <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> /// Checks the given activity for a possible command. /// </summary> /// <param name="activity">The context containing the activity, which in turn may contain a possible command.</param> /// <returns>True, if a command was detected and handled. False otherwise.</returns> public async virtual Task <bool> HandleCommandAsync(ITurnContext context) { Activity activity = context.Activity; Command command = Command.FromMessageActivity(activity); if (command == null) { // Check for back channel command command = Command.FromChannelData(activity); } if (command == null) { return(false); } bool wasHandled = false; Activity replyActivity = null; ConversationReference sender = MessageRouter.CreateSenderConversationReference(activity); switch (command.BaseCommand) { case Commands.ShowOptions: // Present all command options in a card replyActivity = CommandCardFactory.AddCardToActivity( activity.CreateReply(), CommandCardFactory.CreateCommandOptionsCard(activity.Recipient?.Name)); wasHandled = true; break; case Commands.Watch: // Add the sender's channel/conversation into the list of aggregation channels bool isPermittedAggregationChannel = false; if (_permittedAggregationChannels != null && _permittedAggregationChannels.Count > 0) { foreach (string permittedAggregationChannel in _permittedAggregationChannels) { if (!string.IsNullOrWhiteSpace(activity.ChannelId) && activity.ChannelId.ToLower().Equals(permittedAggregationChannel.ToLower())) { isPermittedAggregationChannel = true; break; } } } else { isPermittedAggregationChannel = true; } if (isPermittedAggregationChannel) { ConversationReference aggregationChannelToAdd = new ConversationReference( null, null, null, activity.Conversation, activity.ChannelId, activity.ServiceUrl); ModifyRoutingDataResult modifyRoutingDataResult = _messageRouter.RoutingDataManager.AddAggregationChannel(aggregationChannelToAdd); if (modifyRoutingDataResult.Type == ModifyRoutingDataResultType.Added) { replyActivity = activity.CreateReply(Strings.AggregationChannelSet); } else if (modifyRoutingDataResult.Type == ModifyRoutingDataResultType.AlreadyExists) { replyActivity = activity.CreateReply(Strings.AggregationChannelAlreadySet); } else if (modifyRoutingDataResult.Type == ModifyRoutingDataResultType.Error) { replyActivity = activity.CreateReply( string.Format(Strings.FailedToSetAggregationChannel, modifyRoutingDataResult.ErrorMessage)); } } else { replyActivity = activity.CreateReply( string.Format(Strings.NotPermittedAggregationChannel, activity.ChannelId)); } wasHandled = true; break; case Commands.Unwatch: // Remove the sender's channel/conversation from the list of aggregation channels if (_messageRouter.RoutingDataManager.IsAssociatedWithAggregation(sender)) { ConversationReference aggregationChannelToRemove = new ConversationReference( null, null, null, activity.Conversation, activity.ChannelId, activity.ServiceUrl); if (_messageRouter.RoutingDataManager.RemoveAggregationChannel(aggregationChannelToRemove)) { replyActivity = activity.CreateReply(Strings.AggregationChannelRemoved); } else { replyActivity = activity.CreateReply(Strings.FailedToRemoveAggregationChannel); } wasHandled = true; } break; case Commands.GetRequests: IList <ConnectionRequest> connectionRequests = _messageRouter.RoutingDataManager.GetConnectionRequests(); replyActivity = activity.CreateReply(); if (connectionRequests.Count == 0) { replyActivity.Text = Strings.NoPendingRequests; } else { replyActivity.Attachments = CommandCardFactory.CreateMultipleConnectionRequestCards( connectionRequests, activity.Recipient?.Name); } replyActivity.ChannelData = JsonConvert.SerializeObject(connectionRequests); wasHandled = true; break; case Commands.AcceptRequest: case Commands.RejectRequest: // Accept/reject connection request bool doAccept = (command.BaseCommand == Commands.AcceptRequest); if (_messageRouter.RoutingDataManager.IsAssociatedWithAggregation(sender)) { // The sender is associated with the aggregation and has the right to accept/reject if (command.Parameters.Count == 0) { replyActivity = activity.CreateReply(); connectionRequests = _messageRouter.RoutingDataManager.GetConnectionRequests(); if (connectionRequests.Count == 0) { replyActivity.Text = Strings.NoPendingRequests; } else { replyActivity = CommandCardFactory.AddCardToActivity( replyActivity, CommandCardFactory.CreateMultiConnectionRequestCard( connectionRequests, doAccept, activity.Recipient?.Name)); } } else if (!doAccept && command.Parameters[0].Equals(Command.CommandParameterAll)) { // Reject all pending connection requests if (!await _connectionRequestHandler.RejectAllPendingRequestsAsync( _messageRouter, _messageRouterResultHandler)) { replyActivity = activity.CreateReply(); replyActivity.Text = Strings.FailedToRejectPendingRequests; } } else if (command.Parameters.Count > 1) { // Try to accept/reject the specified connection request ChannelAccount requestorChannelAccount = new ChannelAccount(command.Parameters[0]); ConversationAccount requestorConversationAccount = new ConversationAccount(null, null, command.Parameters[1]); AbstractMessageRouterResult messageRouterResult = await _connectionRequestHandler.AcceptOrRejectRequestAsync( _messageRouter, _messageRouterResultHandler, sender, doAccept, requestorChannelAccount, requestorConversationAccount); await _messageRouterResultHandler.HandleResultAsync(messageRouterResult); } else { replyActivity = activity.CreateReply(Strings.InvalidOrMissingCommandParameter); } } #if DEBUG // We shouldn't respond to command attempts by regular users, but I guess // it's okay when debugging else { replyActivity = activity.CreateReply(Strings.ConnectionRequestResponseNotAllowed); } #endif wasHandled = true; break; case Commands.Disconnect: // End the 1:1 conversation(s) IList <ConnectionResult> disconnectResults = _messageRouter.Disconnect(sender); if (disconnectResults != null && disconnectResults.Count > 0) { foreach (ConnectionResult disconnectResult in disconnectResults) { await _messageRouterResultHandler.HandleResultAsync(disconnectResult); } wasHandled = true; } break; //default: // replyActivity = activity.CreateReply(string.Format(Strings.CommandNotRecognized, command.BaseCommand)); // break; } if (replyActivity != null) { await context.SendActivityAsync(replyActivity); } return(wasHandled); }