Taking the call - My extensions.conf for Asterisk 1.2 and How it Works

I finally got my dialplan configured to some satisfaction. I'm not so sure I want it to work the way I described any more, but I've built pretty much what I said I would. I'll explain here how to do what I did and walk through all the configuration that I did for Asterisk. Remember though that I'm a beginner at this stuff and this is my first dialplan. There could be massive holes in it that I don't know about yet (if you see one, do let me know :) ). The examples that I'll go through give away details of my internal menus, but I'll be changing the structure for my own use (as far as extension numbers & whatnot) so don't expect to just call up & start calling long distance on my dime :P.

I started from the sample extensions.conf to try things out. First I added the features I wanted then I deleted pretty much everything but those new parts I had added. The biggest piece that I kept was most of the standard extension macro. The term 'extension' is overextended. The standard extension macro acts like an extension that you'd typically think of when you think of a phone extension. It calls a user and routes to voicemail if the user is on the phone, busy or otherwise unavailable. I'll go through the interesing parts of my exensions.conf then include a link to the entire file down below.

First off the slightly tweaked macro-stdexten, mine's called calluser:

[macro-calluser]
; ${ARG1} is the user's outside extension, often we're connecting over SIP to PSTN
; so something like 'SIP/lesnet_peer/15195551212'
; allow dialing out, phonebook dialing and voicemail
exten => s,1,SayDigits(${MACRO_EXTEN})
exten => s,n,Dial(${ARG1},30)        ; Ring the interface, 30 seconds maximum
exten => s,n,Goto(s-${DIALSTATUS},1) ; Jump based on status (NOANSWER,BUSY,CHANUNAVAIL,CONGESTION,ANSWER)
exten => s-NOANSWER,1,Voicemail(u${MACRO_EXTEN}) ; If unavailable, send to voicemail w/ unavail announce
exten => s-NOANSWER,2,Goto(incoming,s,1)   ; If they press #, return to start
exten => s-BUSY,1,Voicemail(${MACRO_EXTEN}b)     ; If busy, send to voicemail w/ busy announce
exten => s-BUSY,2,Goto(incoming,s,1)       ; If they press #, return to start
exten => _s-.,1,Goto(s-NOANSWER,1)        ; Treat anything else as no answer
exten => a,1,VoicemailMain(${MACRO_EXTEN})       ; If they press *, send the user into VoicemailMain

I removed the extra argument to make it easier to use. ${MACRO_EXTEN} contains the extension the macro was called from. This means you can't use 855 to call extension 234. I think that's a good thing. I also changed the context for the two spots Goto() is called. My default context is called [incoming]. Here's what that context looks like

[incoming]
exten => s,1,Set(TIMEOUT(digit)=5)      ; Set Digit Timeout to 5 seconds
exten => s,n,Set(TIMEOUT(response)=10)  ; Set Response Timeout to 10 seconds
exten => s,n(restart),BackGround(rob-enter-extension-or-2) ; "Enter the extension of the party you wish to reach or press 2 for a list of extensions"
exten => s,n,WaitExten          ; Wait for an extension to be dialed.
exten => _1[67]X,1,Set(TRYEXT=${EXTEN:1})
exten => _1[67]X,n,Goto(known-user,s,1)
exten => 77,1,Macro(calluser,SIP/lesnet_peer/15195551212) ; Rob
exten => 63,1,Macro(calluser,SIP/lesnet_peer/15195551213) ; Alex
exten => 71,1,Macro(calluser,SIP/lesnet_peer/15195551214) ; Candace
exten => 72,1,Macro(calluser,SIP/lesnet_peer/18475551215) ; Jeff
exten => t,1,Goto(s,restart)
exten => i,1,Playback(invalid)          ; "That's not valid, try again"
exten => i,n,Goto(s,restart)

While working on this I learned a few things. First that every extension has to have a priority 1 action in order to be a valid extension. Sometimes I got an error message like

WARNING[16775]: app_macro.c:162 macro_exec: Context 'macro-mainmenu-outbound' for macro 'mainmenu-outbound' lacks 's' extension, priority 1
but I'm not sure if it it's always reported. Nothing happens when you try to get to them by dialing. Maybe you can still call them with a Goto or some other means, I don't know.

The extensions that begin with s are the start. When someone calls in from my DID I use a Goto() to send them to the [incoming] context and they land at s,1. It should be pretty clear from the comments, but the first couple lines allow up to 5 seconds between keypresses and 10 seconds before Asterisk thinks the user has fallen asleep. If you take more than 5 seconds between keypresses and the result isn't an extension that's accessible then you drop down to i,1. The standard extension i handles invalid extensions and will play a message saying the extension isn't valid, please try again. If the user does nothing for 10 seconds then the standard extension t controls what happens next. At t,1 I've just told Asterisk to loop back to s,restart. In this case restart refers to the label above at s,n(restart). It reminds me a lot of programming in Basic. If nothing else at least the rules are pretty clear.

I'm going to skip the _1[67]X for a second to go on to the simpler two digit extensions. For each of 77, 63, 71, and 72 I've followed the same template. The macro calluser is executed for the extension dialed and a phone number is given to complete the phone call. I started with macros because I thought maybe they'd be pretty similar to a function call. Macros aren't that much like functions. You can pass parameters in (like I've done with the phone number) but there are other rules that are different. One thing to remember with a macro is that it only has one extension, s, internally. There's also a limit of about 7 nested calls to macros in Asterisk. This may have changed, but I think I read it in Asterisk The Future of Telephony (where I also got the bit about the s extension and some other helpful info).

Continuing with the [incoming] context above, after s, I have an extension with the number _1[67]X. The underscore (_) means it's a pattern, the 1 will match only a 1, the [67] means either 6 or 7 will match the next digit in the pattern and the X means this last digit can be any single digit from 0 to 9. I use this extension to match any of 177, 163, 171 or 172. Any other numbers from 160 to 179 will also match here. I use this pattern to allow known users to log in by dialing 1 followed by their extension. The log in is handled by jumping to the beginning of the [known-user] context. Before the Goto(), I set a variable called ${TRYEXT}. I don't generally like the idea of using globals to pass information (in programming) but I don't know another way that I'm sure of in the dialplan. Maybe I could have used a macro, I'm not sure. Here's the [known-user] context.

[known-user]
;7 for voicemail
;8 for meetings
;9 to dial out
exten => s,1,Set(TIMEOUT(digit)=5)      ; Set Digit Timeout to 5 seconds
exten => s,n,Set(TIMEOUT(response)=10)  ; Set Response Timeout to 10 seconds
exten => s,n(restart), Macro(mainmenu-login,${TRYEXT},1)
exten => s,n(nextchoice), NoOp()
exten => s,n,WaitExten          ; Wait for an extension to be dialed.

exten => 7,1,macro(mainmenu-voicemail,${TRYEXT})
exten => 8,1,macro(mainmenu-default-conference,${TRYEXT})
exten => _9XXXXXXXXXXX,1,macro(mainmenu-outbound,${TRYEXT},${EXTEN})

exten => t,1,Goto(s,nextchoice)
exten => i,1,Playback(invalid)          ; "That's not valid, try again"
exten => i,n,Goto(s,nextchoice)

Inside the [known-user] context I've broken each option into a macro. Since the macros ended up being only one line in most cases I know this wasn't necessary. I think it makes it easier to expand in the future though. If I have extra steps that are needed for a menu option then there's a possibility that the macro could be expanded and things stay broken up neatly. The extensions that really compose the menu start with 7, 8 and 9. Before any options in the menu are available, the s extension executes and calls the mainmenu-login macro. In the event of an invalid extension or a timeout things work like they did in the [incoming] context, except the nextchoice label is used instead of restart so that te user doesn't have to log in again. I kept a restart label in the [known-user] context in case I need it for other kinds of errors.

Let's look at the mainmenu-login macro first then the macros that make up the menu options. I mentioned the other day that it seems like Asterisk doesn't have one consistent concept of a user and I didn't have a good way to deal with this. I think VMAuthenticate() is a good solution to this. The way it works is that it prompts the user using the same code as the voicemail system. This means the user id is the same as their voicemail id and the password is the same. So there's no concern about synchronizing passwords for different user IDs that refer to the same real person. All they need is to be set up in voicemail (handled easily in voicemail.conf as I'll show later). So here's how the mainmenu-login macro looks.

[macro-mainmenu-login]
exten => s,1,SayDigits(${ARG1})
exten => s,n,VMAuthenticate(${ARG1})

All it does is repeat the user's extension (which has to be passed in to the macro) then asks them for their password. Voicemail behaviour is coded into the voicemail module directly. It handles incorrect passwords by hanging up after 3 tries. There could be other ways out but I don't know yet. If the user logs in correctly then control passes back to where the macro was called from. So the user is logged in and we can continue by presenting the options that we only want logged in users to get. Cool.

Here are the macros that are hooked up to the 7, 8 and 9 options in the main menu (the known-user context) above.

[macro-mainmenu-voicemail]
; ${ARG1} is the user's extension
; calling example for extension 55
; exten => 7, 1, macro(mainmenu-voicemail, 55)
exten => s,1,VoicemailMain(s${ARG1})
exten => s,n,Goto(known-user,s,nextchoice)

[macro-mainmenu-default-conference]
; ${ARG1} is the user's extension
; calling example for extension 55
; exten => 8, 1, macro(mainmenu-default-conference, 55)
exten => s,1,meetme(101,,123456)
exten => s,n,Goto(known-user,s,nextchoice)

[macro-mainmenu-outbound]
; ${ARG1} is the user's extension
; ${ARG2} is ${EXTEN}
; calling example for extension 55
; exten => _9XXXXXXXXXXX,1,macro(mainmenu-outbound, 55, ${EXTEN})
exten => s,1,Dial(SIP/lesnet_peer/${EXTEN:1})
exten => s,n,Goto(known-user,s,nextchoice)

The comments explain what's happening but I'll go over it briefly now. When I send a user to voicemail, they've already entered their password once. So I don't want to prompt them for it again. The s at the beginning of the options for the call to VoiceMailMain() skips the password check. The last line of each macro uses Goto(known-user,s,nextchoice) to send the user back to the prompt for their next choice. I guess there should be an option to end the call some where, but for now you (the caller) just have to hang up. The mainmenu-default-conference macro puts the user in a conference room. I don't know of any way to control MeetMe once the conference is started so I don't know a way to invite other users. The conferencing method that I have set up is only useful for a group that has a conference arranged outside somehow, for example a standing meeting at 10:00AM that everyone calls in to.

The final macro of the main menu options allows an outbound call. The logic for this is the same as I used in my Quick Guide to Cheap Calls with Asterisk the other day. I've since read that it's a good idea to filter out calls to certain areas within North America (notably the Carribean) that allow exorbhant toll charges. The area code filtering you'd need for that is still beyond me but would be a good addition to any dialplan.

Once again I've written a whole lot more than I thought I would - I expected to just whiz through all my conf files. This is pretty much it for extensions.conf, I'll cover the simpler parts of voicemail.cong, sip.conf and meetme.conf next time. Oh, here's a page with the full text of the extensions.conf.

5
Your rating: None Average: 5 (1 vote)