Telnyx activities improvements including Queue Wait Time

This commit is contained in:
Sipke Schoorstra 2021-06-17 16:59:34 +02:00
parent 844589f966
commit ab151690b1
10 changed files with 79 additions and 15 deletions

View file

@ -120,7 +120,7 @@ namespace Elsa.Activities.Telnyx.Activities
if (CallBridgedPayloadA != null && CallBridgedPayloadB != null)
{
results.Add(Outcome(TelnyxOutcomeNames.Bridged));
results.Add(Outcome(TelnyxOutcomeNames.Bridged, new BridgedCallsOutput(CallBridgedPayloadA, CallBridgedPayloadB)));
}
else
{
@ -146,6 +146,8 @@ namespace Elsa.Activities.Telnyx.Activities
}
}
public record BridgedCallsOutput(CallBridgedPayload PayloadA, CallBridgedPayload PayloadB);
public static class BridgeCallsExtensions
{
public static ISetupActivity<BridgeCalls> WithCallControlIdA(this ISetupActivity<BridgeCalls> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlIdA, value);

View file

@ -98,7 +98,17 @@ namespace Elsa.Activities.Telnyx.Activities
get => GetState(() => Duration.FromSeconds(20));
set => SetState(value);
}
[ActivityInput(
Hint = "The maximum time to wait for anyone to pickup before giving up.",
SupportedSyntaxes = new[] {SyntaxNames.JavaScript, SyntaxNames.Liquid}
)]
public Duration? MaxQueueWaitTime
{
get => GetState<Duration>();
set => SetState(value);
}
[ActivityInput(
Hint = "Enables Answering Machine Detection.",
UIHint = ActivityInputUIHints.Dropdown,
@ -121,11 +131,25 @@ namespace Elsa.Activities.Telnyx.Activities
}
public override void Build(ICompositeActivityBuilder builder) =>
builder.Switch(cases =>
{
cases.Add(RingGroupStrategy.PrioritizedHunt.ToString(), () => Strategy == RingGroupStrategy.PrioritizedHunt, BuildPrioritizedHuntFlow);
cases.Add(RingGroupStrategy.RingAll.ToString(), () => Strategy == RingGroupStrategy.RingAll, BuildRingAllFlow);
});
builder
.StartWith<Fork>(fork => fork.WithBranches("Ring", "Queue Timeout"), fork =>
{
fork.When("Ring")
.While(true, iterate => iterate
.Switch(cases =>
{
cases.Add(RingGroupStrategy.PrioritizedHunt.ToString(), () => Strategy == RingGroupStrategy.PrioritizedHunt, BuildPrioritizedHuntFlow);
cases.Add(RingGroupStrategy.RingAll.ToString(), () => Strategy == RingGroupStrategy.RingAll, BuildRingAllFlow);
}));
fork.When("Queue Timeout")
.IfFalse(() => IsNullOrZero(MaxQueueWaitTime), whenTrue =>
{
whenTrue
.Timer(() => MaxQueueWaitTime!.Value)
.Finish(TelnyxOutcomeNames.NoResponse);
});
});
protected override async ValueTask OnExitAsync(ActivityExecutionContext context, object? output)
{
@ -163,11 +187,11 @@ namespace Elsa.Activities.Telnyx.Activities
.When(TelnyxOutcomeNames.Answered)
.Then<BridgeCalls>(bridgeCalls =>
bridgeCalls.When(TelnyxOutcomeNames.Bridged)
.Finish(TelnyxOutcomeNames.Connected));
.Finish(activity => activity.WithOutcome(TelnyxOutcomeNames.Connected).WithOutput(context => context.GetInput<BridgedCallsOutput>())));
}
)
)
.Finish(TelnyxOutcomeNames.NoResponse);
.IfTrue(() => IsNullOrZero(MaxQueueWaitTime), whenTrue => whenTrue.Finish(TelnyxOutcomeNames.NoResponse));
private void BuildRingAllFlow(IOutcomeBuilder builder) =>
builder
@ -181,12 +205,12 @@ namespace Elsa.Activities.Telnyx.Activities
.WithCallControlIdA(() => CallControlId)
.WithCallControlIdB(() => CallAnsweredPayload!.CallControlId), bridge => bridge
.When(TelnyxOutcomeNames.Bridged)
.Finish(TelnyxOutcomeNames.Connected));
.Finish(activity => activity.WithOutcome(TelnyxOutcomeNames.Connected).WithOutput(context => context.GetInput<BridgedCallsOutput>())));
fork
.When("Timeout")
.StartIn(() => RingTime)
.Finish(TelnyxOutcomeNames.NoResponse);
.IfTrue(() => IsNullOrZero(MaxQueueWaitTime), whenTrue => whenTrue.Finish(TelnyxOutcomeNames.NoResponse));
fork
.When("Dial Everyone")
@ -221,6 +245,8 @@ namespace Elsa.Activities.Telnyx.Activities
return resolvedExtension?.Number ?? extension;
}
private static bool IsNullOrZero(Duration? duration) => duration == null || duration.Value == Duration.Zero;
object IActivityPropertyDefaultValueProvider.GetDefaultValue(PropertyInfo property) => Duration.FromSeconds(20);
}
}

