While trying to think of a bit complex dial plan example for VoIP-students I came up with the idea of a call-back service.
The idea is whenever an external call comes into IP-Telephony server for some user, if that user is unavailable to take call, the call should get routed to voice mail OR should be enlisted in a list. This list will be collection of all the callers to that user who wanted to be called back automatically whenever the user likes.
From IP-telephony user's perspective its a missed-calls register sort of list which is dialled back automatically once triggered.
Not the smartest idea but from Asterisk Dial-plan P.O.V it has lots of things to be learned.
Since this is abit complex to understand dial-plan project so I think I'll break it into two parts. First one is going to deal with Incoming call from outside and saving the number in the call-back register.
Second part will focus on the actual service where an IP-PBX user dials into the service number and go through his call-back list.
What we'll Learn:
1- Setting up voicemail service for users.
2- Using Asterisk built-in DB i.e AstDB to save and retrieve User lists.
3- Use of Macros in Dial-plan
4- Asterisk dialplan applications SayDigits, ExecIf, While/EndWhile, Voicemail
5- Asterisk dialplan functions DB, DB_EXISTS, FIELDQTY, CUT, ARRAY
6- Using Labels in dial-plan to transfer control between priorities
Setting up Voicemail:
Setting up voicemail is fairly simple task. Just edit the file /etc/asterisk/voicemail.conf and insert following lines
[new-vmail-group]
101 => 12345678,Dean Winchester,dean@supernatural.net
where,
101 is User's voicemailbox number - I chose it to be the same as of User's Telephony extension.
12345678 is users's voicemail access pin.
Next two fields are user name and user email respectively.
To make voicemail box configurations active don't forget to execute "voicemail reload" in asterisk command line interface.
Dial-Plan Code:
once Voicemail is configured we need to call the voicemail application if user is unavailable. As you can see from code below that voicemail is called after Dial() application. Dial application is configured to timeout after 10 seconds of ringing to the destination.
[from-external-world]exten => _X.,1,NOOP( Call from ${CALLERID(num)} to ${EXTEN})same => n,Answer()same => n,SET(DEST=${EXTEN})same => n,Dial(SIP/${EXTEN},10)same => n,Voicemail(${EXTEN}@default,d(register-callback))same => n,Hangup()
Once the dial application is timed out or the user in unavailable or busy incomming caller will listen the voicemail prompt. "Please record your message after the tone, when done press # or hangup".
Meanwhile listening to this message user can press one-digit key from his cell-phone and the call control will be transferred to "register-callback" context. thanks to the option "d" in voicemail application.
Hint: execute "core show application voicemail" in asterisk CLI to know more.
[register-callback]exten => s,1,NOOP(Caller ${CALLERID(num)} Registering Call-Back for User ${DEST})same => n,NOOP(do some call-back tricks here)same => n,SET(CBQ=${DB(call-back/${DEST})})same => n,GOTOIF($["${CBQ}" == ""]?first:sec)same => n(first),Set(DB(call-back/${DEST})=${CALLERID(num)})same => n,GOTO(jump)same => n(sec),Macro(duplicate-check,${CALLERID(num)})same => n,GOTOIF($["${RESULT}" == "1"]?jump)same => n,Set(DB(call-back/${DEST})=${CALLERID(num)}-${CBQ})same => n(jump),NOOP(Playback(thanks-willb-called-shortly))same => n,Hangup()exten => i,1,GOTO(s,1)
I know, the above code isn't easy to even look at. thats why I've highlighted important things to learn here.
1- Labels:
Anything that is enclosed in () next to priority field (i.e "n" in my case) becomes a label and control can be transferred from anywhere to these labels. "first","sec","jump" are labels in the above context.
2- Asterisk DB operations:
DB(call-back/${DEST}) is used to GET or SET values from AstDB. In this case "call-back" is family name (DB name) ; Value of ${DEST} will be the Key to access one unique Value/Result set from the AstDB.
If DB() function is enclosed in ${} then asterisk will GET/fetch the value from the DB otherwise asterisk will SET the value in AstDB(given anything appears in right hand operand with operator =)
Example:
Fetch the value from AstDB and Save in variable "CBQ"
same => n,SET(CBQ=${DB(call-back/${DEST})})
Set value from "${CALLERID(num)}-${CBQ}" into the AstDB.
same => n,Set(DB(call-back/${DEST})=${CALLERID(num)}-${CBQ})3- Using Macros
Macros are like functions which accepts arguments. Just like regular contexts now-a-days. See this link for detailed information.
same => n(sec),Macro(duplicate-check,${CALLERID(num)})
I don't recommend using macros but unfortunately these are essential part of asterisk dial-plan learning. In above dial-plan code ${CALLERID(num)} becomes argument-1 to "macro-duplicate-check"
As soon as the above line is executed call-control jumps to context "[macro-duplicate-check]"
[macro-duplicate-check]exten => s,1,NOOP(${CBQ} Checked for Duplicate ${ARG1})exten => s,n,SET(COUNT=${FIELDQTY(CBQ,-)})exten => s,n,SET(ARRAY(i,RESULT)=1,0)exten => s,n,WHILE($["${i}" <= "${COUNT}"])exten => s,n,NOOP(${CUT(CBQ,,${i})} == ${ARG1})exten => s,n,EXECIF($["${CUT(CBQ,,${i})}" == "${ARG1}"]?GOTO(found):SET(i=$[${i} + 1]))exten => s,n,Endwhile()exten => s,n,MacroExit()exten => s,n(found),SET(RESULT=1)exten => s,n,MacroExit()
Again, important things have been highlighted. This macro is just an exceptional handling that the same number don't get inserted into the call-back list more than once.
Everyone knows about While-looping the only difference here is that an EndWhile() defines where the While-Block ends.
EXECIF is just another application to compress 6+ Lines of dial-plan into just one line. It execute one application if the condition is true or execute the second application otherwise.
exten => s,n,EXECIF($["${CUT(CBQ,,${i})}" == "${ARG1}"]?GOTO(found):SET(i=$[${i} + 1]))
I find it easy to compress 6+ lines of GOTOIFs and applications into just one line.
Here we are done with the first part of the service.
So far we've just done what the flow-chart said in this post; Receive incoming call, and skip voicemail if user wants to register for call-back, check if the number already exists in Call-back list. Finally play message to caller that he'll be called back shortly.