Skip to content
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

Update gather-rcml documentation #2392

Merged
merged 4 commits into from
Jul 31, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,53 +1,73 @@
= Restcomm RCML – Gather

[[gather]]
== Gather
The *<Gather>* verb "gathers" digits that a caller enters into his or her telephone keypad. When the caller is done entering digits, RestComm submits that digits to the provided 'action' URL in an HTTP GET or POST request. If no input is received before timeout, *<Gather>* falls through to the next verb in the RestComm document. You may optionally nest **<Say>, <Play>**, and *<Pause>* verbs within a *<Gather>* verb while waiting for input. This allows you to read menu options to the caller while letting her enter a menu selection at any time. After the first digit is received the audio will stop playing.

=== Gather Attributes

[cols=",,",options="header",]
|======================================================
|Name |Allowed Values |Default Value
|action |relative or absolute URL |current document URL
|method |GET, POST |POST
|timeout |positive integer |5 seconds
|finishOnKey |any digit, #, * |#
|numDigits |integer >= 1 |unlimited
|======================================================

* *action.* The 'action' attribute takes an absolute or relative URL as a value. When the caller has finished entering digits RestComm will make a GET or POST request to this URL including the parameters below. If no 'action' is provided, RestComm will by default make a POST request to the current document's URL.

=== Request Parameters

[cols=",",options="header",]
|=======================================================================
|Parameter |Description
|Digits |The digits the caller pressed, excluding the finishOnKey digit.
|=======================================================================


* *method.* The 'method' attribute takes the value 'GET' or 'POST'. This tells RestComm whether to request the 'action' URL via HTTP GET or POST.
* *timeout.* The 'timeout' attribute sets the limit in seconds that RestComm will wait for the caller to press another digit before moving on and making a request to the 'action' URL. For example, if 'timeout' is '10', RestComm will wait ten seconds for the caller to press another key before submitting the previously entered digits to the 'action' URL. RestComm waits until completing the execution of all nested verbs before beginning the timeout period.
* *finishOnKey.* The 'finishOnKey' attribute lets you choose one value that submits the received data when entered. For example, if you set 'finishOnKey' to '\#' and the user enters '1234#', RestComm will immediately stop waiting for more input when the '\#' is received and will submit "Digits=1234" to the 'action' URL. Note that the 'finishOnKey' value is not sent. The allowed values are the digits 0-9, '#', '*' and the empty string (set 'finishOnKey' to ''). If the empty string is used, <Gather> captures all input and no key will end the <Gather> when pressed. In this case RestComm will submit the entered digits to the 'action' URL only after the timeout has been reached. The value can only be a single character.
* *numDigits.* The 'numDigits' attribute lets you set the number of digits you are expecting, and submits the data to the 'action' URL once the caller enters that number of digits.

=== Nesting
You can nest the following verbs within <Gather>: <Say>, <Play>, <Pause>

=== Examples
For an example of how to use the *<Gather>* verb see below.

----
<Response>
<Gather action="handle-user-input.php" numDigits="1">
<Say>Welcome to TPS.</Say>
<Say>For store hours, press 1.</Say>
<Say>To speak to an agent, press 2.</Say>
<Say>To check your package status, press 3.</Say>
</Gather>
<!-- If customer doesn't input anything, prompt and try again. -->
<Say>Sorry, I didn't get your response.</Say>
<Redirect>handle-incoming-call.xml</Redirect>
</Response>
----
= Restcomm RCML – Gather

[[gather]]
== Gather
The *<Gather>* verb supports two modes: *DTMF* and *SPEECH*. In DTMF mode it "gathers" digits that a caller enters into his or her telephone keypad. When the caller is done entering digits, RestComm submits that digits to the provided 'action' URL in an HTTP GET or POST request. In SPEECH mode it "gathers" recognized speech that a caller said. If no input is received before timeout, *<Gather>* falls through to the next verb in the RestComm document. You may optionally nest *<Say>*, *<Play>*, and *<Pause>* verbs within a *<Gather>* verb while waiting for input. This allows you to read menu options to the caller while letting her enter a menu selection at any time. After the first digit is received the audio will stop playing.

