mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 04:30:28 +00:00
Add placeholder IVR application support (static version)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5040 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
177
app.c
177
app.c
@@ -1208,3 +1208,180 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
|
|||||||
cmd = 0;
|
cmd = 0;
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define RES_UPONE (1 << 16)
|
||||||
|
#define RES_EXIT (1 << 17)
|
||||||
|
#define RES_REPEAT (1 << 18)
|
||||||
|
#define RES_RESTART ((1 << 19) | RES_REPEAT)
|
||||||
|
|
||||||
|
static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
|
||||||
|
static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
switch(option->action) {
|
||||||
|
case AST_ACTION_UPONE:
|
||||||
|
return RES_UPONE;
|
||||||
|
case AST_ACTION_EXIT:
|
||||||
|
return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
|
||||||
|
case AST_ACTION_REPEAT:
|
||||||
|
return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
|
||||||
|
case AST_ACTION_RESTART:
|
||||||
|
return RES_RESTART ;
|
||||||
|
case AST_ACTION_NOOP:
|
||||||
|
return 0;
|
||||||
|
case AST_ACTION_BACKGROUND:
|
||||||
|
res = ast_streamfile(chan, (char *)option->adata, chan->language);
|
||||||
|
if (!res) {
|
||||||
|
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
case AST_ACTION_PLAYBACK:
|
||||||
|
res = ast_streamfile(chan, (char *)option->adata, chan->language);
|
||||||
|
if (!res) {
|
||||||
|
res = ast_waitstream(chan, "");
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
case AST_ACTION_MENU:
|
||||||
|
res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
|
||||||
|
return res;
|
||||||
|
case AST_ACTION_CALLBACK:
|
||||||
|
case AST_ACTION_PLAYLIST:
|
||||||
|
case AST_ACTION_TRANSFER:
|
||||||
|
case AST_ACTION_WAITOPTION:
|
||||||
|
ast_log(LOG_NOTICE, "Unimplemented dispatch function %d, ignoring!\n", option->action);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int option_exists(struct ast_ivr_menu *menu, char *option)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
for (x=0;menu->options[x].option;x++)
|
||||||
|
if (!strcasecmp(menu->options[x].option, option))
|
||||||
|
return x;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int option_matchmore(struct ast_ivr_menu *menu, char *option)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
for (x=0;menu->options[x].option;x++)
|
||||||
|
if ((!strncasecmp(menu->options[x].option, option, strlen(option))) &&
|
||||||
|
(menu->options[x].option[strlen(option)]))
|
||||||
|
return x;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
|
||||||
|
{
|
||||||
|
int res=0;
|
||||||
|
int ms;
|
||||||
|
while(option_matchmore(menu, exten)) {
|
||||||
|
ms = chan->pbx ? chan->pbx->dtimeout : 5000;
|
||||||
|
if (strlen(exten) >= maxexten - 1)
|
||||||
|
break;
|
||||||
|
res = ast_waitfordigit(chan, ms);
|
||||||
|
if (res < 1)
|
||||||
|
break;
|
||||||
|
exten[strlen(exten) + 1] = '\0';
|
||||||
|
exten[strlen(exten)] = res;
|
||||||
|
}
|
||||||
|
return res > 0 ? 0 : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
|
||||||
|
{
|
||||||
|
/* Execute an IVR menu structure */
|
||||||
|
int res=0;
|
||||||
|
int pos = 0;
|
||||||
|
int retries = 0;
|
||||||
|
char exten[AST_MAX_EXTENSION] = "s";
|
||||||
|
if (option_exists(menu, "s") < 0) {
|
||||||
|
strcpy(exten, "g");
|
||||||
|
if (option_exists(menu, "g") < 0) {
|
||||||
|
ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(!res) {
|
||||||
|
while(menu->options[pos].option) {
|
||||||
|
if (!strcasecmp(menu->options[pos].option, exten)) {
|
||||||
|
res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
|
||||||
|
if (res < 0)
|
||||||
|
break;
|
||||||
|
else if (res & RES_UPONE)
|
||||||
|
return 0;
|
||||||
|
else if (res & RES_EXIT)
|
||||||
|
return res;
|
||||||
|
else if (res & RES_REPEAT) {
|
||||||
|
int maxretries = res & 0xffff;
|
||||||
|
if (res & RES_RESTART)
|
||||||
|
retries = 0;
|
||||||
|
else
|
||||||
|
retries++;
|
||||||
|
if (!maxretries)
|
||||||
|
maxretries = 3;
|
||||||
|
if ((maxretries > 0) && (retries >= maxretries))
|
||||||
|
return -2;
|
||||||
|
else {
|
||||||
|
if (option_exists(menu, "g") > -1)
|
||||||
|
strcpy(exten, "g");
|
||||||
|
else if (option_exists(menu, "s") > -1)
|
||||||
|
strcpy(exten, "s");
|
||||||
|
}
|
||||||
|
pos=0;
|
||||||
|
} else if (res && strchr(AST_DIGIT_ANY, res)) {
|
||||||
|
ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
|
||||||
|
exten[1] = '\0';
|
||||||
|
exten[0] = res;
|
||||||
|
if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
|
||||||
|
break;
|
||||||
|
if (!option_exists(menu, exten)) {
|
||||||
|
if (option_exists(menu, "i")) {
|
||||||
|
strcpy(exten, "i");
|
||||||
|
pos = 0;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
|
||||||
|
res = -2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pos = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
|
||||||
|
pos = 0;
|
||||||
|
if (!strcasecmp(exten, "s"))
|
||||||
|
strcpy(exten, "g");
|
||||||
|
else if (strcasecmp(exten, "t"))
|
||||||
|
strcpy(exten, "t");
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
res = ast_ivr_menu_run_internal(chan, menu, cbdata);
|
||||||
|
/* Hide internal coding */
|
||||||
|
if (res > 0)
|
||||||
|
res = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@@ -45,6 +45,10 @@ endif
|
|||||||
#APPS+=app_sql_postgres.so
|
#APPS+=app_sql_postgres.so
|
||||||
#APPS+=app_sql_odbc.so
|
#APPS+=app_sql_odbc.so
|
||||||
#APPS+=app_rpt.so
|
#APPS+=app_rpt.so
|
||||||
|
#
|
||||||
|
# Experimental things
|
||||||
|
#
|
||||||
|
#APPS+=app_ivrdemo.so
|
||||||
|
|
||||||
APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
|
APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
|
||||||
APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
|
APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
|
||||||
|
112
apps/app_ivrdemo.c
Executable file
112
apps/app_ivrdemo.c
Executable file
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Asterisk -- A telephony toolkit for Linux.
|
||||||
|
*
|
||||||
|
* Skeleton application
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999, Mark Spencer
|
||||||
|
*
|
||||||
|
* Mark Spencer <markster@linux-support.net>
|
||||||
|
*
|
||||||
|
* This program is free software, distributed under the terms of
|
||||||
|
* the GNU General Public License
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asterisk/file.h>
|
||||||
|
#include <asterisk/logger.h>
|
||||||
|
#include <asterisk/channel.h>
|
||||||
|
#include <asterisk/pbx.h>
|
||||||
|
#include <asterisk/module.h>
|
||||||
|
#include <asterisk/lock.h>
|
||||||
|
#include <asterisk/app.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static char *tdesc = "IVR Demo Application";
|
||||||
|
static char *app = "IVRDemo";
|
||||||
|
static char *synopsis =
|
||||||
|
" This is a skeleton application that shows you the basic structure to create your\n"
|
||||||
|
"own asterisk applications and demonstrates the IVR demo.\n";
|
||||||
|
|
||||||
|
static int ivr_demo_func(struct ast_channel *chan, void *data)
|
||||||
|
{
|
||||||
|
ast_verbose("IVR Demo, data is %s!\n", (char *)data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ast_ivr_menu ivr_submenu = {
|
||||||
|
"IVR Demo Sub Menu", 0, {
|
||||||
|
{ "s", AST_ACTION_BACKGROUND, "demo-abouttotry" },
|
||||||
|
{ "1", AST_ACTION_PLAYBACK, "digits/1" },
|
||||||
|
{ "1", AST_ACTION_PLAYBACK, "digits/1" },
|
||||||
|
{ "1", AST_ACTION_RESTART },
|
||||||
|
{ "2", AST_ACTION_PLAYLIST, "digits/2;digits/3" },
|
||||||
|
{ "*", AST_ACTION_REPEAT },
|
||||||
|
{ "#", AST_ACTION_UPONE },
|
||||||
|
{ NULL },
|
||||||
|
}};
|
||||||
|
|
||||||
|
static struct ast_ivr_menu ivr_demo = {
|
||||||
|
"IVR Demo Main Menu", 0, {
|
||||||
|
{ "s", AST_ACTION_BACKGROUND, "demo-congrats" },
|
||||||
|
{ "g", AST_ACTION_BACKGROUND, "demo-instruct" },
|
||||||
|
{ "g", AST_ACTION_WAITOPTION },
|
||||||
|
{ "1", AST_ACTION_PLAYBACK, "digits/1" },
|
||||||
|
{ "1", AST_ACTION_RESTART },
|
||||||
|
{ "2", AST_ACTION_MENU, &ivr_submenu },
|
||||||
|
{ "2", AST_ACTION_RESTART },
|
||||||
|
{ "i", AST_ACTION_PLAYBACK, "invalid" },
|
||||||
|
{ "i", AST_ACTION_REPEAT, (void *)2 },
|
||||||
|
{ "#", AST_ACTION_EXIT },
|
||||||
|
{ NULL },
|
||||||
|
}};
|
||||||
|
|
||||||
|
STANDARD_LOCAL_USER;
|
||||||
|
|
||||||
|
LOCAL_USER_DECL;
|
||||||
|
|
||||||
|
static int skel_exec(struct ast_channel *chan, void *data)
|
||||||
|
{
|
||||||
|
int res=0;
|
||||||
|
struct localuser *u;
|
||||||
|
if (!data) {
|
||||||
|
ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOCAL_USER_ADD(u);
|
||||||
|
/* Do our thing here */
|
||||||
|
if (chan->_state != AST_STATE_UP)
|
||||||
|
res = ast_answer(chan);
|
||||||
|
if (!res)
|
||||||
|
res = ast_ivr_menu_run(chan, &ivr_demo, data);
|
||||||
|
LOCAL_USER_REMOVE(u);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unload_module(void)
|
||||||
|
{
|
||||||
|
STANDARD_HANGUP_LOCALUSERS;
|
||||||
|
return ast_unregister_application(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_module(void)
|
||||||
|
{
|
||||||
|
return ast_register_application(app, skel_exec, synopsis, tdesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *description(void)
|
||||||
|
{
|
||||||
|
return tdesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usecount(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
STANDARD_USECOUNT(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *key()
|
||||||
|
{
|
||||||
|
return ASTERISK_GPL_KEY;
|
||||||
|
}
|
@@ -18,6 +18,53 @@
|
|||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* IVR stuff */
|
||||||
|
|
||||||
|
/* Callback function for IVR, returns 0 on completion, -1 on hangup or digit if
|
||||||
|
interrupted */
|
||||||
|
typedef int (*ast_ivr_callback)(struct ast_channel *chan, char *option, void *cbdata);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AST_ACTION_UPONE, /* adata is unused */
|
||||||
|
AST_ACTION_EXIT, /* adata is the return value for ast_ivr_menu_run if channel was not hungup */
|
||||||
|
AST_ACTION_CALLBACK, /* adata is an ast_ivr_callback */
|
||||||
|
AST_ACTION_PLAYBACK, /* adata is file to play */
|
||||||
|
AST_ACTION_BACKGROUND, /* adata is file to play */
|
||||||
|
AST_ACTION_PLAYLIST, /* adata is list of files, separated by ; to play */
|
||||||
|
AST_ACTION_MENU, /* adata is a pointer to an ast_ivr_menu */
|
||||||
|
AST_ACTION_REPEAT, /* adata is max # of repeats, cast to a pointer */
|
||||||
|
AST_ACTION_RESTART, /* adata is like repeat, but resets repeats to 0 */
|
||||||
|
AST_ACTION_TRANSFER, /* adata is a string with exten[@context] */
|
||||||
|
AST_ACTION_WAITOPTION, /* adata is a timeout, or 0 for defaults */
|
||||||
|
AST_ACTION_NOOP, /* adata is unused */
|
||||||
|
} ast_ivr_action;
|
||||||
|
|
||||||
|
struct ast_ivr_option {
|
||||||
|
char *option;
|
||||||
|
ast_ivr_action action;
|
||||||
|
void *adata;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Special "options" are:
|
||||||
|
"s" - "start here (one time greeting)"
|
||||||
|
"g" - "greeting/instructions"
|
||||||
|
"t" - "timeout"
|
||||||
|
"h" - "hangup"
|
||||||
|
"i" - "invalid selection"
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ast_ivr_menu {
|
||||||
|
char *title; /* Title of menu */
|
||||||
|
unsigned int flags; /* Flags */
|
||||||
|
struct ast_ivr_option options[]; /* All options */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Runs an IVR menu, returns 0 on successful completion, -1 on hangup, or -2 on user error in menu */
|
||||||
|
extern int ast_ivr_menu_run(struct ast_channel *c, struct ast_ivr_menu *menu, void *cbdata);
|
||||||
|
|
||||||
/*! Plays a stream and gets DTMF data from a channel */
|
/*! Plays a stream and gets DTMF data from a channel */
|
||||||
/*!
|
/*!
|
||||||
* \param c Which channel one is interacting with
|
* \param c Which channel one is interacting with
|
||||||
|
Reference in New Issue
Block a user