diff --git a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder.csproj b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder.csproj
index bfbbffd6..c79d756b 100644
--- a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder.csproj
+++ b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder.csproj
@@ -7,14 +7,10 @@
+
-
-
-
-
-
@@ -23,4 +19,10 @@
+
+
+
+
+
+
diff --git a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallConfiguration.cs b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallConfiguration.cs
index 2b7b25b0..8953ba0c 100644
--- a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallConfiguration.cs
+++ b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/CallConfiguration.cs
@@ -28,6 +28,11 @@ public CallConfiguration()
///
public string SourcePhoneNumber { get; set; }
+ ///
+ /// The phone number to add Participant to the call.
+ ///
+ public string AddParticipantNumber { get; set; }
+
///
/// The base url of the applicaiton.
///
@@ -58,6 +63,21 @@ public CallConfiguration()
///
public string AppointmentCancelledAudio { get; set; }
+ ///
+ /// Appointment to add AgentAudio audio file route
+ ///
+ public string AgentAudio { get; set; }
+
+ ///
+ /// Appointment to AddParticipant audio file route
+ ///
+ public string AddParticipant { get; set; }
+
+ ///
+ /// Appointment to remove RemoveParticipant audio file route
+ ///
+ public string RemoveParticipant { get; set; }
+
///
/// Invalid input audio file route
///
@@ -67,5 +87,10 @@ public CallConfiguration()
/// Time out audio file route
///
public string TimedoutAudio { get; set; }
+
+ ///
+ /// Scenario for hanging up the call
+ ///
+ public int HangUpScenarios { get; set; }
}
}
diff --git a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Program.cs b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Program.cs
index f7f7d3f1..91367ff0 100644
--- a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Program.cs
+++ b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Program.cs
@@ -1,3 +1,4 @@
+using Azure;
using Azure.Communication;
using Azure.Communication.CallAutomation;
using Azure.Messaging;
@@ -5,6 +6,8 @@
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
+using System.ComponentModel.DataAnnotations;
+using System.Text.RegularExpressions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
@@ -16,26 +19,77 @@
builder.Services.AddSingleton(new CallAutomationClient(callConfigurationSection["ConnectionString"]));
var app = builder.Build();
-
+var TargetIdentity = "";
var sourceIdentity = await app.ProvisionAzureCommunicationServicesIdentity(callConfigurationSection["ConnectionString"]);
+var target = callConfigurationSection["AddParticipantNumber"];
+var addedParticipants = target.Split(';');
+int addedParticipantsCount = 0;
+int declineParticipantsCount = 0;
+var SourceId = "";
+
+CommunicationIdentifierKind GetIdentifierKind(string participantnumber)
+{
+ //checks the identity type returns as string
+ return Regex.Match(participantnumber, Constants.userIdentityRegex, RegexOptions.IgnoreCase).Success ? CommunicationIdentifierKind.UserIdentity :
+ Regex.Match(participantnumber, Constants.phoneIdentityRegex, RegexOptions.IgnoreCase).Success ? CommunicationIdentifierKind.PhoneIdentity :
+ CommunicationIdentifierKind.UnknownIdentity;
+}
// Api to initiate out bound call
-app.MapPost("/api/call", async (CallAutomationClient callAutomationClient, IOptions callConfiguration, ILogger logger) =>
+app.MapPost("/api/call", async ([Required] string targetNo, CallAutomationClient callAutomationClient, IOptions callConfiguration, ILogger logger) =>
{
- var source = new CallSource(new CommunicationUserIdentifier(sourceIdentity))
+
+ var acsAcquiredNumber = new PhoneNumberIdentifier(callConfiguration.Value.SourcePhoneNumber);
+ if (!string.IsNullOrEmpty(targetNo))
{
- CallerId = new PhoneNumberIdentifier(callConfiguration.Value.SourcePhoneNumber)
- };
- var target = new PhoneNumberIdentifier(callConfiguration.Value.TargetPhoneNumber);
+ var identities = targetNo.Split(';');
+ foreach (var target in identities)
+ {
+ if (!string.IsNullOrEmpty(target))
+ {
+ TargetIdentity = target;
+ CallInvite? callInvite = null;
+ var identifierKind = GetIdentifierKind(target);
- var createCallOption = new CreateCallOptions(source,
- new List() { target },
- new Uri(callConfiguration.Value.CallbackEventUri));
+ if (identifierKind == CommunicationIdentifierKind.PhoneIdentity)
+ {
+ callInvite = new CallInvite(new PhoneNumberIdentifier(target), acsAcquiredNumber);
+ }
+ else if (identifierKind == CommunicationIdentifierKind.UserIdentity)
+ {
+ callInvite = new CallInvite(new CommunicationUserIdentifier(target));
+ }
- var response = await callAutomationClient.CreateCallAsync(createCallOption).ConfigureAwait(false);
+ var createCallOption = new CreateCallOptions(callInvite, new Uri(callConfiguration.Value.CallbackEventUri));
+ var response = await callAutomationClient.CreateCallAsync(createCallOption).ConfigureAwait(false);
+ logger.LogInformation($"Reponse from create call: {response.GetRawResponse()}" +
+ $"CallConnection Id : {response.Value.CallConnection.CallConnectionId}");
+ SourceId = response.Value.CallConnectionProperties.Source.RawId;
+ }
+ }
+ }
+ else
+ {
+ TargetIdentity = callConfiguration.Value.TargetPhoneNumber;
+ if (!string.IsNullOrEmpty(TargetIdentity))
+ {
+ var identifierKind = GetIdentifierKind(TargetIdentity);
+ CallInvite? callInvite = null;
+ if (identifierKind == CommunicationIdentifierKind.PhoneIdentity)
+ {
+ callInvite = new CallInvite(new PhoneNumberIdentifier(TargetIdentity), acsAcquiredNumber);
+ }
- logger.LogInformation($"Reponse from create call: {response.GetRawResponse()}" +
- $"CallConnection Id : {response.Value.CallConnection.CallConnectionId}");
+ else if (identifierKind == CommunicationIdentifierKind.UserIdentity)
+ {
+ callInvite = new CallInvite(new CommunicationUserIdentifier(TargetIdentity));
+ }
+ var createCallOption = new CreateCallOptions(callInvite, new Uri(callConfiguration.Value.CallbackEventUri));
+ var response = await callAutomationClient.CreateCallAsync(createCallOption).ConfigureAwait(false);
+ logger.LogInformation($"Reponse from create call: {response.GetRawResponse()}" +
+ $"CallConnection Id : {response.Value.CallConnection.CallConnectionId}");
+ }
+ }
});
//api to handle call back events
@@ -50,10 +104,22 @@
var callConnectionMedia = callConnection.GetCallMedia();
if (@event is CallConnected)
{
+ addedParticipantsCount = 0;
+ declineParticipantsCount = 0;
//Initiate recognition as call connected event is received
- logger.LogInformation($"CallConnected event received for call connection id: {@event.CallConnectionId}");
+ logger.LogInformation($"CallConnected event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
+
+ var properties = callConnection.GetCallConnectionProperties();
+ logger.LogInformation($"call connection properties -------> SourceIdentity : {properties.Value.Source.RawId}," +
+ $"CallConnection State : {properties.Value.CallConnectionState}");
+ logger.LogInformation($"targets ------->");
+ foreach (var target in properties.Value.Targets)
+ {
+ logger.LogInformation($"{target.RawId}");
+ }
+
var recognizeOptions =
- new CallMediaRecognizeDtmfOptions(CommunicationIdentifier.FromRawId(callConfiguration.Value.TargetPhoneNumber), maxTonesToCollect: 1)
+ new CallMediaRecognizeDtmfOptions(CommunicationIdentifier.FromRawId(properties.Value.Targets[0].RawId), maxTonesToCollect: 1)
{
InterruptPrompt = true,
InterToneTimeout = TimeSpan.FromSeconds(10),
@@ -68,43 +134,200 @@
if (@event is RecognizeCompleted { OperationContext: "AppointmentReminderMenu" })
{
// Play audio once recognition is completed sucessfully
- logger.LogInformation($"RecognizeCompleted event received for call connection id: {@event.CallConnectionId}");
+ logger.LogInformation($"RecognizeCompleted event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
var recognizeCompletedEvent = (RecognizeCompleted)@event;
- var toneDetected = recognizeCompletedEvent.CollectTonesResult.Tones[0];
- var playSource = Utils.GetAudioForTone(toneDetected, callConfiguration);
+ DtmfTone toneDetected = ((DtmfResult)recognizeCompletedEvent.RecognizeResult).Tones[0];
+ if (toneDetected == DtmfTone.Three)
+ {
+ var playSource = Utils.GetAudioForTone(toneDetected, callConfiguration) ;
+ // Play audio for dtmf response
+ await callConnectionMedia.PlayToAllAsync(new PlayToAllOptions(playSource) { OperationContext = "AgentConnect", Loop = false });
+
+ }
+ else
+ {
+ var playSource = Utils.GetAudioForTone(toneDetected, callConfiguration) ;
+ // Play audio for dtmf response
+ await callConnectionMedia.PlayToAllAsync(new PlayToAllOptions(playSource) { OperationContext = "ResponseToDtmf", Loop = false });
+ }
+ }
+ if (@event is PlayCompleted { OperationContext: "AgentConnect" })
+ {
+ foreach (var Participantindentity in addedParticipants)
+ {
+ CallInvite? callInvite = null;
+ if (!string.IsNullOrEmpty(Participantindentity))
+ {
+ var identifierKind = GetIdentifierKind(Participantindentity.Trim());
+ if (identifierKind == CommunicationIdentifierKind.PhoneIdentity)
+ {
+ callInvite = new CallInvite(new PhoneNumberIdentifier(Participantindentity.Trim()), new PhoneNumberIdentifier(callConfiguration.Value.SourcePhoneNumber));
+ }
+
+ else if (identifierKind == CommunicationIdentifierKind.UserIdentity)
+ {
+ callInvite = new CallInvite(new CommunicationUserIdentifier(Participantindentity.Trim()));
+ }
+ var addParticipantOptions = new AddParticipantOptions(callInvite);
+ var response = await callConnection.AddParticipantAsync(addParticipantOptions);
+ var playSource = new FileSource(new Uri(callConfiguration.Value.AppBaseUri + callConfiguration.Value.AddParticipant));
+ await callConnectionMedia.PlayToAllAsync(new PlayToAllOptions(playSource) { OperationContext = "addParticipant", Loop = false });
+ await Task.Delay(TimeSpan.FromSeconds(10));
- // Play audio for dtmf response
- await callConnectionMedia.PlayToAllAsync(playSource, new PlayOptions { OperationContext = "ResponseToDtmf", Loop = false });
+ logger.LogInformation($"Add participant call : {response.Value.Participant}" + $" Status of call :{response.GetRawResponse().Status}"
+ + $" participant ID: {response.Value.Participant.Identifier}" + $" participat is muted : {response.Value.Participant.IsMuted}");
+ }
+ }
}
+
if (@event is RecognizeFailed { OperationContext: "AppointmentReminderMenu" })
{
- logger.LogInformation($"RecognizeFailed event received for call connection id: {@event.CallConnectionId}");
+ logger.LogInformation($"RecognizeFailed event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
var recognizeFailedEvent = (RecognizeFailed)@event;
// Check for time out, and then play audio message
- if (recognizeFailedEvent.ReasonCode.Equals(ReasonCode.RecognizeInitialSilenceTimedOut))
+ if (recognizeFailedEvent.ReasonCode.Equals(MediaEventReasonCode.RecognizeInitialSilenceTimedOut))
{
- logger.LogInformation($"Recognition timed out for call connection id: {@event.CallConnectionId}");
- var playSource = new FileSource(new Uri(callConfiguration.Value.AppBaseUri + callConfiguration.Value.TimedoutAudio));
-
+ logger.LogInformation($"Recognition timed out for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
+ var playSource = new FileSource(new Uri(callConfiguration.Value.AppBaseUri + callConfiguration.Value.TimedoutAudio)) ;
+
//Play audio for time out
- await callConnectionMedia.PlayToAllAsync(playSource, new PlayOptions { OperationContext = "ResponseToDtmf", Loop = false });
+ await callConnectionMedia.PlayToAllAsync(new PlayToAllOptions(playSource) { OperationContext = "ResponseToDtmf", Loop = false });
}
}
+
if (@event is PlayCompleted { OperationContext: "ResponseToDtmf" })
{
- logger.LogInformation($"PlayCompleted event received for call connection id: {@event.CallConnectionId}");
+ logger.LogInformation($"PlayCompleted event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
await callConnection.HangUpAsync(forEveryone: true);
}
if (@event is PlayFailed { OperationContext: "ResponseToDtmf" })
{
- logger.LogInformation($"PlayFailed event received for call connection id: {@event.CallConnectionId}");
+ logger.LogInformation($"PlayFailed event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
await callConnection.HangUpAsync(forEveryone: true);
}
+ if (@event is AddParticipantSucceeded addParticipantSucceeded)
+ {
+ addedParticipantsCount++;
+ logger.LogInformation($"participant added ---> {addParticipantSucceeded.Participant.RawId}");
+ if ((addedParticipantsCount + declineParticipantsCount) == addedParticipants.Length)
+ {
+ await PerformHangUp(callConnection);
+ }
+ }
+ if (@event is AddParticipantFailed failedParticipant)
+ {
+ declineParticipantsCount++;
+ logger.LogInformation($"Failed participant Reason -------> {failedParticipant.ResultInformation?.Message}");
+ if ((addedParticipantsCount + declineParticipantsCount) == addedParticipants.Length)
+ {
+ await PerformHangUp(callConnection);
+ }
+ }
+ if (@event is RemoveParticipantSucceeded)
+ {
+ RemoveParticipantSucceeded RemoveParticipantSucceeded = (RemoveParticipantSucceeded)@event;
+ logger.LogInformation($"Remove Participant Succeeded RawId : {RemoveParticipantSucceeded.Participant.RawId}");
+ }
+ if (@event is RemoveParticipantFailed)
+ {
+ RemoveParticipantFailed removeParticipantFailed = (RemoveParticipantFailed)@event;
+ logger.LogInformation($"Remove participant failed RawId:{removeParticipantFailed.Participant.RawId}");
+ }
+ if (@event is ParticipantsUpdated updatedParticipantEvent)
+ {
+ logger.LogInformation($"Participant Updated Event Recieved");
+ logger.LogInformation($"------- Updated Participant : {updatedParticipantEvent.Participants.Count} -------- ");
+ foreach (var participant in updatedParticipantEvent.Participants)
+ {
+ logger.LogInformation($"Participant Raw ID : {participant.Identifier.RawId}, IsMuted : {participant.IsMuted}");
+ }
+ }
+ }
+
+ //Perform HangUp
+ async Task PerformHangUp(CallConnection callConnection)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(10));
+
+ var participantlistResponse = await callConnection.GetParticipantsAsync();
+ logger.LogInformation($"-------Participant List : {participantlistResponse.Value.Count} ----- ");
+ foreach (var participant in participantlistResponse.Value)
+ {
+ try
+ {
+ logger.LogInformation($"{participant.Identifier.RawId}");
+ var response = callConnection.GetParticipant(participant.Identifier);
+ logger.LogInformation($"-------get participnat response : {response} ----- ");
+ }
+ catch(Exception ex)
+ {
+ logger.LogInformation($"------Error In GetParticipant() for participnat : {participant.Identifier.RawId} " +
+ $"-----> {ex.Message}");
+ }
+
+ }
+
+ int hangupScenario = callConfiguration.Value.HangUpScenarios;
+ if (hangupScenario == 1)
+ {
+ logger.LogInformation($"CA hanging up the call for everyone." + $"Information of Call:{callConnection.GetCallConnectionProperties()}");
+ var response = await callConnection.HangUpAsync(true);
+ logger.LogInformation($"Hang up response : {response}");
+ }
+ else if (hangupScenario == 2)
+ {
+ logger.LogInformation($"CA hang up the call." + $"Information of Call:{callConnection.GetCallConnectionProperties()}");
+ var response = await callConnection.HangUpAsync(false);
+ logger.LogInformation($"Hang up response : {response}");
+ }
+ else if (hangupScenario == 3 || hangupScenario == 4)
+ {
+ if (addedParticipantsCount == 0 && hangupScenario == 3)
+ {
+ logger.LogInformation($"No participants got addedd to remove");
+ }
+ else
+ {
+ logger.LogInformation($"Going to remove added partipants.");
+ List participantsToRemoveAll = (await callConnection.GetParticipantsAsync()).Value.ToList();
+ CommunicationIdentifier targetParticipant = null;
+ foreach (CallParticipant participantToRemove in participantsToRemoveAll)
+ {
+ if (!string.IsNullOrEmpty(participantToRemove.Identifier.ToString()))
+ {
+ if (participantToRemove.Identifier.RawId.Contains(TargetIdentity))
+ {
+ targetParticipant = participantToRemove.Identifier;
+ }
+ else if (participantToRemove.Identifier.RawId.Contains(SourceId))
+ {
+ SourceId = participantToRemove.Identifier.RawId;
+ }
+ else
+ {
+ var RemoveParticipant = new RemoveParticipantOptions(participantToRemove.Identifier);
+ logger.LogInformation($"going to remove participant : {participantToRemove.Identifier.RawId}");
+ var removeParticipantResponse = await callConnection.RemoveParticipantAsync(RemoveParticipant);
+ logger.LogInformation($"Removing participant Response : {removeParticipantResponse.GetRawResponse()}");
+ }
+ }
+ }
+ if (hangupScenario == 4 && targetParticipant != null)
+ {
+ logger.LogInformation($"going to remove target participant : {targetParticipant.RawId}");
+ var removeParticipantResponse = await callConnection.RemoveParticipantAsync(targetParticipant);
+ logger.LogInformation($"Removing participant Response : {removeParticipantResponse.GetRawResponse()}");
+ }
+ }
+ }
}
+
return Results.Ok();
}).Produces(StatusCodes.Status200OK);
+
+
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || app.Environment.IsProduction())
{
@@ -121,3 +344,16 @@
app.UseHttpsRedirection();
app.Run();
+public enum CommunicationIdentifierKind
+{
+ PhoneIdentity,
+ UserIdentity,
+ UnknownIdentity
+
+}
+public class Constants
+{
+ public const string userIdentityRegex = @"8:acs:[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}_[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}";
+ public const string phoneIdentityRegex = @"^\+\d{10,14}$";
+
+}
diff --git a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Utils.cs b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Utils.cs
index 7c712591..b27b4512 100644
--- a/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Utils.cs
+++ b/CallAutomation_AppointmentReminder/CallAutomation_AppointmentReminder/Utils.cs
@@ -17,6 +17,10 @@ public static PlaySource GetAudioForTone(DtmfTone toneDetected, IOptions
+
-
+
diff --git a/CallAutomation_SimpleIvr/Program.cs b/CallAutomation_SimpleIvr/Program.cs
index 79ae779c..2a796b88 100644
--- a/CallAutomation_SimpleIvr/Program.cs
+++ b/CallAutomation_SimpleIvr/Program.cs
@@ -1,3 +1,4 @@
+using Azure;
using Azure.Communication;
using Azure.Communication.CallAutomation;
using Azure.Messaging;
@@ -8,6 +9,7 @@
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
var builder = WebApplication.CreateBuilder(args);
@@ -21,8 +23,20 @@
{
baseUri = builder.Configuration["BaseUri"];
}
-
+CommunicationIdentifierKind GetIdentifierKind(string participantnumber)
+{
+ //checks the identity type returns as string
+ return Regex.Match(participantnumber, Constants.userIdentityRegex, RegexOptions.IgnoreCase).Success ? CommunicationIdentifierKind.UserIdentity :
+ Regex.Match(participantnumber, Constants.phoneIdentityRegex, RegexOptions.IgnoreCase).Success ? CommunicationIdentifierKind.PhoneIdentity :
+ CommunicationIdentifierKind.UnknownIdentity;
+}
+int addedParticipantsCount = 0;
+int declineParticipantsCount = 0;
+var target = builder.Configuration["ParticipantToAdd"];
+string sourceCallerID = null;
+var Participants = target.Split(';');
var app = builder.Build();
+string caSourceId = null;
app.MapPost("/api/incomingCall", async (
[FromBody] EventGridEvent[] eventGridEvents,
ILogger logger) =>
@@ -44,11 +58,35 @@
}
}
var jsonObject = JsonNode.Parse(eventGridEvent.Data).AsObject();
- var callerId = (string)(jsonObject["from"]["rawId"]);
- var incomingCallContext = (string)jsonObject["incomingCallContext"];
- var callbackUri = new Uri(baseUri + $"/api/calls/{Guid.NewGuid()}?callerId={callerId}");
+ var targetId = (string)(jsonObject["to"]["rawId"]);
+ if(caSourceId == null)
+ {
+ caSourceId = builder.Configuration["TargetId"];
+ }
+ var rejectcall = Convert.ToBoolean(builder.Configuration["declinecall"]);
- AnswerCallResult answerCallResult = await client.AnswerCallAsync(incomingCallContext, callbackUri);
+ if (caSourceId.Contains(targetId) )
+ {
+ sourceCallerID = (string)(jsonObject["from"]["rawId"]);
+ var incomingCallContext = (string)jsonObject["incomingCallContext"];
+ var callbackUri = new Uri(baseUri + $"/api/calls/{Guid.NewGuid()}?callerId={sourceCallerID}");
+
+ if (rejectcall)
+ {
+ var response = client.RejectCallAsync(incomingCallContext);
+ logger.LogInformation($"{response.Result}");
+ }
+ else
+ {
+ AnswerCallResult answerCallResult = await client.AnswerCallAsync(incomingCallContext, callbackUri);
+ logger.LogInformation($"answerCall Response -------> source callerId {answerCallResult.CallConnectionProperties.SourceCallerIdNumber.RawId}");
+ logger.LogInformation($"targets ------->");
+ foreach (var target in answerCallResult.CallConnectionProperties.Targets)
+ {
+ logger.LogInformation($"{target.RawId}");
+ }
+ }
+ }
}
return Results.Ok();
});
@@ -59,11 +97,23 @@
[Required] string callerId,
ILogger logger) =>
{
- var audioPlayOptions = new PlayOptions() { OperationContext = "SimpleIVR", Loop = false };
+ //var audioPlayOptions = new PlayToAllOptions() { OperationContext = "SimpleIVR", Loop = false };
+
+ if (cloudEvents == null)
+ {
+ logger.LogWarning("cloudEvents parameter is null.");
+ return Results.BadRequest("cloudEvents parameter is null.");
+ }
foreach (var cloudEvent in cloudEvents)
{
+ logger.LogInformation($"Event received: {JsonConvert.SerializeObject(cloudEvent)}");
CallAutomationEventBase @event = CallAutomationEventParser.Parse(cloudEvent);
+ if (@event == null)
+ {
+ logger.LogWarning("cloudEvents param is null");
+ continue;
+ }
logger.LogInformation($"Event received: {JsonConvert.SerializeObject(@event)}");
var callConnection = client.GetCallConnection(@event.CallConnectionId);
@@ -76,9 +126,23 @@
if (@event is CallConnected)
{
+ addedParticipantsCount = 0;
+ declineParticipantsCount = 0;
+
+ logger.LogInformation($"CallConnected event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
+
+ var properties = callConnection.GetCallConnectionProperties();
+ logger.LogInformation($"call connection properties -------> SourceIdentity : {properties.Value.SourceCallerIdNumber.RawId}," +
+ $"CallConnection State : {properties.Value.CallConnectionState}");
+ logger.LogInformation($"targets ------->");
+ foreach (var target in properties.Value.Targets)
+ {
+ logger.LogInformation($"{target.RawId}");
+ }
+
// Start recognize prompt - play audio and recognize 1-digit DTMF input
var recognizeOptions =
- new CallMediaRecognizeDtmfOptions(CommunicationIdentifier.FromRawId(callerId), maxTonesToCollect: 1)
+ new CallMediaRecognizeDtmfOptions(CommunicationIdentifier.FromRawId(sourceCallerID), maxTonesToCollect: 1)
{
InterruptPrompt = true,
InterToneTimeout = TimeSpan.FromSeconds(10),
@@ -91,66 +155,254 @@
if (@event is RecognizeCompleted { OperationContext: "MainMenu" })
{
var recognizeCompleted = (RecognizeCompleted)@event;
+ DtmfResult collectedTones = (DtmfResult)recognizeCompleted.RecognizeResult;
- if (recognizeCompleted.CollectTonesResult.Tones[0] == DtmfTone.One)
+ if (collectedTones.Tones[0] == DtmfTone.One)
{
- PlaySource salesAudio = new FileSource(new Uri(baseUri + builder.Configuration["SalesAudio"]));
- await callMedia.PlayToAllAsync(salesAudio, audioPlayOptions);
+ var salesAudio = new FileSource(new Uri(baseUri + builder.Configuration["SalesAudio"]));
+ var audioPlayOptions = new PlayToAllOptions(salesAudio) { OperationContext = "SimpleIVR", Loop = false };
+ await callMedia.PlayToAllAsync(audioPlayOptions);
}
- else if (recognizeCompleted.CollectTonesResult.Tones[0] == DtmfTone.Two)
+ else if (collectedTones.Tones[0] == DtmfTone.Two)
{
- PlaySource marketingAudio = new FileSource(new Uri(baseUri + builder.Configuration["MarketingAudio"]));
- await callMedia.PlayToAllAsync(marketingAudio, audioPlayOptions);
+ var marketingAudio = new FileSource(new Uri(baseUri + builder.Configuration["MarketingAudio"])) ;
+ await callMedia.PlayToAllAsync(new PlayToAllOptions(marketingAudio)
+ { OperationContext = "SimpleIVR", Loop = false });
}
- else if (recognizeCompleted.CollectTonesResult.Tones[0] == DtmfTone.Three)
+ else if (collectedTones.Tones[0] == DtmfTone.Three)
{
- PlaySource customerCareAudio = new FileSource(new Uri(baseUri + builder.Configuration["CustomerCareAudio"]));
- await callMedia.PlayToAllAsync(customerCareAudio, audioPlayOptions);
+ var customerCareAudio = new FileSource(new Uri(baseUri + builder.Configuration["CustomerCareAudio"]));
+ await callMedia.PlayToAllAsync(new PlayToAllOptions(customerCareAudio)
+ { OperationContext = "CustomerCare", Loop = false });
}
- else if (recognizeCompleted.CollectTonesResult.Tones[0] == DtmfTone.Four)
+ else if (collectedTones.Tones[0] == DtmfTone.Four)
{
- PlaySource agentAudio = new FileSource(new Uri(baseUri + builder.Configuration["AgentAudio"]));
- audioPlayOptions.OperationContext = "AgentConnect";
- await callMedia.PlayToAllAsync(agentAudio, audioPlayOptions);
+ var agentAudio = new FileSource(new Uri(baseUri + builder.Configuration["AgentAudio"])) ;
+ await callMedia.PlayToAllAsync(new PlayToAllOptions(agentAudio )
+ { OperationContext = "AgentConnect", Loop = false });
+
}
- else if (recognizeCompleted.CollectTonesResult.Tones[0] == DtmfTone.Five)
+ else if (collectedTones.Tones[0] == DtmfTone.Five)
{
// Hangup for everyone
await callConnection.HangUpAsync(true);
+ logger.LogInformation($"Call disconnected event received call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
}
else
{
- PlaySource invalidAudio = new FileSource(new Uri(baseUri + builder.Configuration["InvalidAudio"]));
- await callMedia.PlayToAllAsync(invalidAudio, audioPlayOptions);
+ var invalidAudio = new FileSource(new Uri(baseUri + builder.Configuration["InvalidAudio"])) ;
+ await callMedia.PlayToAllAsync(new PlayToAllOptions(invalidAudio)
+ { OperationContext = "SimpleIVR", Loop = false });
}
}
if (@event is RecognizeFailed { OperationContext: "MainMenu" })
{
// play invalid audio
- await callMedia.PlayToAllAsync(new FileSource(new Uri(baseUri + builder.Configuration["InvalidAudio"])), audioPlayOptions);
+ var invalidAudio = new FileSource(new Uri(baseUri + builder.Configuration["InvalidAudio"])) ;
+ await callMedia.PlayToAllAsync(new PlayToAllOptions(invalidAudio)
+ { OperationContext = "SimpleIVR", Loop = false });
}
if (@event is PlayCompleted)
{
if (@event.OperationContext == "AgentConnect")
{
- var addParticipantOptions = new AddParticipantsOptions(new List()
+ foreach (var Participantindentity in Participants)
{
- new PhoneNumberIdentifier(builder.Configuration["ParticipantToAdd"])
- });
+ var identifierKind = GetIdentifierKind(Participantindentity);
+ CallInvite? callInvite = null;
+ if (!string.IsNullOrEmpty(Participantindentity))
+ {
+ if (identifierKind == CommunicationIdentifierKind.PhoneIdentity)
+ {
+ callInvite = new CallInvite(new PhoneNumberIdentifier(Participantindentity), new PhoneNumberIdentifier(builder.Configuration["ACSAlternatePhoneNumber"]));
+ }
+ if (identifierKind == CommunicationIdentifierKind.UserIdentity)
+ {
+ callInvite = new CallInvite(new CommunicationUserIdentifier(Participantindentity));
+ }
+ }
- addParticipantOptions.SourceCallerId = new PhoneNumberIdentifier(builder.Configuration["ACSAlternatePhoneNumber"]);
- await callConnection.AddParticipantsAsync(addParticipantOptions);
+ var addParticipantOptions = new AddParticipantOptions(callInvite);
+ var response = await callConnection.AddParticipantAsync(addParticipantOptions);
+
+ // Add the FileSource to the list
+ var fileSource = new FileSource(new Uri(baseUri + builder.Configuration["AddParticipant"]));
+
+ // Pass the list of PlaySources to PlayToAllAsync
+ await callMedia.PlayToAllAsync(new PlayToAllOptions(fileSource) { OperationContext = "addParticipant", Loop = false });
+
+ TimeSpan InterToneTimeout = TimeSpan.FromSeconds(20);
+ TimeSpan InitialSilenceTimeout = TimeSpan.FromSeconds(10);
+ logger.LogInformation($"AddParticipant event received for call connection id: {@event.CallConnectionId}" + $" Correlation id: {@event.CorrelationId}");
+ logger.LogInformation($"Addparticipant call: {response.Value.Participant}" + $" Addparticipant ID: {Participantindentity}"
+ + $" get response fron participant : {response.GetRawResponse()}" + $" call reason : {response.GetRawResponse().ReasonPhrase}");
+ }
}
- if (@event.OperationContext == "SimpleIVR")
+ else if (@event.OperationContext == "CustomerCare")
{
- await callConnection.HangUpAsync(true);
+ var customerCareIdentity = builder.Configuration["customerCareIdentity"];
+ var identifierKind = GetIdentifierKind(customerCareIdentity);
+ CommunicationIdentifier? callInvite = null;
+ if (!string.IsNullOrEmpty(customerCareIdentity))
+ {
+ if (identifierKind == CommunicationIdentifierKind.PhoneIdentity)
+ {
+ callInvite = new PhoneNumberIdentifier(customerCareIdentity);
+ }
+ if (identifierKind == CommunicationIdentifierKind.UserIdentity)
+ {
+ callInvite = new CommunicationUserIdentifier(customerCareIdentity);
+ }
+ }
+ var transferResponse = await callConnection.TransferCallToParticipantAsync(callInvite);
+ logger.LogInformation($"Call Transfered to : {customerCareIdentity}");
+ logger.LogInformation($"Transfer call result : {transferResponse.GetRawResponse()}");
}
}
+ if (@event is AddParticipantSucceeded addedParticipant)
+ {
+ addedParticipantsCount++;
+ logger.LogInformation($"participant added ---> {addedParticipant.Participant.RawId}");
+
+ if ((addedParticipantsCount + declineParticipantsCount) == Participants.Length)
+ {
+ await PerformHangUp(callConnection);
+ }
+ }
+ if (@event is AddParticipantFailed failedParticipant)
+ {
+ declineParticipantsCount++;
+ AddParticipantFailed addParticipantFailed = (AddParticipantFailed)@event;
+ logger.LogInformation($"Failed participant Reason -------> {failedParticipant.ResultInformation?.Message}");
+ if ((addedParticipantsCount + declineParticipantsCount) == Participants.Length)
+ {
+ await PerformHangUp(callConnection);
+ }
+ }
+ if (@event is RemoveParticipantSucceeded)
+ {
+ RemoveParticipantSucceeded RemoveParticipantSucceeded = (RemoveParticipantSucceeded)@event;
+ logger.LogInformation($"Remove Participant Succeeded RawId : {RemoveParticipantSucceeded.Participant.RawId}");
+ }
+ if (@event is RemoveParticipantFailed)
+ {
+ RemoveParticipantFailed removeParticipantFailed = (RemoveParticipantFailed)@event;
+ logger.LogInformation($"Remove participant failed RawId:{removeParticipantFailed.Participant.RawId}");
+ }
+ if (@event.OperationContext == "SimpleIVR")
+ {
+ await callConnection.HangUpAsync(true);
+ }
if (@event is PlayFailed)
{
logger.LogInformation($"PlayFailed Event: {JsonConvert.SerializeObject(@event)}");
await callConnection.HangUpAsync(true);
}
+ if (@event is ParticipantsUpdated updatedParticipantEvent)
+ {
+ logger.LogInformation($"Participant Updated Event Recieved");
+ logger.LogInformation("-------Updated Participant List----- ");
+ if(updatedParticipantEvent.Participants.Count == 2)
+ {
+ if (updatedParticipantEvent.Participants[0].Identifier.RawId == sourceCallerID)
+ {
+ caSourceId = updatedParticipantEvent.Participants[1].Identifier.RawId.Trim();
+ }
+ else if (updatedParticipantEvent.Participants[1].Identifier.RawId == sourceCallerID)
+ {
+ caSourceId = updatedParticipantEvent.Participants[0].Identifier.RawId.Trim();
+ }
+ }
+ foreach (var participant in updatedParticipantEvent.Participants)
+ {
+ logger.LogInformation($"Participant Raw ID : {participant.Identifier.RawId}, IsMuted : {participant.IsMuted}");
+ }
+ }
+ if (@event is CallTransferAccepted callTransferAccepted)
+ {
+ logger.LogInformation($"Transfer call accepted");
+ }
+ if (@event is CallTransferFailed callTransferFailed)
+ {
+ logger.LogInformation($"Transfer call Failed ----> {callTransferFailed.ResultInformation.Message}");
+ }
+
+ async Task PerformHangUp(CallConnection callConnection)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(10));
+ var participantlistResponse = await callConnection.GetParticipantsAsync();
+ logger.LogInformation("-------Participant List----- ");
+ foreach (var participant in participantlistResponse.Value)
+ {
+ try
+ {
+ logger.LogInformation($"{participant.Identifier.RawId}");
+ var response = callConnection.GetParticipant(participant.Identifier);
+ logger.LogInformation($"-------get participnat response : {response} ----- ");
+ }
+ catch (Exception ex)
+ {
+ logger.LogInformation($"------Error In GetParticipant() for participnat : {participant.Identifier.RawId} " +
+ $"-----> {ex.Message}");
+ }
+ }
+
+ logger.LogInformation($"Number of Participants : {participantlistResponse.Value.Count}");
+
+ int hangupScenario = Convert.ToInt32(builder.Configuration["HangUpScenarios"]);
+ if (hangupScenario == 1)
+ {
+ logger.LogInformation($"CA hanging up the call for everyone." + $"Information of Call:{callConnection.GetCallConnectionProperties()}");
+ var response = await callConnection.HangUpAsync(true);
+ logger.LogInformation($"Hang up response : {response}");
+ }
+ else if (hangupScenario == 2)
+ {
+ logger.LogInformation($"CA hang up the call." + $"Information of Call:{callConnection.GetCallConnectionProperties()}");
+ var response = await callConnection.HangUpAsync(false);
+ logger.LogInformation($"Hang up response : {response}");
+ }
+ else if (hangupScenario == 3 || hangupScenario == 4)
+ {
+ if (addedParticipantsCount == 0 && hangupScenario == 3)
+ {
+ logger.LogInformation($"No participants got addedd to remove");
+ }
+ else
+ {
+ logger.LogInformation($"Going to remove added partipants.");
+ List participantsToRemoveAll = (await callConnection.GetParticipantsAsync()).Value.ToList();
+ CommunicationIdentifier sourceParticipant = null;
+ foreach (CallParticipant participantToRemove in participantsToRemoveAll)
+ {
+ if (!string.IsNullOrEmpty(participantToRemove.Identifier.ToString()))
+ {
+ if (participantToRemove.Identifier.RawId.Contains(sourceCallerID) )
+ {
+ sourceParticipant = participantToRemove.Identifier;
+ }
+ else if (participantToRemove.Identifier.RawId.Contains(caSourceId))
+ {
+ caSourceId = participantToRemove.Identifier.RawId;
+ }
+ else
+ {
+ var RemoveParticipant = new RemoveParticipantOptions(participantToRemove.Identifier);
+ logger.LogInformation($"going to remove participant : {participantToRemove.Identifier.RawId}");
+ var removeParticipantResponse = await callConnection.RemoveParticipantAsync(RemoveParticipant);
+ logger.LogInformation($"Removing participant Response : {removeParticipantResponse.Value.ToString}");
+ }
+ }
+ }
+ if(hangupScenario == 4 && sourceParticipant != null)
+ {
+ logger.LogInformation($"going to remove participant : {sourceParticipant.RawId}");
+ var removeParticipantResponse = await callConnection.RemoveParticipantAsync(sourceParticipant);
+ logger.LogInformation($"Removing participant Response : {removeParticipantResponse.Value.ToString}");
+ }
+ }
+ }
+ }
}
return Results.Ok();
}).Produces(StatusCodes.Status200OK);
@@ -175,3 +427,15 @@
app.MapControllers();
app.Run();
+
+public enum CommunicationIdentifierKind
+{
+ PhoneIdentity,
+ UserIdentity,
+ UnknownIdentity
+}
+public class Constants
+{
+ public const string userIdentityRegex = @"8:acs:[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}_[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}";
+ public const string phoneIdentityRegex = @"^\+\d{10,14}$";
+}
diff --git a/CallAutomation_SimpleIvr/appsettings.json b/CallAutomation_SimpleIvr/appsettings.json
index dc405d74..805cea30 100644
--- a/CallAutomation_SimpleIvr/appsettings.json
+++ b/CallAutomation_SimpleIvr/appsettings.json
@@ -1,19 +1,30 @@
{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "ConnectionString": "%ConnectionString%",
- "ACSAlternatePhoneNumber": "%ACSAlternatePhoneNumber%", // get it from acs resouce
- "ParticipantToAdd": "%ParticipantToAdd%",
- "BaseUri": "%BaseUri%",
- "MainMenuAudio": "/audio/mainmenu.wav",
- "SalesAudio": "/audio/sales.wav",
- "MarketingAudio": "/audio/marketing.wav",
- "CustomerCareAudio": "/audio/customercare.wav",
- "AgentAudio": "/audio/agent.wav",
- "InvalidAudio": "/audio/invalid.wav",
- "AllowedHosts": "*"
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "ConnectionString": "%ACS Connection String%",
+ "ACSAlternatePhoneNumber": "%ACS Alternate Phone Number%", // get it from acs resouce
+ "TargetId": "% CA ID",
+ "ParticipantToAdd": "%Participant To Add%",
+ "customerCareIdentity": "%Identity on which we want to transfer the call%",
+ "BaseUri": "%Base Uri%",
+ "declinecall": false,
+ "MainMenuAudio": "/audio/mainmenu.wav",
+ "SalesAudio": "/audio/sales.wav",
+ "MarketingAudio": "/audio/marketing.wav",
+ "CustomerCareAudio": "/audio/customercare.wav",
+ "AddParticipant": "/audio/AddParticipant.wav",
+ "RemoveParticipant": "/audio/RemoveParticipant.wav",
+ "AgentAudio": "/audio/agent.wav",
+ "InvalidAudio": "/audio/invalid.wav",
+ // 1: Hangup for eveyone after adding participant
+ // 2: Hangup CA after adding participant
+ // 3: Remove addedd participants after adding them
+ // 4: Terminate al the participants in te call
+ "HangUpScenarios": 3,
+
+ "AllowedHosts": "*"
}
diff --git a/CallAutomation_SimpleIvr/audio/AddParticipant.wav b/CallAutomation_SimpleIvr/audio/AddParticipant.wav
new file mode 100644
index 00000000..7491fbd4
Binary files /dev/null and b/CallAutomation_SimpleIvr/audio/AddParticipant.wav differ
diff --git a/CallAutomation_SimpleIvr/audio/InvalidInputAudio.wav b/CallAutomation_SimpleIvr/audio/InvalidInputAudio.wav
new file mode 100644
index 00000000..3dfa6563
Binary files /dev/null and b/CallAutomation_SimpleIvr/audio/InvalidInputAudio.wav differ
diff --git a/CallAutomation_SimpleIvr/audio/RemoveParticipant.wav b/CallAutomation_SimpleIvr/audio/RemoveParticipant.wav
new file mode 100644
index 00000000..9b6f577d
Binary files /dev/null and b/CallAutomation_SimpleIvr/audio/RemoveParticipant.wav differ
diff --git a/CallAutomation_SimpleIvr/audio/TimedoutAudio.wav b/CallAutomation_SimpleIvr/audio/TimedoutAudio.wav
new file mode 100644
index 00000000..fb76eebc
Binary files /dev/null and b/CallAutomation_SimpleIvr/audio/TimedoutAudio.wav differ