mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-23 13:09:00 +00:00
Merge queue changes from Bug #214
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@2415 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
2
CHANGES
2
CHANGES
@@ -1,3 +1,5 @@
|
|||||||
|
-- ADPCM Standardization
|
||||||
|
-- Numerous bug fixes
|
||||||
-- Add IAX2 Firmware Support
|
-- Add IAX2 Firmware Support
|
||||||
-- Add G.726 support
|
-- Add G.726 support
|
||||||
-- Add ices/icecast support
|
-- Add ices/icecast support
|
||||||
|
2
Makefile
2
Makefile
@@ -269,7 +269,7 @@ datafiles: all
|
|||||||
exit 1; \
|
exit 1; \
|
||||||
fi; \
|
fi; \
|
||||||
done
|
done
|
||||||
for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-* sounds/agent-* sounds/invalid* sounds/tt-* sounds/auth-* sounds/privacy-*; do \
|
for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-* sounds/agent-* sounds/invalid* sounds/tt-* sounds/auth-* sounds/privacy-* sounds/queue-*; do \
|
||||||
if grep -q "^%`basename $$x`%" sounds.txt; then \
|
if grep -q "^%`basename $$x`%" sounds.txt; then \
|
||||||
install -m 644 $$x $(DESTDIR)$(ASTVARLIBDIR)/sounds ; \
|
install -m 644 $$x $(DESTDIR)$(ASTVARLIBDIR)/sounds ; \
|
||||||
else \
|
else \
|
||||||
|
304
apps/app_queue.c
304
apps/app_queue.c
@@ -7,6 +7,21 @@
|
|||||||
*
|
*
|
||||||
* Mark Spencer <markster@linux-support.net>
|
* Mark Spencer <markster@linux-support.net>
|
||||||
*
|
*
|
||||||
|
* These features added by David C. Troy <dave@toad.net>:
|
||||||
|
* - Per-queue holdtime calculation
|
||||||
|
* - Estimated holdtime announcement
|
||||||
|
* - Position announcement
|
||||||
|
* - Abandoned/completed call counters
|
||||||
|
* - Failout timer passed as optional app parameter
|
||||||
|
* - Optional monitoring of calls, started when call is answered
|
||||||
|
*
|
||||||
|
* Patch Version 1.07 2003-12-24 01
|
||||||
|
*
|
||||||
|
* Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
|
||||||
|
*
|
||||||
|
* Fixed ot work with CVS as of 2004-02-25 and released as 1.07a
|
||||||
|
* by Matthew Enger <m.enger@xi.com.au>
|
||||||
|
*
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License
|
* the GNU General Public License
|
||||||
*/
|
*/
|
||||||
@@ -25,6 +40,7 @@
|
|||||||
#include <asterisk/cli.h>
|
#include <asterisk/cli.h>
|
||||||
#include <asterisk/manager.h> /* JDG */
|
#include <asterisk/manager.h> /* JDG */
|
||||||
#include <asterisk/config.h>
|
#include <asterisk/config.h>
|
||||||
|
#include <asterisk/monitor.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -67,7 +83,7 @@ static char *app = "Queue";
|
|||||||
static char *synopsis = "Queue a call for a call queue";
|
static char *synopsis = "Queue a call for a call queue";
|
||||||
|
|
||||||
static char *descrip =
|
static char *descrip =
|
||||||
" Queue(queuename[|options[|URL][|announceoverride]]):\n"
|
" Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
|
||||||
"Queues an incoming call in a particular call queue as defined in queues.conf.\n"
|
"Queues an incoming call in a particular call queue as defined in queues.conf.\n"
|
||||||
" This application returns -1 if the originating channel hangs up, or if the\n"
|
" This application returns -1 if the originating channel hangs up, or if the\n"
|
||||||
"call is bridged and either of the parties in the bridge terminate the call.\n"
|
"call is bridged and either of the parties in the bridge terminate the call.\n"
|
||||||
@@ -81,7 +97,9 @@ static char *descrip =
|
|||||||
" In addition to transferring the call, a call may be parked and then picked\n"
|
" In addition to transferring the call, a call may be parked and then picked\n"
|
||||||
"up by another user.\n"
|
"up by another user.\n"
|
||||||
" The optional URL will be sent to the called party if the channel supports\n"
|
" The optional URL will be sent to the called party if the channel supports\n"
|
||||||
"it.\n";
|
"it.\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";
|
||||||
|
|
||||||
// [PHM 06/26/03]
|
// [PHM 06/26/03]
|
||||||
static char *app_aqm = "AddQueueMember" ;
|
static char *app_aqm = "AddQueueMember" ;
|
||||||
@@ -127,12 +145,15 @@ LOCAL_USER_DECL;
|
|||||||
struct queue_ent {
|
struct queue_ent {
|
||||||
struct ast_call_queue *parent; /* What queue is our parent */
|
struct ast_call_queue *parent; /* What queue is our parent */
|
||||||
char moh[80]; /* Name of musiconhold to be used */
|
char moh[80]; /* Name of musiconhold to be used */
|
||||||
char announce[80]; /* Announcement to play */
|
char announce[80]; /* Announcement to play for member when call is answered */
|
||||||
char context[80]; /* Context when user exits queue */
|
char context[80]; /* Context when user exits queue */
|
||||||
int pos; /* Where we are in the queue */
|
int pos; /* Where we are in the queue */
|
||||||
|
int last_pos_said; /* Last position we told the user */
|
||||||
|
time_t last_pos; /* Last time we told the user their position */
|
||||||
int opos; /* Where we started in the queue */
|
int opos; /* Where we started in the queue */
|
||||||
int handled; /* Whether our call was handled */
|
int handled; /* Whether our call was handled */
|
||||||
time_t start; /* When we started holding */
|
time_t start; /* When we started holding */
|
||||||
|
int queuetimeout; /* How many seconds before timing out of queue */
|
||||||
struct ast_channel *chan; /* Our channel */
|
struct ast_channel *chan; /* Our channel */
|
||||||
struct queue_ent *next; /* The next queue entry */
|
struct queue_ent *next; /* The next queue entry */
|
||||||
};
|
};
|
||||||
@@ -151,10 +172,24 @@ struct ast_call_queue {
|
|||||||
ast_mutex_t lock;
|
ast_mutex_t lock;
|
||||||
char name[80]; /* Name of the queue */
|
char name[80]; /* Name of the queue */
|
||||||
char moh[80]; /* Name of musiconhold to be used */
|
char moh[80]; /* Name of musiconhold to be used */
|
||||||
char announce[80]; /* Announcement to play */
|
char announce[80]; /* Announcement to play when call is answered */
|
||||||
char context[80]; /* Announcement to play */
|
char context[80]; /* Context for this queue */
|
||||||
int strategy; /* Queueing strategy */
|
int strategy; /* Queueing strategy */
|
||||||
int announcetimeout; /* How often to announce their position */
|
int announcefrequency; /* How often to announce their position */
|
||||||
|
int announceholdtime; /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
|
||||||
|
int holdtime; /* Current avg holdtime for this queue, based on recursive boxcar filter */
|
||||||
|
int callscompleted; /* Number of queue calls completed */
|
||||||
|
int callsabandoned; /* Number of queue calls abandoned */
|
||||||
|
int servicelevel; /* seconds setting for servicelevel*/
|
||||||
|
int callscompletedinsl; /* Number of queue calls answererd with servicelevel*/
|
||||||
|
char monfmt[8]; /* Format to use when recording calls */
|
||||||
|
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)*/
|
||||||
|
char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
|
||||||
|
char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */
|
||||||
|
char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
|
||||||
|
|
||||||
int count; /* How many entries are in the queue */
|
int count; /* How many entries are in the queue */
|
||||||
int maxlen; /* Max number of entries in queue */
|
int maxlen; /* Max number of entries in queue */
|
||||||
|
|
||||||
@@ -288,6 +323,102 @@ static void destroy_queue(struct ast_call_queue *q)
|
|||||||
free(q);
|
free(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int play_file(struct ast_channel *chan, char *filename)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
ast_stopstream(chan);
|
||||||
|
res = ast_streamfile(chan, filename, chan->language);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
res = ast_waitstream(chan, "");
|
||||||
|
else
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
ast_stopstream(chan);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int say_position(struct queue_ent *qe)
|
||||||
|
{
|
||||||
|
int res = 0, avgholdmins;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
/* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
|
||||||
|
time(&now);
|
||||||
|
if ( (now - qe->last_pos) < 15 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* If either our position has changed, or we are over the freq timer, say position */
|
||||||
|
if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ast_moh_stop(qe->chan);
|
||||||
|
/* Say we're next, if we are */
|
||||||
|
if (qe->pos == 1) {
|
||||||
|
res += play_file(qe->chan, qe->parent->sound_next);
|
||||||
|
goto posout;
|
||||||
|
} else {
|
||||||
|
res += play_file(qe->chan, qe->parent->sound_thereare);
|
||||||
|
res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language);
|
||||||
|
res += play_file(qe->chan, qe->parent->sound_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Round hold time to nearest minute */
|
||||||
|
avgholdmins = ( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60;
|
||||||
|
ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes\n", qe->parent->name, avgholdmins);
|
||||||
|
|
||||||
|
/* If the hold time is >1 min, if it's enabled, and if it's not
|
||||||
|
supposed to be only once and we have already said it, say it */
|
||||||
|
if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
|
||||||
|
res += play_file(qe->chan, qe->parent->sound_holdtime);
|
||||||
|
res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language);
|
||||||
|
res += play_file(qe->chan, qe->parent->sound_minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
posout:
|
||||||
|
/* Set our last_pos indicators */
|
||||||
|
qe->last_pos = now;
|
||||||
|
qe->last_pos_said = qe->pos;
|
||||||
|
|
||||||
|
ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
|
||||||
|
res += play_file(qe->chan, qe->parent->sound_thanks);
|
||||||
|
ast_moh_start(qe->chan, qe->moh);
|
||||||
|
|
||||||
|
return (res>0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void record_abandoned(struct queue_ent *qe)
|
||||||
|
{
|
||||||
|
ast_mutex_lock(&qe->parent->lock);
|
||||||
|
qe->parent->callsabandoned++;
|
||||||
|
ast_mutex_unlock(&qe->parent->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recalc_holdtime(struct queue_ent *qe)
|
||||||
|
{
|
||||||
|
int oldvalue, newvalue;
|
||||||
|
|
||||||
|
/* Calculate holdtime using a recursive boxcar filter */
|
||||||
|
/* Thanks to SRT for this contribution */
|
||||||
|
/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
|
||||||
|
|
||||||
|
newvalue = time(NULL) - qe->start;
|
||||||
|
|
||||||
|
ast_mutex_lock(&qe->parent->lock);
|
||||||
|
if (newvalue <= qe->parent->servicelevel)
|
||||||
|
qe->parent->callscompletedinsl++;
|
||||||
|
oldvalue = qe->parent->holdtime;
|
||||||
|
qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
|
||||||
|
ast_mutex_unlock(&qe->parent->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void leave_queue(struct queue_ent *qe)
|
static void leave_queue(struct queue_ent *qe)
|
||||||
{
|
{
|
||||||
struct ast_call_queue *q;
|
struct ast_call_queue *q;
|
||||||
@@ -317,6 +448,7 @@ ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name
|
|||||||
else
|
else
|
||||||
q->head = cur->next;
|
q->head = cur->next;
|
||||||
} else {
|
} else {
|
||||||
|
/* Renumber the people after us in the queue based on a new count */
|
||||||
cur->pos = ++pos;
|
cur->pos = ++pos;
|
||||||
prev = cur;
|
prev = cur;
|
||||||
}
|
}
|
||||||
@@ -611,12 +743,29 @@ static int wait_our_turn(struct queue_ent *qe)
|
|||||||
{
|
{
|
||||||
struct queue_ent *ch;
|
struct queue_ent *ch;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
/* This is the holding pen for callers 2 through maxlen */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Atomically read the parent head -- does not need a lock */
|
/* Atomically read the parent head -- does not need a lock */
|
||||||
ch = qe->parent->head;
|
ch = qe->parent->head;
|
||||||
|
|
||||||
/* If we are now at the top of the head, break out */
|
/* If we are now at the top of the head, break out */
|
||||||
if (qe->parent->head == qe)
|
if (qe == ch)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* If we have timed out, break out */
|
||||||
|
if ( qe->queuetimeout ) {
|
||||||
|
time(&now);
|
||||||
|
if ( (now - qe->start) >= qe->queuetimeout )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a position announcement, if enabled */
|
||||||
|
if (qe->parent->announcefrequency)
|
||||||
|
say_position(qe);
|
||||||
|
|
||||||
|
|
||||||
/* Wait a second before checking again */
|
/* Wait a second before checking again */
|
||||||
res = ast_waitfordigit(qe->chan, RECHECK * 1000);
|
res = ast_waitfordigit(qe->chan, RECHECK * 1000);
|
||||||
if (res)
|
if (res)
|
||||||
@@ -640,6 +789,7 @@ static int update_queue(struct ast_call_queue *q, struct localuser *user)
|
|||||||
}
|
}
|
||||||
cur = cur->next;
|
cur = cur->next;
|
||||||
}
|
}
|
||||||
|
q->callscompleted++;
|
||||||
ast_mutex_unlock(&q->lock);
|
ast_mutex_unlock(&q->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -793,6 +943,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
if (!peer) {
|
if (!peer) {
|
||||||
if (to) {
|
if (to) {
|
||||||
/* Musta gotten hung up */
|
/* Musta gotten hung up */
|
||||||
|
record_abandoned(qe);
|
||||||
res = -1;
|
res = -1;
|
||||||
} else {
|
} else {
|
||||||
if (digit && valid_exit(qe, digit))
|
if (digit && valid_exit(qe, digit))
|
||||||
@@ -817,6 +968,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
|
ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
|
||||||
}
|
}
|
||||||
/* Update parameters for the queue */
|
/* Update parameters for the queue */
|
||||||
|
recalc_holdtime(qe);
|
||||||
update_queue(qe->parent, lpeer);
|
update_queue(qe->parent, lpeer);
|
||||||
hanguptree(outgoing, peer);
|
hanguptree(outgoing, peer);
|
||||||
/* Stop music on hold */
|
/* Stop music on hold */
|
||||||
@@ -849,6 +1001,10 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
ast_hangup(peer);
|
ast_hangup(peer);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/* Begin Monitoring */
|
||||||
|
if (qe->parent->monfmt && *qe->parent->monfmt) {
|
||||||
|
ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
|
||||||
|
}
|
||||||
/* Drop out of the queue at this point, to prepare for next caller */
|
/* Drop out of the queue at this point, to prepare for next caller */
|
||||||
leave_queue(qe);
|
leave_queue(qe);
|
||||||
/* JDG: sendurl */
|
/* JDG: sendurl */
|
||||||
@@ -1120,6 +1276,7 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
char *options = NULL;
|
char *options = NULL;
|
||||||
char *url = NULL;
|
char *url = NULL;
|
||||||
char *announceoverride = NULL;
|
char *announceoverride = NULL;
|
||||||
|
char *queuetimeoutstr = NULL;
|
||||||
/* whether to exit Queue application after the timeout hits */
|
/* whether to exit Queue application after the timeout hits */
|
||||||
int go_on = 0;
|
int go_on = 0;
|
||||||
|
|
||||||
@@ -1135,6 +1292,9 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
|
|
||||||
LOCAL_USER_ADD(u);
|
LOCAL_USER_ADD(u);
|
||||||
|
|
||||||
|
/* Setup our queue entry */
|
||||||
|
memset(&qe, 0, sizeof(qe));
|
||||||
|
|
||||||
/* Parse our arguments XXX Check for failure XXX */
|
/* Parse our arguments XXX Check for failure XXX */
|
||||||
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
|
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
|
||||||
queuename = info;
|
queuename = info;
|
||||||
@@ -1151,25 +1311,39 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
if (announceoverride) {
|
if (announceoverride) {
|
||||||
*announceoverride = '\0';
|
*announceoverride = '\0';
|
||||||
announceoverride++;
|
announceoverride++;
|
||||||
|
queuetimeoutstr = strchr(announceoverride, '|');
|
||||||
|
if (queuetimeoutstr) {
|
||||||
|
*queuetimeoutstr = '\0';
|
||||||
|
queuetimeoutstr++;
|
||||||
|
qe.queuetimeout = atoi(queuetimeoutstr);
|
||||||
|
} else {
|
||||||
|
qe.queuetimeout = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (option_debug)
|
}
|
||||||
ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s\n",
|
|
||||||
queuename, options, url, announceoverride);
|
printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n",
|
||||||
/* Setup our queue entry */
|
queuename, options, url, announceoverride, qe.queuetimeout);
|
||||||
memset(&qe, 0, sizeof(qe));
|
|
||||||
|
|
||||||
qe.chan = chan;
|
qe.chan = chan;
|
||||||
qe.start = time(NULL);
|
qe.start = time(NULL);
|
||||||
|
qe.last_pos_said = 0;
|
||||||
|
qe.last_pos = 0;
|
||||||
if (!join_queue(queuename, &qe)) {
|
if (!join_queue(queuename, &qe)) {
|
||||||
ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
|
||||||
/* Start music on hold */
|
/* Start music on hold */
|
||||||
ast_moh_start(chan, qe.moh);
|
ast_moh_start(chan, qe.moh);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
/* This is the wait loop for callers 2 through maxlen */
|
||||||
|
|
||||||
res = wait_our_turn(&qe);
|
res = wait_our_turn(&qe);
|
||||||
/* If they hungup, return immediately */
|
/* If they hungup, return immediately */
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
/* Record this abandoned call */
|
||||||
|
record_abandoned(&qe);
|
||||||
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
||||||
if (option_verbose > 2) {
|
if (option_verbose > 2) {
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
|
ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
|
||||||
@@ -1186,6 +1360,22 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
}
|
}
|
||||||
if (!res) {
|
if (!res) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
/* This is the wait loop for the head caller*/
|
||||||
|
/* To exit, they may get their call answered; */
|
||||||
|
/* they may dial a digit from the queue context; */
|
||||||
|
/* or, they may may timeout. */
|
||||||
|
|
||||||
|
/* Leave if we have exceeded our queuetimeout */
|
||||||
|
if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a position announcement, if enabled */
|
||||||
|
if (qe.parent->announcefrequency)
|
||||||
|
say_position(&qe);
|
||||||
|
|
||||||
|
/* Try calling all queue members for 'timeout' seconds */
|
||||||
res = try_calling(&qe, options, announceoverride, url, &go_on);
|
res = try_calling(&qe, options, announceoverride, url, &go_on);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
@@ -1195,6 +1385,14 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Leave if we have exceeded our queuetimeout */
|
||||||
|
if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
|
||||||
res = wait_a_bit(&qe);
|
res = wait_a_bit(&qe);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
||||||
@@ -1208,18 +1406,20 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* exit after a timeout if 'n' option enabled */
|
/* exit after 'timeout' cycle if 'n' option enabled */
|
||||||
if (go_on) {
|
if (go_on) {
|
||||||
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||||
res = 0;
|
res = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Don't allow return code > 0 */
|
/* Don't allow return code > 0 */
|
||||||
if (res > 0 && res != AST_PBX_KEEPALIVE) {
|
if (res > 0 && res != AST_PBX_KEEPALIVE) {
|
||||||
res = 0;
|
res = 0;
|
||||||
ast_moh_stop(chan);
|
ast_moh_stop(chan);
|
||||||
|
ast_stopstream(chan);
|
||||||
}
|
}
|
||||||
leave_queue(&qe);
|
leave_queue(&qe);
|
||||||
} else {
|
} else {
|
||||||
@@ -1281,10 +1481,24 @@ static void reload_queues(void)
|
|||||||
q->retry = 0;
|
q->retry = 0;
|
||||||
q->timeout = -1;
|
q->timeout = -1;
|
||||||
q->maxlen = 0;
|
q->maxlen = 0;
|
||||||
|
q->announcefrequency = 0;
|
||||||
|
q->announceholdtime = 0;
|
||||||
|
q->holdtime = 0;
|
||||||
|
q->callscompleted = 0;
|
||||||
|
q->callsabandoned = 0;
|
||||||
|
q->callscompletedinsl = 0;
|
||||||
|
q->servicelevel = 0;
|
||||||
free_members(q, 0);
|
free_members(q, 0);
|
||||||
strcpy(q->moh, "");
|
strcpy(q->moh, "");
|
||||||
strcpy(q->announce, "");
|
strcpy(q->announce, "");
|
||||||
strcpy(q->context, "");
|
strcpy(q->context, "");
|
||||||
|
strcpy(q->monfmt, "");
|
||||||
|
strcpy(q->sound_next, "queue-youarenext");
|
||||||
|
strcpy(q->sound_thereare, "queue-thereare");
|
||||||
|
strcpy(q->sound_calls, "queue-callswaiting");
|
||||||
|
strcpy(q->sound_holdtime, "queue-holdtime");
|
||||||
|
strcpy(q->sound_minutes, "queue-minutes");
|
||||||
|
strcpy(q->sound_thanks, "queue-thankyou");
|
||||||
prev = q->members;
|
prev = q->members;
|
||||||
if (prev) {
|
if (prev) {
|
||||||
/* find the end of any dynamic members */
|
/* find the end of any dynamic members */
|
||||||
@@ -1329,10 +1543,30 @@ static void reload_queues(void)
|
|||||||
strncpy(q->context, var->value, sizeof(q->context) - 1);
|
strncpy(q->context, var->value, sizeof(q->context) - 1);
|
||||||
} else if (!strcasecmp(var->name, "timeout")) {
|
} else if (!strcasecmp(var->name, "timeout")) {
|
||||||
q->timeout = atoi(var->value);
|
q->timeout = atoi(var->value);
|
||||||
|
} else if (!strcasecmp(var->name, "monitor-format")) {
|
||||||
|
strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "queue-youarenext")) {
|
||||||
|
strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "queue-thereare")) {
|
||||||
|
strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "queue-callswaiting")) {
|
||||||
|
strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "queue-holdtime")) {
|
||||||
|
strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "queue-minutes")) {
|
||||||
|
strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "queue-thankyou")) {
|
||||||
|
strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
|
||||||
|
} else if (!strcasecmp(var->name, "announce-frequency")) {
|
||||||
|
q->announcefrequency = atoi(var->value);
|
||||||
|
} else if (!strcasecmp(var->name, "announce-holdtime")) {
|
||||||
|
q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
|
||||||
} else if (!strcasecmp(var->name, "retry")) {
|
} else if (!strcasecmp(var->name, "retry")) {
|
||||||
q->retry = atoi(var->value);
|
q->retry = atoi(var->value);
|
||||||
} else if (!strcasecmp(var->name, "maxlen")) {
|
} else if (!strcasecmp(var->name, "maxlen")) {
|
||||||
q->maxlen = atoi(var->value);
|
q->maxlen = atoi(var->value);
|
||||||
|
} else if (!strcasecmp(var->name, "servicelevel")) {
|
||||||
|
q->servicelevel= atoi(var->value);
|
||||||
} else if (!strcasecmp(var->name, "strategy")) {
|
} else if (!strcasecmp(var->name, "strategy")) {
|
||||||
q->strategy = strat2int(var->value);
|
q->strategy = strat2int(var->value);
|
||||||
if (q->strategy < 0) {
|
if (q->strategy < 0) {
|
||||||
@@ -1390,6 +1624,8 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
|
|||||||
time_t now;
|
time_t now;
|
||||||
char max[80];
|
char max[80];
|
||||||
char calls[80];
|
char calls[80];
|
||||||
|
float sl = 0;
|
||||||
|
|
||||||
time(&now);
|
time(&now);
|
||||||
if ((!queue_show && argc != 2) || (queue_show && argc != 3))
|
if ((!queue_show && argc != 2) || (queue_show && argc != 3))
|
||||||
return RESULT_SHOWUSAGE;
|
return RESULT_SHOWUSAGE;
|
||||||
@@ -1420,7 +1656,11 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
|
|||||||
snprintf(max, sizeof(max), "%d", q->maxlen);
|
snprintf(max, sizeof(max), "%d", q->maxlen);
|
||||||
else
|
else
|
||||||
strcpy(max, "unlimited");
|
strcpy(max, "unlimited");
|
||||||
ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy));
|
sl = 0;
|
||||||
|
if(q->callscompleted > 0)
|
||||||
|
sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
|
||||||
|
ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
|
||||||
|
q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
|
||||||
if (q->members) {
|
if (q->members) {
|
||||||
ast_cli(fd, " Members: \n");
|
ast_cli(fd, " Members: \n");
|
||||||
for (mem = q->members; mem; mem = mem->next) {
|
for (mem = q->members; mem; mem = mem->next) {
|
||||||
@@ -1502,6 +1742,8 @@ static int manager_queues_status( struct mansession *s, struct message *m )
|
|||||||
char idText[256] = "";
|
char idText[256] = "";
|
||||||
struct ast_call_queue *q;
|
struct ast_call_queue *q;
|
||||||
struct queue_ent *qe;
|
struct queue_ent *qe;
|
||||||
|
float sl = 0;
|
||||||
|
struct member *mem;
|
||||||
astman_send_ack(s, m, "Queue status will follow");
|
astman_send_ack(s, m, "Queue status will follow");
|
||||||
time(&now);
|
time(&now);
|
||||||
ast_mutex_lock(&qlock);
|
ast_mutex_lock(&qlock);
|
||||||
@@ -1511,21 +1753,43 @@ static int manager_queues_status( struct mansession *s, struct message *m )
|
|||||||
}
|
}
|
||||||
while(q) {
|
while(q) {
|
||||||
ast_mutex_lock(&q->lock);
|
ast_mutex_lock(&q->lock);
|
||||||
|
|
||||||
|
/* List queue properties */
|
||||||
|
if(q->callscompleted > 0)
|
||||||
|
sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
|
||||||
ast_cli(s->fd, "Event: QueueParams\r\n"
|
ast_cli(s->fd, "Event: QueueParams\r\n"
|
||||||
"Queue: %s\r\n"
|
"Queue: %s\r\n"
|
||||||
"Max: %d\r\n"
|
"Max: %d\r\n"
|
||||||
"Calls: %d\r\n"
|
"Calls: %d\r\n"
|
||||||
|
"Holdtime: %d\r\n"
|
||||||
|
"Completed: %d\r\n"
|
||||||
|
"Abandoned: %d\r\n"
|
||||||
|
"ServiceLevel: %d\r\n"
|
||||||
|
"ServicelevelPerf: %2.1f\r\n"
|
||||||
"%s"
|
"%s"
|
||||||
"\r\n",
|
"\r\n",
|
||||||
q->name, q->maxlen, q->count,idText);
|
q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
|
||||||
#if 0
|
q->callsabandoned, q->servicelevel, sl, idText);
|
||||||
/* Do we care about queue members? */
|
|
||||||
|
/* List Queue Members */
|
||||||
for (mem = q->members; mem; mem = mem->next)
|
for (mem = q->members; mem; mem = mem->next)
|
||||||
ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
|
ast_cli(s->fd, "Event: QueueMember\r\n"
|
||||||
#endif
|
"Queue: %s\r\n"
|
||||||
|
"Location: %s/%s\r\n"
|
||||||
|
"Membership: %s\r\n"
|
||||||
|
"Penalty: %d\r\n"
|
||||||
|
"CallsTaken: %d\r\n"
|
||||||
|
"LastCall: %ld\r\n"
|
||||||
|
"%s"
|
||||||
|
"\r\n",
|
||||||
|
q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
|
||||||
|
mem->penalty, mem->calls, mem->lastcall, idText);
|
||||||
|
|
||||||
|
/* List Queue Entries */
|
||||||
|
|
||||||
pos = 1;
|
pos = 1;
|
||||||
for (qe = q->head; qe; qe = qe->next)
|
for (qe = q->head; qe; qe = qe->next)
|
||||||
ast_cli(s->fd, "Event: QueueMember\r\n"
|
ast_cli(s->fd, "Event: QueueEntry\r\n"
|
||||||
"Queue: %s\r\n"
|
"Queue: %s\r\n"
|
||||||
"Position: %d\r\n"
|
"Position: %d\r\n"
|
||||||
"Channel: %s\r\n"
|
"Channel: %s\r\n"
|
||||||
|
@@ -2,6 +2,11 @@
|
|||||||
;
|
;
|
||||||
; Global settings for call queues
|
; Global settings for call queues
|
||||||
; (none exist currently)
|
; (none exist currently)
|
||||||
|
;
|
||||||
|
; Note that a timeout to fail out of a queue may be passed as part of application call
|
||||||
|
; from extensions.conf:
|
||||||
|
; Queue(queuename|[options]|[optionalurl]|[announceoverride]|[timeout])
|
||||||
|
; example: Queue(dave|t|||45)
|
||||||
|
|
||||||
[default]
|
[default]
|
||||||
;
|
;
|
||||||
@@ -53,6 +58,35 @@
|
|||||||
;
|
;
|
||||||
;maxlen = 0
|
;maxlen = 0
|
||||||
;
|
;
|
||||||
|
;
|
||||||
|
; How often to announce queue position and/or estimated holdtime to caller (0=off)
|
||||||
|
;
|
||||||
|
;announce-frequency = 90
|
||||||
|
;
|
||||||
|
; Should we include estimated hold time in position announcements?
|
||||||
|
; Either yes, no, or only once; hold time will not be announced if <1 minute
|
||||||
|
;
|
||||||
|
;announce-holdtime = yes|no|once
|
||||||
|
;
|
||||||
|
; Use these sound files in making position/holdtime announcements. The
|
||||||
|
; defaults are as listed below -- change only if you need to.
|
||||||
|
;
|
||||||
|
;queue-youarenext = queue-youarenext ("You are now first in line.")
|
||||||
|
;queue-thereare = queue-thereare ("There are")
|
||||||
|
;queue-callswaiting = queue-callswaiting ("calls waiting.")
|
||||||
|
;queue-holdtime = queue-holdtime ("The current est. holdtime is")
|
||||||
|
;queue-minutes = queue-minutes ("minutes.")
|
||||||
|
;queue-thankyou = queue-thankyou ("Thank you for your patience.")
|
||||||
|
;
|
||||||
|
; Calls may be recorded using Asterisk's monitor resource
|
||||||
|
; This can be enabled from within the Queue application, starting recording
|
||||||
|
; when the call is actually picked up; thus, only successful calls are
|
||||||
|
; recorded, and you are not recording while people are listening to MOH.
|
||||||
|
; To enable monitoring, simply specify "monitor-format"; it will be disabled
|
||||||
|
; otherwise.
|
||||||
|
;
|
||||||
|
; monitor-format = gsm|wav|wav49
|
||||||
|
;
|
||||||
; Each member of this call queue is listed on a separate line in
|
; Each member of this call queue is listed on a separate line in
|
||||||
; the form technology/dialstring. "member" means a normal member of a
|
; the form technology/dialstring. "member" means a normal member of a
|
||||||
; queue. An optional penalty may be specified after a comma, such that
|
; queue. An optional penalty may be specified after a comma, such that
|
||||||
|
12
sounds.txt
12
sounds.txt
@@ -72,6 +72,18 @@
|
|||||||
|
|
||||||
%pbx-transfer.gsm%Transfer.
|
%pbx-transfer.gsm%Transfer.
|
||||||
|
|
||||||
|
%queue-callswaiting.gsm%Waiting to speak with a representative
|
||||||
|
|
||||||
|
%queue-holdtime.gsm%The estimated hold time is currently
|
||||||
|
|
||||||
|
%queue-minutes.gsm%Minutes
|
||||||
|
|
||||||
|
%queue-thankyou.gsm%Thank you for your patience
|
||||||
|
|
||||||
|
%queue-thereare.gsm%You are currently caller number
|
||||||
|
|
||||||
|
%queue-youarenext.gsm%Your call is now first in line and will be answered by the next available representative.
|
||||||
|
|
||||||
%ss-noservice.gsm%The number you have dialed is not in service. Please check the number and try again.
|
%ss-noservice.gsm%The number you have dialed is not in service. Please check the number and try again.
|
||||||
|
|
||||||
%transfer.gsm%Please hold while I try that extension.
|
%transfer.gsm%Please hold while I try that extension.
|
||||||
|
BIN
sounds/queue-callswaiting.gsm
Executable file
BIN
sounds/queue-callswaiting.gsm
Executable file
Binary file not shown.
BIN
sounds/queue-holdtime.gsm
Executable file
BIN
sounds/queue-holdtime.gsm
Executable file
Binary file not shown.
BIN
sounds/queue-minutes.gsm
Executable file
BIN
sounds/queue-minutes.gsm
Executable file
Binary file not shown.
BIN
sounds/queue-thankyou.gsm
Executable file
BIN
sounds/queue-thankyou.gsm
Executable file
Binary file not shown.
BIN
sounds/queue-thereare.gsm
Executable file
BIN
sounds/queue-thereare.gsm
Executable file
Binary file not shown.
BIN
sounds/queue-youarenext.gsm
Executable file
BIN
sounds/queue-youarenext.gsm
Executable file
Binary file not shown.
Reference in New Issue
Block a user