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
This commit is contained in:
Michael Jerris 2006-11-13 16:29:16 +00:00
parent 4619302f8f
commit 28c875cfa9
4 changed files with 331 additions and 4 deletions

View File

@ -28,7 +28,7 @@ that much better:
Justin Unger - <justinunger at gmail dot com> Lots of help with patches and SIP testing. Thanks!
Paul D. Tinsley - Various patches and support. <pdt at jackhammer.org>
Ken Rice of Asteria Solutions Group, INC <ken AT asteriasgi.com> - xmlcdr, sofia improvements, load testing.
Neal Horman <neal at wanlink dot com> - conference improvements and other tweaks.
Neal Horman <neal at wanlink dot com> - conference improvements, switch_ivr menu additions and other tweaks.
A big THANK YOU goes to:

View File

@ -24,6 +24,7 @@
* Contributor(s):
*
* Anthony Minessale II <anthmct@yahoo.com>
* Neal Horman <neal at wanlink dot com>
*
*
* 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

View File

@ -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

View File

@ -25,6 +25,7 @@
*
* Anthony Minessale II <anthmct@yahoo.com>
* Paul D. Tinsley <pdt at jackhammer.org>
* Neal Horman <neal at wanlink dot com>
*
* 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<iam_qty && !found; i++) {
found = (strcasecmp(iam[i].name,action) == 0);
}
if (found) {
i--;
// do we need to build a new sub-menu ?
if (iam[i].action == SWITCH_IVR_ACTION_EXECMENU && switch_ivr_find_menu(*menu_stack, param) == NULL) {
if ((xml_menu = switch_xml_find_child(xml_menus, "menu", "name", param)) != NULL) {
status = switch_ivr_build_xml_menu_stack(menu_stack, xml_menus, xml_menu, pool);
}
}
// finally bind the menu entry
if (status == SWITCH_STATUS_SUCCESS) {
status = switch_ivr_menu_bind_action(menu, iam[i].action, param, digits);
}
}
} else {
status = SWITCH_STATUS_FALSE;
}
}
}
if (status != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to build xml menu '%s'\n",menu_name);
}
return status;
}