View file

@ -122,6 +122,9 @@ namespace Elsa.Activities.Telnyx.Activities
[ActivityOutput] public DialResponse? DialResponse { get; set; }
[ActivityOutput] public CallPayload? Output { get; set; }
[ActivityOutput] public CallAnsweredPayload? AnsweredOutput { get; set; }
[ActivityOutput] public CallHangupPayload? HangupOutput { get; set; }
[ActivityOutput] public CallInitiatedPayload? InitiatedOutput { get; set; }
protected override async ValueTask<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context)
{
@ -140,13 +143,31 @@ namespace Elsa.Activities.Telnyx.Activities
return payload switch
{
CallAnsweredPayload => Outcome(TelnyxOutcomeNames.Answered),
CallHangupPayload => Outcome(TelnyxOutcomeNames.Hangup),
CallInitiatedPayload => Combine(Outcome(TelnyxOutcomeNames.CallInitiated), Suspend()),
CallAnsweredPayload answeredPayload => AnsweredOutcome(answeredPayload),
CallHangupPayload hangupPayload => HangupOutcome(hangupPayload),
CallInitiatedPayload initiatedPayload => Combine(InitiatedOutcome(initiatedPayload), Suspend()),
_ => throw new ArgumentOutOfRangeException(nameof(payload))
};
}
private IActivityExecutionResult AnsweredOutcome(CallAnsweredPayload payload)
{
AnsweredOutput = payload;
return Outcome(TelnyxOutcomeNames.Answered, payload);
}
private IActivityExecutionResult HangupOutcome(CallHangupPayload payload)
{
HangupOutput = payload;
return Outcome(TelnyxOutcomeNames.Hangup, payload);
}
private IActivityExecutionResult InitiatedOutcome(CallInitiatedPayload payload)
{
InitiatedOutput = payload;
return Outcome(TelnyxOutcomeNames.CallInitiated, payload);
}
private async Task<DialResponse> DialAsync(ActivityExecutionContext context)
{
var connectionId = string.IsNullOrWhiteSpace(ConnectionId) ? _telnyxOptions.CallControlAppId : ConnectionId;

View file

@ -32,8 +32,12 @@ namespace Elsa.Activities.Telnyx.Activities
context.SetCallControlId(callPayload.CallControlId);
if (callPayload is CallInitiatedPayload callInitiatedPayload)
{
if (!context.HasFromNumber())
context.SetFromNumber(callInitiatedPayload.To);
context.SetCallerNumber(callInitiatedPayload.From);
}
}
Model = webhookModel;

View file

@ -7,6 +7,7 @@ namespace Elsa.Activities.Telnyx.Extensions
{
private const string CallControlIdVariableName = "TelnyxCallControlId";
private const string FromNumberVariableName = "TelnyxFromNumber";
private const string CallerNumberVariableName = "TelnyxCallerNumber";
/// <summary>
/// Returns the control ID of the active call session in the workflow if the specified call control ID is null or empty.
@ -50,7 +51,10 @@ namespace Elsa.Activities.Telnyx.Extensions
/// <summary>
/// Sets a workflow variable with the specified call control ID value.
/// </summary>
public static void SetFromNumber(this ActivityExecutionContext context, string fromNumber) => context.SetVariable(FromNumberVariableName, fromNumber);
public static void SetFromNumber(this ActivityExecutionContext context, string number) => context.SetVariable(FromNumberVariableName, number);
public static void SetCallerNumber(this ActivityExecutionContext context, string number) => context.SetVariable(CallerNumberVariableName, number);
public static string? GetCallerNumber(this ActivityExecutionContext context) => context.GetVariable<string>(CallerNumberVariableName);
public static bool HasFromNumber(this ActivityExecutionContext context) => context.HasVariable(FromNumberVariableName);
}

View file

@ -18,5 +18,6 @@ namespace Elsa.Activities.Telnyx.Models
public string? FirstName { get; init; }
public string? LastName { get; init; }
public string? Email { get; init; }
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Elsa.Activities.Telnyx.Models;
using Elsa.Activities.Telnyx.Webhooks.Models;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
using Elsa.Scripting.JavaScript.Services;
@ -13,6 +14,7 @@ namespace Elsa.Activities.Telnyx.Scripting.JavaScript
return new[]
{
typeof(TelnyxWebhook),
typeof(Extension),
typeof(CallAnsweredPayload),
typeof(CallBridgedPayload),

View file

@ -13,5 +13,7 @@ namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
public string SipHangupCause { get; init; } = default!;
public string HangupSource { get; init; } = default!;
public string HangupCause { get; init; } = default!;
public string From { get; set; } = default!;
public string To { get; set; } = default!;
}
}

View file

@ -10,5 +10,6 @@ namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
public string Direction { get; init; } = default!;
public string State { get; init; } = default!;
public string To { get; init; } = default!;
public string From { get; init; } = default!;
}
}

View file

@ -13,5 +13,6 @@ namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
public CallRecordingUrls RecordingUrls { get; set; } = default!;
public Instant RecordingEndedAt { get; set; }
public Instant RecordingStartedAt { get; set; }
public Duration Duration => RecordingEndedAt - RecordingStartedAt;
}
}