Skip to content

Commit

Permalink
Merge pull request #203 from Nexmo/feature/asr_nccos
Browse files Browse the repository at this point in the history
adding ASR webhook and input items
  • Loading branch information
slorello89 authored Jun 4, 2020
2 parents ce90660 + d9810af commit 71037fd
Show file tree
Hide file tree
Showing 14 changed files with 360 additions and 8 deletions.
127 changes: 127 additions & 0 deletions Nexmo.Api.Test.Unit/MultiInputTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nexmo.Api.Voice.Nccos;
using Nexmo.Api.Voice.EventWebhooks;
using Newtonsoft.Json;

namespace Nexmo.Api.Test.Unit
{
[TestClass]
public class MultiInputTests
{
[TestMethod]
public void TestSerializeNccoKitchenSink()
{
// arrage
var expected = @"[{""dtmf"":{""timeOut"":3,""maxDigits"":1,""submitOnHash"":true},""speech"":{""uuid"":[""aaaaaaaa-bbbb-cccc-dddd-0123456789ab""],""endOnSilence"":1,""language"":""en-US"",""context"":[""dog"",""cat""],""startTimeout"":5,""maxDuration"":30},""action"":""input""}]";
var settings = new SpeechSettings
{
Uuid = new[] { "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" },
EndOnSilence = 1,
Language = "en-US",
Context = new string[] { "dog", "cat" },
StartTimeout = 5,
MaxDuration = 30
};
var dtmfSettings = new DtmfSettings { MaxDigits = 1, TimeOut = 3, SubmitOnHash = true };
var inputAction = new MultiInputAction { Speech = settings, Dtmf=dtmfSettings };

//act
var ncco = new Ncco(inputAction);
var actual = ncco.ToString();

//assert
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void TestSerializeNccoKitchenEmpty()
{
// arrage
var expected = @"[{""dtmf"":{},""speech"":{""uuid"":[""aaaaaaaa-bbbb-cccc-dddd-0123456789ab""]},""action"":""input""}]";
var settings = new SpeechSettings
{
Uuid = new[] { "aaaaaaaa-bbbb-cccc-dddd-0123456789ab" }
};
var inputAction = new MultiInputAction { Speech = settings };

//act
var ncco = new Ncco(inputAction);
var actual = ncco.ToString();

//assert
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void TestWebhookSerialization()
{
//ARRANGE
var inboundString = @"{
""speech"": {
""timeout_reason"": ""end_on_silence_timeout"",
""results"": [
{
""confidence"": ""0.9405097"",
""text"": ""Sales""
},
{
""confidence"": ""0.70543784"",
""text"": ""Sails""
},
{
""confidence"": ""0.5949854"",
""text"": ""Sale""
}
]
},
""dtmf"": {
""digits"": null,
""timed_out"": false
},
""uuid"": ""aaaaaaaa-bbbb-cccc-dddd-0123456789ab"",
""conversation_uuid"": ""bbbbbbbb-cccc-dddd-eeee-0123456789ab"",
""timestamp"": ""2020-01-01T14:00:00.000Z""
}";

var serialized = JsonConvert.DeserializeObject<MultiInput>(inboundString);

Assert.AreEqual("aaaaaaaa-bbbb-cccc-dddd-0123456789ab", serialized.Uuid);
Assert.AreEqual("end_on_silence_timeout", serialized.Speech.TimeoutReason);
Assert.AreEqual("0.9405097", serialized.Speech.SpeechResults[0].Confidence);
Assert.AreEqual("Sales", serialized.Speech.SpeechResults[0].Text);
Assert.AreEqual("0.5949854", serialized.Speech.SpeechResults[2].Confidence);
Assert.AreEqual("Sale", serialized.Speech.SpeechResults[2].Text);
Assert.AreEqual(null, serialized.Dtmf.Digits);
Assert.AreEqual(false, serialized.Dtmf.TimedOut);
}

[TestMethod]
public void TestWebhookSerializationSpeechOveridden()
{
//ARRANGE
var inboundString = @"{
""speech"": {
""error"": ""Speech overridden by DTMF""
},
""dtmf"": {
""digits"": ""1234"",
""timed_out"": false
},
""uuid"": ""aaaaaaaa-bbbb-cccc-dddd-0123456789ab"",
""conversation_uuid"": ""bbbbbbbb-cccc-dddd-eeee-0123456789ab"",
""timestamp"": ""2020-01-01T14:00:00.000Z""
}";

var serialized = JsonConvert.DeserializeObject<MultiInput>(inboundString);

Assert.AreEqual("aaaaaaaa-bbbb-cccc-dddd-0123456789ab", serialized.Uuid);
Assert.AreEqual("Speech overridden by DTMF", serialized.Speech.Error);
Assert.AreEqual("1234", serialized.Dtmf.Digits);
Assert.AreEqual(false, serialized.Dtmf.TimedOut);
}
}
}
4 changes: 2 additions & 2 deletions Nexmo.Api/Nexmo.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<Version>4.3.2</Version>
<Version>4.4.0</Version>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression></PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Nexmo/nexmo-dotnet</PackageProjectUrl>
<PackageReleaseNotes>https://github.com/Nexmo/nexmo-dotnet/releases/tag/v4.3.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/Nexmo/nexmo-dotnet/releases/tag/v4.3.0</PackageReleaseNotes>
<PackageTags>SMS voice telephony phone nexmo</PackageTags>
</PropertyGroup>

