Implement ring group transfer for prioritized hunt strategy
This commit is contained in:
parent
ae27cbb301
commit
4807ace13c
|
|
@ -1,8 +1,7 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Client.Models;
|
||||
using Elsa.Activities.Telnyx.Client.Services;
|
||||
using Elsa.Activities.Telnyx.Extensions;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
|
|
@ -52,7 +51,7 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
|
||||
private async ValueTask AnswerCallAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
|
||||
var callControlId = context.GetCallControlId(CallControlId);
|
||||
var request = new AnswerCallRequest(BillingGroupId, ClientState, CommandId, WebhookUrl, WebhookUrlMethod);
|
||||
await _telnyxClient.Calls.AnswerCallAsync(callControlId, request, context.CancellationToken);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,13 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
set => SetState(value);
|
||||
}
|
||||
|
||||
[ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call.", Category = PropertyCategories.Advanced)]
|
||||
public string? CallControlId
|
||||
{
|
||||
get => GetState<string>();
|
||||
set => SetState(value);
|
||||
}
|
||||
|
||||
[ActivityProperty]
|
||||
public RingGroupStrategy Strategy
|
||||
{
|
||||
|
|
@ -69,7 +76,8 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
builder
|
||||
.ForEach(() => Extensions, iterate => iterate
|
||||
.Then<ResolveExtension>(a => a.WithExtension(context => context.GetInput<string>()))
|
||||
.Then<Dial>(a => a
|
||||
.Then<TransferCall>(a => a
|
||||
.WithCallControlAppId(() => CallControlId)
|
||||
.WithTo(context => context.GetInput<string>())
|
||||
.WithTimeoutSecs(() => (int) RingTime.TotalSeconds)
|
||||
.WithFrom(() => From)
|
||||
|
|
@ -107,7 +115,8 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
.When("Dial Everyone")
|
||||
.ParallelForEach(() => Extensions, iterate => iterate
|
||||
.Then<ResolveExtension>(a => a.WithExtension(context => context.GetInput<string>()))
|
||||
.Then<Dial>(a => a
|
||||
.Then<TransferCall>(a => a
|
||||
.WithCallControlAppId(() => CallControlId)
|
||||
.WithTo(context => context.GetInput<string>())
|
||||
.WithTimeoutSecs(() => (int) RingTime.TotalSeconds)
|
||||
.WithFrom(() => From)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Client.Models;
|
||||
using Elsa.Activities.Telnyx.Client.Services;
|
||||
using Elsa.Activities.Telnyx.Options;
|
||||
using Elsa.Activities.Telnyx.Extensions;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Builders;
|
||||
|
|
@ -25,19 +24,17 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
public class Dial : Activity
|
||||
{
|
||||
private readonly ITelnyxClient _telnyxClient;
|
||||
private readonly TelnyxOptions _telnyxOptions;
|
||||
|
||||
public Dial(ITelnyxClient telnyxClient, TelnyxOptions telnyxOptions)
|
||||
public Dial(ITelnyxClient telnyxClient)
|
||||
{
|
||||
_telnyxClient = telnyxClient;
|
||||
_telnyxOptions = telnyxOptions;
|
||||
}
|
||||
|
||||
[ActivityProperty(Label = "Connection ID", Hint = "The ID of the Call Control App (formerly ID of the connection) to be used when dialing the destination.", Category = PropertyCategories.Advanced)]
|
||||
public string CallControlAppId { get; set; } = default!;
|
||||
[ActivityProperty(Label = "Call Control ID", Hint = "The ID of the Call Control App (formerly ID of the connection) to be used when dialing the destination.", Category = PropertyCategories.Advanced)]
|
||||
public string CallControlId { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(Label = "To", Hint = "The DID or SIP URI to dial out and bridge to the given call.")]
|
||||
public string To { get; set; }
|
||||
public string To { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(Label = "From",
|
||||
Hint = "The 'from' number to be used as the caller id presented to the destination ('To' number). The number should be in +E164 format. This attribute will default to the 'From' number of the original call if omitted.")]
|
||||
|
|
@ -90,10 +87,10 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
|
||||
private async ValueTask DialAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var connectionId = CallControlAppId is not null and not "" ? CallControlAppId : _telnyxOptions.CallControlAppId ?? throw new Exception("No Call Control ID specified and no default value configured");
|
||||
var callControlId = context.GetCallControlId(CallControlId);
|
||||
|
||||
var request = new DialRequest(
|
||||
connectionId,
|
||||
callControlId,
|
||||
To,
|
||||
From,
|
||||
FromDisplayName,
|
||||
|
|
@ -123,11 +120,11 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
|
||||
public static class DialExtensions
|
||||
{
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlAppId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CallControlAppId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CallControlAppId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<string?> value) => setup.Set(x => x.CallControlAppId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, string? value) => setup.Set(x => x.CallControlAppId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<string?> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, string? value) => setup.Set(x => x.CallControlId, value);
|
||||
|
||||
public static ISetupActivity<Dial> WithTo(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.To, value);
|
||||
public static ISetupActivity<Dial> WithTo(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.To, value);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Client.Models;
|
||||
using Elsa.Activities.Telnyx.Client.Services;
|
||||
using Elsa.Activities.Telnyx.Extensions;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
|
|
@ -86,11 +86,11 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
EmptyToNull(ValidDigits)
|
||||
);
|
||||
|
||||
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
|
||||
var callControlId = context.GetCallControlId(CallControlId);
|
||||
|
||||
try
|
||||
{
|
||||
await _telnyxClient.Calls.GatherUsingAudiAsync(callControlId, request, context.CancellationToken);
|
||||
await _telnyxClient.Calls.GatherUsingAudioAsync(callControlId, request, context.CancellationToken);
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Client.Models;
|
||||
using Elsa.Activities.Telnyx.Client.Services;
|
||||
using Elsa.Activities.Telnyx.Extensions;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
|
|
@ -45,7 +44,7 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
|
||||
private async ValueTask HangupCallAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
|
||||
var callControlId = context.GetCallControlId(CallControlId);
|
||||
var request = new HangupCallRequest(ClientState, CommandId);
|
||||
|
||||
try
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Services;
|
||||
using Elsa.ActivityResults;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Client.Models;
|
||||
using Elsa.Activities.Telnyx.Client.Services;
|
||||
using Elsa.Activities.Telnyx.Extensions;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Design;
|
||||
using Elsa.Exceptions;
|
||||
using Elsa.Services;
|
||||
|
|
@ -33,7 +34,7 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
public string CallControlId { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(Label = "To", Hint = "The DID or SIP URI to dial out and bridge to the given call.")]
|
||||
public string To { get; set; }
|
||||
public string To { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(Label = "From",
|
||||
Hint = "The 'from' number to be used as the caller id presented to the destination ('To' number). The number should be in +E164 format. This attribute will default to the 'From' number of the original call if omitted.")]
|
||||
|
|
@ -111,7 +112,7 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
WebhookUrlMethod
|
||||
);
|
||||
|
||||
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
|
||||
var callControlId = context.GetCallControlId(CallControlId);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -123,4 +124,97 @@ namespace Elsa.Activities.Telnyx.Activities
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransferCallExtensions
|
||||
{
|
||||
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.CallControlId, value);
|
||||
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.CallControlId, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.To, value);
|
||||
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.To, value);
|
||||
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.To, value);
|
||||
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.To, value);
|
||||
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.To, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.From, value);
|
||||
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.From, value);
|
||||
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.From, value);
|
||||
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.From, value);
|
||||
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.From, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.FromDisplayName, value);
|
||||
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.FromDisplayName, value);
|
||||
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.FromDisplayName, value);
|
||||
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.FromDisplayName, value);
|
||||
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.FromDisplayName, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.AnsweringMachineDetection, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.AnsweringMachineDetection, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.AnsweringMachineDetection, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.AnsweringMachineDetection, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.AnsweringMachineDetection, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<AnsweringMachineConfig?>> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, AnsweringMachineConfig?> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<ValueTask<AnsweringMachineConfig?>> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<AnsweringMachineConfig?> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
|
||||
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, AnsweringMachineConfig? value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CommandId, value);
|
||||
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CommandId, value);
|
||||
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CommandId, value);
|
||||
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.CommandId, value);
|
||||
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.CommandId, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.ClientState, value);
|
||||
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.ClientState, value);
|
||||
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.ClientState, value);
|
||||
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.ClientState, value);
|
||||
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.ClientState, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<IList<Header>?>> value) => setup.Set(x => x.CustomHeaders, value);
|
||||
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, IList<Header>?> value) => setup.Set(x => x.CustomHeaders, value);
|
||||
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<ValueTask<IList<Header>?>> value) => setup.Set(x => x.CustomHeaders, value);
|
||||
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<IList<Header>?> value) => setup.Set(x => x.CustomHeaders, value);
|
||||
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, IList<Header>? value) => setup.Set(x => x.CustomHeaders, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.SipAuthUsername, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.SipAuthUsername, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.SipAuthUsername, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.SipAuthUsername, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.SipAuthUsername, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.SipAuthPassword, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.SipAuthPassword, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.SipAuthPassword, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.SipAuthPassword, value);
|
||||
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.SipAuthPassword, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<int?>> value) => setup.Set(x => x.TimeLimitSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, int?> value) => setup.Set(x => x.TimeLimitSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<ValueTask<int?>> value) => setup.Set(x => x.TimeLimitSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<int?> value) => setup.Set(x => x.TimeLimitSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, int? value) => setup.Set(x => x.TimeLimitSecs, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<int?>> value) => setup.Set(x => x.TimeoutSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, int?> value) => setup.Set(x => x.TimeoutSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<ValueTask<int?>> value) => setup.Set(x => x.TimeoutSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<int?> value) => setup.Set(x => x.TimeoutSecs, value);
|
||||
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, int? value) => setup.Set(x => x.TimeoutSecs, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.WebhookUrl, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.WebhookUrl, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.WebhookUrl, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.WebhookUrl, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.WebhookUrl, value);
|
||||
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.WebhookUrlMethod, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.WebhookUrlMethod, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.WebhookUrlMethod, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.WebhookUrlMethod, value);
|
||||
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.WebhookUrlMethod, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,18 +7,28 @@ using System.Threading.Tasks;
|
|||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Models;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Services;
|
||||
using Elsa.ActivityProviders;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Metadata;
|
||||
using Elsa.Services.Models;
|
||||
using Microsoft.AspNetCore.Authorization.Policy;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.ActivityTypes
|
||||
{
|
||||
public class NotificationActivityTypeProvider : IActivityTypeProvider
|
||||
internal class NotificationActivityTypeProvider : IActivityTypeProvider
|
||||
{
|
||||
private readonly IWebhookFilterService _webhookFilterService;
|
||||
|
||||
public NotificationActivityTypeProvider(IWebhookFilterService webhookFilterService)
|
||||
{
|
||||
_webhookFilterService = webhookFilterService;
|
||||
}
|
||||
|
||||
public const string NotificationAttribute = "TelnyxNotification";
|
||||
public const string EventTypeAttribute = "EventType";
|
||||
|
||||
|
||||
public ValueTask<IEnumerable<ActivityType>> GetActivityTypesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var activityTypes = GetActivityTypes();
|
||||
|
|
@ -27,20 +37,31 @@ namespace Elsa.Activities.Telnyx.ActivityTypes
|
|||
|
||||
private IEnumerable<ActivityType> GetActivityTypes()
|
||||
{
|
||||
var types = GetType().Assembly.GetAllWithBaseClass<Payload>().Where(x => x.GetCustomAttribute<PayloadAttribute>() != null);
|
||||
return types.Select(CreateWebhookActivityType);
|
||||
var payloadTypes = GetType().Assembly.GetAllWithBaseClass<Payload>().Where(x => x.GetCustomAttribute<WebhookAttribute>() != null).ToList();
|
||||
var activityTypes = payloadTypes.Select(CreateWebhookActivityType).ToList();
|
||||
|
||||
// Add variations on the same webhooks. The webhook filters will conditionally select the appropriate one.
|
||||
var hangupWebhookAttribute = payloadTypes.First(x => x == typeof(CallHangupPayload)).GetCustomAttribute<WebhookAttribute>()!;
|
||||
activityTypes.Add(CreateWebhookActivityType(new WebhookAttribute(hangupWebhookAttribute.EventType, "OriginatorCallHangup", "Originator Call Hangup", "Triggered when an incoming call was hangup by the originator.")));
|
||||
|
||||
return activityTypes;
|
||||
}
|
||||
|
||||
|
||||
private static ActivityType CreateWebhookActivityType(Type payloadType)
|
||||
{
|
||||
var payloadAttribute = payloadType.GetCustomAttribute<PayloadAttribute>();
|
||||
var webhookAttribute = payloadType.GetCustomAttribute<WebhookAttribute>();
|
||||
|
||||
if (payloadAttribute == null)
|
||||
throw new InvalidOperationException($"Make sure that the payload type is annotated with the ${nameof(PayloadAttribute)} attribute");
|
||||
if (webhookAttribute == null)
|
||||
throw new InvalidOperationException($"Make sure that the payload type is annotated with the ${nameof(WebhookAttribute)} attribute");
|
||||
|
||||
var typeName = payloadAttribute.ActivityType;
|
||||
var displayName = payloadAttribute.DisplayName;
|
||||
var description = payloadAttribute.Description;
|
||||
return CreateWebhookActivityType(webhookAttribute);
|
||||
}
|
||||
|
||||
private static ActivityType CreateWebhookActivityType(WebhookAttribute webhookAttribute)
|
||||
{
|
||||
var typeName = webhookAttribute.ActivityType;
|
||||
var displayName = webhookAttribute.DisplayName;
|
||||
var description = webhookAttribute.Description;
|
||||
|
||||
return new ActivityType
|
||||
{
|
||||
|
|
@ -59,18 +80,24 @@ namespace Elsa.Activities.Telnyx.ActivityTypes
|
|||
Attributes = new Dictionary<string, object>
|
||||
{
|
||||
[NotificationAttribute] = true,
|
||||
[EventTypeAttribute] = payloadAttribute.EventType
|
||||
[EventTypeAttribute] = webhookAttribute.EventType
|
||||
},
|
||||
CanExecuteAsync = _ => new ValueTask<bool>(true),
|
||||
ExecuteAsync = context => context.WorkflowExecutionContext.IsFirstPass ? ExecuteInternal(context) : new ValueTask<IActivityExecutionResult>(new SuspendResult()),
|
||||
ResumeAsync = ExecuteInternal,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static ValueTask<IActivityExecutionResult> ExecuteInternal(ActivityExecutionContext context)
|
||||
{
|
||||
var webhook = (TelnyxWebhook) context.Input!;
|
||||
context.WorkflowExecutionContext.CorrelationId ??= (webhook.Data.Payload as ICorrelationId)?.CorrelationId;
|
||||
var webhook = (TelnyxWebhook) context.Input!;
|
||||
|
||||
if (webhook.Data.Payload is CallPayload callPayload)
|
||||
{
|
||||
context.WorkflowExecutionContext.CorrelationId ??= callPayload.CallSessionId;
|
||||
context.SetVariable("CallControlId", callPayload.CallControlId);
|
||||
}
|
||||
|
||||
return new(new CombinedResult(new OutputResult(context.Input), new DoneResult()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,6 @@ namespace Elsa.Activities.Telnyx.Client.Services
|
|||
Task HangupCallAsync(string callControlId, [Body]HangupCallRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Post("/v2/calls/{callControlId}/actions/gather_using_audio")]
|
||||
Task GatherUsingAudiAsync(string callControlId, [Body]GatherUsingAudioRequest request, CancellationToken cancellationToken = default);
|
||||
Task GatherUsingAudioAsync(string callControlId, [Body]GatherUsingAudioRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using Elsa.Activities.Telnyx.Options;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Extensions
|
||||
{
|
||||
public static class ActivityExecutionContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the default call control app ID if the specified value is null or empty.
|
||||
/// </summary>
|
||||
public static string GetCallControlId(this ActivityExecutionContext context, string? callControlId)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(callControlId))
|
||||
return callControlId;
|
||||
|
||||
callControlId = context.GetVariable<string>("CallControlId");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(callControlId))
|
||||
return callControlId;
|
||||
|
||||
var options = context.GetService<TelnyxOptions>();
|
||||
callControlId = options.CallControlAppId;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(callControlId))
|
||||
return callControlId;
|
||||
|
||||
throw new Exception("No Call Control ID specified and no default value configured");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ using Elsa.Activities.Telnyx.ActivityTypes;
|
|||
using Elsa.Activities.Telnyx.Bookmarks;
|
||||
using Elsa.Activities.Telnyx.Client.Services;
|
||||
using Elsa.Activities.Telnyx.Options;
|
||||
using Elsa.Activities.Telnyx.Services;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Consumers;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Events;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Filters;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
|
@ -46,6 +46,9 @@ namespace Elsa.Activities.Telnyx.Extensions
|
|||
.AddBookmarkProvider<NotificationBookmarkProvider>()
|
||||
.AddNotificationHandlers(typeof(TriggerWorkflows))
|
||||
.AddScoped<IWebhookHandler, WebhookHandler>()
|
||||
.AddSingleton<IWebhookFilterService, WebhookFilterService>()
|
||||
.AddSingleton<IWebhookFilter, AttributeBasedWebhookFilter>()
|
||||
.AddSingleton<IWebhookFilter, HangupWebhookFilter>()
|
||||
.AddScoped(telnyxOptions.ExtensionProviderFactory);
|
||||
|
||||
// Telnyx API Client.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
namespace Elsa.Activities.Telnyx.Webhooks.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class PayloadAttribute : Attribute
|
||||
public class WebhookAttribute : Attribute
|
||||
{
|
||||
public PayloadAttribute(string eventType, string activityType, string displayName, string description)
|
||||
public WebhookAttribute(string eventType, string activityType, string displayName, string description)
|
||||
{
|
||||
EventType = eventType;
|
||||
ActivityType = activityType;
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Bookmarks;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Events;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Services;
|
||||
using Elsa.DistributedLock;
|
||||
using Elsa.Models;
|
||||
using Elsa.Persistence;
|
||||
|
|
@ -24,14 +28,22 @@ namespace Elsa.Activities.Telnyx.Webhooks.Consumers
|
|||
private readonly IWorkflowInstanceStore _workflowInstanceStore;
|
||||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||
private readonly ICommandSender _commandSender;
|
||||
private readonly IWebhookFilterService _webhookFilterService;
|
||||
private readonly ILogger<TriggerWorkflows> _logger;
|
||||
|
||||
public TriggerWorkflows(IWorkflowRunner workflowRunner, IWorkflowInstanceStore workflowInstanceStore, IDistributedLockProvider distributedLockProvider, ICommandSender commandSender, ILogger<TriggerWorkflows> logger)
|
||||
public TriggerWorkflows(
|
||||
IWorkflowRunner workflowRunner,
|
||||
IWorkflowInstanceStore workflowInstanceStore,
|
||||
IDistributedLockProvider distributedLockProvider,
|
||||
ICommandSender commandSender,
|
||||
IWebhookFilterService webhookFilterService,
|
||||
ILogger<TriggerWorkflows> logger)
|
||||
{
|
||||
_workflowRunner = workflowRunner;
|
||||
_workflowInstanceStore = workflowInstanceStore;
|
||||
_distributedLockProvider = distributedLockProvider;
|
||||
_commandSender = commandSender;
|
||||
_webhookFilterService = webhookFilterService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
|
@ -40,18 +52,17 @@ namespace Elsa.Activities.Telnyx.Webhooks.Consumers
|
|||
var webhook = message.Webhook;
|
||||
var eventType = webhook.Data.EventType;
|
||||
var payload = message.Webhook.Data.Payload;
|
||||
var payloadAttribute = payload.GetType().GetCustomAttribute<PayloadAttribute>();
|
||||
var activityType = _webhookFilterService.GetActivityTypeName(payload);
|
||||
|
||||
if (payloadAttribute == null)
|
||||
if (activityType == null)
|
||||
{
|
||||
_logger.LogWarning("The received payload type '{PayloadTypeName}' does not have a PayloadAttribute containing metadata, which means it's an unsupported event", payload.GetType().Name);
|
||||
_logger.LogWarning("The received event '{EventType}' is an unsupported event", webhook.Data.EventType);
|
||||
return;
|
||||
}
|
||||
|
||||
var activityType = payloadAttribute.ActivityType;
|
||||
var correlationId = (payload as ICorrelationId)?.CorrelationId;
|
||||
var correlationId = GetCorrelationId(payload);
|
||||
var lockKey = $"telnyx:trigger-workflows:correlation-{correlationId}";
|
||||
|
||||
|
||||
if (!await _distributedLockProvider.AcquireLockAsync(lockKey))
|
||||
{
|
||||
_logger.LogDebug("Lock {LockKey} already taken", lockKey);
|
||||
|
|
@ -75,5 +86,13 @@ namespace Elsa.Activities.Telnyx.Webhooks.Consumers
|
|||
await _distributedLockProvider.ReleaseLockAsync(lockKey);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCorrelationId(Payload payload)
|
||||
{
|
||||
if (payload is CallPayload callPayload)
|
||||
return callPayload.CallSessionId;
|
||||
|
||||
throw new NotSupportedException($"The received payload type {payload.GetType().Name} is not supported yet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ namespace Elsa.Activities.Telnyx.Webhooks.Converters
|
|||
|
||||
var query =
|
||||
from payloadType in payloadTypes
|
||||
let payloadAttribute = payloadType.GetCustomAttribute<PayloadAttribute>()
|
||||
let payloadAttribute = payloadType.GetCustomAttribute<WebhookAttribute>()
|
||||
where payloadAttribute != null
|
||||
select (payloadType, payloadAttribute);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
using System.Reflection;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Services;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Filters
|
||||
{
|
||||
public class AttributeBasedWebhookFilter : IWebhookFilter
|
||||
{
|
||||
public int Priority => 0;
|
||||
public bool CanHandlePayload(Payload payload) => payload.GetType().GetCustomAttribute<WebhookAttribute>() != null;
|
||||
public string GetActivityTypeName(Payload payload) => payload.GetType().GetCustomAttribute<WebhookAttribute>()!.ActivityType;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Services;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Filters
|
||||
{
|
||||
public class HangupWebhookFilter : IWebhookFilter
|
||||
{
|
||||
public int Priority => 1;
|
||||
|
||||
public bool CanHandlePayload(Payload payload) => payload is CallHangupPayload;
|
||||
|
||||
public string GetActivityTypeName(Payload payload)
|
||||
{
|
||||
var hangupPayload = (CallHangupPayload) payload;
|
||||
var attribute = payload.GetType().GetCustomAttribute<WebhookAttribute>()!;
|
||||
|
||||
if (hangupPayload.HangupCause != "originator_cancel")
|
||||
return attribute.ActivityType;
|
||||
|
||||
return "OriginatorCallHangup";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract
|
||||
{
|
||||
public interface ICorrelationId
|
||||
{
|
||||
string CorrelationId { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call Answered", "Triggered when an incoming call is answered.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call Answered", "Triggered when an incoming call is answered.")]
|
||||
public sealed record CallAnsweredPayload : CallPayload
|
||||
{
|
||||
public const string EventType = "call.answered";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call DTMF Received", "Triggered when DTMF input is received.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call DTMF Received", "Triggered when DTMF input is received.")]
|
||||
public sealed record CallDtmfReceivedPayload : CallPayload
|
||||
{
|
||||
public const string EventType = "call.dtmf.received";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call Gather Ended", "Triggered when an call gather has ended.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call Gather Ended", "Triggered when an call gather has ended.")]
|
||||
public sealed record CallGatherEndedPayload : CallPayload
|
||||
{
|
||||
public const string EventType = "call.gather.ended";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using NodaTime;
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call Hangup", "Triggered when an incoming call was hangup.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call Hangup", "Triggered when an incoming call was hangup.")]
|
||||
public sealed record CallHangupPayload : CallPayload
|
||||
{
|
||||
public const string EventType = "call.hangup";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call Initiated", "Triggered when an incoming call is received.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call Initiated", "Triggered when an incoming call is received.")]
|
||||
public sealed record CallInitiatedPayload : CallPayload
|
||||
{
|
||||
public const string EventType = "call.initiated";
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
public abstract record CallPayload : Payload, ICorrelationId
|
||||
public abstract record CallPayload : Payload
|
||||
{
|
||||
public string CallControlId { get; init; } = default!;
|
||||
public string CallLegId { get; init; } = default!;
|
||||
public string CallSessionId { get; init; } = default!;
|
||||
public string ConnectionId { get; init; } = default!;
|
||||
string ICorrelationId.CorrelationId => CallControlId;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call Playback Ended", "Triggered when an audio playback has ended.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call Playback Ended", "Triggered when an audio playback has ended.")]
|
||||
public sealed record CallPlaybackEndedPayload : CallPlayback
|
||||
{
|
||||
public const string EventType = "call.playback.ended";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Payload(EventType, ActivityTypeName, "Call Playback Started", "Triggered when an audio playback has started.")]
|
||||
[Webhook(EventType, ActivityTypeName, "Call Playback Started", "Triggered when an audio playback has started.")]
|
||||
public sealed record CallPlaybackStartedPayload : CallPlayback
|
||||
{
|
||||
public const string EventType = "call.playback.started";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Services
|
||||
{
|
||||
internal interface IWebhookFilter
|
||||
{
|
||||
int Priority { get; }
|
||||
bool CanHandlePayload(Payload payload);
|
||||
string GetActivityTypeName(Payload payload);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Services
|
||||
{
|
||||
internal interface IWebhookFilterService
|
||||
{
|
||||
string? GetActivityTypeName(Payload payload);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Services
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Services
|
||||
{
|
||||
internal class WebhookFilterService : IWebhookFilterService
|
||||
{
|
||||
private readonly IEnumerable<IWebhookFilter> _filters;
|
||||
|
||||
public WebhookFilterService(IEnumerable<IWebhookFilter> filters)
|
||||
{
|
||||
_filters = filters.OrderByDescending(x => x.Priority).ToList();
|
||||
}
|
||||
|
||||
public string? GetActivityTypeName(Payload payload)
|
||||
{
|
||||
var filter = _filters.FirstOrDefault(x => x.CanHandlePayload(payload));
|
||||
return filter?.GetActivityTypeName(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ namespace Elsa.Activities.ControlFlow
|
|||
var collection = Items.ToList();
|
||||
var currentIndex = CurrentIndex ?? 0;
|
||||
|
||||
if (currentIndex < collection.Count - 1)
|
||||
if (currentIndex < collection.Count)
|
||||
{
|
||||
var currentValue = collection[currentIndex];
|
||||
var scope = context.CreateScope();
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace Elsa.Activities.ControlFlow
|
|||
Action<IOutcomeBuilder> iterate,
|
||||
[CallerLineNumber] int lineNumber = default,
|
||||
[CallerFilePath] string? sourceFile = default) =>
|
||||
builder.ForEach(activity => activity.Set(x => x.Items, () => items().Cast<object>()), iterate, lineNumber, sourceFile);
|
||||
builder.ForEach(activity => activity.Set(x => x.Items, () => items().Select(x => (object)x!).ToList()), iterate, lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder ForEach<T>(
|
||||
this IBuilder builder,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,17 @@ namespace Elsa.Builders
|
|||
private readonly Func<ICompositeActivityBuilder> _compositeActivityBuilderFactory;
|
||||
private readonly IGetsStartActivitiesForCompositeActivityBlueprint _startingActivitiesProvider;
|
||||
|
||||
internal CompositeActivityBuilder(
|
||||
IServiceProvider serviceProvider,
|
||||
IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider,
|
||||
Type activityType,
|
||||
string activityTypeName) : this(serviceProvider, startingActivitiesProvider)
|
||||
{
|
||||
ActivityType = activityType;
|
||||
ActivityTypeName = activityTypeName;
|
||||
WorkflowBuilder = this;
|
||||
}
|
||||
|
||||
public CompositeActivityBuilder(IServiceProvider serviceProvider, IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider)
|
||||
{
|
||||
_startingActivitiesProvider = startingActivitiesProvider ?? throw new ArgumentNullException(nameof(startingActivitiesProvider));
|
||||
|
|
@ -106,7 +117,7 @@ namespace Elsa.Builders
|
|||
[CallerFilePath] string? sourceFile = default)
|
||||
where T : class, IActivity
|
||||
{
|
||||
var activityBuilder = new ActivityBuilder( typeof(T), activityTypeName, this, propertyValueProviders, lineNumber, sourceFile);
|
||||
var activityBuilder = new ActivityBuilder(typeof(T), activityTypeName, this, propertyValueProviders, lineNumber, sourceFile);
|
||||
return Add(activityBuilder, branch);
|
||||
}
|
||||
|
||||
|
|
@ -119,12 +130,18 @@ namespace Elsa.Builders
|
|||
return activityBuilder;
|
||||
}
|
||||
|
||||
public override IActivityBuilder Then<T>(string activityTypeName, Action<ISetupActivity<T>>? setup = null, Action<IActivityBuilder>? branch = null, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
StartWith(activityTypeName,setup, branch, lineNumber, sourceFile);
|
||||
public override IActivityBuilder Then<T>(
|
||||
string activityTypeName,
|
||||
Action<ISetupActivity<T>>? setup = null,
|
||||
Action<IActivityBuilder>? branch = null,
|
||||
[CallerLineNumber] int lineNumber = default,
|
||||
[CallerFilePath] string? sourceFile = default) =>
|
||||
StartWith(activityTypeName, setup, branch, lineNumber, sourceFile);
|
||||
|
||||
public override IActivityBuilder Then(IActivityBuilder targetActivity) => Add(targetActivity);
|
||||
|
||||
public override IActivityBuilder Then<T>(string activityTypeName, Action<IActivityBuilder>? branch = null, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) => StartWith<T>(activityTypeName, branch, lineNumber, sourceFile);
|
||||
public override IActivityBuilder Then<T>(string activityTypeName, Action<IActivityBuilder>? branch = null, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
StartWith<T>(activityTypeName, branch, lineNumber, sourceFile);
|
||||
|
||||
public IConnectionBuilder Connect(
|
||||
IActivityBuilder source,
|
||||
|
|
@ -147,7 +164,13 @@ namespace Elsa.Builders
|
|||
{
|
||||
var compositeActivityBlueprint = new CompositeActivityBlueprint
|
||||
{
|
||||
Id = ActivityId
|
||||
Id = ActivityId,
|
||||
Name = Name,
|
||||
Type = ActivityTypeName,
|
||||
PersistOutput = PersistOutputEnabled,
|
||||
PersistWorkflow = PersistWorkflowEnabled,
|
||||
LoadWorkflowContext = LoadWorkflowContextEnabled,
|
||||
SaveWorkflowContext = SaveWorkflowContextEnabled
|
||||
};
|
||||
|
||||
var activityBuilders = ActivityBuilders.ToList();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ namespace Elsa.Expressions
|
|||
if (returnType == typeof(string))
|
||||
return Task.FromResult<object?>(expression);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(expression))
|
||||
return Task.FromResult((object?) null);
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(returnType);
|
||||
var value = converter.CanConvertFrom(typeof(string)) ? converter.ConvertFrom(expression) : default;
|
||||
return Task.FromResult(value)!;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.ActivityProviders;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetBox.Extensions;
|
||||
|
||||
|
|
@ -13,14 +17,18 @@ namespace Elsa.Services
|
|||
public class WorkflowBlueprintMaterializer : IWorkflowBlueprintMaterializer
|
||||
{
|
||||
private readonly IActivityTypeService _activityTypeService;
|
||||
private readonly IGetsStartActivitiesForCompositeActivityBlueprint _startingActivitiesProvider;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider;
|
||||
|
||||
public WorkflowBlueprintMaterializer(IActivityTypeService activityTypeService,
|
||||
ILogger<WorkflowBlueprintMaterializer> logger,
|
||||
IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider)
|
||||
public WorkflowBlueprintMaterializer(
|
||||
IActivityTypeService activityTypeService,
|
||||
IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<WorkflowBlueprintMaterializer> logger)
|
||||
{
|
||||
this.startingActivitiesProvider = startingActivitiesProvider ?? throw new System.ArgumentNullException(nameof(startingActivitiesProvider));
|
||||
_startingActivitiesProvider = startingActivitiesProvider;
|
||||
_serviceProvider = serviceProvider;
|
||||
_activityTypeService = activityTypeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
|
@ -105,7 +113,8 @@ namespace Elsa.Services
|
|||
private async Task<IEnumerable<IActivityBlueprint>> CreateBlueprintsAsync(ActivityDefinition activityDefinition, CancellationToken cancellationToken)
|
||||
{
|
||||
var list = new List<IActivityBlueprint>();
|
||||
|
||||
var activityType = await _activityTypeService.GetActivityTypeAsync(activityDefinition.Type, cancellationToken);
|
||||
|
||||
if (activityDefinition is CompositeActivityDefinition compositeActivityDefinition)
|
||||
{
|
||||
var manyActivityBlueprints = await Task.WhenAll(compositeActivityDefinition.Activities.Select(async x => await CreateBlueprintsAsync(x, cancellationToken)));
|
||||
|
|
@ -130,7 +139,33 @@ namespace Elsa.Services
|
|||
list.Add(compositeActivityBlueprint);
|
||||
|
||||
// Connect the composite activity to its starting activities.
|
||||
var startActivities = startingActivitiesProvider.GetStartActivities(compositeActivityBlueprint).ToList();
|
||||
var startActivities = _startingActivitiesProvider.GetStartActivities(compositeActivityBlueprint).ToList();
|
||||
compositeActivityBlueprint.Connections.AddRange(startActivities.Select(x => new Connection(compositeActivityBlueprint, x, CompositeActivity.Enter)));
|
||||
}
|
||||
else if (typeof(CompositeActivity).IsAssignableFrom(activityType.Type))
|
||||
{
|
||||
var compositeActivity = (CompositeActivity) ActivatorUtilities.CreateInstance(_serviceProvider, activityType.Type);
|
||||
var compositeActivityBuilder = new CompositeActivityBuilder(_serviceProvider, _startingActivitiesProvider, activityType.Type, activityType.TypeName)
|
||||
{
|
||||
ActivityId = activityDefinition.ActivityId,
|
||||
Name = activityDefinition.Name,
|
||||
DisplayName = activityDefinition.DisplayName,
|
||||
PersistOutputEnabled = activityDefinition.PersistOutput,
|
||||
PersistWorkflowEnabled = activityDefinition.PersistWorkflow,
|
||||
LoadWorkflowContextEnabled = activityDefinition.LoadWorkflowContext,
|
||||
SaveWorkflowContextEnabled = activityDefinition.SaveWorkflowContext,
|
||||
Description = activityDefinition.Description
|
||||
};
|
||||
|
||||
compositeActivity.Build(compositeActivityBuilder);
|
||||
|
||||
var compositeActivityBlueprint = compositeActivityBuilder.Build($"{activityDefinition.ActivityId}:activity");
|
||||
|
||||
list.Add(compositeActivityBlueprint);
|
||||
list.AddRange(compositeActivityBlueprint.Activities);
|
||||
|
||||
// Connect the composite activity to its starting activities.
|
||||
var startActivities = _startingActivitiesProvider.GetStartActivities(compositeActivityBlueprint).ToList();
|
||||
compositeActivityBlueprint.Connections.AddRange(startActivities.Select(x => new Connection(compositeActivityBlueprint, x, CompositeActivity.Enter)));
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace ElsaDashboard.Samples.Monolith
|
|||
.AddQuartzTemporalActivities()
|
||||
.AddTelnyx(Configuration.GetSection("Telnyx").Bind)
|
||||
.AddJavaScriptActivities()
|
||||
.AddWorkflowsFrom<Startup>()
|
||||
//.AddWorkflowsFrom<Startup>()
|
||||
);
|
||||
|
||||
services
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
},
|
||||
"Telnyx": {
|
||||
"ApiUrl": "",
|
||||
"ApiKey": ""
|
||||
"ApiKey": "",
|
||||
"CallControlAppId": ""
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue