; FreePBX ; Copyright (C) 2004 Coalescent Systems Inc (Canada) ; Copyright (C) 2006 Why Pay More 4 Less Pty Ltd (Australia) ; Released under the GNU GPL Licence version 2. ; dialparties.agi (http://www.sprackett.com/asterisk/) ; Asterisk::AGI (http://asterisk.gnuinter.net/) ; gsm (http://www.ibiblio.org/pub/Linux/utils/compress/!INDEX.short.html) ; loligo sounds (http://www.loligo.com/asterisk/sounds/) ; mpg123 (http://voip-info.org/wiki-Asterisk+config+musiconhold.conf) ; include extension contexts generated from AMP #include extensions_additional.conf ; Customizations to this dialplan should be made in extensions_custom.conf ; See extensions_custom.conf.sample for an example #include extensions_custom.conf [from-trunk] ; just an alias since VoIP shouldn't be called PSTN include => from-pstn [from-pstn] include => from-pstn-custom ; create this context in extensions_custom.conf to include customizations include => ext-did-direct ; MODIFICATOIN (PL) put before ext-did to take precedence include => ext-did include => from-did-direct ; MODIFICATOIN (PL) for findmefollow if enabled, should be bofore ext-local exten => fax,1,Goto(ext-fax,in_fax,1) ; MODIFICATION (PL) ; ; Required to assure that direct dids go to personal ring group before local extension. ; This could be auto-generated however I it is prefered to be put here and hard coded ; so that it can be modified if ext-local should take precedence in certain situations. ; will have to decide what to do later. ; [from-did-direct] include => ext-findmefollow include => ext-local ; ############################################################################ ; Macros [macro] ; ############################################################################ ; Rings one or more extensions. Handles things like call forwarding and DND ; We don't call dial directly for anything internal anymore. ; ARGS: $TIMER, $OPTIONS, $EXT1, $EXT2, $EXT3, ... ; Use a Macro call such as the following: ; Macro(dial,$DIAL_TIMER,$DIAL_OPTIONS,$EXT1,$EXT2,$EXT3,...) [macro-dial] exten => s,1,DeadAGI(dialparties.agi) exten => s,2,NoOp(Returned from dialparties with no extensions to call) exten => s,3,NoOp(DIALSTATUS is '${DIALSTATUS}') exten => s,10,Dial(${ds}) ; dialparties will set the priority to 10 if $ds is not null exten => s,20,NoOp(Returned from dialparties with hunt groups to dial ) exten => s,21,Set(HuntLoop=0) exten => s,22,GotoIf($[${HuntMembers} >= 1]?30 ) ; if this is from rg-group, don't strip prefix exten => s,23,NoOp(Returning there are no members left in the hunt group to ring) exten => s,30,Set(HuntMember=HuntMember${HuntLoop}) exten => s,31,GotoIf($[$["${CALLTRACE_HUNT}" != "" ] & $["${RingGroupMethod}" = "hunt" ]]?32:35 ) ; Set CAll Trace for Hunt member we are going to call exten => s,32,Set(CT_EXTEN=${CUT(ARG3,,$[${HuntLoop} + 1])}) exten => s,33,Set(DB(CALLTRACE/${CT_EXTEN})=${CALLTRACE_HUNT}) exten => s,34,Goto(s,42) exten => s,35,GotoIf($[$["${CALLTRACE_HUNT}" != "" ] & $["${RingGroupMethod}" = "memoryhunt" ]]?36:50 ) ;Set Call Trace for each hunt member we are going to call "Memory groups have multiple members to set CALL TRACE For hence the loop exten => s,36,Set(CTLoop=0) exten => s,37,GotoIf($[${CTLoop} > ${HuntLoop}]?42 ) ; if this is from rg-group, don't strip prefix exten => s,38,Set(CT_EXTEN=${CUT(ARG3,,$[${CTLoop} + 1])}) exten => s,39,Set(DB(CALLTRACE/${CT_EXTEN})=${CALLTRACE_HUNT}) exten => s,40,Set(CTLoop=$[1 + ${CTLoop}]) exten => s,41,Goto(s,37) exten => s,42,Dial(${${HuntMember}}${ds} ) ; dialparties will set the priority to 20 if $ds is not null and its a hunt group exten => s,43,Set(HuntLoop=$[1 + ${HuntLoop}]) exten => s,44,Set(HuntMembers=$[${HuntMembers} - 1]) exten => s,45,Goto(s,22) exten => s,50,DBdel(CALLTRACE/${CT_EXTEN}) exten => s,51,Goto(s,42) ; make sure hungup calls go here so that proper cleanup occurs from call confirmed calls and the like ; exten => h,1,Macro(hangupcall) ; Ring an extension, if the extension is busy or there is no answer send it ; to voicemail ; ARGS: $VMBOX, $EXT [macro-exten-vm] exten => s,1,Macro(user-callerid) exten => s,n,Set(FROMCONTEXT=exten-vm) exten => s,n,Set(VMBOX=${ARG1}) exten => s,n,Set(EXTTOCALL=${ARG2}) exten => s,n,Set(CFUEXT=${DB(CFU/${EXTTOCALL})}) exten => s,n,Set(CFBEXT=${DB(CFB/${EXTTOCALL})}) exten => s,n,Set(RT=${IF($[$["${VMBOX}"!="novm"] | $["foo${CFUEXT}"!="foo"]]?${RINGTIMER}:"")}) exten => s,n,Macro(record-enable,${EXTTOCALL},IN) exten => s,n,Macro(dial,${RT},${DIAL_OPTIONS},${EXTTOCALL}) exten => s,n,GosubIf($[$["${DIALSTATUS}"="NOANSWER"] & $["foo${CFUEXT}"!="foo"]]?docfu,1) ; check for CFU in use on no answer exten => s,n,GosubIf($[$["${DIALSTATUS}"="BUSY"] & $["foo${CFBEXT}"!="foo"]]?docfb,1) ; check for CFB in use on busy exten => s,n,NoOp(Voicemail is '${VMBOX}') exten => s,n,GotoIf($["${VMBOX}" = "novm"]?s-${DIALSTATUS},1) ; no voicemail in use for this extension exten => s,n,NoOp(Sending to Voicemail box ${EXTTOCALL}) exten => s,n,Macro(vm,${VMBOX},${DIALSTATUS}) ; Try the Call Forward on No Answer / Unavailable number exten => docfu,1,Set(RTCFU=${IF($["${VMBOX}"!="novm"]?${RINGTIMER}:"")}) exten => docfu,n,Dial(Local/${CFUEXT}@from-internal/n,${RTCFU},${DIAL_OPTIONS}) exten => docfu,n,Return ; Try the Call Forward on Busy number exten => docfb,1,Set(RTCFB=${IF($["${VMBOX}"!="novm"]?${RINGTIMER}:"")}) exten => docfb,n,Dial(Local/${CFBEXT}@from-internal/n,${RTCFB},${DIAL_OPTIONS}) exten => docfb,n,Return ; Extensions with no Voicemail box reporting BUSY come here exten => s-BUSY,1,NoOp(Extension is reporting BUSY and not passing to Voicemail) exten => s-BUSY,n,Playtones(busy) exten => s-BUSY,n,Busy(20) ; Anything but BUSY comes here exten => _s-.,1,Playtones(congestion) exten => _s-.,n,Congestion(10) [macro-vm] exten => s,1,Macro(user-callerid,SKIPTTL) exten => s,n,Set(VMGAIN=${IF($["foo${VM_GAIN}"!="foo"]?"g(${VM_GAIN})":"")}) ; ; If BLKVM_OVERRIDE is set, then someone told us to block calls from going to ; voicemail. This variable is reset by the answering channel so subsequent ; transfers will properly function. ; exten => s,n,GotoIf($["foo${DB(${BLKVM_OVERRIDE})}" != "fooTRUE"]?s-${ARG2},1) ; ; we didn't branch so block this from voicemail ; exten => s,n,Noop(CAME FROM: ${NODEST} - Blocking VM cause of key: ${DB(BLKVM_OVERRIDE)}) exten => s-BUSY,1,NoOp(BUSY voicemail) exten => s-BUSY,n,Macro(get-vmcontext,${ARG1}) exten => s-BUSY,n,Voicemail(${ARG1}@${VMCONTEXT}|${VM_OPTS}b${VMGAIN}) ; Voicemail Busy message exten => s-BUSY,n,Goto(exit-${VMSTATUS},1) exten => s-DIRECTDIAL,1,NoOp(DIRECTDIAL voicemail) exten => s-DIRECTDIAL,n,Macro(get-vmcontext,${ARG1}) exten => s-DIRECTDIAL,n,Voicemail(${ARG1}@${VMCONTEXT}|${VM_OPTS}${VM_DDTYPE}${VMGAIN}) exten => s-DIRECTDIAL,n,Goto(exit-${VMSTATUS},1) exten => _s-.,1,Macro(get-vmcontext,${ARG1}) exten => _s-.,n,Voicemail(${ARG1}@${VMCONTEXT}|${VM_OPTS}u${VMGAIN}) ; Voicemail Unavailable message exten => _s-.,n,Goto(exit-${VMSTATUS},1) exten => o,1,Background(one-moment-please) ; 0 during vm message will hangup exten => o,n,GotoIf($["x${OPERATOR_XTN}"="x"]?nooper:from-internal,${OPERATOR_XTN},1) exten => o,n(nooper),GotoIf($["x${FROM_DID}"="x"]?nodid) exten => o,n,Dial(Local/${FROM_DID}@from-pstn) exten => o,n,Macro(hangup) exten => o,n(nodid),Dial(Local/s@from-pstn) exten => o,n,Macro(hangup) exten => a,1,Macro(get-vmcontext,${ARG1}) exten => a,n,VoiceMailMain(${ARG1}@${VMCONTEXT}) exten => a,n,Hangup exten => exit-FAILED,1,Playback(im-sorry&an-error-has-occured) exten => exit-FAILED,n,Hangup() exten => exit-SUCCESS,1,Playback(goodbye) exten => exit-SUCCESS,n,Hangup() exten => exit-USEREXIT,1,Playback(goodbye) exten => exit-USEREXIT,n,Hangup() exten => t,1,Hangup() ;------------------------------------------------------------------------ ; [macro-simple-dial] ;------------------------------------------------------------------------ ; This macro was derived from macro-exten-vm, which is what is normally used to ; ring an extension. It has been simplified and designed to never go to voicemail ; and always return regardless of the DIALSTATUS for any incomplete call. ; ; It's current primary purpose is to allow findmefollow ring an extension prior ; to trying the follow-me ringgroup that is provided. ; ; Ring an extension, if the extension is busy or there is no answer, return ; ARGS: $EXTENSION, $RINGTIME ;------------------------------------------------------------------------ [macro-simple-dial] exten => s,1,Macro(user-callerid,SKIPTTL) ; already called from follow-me ; FROMCONTEXT was in the original macro-exten-vm where this macro was derived from. A ; search through all the modules does not come up with any place using this ; variable, but it is left here as a reminder in case there is functionality ; that eventually behaves in a certain way as a result of this variable being set ; and this macro has to masquerade as exten-vm. ; exten => s,n,Set(EXTTOCALL=${ARG1}) exten => s,n,Set(RT=${ARG2}) exten => s,n,Set(CFUEXT=${DB(CFU/${EXTTOCALL})}) exten => s,n,Set(CFBEXT=${DB(CFB/${EXTTOCALL})}) exten => s,n,Macro(record-enable,${EXTTOCALL},IN) exten => s,n,Macro(dial,${RT},${DIAL_OPTIONS},${EXTTOCALL}) exten => s,n,Set(PR_DIALSTATUS=${DIALSTATUS}) ; if we return, thus no answer, and they have a CFU setting, then we try that next ; exten => s,n,GosubIf($[$["${DIALSTATUS}"="NOANSWER"] & $["foo${CFUEXT}"!="foo"]]?docfu,1) ; check for CFU in use on no answer exten => s,n,GosubIf($[$["${DIALSTATUS}"="BUSY"] & $["foo${CFBEXT}"!="foo"]]?docfb,1) ; check for CFB in use on busy ; Nothing yet, then go to the end (which will just return, but in case we decide to do something with certain ; return situations, this is left in. ; exten => s,n,Goto(s-${DIALSTATUS},1) ; Try the Call Forward on No Answer / Unavailable number. ; We want to try CFU if set, but we want the same ring timer as was set to our call (or do we want the ; system ringtimer? - probably not). Then if no answer there (assuming it doesn't drop into their vm or ; something we return, which will have the net effect of returning to the followme setup.) ; ; want to avoid going to other follow-me settings here. So check if the CFUEXT is a user and if it is ; then direct it straight to ext-local (to avoid getting intercepted by findmefollow) otherwise send it ; to from-internal since it may be an outside line. ; exten => docfu,1,GotoIf( $[ "foo${DB(AMPUSER/${CFUEXT}/device)}" = "foo" ]?chlocal) exten => docfu,n,Dial(Local/${CFUEXT}@ext-local,${RT},${DIAL_OPTIONS}) exten => docfu,n,Return exten => docfu,n(chlocal),Dial(Local/${CFUEXT}@from-internal/n,${RT},${DIAL_OPTIONS}) exten => docfu,n,Return ; Try the Call Forward on Busy number exten => docfb,1,GotoIf( $[ "foo${DB(AMPUSER/${CFBEXT}/device)}" = "foo" ]?chlocal) exten => docfb,n,Dial(Local/${CFBEXT}@ext-local,${RT},${DIAL_OPTIONS}) exten => docfb,n,Return exten => docfb,n(chlocal),Dial(Local/${CFBEXT}@from-internal/n,${RT},${DIAL_OPTIONS}) exten => docfb,n,Return ; In all cases of no connection, come here and simply return, since the calling dialplan will ; decide what to do next exten => _s-.,1,NoOp(Extension is reporting ${EXTEN}) ;------------------------------------------------------------------------ ; get the voicemail context for the user in ARG1 [macro-get-vmcontext] exten => s,1,Set(VMCONTEXT=${DB(AMPUSER/${ARG1}/voicemail)}) exten => s,2,GotoIf($["foo${VMCONTEXT}" = "foo"]?200:300) exten => s,200,Set(VMCONTEXT=default) exten => s,300,NoOp() ; For some reason, if I don't run setCIDname, CALLERID(name) will be blank in my AGI ; ARGS: none [macro-fixcid] exten => s,1,Set(CALLERID(name)=${CALLERID(name)}) ; Ring groups of phones ; ARGS: comma separated extension list ; 1 - Ring Group Strategy ; 2 - ringtimer ; 3 - prefix ; 4 - extension list [macro-rg-group] exten => s,1,Macro(user-callerid,SKIPTTL) ; already called from ringgroup exten => s,2,GotoIf($["${CALLERID(name):0:${LEN(${RGPREFIX})}}" != "${RGPREFIX}"]?4:3) ; check for old prefix exten => s,3,Set(CALLERID(name)=${CALLERID(name):${LEN(${RGPREFIX})}}) ; strip off old prefix exten => s,4,Set(RGPREFIX=${ARG3}) ; set new prefix exten => s,5,Set(CALLERID(name)=${RGPREFIX}${CALLERID(name)}) ; add prefix to callerid name exten => s,6,Set(RecordMethod=Group) ; set new prefix exten => s,7,Macro(record-enable,${MACRO_EXTEN},${RecordMethod}) exten => s,8,Set(RingGroupMethod=${ARG1}) ; exten => s,9,Macro(dial,${ARG2},${DIAL_OPTIONS},${ARG4}) exten => s,10,Set(RingGroupMethod='') ; ; ; Outgoing channel(s) are busy ... inform the client ; but use noanswer features like ringgroups don't break by being answered ; just to play the message. ; [macro-outisbusy] exten => s,1,Playback(all-circuits-busy-now,noanswer) exten => s,n,Playback(pls-try-call-later,noanswer) exten => s,n,Macro(hangupcall) ; What to do on hangup. [macro-hangupcall] exten => s,1,ResetCDR(w) exten => s,n,NoCDR() ; Cleanup any remaining RG flag ; exten => s,n,GotoIf($[ "foo${USE_CONFIRMATION}" = "foo" | "foo${RINGGROUP_INDEX}" = "foo" | "${CHANNEL}" != "${UNIQCHAN}"]?skiprg) exten => s,n,Noop(Cleaning Up Confirmation Flag: RG/${RINGGROUP_INDEX}/${CHANNEL}) exten => s,n,DBDel(RG/${RINGGROUP_INDEX}/${CHANNEL}) ; Cleanup any remaining BLKVM flag ; exten => s,n(skiprg),GotoIf($[ "foo${BLKVM_BASE}" = "foo" | "BLKVM/${BLKVM_BASE}/${CHANNEL}" != "${BLKVM_OVERRIDE}" ]?theend) exten => s,n,Noop(Cleaning Up Block VM Flag: ${BLKVM_OVERRIDE}) exten => s,n,DBDel(${BLKVM_OVERRIDE}) exten => s,n(theend),Wait(5) exten => s,n,Hangup [macro-faxreceive] exten => s,1,Set(FAXFILE=/var/spool/asterisk/fax/${UNIQUEID}.tif) exten => s,2,Set(EMAILADDR=${FAX_RX_EMAIL}) exten => s,3,rxfax(${FAXFILE}) exten => s,103,Set(EMAILADDR=${FAX_RX_EMAIL}) exten => s,104,Goto(3) ; dialout and strip the prefix [macro-dialout] exten => s,1,Macro(user-callerid,SKIPTTL) exten => s,2,GotoIf($["${ECID${CALLERID(number)}}" = ""]?5) ;check for CID override for exten exten => s,3,Set(CALLERID(all)=${ECID${CALLERID(number)}}) exten => s,4,Goto(7) exten => s,5,GotoIf($["${OUTCID_${ARG1}}" = ""]?7) ;check for CID override for trunk exten => s,6,Set(CALLERID(all)=${OUTCID_${ARG1}}) exten => s,7,Set(length=${LEN(${DIAL_OUT_${ARG1}})}) exten => s,8,Dial(${OUT_${ARG1}}/${ARG2:${length}}) exten => s,9,Playtones(congestion) exten => s,10,Congestion(5) exten => s,109,Macro(outisbusy) ; dialout using default OUT trunk - no prefix [macro-dialout-default] exten => s,1,Macro(user-callerid,SKIPTTL) exten => s,2,Macro(record-enable,${CALLERID(number)},OUT) exten => s,3,Macro(outbound-callerid,${ARG1}) exten => s,4,Dial(${OUT}/${ARG1}) exten => s,5,Playtones(congestion) exten => s,6,Congestion(5) exten => s,105,Macro(outisbusy) ; dialout using a trunk, using pattern matching (don't strip any prefix) ; arg1 = trunk number, arg2 = number, arg3 = route password ; ; MODIFIED (PL) ; ; Modified both Dial() commands to include the new TRUNK_OPTIONS from the general ; screen of AMP ; [macro-dialout-trunk] exten => s,1,Set(DIAL_TRUNK=${ARG1}) ; If NODEST is set, clear it. No point in remembering since dialout-trunk will just end in the ; bit bucket. But if answered by an outside line with transfer capability, we want NODEST to be ; clear so a subsequent transfer to an internal extension works and goes to voicmail or other ; destinations. ; exten => s,n,Set(_NODEST=) exten => s,n,Set(DIAL_NUMBER=${ARG2}) exten => s,n,Set(ROUTE_PASSWD=${ARG3}) exten => s,n,Set(DIAL_TRUNK_OPTIONS=${DIAL_OPTIONS}) // will be reset to TRUNK_OPTIONS if not intra-company exten => s,n,GotoIf($["${ROUTE_PASSWD}" = ""]?noauth) ; arg3 is pattern password exten => s,n(auth),Authenticate(${ROUTE_PASSWD}) exten => s,n(noauth),Set(GROUP()=OUT_${DIAL_TRUNK}) exten => s,n,Macro(user-callerid,SKIPTTL) exten => s,n,Macro(record-enable,${CALLERID(number)},OUT) exten => s,n,GotoIf($["${INTRACOMPANYROUTE}" = "YES"]?skipoutcid) ;Set to YES if treated like internal exten => s,n,Set(DIAL_TRUNK_OPTIONS=${TRUNK_OPTIONS}) exten => s,n,Macro(outbound-callerid,${DIAL_TRUNK}) exten => s,n(skipoutcid),GotoIf($["${OUTMAXCHANS_${DIAL_TRUNK}}foo" = "foo"]?nomax) exten => s,n(checkmax),GotoIf($[ ${GROUP_COUNT()} > ${OUTMAXCHANS_${DIAL_TRUNK}} ]?chanfull) exten => s,n(nomax),DeadAGI(fixlocalprefix) ; this sets DIAL_NUMBER to the proper dial string for this trunk exten => s,n,Set(OUTNUM=${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}) ; OUTNUM is the final dial number exten => s,n,Set(custom=${CUT(OUT_${DIAL_TRUNK},:,1)}) ; Custom trunks are prefixed with "AMP:" exten => s,n,GotoIf($["${custom}" = "AMP"]?customtrunk) exten => s,n,Dial(${OUT_${DIAL_TRUNK}}/${OUTNUM},300,${DIAL_TRUNK_OPTIONS}) ; Regular Trunk Dial exten => s,n,Goto(s-${DIALSTATUS},1) exten => s,n(customtrunk),Set(pre_num=${CUT(OUT_${DIAL_TRUNK},$,1)}) exten => s,n,Set(the_num=${CUT(OUT_${DIAL_TRUNK},$,2)}) ; this is where we expect to find string OUTNUM exten => s,n,Set(post_num=${CUT(OUT_${DIAL_TRUNK},$,3)}) exten => s,n,GotoIf($["${the_num}" = "OUTNUM"]?outnum:skipoutnum) ; if we didn't find "OUTNUM", then skip to Dial exten => s,n(outnum),Set(the_num=${OUTNUM}) ; replace "OUTNUM" with the actual number to dial exten => s,n(skipoutnum),Dial(${pre_num:4}${the_num}${post_num},300,${DIAL_TRUNK_OPTIONS}) exten => s,n,Goto(s-${DIALSTATUS},1) exten => s,n(chanfull),Noop(max channels used up) exten => s-BUSY,1,NoOp(Dial failed due to trunk reporting BUSY - giving up) exten => s-BUSY,2,Busy(20) exten => s-NOANSWER,1,NoOp(Dial failed due to trunk reporting NOANSWER - giving up) exten => s-NOANSWER,2,Playtones(congestion) exten => s-NOANSWER,3,Congestion(20) exten => s-CANCEL,1,NoOp(Dial failed due to trunk reporting CANCEL - giving up) exten => s-CANCEL,2,Playtones(congestion) exten => s-CANCEL,3,Congestion(20) exten => _s-.,1,NoOp(Dial failed due to ${DIALSTATUS} - failing through to other trunks) exten => h,1,Macro(hangupcall) ; Adds a dynamic agent/member to a Queue ; Prompts for call-back number - in not entered, uses CIDNum [macro-agent-add] exten => s,1,Wait(1) exten => s,2,Macro(user-callerid,SKIPTTL) exten => s,3,Read(CALLBACKNUM,agent-user) ; get callback number from user exten => s,4,GotoIf($["${CALLBACKNUM}" = ""]?5:7) ; if user just pressed # or timed out, use cidnum exten => s,5,Set(CALLBACKNUM=${CALLERID(number)}) exten => s,6,GotoIf($["${CALLBACKNUM}" = ""]?2) ; if still no number, start over exten => s,7,GotoIf($["${ARG2}" = ""]?9:8) ; arg2 is queue password exten => s,8,Authenticate(${ARG2}) exten => s,9,AddQueueMember(${ARG1}|Local/${CALLBACKNUM}@from-internal/n) ; using chan_local allows us to have agents over trunks exten => s,10,UserEvent(Agentlogin|Agent: ${CALLBACKNUM}) exten => s,11,Wait(1) exten => s,12,Playback(agent-loginok) exten => s,13,Hangup() ; Removes a dynamic agent/member from a Queue ; Prompts for call-back number - in not entered, uses CIDNum [macro-agent-del] exten => s,1,Wait(1) exten => s,2,Macro(user-callerid,SKIPTTL) exten => s,3,Read(CALLBACKNUM,agent-user) ; get callback number from user exten => s,4,GotoIf($["${CALLBACKNUM}" = ""]?5:7) ; if user just pressed # or timed out, use cidnum exten => s,5,Set(CALLBACKNUM=${CALLERID(number)}) exten => s,6,GotoIf($["${CALLBACKNUM}" = ""]?2) ; if still no number, start over exten => s,7,RemoveQueueMember(${ARG1}|Local/${CALLBACKNUM}@from-internal/n) exten => s,8,UserEvent(RefreshQueue) exten => s,9,Wait(1) exten => s,10,Playback(agent-loggedoff) exten => s,11,Hangup() ; arg1 = trunk number, arg2 = number [macro-dialout-enum] ; This has been violently beaten upon by Rob Thomas, xrobau@gmail.com ; to 1: Be compliant with all the depreciated bits in asterisk 1.2 and ; above, and 2: to give a good shot at attempting to be compliant with ; RFC3761 by honouring the order in which records are returned. exten => s,1,GotoIf($["${ARG3}" != ""]?PASSWD:NOPASSWD); arg3 is pattern password exten => s,n(PASSWD),Authenticate(${ARG3}) exten => s,n(NOPASSWD),Macro(user-callerid,SKIPTTL) exten => s,n,Macro(record-enable,${CALLERID(number)},OUT) exten => s,n,Macro(outbound-callerid,${ARG1}) exten => s,n,Set(GROUP()=OUT_${ARG1}) exten => s,n,GotoIf($[ ${GROUP_COUNT()} > ${OUTMAXCHANS_${ARG1}} ]?nochans) exten => s,n,Set(DIAL_NUMBER=${ARG2}) exten => s,n,Set(DIAL_TRUNK=${ARG1}) exten => s,n,DeadAGI(fixlocalprefix) ; this sets DIAL_NUMBER to the proper dial string for this trunk exten => s,n,Set(E164NETWORKS=e164.arpa-e164.info-e164.org) ; enum networks to check exten => s,n,GotoIf($["${DIAL_NUMBER:0:1}" = "+"]?begin) ; Skip next line if it already is prefixed by a plus exten => s,n,Set(DIAL_NUMBER=+${DIAL_NUMBER}) ; Add a plus to the start, becasue ENUMLOOKUP needs it. ; start of main network loop exten => s,n(begin),NoOp(E164NETWORKS is ${E164NETWORKS}) exten => s,n,GotoIf($["${E164NETWORKS:1:2}"=""]?failedtotally) exten => s,n,Set(ENUMNET=${CUT(E164NETWORKS,-,1)}) exten => s,n,Set(E164NETWORKS=${CUT(E164NETWORKS,-,2-)}) exten => s,n,NoOp(E164NETWORKS is now ${E164NETWORKS}) exten => s,n,NoOp(ENUMNET is ${ENUMNET}) exten => s,n,Set(ENUMCOUNT=${ENUMLOOKUP(${DIAL_NUMBER},all,c,${ENUMNET})}) exten => s,n,Set(ENUMPTR=0) exten => s,n,Set(LOOKUPBUG=0) ; start of main lookup loop exten => s,n(startloop),GotoIf($["${ENUMPTR}"<"${ENUMCOUNT}"]?continue:failed) ; Now, let's start through them. exten => s,n(continue),Set(ENUMPTR=$[${ENUMPTR}+1]) exten => s,n,NoOp(Doing ENUMLOOKUP(${DIAL_NUMBER},all,${ENUMPTR},${ENUMNET})) exten => s,n,Set(ENUM=${ENUMLOOKUP(${DIAL_NUMBER},all,${ENUMPTR},${ENUMNET})}) ; Deal with reponse exten => s,n,GotoIf($["${ENUM:0:3}" = "sip" ]?sipuri) exten => s,n,GotoIf($["${ENUM:0:3}" = "iax" ]?iaxuri) ; It doesn't matter if you don't have h323 enabled, as when it tries to dial, it cares ; about dialstatus and retries if there are any enum results left. exten => s,n,GotoIf($["${ENUM:0:3}" = "h32" ]?h323uri) ; e164.org can return 'ADDRESS' lines. Because of *'s poor handling of Enum ; lookups, we want to DECREMENT the enum pointer. Yes. That means we try more ; times than there actually exists entries. exten => s,n,GotoIf($["${ENUM:0:3}" = "ADD" ]?enumbug) ; OK. If we're here, we've still got some enum entries to go through. Back to ; the start with you! exten => s,n,Goto(startloop) ; We're here because of the poor implementation of ENUMLOOKUP in Asterisk. It ; is quite possible to do three ENUMLOOKUPS and get the same entry each time. ; The only workaround I can think of is when we hit an invalid entry, do a ; DECREMENT of the pointer, and keep trying. exten => s,n(enumbug),Set(ENUMPTR=$[${ENUMPTR}-1]) exten => s,n,NoOp(If this is looping with the same ENUM value, The ENUMLOOKUP function is fixed!) exten => s,n,Set(LOOKUPBUG=$[${LOOKUPBUG}+1]) ; If we've done this more than, ooh, 5 times, then give up on this network. Sorry. exten => s,n,GotoIf($["${LOOKUPBUG}" > 5 ]?failed) exten => s,n,Goto(continue) ; If the prefix is 'sip:'... exten => s,n(sipuri),Set(DIALSTR=SIP/${ENUM:4}) exten => s,n,Goto(dodial) ; If it's IAX2... exten => s,n(iaxuri),Set(DIALSTR=IAX2/${ENUM:5}) exten => s,n,Goto(dodial) ; Or even if it's H323. exten => s,n(h323uri),Set(DIALSTR=H323/${ENUM:5}) exten => s,n(dodial),Dial(${DIALSTR}) exten => s,n,NoOp(Dial exited in macro-enum-dialout with ${DIALSTATUS}) ; Now, if we're still here, that means the Dial failed for some reason. ; If it's CONGESTION or CHANUNAVAIL we probably want to try again on a ; different channel. However, if it's the last one, we don't have any ; left, and I didn't keep any previous dialstatuses, so hopefully ; someone looking throught the logs would have seen the NoOp's exten => s,n,GotoIf($["${ENUMPTR}"<"${ENUMCOUNT}"]?maybemore:dialfailed) exten => s,n(maybemore),GotoIf($[ $[ "${DIALSTATUS}" = "CHANUNAVAIL" ] | $[ "${DIALSTATUS}" = "CONGESTION" ] ]?continue) ; If we're here, then it's BUSY or NOANSWER or something and well, deal with it. exten => s,n(dialfailed),Goto(s-${DIALSTATUS},1) ; Here are the exit points for the macro. exten => s,n(failed),NoOp(EnumLookup failed on network ${ENUMNET}) exten => s,n,Goto(begin) exten => s,n(failedtotally),NoOp(EnumLookup failed -- no more networks to try) exten => s,n,Goto(end) exten => s,n(nochans),NoOp(max channels used up) exten => s,n(end),NoOp(Exiting macro-dialout-enum) exten => s-BUSY,1,NoOp(Trunk is reporting BUSY) exten => s-BUSY,2,Busy(20) exten => _s-.,1,NoOp(Dial failed due to ${DIALSTATUS}) [macro-record-enable] exten => s,1,GotoIf($[${LEN(${BLINDTRANSFER})} > 0]?2:4) exten => s,2,ResetCDR(w) exten => s,3,StopMonitor() ; I haven't figured out how, but occasionally a hung up ; call can end up here. If you don't use DeadAGI (which does ; work fine as a normal AGI), asterisk deadlocks a thread, ; and ends up grumpy. exten => s,4,DeadAGI(recordingcheck,${TIMESTAMP},${UNIQUEID}) exten => s,5,Noop(No recording needed) exten => s,999,MixMonitor(${CALLFILENAME}.wav) ;exten => s,3,BackGround(for-quality-purposes) ;exten => s,4,BackGround(this-call-may-be) ;exten => s,5,BackGround(recorded) ; This macro is for dev purposes and just dumps channel/app variables. Useful when designing new contexts. [macro-dumpvars] exten => s,1,Noop(ACCOUNTCODE=${ACCOUNTCODE}) exten => s,2,Noop(ANSWEREDTIME=${ANSWEREDTIME}) exten => s,3,Noop(BLINDTRANSFER=${BLINDTRANSFER}) exten => s,4,Noop(CALLERID=${CALLERID(all)}) exten => s,5,Noop(CALLERID(name)=${CALLERID(name)}) exten => s,6,Noop(CALLERID(number)=${CALLERID(number)}) exten => s,7,Noop(CALLINGPRES=${CALLINGPRES}) exten => s,8,Noop(CHANNEL=${CHANNEL}) exten => s,9,Noop(CONTEXT=${CONTEXT}) exten => s,10,Noop(DATETIME=${DATETIME}) exten => s,11,Noop(DIALEDPEERNAME=${DIALEDPEERNAME}) exten => s,12,Noop(DIALEDPEERNUMBER=${DIALEDPEERNUMBER}) exten => s,13,Noop(DIALEDTIME=${DIALEDTIME}) exten => s,14,Noop(DIALSTATUS=${DIALSTATUS}) exten => s,15,Noop(DNID=${DNID}) exten => s,16,Noop(EPOCH=${EPOCH}) exten => s,17,Noop(EXTEN=${EXTEN}) exten => s,18,Noop(HANGUPCAUSE=${HANGUPCAUSE}) exten => s,19,Noop(INVALID_EXTEN=${INVALID_EXTEN}) exten => s,20,Noop(LANGUAGE=${LANGUAGE}) exten => s,21,Noop(MEETMESECS=${MEETMESECS}) exten => s,22,Noop(PRIORITY=${PRIORITY}) exten => s,23,Noop(RDNIS=${RDNIS}) exten => s,24,Noop(SIPDOMAIN=${SIPDOMAIN}) exten => s,25,Noop(SIP_CODEC=${SIP_CODEC}) exten => s,26,Noop(SIPCALLID=${SIPCALLID}) exten => s,27,Noop(SIPUSERAGENT=${SIPUSERAGENT}) exten => s,28,Noop(TIMESTAMP=${TIMESTAMP}) exten => s,29,Noop(TXTCIDNAME=${TXTCIDNAME}) exten => s,30,Noop(UNIQUEID=${UNIQUEID}) exten => s,31,Noop(TOUCH_MONITOR=${TOUCH_MONITOR}) exten => s,32,Noop(MACRO_CONTEXT=${MACRO_CONTEXT}) exten => s,33,Noop(MACRO_EXTEN=${MACRO_EXTEN}) exten => s,34,Noop(MACRO_PRIORITY=${MACRO_PRIORITY}) [macro-user-logon] ; check device type exten => s,1,Set(DEVICETYPE=${DB(DEVICE/${CALLERID(number)}/type)}) exten => s,2,GotoIf($["${DEVICETYPE}" = "fixed"]?s-FIXED,1) ; get user's extension exten => s,3,Set(AMPUSER=${ARG1}) exten => s,4,GotoIf($["${AMPUSER}" = ""]?5:9) exten => s,5,BackGround(please-enter-your) exten => s,6,Playback(extension) exten => s,7,Read(AMPUSER,then-press-pound) ; get user's password and authenticate exten => s,8,Wait(1) exten => s,9,Set(AMPUSERPASS=${DB(AMPUSER/${AMPUSER}/password)}) exten => s,10,GotoIf($[${LEN(${AMPUSERPASS})} = 0]?s-NOPASSWORD,1) ; do not continue if the user has already logged onto this device exten => s,11,Set(DEVICEUSER=${DB(DEVICE/${CALLERID(number)}/user)}) exten => s,12,GotoIf($["${DEVICEUSER}" = "${AMPUSER}"]?s-ALREADYLOGGEDON,1) exten => s,13,Authenticate(${AMPUSERPASS}) ; devices can only be mapped to one user - loggoff anyone else who is here exten => s,14,Macro(user-logoff) ; map user to device exten => s,15,Set(AMPUSERDEVICES=${DB(AMPUSER/${AMPUSER}/device)}) exten => s,16,GotoIf($[${LEN(${AMPUSERDEVICES})} = 0]?18) exten => s,17,Set(AMPUSERDEVICES=${AMPUSERDEVICES}&) exten => s,18,Set(AMPUSERDEVICES=${AMPUSERDEVICES}${CALLERID(number)}) exten => s,19,Set(DB(AMPUSER/${AMPUSER}/device)=${AMPUSERDEVICES}) ; map device to user exten => s,20,Set(DB(DEVICE/${CALLERID(number)}/user)=${AMPUSER}) ; create symlink from dummy device mailbox to user's mailbox exten => s,21,System(/bin/ln -s /var/spool/asterisk/voicemail/default/${AMPUSER}/ /var/spool/asterisk/voicemail/device/${CALLERID(number)}) exten => s-FIXED,1,NoOp(Device is FIXED and cannot be logged into) exten => s-FIXED,2,Playback(ha/phone) exten => s-FIXED,3,SayDigits(${CALLERID(number)}) exten => s-FIXED,4,Playback(is-curntly-unavail) exten => s-FIXED,5,Playback(vm-goodbye) exten => s-FIXED,6,Hangup ;TODO should play msg indicated device cannot be logged into exten => s-ALREADYLOGGEDON,1,NoOp(This device has already been logged into by this user) exten => s-ALREADYLOGGEDON,2,Playback(vm-goodbye) exten => s-ALREADYLOGGEDON,3,Hangup ;TODO should play msg indicated device is already logged into exten => s-NOPASSWORD,1,NoOp(This extension does not exist or no password is set) exten => s-NOPASSWORD,2,Playback(an-error-has-occured) exten => s-NOPASSWORD,3,Playback(vm-goodbye) exten => s-NOPASSWORD,4,Hangup ;TODO should play msg indicated device is already logged into [macro-user-logoff] ; check device type exten => s,1,Set(DEVICETYPE=${DB(DEVICE/${CALLERID(number)}/type)}) exten => s,2,GotoIf($["${DEVICETYPE}" = "fixed"]?s-FIXED,1) ; remove entry from user's DEVICE key ; delete the symlink to user's voicemail box exten => s,3,System(rm -f /var/spool/asterisk/voicemail/device/${CALLERID(number)}) exten => s,4,Set(DEVAMPUSER=${DB(DEVICE/${CALLERID(number)}/user)}) exten => s,5,Set(AMPUSERDEVICES=${DB(AMPUSER/${DEVAMPUSER}/device)}) exten => s,6,DeadAGI(list-item-remove.php,${AMPUSERDEVICES},${CALLERID(number)},AMPUSERDEVICES,&) ; reset user -> device mapping ; users can log onto multiple devices, need to just remove device from value exten => s,7,Set(DB(AMPUSER/${DEVAMPUSER}/device)=${AMPUSERDEVICES}) ; reset device -> user mapping exten => s,8,Set(DB(DEVICE/${CALLERID(number)}/user)=none) exten => s,9,Playback(vm-goodbye) exten => s-FIXED,1,NoOp(Device is FIXED and cannot be logged out of) exten => s-FIXED,2,Playback(an-error-has-occured) exten => s-FIXED,3,Playback(vm-goodbye) exten => s-FIXED,4,Hangup ;TODO should play msg indicated device cannot be logged into [macro-systemrecording] exten => s,1,Goto(${ARG1},1) exten => dorecord,1,Record(/tmp/${CALLERID(number)}-ivrrecording:wav) exten => dorecord,n,Wait(1) exten => dorecord,n,Goto(confmenu,1) exten => docheck,1,Playback(/tmp/${CALLERID(number)}-ivrrecording) exten => docheck,n,Wait(1) exten => docheck,n,Goto(confmenu,1) exten => confmenu,1,Background(to-listen-to-it&press-1&to-rerecord-it&press-star|m||macro-systemrecording) exten => confmenu,n,Read(RECRESULT||1|||4) exten => confmenu,n,GotoIf($["x${RECRESULT}"="x*"]?dorecord,1) exten => confmenu,n,GotoIf($["x${RECRESULT}"="x1"]?docheck,1) exten => confmenu,n,Goto(1) exten => 1,1,Goto(docheck,1) exten => *,1,Goto(dorecord,1) exten => t,1,Playback(goodbye) exten => t,n,Hangup exten => i,1,Playback(pm-invalid-option) exten => i,n,Goto(confmenu,1) exten => h,1,Hangup ; ; ############################################################################ ; CallerID Handling ; ############################################################################ ;sets the callerid of the device to that of the logged in user [macro-user-callerid] exten => s,1,Noop(user-callerid: ${CALLERID(name)} ${CALLERID(number)}) exten => s,n,GotoIf($["${CHANNEL:0:5}" = "Local"]?report) exten => s,n,GotoIf($["${REALCALLERIDNUM:1:2}" != ""]?start) exten => s,n,Set(REALCALLERIDNUM=${CALLERID(number)}) exten => s,n(start),NoOp(REALCALLERIDNUM is ${REALCALLERIDNUM}) exten => s,n,Set(AMPUSER=${DB(DEVICE/${REALCALLERIDNUM}/user)}) exten => s,n,Set(AMPUSERCIDNAME=${DB(AMPUSER/${AMPUSER}/cidname)}) exten => s,n,GotoIf($["x${AMPUSERCIDNAME:1:2}" = "x"]?report) exten => s,n,Set(CALLERID(all)=${AMPUSERCIDNAME} <${AMPUSER}>) exten => s,n,Set(REALCALLERIDNUM=${DB(DEVICE/${REALCALLERIDNUM}/user)}) exten => s,n(report),Noop(TTL: ${TTL} ARG1: ${ARG1}) exten => s,n,GotoIf($[ "${ARG1}" = "SKIPTTL" ]?continue) exten => s,n(report2),Set(_TTL=${IF($["foo${TTL}" = "foo"]?64:$[ ${TTL} - 1 ])}) exten => s,n,GotoIf($[ ${TTL} > 0 ]?continue) exten => s,n,Wait(${RINGTIMER}) ; wait for a while, to give it a chance to be picked up by voicemail exten => s,n,Answer() exten => s,n,Wait(2) exten => s,n,Playback(im-sorry&an-error-has-occured&with&call-forwarding) exten => s,n,Macro(hangupcall) exten => s,n,Congestion() exten => s,n(continue),NoOp(Using CallerID ${CALLERID(all)}) exten => h,1,Macro(hangupcall) ; overrides callerid out trunks ; arg1 is trunk ; macro-user-callerid should be called _before_ using this macro [macro-outbound-callerid] ; Keep the original CallerID number, for failover to the next trunk. exten => s,1,GotoIf($["${REALCALLERIDNUM:1:2}" != ""]?start) exten => s,n,Set(REALCALLERIDNUM=${CALLERID(number)}) exten => s,n(start),NoOp(REALCALLERIDNUM is ${REALCALLERIDNUM}) ; If this came through a ringgroup or CF, then we want to retain original CID unless ; OUTKEEPCID_${trunknum} is set. ; exten => s,n,GotoIf($["${KEEPCID}" != "TRUE"]?normcid) ;Set to TRUE if coming from ringgroups, CF, etc. exten => s,n,GotoIf($["x${OUTKEEPCID_${ARG1}}" = "xon"]?normcid) exten => s,n,GotoIf($["foo${REALCALLERIDNUM}" = "foo"]?normcid) ;if not set to anything, go through normal processing exten => s,n,Set(USEROUTCID=${REALCALLERIDNUM}) ; We now have to make sure the CID is valid. If we find an AMPUSER with the same CID, we assume it is an internal ; call (would be quite a conincidence if not) and go through the normal processing to get that CID. If a device ; is set for this CID, then it must be internal ; exten => s,n,GotoIf($["foo${DB(AMPUSER/${REALCALLERIDNUM}/device)}" = "foo"]?bypass:normcid) exten => s,n(normcid),Set(USEROUTCID=${DB(AMPUSER/${REALCALLERIDNUM}/outboundcid)}) exten => s,n(bypass),Set(EMERGENCYCID=${DB(DEVICE/${REALCALLERIDNUM}/emergency_cid)}) exten => s,n,Set(TRUNKOUTCID=${OUTCID_${ARG1}}) exten => s,n,GotoIf($["${EMERGENCYROUTE:1:2}" = ""]?trunkcid) ; check EMERGENCY ROUTE exten => s,n,GotoIf($["${EMERGENCYCID:1:2}" = ""]?trunkcid) ; empty EMERGENCY CID, so default back to trunk exten => s,n,Set(CALLERID(all)=${EMERGENCYCID}) ; emergency cid for device exten => s,n,Goto(report) exten => s,n(trunkcid),GotoIf($["${TRUNKOUTCID:1:2}" = ""]?usercid) ;check for CID override for trunk (global var) exten => s,n,Set(CALLERID(all)=${TRUNKOUTCID}) exten => s,n(usercid),GotoIf($["${USEROUTCID:1:2}" = ""]?report) ; check CID override for extension exten => s,n,Set(CALLERID(all)=${USEROUTCID}) exten => s,n,GotoIf($["x${CALLERID(name)}"!="xhidden"]?report:hidecid) ; check CID blocking for extension exten => s,n(hidecid),SetCallerPres(prohib_passed_screen) ; Only works with ISDN (T1/E1/BRI) exten => s,n(report),NoOp(CallerID set to ${CALLERID(all)}) ; Privacy Manager Macro makes sure that any calls that don't pass the privacy manager are presented ; with congestion since there have been observed cases of the call continuing if not stopped with a ; congestion, and this provides a slightly more friendly 'sorry' message in case the user is ; legitamately trying to be cooperative. ; ; Note: the following options are configurable in privacy.conf: ; ; maxretries = 3 ; default value, number of retries before failing ; minlength = 10 ; default value, number of digits to be accepted as valid CID ; [macro-privacy-mgr] exten => s,1,Set(KEEPCID=${CALLERID(num)}) exten => s,n,GotoIf($["foo${CALLERID(num):0:1}"="foo+"]?CIDTEST2:CIDTEST1) exten => s,n(CIDTEST1),Set(TESTCID=${MATH(1+${CALLERID(num)})}) exten => s,n,Goto(TESTRESULT) exten => s,n(CIDTEST2),Set(TESTCID=${MATH(1+${CALLERID(num):1})}) exten => s,n(TESTRESULT),GotoIf($["foo${TESTCID}"="foo"]?CLEARCID:PRIVMGR) exten => s,n(CLEARCID),Set(CALLERID(num)=) exten => s,n(PRIVMGR),PrivacyManager() exten => s,n,SetCallerPres(allowed_passed_screen); stop gap until app_privacy.c clears unavailble bit exten => s,PRIVMGR+101,Noop(STATUS: ${PRIVACYMGRSTATUS} CID: ${CALLERID(num)} ${CALLERID(name)} CALLPRES: ${CALLLINGPRES}) exten => s,n,Playback(sorry-youre-having-problems) exten => s,n,Playback(goodbye) exten => s,n,Playtones(congestion) exten => s,n,Congestion(5) ; ; ############################################################################ ; Inbound Contexts [from] ; ############################################################################ [from-sip-external] ;give external sip users congestion and hangup ; Yes. This is _really_ meant to be _. - I know asterisk whinges about it, but ; I do know what I'm doing. This is correct. exten => _.,1,NoOp(Received incoming SIP connection from unknown peer to ${EXTEN}) exten => _.,n,Set(DID=${IF($["${EXTEN:1:2}"=""]?s:${EXTEN})}) exten => _.,n,Goto(s,1) exten => s,1,Ringing exten => s,n,GotoIf($["${ALLOW_SIP_ANON}"="yes"]?from-trunk,${DID},1) exten => s,n,Set(TIMEOUT(absolute)=15) exten => s,n,Answer exten => s,n,Wait(2) exten => s,n,Playback(ss-noservice) exten => s,n,Playtones(congestion) exten => s,n,Congestion(5) exten => h,1,NoOp(Hangup) exten => i,1,NoOp(Invalid) exten => t,1,NoOp(Timeout) [from-internal] ; applications are now mostly all found in from-internal-additional in _custom.conf include => parkedcalls include => from-internal-custom ;allow phones to dial other extensions include => ext-fax ;allow phones to access generated contexts ; ; MODIFIED (PL) ; ; Currently the include for findmefollow is being auto-generated before ext-local which is the desired behavior. ; However, I haven't been able to do anything that I know of to force this. We need to determine if it should ; be hardcoded into here to make sure it doesn't change with some configuration. For now I will leave it out ; until we can discuss this. ; include => from-internal-additional include => ext-local-confirm ; This causes grief with '#' transfers, commenting out for the moment. ; include => bad-number exten => s,1,Macro(hangupcall) exten => h,1,Macro(hangupcall) ;------------------------------------------------------------------------ ; [bad-number] ;------------------------------------------------------------------------ ; This is where all calls go that don't have any other destination provided ; ;------------------------------------------------------------------------ [bad-number] exten => _X.,1,Wait(1) exten => _X.,n,Playback(silence/1&cannot-complete-as-dialed&check-number-dial-again,noanswer) exten => _X.,n,Wait(1) exten => _X.,n,Congestion(20) exten => _X.,n,Hangup() exten => _*.,1,Wait(1) exten => _*.,n,Playback(silence/1&feature-not-avail-line&silence/1&cannot-complete-as-dialed&check-number-dial-again,noanswer) exten => _*.,n,Wait(1) exten => _*.,n,Congestion(20) exten => _*.,n,Hangup() ;------------------------------------------------------------------------ [from-zaptel] exten => _X.,1,Set(DID=${EXTEN}) exten => _X.,n,Goto(s,1) exten => s,1,NoOp(Entering from-zaptel with DID == ${DID}) ; Some trunks _require_ a RINGING be sent before an Answer. exten => s,n,Ringing() ; If ($did == "") { $did = "s"; } exten => s,n,Set(DID=${IF($["${DID}"= ""]?s:${DID})}) exten => s,n,NoOp(DID is now ${DID}) exten => s,n,GotoIf($["${CHANNEL:0:3}"="Zap"]?zapok:notzap) exten => s,n(notzap),Goto(from-pstn,${DID},1) ; If there's no ext-did,s,1, that means there's not a no did/no cid route. Hangup. exten => s,n,Macro(hangup) exten => s,n(zapok),NoOp(Is a Zaptel Channel) exten => s,n,Set(CHAN=${CHANNEL:4}) exten => s,n,Set(CHAN=${CUT(CHAN,-,1)}) exten => s,n,Macro(from-zaptel-${CHAN},${DID},1) ; If nothing there, then treat it as a DID exten => s,n,NoOp(Returned from Macro from-zaptel-${CHAN}) exten => s,n,Goto(from-pstn,${DID},1) exten => fax,1,Goto(ext-fax,in_fax,1) ; ########################################## ; ## Ring Groups with Confirmation macros ## ; ########################################## ; Used by followme and ringgroups ;------------------------------------------------------------------------ ; [macro-dial-confirm] ;------------------------------------------------------------------------ ; This has now been incorporated into dialparties. It still only works with ringall ; and ringall-prim strategies. Have not investigated why it doesn't work with ; hunt and memory hunt. ; ;------------------------------------------------------------------------ [macro-dial-confirm] ; This was written to make it easy to use macro-dial-confirm instead of macro-dial in generated dialplans. ; This takes the same paramaters, with an additional paramater of the ring group Number ; ARG1 is the timeout ; ARG2 is the DIAL_OPTIONS ; ARG3 is a list of xtns to call - 203-222-240-123123123#-211 ; ARG4 is the ring group number ; This sets a unique value to indicate that the channel is ringing. This is used for warning slow ; users that the call has already been picked up. ; exten => s,1,Set(DB(RG/${ARG4}/${CHANNEL})=RINGING) ; We need to keep that channel variable, because it'll change when we do this dial, so set it to ; fallthrough to every sibling. ; exten => s,n,Set(__UNIQCHAN=${CHANNEL}) ; The calling ringgroup should have set RingGroupMethod appropriately. We need to set two ; additional parameters: ; ; USE_CONFIRMATION, RINGGROUP_INDEX ; ; Thse are passed to inform dialparties to place external calls through the [grps] context ; exten => s,n,Set(USE_CONFIRMATION=TRUE) exten => s,n,Set(RINGGROUP_INDEX=${ARG4}) exten => s,n,Set(ARG4=) ; otherwise it gets passed to dialparties.agi which processes it (prob bug) exten => s,n,Macro(dial,${ARG1},${ARG2},${ARG3}) ; delete the variable, if we are here, we are done trying to dial and it may have been left around ; exten => s,n,DBDel(RG/${RINGGROUP_INDEX}/${CHANNEL}) exten => s,n,Set(USE_CONFIRMATION=) exten => s,n,Set(RINGGROUP_INDEX=) ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ ; [macro-auto-confirm] ;------------------------------------------------------------------------ ; This macro is called from ext-local-confirm to auto-confirm a call so that other extensions ; are aware that the call has been answered. ; ;------------------------------------------------------------------------ [macro-auto-confirm] exten => s,1,Set(__MACRO_RESULT=) exten => s,n,DBDel(${BLKVM_OVERRIDE}) exten => s,n,DBDel(RG/${ARG1}/${UNIQCHAN}) ;------------------------------------------------------------------------ ; [macro-auto-blkvm] ;------------------------------------------------------------------------ ; This macro is called for any extension dialed form a queue, ringgroup ; or followme, so that the answering extension can clear the voicemail block ; override allow subsequent transfers to properly operate. ; ;------------------------------------------------------------------------ [macro-auto-blkvm] exten => s,1,Set(__MACRO_RESULT=) exten => s,n,DBDel(${BLKVM_OVERRIDE}) ;------------------------------------------------------------------------ ; [ext-local-confirm] ;------------------------------------------------------------------------ ; If call confirm is being used in a ringgroup, then calls that do not require confirmation are sent ; to this extension instead of straight to the device. ; ; The sole purpose of sending them here is to make sure we run Macro(auto-confirm) if this ; extension answers the line. This takes care of clearing the database key that is used to inform ; other potential late comers that the extension has been answered by someone else. ; ;------------------------------------------------------------------------ [ext-local-confirm] exten => _LC-.,1,Noop(IN ext-local-confirm with - RT: ${RT}, RG_IDX: ${RG_IDX}) exten => _LC-.,n,dial(${DB(DEVICE/${EXTEN:3}/dial)},${RT},M(auto-confirm^${RG_IDX})${DIAL_OPTIONS}) ;------------------------------------------------------------------------ ; [macro-confirm] ;------------------------------------------------------------------------ ; CONTEXT: macro-confirm ; PURPOSE: added default message if none supplied ; ; Follom-Me and Ringgroups provide an option to supply a message to be ; played as part of the confirmation. These changes have added a default ; message if none is supplied. ; ;------------------------------------------------------------------------ [macro-confirm] exten => s,1,Set(LOOPCOUNT=0) exten => s,n,Noop(CALLCONFIRMCID: ${CALLCONFIRMCID}) ; We set ABORT rather than CONTINUE, as we want the server to forget about this channel ; if it's declined, hung up, or timed out. We don't want it to continue on to the next ; step in the dialplan, which could be anything! exten => s,n,Set(__MACRO_RESULT=ABORT) ; ARG1 is the announcement to play to tell the user that they've got a call they need ; to confirm. Something along the lines of 'You have an incoming call. Press 1 to accept, 9 to reject' exten => s,n,Set(MSG1=${IF($["foo${ARG1}" != "foo"]?${ARG1}:"incoming-call-1-accept-2-decline")}) exten => s,n(start),Read(INPUT|${MSG1}|1||1|5) ; So. We've now read something, or nothing. We should check to make sure that the call hasn't ; already been answered by someone else. If it has, send this call to toolate exten => s,n,GotoIf(${DB_EXISTS(RG/${ARG3}/${UNIQCHAN})}?check:toolate) ; We passed that test, so it means the call hasn't been answered. Has this user pushed 1? If so, ; then go to OK. exten => s,n(check),GotoIf($["${INPUT}"="1"]?ok) ; If they've pushed 9, then they definately don't want the call. Just pretend there was no response ; and go to noanswer (or 2 since that will be default for asterisk) exten => s,n,GotoIf($["${INPUT}"="9"]?noanswer) exten => s,n,GotoIf($["${INPUT}"="2"]?noanswer) exten => s,n,GotoIf($["${INPUT}"="3"]?playcid) ; Increment LOOPCOUNT, and check to make sure we haven't played it 5 times by now. We assume that ; the person is able to push '1' in a reasonably short time. exten => s,n,Set(LOOPCOUNT=$[ ${LOOPCOUNT} + 1 ]) exten => s,n,GotoIf($[ ${LOOPCOUNT} < 5 ]?start) ; If we're here, that means we've played it MORE than 5 times. Set __MACRO_RESULT=ABORT, well, just ; coz, and goto fin, which is the last line, meaning it returns to the previous Dial, and pretends as ; if nothing has happened. exten => s,n(noanswer),Set(__MACRO_RESULT=ABORT) exten => s,n,Goto(fin) ; Test play callerid ; exten => s,n(playcid),Noop(Playing CID: ${CALLCONFIRMCID}) exten => s,n,SayDigits(${CALLCONFIRMCID}) exten => s,n,Goto(start) ; If we're here, it's because the call was already accepted by someone else. exten => s,n(toolate),Set(MSG2=${IF($["foo${ARG2}" != "foo"]?${ARG2}:"incoming-call-no-longer-avail")}) exten => s,n,Playback(${MSG2}) exten => s,n,Goto(noanswer) ; If we made it here, it's because the call _WAS_ accepted, AND it's still ringing. We delete the ; database entry (so that the DB_EXISTS line above will trigger a 'toolate' jump), and set the ; MACRO_RESULT variable to NOTHING. This is the magic string that joins both legs of the call together exten => s,n(ok),DBDel(RG/${ARG3}/${UNIQCHAN}) exten => s,n,DBDel(${BLKVM_OVERRIDE}) exten => s,n,Set(__MACRO_RESULT=) ; The end. exten => s,n(fin),NoOp(Finished) exten => h,1,Noop(Hangup Extension in macro-confirm) exten => h,n,Macro(hangupcall) ;------------------------------------------------------------------------ ; ############################################################################ ; Extension Contexts [ext] ; ############################################################################ [ext-fax] exten => s,1,Answer exten => s,2,Goto(in_fax,1) exten => in_fax,1,StopPlayTones exten => in_fax,2,GotoIf($["${FAX_RX}" = "system"]?3:analog_fax,1) exten => in_fax,3,Macro(faxreceive) exten => in_fax,4,Hangup exten => analog_fax,1,GotoIf($["${FAX_RX}" = "disabled"]?4:2) ;if fax is disabled, just hang up exten => analog_fax,2,Set(DIAL=${DB(DEVICE/${FAX_RX}/dial)}); exten => analog_fax,3,Dial(${DIAL},20,d) exten => analog_fax,4,Hangup ;exten => out_fax,1,wait(7) exten => out_fax,1,txfax(${TXFAX_NAME}|caller) exten => out_fax,2,Hangup exten => h,1,system(/var/lib/asterisk/bin/fax-process.pl --to ${EMAILADDR} --from ${FAX_RX_FROM} --subject "Fax from ${URIENCODE(${CALLERID(number)})} ${URIENCODE(${CALLERID(name)})}" --attachment fax_${URIENCODE(${CALLERID(number)})}.pdf --type application/pdf --file ${FAXFILE}); exten => h,2,Hangup() ;this is where parked calls go if they time-out. Should probably re-ring [default] include => ext-local exten => s,1,Playback(vm-goodbye) exten => s,2,Macro(hangupcall) ;;;;;;;;;;;;;;;;;;;;;billing [a2billing] exten => X.,1,Answer exten => X.,2,Wait,2 exten => X.,3,DeadAGI,a2billing.php exten => X.,4,Wait,2 exten => X.,4,Hangup