Expand Down
5 changes: 2 additions & 3 deletions Nexmo.Api/Nexmo.Api.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package>
<metadata>
<id>Nexmo.Csharp.Client</id>
<version>4.3.2</version>
<version>4.4.0</version>
<title>Nexmo API Client</title>
<authors>Nexmo</authors>
<owners>Nexmo</owners>
Expand All @@ -12,8 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Official C#/.NET wrapper for the Nexmo API</description>
<releaseNotes>
* Fixed bug where streamUrl in where the StreamAction was set to a string rather than an array of strings
* Changed websocket endpoints `headers` type from a string to an object - the string type was injecting escape charecters into the headers object payload.
* Added ASR Webhook and NCCO to Client
</releaseNotes>
<copyright>© Nexmo 2020</copyright>
<tags>SMS voice telephony phone nexmo</tags>
Expand Down
4 changes: 2 additions & 2 deletions Nexmo.Api/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.3.2.0")]
[assembly: AssemblyFileVersion("4.3.2.0")]
[assembly: AssemblyVersion("4.4.0.0")]
[assembly: AssemblyFileVersion("4.4.0.0")]
25 changes: 25 additions & 0 deletions Nexmo.Api/Voice/EventWebhooks/DtmfResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nexmo.Api.Voice.EventWebhooks
{
public class DtmfResult
{
/// <summary>
/// the dtmf digits input by the user
/// </summary>
[JsonProperty("digits")]
public string Digits { get; set; }

/// <summary>
/// indicates whether the dtmf input timed out
/// </summary>
[JsonProperty("timed_out")]
public bool TimedOut { get; set; }

}
}
2 changes: 2 additions & 0 deletions Nexmo.Api/Voice/EventWebhooks/EventBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public static EventBase ParseEvent(string json)

var dtmfProperty = data.Property("dtmf");

var speechProperty = data.Property("speech");

var recordingUrlProperty = data.Property("recording_url");

var reasonProperty = data.Property("reason");
Expand Down
24 changes: 23 additions & 1 deletion Nexmo.Api/Voice/EventWebhooks/Input.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
using Newtonsoft.Json;
using System;

namespace Nexmo.Api.Voice.EventWebhooks
{
[Obsolete("This item has been rendered obsolete due to the new multi-input functionality. Please add dtmf arguments to your input action and use the MultiInput object - see: https://developer.nexmo.com/voice/voice-api/ncco-reference#dtmf-input-settings")]
public class Input : Event
{
/// <summary>
/// The buttons pressed by the user
/// </summary>
[JsonProperty("dtmf")]
public string Dtmf { get; set; }

/// <summary>
/// Whether the input action timed out: true if it did, false if not
/// </summary>
[JsonProperty("timed_out")]
public bool TimedOut { get; set; }

/// <summary>
/// The unique identifier for this conversation
/// </summary>
[JsonProperty("conversation_uuid")]
public string ConversationUuid { get; set; }


/// <summary>
/// The number the call came from
/// </summary>
[JsonProperty("from")]
public string From { get; set; }

/// <summary>
/// The number the call was made to
/// </summary>
[JsonProperty("to")]
public string To { get; set; }
}
}
24 changes: 24 additions & 0 deletions Nexmo.Api/Voice/EventWebhooks/MultiInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nexmo.Api.Voice.EventWebhooks
{
public class MultiInput : Input
{
/// <summary>
/// Result of Dtmf input
/// </summary>
[JsonProperty("dtmf")]
new public DtmfResult Dtmf { get; set; }

/// <summary>
/// Result of the speech recognition
/// </summary>
[JsonProperty("speech")]
public SpeechResult Speech { get; set; }
}
}
19 changes: 19 additions & 0 deletions Nexmo.Api/Voice/EventWebhooks/SpeechRecognitionResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Newtonsoft.Json;

