-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(example): add handling of incoming call event #56
Changes from all commits
4c6adf0
694e542
15b1b61
8b7ce1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
using Sinch; | ||
using Sinch.Voice.Calls.Actions; | ||
using Sinch.Voice.Calls.Instructions; | ||
using Sinch.Voice.Hooks; | ||
|
||
namespace WebApiExamples.Controllers | ||
{ | ||
[ApiController] | ||
[Route("voice")] | ||
public class HandleIncomingIceEventController : ControllerBase | ||
{ | ||
private readonly ISinchClient _sinchClient; | ||
|
||
public HandleIncomingIceEventController(ISinchClient sinchClient) | ||
{ | ||
_sinchClient = sinchClient; | ||
} | ||
|
||
[HttpPost] | ||
[Route("event")] | ||
public IActionResult HandleEvent([FromBody] IVoiceEvent incomingEvent) | ||
{ | ||
switch (incomingEvent) | ||
{ | ||
case AnsweredCallEvent answeredCallEvent: | ||
break; | ||
case DisconnectedCallEvent disconnectedCallEvent: | ||
break; | ||
case IncomingCallEvent incomingCallEvent: | ||
var response = new CallEventResponse() | ||
{ | ||
Action = new Hangup(), | ||
Instructions = new List<IInstruction>() | ||
{ | ||
new Say() | ||
{ | ||
Text = "Thank you for calling Sinch! This call will now end.", | ||
Locale = "en-US" | ||
} | ||
} | ||
}; | ||
return Ok(response); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Side note: like the OK/BadRequest; need to think about having same "helper" for Java SDK to hide/help the response serialization requirements |
||
case NotificationEvent notificationEvent: | ||
break; | ||
case PromptInputEvent promptInputEvent: | ||
break; | ||
default: | ||
return BadRequest(); | ||
} | ||
|
||
return BadRequest(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,14 +14,13 @@ namespace Sinch.Voice.Hooks | |
/// enabled, the amd object will also be present on ACE callbacks. | ||
/// Note: ACE Callbacks are not issued for InApp Calls (destination: username), only PSTN and SIP calls. | ||
/// </summary> | ||
public class AnsweredCallEvent | ||
public class AnsweredCallEvent : IVoiceEvent | ||
{ | ||
/// <summary> | ||
/// Must have the value ace. | ||
/// </summary> | ||
[JsonPropertyName("event")] | ||
[JsonInclude] | ||
public string? Event { get; private set; } | ||
public EventType? Event { get; set; } | ||
|
||
|
||
/// <summary> | ||
|
@@ -64,5 +63,7 @@ public class AnsweredCallEvent | |
/// </summary> | ||
[JsonPropertyName("amd")] | ||
public Amd? Amd { get; set; } | ||
|
||
public EventType? EventType { get; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems this field is not present from specs. Could be removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤯 I was sure I fixed that, I'll open PR with a fix soon |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System.Text.Json.Serialization; | ||
using Sinch.Core; | ||
|
||
namespace Sinch.Voice.Hooks | ||
{ | ||
[JsonConverter(typeof(EnumRecordJsonConverter<EventType>))] | ||
public record EventType(string Value) : EnumRecord(Value) | ||
{ | ||
public static readonly EventType AnsweredCallEvent = new EventType("ace"); | ||
public static readonly EventType DisconnectedCallEvent = new EventType("dice"); | ||
public static readonly EventType IncomingCallEvent = new EventType("ice"); | ||
public static readonly EventType NotificationEvent = new EventType("notify"); | ||
public static readonly EventType PromptInputEvent = new EventType("pie"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using Sinch.Core; | ||
|
||
namespace Sinch.Voice.Hooks | ||
{ | ||
/// <summary> | ||
/// Marker interface for event types of voice. | ||
/// </summary> | ||
[JsonConverter(typeof(InterfaceConverter<IVoiceEvent>))] | ||
public interface IVoiceEvent | ||
{ | ||
[JsonPropertyName("event")] | ||
public EventType? Event { get; } | ||
} | ||
|
||
public class VoiceEventConverter : JsonConverter<IVoiceEvent> | ||
{ | ||
public override IVoiceEvent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
var elem = JsonElement.ParseValue(ref reader); | ||
var descriptor = elem.EnumerateObject().FirstOrDefault(x => x.Name == "event"); | ||
var type = descriptor.Value.GetString(); | ||
|
||
if (type == EventType.NotificationEvent.Value) | ||
{ | ||
return elem.Deserialize<NotificationEvent>(options); | ||
} | ||
|
||
if (type == EventType.IncomingCallEvent.Value) | ||
{ | ||
return elem.Deserialize<IncomingCallEvent>(options); | ||
} | ||
|
||
if (type == EventType.DisconnectedCallEvent.Value) | ||
{ | ||
return elem.Deserialize<DisconnectedCallEvent>(options); | ||
} | ||
|
||
if (type == EventType.AnsweredCallEvent.Value) | ||
{ | ||
return elem.Deserialize<AnsweredCallEvent>(options); | ||
} | ||
|
||
if (type == EventType.PromptInputEvent.Value) | ||
{ | ||
return elem.Deserialize<PromptInputEvent>(options); | ||
} | ||
|
||
throw new JsonException($"Failed to match verification method object, got {descriptor.Name}"); | ||
} | ||
|
||
public override void Write(Utf8JsonWriter writer, IVoiceEvent value, JsonSerializerOptions options) | ||
{ | ||
switch (value) | ||
{ | ||
case AnsweredCallEvent answeredCallEvent: | ||
JsonSerializer.Serialize(writer, answeredCallEvent, options); | ||
break; | ||
case DisconnectedCallEvent disconnectedCallEvent: | ||
JsonSerializer.Serialize(writer, disconnectedCallEvent, options); | ||
break; | ||
case IncomingCallEvent incomingCallEvent: | ||
JsonSerializer.Serialize(writer, incomingCallEvent, options); | ||
break; | ||
case NotificationEvent notificationEvent: | ||
JsonSerializer.Serialize(writer, notificationEvent, options); | ||
break; | ||
case PromptInputEvent promptInputEvent: | ||
JsonSerializer.Serialize(writer, promptInputEvent, options); | ||
break; | ||
default: | ||
throw new ArgumentOutOfRangeException(nameof(value), | ||
$"Cannot find a matching class for the interface {nameof(IVoiceEvent)}"); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally Voice should provide a
ValidateAuthenticationHeader
function (like Conversation domains: https://github.com/sinch/sinch-sdk-dotnet/blob/main/src/Sinch/Conversation/Webhooks/Webhooks.cs#L72)Calling
ValidateAuthenticationHeader
here and because this sample is a tutorial, it will help to highlight endpoint is having to be "protected" against unsolicited requestsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, blocked by #62