QueueMetrics forum

QueueMetrics => Outbound and QueueMetrics => Topic started by: QueueMetrics on May 23, 2008, 11:56:52

Title: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on May 23, 2008, 11:56:52
We have been working for quite a while on a purely-dialplan solution to replace the queueDial.agi that seems to have problems with a number of 1.4 systems.

So we developed this script that targets Asterisk 1.4 and is a complete replacement of queueDial.agi:


Code: [Select]
[queuedial]
; using a global variable to pass values back from the answer-detect macro
; STATUS = U unanswered
;        = A answered
;
exten => _9XXX.,1,Set(MY_QUE=q-${EXTEN:1:3})
exten => _9XXX.,n,Set(MY_NUM=${EXTEN:4})
exten => _9XXX.,n,Set(MY_AGENT=Agent/${CALLERID(num)})
exten => _9XXX.,n,Set(MY_TECH=Zap/g0/)
exten => _9XXX.,n,NoOp,Ag: ${MY_AGENT} N: ${MY_NUM} Q: ${MY_QUE} T: ${MY_TECH}
exten => _9XXX.,n,MixMonitor(Q-${MY_QUE}-${UNIQUEID}.wav|b|)

exten => _9XXX.,n,Set(ST=${EPOCH})
exten => _9XXX.,n,Set(GM=QDIALV${MY_AGENT})
exten => _9XXX.,n,Set(GLOBAL(${GM})=U)
exten => _9XXX.,n,Set(GLOBAL(${GM}ans)=0)
exten => _9XXX.,n,System( echo "${ST}|${UNIQUEID}|${MY_QUE}|${MY_AGENT}|ENTERQUEUE|-|${MY_NUM}" >> /var/log/asterisk/queue_log )
exten => _9XXX.,n,Dial(${MY_TECH}${MY_NUM}||M(queuedial-answer^${UNIQUEID}^${GM}^${MY_QUE}^${MY_AGENT}^${ST}))

; Trapping call termination here
exten => h,1,NoOp( "Call exiting: status ${GLOBAL(${GM})} answered at: ${GLOBAL(${GM}ans)} DS: ${DIALSTATUS} HU: ${HANGUPCAUSE} "  )
exten => h,n,Goto(case-${GLOBAL(${GM})})
exten => h,n,Hangup()

; Call unanswered
exten => h,n(case-U),Set(WT=$[${EPOCH} - ${ST}])
exten => h,n,System( echo "${EPOCH}|${UNIQUEID}|${MY_QUE}|${MY_AGENT}|ABANDON|1|1|${WT}" >> /var/log/asterisk/queue_log )
exten => h,n,Hangup()

; call answered
exten => h,n(case-A),Set(WT=$[${GLOBAL(${GM}ans)} - ${ST}])
exten => h,n,Set(CT=$[${EPOCH} - ${GLOBAL(${GM}ans)}])
exten => h,n,System( echo "${EPOCH}|${UNIQUEID}|${MY_QUE}|${MY_AGENT}|COMPLETECALLER|${WT}|${CT}" >> /var/log/asterisk/queue_log )
exten => h,n,Hangup()

[macro-queuedial-answer]
; Expecting $ARG1: uniqueid of the caller channel
;           $ARG2: global variable to store the answer results
;           $ARG3: queue name
;           $ARG4: agent name
;           $ARG5: enterqueue
exten => s,1,NoOp("Macro: queuedial-answer UID:${ARG1} GR:${ARG2} Q:${ARG3} A:${ARG4} E:${ARG5}")
exten => s,n,Set(NOW=${EPOCH})
exten => s,n,Set(WD=$[${NOW} - ${ARG5}])
exten => s,n,System( echo "${NOW}|${ARG1}|${ARG3}|${ARG4}|CONNECT|${WD}" >> /var/log/asterisk/queue_log )
;exten => s,n,DumpChan()
exten => s,n,Set(GLOBAL(${ARG2})=A)
exten => s,n,Set(GLOBAL(${ARG2}ans)=${NOW})
exten => s,n,NoOp("Macro queuedial-answer terminating" )

