/*
* mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- * Copyright (C) 2013-2014, Grasshopper
+ * Copyright (C) 2013-2018, Grasshopper
*
* Version: MPL 1.1
*
#include <iksemel.h>
#include "nlsml.h"
+#include "iks_helpers.h"
struct nlsml_parser;
}
/**
- * Construct an NLSML result for digit match
- * @param digits the matching digits
+ * Construct an NLSML result for match
+ * @param match the matching digits or text
* @param interpretation the optional digit interpretation
+ * @param mode dtmf or speech
+ * @param confidence 0-100
* @return the NLSML <result>
*/
-iks *nlsml_create_dtmf_match(const char *digits, const char *interpretation)
+iks *nlsml_create_match(const char *match, const char *interpretation, const char *mode, int confidence)
{
iks *result = iks_new("result");
iks_insert_attrib(result, "xmlns", NLSML_NS);
iks_insert_attrib(result, "xmlns:xf", "http://www.w3.org/2000/xforms");
- if (!zstr(digits)) {
- int first = 1;
- int i;
- int num_digits = strlen(digits);
- switch_stream_handle_t stream = { 0 };
-
+ if (!zstr(match)) {
iks *interpretation_node = iks_insert(result, "interpretation");
iks *input_node = iks_insert(interpretation_node, "input");
iks *instance_node = iks_insert(interpretation_node, "instance");
- iks_insert_attrib(input_node, "mode", "dtmf");
- iks_insert_attrib(input_node, "confidence", "100");
-
- SWITCH_STANDARD_STREAM(stream);
- for (i = 0; i < num_digits; i++) {
- if (isdtmf(digits[i])) {
- if (first) {
- stream.write_function(&stream, "%c", digits[i]);
- first = 0;
- } else {
- stream.write_function(&stream, " %c", digits[i]);
- }
- }
- }
- iks_insert_cdata(input_node, stream.data, strlen(stream.data));
-
+ iks_insert_attrib(input_node, "mode", mode);
+ iks_insert_attrib_printf(input_node, "confidence", "%d", confidence);
+ iks_insert_cdata(input_node, match, strlen(match));
if (zstr(interpretation)) {
- iks_insert_cdata(instance_node, stream.data, strlen(stream.data));
+ iks_insert_cdata(instance_node, match, strlen(match));
} else {
iks_insert_cdata(instance_node, interpretation, strlen(interpretation));
}
- switch_safe_free(stream.data);
}
return result;
}
+/**
+ * Construct an NLSML result for match
+ * @param match the matching digits or text
+ * @param interpretation the optional digit interpretation
+ * @return the NLSML <result>
+ */
+iks *nlsml_create_dtmf_match(const char *digits, const char *interpretation)
+{
+ iks *result = NULL;
+ int first = 1;
+ int i;
+ int num_digits = strlen(digits);
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ for (i = 0; i < num_digits; i++) {
+ if (isdtmf(digits[i])) {
+ if (first) {
+ stream.write_function(&stream, "%c", digits[i]);
+ first = 0;
+ } else {
+ stream.write_function(&stream, " %c", digits[i]);
+ }
+ }
+ }
+ result = nlsml_create_match((const char *)stream.data, interpretation, "dtmf", 100);
+ switch_safe_free(stream.data);
+ return result;
+}
+
/**
* Initialize NLSML parser. This function is not thread safe.
*/
return iks_new_iq_result(iq);
}
+/**
+ * Get text / error from result
+ */
+static const char *get_detected_speech_result_text(cJSON *result_json, double *confidence, const char **error_text)
+{
+ const char *result_text = NULL;
+ const char *text = cJSON_GetObjectCstr(result_json, "text");
+ if (confidence) {
+ *confidence = 0.0;
+ }
+ if (!zstr(text)) {
+ cJSON *json_confidence = cJSON_GetObjectItem(result_json, "confidence");
+ if (json_confidence && json_confidence->valuedouble > 0.0) {
+ *confidence = json_confidence->valuedouble;
+ } else {
+ *confidence = 100.0;
+ }
+ result_text = text;
+ } else if (error_text) {
+ *error_text = cJSON_GetObjectCstr(result_json, "error");
+ }
+ return result_text;
+}
+
/**
* Handle speech detection event
*/
if (zstr(result)) {
rayo_component_send_complete(component, INPUT_NOMATCH);
} else {
- if (strchr(result, '<')) {
+ if (result[0] == '{') {
+ // internal FS JSON format
+ cJSON *json_result = cJSON_Parse(result);
+ if (json_result) {
+ // examine result to determine what happened
+ double confidence = 0.0;
+ const char *error_text = NULL;
+ const char *result_text = NULL;
+ result_text = get_detected_speech_result_text(json_result, &confidence, &error_text);
+ if (!zstr(result_text)) {
+ // got result... send as NLSML
+ iks *result = nlsml_create_match(result_text, NULL, "speech", (int)confidence);
+ /* notify of match */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "MATCH = %s\n", result_text);
+ send_match_event(RAYO_COMPONENT(component), result);
+ iks_delete(result);
+ } else if (zstr(error_text)) {
+ // unknown error
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "No matching text nor error in result: %s!\n", result);
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ } else if (!strcmp(error_text, "no_input")) {
+ // no input error
+ rayo_component_send_complete(component, INPUT_NOINPUT);
+ } else if (!strcmp(error_text, "no_match")) {
+ // no match error
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ } else {
+ // generic error
+ iks *response = rayo_component_create_complete_event(component, COMPONENT_COMPLETE_ERROR);
+ iks *error = NULL;
+ if ((error = iks_find(response, "complete"))) {
+ if ((error = iks_find(error, "error"))) {
+ iks_insert_cdata(error, error_text, strlen(error_text));
+ }
+ }
+ rayo_component_send_complete_event(component, response);
+ }
+ cJSON_Delete(json_result);
+ } else {
+ // failed to parse JSON result
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse JSON result: %s!\n", result);
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ }
+ } else if (strchr(result, '<')) {
/* got an XML result */
enum nlsml_match_type match_type = nlsml_parse(result, uuid);
switch (match_type) {