app_queue: Add option for predial handlers on caller and callee channels

Add predial handler support to app_queue.  app_dial (ASTERISK_19548) and
app_originate (ASTERISK_26587) have the ability to execute predial
handlers on caller and callee channels.  This patch adds predial handlers
to app_queue and uses the same options as Dial and Originate (b and B).
The caller routine gets executed when the caller first enters the queue.
The callee routine gets executed for each queue member when they are about
to be called.

ASTERISK-27912

Change-Id: I5acf5c32587ee008658d12e8a8049eb8fa4d0f24
This commit is contained in:
Kristian F. Høgh
2018-06-13 09:25:12 +02:00
committed by Richard Mudgett
parent d87631d21f
commit 184b375b41
2 changed files with 76 additions and 1 deletions

View File

@@ -67,6 +67,10 @@ app_queue
When set the wrapuptime on the member is used instead of the wrapuptime When set the wrapuptime on the member is used instead of the wrapuptime
defined for the queue itself. defined for the queue itself.
* Added predial handler support for caller and callee channels with the
B and b options respectively. This is similar to the predial support
in app_dial.
res_config_sqlite res_config_sqlite
------------------ ------------------
* The res_config_sqlite module is now deprecated, users should migrate to the * The res_config_sqlite module is now deprecated, users should migrate to the

View File