namespace Nexmo.Api.Voice.EventWebhooks
{
public class SpeechRecognitionResult
{
/// <summary>
/// Transcript text representing the words that the user spoke.
/// </summary>
[JsonProperty("text")]
public string Text { get; set; }

/// <summary>
/// The confidence estimate between 0.0 and 1.0. A higher number indicates an estimated greater likelihood that the recognized words are correct.
/// </summary>
[JsonProperty("confidence")]
public string Confidence { get; set; }
}
}
30 changes: 30 additions & 0 deletions Nexmo.Api/Voice/EventWebhooks/SpeechResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nexmo.Api.Voice.EventWebhooks
{
public class SpeechResult
{
/// <summary>
/// Indicates if the input ended when user stopped speaking (end_on_silence_timeout), by max duration timeout (max_duration) or if the user didn't say anything (start_timeout)
/// </summary>
[JsonProperty("timeout_reason")]
public string TimeoutReason { get; set; }

/// <summary>
/// Error field in case there was a problem during speech recognition - will not be present if nothing went wrong.
/// </summary>
[JsonProperty("error")]
public string Error { get; set; }

/// <summary>
/// Array of SpeechRecognitionResults
/// </summary>
[JsonProperty("results")]
public SpeechRecognitionResult[] SpeechResults { get; set; }
}
}
21 changes: 21 additions & 0 deletions Nexmo.Api/Voice/Nccos/DtmfSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nexmo.Api.Voice.Nccos
{
public class DtmfSettings
{
[JsonProperty("timeOut")]
public int? TimeOut { get; set; }

[JsonProperty("maxDigits")]
public int? MaxDigits { get; set; }

[JsonProperty("submitOnHash")]
public bool? SubmitOnHash { get; set; }
}
}
2 changes: 2 additions & 0 deletions Nexmo.Api/Voice/Nccos/InputAction.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Newtonsoft.Json;
using System;

namespace Nexmo.Api.Voice.Nccos
{
[Obsolete("This is made obsolete by the new MultiInputAction type and the MultiInput event see: https://developer.nexmo.com/voice/voice-api/ncco-reference#input for more details")]
public class InputAction : NccoAction
{
[JsonProperty("timeOut")]
Expand Down
38 changes: 38 additions & 0 deletions Nexmo.Api/Voice/Nccos/MultiInputAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Newtonsoft.Json;

namespace Nexmo.Api.Voice.Nccos
{
public class MultiInputAction : NccoAction
{
/// <summary>
/// Nexmo sends the digits pressed by the callee to this URL 1) after timeOut pause in activity or when # is pressed for DTMF or 2) after user stops speaking or 30 seconds of speech for speech input.
/// </summary>
[JsonProperty("eventUrl")]
public string[] EventUrl { get; set; }

/// <summary>
/// The HTTP method used to send event information to event_url The default value is POST.
/// </summary>
[JsonProperty("eventMethod")]
public string EventMethod { get; set; }

/// <summary>
/// DTMF settings. Should be specified to enable DTMF input. If all the DTMF input settings will have default values, it should be specified as empty object
/// NOTE: this is serialized as an empty object if not initalized by the user
/// </summary>
[JsonProperty("dtmf", DefaultValueHandling = DefaultValueHandling.Include)]
public DtmfSettings Dtmf { get; set; }

/// <summary>
/// Speech recognition settings. Should be specified to enable speech input.
/// </summary>
[JsonProperty("speech")]
public SpeechSettings Speech { get; set; }

public MultiInputAction()
{
Dtmf = new DtmfSettings();
this.Action = ActionType.input;
}
}
}
Loading

0 comments on commit 71037fd

Please sign in to comment.