From 28c875cfa92809a3ff9d09d3d530865b424ac4b4 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Mon, 13 Nov 2006 16:29:16 +0000 Subject: [PATCH] add switch_ivr_digit_stream functions and switch_ivr_build_xml_menu_stack to switch ivr, merged from knhor branch. thanks neal! git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3341 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- AUTHORS | 4 +- src/include/switch_ivr.h | 74 ++++++++++- src/include/switch_types.h | 2 + src/switch_ivr.c | 255 ++++++++++++++++++++++++++++++++++++- 4 files changed, 331 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 31a8f30545..9df1dd89bd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -26,9 +26,9 @@ that much better: Yossi Neiman of Cartis Solutions, Inc. - implementation of mod_cdr (perldd, mysql, csv) Stefan Knoblich - Various patches and support. Thanks. Justin Unger - Lots of help with patches and SIP testing. Thanks! - Paul D. Tinsley - Various patches and support. + Paul D. Tinsley - Various patches and support. Ken Rice of Asteria Solutions Group, INC - xmlcdr, sofia improvements, load testing. - Neal Horman - conference improvements and other tweaks. + Neal Horman - conference improvements, switch_ivr menu additions and other tweaks. A big THANK YOU goes to: diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 65cf1c7395..1e4102bafa 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Neal Horman * * * switch_ivr.h -- IVR Library @@ -402,6 +403,63 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, swi */ SWITCH_DECLARE(switch_status_t) switch_ivr_transfer_variable(switch_core_session_t *sessa, switch_core_session_t *sessb, char *var); + +struct switch_ivr_digit_stream_parser; +typedef struct switch_ivr_digit_stream_parser switch_ivr_digit_stream_parser_t; +/*! + \brief Create a digit stream parser object + \param pool the pool to use for the new hash + \param parser a pointer to the object pointer + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_new(switch_memory_pool_t *pool, switch_ivr_digit_stream_parser_t **parser); + +/*! + \brief Destroy a digit stream parser object + \param parser a pointer to the object pointer + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_destroy(switch_ivr_digit_stream_parser_t **parser); + +/*! + \brief Set a digit string to action mapping + \param parser a pointer to the parser object created by switch_ivr_digit_stream_parser_new + \param digits a string of digits to associate with an action + \param data consumer data attached to this digit string + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_set_event(switch_ivr_digit_stream_parser_t *parser, char *digits, void *data); + +/*! + \brief Delete a string to action mapping + \param parser a pointer to the parser object created by switch_ivr_digit_stream_parser_new + \param digits the digit string to be removed from the map + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_del_event(switch_ivr_digit_stream_parser_t *parser, char *digits); + +/*! + \brief Create a digit stream parser object + \param parser a pointer to the parser object created by switch_ivr_digit_stream_parser_new + \param digit a digit to collect and test against the map of digit strings + \return NULL if no match found or consumer data that was associated with a given digit string when matched +*/ +SWITCH_DECLARE(void *) switch_ivr_digit_stream_parser_feed(switch_ivr_digit_stream_parser_t *parser, char digit); + +/*! + \brief Reset the collected digit stream to nothing + \param parser a pointer to the parser object created by switch_ivr_digit_stream_parser_new + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_reset(switch_ivr_digit_stream_parser_t *parser); + +/*! + \brief Set a digit string terminator + \param parser a pointer to the parser object created by switch_ivr_digit_stream_parser_new + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_set_terminator(switch_ivr_digit_stream_parser_t *parser, char digit); + /** @} */ /** @@ -442,10 +500,12 @@ typedef struct switch_ivr_menu_action switch_ivr_menu_action_t; *\param greeting_sound Optional pointer to a main sound (press 1 for this 2 for that). *\param short_greeting_sound Optional pointer to a shorter main sound for subsequent loops. *\param invalid_sound Optional pointer to a sound to play after invalid input. + *\param tts_engine Text To Speech engine name + *\param tts_voice Text To Speech engine voice name *\param timeout A number of milliseconds to pause before looping. *\param max_failures Maximum number of failures to withstand before hangingup This resets everytime you enter the menu. *\param pool memory pool (NULL to create one) - *\return SWUTCH_STATUS_SUCCESS if the menu was created + *\return SWITCH_STATUS_SUCCESS if the menu was created */ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_init(switch_ivr_menu_t **new_menu, switch_ivr_menu_t *main, @@ -500,6 +560,18 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_execute(switch_core_session_t *s */ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_free_stack(switch_ivr_menu_t *stack); +/*! + *\brief build a menu stack from an xml source + *\param menu_stack The menu stack object that will be created for you + *\param xml_menus The xml Menus source + *\param xml_menu The xml Menu source of the menu to be created + *\param pool memory pool (NULL to create one) + *\return SWITCH_STATUS_SUCCESS if all is well + */ +SWITCH_DECLARE(switch_status_t) switch_ivr_build_xml_menu_stack(switch_ivr_menu_t **menu_stack, + switch_xml_t xml_menus, + switch_xml_t xml_menu, + switch_memory_pool_t *pool); /** @} */ SWITCH_END_EXTERN_C diff --git a/src/include/switch_types.h b/src/include/switch_types.h index fe1b41595d..e553d54605 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -905,6 +905,8 @@ struct switch_channel; struct switch_core_session; /*! \brief An audio bug */ struct switch_media_bug; +/*! \brief A digit stream parser object */ +struct switch_ivr_digit_stream_parser; SWITCH_END_EXTERN_C diff --git a/src/switch_ivr.c b/src/switch_ivr.c index a2d7c20314..28e5183a23 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -25,6 +25,7 @@ * * Anthony Minessale II * Paul D. Tinsley + * Neal Horman * * switch_ivr_api.c -- IVR Library * @@ -3254,6 +3255,168 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_transfer_variable(switch_core_session return SWITCH_STATUS_SUCCESS; } +struct switch_ivr_digit_stream_parser { + int pool_auto_created; + switch_memory_pool_t *pool; + switch_hash_t *hash; + char *digits; + char terminator; + switch_size_t maxlen; +}; + +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_new(switch_memory_pool_t *pool, switch_ivr_digit_stream_parser_t **parser) +{ switch_status_t status = SWITCH_STATUS_FALSE; + + if(parser != NULL) { + int pool_auto_created = 0; + + // if the caller didn't provide a pool, make one + if (pool == NULL) { + switch_core_new_memory_pool(&pool); + if (pool != NULL) { + pool_auto_created = 1; + } + } + + // if we have a pool, make a parser object + if (pool != NULL) { + *parser = (switch_ivr_digit_stream_parser_t *)switch_core_alloc(pool,sizeof(switch_ivr_digit_stream_parser_t)); + } + + // if we have parser object, initialize it for the caller + if (*parser != NULL) { + memset(*parser,0,sizeof(switch_ivr_digit_stream_parser_t)); + (*parser)->pool_auto_created = pool_auto_created; + (*parser)->pool = pool; + switch_core_hash_init(&(*parser)->hash,(*parser)->pool); + + status = SWITCH_STATUS_SUCCESS; + } else { + status = SWITCH_STATUS_MEMERR; + // clean up the pool if we created it + if (pool != NULL && pool_auto_created) { + switch_core_destroy_memory_pool(&pool); + } + } + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_destroy(switch_ivr_digit_stream_parser_t **parser) +{ switch_status_t status = SWITCH_STATUS_FALSE; + + if (parser != NULL && *parser != NULL) { + if ((*parser)->hash != NULL) { + switch_core_hash_destroy((*parser)->hash); + (*parser)->hash = NULL; + } + // free the memory pool if we created it + if ((*parser)->pool_auto_created && (*parser)->pool != NULL) { + status = switch_core_destroy_memory_pool(&(*parser)->pool); + } + // clean up for the caller + *parser = NULL; + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_set_event(switch_ivr_digit_stream_parser_t *parser, char *digits, void *data) +{ switch_status_t status = SWITCH_STATUS_FALSE; + + if (parser != NULL && digits != NULL && *digits && parser->hash != NULL) { + switch_size_t len; + + status = switch_core_hash_insert_dup(parser->hash,digits,data); + if (status == SWITCH_STATUS_SUCCESS && parser->terminator == '\0' && (len = strlen(digits)) > parser->maxlen) { + parser->maxlen = len; + } + } + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "unable to add hash for '%s'\n",digits); + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_del_event(switch_ivr_digit_stream_parser_t *parser, char *digits) +{ switch_status_t status = SWITCH_STATUS_FALSE; + + if (parser != NULL && digits != NULL && *digits) { + status = switch_core_hash_delete(parser->hash,digits); + } + + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "unable to del hash for '%s'\n",digits); + } + + return status; +} + +SWITCH_DECLARE(void *) switch_ivr_digit_stream_parser_feed(switch_ivr_digit_stream_parser_t *parser, char digit) +{ void *result = NULL; + + if (parser != NULL && digit != '\0') { + size_t len = (parser->digits != NULL ? strlen(parser->digits) : 0); + + // if it's not a terminator digit, add it to the collected digits + if (digit != parser->terminator) { + // if collected digits length >= the max length of the keys + // in the hash table, then left shift the digit string + if ( len > 1 && parser->maxlen != 0 && len >= parser->maxlen) { + char *src = parser->digits + 1; + char *dst = parser->digits; + + while (*src) { + *(dst++) = *(src++); + } + *dst = digit; + } else { + parser->digits = realloc(parser->digits,len+2); + *(parser->digits+(len++)) = digit; + *(parser->digits+len) = '\0'; + } + } + + // if we have digits to test + if (len) { + result = switch_core_hash_find(parser->hash,parser->digits); + // if we matched the digit string, or this digit is the terminator + // reset the collected digits for next digit string + if (result != NULL || parser->terminator == digit) { + free(parser->digits); + parser->digits = NULL; + } + } + } + + return result; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_reset(switch_ivr_digit_stream_parser_t *parser) +{ switch_status_t status = SWITCH_STATUS_FALSE; + + if (parser != NULL && parser->digits != NULL) { + free(parser->digits); + parser->digits = NULL; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_digit_stream_parser_set_terminator(switch_ivr_digit_stream_parser_t *parser, char digit) +{ switch_status_t status = SWITCH_STATUS_FALSE; + + if (parser != NULL) { + parser->terminator = digit; + parser->maxlen = 0; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} struct switch_ivr_menu_action; @@ -3533,15 +3696,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_execute(switch_core_session_t *s if (!strcmp(menu->buf, ap->bind)) { char *membuf; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IVR menu %s matched %s\n", menu->name, menu->buf); match++; errs = 0; if (ap->function) { todo = ap->function(menu, arg, sizeof(arg), obj); aptr = arg; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IVR function on menu '%s' matched '%s'\n", menu->name, menu->buf); } else { todo = ap->ivr_action; aptr = ap->arg; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IVR action on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf,aptr); } switch(todo) { @@ -3566,6 +3730,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_execute(switch_core_session_t *s break; case SWITCH_IVR_ACTION_EXECAPP: { const switch_application_interface_t *application_interface; + if ((membuf = strdup(aptr))) { char *app_name = membuf; char *app_arg = strchr(app_name, ' '); @@ -3630,3 +3795,91 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_execute(switch_core_session_t *s return status; } +SWITCH_DECLARE(switch_status_t) switch_ivr_build_xml_menu_stack(switch_ivr_menu_t **menu_stack, + switch_xml_t xml_menus, + switch_xml_t xml_menu, + switch_memory_pool_t *pool) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + char *menu_name = (char *)switch_xml_attr_soft(xml_menu,"name"); // if the attr doesn't exist, return "" + char *greet_long = (char *)switch_xml_attr(xml_menu,"greet-long"); // if the attr doesn't exist, return NULL + char *greet_short = (char *)switch_xml_attr(xml_menu,"greet-short"); // if the attr doesn't exist, return NULL + char *invalid_sound = (char *)switch_xml_attr(xml_menu,"invalid-sound"); // if the attr doesn't exist, return NULL + char *tts_engine = (char *)switch_xml_attr(xml_menu,"tts-engine"); // if the attr doesn't exist, return NULL + char *tts_voice = (char *)switch_xml_attr(xml_menu,"tts-voice"); // if the attr doesn't exist, return NULL + char *timeout = (char *)switch_xml_attr_soft(xml_menu,"timeout"); // if the attr doesn't exist, return "" + char *max_failures = (char *)switch_xml_attr_soft(xml_menu,"max-failures"); // if the attr doesn't exist, return "" + switch_ivr_menu_t *menu = NULL; + + status = switch_ivr_menu_init(&menu, + *menu_stack, + menu_name, + greet_long, + greet_short, + invalid_sound, + tts_engine, + tts_voice, + atoi(timeout)*1000, + atoi(max_failures), + pool + ); + if (status == SWITCH_STATUS_SUCCESS) { + switch_xml_t xml_kvp; + struct ivr_action_map { + char *name; + switch_ivr_action_t action; + } iam [] = { + {"exit", SWITCH_IVR_ACTION_DIE}, + {"menu-sub", SWITCH_IVR_ACTION_EXECMENU}, + {"exec-api", SWITCH_IVR_ACTION_EXECAPP}, + {"play-sound", SWITCH_IVR_ACTION_PLAYSOUND}, + {"say-text", SWITCH_IVR_ACTION_SAYTEXT}, + {"menu-back", SWITCH_IVR_ACTION_BACK}, + {"menu-top", SWITCH_IVR_ACTION_TOMAIN}, + {"call-transfer", SWITCH_IVR_ACTION_TRANSFER}, + }; + int iam_qty = sizeof(iam)/sizeof(iam[0]); + + // set the menu stack for the caller + if (*menu_stack == NULL) { + *menu_stack = menu; + } + + // build menu entries + for(xml_kvp = switch_xml_child(xml_menu, "entry"); xml_kvp != NULL && status == SWITCH_STATUS_SUCCESS; xml_kvp = xml_kvp->next) { + char *action = (char *)switch_xml_attr(xml_kvp, "action"); + char *digits = (char *)switch_xml_attr(xml_kvp, "digits"); + char *param = (char *)switch_xml_attr_soft(xml_kvp, "param"); + + if (!switch_strlen_zero(action) && !switch_strlen_zero(digits)) { + int i,found; + + for(i=0,found=0; i