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 diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index 4f085605..733dcd43 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; @@ -166,7 +164,7 @@ public class TwilioVoiceController : TwilioController } else { - response = twilio.ReturnInstructions(new List + response = twilio.ReturnInstructions(new List { $"twilio/hold-on-long-{Random.Shared.Next(1, 9)}.mp3", $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" @@ -175,7 +173,11 @@ public class TwilioVoiceController : TwilioController } 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 4af517f5..fa164d99 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; } }