Asterisk, and other worldly endeavours.

A blog by Leif Madsen

Set() and Goto() on same line


(Thanks to Jared Smith for answering my question in IRC which is the inspiration for this post.)

Typically when I write dialplan, primarily in the case where I’m using a pattern match, I’ll save the dialed extension to a channel variable using Set(), then do a Goto() where the call logic is handled. The Set() is so that I don’t lose the value of ${EXTEN} throughout the dialplan process, especially if I'm using other mechanics such as GoSub() and others.

I’ve been doing this on two or three lines like this (usually three because I like using a NoOp() or Verbose() for the first priority):

exten => _NXXNXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

This is kind of annoying for each pattern match, especially if you’re going to do multiple. Here is a legitimate example of the CallHandler extension:

exten => _NXXNXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

exten => _1NXXNXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

exten => _NXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

exten => CallHandler,1,NoOp()
   same => n,Dial(${GLOBAL(PSTN_CONNECTION)}/${DialedExtension},30)
   same => n,Hangup()

It’s a bit annoying having to either type out the same type of logic multiple times, even if it’s only 2-3 lines (even if you just copy and paste the same => lines it’s a bit better, but still not ideal). So here’s a solution to the same problem of multiple pattern matches and doing a Goto() our CallHandler extension.

exten => _NXXNXXXXXX,1,GotoIf($[${EXISTS(${SET(DialedExtension=${EXTEN})})}]?CallHandler,1:i,1)
exten => _1NXXNXXXXXX,1,GotoIf($[${EXISTS(${SET(DialedExtension=${EXTEN})})}]?CallHandler,1:i,1)
exten => _NXXXXXX,1,GotoIf($[${EXISTS(${SET(DialedExtension=${EXTEN})})}]?CallHandler,1:i,1)

exten => CallHandler,1,NoOp()
   same => n,Dial(${GLOBAL(PSTN_CONNECTION)}/${DialedExtension},30)
   same => n,Hangup()

exten => i,1,Congestion()

While both ways are perfectly reasonable (and some may argue the more verbose method is easier to read), I like embedding dialplan into a single line when I can as I find it easier to maintain. I’m also pretty good at knowing how many brackets to end with when nesting functions, but not everyone is comfortable doing that; in those cases you should probably break it out to multiple lines in order to save debugging time. Both methods are perfectly valid, so enjoy using whichever you prefer!

Advertisements

Written by Leif Madsen

2011/08/12 at 9:14 pm

Posted in Asterisk, Programming

Tagged with , ,

8 Responses

Subscribe to comments with RSS.

  1. Why does it appear like you’re trying to reimplement Macro or Gosub in the dialplan, badly?

    Tilghman Lesher

    2011/08/13 at 9:16 am

  2. Well that does raise an interesting point πŸ™‚ I guess the *real* solution is this:

    exten => _NXXNXXXXXX,1,GoSub(CallHandler,1(${EXTEN}))
    exten => _1NXXNXXXXXX,1,GoSub(CallHandler,1(${EXTEN}))
    exten => _NXXXXXX,1,GoSub(CallHandler,1(${EXTEN}))
    
    exten => CallHandler,1,NoOp()
       same => n,Set(DialedExtension=${ARG1})
       same => n,Dial(${GLOBAL(PSTN_CONNECTION)}/${DialedExtension})
       same => n,Hangup()
    

    Leif Madsen

    2011/08/13 at 10:10 am

  3. After talking to Tilghman in IRC (and kidding him that we should remove GoSub() because it’s duplicate functionality as I’ve proven you can do the same thing with the existing dialplan functions) he noted we also have LOCAL(); a function that keeps the channel variables local to the subroutine where the variable was set (similar in style to Macro()).

    Here is the modified dialplan using the LOCAL() function:

    exten => _NXXNXXXXXX,1,GoSub(CallHandler,1(${EXTEN}))
    exten => _1NXXNXXXXXX,1,GoSub(CallHandler,1(${EXTEN}))
    exten => _NXXXXXX,1,GoSub(CallHandler,1(${EXTEN}))
    
    exten => CallHandler,1,NoOp()
       same => n,Set(LOCAL(DialedExtension)=${ARG1})
       same => n,Dial(${GLOBAL(PSTN_CONNECTION)}/${DialedExtension})
       same => n,Hangup()
    

    It should be noted that you don’t need to read the channel variable set with LOCAL(). (Although there is no harm in wrapping the channel variable in LOCAL(), in complex dialplans it might be nice to wrap the read in LOCAL() so it’s clear the channel variable will no longer be available after Return()‘ing from the subroutine).

    Again this is pretty much the way a Macro() would be expected to work, so when converting dialplans over to using GoSub() (or simply writing new dialplans using GoSub()) you can mimic the behaviour of Macro().

    Thanks to Tilghman for the explanations!

    Tip: If you are using a nested GoSub() (i.e. a subroutine calling another subroutine) you can use the PEEK() function to look at the locally set channel variable from within the nested subroutine.

    Leif Madsen

    2011/08/13 at 10:28 am

  4. Huge favor, I’m trying to do something similar but cant work out how…

    set(CALLERID(all)=6491234567 )

    Everything works fine, call comes in hits the extension and does a call divert for the customer, problem lies where the person calling the extension has a hidden ‘anonymous’ number. the call comes in and then sets the caller id as anonymous and our providers dont accept the call.
    For EG: — Executing [6491234567@asterisk:1] Set(“SIP/vibe-000005c0”, “CALLERID(all)=6491234567”)

    What I need is an if statement within the same set rule to say if $CALLERID(name) == ‘anonymous’ then set then I want it to set(CALLERID(all)=6491234567 )

    I would really really like to do this on the same line rather than gotoif’s to different lines

    Barry Murphy

    2012/09/05 at 8:55 pm

    • Well the right way is likely the ExecIf() application:

      exten => foo,1,ExecIf($["${CALLERID(name)}" = "anonymous"]?Set(CALLERID(all)=4165551212))
      

      Otherwise you can use the IF() function:

      exten => foo,1,Set(CALLERID(all)=${IF($["${CALLERID(name)}" = "anonymous"]?4165551212:${CALLERID(all)})})
      

      Leif Madsen

      2012/09/06 at 6:31 am

      • So am I correct in saying i’ll get the following outcome

        (CALLERID(all)=6494409877 ${IF($["${CALLERID(name)}" = "anonymous"]?6494409877:${CALLERID(num)})}
        

        If a call came in with anonymous it would set CALLERID(all)=6494409877

        And if a call came in from 6491234567 it would set CALLERID(all)=6494409877

        This is my desired outcome.

        Many thanks for your last post and your assistance so far.

        Barry Murphy

        2012/09/06 at 5:18 pm

        • I’d just give it a shot and see if it works as you expect πŸ™‚

          Leif Madsen

          2012/09/06 at 5:19 pm

          • excellent, it worked thanks!

            Barry Murphy

            2012/09/06 at 6:26 pm


Comments are closed.

%d bloggers like this: