Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 243777) +++ apps/app_queue.c (working copy) @@ -796,6 +796,7 @@ int metric; int oldstatus; time_t lastcall; + time_t lastnoanswer; struct call_queue *lastqueue; struct member *member; unsigned int update_connectedline:1; @@ -846,6 +847,7 @@ int status; /*!< Status of queue member */ int paused; /*!< Are we paused (not accepting calls)? */ time_t lastcall; /*!< When last successful call was hungup */ + time_t lastnoanswer; /*!< Last time we got no answer trying to call a member */ struct call_queue *lastqueue; /*!< Last queue we received a call */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */ unsigned int delme:1; /*!< Flag to delete entry on reload */ @@ -967,6 +969,7 @@ int timeout; /*!< How long to wait for an answer */ int weight; /*!< Respective weight */ int autopause; /*!< Auto pause queue members if they fail to answer */ + int notpresentpenalty; /*!< How long do we want to wait before trying member again after a no-answer */ int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */ /* Queue strategy things */ @@ -997,6 +1000,7 @@ static struct ao2_container *queues; static void update_realtime_members(struct call_queue *q); +static int set_member_last_noanswer(const char *interface); static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused); static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); @@ -1772,6 +1776,8 @@ q->montype = 1; } else if (!strcasecmp(param, "autopause")) { q->autopause = ast_true(val); + } else if (!strcasecmp(param, "notpresent-penalty")) { + q->notpresentpenalty = atoi(val); } else if (!strcasecmp(param, "maxlen")) { q->maxlen = atoi(val); if (q->maxlen < 0) @@ -2764,6 +2770,16 @@ return 0; } + if ((tmp->lastqueue && tmp->lastqueue->notpresentpenalty && (time(NULL) - tmp->lastnoanswer < tmp->lastqueue->notpresentpenalty)) || + (!tmp->lastqueue && qe->parent->notpresentpenalty && (time(NULL) - tmp->lastnoanswer < qe->parent->notpresentpenalty))) { + ast_debug(1, "Not-present penalty time not yet expired for %s\n", tmp->interface); + if (qe->chan->cdr) + ast_cdr_busy(qe->chan->cdr); + tmp->stillgoing = 0; + (*busies)++; + return 0; + } + if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { ast_debug(1, "%s in use, can't receive call\n", tmp->interface); if (qe->chan->cdr) @@ -3111,6 +3127,9 @@ ast_moh_start(qe->chan, qe->moh, NULL); } + // Keep track of when we last didn't get an answer from this member + set_member_last_noanswer(interface); + if (qe->parent->eventwhencalled) { char vars[2048]; @@ -4209,6 +4228,7 @@ tmp->member = cur; tmp->oldstatus = cur->status; tmp->lastcall = cur->lastcall; + tmp->lastnoanswer = cur->lastnoanswer; tmp->lastqueue = cur->lastqueue; tmp->update_connectedline = 1; ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); @@ -4932,6 +4952,31 @@ return res; } +static int set_member_last_noanswer(const char *interface) { + int found = 0; + struct call_queue *q; + struct member *mem; + struct ao2_iterator queue_iter; + + if (ast_strlen_zero(interface)) + return RESULT_FAILURE; + + queue_iter = ao2_iterator_init(queues, 0); + while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { + ao2_lock(q); + if ((mem = interface_exists(q, interface))) { + time(&(mem->lastnoanswer)); + found++; + ao2_ref(mem, -1); + } + ao2_unlock(q); + queue_t_unref(q, "Done with iterator"); + } + ao2_iterator_destroy(&queue_iter); + + return found ? RESULT_SUCCESS : RESULT_FAILURE; +} + static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused) { int found = 0; Index: CHANGES =================================================================== --- CHANGES (revision 243777) +++ CHANGES (working copy) @@ -72,6 +72,8 @@ Applications ------------ + * Added 'notpresent-penalty' option to queues, which will give members a penalty for + not answering their call. * Added "ready" option to QUEUE_MEMBER counting to count free agents who's wrap-up timeout has expired. * Added 'R' option to app_queue. This option stops moh and indicates ringing Index: configs/queues.conf.sample =================================================================== --- configs/queues.conf.sample (revision 243777) +++ configs/queues.conf.sample (working copy) @@ -193,6 +193,13 @@ ; ;autopause=yes ; +; Stop trying to call a person that doesn't answer for this number of +; seconds. If you are using penalties on members, this will help calls +; to arrive to the next penalty level if no one in a penalty group is +; answering within the timeout. +; +;notpresent-penalty=30 +; ; Maximum number of people waiting in the queue (0 for unlimited) ; ;maxlen = 0 Index: contrib/scripts/realtime_pgsql.sql =================================================================== --- contrib/scripts/realtime_pgsql.sql (revision 243777) +++ contrib/scripts/realtime_pgsql.sql (working copy) @@ -120,6 +120,7 @@ timeoutrestart bool, setinterfacevar bool, autopause varchar(128), +notpresent_penalty int8, PRIMARY KEY (name) ) WITHOUT OIDS; ALTER TABLE queue_table OWNER TO asterisk;