Before telling everybody to use this, though, we would like our power-users to test this out and find any flaws that may be lingering :)

Known advantages over QueueDial.agi:
- Simpler setup and debugging
- No 'h' problems with calls that are not closed
- Correct tracking of answering time even for outbound (the calls stays unsnswered and then answered at the right time)
- Easier to add options (eg a timeout) to the actual DIAL commend executed

Known issues:
- The script does not distinguish whether it was the AGENT or the CALLED PARTY to hang up (anybody knows how?)
- It's for 1.4.x only
- It uses System() a number of times and not the QueueLog() app
- It uses global variables to pass values back from a different leg of the call


Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: David on June 05, 2008, 04:54:24
It's working well with Asterisk 1.6 but in Asterisk 1.6 the | (pipe) are not parsed anymore, you must replace them with , (coma).

Note : Not in the System() application.

The only thing not working correctly is the fact that we now in queuemetrics it show that it's always the caller who hangup, other than that everything seem to be working fine with 1.6
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on June 05, 2008, 10:16:30
Yes that is knows - we have tried a number of tricks to do that but it does not seem to be working. Any suggestion is welcome.

The changes of pipes to commas are basically only in the Dial() command, correct?
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: tonils on June 06, 2008, 16:59:34
How about the following

....
exten => _X.,n,System(echo "${ST}|${UNIQUEID}|${MY_QUE}|${MY_AGENT}|ENTERQUEUE|-|${MY_NUM}" >>/var/log/asterisk/queue_log)
exten => _X.,n,Dial(${MY_TECH}${MY_NUM},30,M(queuedial-answer^${UNIQUEID}^${GM}^${MY_QUE}^${MY_AGENT}^${ST})g)
exten => _X.,n,Set(REMOTEHANGUP=YES)
exten => _X.,n,Hangup()
....

and

....
; call answered
exten => h,n(case-A),Set(WT=$[${GLOBAL(${GM}ans)} - ${ST}])
exten => h,n,Set(CT=$[${EPOCH} - ${GLOBAL(${GM}ans)}])
exten => h,n,GotoIf($[x${REMOTEHANGUP}=xYES]?remote)
exten => h,n,System(echo "${EPOCH}|${UNIQUEID}|${MY_QUE}|${MY_AGENT}|COMPLETEAGENT|${WT}|${CT}" >> /var/log/asterisk/queue_log)
exten => h,n,Hangup()
exten => h,n(remote),System(echo "${EPOCH}|${UNIQUEID}|${MY_QUE}|${MY_AGENT}|COMPLETECALLER|${WT}|${CT}" >> /var/log/asterisk/queue_log)
exten => h,n,Hangup()
....

The g option to Dial makes asterisk continue dialplan processing after the call is terminated. If Dial does not return, it is because the agent hungup and the channel has disappeared. If it does return it is because the called person has hung up as the agent channel still exists.

Many thanks for this replacement for the AGI script. It will solve a lot of work arounds I was doing (e.g. integrating the trunk with FreePBX, etc)

Tony
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on June 07, 2008, 09:29:08
How about the following

....
exten => _X.,n,Dial(${MY_TECH}${MY_NUM},30,M(queuedial-answer^${UNIQUEID}^${GM}^${MY_QUE}^${MY_AGENT}^${ST})g)
exten => _X.,n,Set(REMOTEHANGUP=YES)
....


It is a bit more complex than that, as you can go to the line after the Dial() either because callee hung up or because the call did not complete. Likely one should test for the ${DIALSTATUS} and if it is OK it should be cosidered callee hung, else call not completed.

Anyway what's cool is that it's feasible to have all of this logic implemented in the dialplan.


Title: Pure scripting solution version 1.1
Post by: QueueMetrics on June 07, 2008, 13:09:09
This piece of dialplan should be final and address all concerns.

