Now that you’ve learned a bit about expressions and functions, it’s time to put them to use. By using expressions and functions, you can add even more advanced logic to your dialplan. To allow your dialplan to make decisions, you’ll use conditional branching. Let’s take a closer look.
The key to conditional branching is the GotoIf()
application. GotoIf()
evaluates an expression and sends the caller to a specific destination
based on whether the expression evaluates to true
or false
.
GotoIf()
uses a special
syntax, often called the conditional
syntax:
GotoIf(expression
?destination1
:destination2
)
If the expression evaluates to true, the caller is sent to
destination1
. If the expression evaluates
to false, the caller is sent to the second destination. So, what is
true and what is false? An empty string and the number 0 evaluate as
false. Anything else evaluates as true
.
The destinations can each be one of the following:
A priority label within the same extension, such as
weasels
An extension and a priority label within the same context, such as
123,weasels
A context, extension, and priority label, such as
incoming,123,weasels
Either of the destinations may be omitted, but not both. If the omitted destination is to be followed, Asterisk simply goes on to the next priority in the current extension.
Let’s use GotoIf()
in an
example:
exten => 345,1,Set(TEST=1) exten => 345,n,GotoIf($[${TEST} = 1]?weasels:iguanas) exten => 345,n(weasels),Playback(weasels-eaten-phonesys) exten => 345,n,Hangup() exten => 345,n(iguanas),Playback(office-iguanas) exten => 345,n,Hangup()
Note
You will notice that we have used the Hangup()
application following each
Playback()
application. This is
done so that when we jump to the weasels
label, the call stops before
execution gets to the office-
iguanas
sound file. It is becoming
increasingly common to see extensions broken up in to multiple
components (protected from each other by the Hangup()
command), each one acting as
steps executed following a GotoIf()
.
Typically when you have this type of layout where you end up wanting to limit Asterisk from falling through to the next priority after you’ve performed that jump, it’s probably better to jump to separate extensions instead of priority labels. If anything, it makes it a bit more clear when reading the dialplan. We could rewrite the previous bit of dialplan like this:
exten => 345,1,Set(TEST=1)
exten => 345,n,GotoIf($[${TEST} = 1]?weasels,1:iguanas,1); now we're going to
; extension,priority
exten => weasels,1,Playback(weasels-eaten-phonesys); this is NOT a label.
; It is a different extension
exten => weasels,n,Hangup()
exten => iguanas,1,Playback(office-iguanas)
exten => iguanas,n,Hangup()
By changing the value assigned
to TEST
in the first line, you
should be able to have your Asterisk server play a different
greeting.
Let’s look at another example of conditional branching. This
time, we’ll use both Goto()
and GotoIf()
to count down from 10 and then hang
up:
exten => 123,1,Set(COUNT=10) exten => 123,n(start),GotoIf($[${COUNT} > 0]?:goodbye) exten => 123,n,SayNumber(${COUNT}) exten => 123,n,Set(COUNT=$[${COUNT} - 1]) exten => 123,n,Goto(start) exten => 123,n(goodbye),Hangup()
Let’s analyze this example. In the first priority, we set the
variable COUNT
to 10
. Next, we check to see if COUNT
is greater than 0
. If it is, we move on to the next
priority. (Don’t forget that if we omit a destination in the GotoIf()
application, control goes to the
next priority.) From there we speak the number, subtract 1
from COUNT
, and go back to priority label
start
. If COUNT
is less than or equal to 0
, control goes to priority label goodbye
, and the call is hung up.
The classic example of conditional branching is affectionately known as the anti-girlfriend logic. If the Caller ID number of the incoming call matches the phone number of the recipient’s ex-girlfriend, Asterisk gives a different message than it ordinarily would to any other caller. While somewhat simple and primitive, it’s a good example for learning about conditional branching within the Asterisk dialplan.
This example uses the CALLERID
function, which allows us to
retrieve the Caller ID information on the inbound call. Let’s assume
for the sake of this example that the victim’s phone number is
888-555-1212:
exten => 123,1,GotoIf($[${CALLERID(num)} = 8885551212]?reject:allow) exten => 123,n(allow),Dial(Zap/4) exten => 123,n,Hangup() exten => 123,n(reject),Playback(abandon-all-hope) exten => 123,n,Hangup()
In priority 1, we call the GotoIf()
application. It tells Asterisk to
go to priority label reject
if the
Caller ID number matches 8885551212
, and otherwise to go to priority
label allow
(we could have simply
omitted the label name, causing the GotoIf()
to fall through). If the Caller ID
number matches, control of the call goes to priority label reject
, which plays back an uninspiring
message to the undesired caller. Otherwise, the call attempts to dial
the recipient on channel Zap/4
.
Another way to use conditional branching in your dialplan is with
the GotoIfTime()
application.
Whereas GotoIf()
evaluates an
expression to decide what to do, GotoIfTime()
looks at the current system
time and uses that to decide whether or not to follow a different
branch in the dialplan.
The most obvious use of this application is to give your callers a different greeting before and after normal business hours.
The syntax for the GotoIfTime()
application looks like
this:
GotoIfTime(times
,days_of_week
,days_of_month
,months
?label
)
In short, GotoIfTime()
sends
the call to the specified label
if the
current date and time match the criteria specified by
times
,
days_of_week
,
days_of_month
, and
months
. Let’s look at each argument in more
detail:
times
This is a list of one or more time ranges, in a 24-hour format. As an example, 9:00 A.M. through 5:00 P.M. would be specified as
09:00-17:00
. The day starts at 0:00 and ends at 23:59.Note
It is worth noting that times will properly wrap around. So if you wish to specify the times your office is closed, you might write
18:00-9:00
in thetimes
parameter, and it will perform as expected. Note that this technique works as well for the other components ofGotoIfTime
. For example, you can writesat-sun
to specify the weekend days.days_of_week
This is a list of one or more days of the week. The days should be specified as
mon
,tue
,wed
,thu
,fri
,sat
, and/orsun
. Monday through Friday would be expressed asmon-fri
. Tuesday and Thursday would be expressed astue&thu
.Note
Note that you can specify a combination of ranges and single days, as in:
sun-mon&wed&fri-sat
, or, more simply:wed&fri-mon
.days_of_month
This is a list of the numerical days of the month. Days are specified by the numbers
1
through31
. The 7th through the 12th would be expressed as7-12
, and the 15th and 30th of the month would be written as15&30
.months
This is a list of one or more months of the year. The months should be written as
jan
-apr
for a range, and separated with ampersands when wanting to include nonsequencial months, such asjan&mar&jun
. You can also combine them like so:jan-apr&jun&oct-dec
.
If you wish to match on all possible values for any of these
arguments, simply put an *
in for that
argument.
The label
argument can be any of the following:
A priority label within the same extension, such as
time_has_passed
An extension and a priority within the same context, such as
123,time_has_passed
A context, extension, and priority, such as
incoming,123,time_has_passed
Now that we’ve covered the syntax, let’s look at a couple of examples. The following example would match from 9:00 A.M. to 5:59 P.M., on Monday through Friday, on any day of the month, in any month of the year:
exten => s,1,GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)
If the caller calls during these hours, the call will be sent to
the first priority of the s
extension in the context named open
. If the call is made outside of the
specified times, it will be sent to the next priority of the current
extension. This allows you to easily branch on multiple times, as
shown in the next example (note that you should always put your most
specific time matches before the least specific ones):
; If it's any hour of the day, on any day of the week, ; during the fourth day of the month, in the month of July, ; we're closed exten => s,1,GotoIfTime(*,*,4,jul?closed,s,1) ; During business hours, send calls to the open context exten => s,n,GotoIfTime(09:00-17:59|mon-fri|*|*?open,s,1) exten => s,n,GotoIfTime(09:00-11:59|sat|*|*?open,s,1) ; Otherwise, we're closed exten => s,n,Goto(closed,s,1)
Tip
If you run into the situation where you ask the question, “But
I specified 17:58 and it’s now 17:59. Why is it still doing the same
thing?” it should be noted that the granularity of the GotoIfTime()
application is only to a
two-minute period. So if you specify 18:00 as the ending time of a
period, the system will continue to perform the same way for an
additional minute, until 18:01:59.
Get Asterisk: The Future of Telephony, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.