@@ -133,6 +133,27 @@
<parameter name="queuename" required="true" /> <parameter name="queuename" required="true" />
<parameter name="options"> <parameter name="options">
<optionlist> <optionlist>
<option name="b" argsep="^">
<para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
location using the newly created channel. The <literal>Gosub</literal> will be
executed for each destination channel.</para>
<argument name="context" required="false" />
<argument name="exten" required="false" />
<argument name="priority" required="true" hasparams="optional" argsep="^">
<argument name="arg1" multiple="true" required="true" />
<argument name="argN" />
</argument>
</option>
<option name="B" argsep="^">
<para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
specified location using the current channel.</para>
<argument name="context" required="false" />
<argument name="exten" required="false" />
<argument name="priority" required="true" hasparams="optional" argsep="^">
<argument name="arg1" multiple="true" required="true" />
<argument name="argN" />
</argument>
</option>
<option name="C"> <option name="C">
<para>Mark all calls as "answered elsewhere" when cancelled.</para> <para>Mark all calls as "answered elsewhere" when cancelled.</para>
</option> </option>
@@ -1325,15 +1346,21 @@ enum {
OPT_CALLER_AUTOMIXMON = (1 << 16), OPT_CALLER_AUTOMIXMON = (1 << 16),
OPT_CALLEE_AUTOMON = (1 << 17), OPT_CALLEE_AUTOMON = (1 << 17),
OPT_CALLER_AUTOMON = (1 << 18), OPT_CALLER_AUTOMON = (1 << 18),
OPT_PREDIAL_CALLEE = (1 << 19),
OPT_PREDIAL_CALLER = (1 << 20),
}; };
enum { enum {
OPT_ARG_CALLEE_GO_ON = 0, OPT_ARG_CALLEE_GO_ON = 0,
OPT_ARG_PREDIAL_CALLEE,
OPT_ARG_PREDIAL_CALLER,
/* note: this entry _MUST_ be the last one in the enum */ /* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE OPT_ARG_ARRAY_SIZE
}; };
AST_APP_OPTIONS(queue_exec_options, BEGIN_OPTIONS AST_APP_OPTIONS(queue_exec_options, BEGIN_OPTIONS
AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
AST_APP_OPTION('C', OPT_MARK_AS_ANSWERED), AST_APP_OPTION('C', OPT_MARK_AS_ANSWERED),
AST_APP_OPTION('c', OPT_GO_ON), AST_APP_OPTION('c', OPT_GO_ON),
AST_APP_OPTION('d', OPT_DATA_QUALITY), AST_APP_OPTION('d', OPT_DATA_QUALITY),
@@ -1545,6 +1572,7 @@ struct queue_ent {
char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */ char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */
char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */ char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */ char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
const char *predial_callee; /*!< Gosub app arguments for outgoing calls. NULL if not supplied. */
int valid_digits; /*!< Digits entered correspond to valid extension. Exited */ int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
int pos; /*!< Where we are in the queue */ int pos; /*!< Where we are in the queue */
int prio; /*!< Our priority */ int prio; /*!< Our priority */
@@ -4559,6 +4587,11 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
ast_channel_unlock(tmp->chan); ast_channel_unlock(tmp->chan);
ast_channel_unlock(qe->chan); ast_channel_unlock(qe->chan);
/* PREDIAL: Run gosub on the callee's channel */
if (qe->predial_callee) {
ast_pre_call(tmp->chan, qe->predial_callee);
}
/* Place the call, but don't wait on the answer */ /* Place the call, but don't wait on the answer */
if ((res = ast_call(tmp->chan, location, 0))) { if ((res = ast_call(tmp->chan, location, 0))) {
/* Again, keep going even if there's an error */ /* Again, keep going even if there's an error */
@@ -4615,6 +4648,16 @@ static struct callattempt *find_best(struct callattempt *outgoing)
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies) static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
{ {
int ret = 0; int ret = 0;
struct callattempt *cur;
if (qe->predial_callee) {
ast_autoservice_start(qe->chan);
for (cur = outgoing; cur; cur = cur->q_next) {
if (cur->stillgoing && cur->chan) {
ast_autoservice_start(cur->chan);
}
}
}
while (ret == 0) { while (ret == 0) {
struct callattempt *best = find_best(outgoing); struct callattempt *best = find_best(outgoing);
@@ -4623,18 +4666,23 @@ static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *bus
break; break;
} }
if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
struct callattempt *cur;
/* Ring everyone who shares this best metric (for ringall) */ /* Ring everyone who shares this best metric (for ringall) */
for (cur = outgoing; cur; cur = cur->q_next) { for (cur = outgoing; cur; cur = cur->q_next) {
if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
ret |= ring_entry(qe, cur, busies); ret |= ring_entry(qe, cur, busies);
if (qe->predial_callee && cur->chan) {
ast_autoservice_start(cur->chan);
}
} }
} }
} else { } else {
/* Ring just the best channel */ /* Ring just the best channel */
ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric); ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
ret = ring_entry(qe, best, busies); ret = ring_entry(qe, best, busies);
if (qe->predial_callee && cur->chan) {
ast_autoservice_start(best->chan);
}
} }
/* If we have timed out, break out */ /* If we have timed out, break out */
@@ -4644,6 +4692,14 @@ static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *bus
break; break;
} }
} }
if (qe->predial_callee) {
for (cur = outgoing; cur; cur = cur->q_next) {
if (cur->stillgoing && cur->chan) {
ast_autoservice_stop(cur->chan);
}
}
ast_autoservice_stop(qe->chan);
}
return ret; return ret;
} }
@@ -8270,6 +8326,21 @@ static int queue_exec(struct ast_channel *chan, const char *data)
S_OR(args.url, ""), S_OR(args.url, ""),
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
qe.opos); qe.opos);
/* PREDIAL: Preprocess any callee gosub arguments. */
if (ast_test_flag(&opts, OPT_PREDIAL_CALLEE)
&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]);
qe.predial_callee = opt_args[OPT_ARG_PREDIAL_CALLEE];
}
/* PREDIAL: Run gosub on the caller's channel */
if (ast_test_flag(&opts, OPT_PREDIAL_CALLER)
&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]);
ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
}
copy_rules(&qe, args.rule); copy_rules(&qe, args.rule);
qe.pr = AST_LIST_FIRST(&qe.qe_rules); qe.pr = AST_LIST_FIRST(&qe.qe_rules);
check_turns: check_turns: