From c5d83fbd240f6e82b79de8f3bd975aa03a2a41ca Mon Sep 17 00:00:00 2001 From: Bo Yin Date: Fri, 6 Sep 2024 10:14:10 -0500 Subject: [PATCH 1/2] support dialCSR --- .../Controllers/TwilioVoiceController.cs | 16 +++++++++------- .../Models/AssistantMessage.cs | 1 + .../Services/TwilioMessageQueueService.cs | 1 + .../Services/TwilioService.cs | 11 +++++++++++ .../Settings/TwilioSetting.cs | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index c820e40c..4e37ac78 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -1,11 +1,9 @@ -using Azure.Messaging; using BotSharp.Abstraction.Files; using BotSharp.Core.Infrastructures; using BotSharp.Plugin.Twilio.Models; using BotSharp.Plugin.Twilio.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using System.Security.Cryptography; using Twilio.Http; namespace BotSharp.Plugin.Twilio.Controllers; @@ -109,14 +107,14 @@ public class TwilioVoiceController : TwilioController else { response = twilio.ReturnInstructions(null, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}&attempts={++attempts}", true); - } + } } return TwiML(response); } [ValidateRequest] [HttpPost("twilio/voice/{conversationId}/reply/{seqNum}")] - public async Task ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, + public async Task ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request) { var nextSeqNum = seqNum + 1; @@ -164,16 +162,20 @@ public class TwilioVoiceController : TwilioController } else { - response = twilio.ReturnInstructions(new List + response = twilio.ReturnInstructions(new List { $"twilio/hold-on-{Random.Shared.Next(1, 6)}.mp3", - $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" + $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" }, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true); } } else { - if (reply.ConversationEnd) + if (reply.HumanIntervationNeeded) + { + response = twilio.DialCsrAgent($"twilio/voice/speeches/{conversationId}/{reply.SpeechFileName}"); + } + else if (reply.ConversationEnd) { response = twilio.HangUp($"twilio/voice/speeches/{conversationId}/{reply.SpeechFileName}"); } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs index d53a9b0b..521dba43 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs @@ -3,6 +3,7 @@ namespace BotSharp.Plugin.Twilio.Models public class AssistantMessage { public bool ConversationEnd { get; set; } + public bool HumanIntervationNeeded { get; set; } public string Content { get; set; } public string MessageId { get; set; } public string SpeechFileName { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index 39eac1cf..4679c653 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -86,6 +86,7 @@ namespace BotSharp.Plugin.Twilio.Services reply = new AssistantMessage() { ConversationEnd = msg.Instruction?.ConversationEnd ?? false, + HumanIntervationNeeded = string.Equals("human_intervention_needed", msg.FunctionName), Content = msg.Content, MessageId = msg.MessageId }; diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs index 285d3321..d72f549b 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs @@ -133,6 +133,17 @@ public class TwilioService return response; } + public VoiceResponse DialCsrAgent(string speechPath) + { + var response = new VoiceResponse(); + if (!string.IsNullOrEmpty(speechPath)) + { + response.Play(new Uri($"{_settings.CallbackHost}/{speechPath}")); + } + response.Dial(_settings.CsrAgentNumber); + return response; + } + public VoiceResponse HoldOn(int interval, string message = null) { var twilioSetting = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs b/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs index c7170ee2..bbf4bab3 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs @@ -10,4 +10,5 @@ public class TwilioSetting public string ApiSecret { get; set; } public string CallbackHost { get; set; } public string AgentId { get; set; } + public string CsrAgentNumber { get; set; } } From 2faed9f3cb708b264d233ba2e0b7bd360f98dc8e Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Sat, 7 Sep 2024 11:00:11 -0500 Subject: [PATCH 2/2] remove duplicate --- .../BotSharp.Abstraction/Models/KeyValue.cs | 5 +++++ .../Repositories/Filters/ConversationFilter.cs | 11 ----------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs index f981ba6e..81286489 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs @@ -7,4 +7,9 @@ public class KeyValue [JsonPropertyName("value")] public string Value { get; set; } + + public override string ToString() + { + return $"Key: {Key}, Value: {Value}"; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs index 48a19fa6..12864ac3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs @@ -23,15 +23,4 @@ public class ConversationFilter /// Check whether each key in the list is in the conversation states and its value equals to target value if not empty /// public IEnumerable States { get; set; } = new List(); -} - -public class KeyValue -{ - public string Key { get; set; } - public string? Value { get; set; } - - public override string ToString() - { - return $"Key: {Key}, Value: {Value}"; - } } \ No newline at end of file