Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 45571) +++ apps/app_queue.c (working copy) @@ -127,7 +127,7 @@ static char *synopsis = "Queue a call for a call queue"; static char *descrip = -" Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n" +" Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI][|macro]):\n" "Queues an incoming call in a particular call queue as defined in queues.conf.\n" "This application will return to the dialplan if the queue does not exist, or\n" "any of the join options cause the caller to not enter the queue.\n" @@ -150,6 +150,8 @@ "it.\n" " The optional AGI parameter will setup an AGI script to be executed on the \n" "calling party's channel once they are connected to a queue member.\n" +" The optional macro parameter will run a macro on the \n" +"calling party's channel once they are connected to a queue member.\n" " The timeout will cause the queue to fail out after a specified number of\n" "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n" " This application sets the following channel variable upon completion:\n" @@ -353,6 +355,8 @@ unsigned int leavewhenempty:2; unsigned int ringinuse:1; unsigned int setinterfacevar:1; + unsigned int setqueuevar:1; + unsigned int setqueueentryvar:1; unsigned int reportholdtime:1; unsigned int wrapped:1; unsigned int timeoutrestart:1; @@ -370,6 +374,7 @@ int callscompletedinsl; /*!< Number of calls answered with servicelevel*/ char monfmt[8]; /*!< Format to use when recording calls */ int montype; /*!< Monitor type Monitor vs. MixMonitor */ + char membermacro[32]; /*!< Macro to run upon member connection */ char sound_next[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */ char sound_thereare[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */ char sound_calls[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/ @@ -646,8 +651,11 @@ q->servicelevel = 0; q->ringinuse = 1; q->setinterfacevar = 0; + q->setqueuevar = 0; + q->setqueueentryvar = 0; q->autofill = autofill_default; q->montype = montype_default; + q->membermacro[0] = '\0'; q->moh[0] = '\0'; q->announce[0] = '\0'; q->context[0] = '\0'; @@ -786,10 +794,16 @@ q->ringinuse = ast_true(val); } else if (!strcasecmp(param, "setinterfacevar")) { q->setinterfacevar = ast_true(val); + } else if (!strcasecmp(param, "setqueuevar")) { + q->setqueuevar = ast_true(val); + } else if (!strcasecmp(param, "setqueueentryvar")) { + q->setqueueentryvar = ast_true(val); } else if (!strcasecmp(param, "monitor-join")) { q->monjoin = ast_true(val); } else if (!strcasecmp(param, "monitor-format")) { ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); + } else if (!strcasecmp(param, "membermacro")) { + ast_copy_string(q->membermacro, val, sizeof(q->membermacro)); } else if (!strcasecmp(param, "queue-youarenext")) { ast_copy_string(q->sound_next, val, sizeof(q->sound_next)); } else if (!strcasecmp(param, "queue-thereare")) { @@ -2269,7 +2283,7 @@ return 0; } -static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi) +static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi, const char *macro) { struct member *cur; struct callattempt *outgoing = NULL; /* the list of calls we are building */ @@ -2277,6 +2291,7 @@ char oldexten[AST_MAX_EXTENSION]=""; char oldcontext[AST_MAX_CONTEXT]=""; char queuename[256]=""; + char interfacevar[256]=""; struct ast_channel *peer; struct ast_channel *which; struct callattempt *lpeer; @@ -2292,6 +2307,7 @@ struct ast_bridge_config bridge_config; char nondataquality = 1; char *agiexec = NULL; + char *macroexec = NULL; int ret = 0; const char *monitorfilename; const char *monitor_exec; @@ -2303,7 +2319,7 @@ char *p; char vars[2048]; int forwardsallowed = 1; - + float sl = 0; memset(&bridge_config, 0, sizeof(bridge_config)); time(&now); @@ -2586,10 +2602,72 @@ ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); ast_channel_sendurl(peer, url); } + + /* if setinterfacevar is defined, make member variables available to the channel */ + /* use pbx_builtin_setvar to set a load of variables with one call */ if (qe->parent->setinterfacevar) { - pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface); - pbx_builtin_setvar_helper(qe->chan, "MEMBERNAME", member->membername); + snprintf(interfacevar,sizeof(interfacevar), "MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERDYNAMIC=%d", + member->interface, member->membername, member->calls, member->lastcall, member->penalty, member->dynamic); + pbx_builtin_setvar(qe->chan, interfacevar); } + + /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */ + /* use pbx_builtin_setvar to set a load of variables with one call */ + if (qe->parent->setqueueentryvar) { + snprintf(interfacevar,sizeof(interfacevar), "QEHOLDTIME=%ld|QEORIGINALPOS=%d", + (long)time(NULL) - qe->start, qe->opos); + pbx_builtin_setvar(qe->chan, interfacevar); + } + + /* if setqueuevar is defined, make queue variables available to the channel */ + /* use pbx_builtin_setvar to set a load of variables with one call */ + if (qe->parent->setqueuevar) { + sl = 0; + if (qe->parent->callscompleted > 0) + sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted); + snprintf(interfacevar,sizeof(interfacevar), + "QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f", + qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted, + qe->parent->callsabandoned, qe->parent->servicelevel, sl); + } + + /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */ + /* use macro from dialplan if passed as a option, otherwise use the default queue macro */ + if (!ast_strlen_zero(macro)) { + macroexec = ast_strdupa(macro); + } else { + if (qe->parent->membermacro) + macroexec = ast_strdupa(qe->parent->membermacro); + } + + if (!ast_strlen_zero(macroexec)) { + if (option_debug) + ast_log(LOG_DEBUG, "app_queue: macro=%s.\n", macroexec); + + res = ast_autoservice_start(qe->chan); + if (res) { + ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); + res = -1; + } + + app = pbx_findapp("Macro"); + + if (app) { + res = pbx_exec(qe->chan, app, macroexec); + if (option_debug) + ast_log(LOG_DEBUG, "Macro exited with status %d\n", res); + res = 0; + } else { + ast_log(LOG_ERROR, "Could not find application Macro\n"); + res = -1; + } + + if (ast_autoservice_stop(qe->chan) < 0) { + ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); + res = -1; + } + } + if (!ast_strlen_zero(agi)) { if (option_debug) ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi); @@ -3278,6 +3356,7 @@ AST_APP_ARG(announceoverride); AST_APP_ARG(queuetimeoutstr); AST_APP_ARG(agi); + AST_APP_ARG(macro); ); /* Our queue entry */ struct queue_ent qe; @@ -3420,7 +3499,7 @@ } /* Try calling all queue members for 'timeout' seconds */ - res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi); + res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi, args.macro); if (res) { if (res < 0) { if (!qe.handled) { Index: configs/queues.conf.sample =================================================================== --- configs/queues.conf.sample (revision 45571) +++ configs/queues.conf.sample (working copy) @@ -128,13 +128,39 @@ ;maxlen = 0 ; ; If set to yes, just prior to the caller being bridged with a queue member -; the MEMBERINTERFACE variable will be set with the interface name (eg. Agent/1234) -; and the MEMBERNAME variable will be set with the member name (eg. Joe Soap) -; of the queue member that was chosen and is now connected to be bridged with -; the caller +; the following variables will be set +; MEMBERINTERFACE is the interface name (eg. Agent/1234) +; MEMBERNAME is the member name (eg. Joe Soap) +; MEMBERCALLS is the number of calls that interface has taken, +; MEMBERLASTCALL is the last time the member took a call. +; MEMBERPENALTY is the penalty of the member +; MEMBERDYNAMIC indicates if a member is dynamic or not ; ;setinterfacevar=no ; +; If set to yes, just prior to the caller being bridged with a queue member +; the following variables will be set: +; QEHOLDTIME callers hold time +; QEORIGINALPOS original position of the caller in the queue +; +;setqueueentryvar=no +; +; If set to yes, just prior to the caller being bridged with a queue member +; the following variables will be set: +; QUEUEMAX maxmimum number of calls allowed +; QUEUESTRATEGY the strategy of the queue; +; QUEUECALLS number of calls currently in the queue +; QUEUEHOLDTIME current average hold time +; QUEUECOMPLETED number of completed calls for the queue +; QUEUEABANDONED number of abandoned calls +; QUEUESRVLEVEL queue service level +; QUEUESRVLEVELPERF current service level performance +; +;setqueuevar=no +; +; if set, run this macro when connected to the queue member +; membermacro=somemacro + ; How often to announce queue position and/or estimated ; holdtime to caller (0=off) ;