=== Gather Attributes

[cols=",,",options="header",]
|======================================================
|Name |Allowed Values |Default Value
|action |relative or absolute URL |current document URL
|method |GET, POST |POST
|timeout |positive integer |5 seconds
|finishOnKey |any digit, #, * |#
|numDigits |integer >= 1 |unlimited
|input |dtmf, speech |dtmf
|partialResultCallback |relative or absolute url |none
|partialResultCallbackMethod |GET, POST |POST
|language |en-US, en-GB, es-ES, it-IT, fr-FR, pl-PL, pt-PT |en-US
|hints |"words, phrases that have many words" |none
|======================================================

* *action.* The 'action' attribute takes an absolute or relative URL as a value. When the caller has finished entering digits RestComm will make a GET or POST request to this URL including the parameters below. If no 'action' is provided, RestComm will by default make a POST request to the current document's URL.

=== Request Parameters

[cols=",",options="header",]
|=======================================================================
|Parameter |Description
|Digits |The digits the caller pressed, excluding the finishOnKey digit.
|SpeechResult |The transcribed result of the speech.
|=======================================================================


* *method.* The 'method' attribute takes the value 'GET' or 'POST'. This tells RestComm whether to request the 'action' URL via HTTP GET or POST.
* *timeout.* The 'timeout' attribute sets the limit in seconds that RestComm will wait for the caller to press another digit before moving on and making a request to the 'action' URL. For example, if 'timeout' is '10', RestComm will wait ten seconds for the caller to press another key before submitting the previously entered digits to the 'action' URL. RestComm waits until completing the execution of all nested verbs before beginning the timeout period.
* *finishOnKey.* The 'finishOnKey' attribute lets you choose one value that submits the received data when entered. For example, if you set 'finishOnKey' to '\#' and the user enters '1234#', RestComm will immediately stop waiting for more input when the '\#' is received and will submit "Digits=1234" to the 'action' URL. Note that the 'finishOnKey' value is not sent. The allowed values are the digits 0-9, '#', '*' and the empty string (set 'finishOnKey' to ''). If the empty string is used, <Gather> captures all input and no key will end the <Gather> when pressed. In this case RestComm will submit the entered digits to the 'action' URL only after the timeout has been reached. The value can only be a single character.
* *numDigits.* The 'numDigits' attribute lets you set the number of digits you are expecting, and submits the data to the 'action' URL once the caller enters that number of digits.
* *input* A list of inputs that RestComm should accept for <Gather>. Can be "dtmf" or "speech". Defaults to "dtmf".
* *partialResultCallback* A relative or fully qualified URL. Is a mandatory attribute for “speech” mode. RestComm will make requests to your partialResultCallback in real-time as speech is recognized.

=== Request Parameters

[cols=",",options="header",]
|=======================================================================
|Parameter |Description
|UnstableSpeechResult |Partially recognized speech the caller said.
|=======================================================================


* *language* The language RestComm should recognize. Defaults to en-US
* *hints* A list of words or phrases that RestComm should expect during recognition. These are very useful for improving recognition of single words or phrases. Entries into hints should be separated by a comma.

=== Nesting
You can nest the following verbs within <Gather>: <Say>, <Play>, <Pause>

=== Examples
For an example of how to use the *<Gather>* verb see below.

----
<Response>
<Gather action="handle-user-input.php" numDigits="1">
<Say>Welcome to TPS.</Say>
<Say>For store hours, press 1.</Say>
<Say>To speak to an agent, press 2.</Say>
<Say>To check your package status, press 3.</Say>
</Gather>
<!-- If customer doesn't input anything, prompt and try again. -->
<Say>Sorry, I didn't get your response.</Say>
<Redirect>handle-incoming-call.xml</Redirect>
</Response>
----
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ public abstract class BaseVoiceInterpreter extends RestcommUntypedActor {
String finishOnKey;
int numberOfDigits = Short.MAX_VALUE;
StringBuffer collectedDigits;
String speechResult;
//Monitoring service
ActorRef monitoring;

Expand Down Expand Up @@ -1702,14 +1703,15 @@ public void execute(final Object message) throws Exception {
// processing
// the current TwiML document with the verb immediately following the <Gather>
Attribute action = verb.attribute(GatherAttributes.ATTRIBUTE_ACTION);
if (action != null && !digits.trim().isEmpty()) {
if (action != null && (!digits.trim().isEmpty() || !StringUtils.isEmpty(speechResult))) {
// Redirect to the action url.
if (digits.endsWith(finishOnKey)) {
final int finishOnKeyIndex = digits.lastIndexOf(finishOnKey);
digits = digits.substring(0, finishOnKeyIndex);
}
final List<NameValuePair> parameters = parameters();
parameters.add(new BasicNameValuePair("Digits", digits));
parameters.add(new BasicNameValuePair("SpeechResult", speechResult));

execHttpRequest(notifications, action, verb.attribute(GatherAttributes.ATTRIBUTE_METHOD), parameters);
return;
Expand All @@ -1734,7 +1736,7 @@ public void execute(final Object message) throws Exception {
final MediaGroupResponse<CollectedResult> asrResponse = (MediaGroupResponse<CollectedResult>) message;

final List<NameValuePair> parameters = parameters();
parameters.add(new BasicNameValuePair("Speech", asrResponse.get().getResult()));
parameters.add(new BasicNameValuePair("UnstableSpeechResult", asrResponse.get().getResult()));

final NotificationsDao notifications = storage.getNotificationsDao();
execHttpRequest(notifications, verb.attribute(GatherAttributes.ATTRIBUTE_PARTIAL_RESULT_CALLBACK),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,9 @@ else if (is(gathering) || is(continuousGathering) || (is(finishGathering) && !su
Object data = dtmfResponse.get();
if (data instanceof CollectedResult && ((CollectedResult)data).isAsr() && ((CollectedResult)data).isPartial()) {
fsm.transition(message, continuousGathering);
} else if (data instanceof CollectedResult && ((CollectedResult)data).isAsr() && !((CollectedResult)data).isPartial() && collectedDigits.length() == 0) {
speechResult = ((CollectedResult)data).getResult();
fsm.transition(message, finishGathering);
} else {
if (sender == call) {
// DTMF using SIP INFO, check if all digits collected here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public void testPartialHangupScenario() throws Exception {

callback = expectMsgClass(HttpRequestDescriptor.class);
assertEquals(callback.getUri(), partialCallbackUri);
assertEquals(findParam(callback.getParameters(), "Speech").getValue(), "1");
assertEquals(findParam(callback.getParameters(), "UnstableSpeechResult").getValue(), "1");

interpreter.tell(new DownloaderResponse(getOkRcml(partialCallbackUri, "")), observer);

Expand All @@ -241,7 +241,14 @@ public void testPartialHangupScenario() throws Exception {

callback = expectMsgClass(HttpRequestDescriptor.class);
assertEquals(callback.getUri(), partialCallbackUri);
assertEquals(findParam(callback.getParameters(), "Speech").getValue(), "12");
assertEquals(findParam(callback.getParameters(), "UnstableSpeechResult").getValue(), "12");

//generate final response
interpreter.tell(new MediaGroupResponse(new CollectedResult("Hello. World.", true, false)), observer);

callback = expectMsgClass(HttpRequestDescriptor.class);
assertEquals(callback.getUri(), actionCallbackUri);
assertEquals(findParam(callback.getParameters(), "SpeechResult").getValue(), "Hello. World.");

interpreter.tell(new DownloaderResponse(getOkRcml(partialCallbackUri, endRcml)), observer);

Expand Down Expand Up @@ -291,7 +298,7 @@ public void testPartialAndPlayScenario() throws Exception {

callback = expectMsgClass(HttpRequestDescriptor.class);
assertEquals(callback.getUri(), partialCallbackUri);
assertEquals(findParam(callback.getParameters(), "Speech").getValue(), "1");
assertEquals(findParam(callback.getParameters(), "UnstableSpeechResult").getValue(), "1");

interpreter.tell(new DownloaderResponse(getOkRcml(partialCallbackUri, "")), observer);

Expand All @@ -300,7 +307,14 @@ public void testPartialAndPlayScenario() throws Exception {

callback = expectMsgClass(HttpRequestDescriptor.class);
assertEquals(callback.getUri(), partialCallbackUri);
assertEquals(findParam(callback.getParameters(), "Speech").getValue(), "12");
assertEquals(findParam(callback.getParameters(), "UnstableSpeechResult").getValue(), "12");

//generate final response
interpreter.tell(new MediaGroupResponse(new CollectedResult("Hello. World.", true, false)), observer);

callback = expectMsgClass(HttpRequestDescriptor.class);
assertEquals(callback.getUri(), actionCallbackUri);
assertEquals(findParam(callback.getParameters(), "SpeechResult").getValue(), "Hello. World.");

interpreter.tell(new DownloaderResponse(getOkRcml(partialCallbackUri, playRcml)), observer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,26 @@ public class AsrSignal {
private final long timeAfterSpeech;
private final String hotWords;
private final String lang;
private final String input;
private final int minNumber;
private final int maxNumber;

/**
*
* @param driver ASR driver
* @param lang speech language
* @param initialPrompts Initial prompt
* @param endInputKey end input key, if present stop ASR with dtmf signal
* @param maximumRecTimer maximum recognition time
* @param waitingInputTimer waiting time to detect user input (gather timeout)
* @param timeAfterSpeech amount of silence necessary after the end of speech (gather timeout)
* @param hotWords hints for speech analyzer tool
* @param input "dtmf", "speech", "dtmf speech"
* @param numberOfDigits number of digits system expects from User
*/

public AsrSignal(String driver, String lang, List<URI> initialPrompts, String endInputKey, long maximumRecTimer, long waitingInputTimer,
long timeAfterSpeech, String hotWords) {
long timeAfterSpeech, String hotWords, String input, int numberOfDigits) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo this constructor deserves to have a java doc, with a brief description of these parameters.. you can take from specification document.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added JavaDoc

this.driver = driver;
this.initialPrompts = initialPrompts;
this.endInputKey = endInputKey;
Expand All @@ -57,6 +74,9 @@ public AsrSignal(String driver, String lang, List<URI> initialPrompts, String en
this.timeAfterSpeech = timeAfterSpeech;
this.hotWords = hotWords;
this.lang = lang;
this.input = input;
this.minNumber = numberOfDigits;
this.maxNumber = numberOfDigits;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a code comment here why we are assigning both members same incoming value

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RMS expects two parameters but there is the only one input 'Number of Digits' in collect in RVD for DTMF mode.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @YevgenL as i suggested in my initial comment, can we add this explanation as comment so its helpful for code reader?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment added

}

@Override
Expand Down Expand Up @@ -106,6 +126,21 @@ public String toString() {
buffer.append(SPACE_CHARACTER);
buffer.append("hw=").append(Hex.encodeHexString(hotWords.getBytes()));
}
if (input != null) {
if (buffer.length() > 0)
buffer.append(SPACE_CHARACTER);
buffer.append("in=").append(input);
}
if (minNumber > 0) {
if (buffer.length() > 0)
buffer.append(SPACE_CHARACTER);
buffer.append("mn=").append(minNumber);
}
if (maxNumber > 0) {
if (buffer.length() > 0)
buffer.append(SPACE_CHARACTER);
buffer.append("mx=").append(maxNumber);
}
return buffer.toString();
}
}
Loading