Code: [Select]
[queuedial]
; this piece of dialplan is just a calling hook into the [qm-queuedial] context that actually does the
; outbound dialing - replace as needed - just fill in the same variables.
exten => _XXX.,1,Set(QDIALER_QUEUE=q-${EXTEN:0:3})
exten => _XXX.,n,Set(QDIALER_NUMBER=${EXTEN:3})
exten => _XXX.,n,Set(QDIALER_AGENT=Agent/${CALLERID(num)})
exten => _XXX.,n,Set(QDIALER_CHANNEL=SIP/${QDIALER_NUMBER})
exten => _XXX.,n,Set(QueueName=${QDIALER_QUEUE})
exten => _XXX.,n,MixMonitor(Q-${QDIALER_QUEUE}-${UNIQUEID}.WAV|b|)
exten => _XXX.,n,Goto(qm-queuedial,s,1)

[qm-queuedial]
; We use a global variable to pass values back from the answer-detect macro.
; STATUS = U unanswered
;        = A answered    (plus CAUSECOMPLETE=C when callee hung up)
; The 'g' dial parameter must be used in order to track callee disconnecting.
; Note that we'll be using the 'h' hook in any case to do the logging when channels go down.
; We set the CDR(accountcode) for live monitoring by QM.
;
exten => s,1,NoOp,Outbound call -> A:${QDIALER_AGENT} N:${QDIALER_NUMBER} Q:${QDIALER_QUEUE} Ch:${QDIALER_CHANNEL}
exten => s,n,Set(CDR(accountcode)=QDIALAGI)
exten => s,n,Set(ST=${EPOCH})
exten => s,n,Set(GM=QDV-${QDIALER_AGENT})
exten => s,n,Set(GLOBAL(${GM})=U)
exten => s,n,Set(GLOBAL(${GM}ans)=0)
exten => s,n,Macro(queuelog,${ST},${UNIQUEID},${QDIALER_QUEUE},${QDIALER_AGENT},ENTERQUEUE,-,${QDIALER_NUMBER})
exten => s,n,Dial(${QDIALER_CHANNEL},30,gM(queuedial-answer^${UNIQUEID}^${GM}^${QDIALER_QUEUE}^${QDIALER_AGENT}^${ST}))
exten => s,n,Set(CAUSECOMPLETE=${IF($["${DIALSTATUS}" = "ANSWER"]?C)})

; Trapping call termination here
exten => h,1,NoOp( "Call exiting: status ${GLOBAL(${GM})} answered at: ${GLOBAL(${GM}ans)} DS: ${DIALSTATUS}"  )
exten => h,n,Goto(case-${GLOBAL(${GM})})
exten => h,n,Hangup()

; Call unanswered
exten => h,n(case-U),Set(WT=$[${EPOCH} - ${ST}])
exten => h,n,Macro(queuelog,${EPOCH},${UNIQUEID},${QDIALER_QUEUE},${QDIALER_AGENT},ABANDON,1,1,${WT})
exten => h,n,Hangup()

; call answered: agent/callee hung
exten => h,n(case-A),Set(COMPLETE=${IF($["${CAUSECOMPLETE}" = "C"]?COMPLETECALLER:COMPLETEAGENT)})
exten => h,n,Set(WT=$[${GLOBAL(${GM}ans)} - ${ST}])
exten => h,n,Set(CT=$[${EPOCH} - ${GLOBAL(${GM}ans)}])
exten => h,n,Macro(queuelog,${EPOCH},${UNIQUEID},${QDIALER_QUEUE},${QDIALER_AGENT},${COMPLETE},${WT},${CT})
exten => h,n,Hangup()


[macro-queuedial-answer]
; Expecting $ARG1: uniqueid of the caller channel
;           $ARG2: global variable to store the answer results
;           $ARG3: queue name
;           $ARG4: agent name
;           $ARG5: enterqueue
;
exten => s,1,NoOp("Macro: queuedial-answer UID:${ARG1} GR:${ARG2} Q:${ARG3} A:${ARG4} E:${ARG5}")
exten => s,n,Set(NOW=${EPOCH})
exten => s,n,Set(WD=$[${NOW} - ${ARG5}])
exten => s,n,Macro(queuelog,${NOW},${ARG1},${ARG3},${ARG4},CONNECT,${WD})
exten => s,n,Set(GLOBAL(${ARG2})=A)
exten => s,n,Set(GLOBAL(${ARG2}ans)=${NOW})
exten => s,n,NoOp("Macro queuedial-answer terminating" )

[macro-queuelog]
; The advantage of using this macro is that you can choose whether to use the Shell version
; (where you have complete control of what gets written) or the Application version (where you
; do not need a shellout, so it's way faster).
;
; Expecting  $ARG1: Timestamp
;            $ARG2: Call-id
;            $ARG3: Queue
;            $ARG4: Agent
;            $ARG5: Verb
;            $ARG6: Param1
;            $ARG7: Param2
;            $ARG8: Param3
;
;exten => s,1,System( echo "${ARG1}|${ARG2}|${ARG3|${ARG4}|${ARG5}|${ARG6}|${ARG7}|${ARG8}" >> /var/log/asterisk/queue_log )
exten => s,1,QueueLog(${ARG3},${ARG2},${ARG4},${ARG5},${ARG6}|${ARG7}|${ARG8})

Please note we separated the [queuedial] context that prepares parameters, sets call recordings, etc., from the [qm-queuedial] context, that is the actual workhorse doing the calling and the logging. This makes it easy for you to change the calling parameters if you need to do so.

We now use a macro for logging so that you can easily switch between using the QueueLog asterisk application (way faster, but the timestamp is implicit, so it might be a second after the call completed) versus the standard echo to shellout. This also makes the code cleaner and neater.

We mark outbound calls under the CDR account code of QDIALAGI. This is needed for QueueMetrics to know that those channels are not "regular" channels but are actually outgoing calls and need to be shown as such on the Live page. If you se third-party CDR processing apps, you might need to configure them to accept this change.

We changed the function parameters to use commas instead of pipes, so we expect this to be compatible with 1.6 as well.

Overall, we expect this code to be quite an advancement in terms of speed, ease of setup and configuration and reporting over the old QueueDial.agi script, so using this pure.dialplan solution will be the preferred way to implement outbound campaigns in QM 1.4.5.




Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: adolfo on August 05, 2008, 13:32:28
Hello

I have not too much experience in asterisk + queuemetrics

In what file is necessary include this portion of code?

extensions_custom.conf???

There is necessary another configuration in other file????

Thanks in advance
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on August 06, 2008, 10:57:02
It really depends on the config of the system. If you are not very confortable doing it, wait for the public release and use the queueDial.agi method instead.
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: knightfal on August 20, 2008, 16:42:44
I have tried to Impliment the pure scripting solution but have run into a few issues.
I am using Asterisk 1.4.19 and Elastix  QM is working great and the OB Call are actually posting to the Java app but the call is not completing getting the "ALL CIRCUITS ARE BUSY NOW" message through the sip phone.

When I dial 82035551212 I am getting the below in my CLI
(203 is my queue and 8 is the dial rule in my outbound route  that directs the call down the Trunk that was configured just like the PDF states)

I currently have 3 trunks set up
ZAP/g0 ( Pri trunk)
SIP/RIOTRU (Sip Trunk)
Local/$OUTNUM$@queue (Custom trunk for queuedial)

Im sure im not doing something correctly please advise


Code: [Select]

   -- Executing [s@qm-queuedial:8] Dial("Local/2035551212@queuedial-3c8f,2", "SIP/5551212|30|gM(queuedial-answer^1219242382.10^QDV-Agent/20000^q-203^Agent/20000^1219242382)") in new stack
  == Everyone is busy/congested at this time (1:0/0/1)
    -- Executing [s@qm-queuedial:9] Set("Local/2035551212@queuedial-3c8f,2", "CAUSECOMPLETE=") in new stack
  == End MixMonitor Recording Local/2035551212@queuedial-3c8f,2
    -- Executing [s@macro-outisbusy:2] Playback("SIP/20000-b7a27c30", "pls-try-call-later|noanswer") in new stack
    -- <SIP/20000-b7a27c30> Playing 'pls-try-call-later' (language 'en')

Knightfal
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on August 24, 2008, 12:10:19
I guess the number called SIP/5551212 makes no sense, so it's rejected. I think it should be something like SIP/RIOTRU/5551212 or Zap/g0/5551212, doesn't it?
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: silmaril on October 17, 2008, 12:45:42

Hi,

I'm using actually a modified version of the AEL macro qmoutqdial (http://forum.queuemetrics.com/index.php?topic=77.msg276#msg276), where i've added a call to our dialstatus macro to handle
busy/congestion/unavailable signal.

Is there an advantage in your new diaplan version ?
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on October 17, 2008, 15:56:21
They are pretty much the same thing - the underlying logic is the same.
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: goit on December 16, 2008, 16:42:06
Hello,
I am using Druid OSE 1.3. Can you someone please explain to me how the script above will fit in with the dial plan below.

exten=>_XXXXXXXXXX,n,Macro(ael-druid-trunkdial,SIP/trunk-name,1${EXTEN},WTK,,,,,1${EXTEN})
exten=>_XXXXXXXXXX,n,Hangup
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on December 17, 2008, 09:15:14
I'd say that you do something like:

Code: [Select]
exten =>_XXXXXXXXXX,1,Set(QDIALER_QUEUE=q-999)
exten => _XXXXXXXXXX,n,Set(QDIALER_NUMBER=1${EXTEN})
exten => _XXXXXXXXXX.,n,Set(QDIALER_AGENT=Agent/${CALLERID(num)})
exten => _XXXXXXXXXX,n,Set(QDIALER_CHANNEL=SIP/trunk-name/${QDIALER_NUMBER})
exten => _XXXXXXXXXX,n,Set(QueueName=${QDIALER_QUEUE})
exten => _XXXXXXXXXX,n,MixMonitor(Q-${QDIALER_QUEUE}-${UNIQUEID}.WAV|b|)
exten => _XXXXXXXXXX,n,Goto(qm-queuedial,s,1)
Basically you take the Agent code from the caller-id, the number is 1+number, and you dial on SIP/trunk-name/number.
This has NOT been tested, so take this as an example.
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: goit on December 17, 2008, 14:27:03
Thank you for the reply. 

My concern is for the "Dial" command. As you can see (Macro(ael-druid-trunkdial), DRUID executes an AEL script to do the dialing. It does not use asterisk "dial" command direclty. the AEL script is the one that executes the "Dial" command. I don't think what you suggested will work, but i will give it a try.
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on December 17, 2008, 14:44:57
Well there is no reason to be concerned - I'm not sure what the macro does, but surely is not needed by the underlying Asterisk architecture, as long as the trunk name makes sense.  ;D
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: qmax on June 25, 2009, 12:20:23
the Dial application sets channel variables
DIALEDTIME and ANSWEREDTIME

what is the reason of using EPOCH and having global variables ?
Upd. what i mean is:

Why:
Code: [Select]
exten => h,n(case-A),Set(WT=$[${GLOBAL(${GM}ans)} - ${ST}])
exten => h,n,Set(CT=$[${EPOCH} - ${GLOBAL(${GM}ans)}])
exten => h,n,QueueLog(${QUEUEID},${UNIQUEID},Agent/${AGENTID},COMPLETEAGENT,${WT},${CT})
is preffered over:
Code: [Select]
exten => h,n(case-A),QueueLog(${QUEUEID},${UNIQUEID},Agent/${AGENTID},COMPLETEAGENT,$[${DIALEDTIME} - ${ANSWEREDTIME}],${ANSWEREDTIME})
w/out using any global variables
here, DIALEDTIME and ANSWEREDTIME are channel variables set by Dial application
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: qmax on June 25, 2009, 12:47:44
another question...
when call is answered, Dial sets DIALSTASUS to "ANSWER"

what is the reason of using GLOBAL(${GM}) ?

is it possible, that macro set by M option is not called, while dialstatus is still set to ANSWER ?
asterisk documentation it is not quote clean for me.
Title: Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
Post by: QueueMetrics on June 26, 2009, 17:25:23
You would better do a trace ands see steèp-by-step what it does.