Author Topic: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution  (Read 31998 times)

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
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



David

  • Newbie
  • *
  • Posts: 10
  • Karma: 1
    • View Profile
    • Email
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
« Last Edit: June 05, 2008, 04:56:08 by David »

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
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?

tonils

  • Jr. Member
  • **
  • Posts: 54
  • Karma: 0
    • View Profile
    • Email
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

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
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.



QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
Pure scripting solution version 1.1
« Reply #5 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.





adolfo

  • Newbie
  • *
  • Posts: 10
  • Karma: 0
    • View Profile
    • Email
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

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
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.

knightfal

  • Newbie
  • *
  • Posts: 6
  • Karma: 0
    • View Profile
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

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
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?

silmaril

  • Newbie
  • *
  • Posts: 13
  • Karma: 0
    • View Profile
    • Email

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 ?

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
They are pretty much the same thing - the underlying logic is the same.

goit

  • Newbie
  • *
  • Posts: 6
  • Karma: 1
    • View Profile
Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
« Reply #12 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

QueueMetrics

  • Loway
  • Hero Member
  • *
  • Posts: 2999
  • Karma: 39
    • View Profile
    • QueueMetrics
Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
« Reply #13 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.

goit

  • Newbie
  • *
  • Posts: 6
  • Karma: 1
    • View Profile
Re: EXPERIMENTAL: replace the queueDial.agi with a pure-scripting solution
« Reply #14 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.