/// <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> /// Handles the received message. /// </summary> public async Task <HttpResponseMessage> Post([FromBody] Activity activity) { if (activity.Locale != null) { ConversationText.Culture = new CultureInfo(activity.Locale); } if (activity.Type == ActivityTypes.Message) { MessageRouterManager messageRouterManager = WebApiConfig.MessageRouterManager; IMessageRouterResultHandler messageRouterResultHandler = WebApiConfig.MessageRouterResultHandler; messageRouterManager.MakeSurePartiesAreTracked(activity); // First check for commands (both from back channel and the ones directly typed) MessageRouterResult messageRouterResult = WebApiConfig.BackChannelMessageHandler.HandleBackChannelMessage(activity); if (messageRouterResult.Type != MessageRouterResultType.Connected && await WebApiConfig.CommandMessageHandler.HandleCommandAsync(activity) == false) { // No valid back channel (command) message or typed command detected // Let the message router manager instance handle the activity messageRouterResult = await messageRouterManager.HandleActivityAsync(activity, false); if (messageRouterResult.Type == MessageRouterResultType.NoActionTaken) { // No action was taken by the message router manager. This means that the // user is not connected (in a 1:1 conversation) with a human // (e.g. customer service agent) yet. // // You can, for example, check if the user (customer) needs human // assistance here or forward the activity to a dialog. You could also do // the check in the dialog too... // // Here's an example: if (!string.IsNullOrEmpty(activity.Text) && activity.Text.ToLower().Contains(CommandRequestConnection)) { messageRouterResult = messageRouterManager.RequestConnection(activity); } else { await Conversation.SendAsync(activity, () => new RootDialog()); } } } // Handle the result, if required await messageRouterResultHandler.HandleResultAsync(messageRouterResult); } else { await HandleSystemMessageAsync(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return(response); }
/// <summary> /// Handles the received message. /// </summary> public async Task <HttpResponseMessage> Post([FromBody] Activity activity) { //await Repository.UtilityRepo.LogMsgAsync("activity id" + activity.From.Id); //await Repository.UtilityRepo.LogMsgAsync("activity from" + activity.From.Name); //await Repository.UtilityRepo.LogMsgAsync("activity channel" + activity.ChannelId); // var t = System.Web.HttpContext.Current.Request.UserHostAddress; //var CallerIp = System.Web.HttpContext.Current.Request.UserHostAddress; var CallerAgent = System.Web.HttpContext.Current.Request.UserAgent; //var CalledUrl = System.Web.HttpContext.Current.Request.Url.OriginalString; var current = System.Web.HttpContext.Current; var ip = GetUserIP(current); await Repository.UtilityRepo.UpdatedUserAttendedByAsync(activity); if (activity.Locale != null) { ConversationText.Culture = new CultureInfo(activity.Locale); } if (activity.Type == ActivityTypes.Message) { MessageRouterManager messageRouterManager = WebApiConfig.MessageRouterManager; IMessageRouterResultHandler messageRouterResultHandler = WebApiConfig.MessageRouterResultHandler; messageRouterManager.MakeSurePartiesAreTracked(activity); // First check for commands (both from back channel and the ones directly typed) MessageRouterResult messageRouterResult = WebApiConfig.BackChannelMessageHandler.HandleBackChannelMessage(activity); if (messageRouterResult.Type != MessageRouterResultType.Connected && await WebApiConfig.CommandMessageHandler.HandleCommandAsync(activity) == false) { // No valid back channel (command) message or typed command detected // Let the message router manager instance handle the activity messageRouterResult = await messageRouterManager.HandleActivityAsync(activity, false); if (messageRouterResult.Type == MessageRouterResultType.NoActionTaken) { // No action was taken by the message router manager. This means that the // user is not connected (in a 1:1 conversation) with a human // (e.g. customer service agent) yet. // // You can, for example, check if the user (customer) needs human // assistance here or forward the activity to a dialog. You could also do // the check in the dialog too... // // Here's an example: if (!string.IsNullOrEmpty(activity.Text) && activity.Text.ToLower().Contains(CommandRequestConnection)) //&& System.Web.HttpContext.Current.Session["UserID"] != null) { messageRouterResult = messageRouterManager.RequestConnection(activity); // log all the request and thier sources try { await Repository.UtilityRepo.LogPendingRequestAsync(activity, ip, CallerAgent); } catch (System.Data.Entity.Validation.DbEntityValidationException e) { foreach (var eve in e.EntityValidationErrors) { await Repository.UtilityRepo.LogMsgAsync(string.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", eve.Entry.Entity.GetType().Name, eve.Entry.State)); foreach (var ve in eve.ValidationErrors) { await Repository.UtilityRepo.LogMsgAsync(string.Format("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage)); } } //throw; } catch (Exception ex) { await Repository.UtilityRepo.LogMsgAsync("Eror on human request : " + ex.Message); } } else { try { await Repository.UtilityRepo.LogRequestMessageAsync(activity); // Call await Conversation.SendAsync(activity, () => new RootDialog()); } catch (FormCanceledException fcEx) when(fcEx.InnerException is TooManyAttemptsException) { ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl)); Activity reply = activity.CreateReply( $"Too Many Attempts at {fcEx.Last}. " + $"Completed Steps: {string.Join(", ", fcEx.Completed)}"); await Repository.UtilityRepo.LogMsgAsync("reply : " + reply.Text); await connector.Conversations.ReplyToActivityAsync(reply); } catch (FormCanceledException fcEx) { ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl)); Activity reply = activity.CreateReply( $"Form cancelled at {fcEx.Last}. " + $"Completed Steps: {string.Join(", ", fcEx.Completed)}"); await Repository.UtilityRepo.LogMsgAsync("reply : " + reply.Text); await connector.Conversations.ReplyToActivityAsync(reply); } catch (Exception ex) { await Repository.UtilityRepo.LogErrorAsync(ex); } } } } if (messageRouterResult != null && messageRouterResult.Type == MessageRouterResultType.OK && string.IsNullOrEmpty(messageRouterResult.ErrorMessage)) { await Repository.UtilityRepo.CustomerAgentChatHistoryLogAsync(messageRouterResult); } // Handle the result, if required await messageRouterResultHandler.HandleResultAsync(messageRouterResult); } else { await HandleSystemMessageAsync(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return(response); }