Add SpeakText and StartRecording activities
This commit is contained in:
parent
f1c80b5fad
commit
326129e45a
110
src/activities/Elsa.Activities.Telnyx/Activities/SpeakText.cs
Normal file
110
src/activities/Elsa.Activities.Telnyx/Activities/SpeakText.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
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;
|
||||
using Elsa.Exceptions;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Refit;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Activities
|
||||
{
|
||||
[Action(
|
||||
Category = Constants.Category,
|
||||
Description = "Convert text to speech and play it back on the call.",
|
||||
Outcomes = new[] { OutcomeNames.Done, TelnyxOutcomeNames.CallIsNoLongerActive },
|
||||
DisplayName = "Speak Text"
|
||||
)]
|
||||
public class SpeakText : Activity
|
||||
{
|
||||
private readonly ITelnyxClient _telnyxClient;
|
||||
public SpeakText(ITelnyxClient telnyxClient) => _telnyxClient = telnyxClient;
|
||||
|
||||
[ActivityProperty(
|
||||
Label = "Call Control ID",
|
||||
Hint = "Unique identifier and token for controlling the call",
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string CallControlId { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "The language you want spoken.",
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
Options = new[] { "en-US", "en-AU", "nl-NL", "es-ES", "ru-RU" }
|
||||
)]
|
||||
public string Language { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "The gender of the voice used to speak back the text.",
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
Options = new[] { "female", "male" }
|
||||
)]
|
||||
public string Voice { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(Hint = "The text or SSML to be converted into speech. There is a 5,000 character limit.")]
|
||||
public string Payload { get; set; }
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "The type of the provided payload. The payload can either be plain text, or Speech Synthesis Markup Language (SSML).",
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
Options = new[] { "", "text", "ssml" }
|
||||
)]
|
||||
public string? PayloadType { get; set; }
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "This parameter impacts speech quality, language options and payload types. When using `basic`, only the `en-US` language and payload type `text` are allowed.",
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
Options = new[] { "", "basic", "premium" },
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string? ServiceLevel { get; set; }
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.",
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string? ClientState { get; set; }
|
||||
|
||||
[ActivityProperty(
|
||||
Label = "Command ID",
|
||||
Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.",
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string? CommandId { get; set; }
|
||||
|
||||
protected override async ValueTask<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var request = new SpeakTextRequest(
|
||||
Language,
|
||||
Voice,
|
||||
Payload,
|
||||
EmptyToNull(PayloadType),
|
||||
EmptyToNull(ClientState),
|
||||
EmptyToNull(CommandId),
|
||||
EmptyToNull(ServiceLevel),
|
||||
null
|
||||
);
|
||||
|
||||
var callControlId = CallControlId;
|
||||
|
||||
try
|
||||
{
|
||||
await _telnyxClient.Calls.SpeakTextAsync(callControlId, request, context.CancellationToken);
|
||||
return Done();
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
if (await e.CallIsNoLongerActiveAsync())
|
||||
return Outcome(TelnyxOutcomeNames.CallIsNoLongerActive);
|
||||
|
||||
throw new WorkflowException(e.Content ?? e.Message, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? EmptyToNull(string? value) => value is "" ? null : value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
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;
|
||||
using Elsa.Exceptions;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Refit;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Activities
|
||||
{
|
||||
[Action(
|
||||
Category = Constants.Category,
|
||||
Description = "Start recording the call.",
|
||||
Outcomes = new[] { OutcomeNames.Done, TelnyxOutcomeNames.CallIsNoLongerActive },
|
||||
DisplayName = "Start Recording"
|
||||
)]
|
||||
public class StartRecording : Activity
|
||||
{
|
||||
private readonly ITelnyxClient _telnyxClient;
|
||||
public StartRecording(ITelnyxClient telnyxClient) => _telnyxClient = telnyxClient;
|
||||
|
||||
[ActivityProperty(
|
||||
Label = "Call Control ID",
|
||||
Hint = "Unique identifier and token for controlling the call",
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string CallControlId { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "When 'dual', final audio file will be stereo recorded with the first leg on channel A, and the rest on channel B.",
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
Options = new[] { "single", "dual" }
|
||||
)]
|
||||
public string Channels { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "The audio file format used when storing the call recording. Can be either 'mp3' or 'wav'.",
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
Options = new[] { "wav", "mp3" }
|
||||
)]
|
||||
public string Format { get; set; } = default!;
|
||||
|
||||
[ActivityProperty(
|
||||
Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.",
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string? ClientState { get; set; }
|
||||
|
||||
[ActivityProperty(
|
||||
Label = "Command ID",
|
||||
Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.",
|
||||
Category = PropertyCategories.Advanced
|
||||
)]
|
||||
public string? CommandId { get; set; }
|
||||
|
||||
[ActivityProperty(Hint = "If enabled, a beep sound will be played at the start of a recording.")]
|
||||
public bool? PlayBeep { get; set; }
|
||||
|
||||
protected override async ValueTask<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var request = new StartRecordingRequest(
|
||||
Channels,
|
||||
Format,
|
||||
EmptyToNull(ClientState),
|
||||
EmptyToNull(CommandId),
|
||||
PlayBeep
|
||||
);
|
||||
|
||||
var callControlId = CallControlId;
|
||||
|
||||
try
|
||||
{
|
||||
await _telnyxClient.Calls.StartRecordingAsync(callControlId, request, context.CancellationToken);
|
||||
return Done();
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
if (await e.CallIsNoLongerActiveAsync())
|
||||
return Outcome(TelnyxOutcomeNames.CallIsNoLongerActive);
|
||||
|
||||
throw new WorkflowException(e.Content ?? e.Message, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? EmptyToNull(string? value) => value is "" ? null : value;
|
||||
}
|
||||
}
|
||||
|
|
@ -71,5 +71,25 @@ namespace Elsa.Activities.Telnyx.Client.Models
|
|||
string? CommandId,
|
||||
object? Loop,
|
||||
bool Overlay,
|
||||
string? TargetLegs);
|
||||
string? TargetLegs
|
||||
);
|
||||
|
||||
public record StartRecordingRequest(
|
||||
string Channels,
|
||||
string Format,
|
||||
string? ClientState,
|
||||
string? CommandId,
|
||||
bool? PlayBeep
|
||||
);
|
||||
|
||||
public record SpeakTextRequest(
|
||||
string Language,
|
||||
string Voice,
|
||||
string Payload,
|
||||
string? PayloadType,
|
||||
string? ClientState,
|
||||
string? CommandId,
|
||||
string? ServiceLevel,
|
||||
string? Stop
|
||||
);
|
||||
}
|
||||
|
|
@ -27,5 +27,11 @@ namespace Elsa.Activities.Telnyx.Client.Services
|
|||
|
||||
[Post("/v2/calls/{callControlId}/actions/playback_start")]
|
||||
Task PlayAudioAsync(string callControlId, [Body]PlayAudioRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Post("/v2/calls/{callControlId}/actions/record_start")]
|
||||
Task StartRecordingAsync(string callControlId, [Body]StartRecordingRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Post("/v2/calls/{callControlId}/actions/speak")]
|
||||
Task SpeakTextAsync(string callControlId, [Body]SpeakTextRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,10 @@ namespace Elsa.Activities.Telnyx.Liquid
|
|||
memberAccessStrategy.Register<CallPlayback>();
|
||||
memberAccessStrategy.Register<CallPlaybackEndedPayload>();
|
||||
memberAccessStrategy.Register<CallPlaybackStartedPayload>();
|
||||
memberAccessStrategy.Register<CallRecordingSaved>();
|
||||
memberAccessStrategy.Register<CallRecordingUrls>();
|
||||
memberAccessStrategy.Register<CallSpeakStarted>();
|
||||
memberAccessStrategy.Register<CallSpeakEnded>();
|
||||
|
||||
memberAccessStrategy.Register<DialResponse>();
|
||||
memberAccessStrategy.Register<ErrorResponse>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
using NodaTime;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Webhook(EventType, ActivityTypeName, "Call Recording Saved", "Triggered when a recording has been saved.")]
|
||||
public sealed record CallRecordingSaved : CallPayload
|
||||
{
|
||||
public const string EventType = "call.recording.saved";
|
||||
public const string ActivityTypeName = "CallRecordingSaved";
|
||||
public string Channels { get; set; }
|
||||
public CallRecordingUrls PublicRecordingUrls { get; set; }
|
||||
public CallRecordingUrls RecordingUrls { get; set; }
|
||||
public Instant RecordingEndedAt { get; set; }
|
||||
public Instant RecordingStartedAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
public record CallRecordingUrls(string? Wav, string? Mp3);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Webhook(EventType, ActivityTypeName, "Call Speak Ended", "Triggered when speaking has ended.")]
|
||||
public sealed record CallSpeakEnded : CallPayload
|
||||
{
|
||||
public const string EventType = "call.speak.ended";
|
||||
public const string ActivityTypeName = "CallSpeakEnded";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Elsa.Activities.Telnyx.Webhooks.Attributes;
|
||||
|
||||
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
|
||||
{
|
||||
[Webhook(EventType, ActivityTypeName, "Call Speak Started", "Triggered when speaking has started.")]
|
||||
public sealed record CallSpeakStarted : CallPayload
|
||||
{
|
||||
public const string EventType = "call.speak.started";
|
||||
public const string ActivityTypeName = "CallSpeakStarted";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue