diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/CallAutomationService.cs b/Call_Automation_GCCH/Call_Automation_GCCH/CallAutomationService.cs index 14207dcf..c8f40cf1 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/CallAutomationService.cs +++ b/Call_Automation_GCCH/Call_Automation_GCCH/CallAutomationService.cs @@ -1,174 +1,136 @@ using Azure.Communication.CallAutomation; -using Call_Automation_GCCH.Controllers; -using Microsoft.Extensions.Logging; namespace Call_Automation_GCCH.Services { - public class CallAutomationService - { - private CallAutomationClient _client; - private ILogger _logger; - private static string? _recordingLocation; - private static string _recordingFileFormat = "mp4"; - private string _currentPmaEndpoint = string.Empty; - - public CallAutomationService(string connectionString, string pmaEndpoint, ILogger logger) + public class CallAutomationService { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _currentPmaEndpoint = pmaEndpoint; - - if (!string.IsNullOrEmpty(pmaEndpoint)) - { - _client = new CallAutomationClient(pmaEndpoint: new Uri(pmaEndpoint), connectionString: connectionString); - } - else - { - _logger.LogWarning("PmaEndpoint is empty. Creating CallAutomationClient without PmaEndpoint parameter."); - _client = new CallAutomationClient(connectionString: connectionString); - } - } - - /// - /// Gets the call automation client for direct operations - /// - /// CallAutomationClient instance - public CallAutomationClient GetCallAutomationClient() - { - return _client; - } - - /// - /// Gets the recording location - /// - /// Recording location string - public static string GetRecordingLocation() - { - return _recordingLocation; - } - - /// - /// Sets the recording location - /// - /// The recording location to set - public static void SetRecordingLocation(string location) - { - _recordingLocation = location; - } - - /// - /// Gets the recording file format - /// - /// Recording file format string - public static string GetRecordingFileFormat() - { - return _recordingFileFormat; - } - - /// - /// Sets the recording file format - /// - /// The recording file format to set - public static void SetRecordingFileFormat(string format) - { - _recordingFileFormat = format; - } - - public CallConnection GetCallConnection(string callConnectionId) - { - try - { - return _client.GetCallConnection(callConnectionId); - } - catch (Exception ex) - { - string errorMessage = $"Error in GetCallConnection: {ex.Message}. CallConnectionId: {callConnectionId}"; - _logger.LogError(errorMessage); - throw; - } - } + private CallAutomationClient _client; + private ILogger _logger; + private static string? _recordingLocation; + private static string _recordingFileFormat = "mp4"; + private string _currentPmaEndpoint = string.Empty; + + public CallAutomationService(string connectionString, ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _client = new CallAutomationClient(connectionString: connectionString); + } + + /// + /// Gets the call automation client for direct operations + /// + /// CallAutomationClient instance + public CallAutomationClient GetCallAutomationClient() + { + return _client; + } + + /// + /// Gets the recording location + /// + /// Recording location string + public static string GetRecordingLocation() + { + return _recordingLocation; + } + + /// + /// Sets the recording location + /// + /// The recording location to set + public static void SetRecordingLocation(string location) + { + _recordingLocation = location; + } + + /// + /// Gets the recording file format + /// + /// Recording file format string + public static string GetRecordingFileFormat() + { + return _recordingFileFormat; + } + + /// + /// Sets the recording file format + /// + /// The recording file format to set + public static void SetRecordingFileFormat(string format) + { + _recordingFileFormat = format; + } + + public CallConnection GetCallConnection(string callConnectionId) + { + try + { + return _client.GetCallConnection(callConnectionId); + } + catch (Exception ex) + { + string errorMessage = $"Error in GetCallConnection: {ex.Message}. CallConnectionId: {callConnectionId}"; + _logger.LogError(errorMessage); + throw; + } + } + + public CallMedia GetCallMedia(string callConnectionId) + { + try + { + return _client.GetCallConnection(callConnectionId).GetCallMedia(); + } + catch (Exception ex) + { + string errorMessage = $"Error in GetCallMedia: {ex.Message}. CallConnectionId: {callConnectionId}"; + _logger.LogError(errorMessage); + throw; // Rethrow so the caller can handle or return an error response + } + } + + public CallConnectionProperties GetCallConnectionProperties(string callConnectionId) + { + try + { + return _client.GetCallConnection(callConnectionId).GetCallConnectionProperties(); + } + catch (Exception ex) + { + string errorMessage = $"Error in GetCallConnectionProperties: {ex.Message}. CallConnectionId: {callConnectionId}"; + _logger.LogError(errorMessage); + throw; + } + } + + ///// + ///// Updates the CallAutomationClient with new connection settings + ///// + ///// The ACS connection string + ///// The PMA endpoint to use + //public void UpdateClient(string connectionString, string pmaEndpoint) + //{ + // _logger = _logger ?? throw new InvalidOperationException("Logger is not initialized"); + // _currentPmaEndpoint = pmaEndpoint; + + // if (!string.IsNullOrEmpty(pmaEndpoint)) + // { + // _client = new CallAutomationClient(pmaEndpoint: new Uri(pmaEndpoint), connectionString: connectionString); + // _logger.LogInformation($"CallAutomationClient recreated with PMA endpoint: {pmaEndpoint}"); + // } + // else + // { + // _logger.LogWarning("PmaEndpoint is empty. Creating CallAutomationClient without PmaEndpoint parameter."); + // _client = new CallAutomationClient(connectionString: connectionString); + // } + //} + + //public string GetCurrentPmaEndpoint() + //{ + // return _currentPmaEndpoint; + //} - public CallMedia GetCallMedia(string callConnectionId) - { - try - { - return _client.GetCallConnection(callConnectionId).GetCallMedia(); - } - catch (Exception ex) - { - string errorMessage = $"Error in GetCallMedia: {ex.Message}. CallConnectionId: {callConnectionId}"; - _logger.LogError(errorMessage); - throw; // Rethrow so the caller can handle or return an error response - } - } - - public CallConnectionProperties GetCallConnectionProperties(string callConnectionId) - { - try - { - return _client.GetCallConnection(callConnectionId).GetCallConnectionProperties(); - } - catch (Exception ex) - { - string errorMessage = $"Error in GetCallConnectionProperties: {ex.Message}. CallConnectionId: {callConnectionId}"; - _logger.LogError(errorMessage); - throw; - } } - - /// - /// Updates the CallAutomationClient with new connection settings - /// - /// The ACS connection string - /// The PMA endpoint to use - public void UpdateClient(string connectionString, string pmaEndpoint) - { - _logger = _logger ?? throw new InvalidOperationException("Logger is not initialized"); - _currentPmaEndpoint = pmaEndpoint; - - if (!string.IsNullOrEmpty(pmaEndpoint)) - { - _client = new CallAutomationClient(pmaEndpoint: new Uri(pmaEndpoint), connectionString: connectionString); - _logger.LogInformation($"CallAutomationClient recreated with PMA endpoint: {pmaEndpoint}"); - } - else - { - _logger.LogWarning("PmaEndpoint is empty. Creating CallAutomationClient without PmaEndpoint parameter."); - _client = new CallAutomationClient(connectionString: connectionString); - } - } - - public string GetCurrentPmaEndpoint() - { - return _currentPmaEndpoint; - } - - //Need Azure Cognitive services for this so in phase 2 - //public List GetChoices() - //{ - // return new List { - // new RecognitionChoice("Confirm", new List { - // "Confirm", - // "First", - // "One" - // }) { - // Tone = DtmfTone.One - // }, - // new RecognitionChoice("Cancel", new List { - // "Cancel", - // "Second", - // "Two" - // }) { - // Tone = DtmfTone.Two - // } - //}; - //public List GetChoices() => new List - // { - // // Only DTMF tones, no speech phrases - // new RecognitionChoice("Confirm", new List()) { Tone = DtmfTone.One }, - // new RecognitionChoice("Cancel", new List()) { Tone = DtmfTone.Two } - // }; - - } } diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/Call_Automation_GCCH.csproj b/Call_Automation_GCCH/Call_Automation_GCCH/Call_Automation_GCCH.csproj index 0bad6421..6a957652 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/Call_Automation_GCCH.csproj +++ b/Call_Automation_GCCH/Call_Automation_GCCH/Call_Automation_GCCH.csproj @@ -7,7 +7,7 @@ - + diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/CallAutomationEventsController.cs b/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/CallAutomationEventsController.cs index 8dc26f2c..000e1ba5 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/CallAutomationEventsController.cs +++ b/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/CallAutomationEventsController.cs @@ -1,16 +1,10 @@ -using System; -using System.Threading.Tasks; -using Azure.Communication; -using Azure.Communication.CallAutomation; +using Azure.Communication.CallAutomation; using Azure.Messaging; using Azure.Messaging.EventGrid; using Azure.Messaging.EventGrid.SystemEvents; using Call_Automation_GCCH.Models; using Call_Automation_GCCH.Services; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Call_Automation_GCCH.Controllers @@ -177,68 +171,68 @@ public IActionResult HandleCallbacks([FromBody] CloudEvent[] cloudEvents) return Problem($"Error processing callbacks: {ex.Message}"); } } - /// - /// Updates the IsArizona configuration and switches PMA endpoint accordingly - /// - /// Boolean flag to determine which PMA endpoint to use - /// Action result indicating success or error - [HttpPost("/setRegion")] - [Tags("Region Configuration")] - public IActionResult SetRegion(bool isArizona) - { - try - { - _logger.LogInformation($"Changing region configuration. IsArizona: {isArizona}"); - - // Get the current configuration section - var configSection = HttpContext.RequestServices.GetRequiredService().GetSection("CommunicationSettings"); - - // Get the current endpoint being used to determine if an update is needed - string currentEndpoint = _service.GetCurrentPmaEndpoint() ?? string.Empty; - string newEndpoint = isArizona - ? configSection["PmaEndpointArizona"] ?? string.Empty - : configSection["PmaEndpointTexas"] ?? string.Empty; - - // Check if new endpoint is empty - if (string.IsNullOrEmpty(newEndpoint)) - { - _logger.LogWarning($"The {(isArizona ? "PmaEndpointArizona" : "PmaEndpointTexas")} setting is empty"); - } - - // Only update if the endpoint would actually change - if (currentEndpoint == newEndpoint) - { - if (string.IsNullOrEmpty(currentEndpoint)) - { - return Ok($"Configuration unchanged as the endpoints are empty"); - } - else - { - return Ok($"Configuration unchanged. Already using {(isArizona ? "Arizona" : "Texas")} region."); - } - } - - // Update the IsArizona setting in memory - ((IConfigurationSection)configSection.GetSection("IsArizona")).Value = isArizona.ToString(); - - // Update the client with the new endpoint - var connectionString = configSection["AcsConnectionString"] ?? string.Empty; - if (string.IsNullOrEmpty(connectionString)) - { - _logger.LogError("AcsConnectionString is empty"); - return Problem("AcsConnectionString is empty"); - } - - _service.UpdateClient(connectionString, newEndpoint); - - return Ok($"Region updated successfully to {(isArizona ? "Arizona" : "Texas")}."); - } - catch (Exception ex) - { - _logger.LogError($"Error updating region configuration: {ex.Message}"); - return Problem($"Failed to update region configuration: {ex.Message}"); - } - } + ///// + ///// Updates the IsArizona configuration and switches PMA endpoint accordingly + ///// + ///// Boolean flag to determine which PMA endpoint to use + ///// Action result indicating success or error + //[HttpPost("/setRegion")] + //[Tags("Region Configuration")] + //public IActionResult SetRegion(bool isArizona) + //{ + // try + // { + // _logger.LogInformation($"Changing region configuration. IsArizona: {isArizona}"); + + // // Get the current configuration section + // var configSection = HttpContext.RequestServices.GetRequiredService().GetSection("CommunicationSettings"); + + // // Get the current endpoint being used to determine if an update is needed + // string currentEndpoint = _service.GetCurrentPmaEndpoint() ?? string.Empty; + // string newEndpoint = isArizona + // ? configSection["PmaEndpointArizona"] ?? string.Empty + // : configSection["PmaEndpointTexas"] ?? string.Empty; + + // // Check if new endpoint is empty + // if (string.IsNullOrEmpty(newEndpoint)) + // { + // _logger.LogWarning($"The {(isArizona ? "PmaEndpointArizona" : "PmaEndpointTexas")} setting is empty"); + // } + + // // Only update if the endpoint would actually change + // if (currentEndpoint == newEndpoint) + // { + // if (string.IsNullOrEmpty(currentEndpoint)) + // { + // return Ok($"Configuration unchanged as the endpoints are empty"); + // } + // else + // { + // return Ok($"Configuration unchanged. Already using {(isArizona ? "Arizona" : "Texas")} region."); + // } + // } + + // // Update the IsArizona setting in memory + // ((IConfigurationSection)configSection.GetSection("IsArizona")).Value = isArizona.ToString(); + + // // Update the client with the new endpoint + // var connectionString = configSection["AcsConnectionString"] ?? string.Empty; + // if (string.IsNullOrEmpty(connectionString)) + // { + // _logger.LogError("AcsConnectionString is empty"); + // return Problem("AcsConnectionString is empty"); + // } + + // _service.UpdateClient(connectionString, newEndpoint); + + // return Ok($"Region updated successfully to {(isArizona ? "Arizona" : "Texas")}."); + // } + // catch (Exception ex) + // { + // _logger.LogError($"Error updating region configuration: {ex.Message}"); + // return Problem($"Failed to update region configuration: {ex.Message}"); + // } + //} /// /// Processes individual call automation events /// @@ -378,22 +372,6 @@ private void ProcessCallEvent(CallAutomationEventBase parsedEvent) _logger.LogInformation($"Received call event: {continuousDtmfRecognitionToneFailed.GetType()}, CallConnectionId: {continuousDtmfRecognitionToneFailed.CallConnectionId}, CorrelationId: {continuousDtmfRecognitionToneFailed.CorrelationId}, " + $"subCode: {continuousDtmfRecognitionToneFailed.ResultInformation?.SubCode}, message: {continuousDtmfRecognitionToneFailed.ResultInformation?.Message}, context: {continuousDtmfRecognitionToneFailed.OperationContext}"); } - else if (parsedEvent is HoldAudioStarted holdAudioStarted) - { - _logger.LogInformation($"Received call event: {holdAudioStarted.GetType()}, CallConnectionId: {holdAudioStarted.CallConnectionId}"); - } - else if (parsedEvent is HoldAudioPaused holdAudioPaused) - { - _logger.LogInformation($"Received call event: {holdAudioPaused.GetType()}, CallConnectionId: {holdAudioPaused.CallConnectionId}"); - } - else if (parsedEvent is HoldAudioResumed holdAudioResumed) - { - _logger.LogInformation($"Received call event: {holdAudioResumed.GetType()}, CallConnectionId: {holdAudioResumed.CallConnectionId}"); - } - else if (parsedEvent is HoldAudioCompleted holdAudioCompleted) - { - _logger.LogInformation($"Received call event: {holdAudioCompleted.GetType()}, CallConnectionId: {holdAudioCompleted.CallConnectionId}"); - } else if (parsedEvent is HoldFailed holdFailed) { _logger.LogInformation($"Received call event: {holdFailed.GetType()}, CallConnectionId: {holdFailed.CallConnectionId}, CorrelationId: {holdFailed.CorrelationId}, " + @@ -483,10 +461,10 @@ public async Task HandleIncomingCallWithOptions( [FromQuery] bool mediaStreaming = true, [FromQuery] bool bidirectionalStreaming = true) { - MediaStreamingAudioChannel audioChannel = audioChannelMixed - ? MediaStreamingAudioChannel.Mixed + MediaStreamingAudioChannel audioChannel = audioChannelMixed + ? MediaStreamingAudioChannel.Mixed : MediaStreamingAudioChannel.Unmixed; - + bool isPcm24kHz = !audioFormat16k; return await HandleIncomingCallWithMediaStreaming( @@ -537,20 +515,18 @@ private async Task HandleIncomingCallWithMediaStreaming( var callbackUri = new Uri(new Uri(_config.CallbackUriHost), $"/api/callbacks"); var websocketUri = new Uri(_config.CallbackUriHost.Replace("https", "wss") + "/ws"); - + _logger.LogInformation($"Incoming call with media streaming - correlationId: {incomingCallEventData.CorrelationId}, " + $"AudioChannel: {audioChannel}, EnableMediaStreaming: {enableMediaStreaming}, " + $"IsPcm24kHz: {isPcm24kHz}, EnableBidirectional: {enableBidirectional}"); - MediaStreamingOptions mediaStreamingOptions = new MediaStreamingOptions( - websocketUri, - MediaStreamingContent.Audio, - audioChannel, - MediaStreamingTransport.Websocket, - enableMediaStreaming) + MediaStreamingOptions mediaStreamingOptions = new MediaStreamingOptions(audioChannel, StreamingTransport.Websocket) { + TransportUri = websocketUri, + MediaStreamingContent = MediaStreamingContent.Audio, EnableBidirectional = enableBidirectional, - AudioFormat = isPcm24kHz ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono + AudioFormat = isPcm24kHz ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono, + StartMediaStreaming = enableMediaStreaming }; var options = new AnswerCallOptions(incomingCallEventData.IncomingCallContext, callbackUri) @@ -621,47 +597,3 @@ private async Task HandleIncomingCallWithMediaStreaming( #endregion } } - - - -//app.MapPost("/setConfigurations", (ConfigurationRequest configurationRequest, CallAutomationService service, ILogger logger) => -//{ -// try -// { -// logger.LogInformation("Setting configurations..."); - -// acsConnectionString = string.Empty; -// acsPhoneNumber = string.Empty; -// callbackUriHost = string.Empty; -// // fileSourceUri = string.Empty; - -// if (configurationRequest != null) -// { -// configuration.AcsConnectionString = !string.IsNullOrEmpty(configurationRequest.AcsConnectionString) -// ? configurationRequest.AcsConnectionString -// : throw new ArgumentNullException(nameof(configurationRequest.AcsConnectionString)); - -// configuration.pmaEndpoint = !string.IsNullOrEmpty(configurationRequest.pmaEndpoint) ? configurationRequest.pmaEndpoint : throw new ArgumentNullException(nameof(configurationRequest.pmaEndpoint)); -// //configuration.CongnitiveServiceEndpoint = !string.IsNullOrEmpty(configurationRequest.CongnitiveServiceEndpoint) ? configurationRequest.CongnitiveServiceEndpoint : throw new ArgumentNullException(nameof(configurationRequest.CongnitiveServiceEndpoint)); -// configuration.AcsPhoneNumber = !string.IsNullOrEmpty(configurationRequest.AcsPhoneNumber) ? configurationRequest.AcsPhoneNumber : throw new ArgumentNullException(nameof(configurationRequest.AcsPhoneNumber)); -// configuration.CallbackUriHost = !string.IsNullOrEmpty(configurationRequest.CallbackUriHost) ? configurationRequest.CallbackUriHost : throw new ArgumentNullException(nameof(configurationRequest.CallbackUriHost)); -// service.SetConfiguration(configurationRequest); -// } - -// acsConnectionString = configuration.AcsConnectionString; -// acsPhoneNumber = configuration.AcsPhoneNumber; -// callbackUriHost = configuration.CallbackUriHost; -// // fileSourceUri = "https://sample-videos.com/audio/mp3/crowd-cheering.mp3"; - -// logger.LogInformation($"Configuration set: AcsPhoneNumber={acsPhoneNumber}, CallbackUriHost={callbackUriHost}"); - -// client = new CallAutomationClient(connectionString: acsConnectionString); -// logger.LogInformation("Initialized call automation client."); -// return Results.Ok("Configuration set successfully. Initialized call automation client."); -// } -// catch (Exception ex) -// { -// logger.LogError($"Error in setConfigurations: {ex.Message}"); -// return Results.Problem($"Failed to set configuration: {ex.Message}"); -// } -//}).WithTags("1. Add Connection string and configuration settings."); \ No newline at end of file diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/MediaController.cs b/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/MediaController.cs index 530dda27..e9d7d50f 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/MediaController.cs +++ b/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/MediaController.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Azure; +using Azure; using Azure.Communication; using Azure.Communication.CallAutomation; using Call_Automation_GCCH.Models; using Call_Automation_GCCH.Services; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Call_Automation_GCCH.Controllers @@ -41,14 +36,14 @@ public Task PlayFileSourceToTargetAsync( { if (string.IsNullOrEmpty(target)) return Task.FromResult(BadRequest("Target is required")); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandlePlayFileSource( callConnectionId, new List { identifier }, @@ -65,14 +60,14 @@ public IActionResult PlayFileSourceToTarget( { if (string.IsNullOrEmpty(target)) return BadRequest("Target is required"); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandlePlayFileSource( callConnectionId, new List { identifier }, @@ -96,30 +91,6 @@ public IActionResult PlayFileSourceToAll(string callConnectionId) public Task PlayFileSourceBargeInAsync(string callConnectionId) => HandlePlayFileSource(callConnectionId, targets: null, playToAll: true, bargeIn: true, async: true); - [HttpPost("/interruptHoldWithPlay")] - [Tags("Play FileSource Media")] - public IActionResult InterruptHoldWithPlay( - string callConnectionId, - string target) - { - if (string.IsNullOrEmpty(target)) - return BadRequest("Target is required"); - - if (!target.StartsWith("8:") && !target.StartsWith("+")) - return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - - CommunicationIdentifier identifier = target.StartsWith("8:") - ? new CommunicationUserIdentifier(target) - : new PhoneNumberIdentifier(target); - - return HandlePlayFileSource( - callConnectionId, - new List { identifier }, - playToAll: false, - bargeIn: true, - async: false).Result; - } - // ──────────── MEDIA STREAMING: CREATE CALL ───────────────────────────────── /// /// Creates a call with media streaming capabilities asynchronously @@ -140,12 +111,12 @@ public Task CreateCallWithMediaStreamingAsync( { if (string.IsNullOrEmpty(target)) return Task.FromResult(BadRequest("Target is required")); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); - + var audioChannel = isMixed ? MediaStreamingAudioChannel.Mixed : MediaStreamingAudioChannel.Unmixed; - + return HandleCreateCallWithMediaStreaming( target, audioChannel, @@ -174,12 +145,12 @@ public IActionResult CreateCallWithMediaStreaming( { if (string.IsNullOrEmpty(target)) return BadRequest("Target is required"); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - + var audioChannel = isMixed ? MediaStreamingAudioChannel.Mixed : MediaStreamingAudioChannel.Unmixed; - + return HandleCreateCallWithMediaStreaming( target, audioChannel, @@ -248,14 +219,14 @@ public Task RecognizeDTMFAsync(string callConnectionId, string ta { if (string.IsNullOrEmpty(target)) return Task.FromResult(BadRequest("Target is required")); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandleRecognize(callConnectionId, identifier, RecognizeType.Dtmf, async: true); } @@ -265,17 +236,119 @@ public IActionResult RecognizeDTMF(string callConnectionId, string target) { if (string.IsNullOrEmpty(target)) return BadRequest("Target is required"); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandleRecognize(callConnectionId, identifier, RecognizeType.Dtmf, async: false).Result; } + //[HttpPost("/recognizeChoiceAsync")] + //[Tags("Recognition")] + //public Task RecognizeChoiceAsync(string callConnectionId, string target) + //{ + // if (string.IsNullOrEmpty(target)) + // return Task.FromResult(BadRequest("Target is required")); + + // if (!target.StartsWith("8:") && !target.StartsWith("+")) + // return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); + + // CommunicationIdentifier identifier = target.StartsWith("8:") + // ? new CommunicationUserIdentifier(target) + // : new PhoneNumberIdentifier(target); + + // return HandleRecognize(callConnectionId, identifier, RecognizeType.Choice, async: true); + //} + + //[HttpPost("/recognizeChoice")] + //[Tags("Recognition")] + //public IActionResult RecognizeChoice(string callConnectionId, string target) + //{ + // if (string.IsNullOrEmpty(target)) + // return BadRequest("Target is required"); + + // if (!target.StartsWith("8:") && !target.StartsWith("+")) + // return BadRequest("PSTN number must include country code (e.g., +1 for US)"); + + // CommunicationIdentifier identifier = target.StartsWith("8:") + // ? new CommunicationUserIdentifier(target) + // : new PhoneNumberIdentifier(target); + + // return HandleRecognize(callConnectionId, identifier, RecognizeType.Choice, async: false).Result; + //} + + //[HttpPost("/recognizeSpeechAsync")] + //[Tags("Recognition")] + //public Task RecognizeSpeechAsync(string callConnectionId, string target) + //{ + // if (string.IsNullOrEmpty(target)) + // return Task.FromResult(BadRequest("Target is required")); + + // if (!target.StartsWith("8:") && !target.StartsWith("+")) + // return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); + + // CommunicationIdentifier identifier = target.StartsWith("8:") + // ? new CommunicationUserIdentifier(target) + // : new PhoneNumberIdentifier(target); + + // return HandleRecognize(callConnectionId, identifier, RecognizeType.Speech, async: true); + //} + + //[HttpPost("/recognizeSpeech")] + //[Tags("Recognition")] + //public IActionResult RecognizeSpeech(string callConnectionId, string target) + //{ + // if (string.IsNullOrEmpty(target)) + // return BadRequest("Target is required"); + + // if (!target.StartsWith("8:") && !target.StartsWith("+")) + // return BadRequest("PSTN number must include country code (e.g., +1 for US)"); + + // CommunicationIdentifier identifier = target.StartsWith("8:") + // ? new CommunicationUserIdentifier(target) + // : new PhoneNumberIdentifier(target); + + // return HandleRecognize(callConnectionId, identifier, RecognizeType.Speech, async: false).Result; + //} + + //[HttpPost("/recognizeSpeechOrDTMFAsync")] + //[Tags("Recognition")] + //public Task RecognizeSpeechOrDTMFAsync(string callConnectionId, string target) + //{ + // if (string.IsNullOrEmpty(target)) + // return Task.FromResult(BadRequest("Target is required")); + + // if (!target.StartsWith("8:") && !target.StartsWith("+")) + // return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); + + // CommunicationIdentifier identifier = target.StartsWith("8:") + // ? new CommunicationUserIdentifier(target) + // : new PhoneNumberIdentifier(target); + + // return HandleRecognize(callConnectionId, identifier, RecognizeType.SpeechOrDtmf, async: true); + //} + + //[HttpPost("/recognizeSpeechOrDTMF")] + //[Tags("Recognition")] + //public IActionResult RecognizeSpeechOrDTMF(string callConnectionId, string target) + //{ + // if (string.IsNullOrEmpty(target)) + // return BadRequest("Target is required"); + + // if (!target.StartsWith("8:") && !target.StartsWith("+")) + // return BadRequest("PSTN number must include country code (e.g., +1 for US)"); + + // CommunicationIdentifier identifier = target.StartsWith("8:") + // ? new CommunicationUserIdentifier(target) + // : new PhoneNumberIdentifier(target); + + // return HandleRecognize(callConnectionId, identifier, RecognizeType.SpeechOrDtmf, async: false).Result; + //} + // ──────────── HOLD / UNHOLD ───────────────────────────────────────────────── [HttpPost("/holdTargetAsync")] [Tags("Hold Management")] @@ -283,14 +356,14 @@ public Task HoldTargetAsync(string callConnectionId, string targe { if (string.IsNullOrEmpty(target)) return Task.FromResult(BadRequest("Target is required")); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandleHold(callConnectionId, identifier, isPlaySource, unhold: false, async: true); } @@ -300,14 +373,14 @@ public IActionResult HoldTarget(string callConnectionId, string target, bool isP { if (string.IsNullOrEmpty(target)) return BadRequest("Target is required"); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandleHold(callConnectionId, identifier, isPlaySource, unhold: false, async: false).Result; } @@ -317,14 +390,14 @@ public Task UnholdTargetAsync(string callConnectionId, string tar { if (string.IsNullOrEmpty(target)) return Task.FromResult(BadRequest("Target is required")); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); - - CommunicationIdentifier identifier = target.StartsWith("8:") + + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - + return HandleHold(callConnectionId, identifier, playSource: false, unhold: true, async: true); } @@ -334,50 +407,15 @@ public IActionResult UnholdTarget(string callConnectionId, string target) { if (string.IsNullOrEmpty(target)) return BadRequest("Target is required"); - + if (!target.StartsWith("8:") && !target.StartsWith("+")) return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - - CommunicationIdentifier identifier = target.StartsWith("8:") - ? new CommunicationUserIdentifier(target) - : new PhoneNumberIdentifier(target); - - return HandleHold(callConnectionId, identifier, playSource: false, unhold: true, async: false).Result; - } - // ────────── INTERRUPT AUDIO AND ANNOUNCE ─────────────────────────────────── - [HttpPost("/interruptAudioAndAnnounceAsync")] - [Tags("Audio Announcements")] - public Task InterruptAudioAndAnnounceAsync(string callConnectionId, string target) - { - if (string.IsNullOrEmpty(target)) - return Task.FromResult(BadRequest("Target is required")); - - if (!target.StartsWith("8:") && !target.StartsWith("+")) - return Task.FromResult(BadRequest("PSTN number must include country code (e.g., +1 for US)")); - - CommunicationIdentifier identifier = target.StartsWith("8:") + CommunicationIdentifier identifier = target.StartsWith("8:") ? new CommunicationUserIdentifier(target) : new PhoneNumberIdentifier(target); - - return HandleInterruptAudioAndAnnounce(callConnectionId, identifier, async: true); - } - [HttpPost("/interruptAudioAndAnnounce")] - [Tags("Audio Announcements")] - public IActionResult InterruptAudioAndAnnounce(string callConnectionId, string target) - { - if (string.IsNullOrEmpty(target)) - return BadRequest("Target is required"); - - if (!target.StartsWith("8:") && !target.StartsWith("+")) - return BadRequest("PSTN number must include country code (e.g., +1 for US)"); - - CommunicationIdentifier identifier = target.StartsWith("8:") - ? new CommunicationUserIdentifier(target) - : new PhoneNumberIdentifier(target); - - return HandleInterruptAudioAndAnnounce(callConnectionId, identifier, async: false).Result; + return HandleHold(callConnectionId, identifier, playSource: false, unhold: true, async: false).Result; } // ───────────── PRIVATE HANDLERS ────────────────────────────────────────── @@ -416,7 +454,6 @@ private async Task HandlePlayFileSource( var options = new PlayOptions(fileSource, targets) { OperationContext = "playToContext", - InterruptHoldAudio = bargeIn }; var response = async ? await callMedia.PlayAsync(options) : callMedia.Play(options); var status = response.GetRawResponse().ToString(); @@ -441,7 +478,7 @@ private async Task HandleCreateCallWithMediaStreaming( { bool isPstn = !target.StartsWith("8:"); string targetType = isPstn ? "PSTN" : "ACS"; - + _logger.LogInformation($"Creating call with media streaming. Target={target}, Type={targetType}, Channel={audioChannel}, EnableMedia={enableMediaStreaming}, Bidirectional={enableBidirectional}, PCM24kMono={pcm24kMono}, Async={async}"); try { @@ -449,20 +486,20 @@ private async Task HandleCreateCallWithMediaStreaming( var websocketUri = _config.CallbackUriHost.Replace("https", "wss") + "/ws"; MediaStreamingOptions mediaOpts = new MediaStreamingOptions( - new Uri(websocketUri), - MediaStreamingContent.Audio, - audioChannel, - MediaStreamingTransport.Websocket, - enableMediaStreaming) + audioChannel, + StreamingTransport.Websocket) { + TransportUri = new Uri(websocketUri), + MediaStreamingContent = MediaStreamingContent.Audio, EnableBidirectional = enableBidirectional, - AudioFormat = pcm24kMono ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono + AudioFormat = pcm24kMono ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono, + StartMediaStreaming = enableMediaStreaming }; var invite = isPstn ? new CallInvite(new PhoneNumberIdentifier(target), new PhoneNumberIdentifier(_config.AcsPhoneNumber)) : new CallInvite(new CommunicationUserIdentifier(target)); - + var createOpts = new CreateCallOptions(invite, callbackUri) { MediaStreamingOptions = mediaOpts @@ -686,946 +723,8 @@ private async Task HandleHold( return Problem($"Failed to {(unhold ? "unhold" : "hold")}: {ex.Message}"); } } - - private async Task HandleInterruptAudioAndAnnounce( - string callConnectionId, - CommunicationIdentifier target, - bool async) - { - _logger.LogInformation($"Interrupt audio and announce. CallId={callConnectionId}, Target={target.RawId}, Async={async}"); - try - { - var callMedia = _service.GetCallMedia(callConnectionId); - var props = _service.GetCallConnectionProperties(callConnectionId); - var fileSource = new FileSource(new Uri(_config.CallbackUriHost + "/audio/prompt.wav")); - var opts = new InterruptAudioAndAnnounceOptions(fileSource, target) - { - OperationContext = "interruptContext" - }; - - if (async) await callMedia.InterruptAudioAndAnnounceAsync(opts); else callMedia.InterruptAudioAndAnnounce(opts); - - _logger.LogInformation("Interrupt audio and announce completed"); - return Ok(new CallConnectionResponse { CallConnectionId = callConnectionId, CorrelationId = props.CorrelationId, Status = props.CallConnectionState.ToString() }); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error during interrupt audio and announce"); - return Problem($"Failed to interrupt audio and announce: {ex.Message}"); - } - } } } - -#region Play Media with File Source - - - -//app.MapPost("/playFileSourceToPstnTargetAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -//{ -// try -// { -// CallMedia callMedia = GetCallMedia(callConnectionId); -// // FileSource fileSource = new FileSource(new Uri(fileSourceUri)); -// List playTo = new List { new PhoneNumberIdentifier(pstnTarget) }; -// PlayOptions playToOptions = new PlayOptions(fileSource, playTo) -// { -// OperationContext = "playToContext" -// }; - -// logger.LogInformation($"Playing file source to PSTN target. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); -// await callMedia.PlayAsync(playToOptions); - -// string successMessage = $"File source played successfully to PSTN target. CallConnectionId: {callConnectionId}"; -// LogCollector.Log(successMessage); -// logger.LogInformation(successMessage); - -// return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); -// } -// catch (Exception ex) -// { -// string errorMessage = $"Error playing file source to PSTN target. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; -// LogCollector.Log(errorMessage); -// logger.LogInformation(errorMessage); - -// return Results.Problem($"Failed to play file source: {ex.Message}"); -// } -//}).WithTags("Play FileSource Media APIs"); - -//app.MapPost("/playFileSourceToPstnTarget", (string callConnectionId, string pstnTarget, ILogger logger) => -//{ -// try -// { -// CallMedia callMedia = GetCallMedia(callConnectionId); -// // FileSource fileSource = new FileSource(new Uri(fileSourceUri)); -// List playTo = new List { new PhoneNumberIdentifier(pstnTarget) }; -// PlayOptions playToOptions = new PlayOptions(fileSource, playTo) -// { -// OperationContext = "playToContext" -// }; - -// logger.LogInformation($"Playing file source to PSTN target. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); -// callMedia.Play(playToOptions); - -// string successMessage = $"File source played successfully to PSTN target. CallConnectionId: {callConnectionId}"; -// LogCollector.Log(successMessage); -// logger.LogInformation(successMessage); - -// return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); -// } -// catch (Exception ex) -// { -// string errorMessage = $"Error playing file source to PSTN target. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; -// LogCollector.Log(errorMessage); -// logger.LogInformation(errorMessage); - -// return Results.Problem($"Failed to play file source: {ex.Message}"); -// } -//}).WithTags("Play FileSource Media APIs"); - - -//app.MapPost("/playFileSourceToTeamsTargetAsync", async (string callConnectionId, string teamsObjectId, ILogger logger) => -//{ -// try -// { -// CallMedia callMedia = GetCallMedia(callConnectionId); -// // FileSource fileSource = new FileSource(new Uri(fileSourceUri)); -// List playTo = new List { new MicrosoftTeamsUserIdentifier(teamsObjectId) }; -// PlayOptions playToOptions = new PlayOptions(fileSource, playTo) -// { -// OperationContext = "playToContext" -// }; - -// logger.LogInformation($"Playing file source to Teams target. CallConnectionId: {callConnectionId}, Target: {teamsObjectId}"); -// await callMedia.PlayAsync(playToOptions); - -// string successMessage = $"File source played successfully to Teams target. CallConnectionId: {callConnectionId}"; -// LogCollector.Log(successMessage); -// logger.LogInformation(successMessage); - -// return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); -// } -// catch (Exception ex) -// { -// string errorMessage = $"Error playing file source to Teams target. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; -// LogCollector.Log(errorMessage); -// logger.LogInformation(errorMessage); - -// return Results.Problem($"Failed to play file source: {ex.Message}"); -// } -//}).WithTags("Play FileSource Media APIs"); - -//app.MapPost("/playFileSourceToTeamsTarget", (string callConnectionId, string teamsObjectId, ILogger logger) => -//{ -// try -// { -// CallMedia callMedia = GetCallMedia(callConnectionId); -// // FileSource fileSource = new FileSource(new Uri(fileSourceUri)); -// List playTo = new List { new MicrosoftTeamsUserIdentifier(teamsObjectId) }; -// PlayOptions playToOptions = new PlayOptions(fileSource, playTo) -// { -// OperationContext = "playToContext" -// }; - -// logger.LogInformation($"Playing file source to Teams target. CallConnectionId: {callConnectionId}, Target: {teamsObjectId}"); -// callMedia.Play(playToOptions); - -// string successMessage = $"File source played successfully to Teams target. CallConnectionId: {callConnectionId}"; -// LogCollector.Log(successMessage); -// logger.LogInformation(successMessage); - -// return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); -// } -// catch (Exception ex) -// { -// string errorMessage = $"Error playing file source to Teams target. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; -// LogCollector.Log(errorMessage); -// logger.LogInformation(errorMessage); - -// return Results.Problem($"Failed to play file source: {ex.Message}"); -// } -//}).WithTags("Play FileSource Media APIs"); - -#endregion -#region Recognization -/* -app.MapPost("/recognizeDTMFAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - var recognizeOptions = - new CallMediaRecognizeDtmfOptions( - targetParticipant: target, maxTonesToCollect: 4) - { - InterruptPrompt = false, - InterToneTimeout = TimeSpan.FromSeconds(5), - OperationContext = "DtmfContext", - InitialSilenceTimeout = TimeSpan.FromSeconds(15), - Prompt = textSource - }; - - logger.LogInformation($"Starting DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - await callMedia.StartRecognizingAsync(recognizeOptions); - - string successMessage = $"DTMF recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start DTMF recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -app.MapPost("/recognizeDTMF", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - var recognizeOptions = - new CallMediaRecognizeDtmfOptions( - targetParticipant: target, maxTonesToCollect: 4) - { - InterruptPrompt = false, - InterToneTimeout = TimeSpan.FromSeconds(5), - OperationContext = "DtmfContext", - InitialSilenceTimeout = TimeSpan.FromSeconds(15), - Prompt = textSource - }; - - logger.LogInformation($"Starting DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - callMedia.StartRecognizing(recognizeOptions); - - string successMessage = $"DTMF recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start DTMF recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - - -app.MapPost("/recognizeChoiceAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - - var recognizeOptions = - new CallMediaRecognizeChoiceOptions(targetParticipant: target, GetChoices()) - { - InterruptCallMediaOperation = false, - InterruptPrompt = false, - InitialSilenceTimeout = TimeSpan.FromSeconds(10), - Prompt = textSource, - OperationContext = "ChoiceContext" - }; - - logger.LogInformation($"Starting choice recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - await callMedia.StartRecognizingAsync(recognizeOptions); - - string successMessage = $"Choice recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting choice recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start choice recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -app.MapPost("/recognizeChoice", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - - var recognizeOptions = - new CallMediaRecognizeChoiceOptions(targetParticipant: target, GetChoices()) - { - InterruptCallMediaOperation = false, - InterruptPrompt = false, - InitialSilenceTimeout = TimeSpan.FromSeconds(10), - Prompt = textSource, - OperationContext = "ChoiceContext" - }; - - logger.LogInformation($"Starting choice recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - callMedia.StartRecognizingAsync(recognizeOptions); - - string successMessage = $"Choice recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting choice recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start choice recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -app.MapPost("/recognizeSpeechAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - var recognizeOptions = new CallMediaRecognizeSpeechOptions(targetParticipant: target) - { - InterruptPrompt = false, - OperationContext = "SpeechContext", - InitialSilenceTimeout = TimeSpan.FromSeconds(15), - Prompt = textSource, - EndSilenceTimeout = TimeSpan.FromSeconds(15) - }; - - logger.LogInformation($"Starting speech recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - await callMedia.StartRecognizingAsync(recognizeOptions); - - string successMessage = $"Speech recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting speech recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start speech recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -app.MapPost("/recognizeSpeech", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - var recognizeOptions = new CallMediaRecognizeSpeechOptions(targetParticipant: target) - { - InterruptPrompt = false, - OperationContext = "SpeechContext", - InitialSilenceTimeout = TimeSpan.FromSeconds(15), - Prompt = textSource, - EndSilenceTimeout = TimeSpan.FromSeconds(15) - }; - - logger.LogInformation($"Starting speech recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - callMedia.StartRecognizing(recognizeOptions); - - string successMessage = $"Speech recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting speech recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start speech recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -app.MapPost("/recognizeSpeechOrDtmfAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - var recognizeOptions = - new CallMediaRecognizeSpeechOrDtmfOptions( - targetParticipant: target, maxTonesToCollect: 4) - { - InterruptPrompt = false, - OperationContext = "SpeechOrDTMFContext", - InitialSilenceTimeout = TimeSpan.FromSeconds(15), - Prompt = textSource, - EndSilenceTimeout = TimeSpan.FromSeconds(5) - }; - - logger.LogInformation($"Starting speech/DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - await callMedia.StartRecognizingAsync(recognizeOptions); - - string successMessage = $"Speech/DTMF recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting speech/DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start speech/DTMF recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -app.MapPost("/recognizeSpeechOrDtmf", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("Hi, this is recognize test. please provide input thanks!.") - { - VoiceName = "en-US-NancyNeural" - }; - - var target = new PhoneNumberIdentifier(pstnTarget); - - var recognizeOptions = - new CallMediaRecognizeSpeechOrDtmfOptions( - targetParticipant: target, maxTonesToCollect: 4) - { - InterruptPrompt = false, - OperationContext = "SpeechOrDTMFContext", - InitialSilenceTimeout = TimeSpan.FromSeconds(15), - Prompt = textSource, - EndSilenceTimeout = TimeSpan.FromSeconds(5) - }; - - logger.LogInformation($"Starting speech/DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - callMedia.StartRecognizingAsync(recognizeOptions); - - string successMessage = $"Speech/DTMF recognition started successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting speech/DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to start speech/DTMF recognition: {ex.Message}"); - } -}).WithTags("Start Recognization APIs"); - -*/ -#endregion -#region DTMF -/* -app.MapPost("/sendDTMFTonesAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - List tones = new List - { - DtmfTone.Zero, - DtmfTone.One - }; - - CallMedia callMedia = GetCallMedia(callConnectionId); - - logger.LogInformation($"Sending DTMF tones. CallConnectionId: {callConnectionId}, Target: {pstnTarget}, Tones: 0,1"); - await callMedia.SendDtmfTonesAsync(tones, target); - - string successMessage = $"DTMF tones sent successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error sending DTMF tones. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to send DTMF tones: {ex.Message}"); - } -}).WithTags("Send or Start DTMF APIs"); - -app.MapPost("/sendDTMFTones", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - List tones = new List - { - DtmfTone.Zero, - DtmfTone.One - }; - - CallMedia callMedia = GetCallMedia(callConnectionId); - - logger.LogInformation($"Sending DTMF tones. CallConnectionId: {callConnectionId}, Target: {pstnTarget}, Tones: 0,1"); - callMedia.SendDtmfTones(tones, target); - - string successMessage = $"DTMF tones sent successfully. CallConnectionId: {callConnectionId}"; - LogCollector.Log(successMessage); - logger.LogInformation(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error sending DTMF tones. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - LogCollector.Log(errorMessage); - logger.LogInformation(errorMessage); - - return Results.Problem($"Failed to send DTMF tones: {ex.Message}"); - } -}).WithTags("Send or Start DTMF APIs"); - -app.MapPost("/startContinuousDTMFTonesAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - logger.LogInformation($"Starting continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Starting continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - logger.LogInformation($"Executing StartContinuousDtmfRecognitionAsync. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Executing StartContinuousDtmfRecognitionAsync. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - await callMedia.StartContinuousDtmfRecognitionAsync(target); - - string successMessage = $"Continuous DTMF recognition started successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting continuous DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - - return Results.Problem($"Failed to start continuous DTMF recognition: {ex.Message}"); - } -}).WithTags("Send or Start DTMF APIs"); - -app.MapPost("/startContinuousDTMFTones", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - logger.LogInformation($"Starting continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Starting continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - logger.LogInformation($"Executing StartContinuousDtmfRecognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Executing StartContinuousDtmfRecognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - callMedia.StartContinuousDtmfRecognition(target); - - string successMessage = $"Continuous DTMF recognition started successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error starting continuous DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - - return Results.Problem($"Failed to start continuous DTMF recognition: {ex.Message}"); - } -}).WithTags("Send or Start DTMF APIs"); - -app.MapPost("/stopContinuousDTMFTonesAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - logger.LogInformation($"Stopping continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Stopping continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - logger.LogInformation($"Executing StopContinuousDtmfRecognitionAsync. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Executing StopContinuousDtmfRecognitionAsync. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - await callMedia.StopContinuousDtmfRecognitionAsync(target); - - string successMessage = $"Continuous DTMF recognition stopped successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error stopping continuous DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - - return Results.Problem($"Failed to stop continuous DTMF recognition: {ex.Message}"); - } -}).WithTags("Send or Start DTMF APIs"); - -app.MapPost("/stopContinuousDTMFTones", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - logger.LogInformation($"Stopping continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Stopping continuous DTMF recognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - logger.LogInformation($"Executing StopContinuousDtmfRecognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - LogCollector.Log($"Executing StopContinuousDtmfRecognition. CallConnectionId: {callConnectionId}, Target: {pstnTarget}"); - - callMedia.StopContinuousDtmfRecognition(target); - - string successMessage = $"Continuous DTMF recognition stopped successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error stopping continuous DTMF recognition. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - - return Results.Problem($"Failed to stop continuous DTMF recognition: {ex.Message}"); - } -}).WithTags("Send or Start DTMF APIs"); -*/ -#endregion -#region Hold/Unhold -/* -app.MapPost("/holdParticipantAsync", async (string callConnectionId, string pstnTarget, bool isPlaySource, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("You are on hold please wait..") - { - VoiceName = "en-US-NancyNeural" - }; - - if (isPlaySource) - { - HoldOptions holdOptions = new HoldOptions(target) - { - PlaySource = textSource, - OperationContext = "holdUserContext" - }; - await callMedia.HoldAsync(holdOptions); - } - else - { - HoldOptions holdOptions = new HoldOptions(target) - { - OperationContext = "holdUserContext" - }; - await callMedia.HoldAsync(holdOptions); - } - string successMessage = $"Participant put on hold successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error putting participant on hold. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to put participant on hold: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); - -app.MapPost("/holdParticipant", (string callConnectionId, string pstnTarget, bool isPlaySource, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - TextSource textSource = new TextSource("You are on hold please wait..") - { - VoiceName = "en-US-NancyNeural" - }; - - if (isPlaySource) - { - HoldOptions holdOptions = new HoldOptions(target) - { - PlaySource = textSource, - OperationContext = "holdUserContext" - }; - callMedia.Hold(holdOptions); - } - else - { - HoldOptions holdOptions = new HoldOptions(target) - { - OperationContext = "holdUserContext" - }; - callMedia.Hold(holdOptions); - } - string successMessage = $"Participant put on hold successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error putting participant on hold. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to put participant on hold: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); - -app.MapPost("/interrupAudioAndAnnounceAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - TextSource textSource = new TextSource("Hi, This is interrup audio and announcement test") - { - VoiceName = "en-US-NancyNeural" - }; - - InterruptAudioAndAnnounceOptions interruptAudio = new InterruptAudioAndAnnounceOptions(textSource, target) - { - OperationContext = "innterruptContext" - }; - - await callMedia.InterruptAudioAndAnnounceAsync(interruptAudio); - string successMessage = $"Audio interrupted and announcement made successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error interrupting audio and announcing. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to interrupt audio and announce: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); - -app.MapPost("/interrupAudioAndAnnounce", (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - TextSource textSource = new TextSource("Hi, This is interrupt audio and announcement test.") - { - VoiceName = "en-US-NancyNeural" - }; - - InterruptAudioAndAnnounceOptions interruptAudio = new InterruptAudioAndAnnounceOptions(textSource, target) - { - OperationContext = "innterruptContext" - }; - - callMedia.InterruptAudioAndAnnounce(interruptAudio); - string successMessage = $"Audio interrupted and announcement made successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error interrupting audio and announcing. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to interrupt audio and announce: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); - - -app.MapPost("/unholdParticipantAsync", async (string callConnectionId, string pstnTarget, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - UnholdOptions unholdOptions = new UnholdOptions(target) - { - OperationContext = "unholdUserContext" - }; - - await callMedia.UnholdAsync(unholdOptions); - string successMessage = $"Participant taken off hold successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error taking participant off hold. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to take participant off hold: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); - -app.MapPost("/unholdParticipant", (string pstnTarget, string callConnectionId, ILogger logger) => -{ - try - { - CommunicationIdentifier target = new PhoneNumberIdentifier(pstnTarget); - - CallMedia callMedia = GetCallMedia(callConnectionId); - - UnholdOptions unholdOptions = new UnholdOptions(target) - { - OperationContext = "unholdUserContext" - }; - - callMedia.Unhold(unholdOptions); - string successMessage = $"Participant taken off hold successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error taking participant off hold. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to take participant off hold: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); - -app.MapPost("/interruptHoldWithPlay", (string pstnTarget, string callConnectionId, ILogger logger) => -{ - try - { - CallMedia callMedia = GetCallMedia(callConnectionId); - - TextSource textSource = new TextSource("Hi, This is interrupt audio and announcement test.") - { - VoiceName = "en-US-NancyNeural" - }; - - List playTo = new List { new PhoneNumberIdentifier(pstnTarget) }; - PlayOptions playToOptions = new PlayOptions(textSource, playTo) - { - OperationContext = "playToContext", - InterruptHoldAudio = true - }; - - callMedia.Play(playToOptions); - string successMessage = $"Hold audio interrupted successfully. CallConnectionId: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error interrupting hold audio. CallConnectionId: {callConnectionId}. Error: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to interrupt hold audio: {ex.Message}"); - } -}).WithTags("Hold Participant APIs"); -*/ -#endregion #region Transcription /* app.MapPost("/createCallToPstnWithTranscriptionAsync", async (string targetPhoneNumber, ILogger logger) => @@ -2152,164 +1251,3 @@ private async Task HandleInterruptAudioAndAnnounce( //}).WithTags("Play SsmlSource Media APIs"); #endregion -#region Media streaming -/* -app.MapPost("/createCallToPstnWithMediaStreamingAsync", async (string targetPhoneNumber, bool isEnableBidirectional, bool isPcm24kMono, ILogger logger) => -{ - try - { - PhoneNumberIdentifier target = new PhoneNumberIdentifier(targetPhoneNumber); - PhoneNumberIdentifier caller = new PhoneNumberIdentifier(acsPhoneNumber); - - - var callbackUri = new Uri(new Uri(callbackUriHost), "/api/callbacks"); - eventCallbackUri = callbackUri; - CallInvite callInvite = new CallInvite(target, caller); - var websocketUri = callbackUriHost.Replace("https", "wss") + "/ws"; - MediaStreamingOptions mediaStreamingOptions = new MediaStreamingOptions(new Uri(websocketUri), MediaStreamingContent.Audio, - MediaStreamingAudioChannel.Unmixed, MediaStreamingTransport.Websocket, true); - mediaStreamingOptions.EnableBidirectional = isEnableBidirectional; - mediaStreamingOptions.AudioFormat = isPcm24kMono ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono; - - var createCallOptions = new CreateCallOptions(callInvite, callbackUri) - { - // ACS GCCH Phase 2 - // CallIntelligenceOptions = new CallIntelligenceOptions() { CognitiveServicesEndpoint = new Uri(cognitiveServicesEndpoint) }, - MediaStreamingOptions = mediaStreamingOptions - }; - - CreateCallResult createCallResult = await client.CreateCallAsync(createCallOptions); - string callConnectionId = createCallResult.CallConnectionProperties.CallConnectionId; - - string successMessage = $"Created async pstn media streaming call with connection id: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error creating PSTN call with media streaming: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to create PSTN call with media streaming: {ex.Message}"); - } -}).WithTags("Media streaming APIs"); - -app.MapPost("/createCallToPstnWithMediaStreaming", (string targetPhoneNumber, bool isEnableBidirectional, bool isPcm24kMono, ILogger logger) => -{ - try - { - PhoneNumberIdentifier target = new PhoneNumberIdentifier(targetPhoneNumber); - PhoneNumberIdentifier caller = new PhoneNumberIdentifier(acsPhoneNumber); - - - var callbackUri = new Uri(new Uri(callbackUriHost), "/api/callbacks"); - eventCallbackUri = callbackUri; - CallInvite callInvite = new CallInvite(target, caller); - var websocketUri = callbackUriHost.Replace("https", "wss") + "/ws"; - MediaStreamingOptions mediaStreamingOptions = new MediaStreamingOptions(new Uri(websocketUri), MediaStreamingContent.Audio, - MediaStreamingAudioChannel.Unmixed, MediaStreamingTransport.Websocket, true); - mediaStreamingOptions.EnableBidirectional = isEnableBidirectional; - mediaStreamingOptions.AudioFormat = isPcm24kMono ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono; - - var createCallOptions = new CreateCallOptions(callInvite, callbackUri) - { - // ACS GCCH Phase 2 - // CallIntelligenceOptions = new CallIntelligenceOptions() { CognitiveServicesEndpoint = new Uri(cognitiveServicesEndpoint) }, - MediaStreamingOptions = mediaStreamingOptions - }; - - CreateCallResult createCallResult = client.CreateCall(createCallOptions); - string callConnectionId = createCallResult.CallConnectionProperties.CallConnectionId; - - string successMessage = $"Created pstn media streaming call with connection id: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error creating PSTN call with media streaming: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to create PSTN call with media streaming: {ex.Message}"); - } -}).WithTags("Media streaming APIs"); -*/ -/* -app.MapPost("/createCallToTeamsWithMediaStreamingAsync", async (string teamsObjectId, bool isEnableBidirectional, bool isPcm24kMono, ILogger logger) => -{ - try - { - var callbackUri = new Uri(new Uri(callbackUriHost), "/api/callbacks"); - eventCallbackUri = callbackUri; - CallInvite callInvite = new CallInvite(new MicrosoftTeamsUserIdentifier(teamsObjectId)); - var websocketUri = callbackUriHost.Replace("https", "wss") + "/ws"; - MediaStreamingOptions mediaStreamingOptions = new MediaStreamingOptions(new Uri(websocketUri), MediaStreamingContent.Audio, - MediaStreamingAudioChannel.Unmixed, MediaStreamingTransport.Websocket, true); - mediaStreamingOptions.EnableBidirectional = isEnableBidirectional; - mediaStreamingOptions.AudioFormat = isPcm24kMono ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono; - - var createCallOptions = new CreateCallOptions(callInvite, callbackUri) - { - // ACS GCCH Phase 2 - // CallIntelligenceOptions = new CallIntelligenceOptions() { CognitiveServicesEndpoint = new Uri(cognitiveServicesEndpoint) }, - MediaStreamingOptions = mediaStreamingOptions - }; - - CreateCallResult createCallResult = await client.CreateCallAsync(createCallOptions); - string callConnectionId = createCallResult.CallConnectionProperties.CallConnectionId; - - string successMessage = $"Created async teams call with connection id: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error creating Teams call with media streaming: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to create Teams call with media streaming: {ex.Message}"); - } -}).WithTags("Media streaming APIs"); - -app.MapPost("/createCallToTeamsWithMediaStreaming", (string teamsObjectId, bool isEnableBidirectional, bool isPcm24kMono, ILogger logger) => -{ - try - { - var callbackUri = new Uri(new Uri(callbackUriHost), "/api/callbacks"); - eventCallbackUri = callbackUri; - CallInvite callInvite = new CallInvite(new MicrosoftTeamsUserIdentifier(teamsObjectId)); - var websocketUri = callbackUriHost.Replace("https", "wss") + "/ws"; - MediaStreamingOptions mediaStreamingOptions = new MediaStreamingOptions(new Uri(websocketUri), MediaStreamingContent.Audio, - MediaStreamingAudioChannel.Unmixed, MediaStreamingTransport.Websocket, true); - mediaStreamingOptions.EnableBidirectional = isEnableBidirectional; - mediaStreamingOptions.AudioFormat = isPcm24kMono ? AudioFormat.Pcm24KMono : AudioFormat.Pcm16KMono; - - var createCallOptions = new CreateCallOptions(callInvite, callbackUri) - { - // ACS GCCH Phase 2 - // CallIntelligenceOptions = new CallIntelligenceOptions() { CognitiveServicesEndpoint = new Uri(cognitiveServicesEndpoint) }, - MediaStreamingOptions = mediaStreamingOptions - }; - - CreateCallResult createCallResult = client.CreateCall(createCallOptions); - string callConnectionId = createCallResult.CallConnectionProperties.CallConnectionId; - - string successMessage = $"Created teams call with connection id: {callConnectionId}"; - logger.LogInformation(successMessage); - LogCollector.Log(successMessage); - return Results.Ok(new { CallConnectionId = callConnectionId, Status = "Succeeded" }); - } - catch (Exception ex) - { - string errorMessage = $"Error creating Teams call with media streaming: {ex.Message}"; - logger.LogInformation(errorMessage); - LogCollector.Log(errorMessage); - return Results.Problem($"Failed to create Teams call with media streaming: {ex.Message}"); - } -}).WithTags("Media streaming APIs"); -*/ -#endregion - diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/RecordingsController.cs b/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/RecordingsController.cs index d62c8914..17e809c8 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/RecordingsController.cs +++ b/Call_Automation_GCCH/Call_Automation_GCCH/Controllers/RecordingsController.cs @@ -1,14 +1,8 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Azure.Communication; -using Azure.Communication.CallAutomation; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; +using Azure.Communication.CallAutomation; using Call_Automation_GCCH.Models; using Call_Automation_GCCH.Services; -using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; namespace Call_Automation_GCCH.Controllers { @@ -50,7 +44,6 @@ public async Task StartRecordingAsync( bool isAudioVideo = false, string recordingFormat = "Mp3", bool isMixed = true, - bool isRecordingWithCallConnectionId = true, bool isPauseOnStart = false, bool isBlobStorage = false) { @@ -97,41 +90,39 @@ public async Task StartRecordingAsync( var correlationId = callConnectionProperties.CorrelationId; CallLocator callLocator = new ServerCallLocator(serverCallId); - var recordingOptions = isRecordingWithCallConnectionId - ? new StartRecordingOptions(callConnectionProperties.CallConnectionId) - : new StartRecordingOptions(callLocator); + var recordingOptions = new StartRecordingOptions(callLocator); recordingOptions.RecordingContent = recordingContent; recordingOptions.RecordingFormat = format; recordingOptions.RecordingChannel = recordingChannel; recordingOptions.RecordingStateCallbackUri = new Uri(new Uri(_config.CallbackUriHost), "/api/callbacks"); recordingOptions.PauseOnStart = isPauseOnStart; - + // Configure recording storage if blob storage is requested if (isBlobStorage) { var blobStorageConnectionString = _config.StorageConnectionString; var blobContainerName = _config.RecordingBlobStorageContainerName ?? "recordingbyos"; - + // Create the container URL for blob storage var containerUrl = blobStorageConnectionString.Replace("DefaultEndpointsProtocol=https;", "https://") .Replace("AccountName=", "") .Replace("AccountKey=", "") .Replace("EndpointSuffix=", ""); - + // Extract account name from connection string to build proper URL var accountNameStart = blobStorageConnectionString.IndexOf("AccountName=") + "AccountName=".Length; var accountNameEnd = blobStorageConnectionString.IndexOf(";", accountNameStart); var accountName = blobStorageConnectionString.Substring(accountNameStart, accountNameEnd - accountNameStart); - + var endpointSuffixStart = blobStorageConnectionString.IndexOf("EndpointSuffix=") + "EndpointSuffix=".Length; var endpointSuffix = blobStorageConnectionString.Substring(endpointSuffixStart); - + var blobContainerUrl = $"https://{accountName}.blob.{endpointSuffix}/{blobContainerName}"; - + recordingOptions.RecordingStorage = RecordingStorage.CreateAzureBlobContainerRecordingStorage(new Uri(blobContainerUrl)); } - + CallAutomationService.SetRecordingFileFormat(format.ToString()); var recordingResult = await _service.GetCallAutomationClient().GetCallRecording().StartAsync(recordingOptions); @@ -171,7 +162,6 @@ public IActionResult StartRecording( bool isAudioVideo = false, string recordingFormat = "Mp3", bool isMixed = true, - bool isRecordingWithCallConnectionId = true, bool isPauseOnStart = false, bool isBlobStorage = false) { @@ -218,35 +208,33 @@ public IActionResult StartRecording( var correlationId = callConnectionProperties.CorrelationId; CallLocator callLocator = new ServerCallLocator(serverCallId); - var recordingOptions = isRecordingWithCallConnectionId - ? new StartRecordingOptions(callConnectionProperties.CallConnectionId) - : new StartRecordingOptions(callLocator); + var recordingOptions = new StartRecordingOptions(callLocator); recordingOptions.RecordingContent = recordingContent; recordingOptions.RecordingFormat = format; recordingOptions.RecordingChannel = recordingChannel; recordingOptions.RecordingStateCallbackUri = new Uri(new Uri(_config.CallbackUriHost), "/api/callbacks"); recordingOptions.PauseOnStart = isPauseOnStart; - + // Configure recording storage if blob storage is requested if (isBlobStorage) { var blobStorageConnectionString = _config.StorageConnectionString; var blobContainerName = _config.RecordingBlobStorageContainerName ?? "recordingbyos"; - + // Extract account name from connection string to build proper URL var accountNameStart = blobStorageConnectionString.IndexOf("AccountName=") + "AccountName=".Length; var accountNameEnd = blobStorageConnectionString.IndexOf(";", accountNameStart); var accountName = blobStorageConnectionString.Substring(accountNameStart, accountNameEnd - accountNameStart); - + var endpointSuffixStart = blobStorageConnectionString.IndexOf("EndpointSuffix=") + "EndpointSuffix=".Length; var endpointSuffix = blobStorageConnectionString.Substring(endpointSuffixStart); - + var blobContainerUrl = $"https://{accountName}.blob.{endpointSuffix}/{blobContainerName}"; - + recordingOptions.RecordingStorage = RecordingStorage.CreateAzureBlobContainerRecordingStorage(new Uri(blobContainerUrl)); } - + CallAutomationService.SetRecordingFileFormat(format.ToString()); var recordingResult = _service.GetCallAutomationClient().GetCallRecording().Start(recordingOptions); diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/Middleware/Helper.cs b/Call_Automation_GCCH/Call_Automation_GCCH/Middleware/Helper.cs index 94d5a741..de9793c4 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/Middleware/Helper.cs +++ b/Call_Automation_GCCH/Call_Automation_GCCH/Middleware/Helper.cs @@ -30,7 +30,6 @@ public static async Task ProcessRequest(WebSocket webSocket) LogCollector.Log("ENCODING-->" + audioMetadata.Encoding); LogCollector.Log("SAMPLE RATE-->" + audioMetadata.SampleRate); LogCollector.Log("CHANNELS-->" + audioMetadata.Channels); - LogCollector.Log("LENGTH-->" + audioMetadata.Length); LogCollector.Log("***************************************************************************************"); } if (response is AudioData audioData) @@ -46,8 +45,9 @@ public static async Task ProcessRequest(WebSocket webSocket) LogCollector.Log("***************************************************************************************"); // Send audio bytes back to client if bidirectional streaming is enabled - if (!audioData.IsSilent && audioData.Data is byte[] audioBytes) + if (!audioData.IsSilent) { + var audioBytes = audioData.Data.ToArray(); // Convert ReadOnlyMemory to byte[] LogCollector.Log("Bidirectional Logs are given below:"); await webSocket.SendAsync( new ArraySegment(audioBytes, 0, audioBytes.Length), diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/Program.cs b/Call_Automation_GCCH/Call_Automation_GCCH/Program.cs index 17bec8c3..43520ef5 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/Program.cs +++ b/Call_Automation_GCCH/Call_Automation_GCCH/Program.cs @@ -1,14 +1,7 @@ -using Azure.Communication; -using Azure.Communication.CallAutomation; -using Azure.Messaging; -using Azure.Messaging.EventGrid; -using Azure.Messaging.EventGrid.SystemEvents; using Call_Automation_GCCH; -using Call_Automation_GCCH.Controllers; using Call_Automation_GCCH.Models; using Call_Automation_GCCH.Services; using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging; var builder = WebApplication.CreateBuilder(args); @@ -20,16 +13,10 @@ builder.Services.AddSwaggerGen(); // Add CallAutomationService as a singleton -builder.Services.AddSingleton(sp => { +builder.Services.AddSingleton(sp => +{ string connectionString = commSection["AcsConnectionString"]; - bool isArizona = bool.Parse(commSection["IsArizona"] ?? "true"); - string pmaEndpoint = isArizona ? commSection["PmaEndpointArizona"] : commSection["PmaEndpointTexas"]; - - if (string.IsNullOrEmpty(pmaEndpoint)) { - sp.GetRequiredService>().LogWarning($"The {(isArizona ? "PmaEndpointArizona" : "PmaEndpointTexas")} setting is empty"); - } - - return new CallAutomationService(connectionString, pmaEndpoint, sp.GetRequiredService>()); + return new CallAutomationService(connectionString: connectionString, sp.GetRequiredService>()); }); // Add Storage Service @@ -68,27 +55,27 @@ app.UseWebSockets(); app.Use(async (context, next) => { - // Get the logger instance from the DI container - var logger = context.RequestServices.GetRequiredService>(); + // Get the logger instance from the DI container + var logger = context.RequestServices.GetRequiredService>(); - if (context.Request.Path == "/ws") - { - logger.LogInformation($"Request received. Path: {context.Request.Path}"); - if (context.WebSockets.IsWebSocketRequest) + if (context.Request.Path == "/ws") { - logger.LogInformation("WebSocket request received."); - using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Helper.ProcessRequest(webSocket); + logger.LogInformation($"Request received. Path: {context.Request.Path}"); + if (context.WebSockets.IsWebSocketRequest) + { + logger.LogInformation("WebSocket request received."); + using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Helper.ProcessRequest(webSocket); + } + else + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + } } else { - context.Response.StatusCode = StatusCodes.Status400BadRequest; + await next(context); } - } - else - { - await next(context); - } }); // Add custom WebSocket middleware diff --git a/Call_Automation_GCCH/Call_Automation_GCCH/appsettings.json b/Call_Automation_GCCH/Call_Automation_GCCH/appsettings.json index c9c4981f..ded47b54 100644 --- a/Call_Automation_GCCH/Call_Automation_GCCH/appsettings.json +++ b/Call_Automation_GCCH/Call_Automation_GCCH/appsettings.json @@ -7,12 +7,9 @@ }, "AllowedHosts": "*", - "CommunicationSettings": { - "AcsConnectionString": "", - "AcsPhoneNumber": "", - "CallbackUriHost": "", - "PmaEndpointArizona": "", - "PmaEndpointTexas": "", - "IsArizona": true - } + "CommunicationSettings": { + "AcsConnectionString": "", + "AcsPhoneNumber": "", + "CallbackUriHost": "", + } }